Esempio n. 1
0
def main():
    # vytvoření aplikace s textovým uživatelským rozhraním
    application = Application(full_screen=True)

    # spuštění aplikace
    application.run()
Esempio n. 2
0
    def run_cli(self):
        sqlexecute = self.sqlexecute
        logger = self.logger
        self.configure_pager()

        self.refresh_completions()

        project_root = os.path.dirname(PACKAGE_ROOT)
        author_file = os.path.join(project_root, 'AUTHORS')
        sponsor_file = os.path.join(project_root, 'SPONSORS')

        key_binding_manager = mycli_bindings()

        if not self.less_chatty:
            print('Version:', __version__)
            print('Chat: https://gitter.im/dbcli/mycli')
            print('Mail: https://groups.google.com/forum/#!forum/mycli-users')
            print('Home: http://mycli.net')
            print('Thanks to the contributor -',
                  thanks_picker([author_file, sponsor_file]))

        def prompt_tokens(cli):
            return [(Token.Prompt, self.get_prompt(self.prompt_format))]

        def get_continuation_tokens(cli, width):
            continuation_prompt = self.get_prompt(
                self.prompt_continuation_format)
            return [(Token.Continuation, ' ' *
                     (width - len(continuation_prompt)) + continuation_prompt)]

        get_toolbar_tokens = create_toolbar_tokens_func(
            self.completion_refresher.is_refreshing)

        layout = create_prompt_layout(
            lexer=MyCliLexer,
            multiline=True,
            get_prompt_tokens=prompt_tokens,
            get_continuation_tokens=get_continuation_tokens,
            get_bottom_toolbar_tokens=get_toolbar_tokens,
            display_completions_in_columns=self.wider_completion_menu,
            extra_input_processors=[
                ConditionalProcessor(
                    processor=HighlightMatchingBracketProcessor(
                        chars='[](){}'),
                    filter=HasFocus(DEFAULT_BUFFER) & ~IsDone()),
            ])
        with self._completer_lock:
            buf = CLIBuffer(always_multiline=self.multi_line,
                            completer=self.completer,
                            history=FileHistory(
                                os.path.expanduser(
                                    os.environ.get('MYCLI_HISTFILE',
                                                   '~/.mycli-history'))),
                            complete_while_typing=Always(),
                            accept_action=AcceptAction.RETURN_DOCUMENT)

            if self.key_bindings == 'vi':
                editing_mode = EditingMode.VI
            else:
                editing_mode = EditingMode.EMACS

            application = Application(
                style=style_factory(self.syntax_style, self.cli_style),
                layout=layout,
                buffer=buf,
                key_bindings_registry=key_binding_manager.registry,
                on_exit=AbortAction.RAISE_EXCEPTION,
                on_abort=AbortAction.RETRY,
                editing_mode=editing_mode,
                ignore_case=True)
            self.cli = CommandLineInterface(application=application,
                                            eventloop=create_eventloop())

        try:
            while True:
                document = self.cli.run(reset_current_buffer=True)

                special.set_expanded_output(False)

                # The reason we check here instead of inside the sqlexecute is
                # because we want to raise the Exit exception which will be
                # caught by the try/except block that wraps the
                # sqlexecute.run() statement.
                if quit_command(document.text):
                    raise EOFError

                try:
                    document = self.handle_editor_command(self.cli, document)
                except RuntimeError as e:
                    logger.error("sql: %r, error: %r", document.text, e)
                    logger.error("traceback: %r", traceback.format_exc())
                    self.output(str(e), err=True, fg='red')
                    continue
                if self.destructive_warning:
                    destroy = confirm_destructive_query(document.text)
                    if destroy is None:
                        pass  # Query was not destructive. Nothing to do here.
                    elif destroy is True:
                        self.output('Your call!')
                    else:
                        self.output('Wise choice!')
                        continue

                # Keep track of whether or not the query is mutating. In case
                # of a multi-statement query, the overall query is considered
                # mutating if any one of the component statements is mutating
                mutating = False

                try:
                    logger.debug('sql: %r', document.text)

                    if self.logfile:
                        self.logfile.write('\n# %s\n' % datetime.now())
                        self.logfile.write(document.text)
                        self.logfile.write('\n')

                    successful = False
                    start = time()
                    res = sqlexecute.run(document.text)
                    successful = True
                    output = []
                    total = 0
                    for title, cur, headers, status in res:
                        logger.debug("headers: %r", headers)
                        logger.debug("rows: %r", cur)
                        logger.debug("status: %r", status)
                        threshold = 1000
                        if (is_select(status) and cur
                                and cur.rowcount > threshold):
                            self.output(
                                'The result set has more than %s rows.' %
                                threshold,
                                fg='red')
                            if not click.confirm('Do you want to continue?'):
                                self.output("Aborted!", err=True, fg='red')
                                break

                        if self.auto_vertical_output:
                            max_width = self.cli.output.get_size().columns
                        else:
                            max_width = None

                        formatted = format_output(title, cur, headers, status,
                                                  self.table_format,
                                                  special.is_expanded_output(),
                                                  max_width)

                        output.extend(formatted)
                        end = time()
                        total += end - start
                        mutating = mutating or is_mutating(status)
                except UnicodeDecodeError as e:
                    import pymysql
                    if pymysql.VERSION < (0, 6, 7):
                        message = (
                            'You are running an older version of pymysql.\n'
                            'Please upgrade to 0.6.7 or above to view binary data.\n'
                            'Try \'pip install -U pymysql\'.')
                        self.output(message)
                    else:
                        raise e
                except KeyboardInterrupt:
                    # Restart connection to the database
                    sqlexecute.connect()
                    logger.debug("cancelled query, sql: %r", document.text)
                    self.output("cancelled query", err=True, fg='red')
                except NotImplementedError:
                    self.output('Not Yet Implemented.', fg="yellow")
                except OperationalError as e:
                    logger.debug("Exception: %r", e)
                    reconnect = True
                    if (e.args[0] in (2003, 2006, 2013)):
                        reconnect = click.prompt(
                            'Connection reset. Reconnect (Y/n)',
                            show_default=False,
                            type=bool,
                            default=True)
                        if reconnect:
                            logger.debug('Attempting to reconnect.')
                            try:
                                sqlexecute.connect()
                                logger.debug('Reconnected successfully.')
                                self.output(
                                    'Reconnected!\nTry the command again.',
                                    fg='green')
                            except OperationalError as e:
                                logger.debug('Reconnect failed. e: %r', e)
                                self.output(str(e), err=True, fg='red')
                                continue  # If reconnection failed, don't proceed further.
                        else:  # If user chooses not to reconnect, don't proceed further.
                            continue
                    else:
                        logger.error("sql: %r, error: %r", document.text, e)
                        logger.error("traceback: %r", traceback.format_exc())
                        self.output(str(e), err=True, fg='red')
                except Exception as e:
                    logger.error("sql: %r, error: %r", document.text, e)
                    logger.error("traceback: %r", traceback.format_exc())
                    self.output(str(e), err=True, fg='red')
                else:
                    try:
                        if special.is_pager_enabled():
                            self.output_via_pager('\n'.join(output))
                        else:
                            self.output('\n'.join(output))
                    except KeyboardInterrupt:
                        pass
                    if special.is_timing_enabled():
                        self.output('Time: %0.03fs' % total)

                    # Refresh the table names and column names if necessary.
                    if need_completion_refresh(document.text):
                        self.refresh_completions(
                            reset=need_completion_reset(document.text))
                finally:
                    if self.logfile is False:
                        self.output("Warning: This query was not logged.",
                                    err=True,
                                    fg='red')
                query = Query(document.text, successful, mutating)
                self.query_history.append(query)

        except EOFError:
            if not self.less_chatty:
                self.output('Goodbye!')
Esempio n. 3
0
    def create_content(self):

        # 文本编辑器
        text_editor = self.create_text_editor()

        # 播放列表属性编辑器
        property_editor = self.create_property_editor()

        body = HSplit([
            HSplit(
                [
                    property_editor,
                    Label(' '),
                    Window(height=1, char="-", style="class:line"),
                    text_editor,
                ],
                height=D(),
            ),
            ConditionalContainer(
                content=VSplit(
                    [
                        Window(FormattedTextControl(self.get_statusbar_text),
                               style="class:status"),
                        Window(
                            FormattedTextControl(
                                self.get_statusbar_right_text),
                            style="class:status.right",
                            width=9,
                            align=WindowAlign.RIGHT,
                        ),
                    ],
                    height=1,
                ),
                filter=Condition(lambda: self.is_show_status_bar()),
            ),
        ])

        self.create_key_bindings()

        root_container = MenuContainer(
            body=body,
            menu_items=[
                MenuItem(
                    "File",
                    children=[
                        MenuItem("Exit", handler=self.exit),
                    ],
                ),
            ],
            floats=[
                Float(
                    xcursor=True,
                    ycursor=True,
                    content=CompletionsMenu(max_height=16, scroll_offset=1),
                ),
            ],
            key_bindings=self.key_bindings,
        )

        style = Style.from_dict({
            "status": "reverse",
            "shadow": "bg:#440044",
        })

        self.layout = Layout(root_container, focused_element=self.text_field)

        self.application = Application(
            layout=self.layout,
            enable_page_navigation_bindings=True,
            style=style,
            mouse_support=True,
            full_screen=True,
        )
Esempio n. 4
0
File: main.py Progetto: nosun/pgcli
    def run_cli(self):
        pgexecute = self.pgexecute
        logger = self.logger
        original_less_opts = self.adjust_less_opts()

        completer = self.completer
        self.refresh_completions()

        def set_vi_mode(value):
            self.vi_mode = value

        key_binding_manager = pgcli_bindings(
            get_vi_mode_enabled=lambda: self.vi_mode,
            set_vi_mode_enabled=set_vi_mode)

        print('Version:', __version__)
        print('Chat: https://gitter.im/dbcli/pgcli')
        print('Mail: https://groups.google.com/forum/#!forum/pgcli')
        print('Home: http://pgcli.com')

        def prompt_tokens(cli):
            return [(Token.Prompt, '%s> ' % pgexecute.dbname)]

        get_toolbar_tokens = create_toolbar_tokens_func(lambda: self.vi_mode)
        layout = create_default_layout(
            lexer=PostgresLexer,
            reserve_space_for_menu=True,
            get_prompt_tokens=prompt_tokens,
            get_bottom_toolbar_tokens=get_toolbar_tokens,
            extra_input_processors=[
                # Highlight matching brackets while editing.
                ConditionalProcessor(
                    processor=HighlightMatchingBracketProcessor(
                        chars='[](){}'),
                    filter=HasFocus(DEFAULT_BUFFER) & ~IsDone()),
            ])
        history_file = self.config['main']['history_file']
        buf = PGBuffer(always_multiline=self.multi_line,
                       completer=completer,
                       history=FileHistory(os.path.expanduser(history_file)),
                       complete_while_typing=Always())

        application = Application(
            style=style_factory(self.syntax_style),
            layout=layout,
            buffer=buf,
            key_bindings_registry=key_binding_manager.registry,
            on_exit=AbortAction.RAISE_EXCEPTION)
        cli = CommandLineInterface(application=application,
                                   eventloop=create_eventloop())

        try:
            while True:
                document = cli.run()

                # The reason we check here instead of inside the pgexecute is
                # because we want to raise the Exit exception which will be
                # caught by the try/except block that wraps the pgexecute.run()
                # statement.
                if quit_command(document.text):
                    raise EOFError

                try:
                    document = self.handle_editor_command(cli, document)
                except RuntimeError as e:
                    logger.error("sql: %r, error: %r", document.text, e)
                    logger.error("traceback: %r", traceback.format_exc())
                    click.secho(str(e), err=True, fg='red')
                    continue

                # Keep track of whether or not the query is mutating. In case
                # of a multi-statement query, the overall query is considered
                # mutating if any one of the component statements is mutating
                mutating = False

                try:
                    logger.debug('sql: %r', document.text)
                    successful = False
                    # Initialized to [] because res might never get initialized
                    # if an exception occurs in pgexecute.run(). Which causes
                    # finally clause to fail.
                    res = []
                    start = time()
                    # Run the query.
                    res = pgexecute.run(document.text, self.pgspecial)
                    duration = time() - start
                    successful = True
                    output = []
                    total = 0
                    for title, cur, headers, status in res:
                        logger.debug("headers: %r", headers)
                        logger.debug("rows: %r", cur)
                        logger.debug("status: %r", status)
                        start = time()
                        threshold = 1000
                        if (is_select(status) and cur
                                and cur.rowcount > threshold):
                            click.secho(
                                'The result set has more than %s rows.' %
                                threshold,
                                fg='red')
                            if not click.confirm('Do you want to continue?'):
                                click.secho("Aborted!", err=True, fg='red')
                                break

                        formatted = format_output(
                            title, cur, headers, status, self.table_format,
                            self.pgspecial.expanded_output)
                        output.extend(formatted)
                        end = time()
                        total += end - start
                        mutating = mutating or is_mutating(status)

                except KeyboardInterrupt:
                    # Restart connection to the database
                    pgexecute.connect()
                    logger.debug("cancelled query, sql: %r", document.text)
                    click.secho("cancelled query", err=True, fg='red')
                except NotImplementedError:
                    click.secho('Not Yet Implemented.', fg="yellow")
                except OperationalError as e:
                    reconnect = True
                    if ('server closed the connection'
                            in utf8tounicode(e.args[0])):
                        reconnect = click.prompt(
                            'Connection reset. Reconnect (Y/n)',
                            show_default=False,
                            type=bool,
                            default=True)
                        if reconnect:
                            try:
                                pgexecute.connect()
                                click.secho(
                                    'Reconnected!\nTry the command again.',
                                    fg='green')
                            except OperationalError as e:
                                click.secho(str(e), err=True, fg='red')
                    else:
                        logger.error("sql: %r, error: %r", document.text, e)
                        logger.error("traceback: %r", traceback.format_exc())
                        click.secho(str(e), err=True, fg='red')
                except Exception as e:
                    logger.error("sql: %r, error: %r", document.text, e)
                    logger.error("traceback: %r", traceback.format_exc())
                    click.secho(str(e), err=True, fg='red')
                else:
                    click.echo_via_pager('\n'.join(output))
                    if self.pgspecial.timing_enabled:
                        print('Command Time: %0.03fs' % duration)
                        print('Format Time: %0.03fs' % total)

                # Refresh the table names and column names if necessary.
                if need_completion_refresh(document.text):
                    self.refresh_completions()

                # Refresh search_path to set default schema.
                if need_search_path_refresh(document.text):
                    logger.debug('Refreshing search path')
                    completer.set_search_path(pgexecute.search_path())
                    logger.debug('Search path: %r', completer.search_path)

                query = Query(document.text, successful, mutating)
                self.query_history.append(query)

        except EOFError:
            print('Goodbye!')
        finally:  # Reset the less opts back to original.
            logger.debug('Restoring env var LESS to %r.', original_less_opts)
            os.environ['LESS'] = original_less_opts
Esempio n. 5
0
# -*- coding: utf-8 -*-

"""Main module."""
from prompt_toolkit import Application
from http_cli_lib.layout.main_layout import layout
from logging.config import dictConfig
from http_cli_lib.logger import logging_config
from http_cli_lib.key_bindins.kbs import global_kb as kb
from prompt_toolkit.enums import EditingMode

dictConfig(logging_config)
app = Application(full_screen=True, layout=layout, key_bindings=kb, editing_mode=EditingMode.VI)
Esempio n. 6
0
    def run_cli(self):
        sqlexecute = self.sqlexecute
        logger = self.logger
        original_less_opts = self.adjust_less_opts()
        self.set_pager_from_config()

        self.initialize_completions()
        completer = self.completer

        def set_key_bindings(value):
            if value not in ('emacs', 'vi'):
                value = 'emacs'
            self.key_bindings = value

        project_root = os.path.dirname(PACKAGE_ROOT)
        author_file = os.path.join(project_root, 'AUTHORS')
        sponsor_file = os.path.join(project_root, 'SPONSORS')

        key_binding_manager = mycli_bindings(
            get_key_bindings=lambda: self.key_bindings,
            set_key_bindings=set_key_bindings)
        print('Version:', __version__)
        print('Chat: https://gitter.im/dbcli/mycli')
        print('Mail: https://groups.google.com/forum/#!forum/mycli-users')
        print('Home: http://mycli.net')
        print('Thanks to the contributor -',
              thanks_picker([author_file, sponsor_file]))

        def prompt_tokens(cli):
            return [(Token.Prompt, self.get_prompt(self.prompt_format))]

        get_toolbar_tokens = create_toolbar_tokens_func(
            lambda: self.key_bindings)
        layout = create_default_layout(
            lexer=MyCliLexer,
            reserve_space_for_menu=True,
            multiline=True,
            get_prompt_tokens=prompt_tokens,
            get_bottom_toolbar_tokens=get_toolbar_tokens,
            display_completions_in_columns=self.wider_completion_menu,
            extra_input_processors=[
                ConditionalProcessor(
                    processor=HighlightMatchingBracketProcessor(
                        chars='[](){}'),
                    filter=HasFocus(DEFAULT_BUFFER) & ~IsDone()),
            ])
        buf = CLIBuffer(always_multiline=self.multi_line,
                        completer=completer,
                        history=FileHistory(
                            os.path.expanduser('~/.mycli-history')),
                        complete_while_typing=Always())

        application = Application(
            style=style_factory(self.syntax_style, self.cli_style),
            layout=layout,
            buffer=buf,
            key_bindings_registry=key_binding_manager.registry,
            on_exit=AbortAction.RAISE_EXCEPTION,
            ignore_case=True)
        cli = CommandLineInterface(application=application,
                                   eventloop=create_eventloop())

        try:
            while True:
                document = cli.run()

                special.set_expanded_output(False)

                # The reason we check here instead of inside the sqlexecute is
                # because we want to raise the Exit exception which will be
                # caught by the try/except block that wraps the
                # sqlexecute.run() statement.
                if quit_command(document.text):
                    raise EOFError

                try:
                    document = self.handle_editor_command(cli, document)
                except RuntimeError as e:
                    logger.error("sql: %r, error: %r", document.text, e)
                    logger.error("traceback: %r", traceback.format_exc())
                    self.output(str(e), err=True, fg='red')
                    continue
                if self.destructive_warning:
                    destroy = confirm_destructive_query(document.text)
                    if destroy is None:
                        pass  # Query was not destructive. Nothing to do here.
                    elif destroy is True:
                        self.output('Your call!')
                    else:
                        self.output('Wise choice!')
                        continue

                # Keep track of whether or not the query is mutating. In case
                # of a multi-statement query, the overall query is considered
                # mutating if any one of the component statements is mutating
                mutating = False

                try:
                    logger.debug('sql: %r', document.text)
                    if self.logfile:
                        self.logfile.write('\n# %s\n' % datetime.now())
                        self.logfile.write(document.text)
                        self.logfile.write('\n')
                    successful = False
                    start = time()
                    res = sqlexecute.run(document.text)
                    duration = time() - start
                    successful = True
                    output = []
                    total = 0
                    for title, cur, headers, status in res:
                        logger.debug("headers: %r", headers)
                        logger.debug("rows: %r", cur)
                        logger.debug("status: %r", status)
                        start = time()
                        threshold = 1000
                        if (is_select(status) and cur
                                and cur.rowcount > threshold):
                            self.output(
                                'The result set has more than %s rows.' %
                                threshold,
                                fg='red')
                            if not click.confirm('Do you want to continue?'):
                                self.output("Aborted!", err=True, fg='red')
                                break
                        output.extend(
                            format_output(title, cur, headers, status,
                                          self.table_format))
                        end = time()
                        total += end - start
                        mutating = mutating or is_mutating(status)
                except KeyboardInterrupt:
                    # Restart connection to the database
                    sqlexecute.connect()
                    logger.debug("cancelled query, sql: %r", document.text)
                    self.output("cancelled query", err=True, fg='red')
                except NotImplementedError:
                    self.output('Not Yet Implemented.', fg="yellow")
                except OperationalError as e:
                    logger.debug("Exception: %r", e)
                    reconnect = True
                    if (e.args[0] in (2003, 2006, 2013)):
                        reconnect = click.prompt(
                            'Connection reset. Reconnect (Y/n)',
                            show_default=False,
                            type=bool,
                            default=True)
                        if reconnect:
                            logger.debug('Attempting to reconnect.')
                            try:
                                sqlexecute.connect()
                                logger.debug('Reconnected successfully.')
                                self.output(
                                    'Reconnected!\nTry the command again.',
                                    fg='green')
                            except OperationalError as e:
                                logger.debug('Reconnect failed. e: %r', e)
                                self.output(str(e), err=True, fg='red')
                                continue  # If reconnection failed, don't proceed further.
                        else:  # If user chooses not to reconnect, don't proceed further.
                            continue
                    else:
                        logger.error("sql: %r, error: %r", document.text, e)
                        logger.error("traceback: %r", traceback.format_exc())
                        self.output(str(e), err=True, fg='red')
                except Exception as e:
                    logger.error("sql: %r, error: %r", document.text, e)
                    logger.error("traceback: %r", traceback.format_exc())
                    self.output(str(e), err=True, fg='red')
                else:
                    try:
                        self.output_via_pager('\n'.join(output))
                    except KeyboardInterrupt:
                        pass
                    if special.is_timing_enabled():
                        self.output('Command Time: %0.03fs' % duration)
                        self.output('Format Time: %0.03fs' % total)

                # Refresh the table names and column names if necessary.
                if need_completion_refresh(document.text):
                    self.refresh_dynamic_completions()

                query = Query(document.text, successful, mutating)
                self.query_history.append(query)

        except EOFError:
            self.output('Goodbye!')
        finally:  # Reset the less opts back to original.
            logger.debug('Restoring env var LESS to %r.', original_less_opts)
            os.environ['LESS'] = original_less_opts
            os.environ['PAGER'] = special.get_original_pager()
Esempio n. 7
0
class UI(Client):
    def __init__(self, username: str, password: str):
        super().__init__(username, password, handle_data=DataFormat.ANSI)

        self.commands = []

        self.output_buffer = Buffer_()
        self.cursor_pos = 0

        self.chat_buffer = Buffer_()

        self.output = BufferControl(self.output_buffer,
                                    input_processors=[FormatText()],
                                    include_default_input_processors=True)

        self.chat = BufferControl(self.chat_buffer,
                                  input_processors=[FormatText()],
                                  include_default_input_processors=True)

        self.hide_ip = "--hide-ip" in sys.argv

        self.suggest = AutoSuggestFromLogs([
            CommandSuggest(),
        ])

        self.input = TextArea(height=1,
                              prompt=" >> ",
                              multiline=False,
                              wrap_lines=False,
                              accept_handler=self.accept,
                              auto_suggest=self.suggest,
                              dont_extend_width=True)

        self.host_ip = FormattedTextControl(ANSI(""))

        self.chat_float = Float(Frame(Window(self.chat, wrap_lines=True)),
                                right=1,
                                top=0,
                                width=40,
                                height=12,
                                hide_when_covering_content=True)

        self.text = ""
        self.chat_text = ""

        def set_frame_size(fn):
            def inner(*args):
                size = self.app.output.get_size()
                self.chat_float.width = size.columns // 3
                self.chat_float.height = size.rows // 2
                return fn(*args)

            return inner

        self.out_window = Window(self.output, wrap_lines=True)

        kb = KeyBindings()

        @kb.add('c-c')
        @kb.add('c-q')
        def _(_):
            self.app.exit()
            self._loop = False
            self.run_again = False

        @kb.add('c-i', filter=has_focus(self.input))
        def __(_):
            fut = self.suggest.get_suggestion_future(self.input.buffer,
                                                     self.input.document)
            text = self.input.text

            def set_input(fut_2):
                res = fut_2.result()
                if res is not None:
                    self.input.text = text + res.text
                    self.input.document = Document(self.input.text,
                                                   cursor_position=len(
                                                       self.input.text))

            fut.add_done_callback(set_input)

        @kb.add(Keys.ScrollUp)
        def sup(_):
            self.output_buffer.cursor_up(1)
            self.out_window._scroll_up()  # pylint: disable=protected-access

        @kb.add(Keys.ScrollDown)
        def sdown(_):
            self.output_buffer.cursor_down(1)
            self.out_window._scroll_down()  # pylint: disable=protected-access

        self.app = Application(
            layout=Layout(
                container=HSplit([
                    Frame(
                        FloatContainer(self.out_window,
                                       floats=[self.chat_float])),
                    Frame(
                        VSplit([
                            self.input,
                            Window(self.host_ip,
                                   align=WindowAlign.RIGHT,
                                   dont_extend_width=True)
                        ]))
                ]),
                focused_element=self.input,
            ),
            full_screen=True,
            mouse_support=True,
            enable_page_navigation_bindings=True,
            key_bindings=merge_key_bindings([kb]),
            paste_mode=True,
        )

        self.app._on_resize = set_frame_size(self.app._on_resize)  # pylint: disable=protected-access

        self.run_again = True
        self.loop = get_event_loop()
        self._loop = False

        self.own_pass = ""
        self.own_ip = ""
        self.current_ip = ""

        # patch_stdout()

    @classmethod
    def create(cls) -> 'UI':
        user = prompt("Username: "******"Password: "******"{Fore.BLUE}[BROADCAST]{Fore.RESET} {converted_color_codes(e.msg)}"
                )
            else:
                self.set_output(converted_color_codes(e.msg))

    def event_error(self, e: ErrorEvent):
        self.err(e.error)

    @staticmethod
    def sanitize(arg: str) -> ANSI:
        return to_formatted_text(ANSI(arg))

    def err(self, message: str):
        self.set_output(
            f"{Fore.RED}{converted_color_codes(message)}{Fore.RESET}")

    def set_chat_output(self, text: str):
        new_text = (self.chat_text + "\n" + text).strip()
        self.chat_buffer.set_text(new_text)
        self.chat_text = new_text.replace("\t", " ")

    def set_output(self, text: str):
        new_text = (self.text + "\n" + text).strip()
        self.output_buffer.set_text(new_text)
        self.text = new_text.replace("\t", " ")
        self.cursor_pos = len(self.text)

    def clear_chat(self):
        self.chat_buffer.set_text("")
        self.chat_text = ""

    def clear(self):
        self.output_buffer.set_text("")
        self.text = ""

    def accept(self, _):
        if self.input.text.strip():
            self.set_output(f"\n>> {self.input.text}")
            self.commands.append(self.input.text)
            self.suggest.last_command(self.input.text)
            self.input.text = ""

    async def get_host_data(self):
        data = await self.command("pass see -l")
        self.own_pass = data.msg
        sys_info = await self.command('specs -l')
        self.current_ip = self.own_ip = re.search(
            r"(?P<ip>\d{1,3}(\.\d{1,3}){3,4})", sys_info.msg).group("ip")

    def launch(self):
        colorama.init()
        use_asyncio_event_loop()
        patch_stdout()

        while self.run_again:
            self.run_again = False
            self._loop = True

            self.loop.create_task(self.start())
            try:
                self.loop.run_until_complete(
                    self.app.run_async().to_asyncio_future())
            except KeyboardInterrupt:
                if self.current_ip != self.own_ip:
                    self.loop.run_until_complete(self.command("dc"))

    async def event_ready(self):
        o_text = self.output_buffer.text

        await self.get_host_data()

        text = ((
            f"{Fore.YELLOW}{self.own_ip} - {self.own_pass}{Fore.RESET} "
            if self.own_ip == self.current_ip else
            f"{Fore.YELLOW}{self.current_ip} / {self.own_ip} - {self.own_pass}{Fore.RESET} "
        ) if not self.hide_ip else "")
        self.host_ip.text = self.sanitize(text)

        self.clear()

        self.set_output(o_text)

        macros = MacroHolder()

        while self._loop:
            # TODO: Update these when they change
            macros.macros["self"] = self.own_ip
            macros.macros["pass"] = self.own_pass

            while not self.commands:
                await asyncio.sleep(0.1)

            try:
                line = self.commands.pop(0).strip()
                line = macros.parse(line)
                command, *args = line.split()

                if command == "chats":
                    if len(args) == 1 and args[0] == "clear":
                        self.clear_chat()
                    else:
                        await self.command(" ".join([command, *args]))

                elif command == "macro":
                    if args and args[0] in ("add", "remove", "list"):
                        sub = args.pop(0)
                        if sub == "add":
                            if len(args) >= 2:
                                macros += args[0], " ".join(args[1:])
                            else:
                                self.set_output(
                                    "Usage: macro add <name> <value>")
                        elif sub == "remove":
                            if args:
                                macros -= args[0]
                            else:
                                self.set_output("Usage: macro remove <name>")
                        else:
                            self.set_output("Macros:")
                            for key in sorted(list(macros)):
                                self.set_output(f"${key} -> {macros[key]}")

                    else:
                        self.set_output("Usage: macro [add/remove/list] ...")

                elif command == "clear":
                    self.clear()

                elif command == "quit":
                    self._loop = False
                    self.run_again = False
                    self.stop()
                    self.app.exit()

                else:
                    await self.command(" ".join([command, *args]))

            except Exception as e:  # pylint: disable=broad-except
                self.err(repr(e))
Esempio n. 8
0
    def _build_cli(self, history):
        key_binding_manager = cli_bindings()

        def prompt_tokens(cli):
            prompt = self.get_prompt(self.prompt)
            if len(prompt) > self.MAX_LEN_PROMPT:
                prompt = self.get_prompt('\\r:\\d> ')
            return [(Token.Prompt, prompt)]

        def get_continuation_tokens(cli, width):
            prompt = self.get_prompt(self.prompt_continuation_format)
            token = (Token.Continuation, ' ' * (width - len(prompt)) + prompt)
            return [token]

        def show_suggestion_tip():
            return self.iterations < 2

        get_toolbar_tokens = create_toolbar_tokens_func(
            self.completion_refresher.is_refreshing, show_suggestion_tip)

        layout = create_prompt_layout(
            lexer=Lexer,
            multiline=True,
            get_prompt_tokens=prompt_tokens,
            get_continuation_tokens=get_continuation_tokens,
            get_bottom_toolbar_tokens=get_toolbar_tokens,
            display_completions_in_columns=False,
            extra_input_processors=[
                ConditionalProcessor(
                    processor=HighlightMatchingBracketProcessor(
                        chars='[](){}'),
                    filter=HasFocus(DEFAULT_BUFFER) & ~IsDone())
            ],
            reserve_space_for_menu=self.get_reserved_space())

        with self._completer_lock:
            buf = CLIBuffer(always_multiline=self.multi_line,
                            completer=self.completer,
                            history=history,
                            auto_suggest=AutoSuggestFromHistory(),
                            complete_while_typing=Always(),
                            accept_action=AcceptAction.RETURN_DOCUMENT)

            if self.key_bindings == 'vi':
                editing_mode = EditingMode.VI
            else:
                editing_mode = EditingMode.EMACS

            application = Application(
                style=style_from_pygments(style_cls=self.output_style),
                layout=layout,
                buffer=buf,
                key_bindings_registry=key_binding_manager.registry,
                on_exit=AbortAction.RAISE_EXCEPTION,
                on_abort=AbortAction.RETRY,
                editing_mode=editing_mode,
                ignore_case=True)

            cli = CommandLineInterface(application=application,
                                       eventloop=create_eventloop())

            return cli
Esempio n. 9
0
def run():
    app = Application(full_screen=True, mouse_support=True, key_bindings=kb, layout=main_layout)
    app.run()
Esempio n. 10
0
    buf.text = new_text


@kb.add('c-e')
def edit_http(event):
    if layout.has_focus(request_buffer):
        request_buffer.open_in_editor()

    if layout.has_focus(response_buffer):
        response_buffer.open_in_editor()

    response_buffer.tempfile_suffix = get_extention(ApplicationState.response_history[-1])

@kb.add('c-@')
def send_request(event):
    response = send_raw_request(request_buffer.text)
    response_buffer.text = format_raw_response(response)
    ApplicationState.response_history.append(response)


@kb.add('c-c')
def exit_for_real(event):
    event.app.exit()

class ApplicationState:
    response_history = []

app = Application(layout=layout, full_screen=True, key_bindings=kb,
                  editing_mode=EditingMode.VI, mouse_support=True)
app.run()
Esempio n. 11
0
         Window(height=4, content=BufferControl(buffer=buffer1))],
        key_bindings=kb_search,
    ),
    floats=[
        Float(
            xcursor=True,
            ycursor=True,
            content=CompletionsMenu(max_height=3, scroll_offset=1),
        )
    ],
)

# Put it all together
root_container = HSplit([searchbar, output, infoFrame])
layout = Layout(root_container)
app = Application(layout=layout, full_screen=True, key_bindings=kb)

# These are app-wide stateful variables; not sure how to structure things.
app.ads_q = None  # either None or ads.SearchQuery
app.ads_page = 0  # current page
app.ads_item_idx = 0  # current item index within page
app.NITEMS = 5  # number of items per page
app.pages = None  # chunked list of articles (list of lists)

tidy_author = lambda x: [] if x is None else x
tidy_title = lambda x: ["NO TITLE AVAILABLE"] if x is None else x


def format_article_info(article):
    """Format current article's info for infoFrame"""
    return HTML(f"<b>{html.escape(tidy_title(article.title)[0])}</b>\n"
Esempio n. 12
0
    def __init__(self, filepath):
        self.db = Database(filepath)
        cursor = FormattedTextControl(focusable=True,
                                      text=[("", CURSOR)],
                                      show_cursor=False)
        sep = Window(width=len(SEPARATOR), char=SEPARATOR, style="class:line")

        self.ids = Column("ID")
        self.names = Column("Name")
        self.sources = Column("Source")
        self.tags = Column("Tags")
        self.urls = Column("URL")

        table_header = VSplit([
            Label(text="", width=len(CURSOR)),
            sep,
            Label(self.ids.title, width=self.ids.width, style="bold"),
            sep,
            Label(self.names.title, width=self.names.width, style="bold"),
            sep,
            Label(self.tags.title, width=self.tags.width, style="bold"),
            sep,
            Label(self.sources.title, width=self.sources.width, style="bold"),
            sep,
            Label(self.urls.title, width=self.urls.width, style="bold"),
        ])
        self.selection = Window(cursor,
                                width=len(CURSOR),
                                style="class:selector")
        table_body = VSplit([
            self.selection,
            sep,
            Window(self.ids.col, width=self.ids.width),
            sep,
            Window(self.names.col, width=self.names.width),
            sep,
            Window(self.tags.col, width=self.tags.width),
            sep,
            Window(self.sources.col, width=self.sources.width),
            sep,
            Window(self.urls.col, width=self.urls.width),
        ])

        self.prompt = TextArea(
            multiline=False,
            focusable=True,
            accept_handler=self._do_search,
            get_line_prefix=self._get_prompt,
        )

        table = HSplit([table_header, table_body])
        layout = HSplit([table, self.prompt])

        kb = KeyBindings()

        @kb.add("c-c", eager=True)
        @kb.add("q", filter=has_focus(self.selection))
        def close(event):
            event.app.exit()

        @kb.add("s", filter=has_focus(self.selection))
        def start_search(event):
            event.app.layout.focus(self.prompt)

        @kb.add("o", filter=has_focus(self.selection))
        @kb.add(Keys.Enter, filter=has_focus(self.selection))
        def open_link(event):
            cursor = self.selection.content.text
            idx = len(cursor)

            selected = self.ids.col.text[idx - 1]
            link_id = int(selected[1])

            Link.open(self.db, link_id)

        @kb.add(Keys.Down, filter=has_focus(self.selection))
        def next_item(event):
            cursor = self.selection.content.text
            idx = len(cursor)
            max_idx = len(self.ids.col.text)

            if idx + 1 > max_idx:
                return

            cursor.insert(0, ("", "\n"))

        @kb.add(Keys.Up, filter=has_focus(self.selection))
        def prev_item(event):
            cursor = self.selection.content.text
            idx = len(cursor)

            if idx == 1:
                return

            cursor.pop(0)

        self.app = Application(layout=Layout(layout), key_bindings=kb)
Esempio n. 13
0
class LinkTable:
    def __init__(self, filepath):
        self.db = Database(filepath)
        cursor = FormattedTextControl(focusable=True,
                                      text=[("", CURSOR)],
                                      show_cursor=False)
        sep = Window(width=len(SEPARATOR), char=SEPARATOR, style="class:line")

        self.ids = Column("ID")
        self.names = Column("Name")
        self.sources = Column("Source")
        self.tags = Column("Tags")
        self.urls = Column("URL")

        table_header = VSplit([
            Label(text="", width=len(CURSOR)),
            sep,
            Label(self.ids.title, width=self.ids.width, style="bold"),
            sep,
            Label(self.names.title, width=self.names.width, style="bold"),
            sep,
            Label(self.tags.title, width=self.tags.width, style="bold"),
            sep,
            Label(self.sources.title, width=self.sources.width, style="bold"),
            sep,
            Label(self.urls.title, width=self.urls.width, style="bold"),
        ])
        self.selection = Window(cursor,
                                width=len(CURSOR),
                                style="class:selector")
        table_body = VSplit([
            self.selection,
            sep,
            Window(self.ids.col, width=self.ids.width),
            sep,
            Window(self.names.col, width=self.names.width),
            sep,
            Window(self.tags.col, width=self.tags.width),
            sep,
            Window(self.sources.col, width=self.sources.width),
            sep,
            Window(self.urls.col, width=self.urls.width),
        ])

        self.prompt = TextArea(
            multiline=False,
            focusable=True,
            accept_handler=self._do_search,
            get_line_prefix=self._get_prompt,
        )

        table = HSplit([table_header, table_body])
        layout = HSplit([table, self.prompt])

        kb = KeyBindings()

        @kb.add("c-c", eager=True)
        @kb.add("q", filter=has_focus(self.selection))
        def close(event):
            event.app.exit()

        @kb.add("s", filter=has_focus(self.selection))
        def start_search(event):
            event.app.layout.focus(self.prompt)

        @kb.add("o", filter=has_focus(self.selection))
        @kb.add(Keys.Enter, filter=has_focus(self.selection))
        def open_link(event):
            cursor = self.selection.content.text
            idx = len(cursor)

            selected = self.ids.col.text[idx - 1]
            link_id = int(selected[1])

            Link.open(self.db, link_id)

        @kb.add(Keys.Down, filter=has_focus(self.selection))
        def next_item(event):
            cursor = self.selection.content.text
            idx = len(cursor)
            max_idx = len(self.ids.col.text)

            if idx + 1 > max_idx:
                return

            cursor.insert(0, ("", "\n"))

        @kb.add(Keys.Up, filter=has_focus(self.selection))
        def prev_item(event):
            cursor = self.selection.content.text
            idx = len(cursor)

            if idx == 1:
                return

            cursor.pop(0)

        self.app = Application(layout=Layout(layout), key_bindings=kb)

    def run(self):
        self._do_search()
        self.app.run()

    def _do_search(self, inpt: Buffer = None):
        self.selection.content.text = [("", CURSOR)]
        self.ids.clear()
        self.names.clear()
        self.tags.clear()
        self.sources.clear()
        self.urls.clear()

        name = None
        tags = None

        if inpt is not None and len(inpt.text) > 0:
            terms = inpt.text.split(" ")

            tags = [t.replace("#", "") for t in terms if t.startswith("#")]
            name = " ".join(n for n in terms if not n.startswith("#"))

        links = Link.search(self.db,
                            name=name,
                            top=10,
                            tags=tags,
                            sort="visits")

        for idx, link in enumerate(links):

            newline = "\n" if idx < len(links) - 1 else ""

            self.ids.col.text.append(("", f"{link.id}{newline}"))
            self.names.col.text.append(("", f"{link.name}{newline}"))
            self.urls.col.text.append(("", f"{link.url_expanded}{newline}"))

            tags = ", ".join(f"#{t.name}" for t in link.tags)
            self.tags.col.text.append(("", f"{tags}{newline}"))

            source = "" if not link.source else link.source.name
            self.sources.col.text.append(("", f"{source}{newline}"))

        self.app.layout.focus(self.selection)

    def _get_prompt(self, line_no, other):

        if has_focus(self.prompt)():
            return "Search: "

        return "[s]earch | [o]pen | [q]uit"
Esempio n. 14
0
    def create_app(self):
        app = Application(key_bindings=self.kb,layout=self.layout, full_screen=True)
        app.before_render = self

        return app
Esempio n. 15
0
async def init():
    global root
    global chat_container
    global app
    global log_buf
    global log_win
    global input_buf
    global input_win
    global convos
    global status_bar
    global status_label
    global client
    global convo_stack
    global websocket
    global app
    global uri
    global websocket
    global ws_handler

    uri = "ws://localhost:15555"
    ws_handler = handler(uri)
    await ws_handler.connect()

    # message area
    log_buf = Buffer(document=Document())
    log_win = Window(BufferControl(log_buf), wrap_lines=True)

    # input area
    input_buf = Buffer(document=Document())
    input_win = Window(BufferControl(input_buf), height=1, wrap_lines=True)

    # status bar
    status_bar = FormattedTextToolbar(
        text=HTML("<b>Chatting with: Loading </b>"),
        style="bg:ansired fg:ansiblack")
    status_label = Label(text="[ 00:29 ] ", width=10)

    # call backs
    input_buf.accept_handler = accept_message
    input_buf.on_text_changed += resize_input
    log_buf.on_text_changed += auto_scroll
    convos = convo_list_widget()

    chat_container = HSplit(
        [log_win, status_bar,
         VSplit([status_label, input_win])])

    root = VSplit([
        convos,
        chat_container,
    ])

    style = Style.from_dict(
        {"select-box cursor-line": "nounderline bg:ansired fg:ansiwhite"})

    app = Application(editing_mode=EditingMode.VI,
                      key_bindings=kb,
                      layout=Layout(chat_container),
                      full_screen=True,
                      style=style)
    app.invalidate()
    app.layout.focus(input_buf)
    ViState._input_mode = InputMode.INSERT
    ViState.input_mode = property(get_input_mode, set_input_mode)

    asyncio.ensure_future(ws_handler.listen())
    asyncio.ensure_future(ws_handler.command(('get_convo', 'all')))

    auto_scroll(log_buf)
    await app.run_async()
Esempio n. 16
0
    height=4)

root_container = HSplit([
    content_container,
    #           HorizontalLine(),
    #Window(height=1, char='-'),
    commandWindowFrame
])

layout = Layout(root_container)

#call 'before render'-function only when app is started the first time
#=> sets content to pod window
started = False


def before_render(application):
    global started
    if started == False:
        updateState()
        started = True
        if args.no_help == False:
            executeCommand("help")


app = Application(layout=layout,
                  key_bindings=kb,
                  full_screen=True,
                  mouse_support=enableMouseSupport,
                  before_render=before_render)
app.run()
Esempio n. 17
0
def loop(cmd, history_file):
    def session_toolbar(cli):
        return _get_toolbar_tokens(cmd.is_conn_available, cmd.username,
                                   cmd.connection.client.active_servers)

    key_binding_manager = KeyBindingManager(
        enable_search=True,
        enable_abort_and_exit_bindings=True,
        enable_system_bindings=True,
        enable_open_in_editor=True)
    bind_keys(key_binding_manager.registry)
    layout = create_layout(
        message='cr> ',
        multiline=True,
        lexer=SqlLexer,
        extra_input_processors=[
            ConditionalProcessor(
                processor=HighlightMatchingBracketProcessor(chars='[](){}'),
                filter=HasFocus(DEFAULT_BUFFER) & ~IsDone())
        ],
        get_bottom_toolbar_tokens=session_toolbar)
    application = Application(
        layout=layout,
        buffer=create_buffer(cmd, history_file),
        style=PygmentsStyle.from_defaults(pygments_style_cls=CrateStyle),
        key_bindings_registry=key_binding_manager.registry,
        editing_mode=_get_editing_mode(),
        on_exit=AbortAction.RAISE_EXCEPTION,
        on_abort=AbortAction.RETRY,
    )
    eventloop = create_eventloop()
    output = create_output()
    cli = CommandLineInterface(application=application,
                               eventloop=eventloop,
                               output=output)

    def get_num_columns_override():
        return output.get_size().columns

    cmd.get_num_columns = get_num_columns_override

    while True:
        try:
            doc = cli.run(reset_current_buffer=True)
            if doc:
                cmd.process(doc.text)
        except ProgrammingError as e:
            if '401' in e.message:
                username = cmd.username
                password = cmd.password
                cmd.username = input('Username: '******'Bye!')
            return
Esempio n. 18
0
class PybreakGui(Bdb):
    def __init__(self):
        super().__init__()
        self.paused = True
        self.app_thread = threading.Thread(target=self.start_gui, args=(asyncio.get_event_loop(),))
        self.frame_history = FrameHistory()

        self.search_toolbar = SearchToolbar(
            text_if_not_searching=[("class:not-searching", "Press '/' to start searching.")]
        )

        def get_view_file():
            if len(self.frame_history.history) > 0:
                return self.frame_history.hist_frame.filename
            return ".py"

        self.text_area = TextArea(
            lexer=DynamicLexer(
                lambda: PygmentsLexer.from_filename(
                    get_view_file(), sync_from_start=False
                )
            ),
            search_field=self.search_toolbar,
            scrollbar=True,
            line_numbers=True,
        )
        self.container = HSplit(
            [
                self.text_area,
                self.search_toolbar,
            ]
        )

        kb = KeyBindings()

        @kb.add("c-q")
        def _(event: KeyPressEvent):
            self.paused = False
            self._quit()

        @kb.add("c-n")
        def _(event):
            self.set_next(self.frame_history.exec_frame.raw_frame)
            self.paused = False  # allow another frame to be processed

        self.app = Application(
            full_screen=True,
            layout=Layout(container=self.container),
            key_bindings=kb,
        )
        self.app.loop = asyncio.get_event_loop()

    def start_gui(self, loop):
        asyncio.set_event_loop(loop)
        self.app.run()
        self.text_area.buffer.insert_text("HELLO WORLD")

    def start(self, frame):
        self.app_thread.start()
        super().set_trace(frame)

    def _quit(self):
        sys.settrace(None)
        self.quitting = True
        self.app.exit()

    def user_call(self, frame: types.FrameType, argument_list):
        # if self.stop_here(frame):
        # self.frame_history.append(frame)
        pass

    def user_line(self, frame: types.FrameType):
        """
        This method is called from dispatch_line() when either
        stop_here() or break_here() yields True.
        i.e. when we stop OR break at this line.
         * stop_here() yields true if the frame lies below the frame where
         debugging started on the call stack. i.e. it will be called for
         every line after we start debugging.
         * break_here() yields true only if there's a breakpoint for this
         line
        """
        self.text_area.buffer.insert_text(f"FRAME = {frame.f_code.co_filename}:{frame.f_lineno}")
        if self.stop_here(frame):
            self.text_area.buffer.insert_text(str(frame.f_code.co_filename) + str(frame.f_lineno) + "\n")
            self.frame_history.append(frame)

            while self.paused:
                time.sleep(.1)
            self.paused = True
Esempio n. 19
0
        Float(
            xcursor=True,
            ycursor=True,
            content=CompletionsMenu(max_height=16, scroll_offset=1),
        ),
    ],
    key_bindings=kb,
)


layout = Layout(root_container)

app: Application = Application(
    layout=layout,
    full_screen=True,
    mouse_support=True,
    style=style,
    enable_page_navigation_bindings=True,
    color_depth=ColorDepth.DEPTH_8_BIT,
)


def start(argv=None):
    global current_file
    global isort_config

    argv = sys.argv if argv is None else argv
    if len(sys.argv) > 2:
        sys.exit("Usage: qpython [filename]")
    elif len(sys.argv) == 2:
        current_file = Path(sys.argv[1]).resolve()
        isort_config = isort.Config(settings_path=current_file.parent)
Esempio n. 20
0
    def run(self):
        labels = self.neo4j.get_labels()
        relationship_types = self.neo4j.get_relationship_types()
        properties = self.neo4j.get_property_keys()

        if self.filename:
            queries = self.filename.read()
            queries = queries.split(";")[:-1]

            for query in queries:
                query += ";"
                query = query.strip()

                print("> " + query)
                self.handle_query(query)
                print()

            return

        click.secho(" ______     __  __     ______     __         __    ",
                    fg="red")
        click.secho("/\  ___\   /\ \_\ \   /\  ___\   /\ \       /\ \   ",
                    fg="yellow")
        click.secho("\ \ \____  \ \____ \  \ \ \____  \ \ \____  \ \ \  ",
                    fg="green")
        click.secho(" \ \_____\  \/\_____\  \ \_____\  \ \_____\  \ \_\ ",
                    fg="blue")
        click.secho("  \/_____/   \/_____/   \/_____/   \/_____/   \/_/ ",
                    fg="magenta")

        msg = "\nUsing Bolt." if self.neo4j.graph.address.bolt else ""

        print(msg)
        print("Version: {}".format(__version__))
        print("Bug reports: https://github.com/nicolewhite/cycli/issues\n")

        completer = CypherCompleter(labels, relationship_types, properties)

        layout = create_prompt_layout(
            lexer=CypherLexer,
            get_prompt_tokens=get_tokens,
            reserve_space_for_menu=8,
        )

        buff = CypherBuffer(
            accept_action=AcceptAction.RETURN_DOCUMENT,
            history=FileHistory(
                filename=os.path.expanduser('~/.cycli_history')),
            completer=completer,
            complete_while_typing=True,
        )

        application = Application(style=PygmentsStyle(CypherStyle),
                                  buffer=buff,
                                  layout=layout,
                                  on_exit=AbortAction.RAISE_EXCEPTION,
                                  key_bindings_registry=CypherBinder.registry)

        cli = CommandLineInterface(application=application,
                                   eventloop=create_eventloop())

        try:
            while True:
                document = cli.run()
                query = document.text
                self.handle_query(query)
        except UserWantsOut:
            print("Goodbye!")
        except Exception as e:
            print(e)
Esempio n. 21
0
    def __init__(self, username: str, password: str):
        super().__init__(username, password, handle_data=DataFormat.ANSI)

        self.commands = []

        self.output_buffer = Buffer_()
        self.cursor_pos = 0

        self.chat_buffer = Buffer_()

        self.output = BufferControl(self.output_buffer,
                                    input_processors=[FormatText()],
                                    include_default_input_processors=True)

        self.chat = BufferControl(self.chat_buffer,
                                  input_processors=[FormatText()],
                                  include_default_input_processors=True)

        self.hide_ip = "--hide-ip" in sys.argv

        self.suggest = AutoSuggestFromLogs([
            CommandSuggest(),
        ])

        self.input = TextArea(height=1,
                              prompt=" >> ",
                              multiline=False,
                              wrap_lines=False,
                              accept_handler=self.accept,
                              auto_suggest=self.suggest,
                              dont_extend_width=True)

        self.host_ip = FormattedTextControl(ANSI(""))

        self.chat_float = Float(Frame(Window(self.chat, wrap_lines=True)),
                                right=1,
                                top=0,
                                width=40,
                                height=12,
                                hide_when_covering_content=True)

        self.text = ""
        self.chat_text = ""

        def set_frame_size(fn):
            def inner(*args):
                size = self.app.output.get_size()
                self.chat_float.width = size.columns // 3
                self.chat_float.height = size.rows // 2
                return fn(*args)

            return inner

        self.out_window = Window(self.output, wrap_lines=True)

        kb = KeyBindings()

        @kb.add('c-c')
        @kb.add('c-q')
        def _(_):
            self.app.exit()
            self._loop = False
            self.run_again = False

        @kb.add('c-i', filter=has_focus(self.input))
        def __(_):
            fut = self.suggest.get_suggestion_future(self.input.buffer,
                                                     self.input.document)
            text = self.input.text

            def set_input(fut_2):
                res = fut_2.result()
                if res is not None:
                    self.input.text = text + res.text
                    self.input.document = Document(self.input.text,
                                                   cursor_position=len(
                                                       self.input.text))

            fut.add_done_callback(set_input)

        @kb.add(Keys.ScrollUp)
        def sup(_):
            self.output_buffer.cursor_up(1)
            self.out_window._scroll_up()  # pylint: disable=protected-access

        @kb.add(Keys.ScrollDown)
        def sdown(_):
            self.output_buffer.cursor_down(1)
            self.out_window._scroll_down()  # pylint: disable=protected-access

        self.app = Application(
            layout=Layout(
                container=HSplit([
                    Frame(
                        FloatContainer(self.out_window,
                                       floats=[self.chat_float])),
                    Frame(
                        VSplit([
                            self.input,
                            Window(self.host_ip,
                                   align=WindowAlign.RIGHT,
                                   dont_extend_width=True)
                        ]))
                ]),
                focused_element=self.input,
            ),
            full_screen=True,
            mouse_support=True,
            enable_page_navigation_bindings=True,
            key_bindings=merge_key_bindings([kb]),
            paste_mode=True,
        )

        self.app._on_resize = set_frame_size(self.app._on_resize)  # pylint: disable=protected-access

        self.run_again = True
        self.loop = get_event_loop()
        self._loop = False

        self.own_pass = ""
        self.own_ip = ""
        self.current_ip = ""
Esempio n. 22
0
class Client:
    def __init__(self, url: str = "127.0.0.1", port: int = "9999"):
        self.communicator = Communicator(url, port)

        self.CMDIN = CommandInput(title="DLNest Command Line(F1)",
                                  onAccept=self.onCommandAccept)
        self.w1 = self.CMDIN.getWindow()

        self.DLOutput = ResultsOutput(routineTask=self.routineTaskDLOutput,
                                      title="DLNest Output (F2)",
                                      style="class:dlnest_output")
        self.w2 = self.DLOutput.getWindow()

        self.ANOutput = AnalyzeOutput(routineTask=self.routineTaskANOutput,
                                      title="Analyzer Output (F3)",
                                      style="class:analyzer_output")
        self.w3 = self.ANOutput.getWindow()
        self.analyzeTaskID = ""

        self.TaskInfo = TaskInfoShower(routineTask=self.routineTaskInfo,
                                       title="Tasks (F4)")
        self.w4 = self.TaskInfo.getWindow()

        self.DevicesInfo = DevicesInfoShower(
            routineTask=self.routineTaskDevices, title="Devices (F5)")
        self.w5 = self.DevicesInfo.getWindow()

        self.container_fat = HSplit(
            [self.w1,
             VSplit([self.w2, self.w3]),
             VSplit([self.w4, self.w5])])
        self.container_tall = HSplit(
            [self.w1, self.w2, self.w3, self.w4, self.w5])

        self.kb = KeyBindings()

        @self.kb.add('c-c')
        def exit_(event):
            event.app.exit()

        @self.kb.add('f1')
        def focus1(event):
            event.app.layout.focus(self.w1)

        @self.kb.add('f2')
        def focus2(event):
            event.app.layout.focus(self.w2)

        @self.kb.add('f3')
        def focus3(event):
            event.app.layout.focus(self.w3)

        @self.kb.add('f4')
        def focus4(event):
            event.app.layout.focus(self.w4)

        @self.kb.add('f5')
        def focus5(event):
            event.app.layout.focus(self.w5)

        self.style = Style.from_dict({
            "frame.border": "fg:#ffb6c1",
            "frame.title": "fg:#1ef0ff",
            "command_frame": "bg:#008b8b",
            "dlnest_output": "bg:#451a4a",
            "analyzer_output": "bg:#451a4a",
            "analyzer_info_label": "bg:#da70d6",
            "analyzer_info_text1": "bg:#3f3f00",
            "analyzer_info_text2": "bg:#ff00ff",
            "running_task_status": "bg:#a01010 bold",
            "running_task_id": "bg:#303030",
            "running_task_gpu": "bg:#556b2f",
            "running_task_des": "bg:#c71585",
            "running_task_time": "bg:#2e3b37",
            "pending_task_status": "bg:#1010a0 bold",
            "pending_task_id": "bg:#303030",
            "pending_task_gpu": "bg:#556b2f",
            "pending_task_des": "bg:#c71585",
            "pending_task_time": "bg:#2e3b37",
            "suspend_task_status": "bg:#10a010 bold",
            "suspend_task_id": "bg:#303030",
            "suspend_task_gpu": "bg:#556b2f",
            "suspend_task_des": "bg:#c71585",
            "suspend_task_time": "bg:#2e3b37",
            "task_info_shower": "bg:#008bc0",
            "devices_info_shower": "bg:#008bc0",
            "devices_id": "bg:#303030",
            "devices_status_valid": "bg:#3cb371 bold",
            "devices_status_break": "bg:#a01010 bold",
            "devices_free_memory": "bg:#556b2f",
            "devices_tasks": "bg:#c71585"
        })

        self.layout = Layout(self.container_fat, focused_element=self.w1)
        self.app = Application(key_bindings=self.kb,
                               layout=self.layout,
                               full_screen=True,
                               style=self.style)
        self.app._on_resize = self.on_resize

    def on_resize(self):
        cols, rows = os.get_terminal_size(0)
        focused_element = self.layout.current_window
        if cols >= 2 * rows:  # fat
            self.app.layout = Layout(self.container_fat,
                                     focused_element=focused_element)
        else:  # tall
            self.app.layout = Layout(self.container_tall,
                                     focused_element=focused_element)

        self.app.renderer.erase(leave_alternate_screen=False)
        self.app._request_absolute_cursor_position()
        self.app._redraw()

    def getApp(self):
        return self.app

    def onCommandAccept(self, s: str):
        commandWordList = s.split(" ")
        while "" in commandWordList:
            commandWordList.remove("")

        if commandWordList[0] == "watch":
            self.analyzeTaskID = commandWordList[1]
        elif commandWordList[0] == "withdraw":
            self.analyzeTaskID = ""

        if commandWordList[0] == "runExp":
            if len(commandWordList) != 3:
                if self.analyzeTaskID != "":
                    commandWordList = [
                        commandWordList[0], self.analyzeTaskID,
                        commandWordList[1]
                    ]
                else:
                    return

        ret = self.communicator.giveACommand(commandWordList)

        if commandWordList[0] == "del":
            if ret["status"] == "success" and commandWordList[
                    1] == self.analyzeTaskID:
                self.analyzeTaskID = ""

        if "exit" in ret:
            self.app.exit()

    def routineTaskDLOutput(self, obj):
        #for buffer fresh
        if not hasattr(obj, "_count_"):
            obj._count_ = 0

        outStyledDict = self.communicator.giveACommand(["showDL", "-s"])
        outPlainDict = self.communicator.giveACommand(["showDL"])
        if "text" in outStyledDict and "text" in outPlainDict:
            try:
                obj.lexer.styled_text = outStyledDict["text"]
                obj.shower.text = outPlainDict["text"]
            except Exception as e:
                pass

    def routineTaskANOutput(self, obj):
        #for buffer fresh
        if not hasattr(obj, "_count_"):
            obj._count_ = 0

        if self.analyzeTaskID == "":
            obj.lexer.styled_text = []
            obj.shower.text = ""
            obj.infoText.text = [("", "No valid analyzer task is running")]
            obj.infoWindow.width = 33
            return

        outStyledDict = self.communicator.giveACommand(
            ["showAN", "-t", self.analyzeTaskID, "-s"])
        outPlainDict = self.communicator.giveACommand(
            ["showAN", "-t", self.analyzeTaskID])
        if "text" in outStyledDict and "text" in outPlainDict:
            try:
                obj.lexer.styled_text = outStyledDict["text"]
                obj.shower.text = outPlainDict["text"]
                obj.infoText.text = [("class:analyzer_info_text1",
                                      self.analyzeTaskID)]
                obj.infoWindow.width = len(self.analyzeTaskID)
            except Exception as e:
                pass
        else:
            self.analyzeTaskID = ""

    def routineTaskInfo(self, obj):
        # for buffer fresh
        if not hasattr(obj, "_count_"):
            obj._count_ = 0

        r = self.communicator.giveACommand(["showTask"])
        if r["status"] != "success":
            obj.lexer.taskInfo = []
            obj.shower.text = obj.lexer.get_text()
            return
        taskInfo = r["info"]
        try:
            obj.lexer.taskInfo = taskInfo
            obj.shower.text = obj.lexer.get_text()
        except Exception as e:
            pass

    def routineTaskDevices(self, obj):
        # for buffer fresh
        if not hasattr(obj, "_count_"):
            obj._count_ = 0

        r = self.communicator.giveACommand(["showDevice"])
        if r["status"] != "success":
            obj.lexer.devicesInfo = []
            obj.shower.text = obj.lexer.get_text()
            return
        obj.lexer.devicesInfo = r["info"]
        try:
            obj.shower.text = obj.lexer.get_text()
        except Exception as e:
            pass
Esempio n. 23
0
    def _build_cli(self, history):

        def set_vi_mode(value):
            self.vi_mode = value

        key_binding_manager = pgcli_bindings(
            get_vi_mode_enabled=lambda: self.vi_mode,
            set_vi_mode_enabled=set_vi_mode)

        def prompt_tokens(_):
            if self.dsn_alias and self.prompt_dsn_format is not None:
                prompt_format = self.prompt_dsn_format
            else:
                prompt_format = self.prompt_format

            prompt = self.get_prompt(prompt_format)

            if (prompt_format == self.default_prompt and
                    len(prompt) > self.max_len_prompt):
                prompt = self.get_prompt('\\d> ')

            return [(Token.Prompt, prompt)]

        def get_continuation_tokens(cli, width):
            continuation=self.multiline_continuation_char * (width - 1) + ' '
            return [(Token.Continuation, continuation)]

        get_toolbar_tokens = create_toolbar_tokens_func(
            lambda: self.vi_mode, self.completion_refresher.is_refreshing,
            self.pgexecute.failed_transaction,
            self.pgexecute.valid_transaction)

        layout = create_prompt_layout(
            lexer=PygmentsLexer(PostgresLexer),
            reserve_space_for_menu=self.min_num_menu_lines,
            get_prompt_tokens=prompt_tokens,
            get_continuation_tokens=get_continuation_tokens,
            get_bottom_toolbar_tokens=get_toolbar_tokens,
            display_completions_in_columns=self.wider_completion_menu,
            multiline=True,
            extra_input_processors=[
               # Highlight matching brackets while editing.
               ConditionalProcessor(
                   processor=HighlightMatchingBracketProcessor(chars='[](){}'),
                   filter=HasFocus(DEFAULT_BUFFER) & ~IsDone()),
            ])

        with self._completer_lock:
            buf = PGBuffer(
                auto_suggest=AutoSuggestFromHistory(),
                always_multiline=self.multi_line,
                multiline_mode=self.multiline_mode,
                completer=self.completer,
                history=history,
                complete_while_typing=Always(),
                accept_action=AcceptAction.RETURN_DOCUMENT)

            editing_mode = EditingMode.VI if self.vi_mode else EditingMode.EMACS

            application = Application(
                style=style_factory(self.syntax_style, self.cli_style),
                layout=layout,
                buffer=buf,
                key_bindings_registry=key_binding_manager.registry,
                on_exit=AbortAction.RAISE_EXCEPTION,
                on_abort=AbortAction.RETRY,
                ignore_case=True,
                editing_mode=editing_mode)

            cli = CommandLineInterface(application=application,
                                       eventloop=self.eventloop)

            return cli
Esempio n. 24
0
    def __init__(self, url: str = "127.0.0.1", port: int = "9999"):
        self.communicator = Communicator(url, port)

        self.CMDIN = CommandInput(title="DLNest Command Line(F1)",
                                  onAccept=self.onCommandAccept)
        self.w1 = self.CMDIN.getWindow()

        self.DLOutput = ResultsOutput(routineTask=self.routineTaskDLOutput,
                                      title="DLNest Output (F2)",
                                      style="class:dlnest_output")
        self.w2 = self.DLOutput.getWindow()

        self.ANOutput = AnalyzeOutput(routineTask=self.routineTaskANOutput,
                                      title="Analyzer Output (F3)",
                                      style="class:analyzer_output")
        self.w3 = self.ANOutput.getWindow()
        self.analyzeTaskID = ""

        self.TaskInfo = TaskInfoShower(routineTask=self.routineTaskInfo,
                                       title="Tasks (F4)")
        self.w4 = self.TaskInfo.getWindow()

        self.DevicesInfo = DevicesInfoShower(
            routineTask=self.routineTaskDevices, title="Devices (F5)")
        self.w5 = self.DevicesInfo.getWindow()

        self.container_fat = HSplit(
            [self.w1,
             VSplit([self.w2, self.w3]),
             VSplit([self.w4, self.w5])])
        self.container_tall = HSplit(
            [self.w1, self.w2, self.w3, self.w4, self.w5])

        self.kb = KeyBindings()

        @self.kb.add('c-c')
        def exit_(event):
            event.app.exit()

        @self.kb.add('f1')
        def focus1(event):
            event.app.layout.focus(self.w1)

        @self.kb.add('f2')
        def focus2(event):
            event.app.layout.focus(self.w2)

        @self.kb.add('f3')
        def focus3(event):
            event.app.layout.focus(self.w3)

        @self.kb.add('f4')
        def focus4(event):
            event.app.layout.focus(self.w4)

        @self.kb.add('f5')
        def focus5(event):
            event.app.layout.focus(self.w5)

        self.style = Style.from_dict({
            "frame.border": "fg:#ffb6c1",
            "frame.title": "fg:#1ef0ff",
            "command_frame": "bg:#008b8b",
            "dlnest_output": "bg:#451a4a",
            "analyzer_output": "bg:#451a4a",
            "analyzer_info_label": "bg:#da70d6",
            "analyzer_info_text1": "bg:#3f3f00",
            "analyzer_info_text2": "bg:#ff00ff",
            "running_task_status": "bg:#a01010 bold",
            "running_task_id": "bg:#303030",
            "running_task_gpu": "bg:#556b2f",
            "running_task_des": "bg:#c71585",
            "running_task_time": "bg:#2e3b37",
            "pending_task_status": "bg:#1010a0 bold",
            "pending_task_id": "bg:#303030",
            "pending_task_gpu": "bg:#556b2f",
            "pending_task_des": "bg:#c71585",
            "pending_task_time": "bg:#2e3b37",
            "suspend_task_status": "bg:#10a010 bold",
            "suspend_task_id": "bg:#303030",
            "suspend_task_gpu": "bg:#556b2f",
            "suspend_task_des": "bg:#c71585",
            "suspend_task_time": "bg:#2e3b37",
            "task_info_shower": "bg:#008bc0",
            "devices_info_shower": "bg:#008bc0",
            "devices_id": "bg:#303030",
            "devices_status_valid": "bg:#3cb371 bold",
            "devices_status_break": "bg:#a01010 bold",
            "devices_free_memory": "bg:#556b2f",
            "devices_tasks": "bg:#c71585"
        })

        self.layout = Layout(self.container_fat, focused_element=self.w1)
        self.app = Application(key_bindings=self.kb,
                               layout=self.layout,
                               full_screen=True,
                               style=self.style)
        self.app._on_resize = self.on_resize
Esempio n. 25
0
    def run_cli(self):
        iterations = 0
        sqlexecute = self.sqlexecute
        logger = self.logger
        self.configure_pager()

        if self.smart_completion:
            self.refresh_completions()

        author_file = os.path.join(PACKAGE_ROOT, 'AUTHORS')
        sponsor_file = os.path.join(PACKAGE_ROOT, 'SPONSORS')

        key_binding_manager = mycli_bindings()

        if not self.less_chatty:
            print('Version:', __version__)
            print('Chat: https://gitter.im/dbcli/mycli')
            print('Mail: https://groups.google.com/forum/#!forum/mycli-users')
            print('Home: http://mycli.net')
            print('Thanks to the contributor -', thanks_picker([author_file, sponsor_file]))

        def prompt_tokens(cli):
            prompt = self.get_prompt(self.prompt_format)
            if self.prompt_format == self.default_prompt and len(prompt) > self.max_len_prompt:
                prompt = self.get_prompt('\\d> ')
            return [(Token.Prompt, prompt)]

        def get_continuation_tokens(cli, width):
            continuation_prompt = self.get_prompt(self.prompt_continuation_format)
            return [(Token.Continuation, ' ' * (width - len(continuation_prompt)) + continuation_prompt)]

        def show_suggestion_tip():
            return iterations < 2

        def one_iteration(document=None):
            if document is None:
                document = self.cli.run()

                special.set_expanded_output(False)

                try:
                    document = self.handle_editor_command(self.cli, document)
                except RuntimeError as e:
                    logger.error("sql: %r, error: %r", document.text, e)
                    logger.error("traceback: %r", traceback.format_exc())
                    self.echo(str(e), err=True, fg='red')
                    return

            if not document.text.strip():
                return

            if self.destructive_warning:
                destroy = confirm_destructive_query(document.text)
                if destroy is None:
                    pass  # Query was not destructive. Nothing to do here.
                elif destroy is True:
                    self.echo('Your call!')
                else:
                    self.echo('Wise choice!')
                    return

            # Keep track of whether or not the query is mutating. In case
            # of a multi-statement query, the overall query is considered
            # mutating if any one of the component statements is mutating
            mutating = False

            try:
                logger.debug('sql: %r', document.text)

                special.write_tee(self.get_prompt(self.prompt_format) + document.text)
                if self.logfile:
                    self.logfile.write('\n# %s\n' % datetime.now())
                    self.logfile.write(document.text)
                    self.logfile.write('\n')

                successful = False
                start = time()
                res = sqlexecute.run(document.text)
                successful = True
                result_count = 0
                for title, cur, headers, status in res:
                    logger.debug("headers: %r", headers)
                    logger.debug("rows: %r", cur)
                    logger.debug("status: %r", status)
                    threshold = 1000
                    if (is_select(status) and
                            cur and cur.rowcount > threshold):
                        self.echo('The result set has more than {} rows.'.format(
                            threshold), fg='red')
                        if not click.confirm('Do you want to continue?'):
                            self.echo("Aborted!", err=True, fg='red')
                            break

                    if self.auto_vertical_output:
                        max_width = self.cli.output.get_size().columns
                    else:
                        max_width = None

                    formatted = self.format_output(
                        title, cur, headers, special.is_expanded_output(),
                        max_width)

                    t = time() - start
                    try:
                        if result_count > 0:
                            self.echo('')
                        try:
                            self.output(formatted, status)
                        except KeyboardInterrupt:
                            pass
                        if special.is_timing_enabled():
                            self.echo('Time: %0.03fs' % t)
                    except KeyboardInterrupt:
                        pass

                    start = time()
                    result_count += 1
                    mutating = mutating or is_mutating(status)
                special.unset_once_if_written()
            except EOFError as e:
                raise e
            except KeyboardInterrupt:
                # get last connection id
                connection_id_to_kill = sqlexecute.connection_id
                logger.debug("connection id to kill: %r", connection_id_to_kill)
                # Restart connection to the database
                sqlexecute.connect()
                try:
                    for title, cur, headers, status in sqlexecute.run('kill %s' % connection_id_to_kill):
                        status_str = str(status).lower()
                        if status_str.find('ok') > -1:
                            logger.debug("cancelled query, connection id: %r, sql: %r",
                                         connection_id_to_kill, document.text)
                            self.echo("cancelled query", err=True, fg='red')
                except Exception as e:
                    self.echo('Encountered error while cancelling query: {}'.format(e),
                              err=True, fg='red')
            except NotImplementedError:
                self.echo('Not Yet Implemented.', fg="yellow")
            except OperationalError as e:
                logger.debug("Exception: %r", e)
                if (e.args[0] in (2003, 2006, 2013)):
                    logger.debug('Attempting to reconnect.')
                    self.echo('Reconnecting...', fg='yellow')
                    try:
                        sqlexecute.connect()
                        logger.debug('Reconnected successfully.')
                        one_iteration(document)
                        return  # OK to just return, cuz the recursion call runs to the end.
                    except OperationalError as e:
                        logger.debug('Reconnect failed. e: %r', e)
                        self.echo(str(e), err=True, fg='red')
                        # If reconnection failed, don't proceed further.
                        return
                else:
                    logger.error("sql: %r, error: %r", document.text, e)
                    logger.error("traceback: %r", traceback.format_exc())
                    self.echo(str(e), err=True, fg='red')
            except Exception as e:
                logger.error("sql: %r, error: %r", document.text, e)
                logger.error("traceback: %r", traceback.format_exc())
                self.echo(str(e), err=True, fg='red')
            else:
                if is_dropping_database(document.text, self.sqlexecute.dbname):
                    self.sqlexecute.dbname = None
                    self.sqlexecute.connect()

                # Refresh the table names and column names if necessary.
                if need_completion_refresh(document.text):
                    self.refresh_completions(
                            reset=need_completion_reset(document.text))
            finally:
                if self.logfile is False:
                    self.echo("Warning: This query was not logged.",
                              err=True, fg='red')
            query = Query(document.text, successful, mutating)
            self.query_history.append(query)

        get_toolbar_tokens = create_toolbar_tokens_func(
            self.completion_refresher.is_refreshing,
            show_suggestion_tip)

        layout = create_prompt_layout(
            lexer=MyCliLexer,
            multiline=True,
            get_prompt_tokens=prompt_tokens,
            get_continuation_tokens=get_continuation_tokens,
            get_bottom_toolbar_tokens=get_toolbar_tokens,
            display_completions_in_columns=self.wider_completion_menu,
            extra_input_processors=[ConditionalProcessor(
                processor=HighlightMatchingBracketProcessor(chars='[](){}'),
                filter=HasFocus(DEFAULT_BUFFER) & ~IsDone()
            )],
            reserve_space_for_menu=self.get_reserved_space()
        )
        with self._completer_lock:
            buf = CLIBuffer(always_multiline=self.multi_line, completer=self.completer,
                            history=FileHistory(os.path.expanduser(
                                os.environ.get('MYCLI_HISTFILE', '~/.mycli-history'))),
                            auto_suggest=AutoSuggestFromHistory(),
                            complete_while_typing=Always(), accept_action=AcceptAction.RETURN_DOCUMENT)

            if self.key_bindings == 'vi':
                editing_mode = EditingMode.VI
            else:
                editing_mode = EditingMode.EMACS

            application = Application(
                style=style_from_pygments(style_cls=self.output_style),
                layout=layout, buffer=buf,
                key_bindings_registry=key_binding_manager.registry,
                on_exit=AbortAction.RAISE_EXCEPTION,
                on_abort=AbortAction.RETRY, editing_mode=editing_mode,
                ignore_case=True)
            self.cli = CommandLineInterface(application=application,
                                       eventloop=create_eventloop())

        try:
            while True:
                one_iteration()
                iterations += 1
        except EOFError:
            special.close_tee()
            if not self.less_chatty:
                self.echo('Goodbye!')
Esempio n. 26
0
#!/usr/bin/env python
"""
An empty full screen application without layout.
"""
from prompt_toolkit import Application

Application(full_screen=True).run()
Esempio n. 27
0
class PlayListEditor:
    def __init__(self, playlist: LocalFilePlaylist, editable=False) -> None:
        self.playlist: LocalFilePlaylist = playlist
        self.editable = False
        self.text_field = None
        self.application = None
        self.key_bindings = None
        self.layout = None
        self.play_info_dialog = None
        self.skip_info_dialog = None
        self.show_status_bar = True
        self.focus_index = 0

    def run(self):
        if self.application is not None:
            self.application.run()

    def is_show_status_bar(self):
        return self.show_status_bar

    def create_key_bindings(self):
        kb = KeyBindings()

        @kb.add('c-q')
        def _(event):
            self.exit()

        kb.add("tab")(focus_next)

        self.key_bindings = kb

    def create_content(self):

        # 文本编辑器
        text_editor = self.create_text_editor()

        # 播放列表属性编辑器
        property_editor = self.create_property_editor()

        body = HSplit([
            HSplit(
                [
                    property_editor,
                    Label(' '),
                    Window(height=1, char="-", style="class:line"),
                    text_editor,
                ],
                height=D(),
            ),
            ConditionalContainer(
                content=VSplit(
                    [
                        Window(FormattedTextControl(self.get_statusbar_text),
                               style="class:status"),
                        Window(
                            FormattedTextControl(
                                self.get_statusbar_right_text),
                            style="class:status.right",
                            width=9,
                            align=WindowAlign.RIGHT,
                        ),
                    ],
                    height=1,
                ),
                filter=Condition(lambda: self.is_show_status_bar()),
            ),
        ])

        self.create_key_bindings()

        root_container = MenuContainer(
            body=body,
            menu_items=[
                MenuItem(
                    "File",
                    children=[
                        MenuItem("Exit", handler=self.exit),
                    ],
                ),
            ],
            floats=[
                Float(
                    xcursor=True,
                    ycursor=True,
                    content=CompletionsMenu(max_height=16, scroll_offset=1),
                ),
            ],
            key_bindings=self.key_bindings,
        )

        style = Style.from_dict({
            "status": "reverse",
            "shadow": "bg:#440044",
        })

        self.layout = Layout(root_container, focused_element=self.text_field)

        self.application = Application(
            layout=self.layout,
            enable_page_navigation_bindings=True,
            style=style,
            mouse_support=True,
            full_screen=True,
        )

    def create_property_editor(self):

        current_file_path = self.playlist.media_list[
            self.playlist.current_index]
        _, current_file_name = os.path.split(current_file_path)

        self.play_info_dialog = Dialog(
            modal=False,
            title="播放记录",
            body=HSplit([
                Label(''),
                Label(' 播放文件'),
                Button(f'{current_file_name}', ),
                Label(' 播放位置'),
                Button(f'{self.playlist.current_pos}', ),
                Label(''),
            ],
                        width=38,
                        padding=1))
        self.skip_info_dialog = Dialog(
            modal=False,
            title="设置",
            body=HSplit([
                Label(''),
                Label(' 跳过片头'),
                Button(f'{self.playlist.skip_head}', ),
                Label(' 跳过片尾'),
                Button(f'{self.playlist.skip_tail}', ),
                Label(''),
            ],
                        width=38,
                        padding=1))

        left_window = VSplit([
            self.play_info_dialog,
            self.skip_info_dialog,
        ],
                             width=40,
                             padding=2)
        return left_window

    def create_text_editor(self):
        search_toolbar = SearchToolbar()
        text = ''
        for file_path in self.playlist.media_list:
            text += file_path + '\n'
        self.text_field = TextArea(
            text=text,
            read_only=True,
            #lexer=DynamicLexer(
            #    lambda: PygmentsLexer.from_filename(
            #        ApplicationState.current_path or ".txt", sync_from_start=False
            #    )
            #),
            scrollbar=True,
            line_numbers=True,
            search_field=search_toolbar,
        )
        text_editor = HSplit([self.text_field, search_toolbar])
        return text_editor

    def exit(self):
        if self.application.is_running:
            self.application.exit()

    def get_statusbar_text(self):
        return " Press ctrl-q to exit. "

    def get_statusbar_right_text(self):
        return " {}:{}  ".format(
            self.text_field.document.cursor_position_row + 1,
            self.text_field.document.cursor_position_col + 1,
        )
Esempio n. 28
0
show_window = Window(content=BufferControl(buffer=show_buffer,
                                           focus_on_click=True), )
control_window = Window(content=BufferControl(buffer=control_buffer,
                                              focus_on_click=True),
                        height=5)

root_container = HSplit([

    # Display the text 'Hello world' on the right.
    show_window,

    # A vertical line in the middle. We explicitly specify the width, to
    # make sure that the layout engine will not try to divide the whole
    # width by three for all these windows. The window will simply fill its
    # content by repeating this character.
    Window(char='─', height=1),

    # One window that holds the BufferControl with the default buffer on
    # the left.
    control_window,
])

t = ''
for i in range(99):
    t += '<style bg="blue" fg="white">' + str(i) + '</style>' + '\n'

show_buffer.reset(Document(HTML(t, )))
layout = Layout(root_container)
app = Application(layout=layout, full_screen=True, key_bindings=kb)
app.run()
Esempio n. 29
0
class FullNodeUI:
    """
    Full node UI instance. Displays node state, blocks, and connections. Calls parent_close_cb
    when the full node is closed. Uses the RPC client to fetch data from a full node and to display relevant
    information. The UI is updated periodically.
    """
    def __init__(self, parent_close_cb: Callable, rpc_client: RpcClient):
        self.rpc_client = rpc_client
        self.app: Optional[Application] = None
        self.data_initialized = False
        self.block = None
        self.closed: bool = False
        self.num_blocks: int = 10
        self.num_top_block_pools: int = 10
        self.top_winners: List[Tuple[uint64, bytes32]] = []
        self.our_winners: List[Tuple[uint64, bytes32]] = []
        self.prev_route: str = "home/"
        self.route: str = "home/"
        self.focused: bool = False
        self.parent_close_cb = parent_close_cb
        self.kb = self.setup_keybindings()
        self.style = Style([("error", "#ff0044")])
        self.pool_pks: List[PublicKey] = []
        key_config_filename = os.path.join(ROOT_DIR, "config", "keys.yaml")
        if os.path.isfile(key_config_filename):
            config = safe_load(open(key_config_filename, "r"))

            self.pool_pks = [
                PrivateKey.from_bytes(bytes.fromhex(ce)).get_public_key()
                for ce in config["pool_sks"]
            ]

        self.draw_initial()
        self.app = Application(
            style=self.style,
            layout=self.layout,
            full_screen=True,
            key_bindings=self.kb,
            mouse_support=True,
        )

        self.closed = False
        self.update_ui_task = asyncio.get_running_loop().create_task(
            self.update_ui())
        self.update_data_task = asyncio.get_running_loop().create_task(
            self.update_data())

    def close(self):
        # Closes this instance of the UI
        if not self.closed:
            self.closed = True
            self.route = "home/"
            if self.app:
                self.app.exit(0)

    def stop(self):
        # Closes this instance of the UI, and call parent close, which closes
        # all other instances, and shuts down the full node.
        self.close()
        self.parent_close_cb(True)

    def setup_keybindings(self) -> KeyBindings:
        kb = KeyBindings()
        kb.add("tab")(focus_next)
        kb.add("s-tab")(focus_previous)
        kb.add("down")(focus_next)
        kb.add("up")(focus_previous)
        kb.add("right")(focus_next)
        kb.add("left")(focus_previous)

        @kb.add("c-c")
        def exit_(event):
            self.close()

        return kb

    def draw_initial(self):
        search_field = SearchToolbar()
        self.empty_row = TextArea(focusable=False, height=1)

        # home/
        self.loading_msg = Label(text=f"Initializing UI....")
        self.syncing = TextArea(focusable=False, height=1)
        self.current_heads_label = TextArea(focusable=False, height=1)
        self.lca_label = TextArea(focusable=False, height=1)
        self.difficulty_label = TextArea(focusable=False, height=1)
        self.ips_label = TextArea(focusable=False, height=1)
        self.total_iters_label = TextArea(focusable=False, height=2)
        self.con_rows = []
        self.displayed_cons = set()
        self.latest_blocks: List[HeaderBlock] = []
        self.connections_msg = Label(text=f"Connections")
        self.connection_rows_vsplit = Window()
        self.add_connection_msg = Label(text=f"Add a connection ip:port")
        self.add_connection_field = TextArea(
            height=1,
            prompt=">>> ",
            style="class:input-field",
            multiline=False,
            wrap_lines=False,
            search_field=search_field,
        )
        self.add_connection_field.accept_handler = self.async_to_sync(
            self.add_connection)
        self.latest_blocks_msg = Label(text=f"Latest blocks")
        self.latest_blocks_labels = [
            Button(text="block") for _ in range(self.num_blocks)
        ]

        self.search_block_msg = Label(text=f"Search block by hash")
        self.search_block_field = TextArea(
            height=1,
            prompt=">>> ",
            style="class:input-field",
            multiline=False,
            wrap_lines=False,
            search_field=search_field,
        )
        self.search_block_field.accept_handler = self.async_to_sync(
            self.search_block)

        self.top_block_pools_msg = Label(text=f"Top block pools")
        self.top_block_pools_labels = [
            Label(text="Top block pool")
            for _ in range(self.num_top_block_pools)
        ]
        self.our_pools_msg = Label(text=f"Our pool winnings")
        self.our_pools_labels = [
            Label(text="Our winnings") for _ in range(len(self.pool_pks))
        ]

        self.close_ui_button = Button("Close UI", handler=self.close)
        self.quit_button = Button("Stop node and close UI", handler=self.stop)
        self.error_msg = Label(style="class:error", text=f"")

        # block/
        self.block_msg = Label(text=f"Block")
        self.block_label = TextArea(focusable=True,
                                    scrollbar=True,
                                    focus_on_click=True)
        self.back_button = Button(text="Back",
                                  handler=self.change_route_handler("home/"))
        self.challenge_msg = Label(text=f"Block Header")
        self.challenge = TextArea(focusable=False)

        body = HSplit([self.loading_msg], height=D(), width=D())
        self.content = Frame(title="Chia Full Node", body=body)
        self.layout = Layout(VSplit([self.content], height=D(), width=D()))

    def change_route_handler(self, route):
        def change_route():
            self.prev_route = self.route
            self.route = route
            self.focused = False
            self.error_msg.text = ""

        return change_route

    def async_to_sync(self, coroutine):
        def inner(buff=None):
            if buff is None:
                asyncio.get_running_loop().create_task(coroutine())
            else:
                asyncio.get_running_loop().create_task(coroutine(buff.text))

        return inner

    async def search_block(self, text: str):
        try:
            block = await self.rpc_client.get_block(bytes.fromhex(text))
        except ValueError:
            self.error_msg.text = "Enter a valid hex block hash"
            return
        if block is not None:
            self.change_route_handler(f"block/{text}")()
        else:
            self.error_msg.text = "Block not found"

    async def add_connection(self, text: str):
        if ":" not in text:
            self.error_msg.text = (
                "Enter a valid IP and port in the following format: 10.5.4.3:8000"
            )
            return
        else:
            ip, port = ":".join(text.split(":")[:-1]), text.split(":")[-1]
        log.info(f"Want to connect to {ip}, {port}")
        try:
            await self.rpc_client.open_connection(ip, int(port))
        except BaseException:
            # TODO: catch right exception
            self.error_msg.text = f"Failed to connect to {ip}:{port}"

    async def get_latest_blocks(
            self, heads: List[SmallHeaderBlock]) -> List[SmallHeaderBlock]:
        added_blocks: List[SmallHeaderBlock] = []
        while len(added_blocks) < self.num_blocks and len(heads) > 0:
            heads = sorted(heads, key=lambda b: b.height, reverse=True)
            max_block = heads[0]
            if max_block not in added_blocks:
                added_blocks.append(max_block)
            heads.remove(max_block)
            prev: Optional[
                SmallHeaderBlock] = await self.rpc_client.get_header(
                    max_block.prev_header_hash)
            if prev is not None:
                heads.append(prev)
        return added_blocks

    async def draw_home(self):
        connections: List[Dict] = [c for c in self.connections]
        if set([con["node_id"] for con in connections]) != self.displayed_cons:
            new_con_rows = []
            for con in connections:
                con_str = (
                    f"{NodeType(con['type']).name} {con['peer_host']} {con['peer_port']}/{con['peer_server_port']}"
                    f" {con['node_id'].hex()[:10]}...")
                con_label = Label(text=con_str)

                def disconnect(c):
                    async def inner():
                        await self.rpc_client.close_connection(c["node_id"])
                        self.layout.focus(self.quit_button)

                    return inner

                disconnect_button = Button("Disconnect",
                                           handler=self.async_to_sync(
                                               disconnect(con)))
                row = VSplit([con_label, disconnect_button])
                new_con_rows.append(row)
            self.displayed_cons = set([con["node_id"] for con in connections])
            self.con_rows = new_con_rows
            if len(self.con_rows) > 0:
                self.layout.focus(self.con_rows[0])
            else:
                self.layout.focus(self.quit_button)

        if len(self.con_rows):
            new_con_rows = HSplit(self.con_rows)
        else:
            new_con_rows = Window(width=D(), height=0)

        if self.sync_mode:
            if self.max_height >= 0:
                self.syncing.text = f"Syncing up to {self.max_height}"
            else:
                self.syncing.text = f"Syncing"
        else:
            self.syncing.text = "Not syncing"

        total_iters = self.lca_block.challenge.total_iters

        new_block_labels = []
        for i, b in enumerate(self.latest_blocks):
            self.latest_blocks_labels[i].text = (
                f"{b.height}:{b.header_hash}"
                f" {'LCA' if b.header_hash == self.lca_block.header_hash else ''}"
                f" {'TIP' if b.header_hash in [h.header_hash for h in self.tips] else ''}"
            )
            self.latest_blocks_labels[i].handler = self.change_route_handler(
                f"block/{b.header_hash}")
            new_block_labels.append(self.latest_blocks_labels[i])

        top_block_pools_labels = self.top_block_pools_labels
        if len(self.top_winners) > 0:
            new_top_block_pools_labels = []
            for i, (winnings, pk) in enumerate(self.top_winners):
                self.top_block_pools_labels[
                    i].text = f"Public key {pk.hex()}: {winnings/1000000000000} chias."
                new_top_block_pools_labels.append(
                    self.top_block_pools_labels[i])
            top_block_pools_labels = new_top_block_pools_labels

        our_pools_labels = self.our_pools_labels
        if len(self.our_winners) > 0:
            new_our_pools_labels = []
            for i, (winnings, pk) in enumerate(self.our_winners):
                self.our_pools_labels[
                    i].text = f"Public key {pk.hex()}: {winnings/(1000000000000)} chias."
                new_our_pools_labels.append(self.our_pools_labels[i])
            our_pools_labels = new_our_pools_labels

        self.lca_label.text = (
            f"Current least common ancestor {self.lca_block.header_hash}"
            f" height {self.lca_block.height}")
        self.current_heads_label.text = "Heights of tips: " + str(
            [h.height for h in self.tips])
        self.difficulty_label.text = f"Current difficulty: {self.difficulty}"

        self.ips_label.text = f"Current VDF iterations per second: {self.ips}"
        self.total_iters_label.text = f"Total iterations since genesis: {total_iters}"

        try:
            if not self.focused:
                self.layout.focus(self.close_ui_button)
                self.focused = True
        except ValueError:  # Not yet in layout
            pass
        return HSplit(
            [
                self.syncing,
                self.lca_label,
                self.current_heads_label,
                self.difficulty_label,
                self.ips_label,
                self.total_iters_label,
                Window(height=1, char="-", style="class:line"),
                self.connections_msg,
                new_con_rows,
                Window(height=1, char="-", style="class:line"),
                self.add_connection_msg,
                self.add_connection_field,
                Window(height=1, char="-", style="class:line"),
                self.latest_blocks_msg,
                *new_block_labels,
                Window(height=1, char="-", style="class:line"),
                self.search_block_msg,
                self.search_block_field,
                Window(height=1, char="-", style="class:line"),
                self.top_block_pools_msg,
                *top_block_pools_labels,
                Window(height=1, char="-", style="class:line"),
                self.our_pools_msg,
                *our_pools_labels,
                Window(height=1, char="-", style="class:line"),
                self.close_ui_button,
                self.quit_button,
                self.error_msg,
            ],
            width=D(),
            height=D(),
        )

    async def draw_block(self):
        block_hash: str = self.route.split("block/")[1]
        if self.block is None or self.block.header_hash != bytes32(
                bytes.fromhex(block_hash)):
            self.block: Optional[FullBlock] = await self.rpc_client.get_block(
                bytes32(bytes.fromhex(block_hash)))
        if self.block is not None:
            self.block_msg.text = f"Block {str(self.block.header_hash)}"
            if self.block_label.text != str(self.block):
                self.block_label.text = str(self.block)
        else:
            self.block_label.text = f"Block hash {block_hash} not found"
        try:
            if not self.focused:
                self.layout.focus(self.back_button)
                self.focused = True
        except ValueError:  # Not yet in layout
            pass
        return HSplit([self.block_msg, self.block_label, self.back_button],
                      width=D(),
                      height=D())

    async def update_ui(self):
        try:
            while not self.closed:
                if self.data_initialized:
                    if self.route.startswith("home/"):
                        self.content.body = await self.draw_home()
                    elif self.route.startswith("block/"):
                        self.content.body = await self.draw_block()

                    if self.app and not self.app.invalidated:
                        self.app.invalidate()
                await asyncio.sleep(0.5)
        except Exception as e:
            log.error(f"Exception in UI update_ui {type(e)}: {e}")
            raise e

    async def update_data(self):
        self.data_initialized = False
        counter = 0
        try:
            while not self.closed:
                try:
                    blockchain_state = await self.rpc_client.get_blockchain_state(
                    )
                    self.lca_block = blockchain_state["lca"]
                    self.tips = blockchain_state["tips"]
                    self.difficulty = blockchain_state["difficulty"]
                    self.ips = blockchain_state["ips"]
                    self.sync_mode = blockchain_state["sync_mode"]
                    self.connections = await self.rpc_client.get_connections()
                    if self.sync_mode:
                        max_block = await self.rpc_client.get_heaviest_block_seen(
                        )
                        self.max_height = max_block.height

                    self.latest_blocks = await self.get_latest_blocks(self.tips
                                                                      )

                    self.data_initialized = True
                    if counter % 20 == 0:
                        # Only request balances periodically, since it's an expensive operation
                        coin_balances: Dict[
                            bytes,
                            uint64] = await self.rpc_client.get_pool_balances(
                            )
                        self.top_winners = sorted(
                            [(rewards, key)
                             for key, rewards in coin_balances.items()],
                            reverse=True,
                        )[:self.num_top_block_pools]

                        self.our_winners = [
                            (coin_balances[bytes(pk)],
                             bytes(pk)) if bytes(pk) in coin_balances else
                            (0, bytes(pk)) for pk in self.pool_pks
                        ]

                    counter += 1
                    await asyncio.sleep(5)
                except (
                        aiohttp.client_exceptions.ClientConnectorError,
                        aiohttp.client_exceptions.ServerConnectionError,
                ) as e:
                    log.warning(
                        f"Could not connect to full node. Is it running? {e}")
                    await asyncio.sleep(5)
        except Exception as e:
            log.error(f"Exception in UI update_data {type(e)}: {e}")
            raise e

    async def await_closed(self):
        await self.update_ui_task
        await self.update_data_task
Esempio n. 30
0
    def __init__(
        self,
        entries,
        preview_callback=None,
        input_callback=None,
        input_completions=None,
    ):
        if not entries or len(entries) == 0:
            raise RuntimeError("Entries cannot be empty.")
        self.entries = entries
        self.input_callback = input_callback
        self.preview_callback = preview_callback

        self.ansi_escape_8bit = re.compile(
            r"(?:\x1B[@-Z\\-_]|[\x80-\x9A\x9C-\x9F]|(?:\x1B\[|\x9B)[0-?]*[ -/]*[@-~])"
        )
        self.current_lineno = 1
        self.max_entry_width = min(
            max(
                map(
                    lambda x: len(x) + 1, self.ansi_escape_8bit.sub("", entries).split("\n")
                )
            ),
            48,
        )

        entries_text = ANSI(self.entries)
        entries_formatted_text = to_formatted_text(entries_text)
        entries_plain_text = fragment_list_to_text(entries_formatted_text)
        self.entries_control = FormattedBufferControl(
            buffer=Buffer(
                document=Document(entries_plain_text, 0),
                name="entries",
                on_cursor_position_changed=self.update_entries,
                read_only=True,
            ),
            focusable=True,
            formatted_text=entries_formatted_text,
            include_default_input_processors=False,
            input_processors=[FormatTextProcessor(), HighlightSelectionProcessor()],
        )

        if self.preview_callback:
            self.preview_text = ANSI(self.preview_callback(self.current_lineno))
            formatted_text = to_formatted_text(self.preview_text)
            plain_text = fragment_list_to_text(formatted_text)
            self.preview_control = FormattedBufferControl(
                buffer=Buffer(
                    document=Document(plain_text, 0),
                    name="preview",
                    on_cursor_position_changed=self.update_preview,
                    read_only=True,
                ),
                focusable=True,
                formatted_text=formatted_text,
                include_default_input_processors=False,
                input_processors=[FormatTextProcessor(), HighlightSelectionProcessor()],
            )
            # Alternative (not scrollable):
            # self.preview_control = FormattedTextControl(
            #     focusable=True,
            #     show_cursor=True,
            #     text=ANSI(self.preview_callback(self.current_lineno)),
            # )
            entries_container = VSplit(
                [
                    Window(
                        content=self.entries_control,
                        width=self.max_entry_width,
                        wrap_lines=True,
                    ),
                    Window(width=3, char=" | "),
                    Window(content=self.preview_control, wrap_lines=True),
                ]
            )
        else:
            entries_container = Window(
                content=self.entries_control,
                width=self.max_entry_width,
                wrap_lines=True,
            )
        if self.input_callback:
            self.search_field = SearchToolbar()
            self.input_field = TextArea(
                accept_handler=self.input_accept,
                completer=FuzzyWordCompleter(list(input_completions)),
                complete_while_typing=True,
                height=1,
                multiline=False,
                prompt="> ",
                search_field=self.search_field,
                wrap_lines=False,
            )
            self.root_container = FloatContainer(
                content=HSplit(
                    [entries_container, Window(height=1, char="-"), self.input_field,]
                ),
                floats=[
                    Float(
                        content=CompletionsMenu(max_height=16, scroll_offset=1),
                        xcursor=True,
                        ycursor=True,
                    )
                ],
            )

        else:
            self.root_container = entries_container
        self.layout = Layout(self.root_container)

        self.app = Application(full_screen=True, key_bindings=kb, layout=self.layout)
                            content=controls.BufferControl(buffer=buffer), height=1
                        ),
                        containers.Window(height=1, char="-", style="class:line"),
                        containers.Window(content=display),
                    ]
                ),
                floats=[
                    containers.Float(
                        xcursor=True,
                        ycursor=True,
                        content=menus.CompletionsMenu(max_height=12, scroll_offset=1),
                    )
                ],
            ),
        ]
    )
)


kb = KeyBindings()


@kb.add("c-g")
@kb.add("c-c")
def exit_(event):
    event.app.exit()


app = Application(layout=layout, full_screen=True, key_bindings=kb)
app.run()
Esempio n. 32
0
def cli(ctx, email, password, token, graphql, **kwargs):
    '''Music player'''
    ctx.obj.u = lambda: user.User.new(email=email, password=password, token=token, graphql=graphql)
    mf = mfilter.Filter(**kwargs)
    p = ctx.obj.u().do_filter(mf)
    if not p:
        logger.warning('Empty playlist')
        return
    instance = vlc.Instance()
    songs = [song['path'] for song in p]
    player = instance.media_list_player_new()
    media_list = instance.media_list_new(songs)
    player.set_media_list(media_list)
    bindings = KeyBindings()

    @bindings.add('p')
    def _play_binding(event):
        def play():
            """Play song"""
            player.play()
        run_in_terminal(play)

    @bindings.add('q')
    def _quit_binding(event):
        player.pause()
        event.app.exit()

    @bindings.add('s')
    def _pause_binding(event):
        player.pause()

    @bindings.add('l')
    def _playlist_binding(event):
        def playlist():
            """List songs"""
            for s in songs:
                print(s)
        run_in_terminal(playlist)

    @bindings.add('right')
    def _next_binding(event):
        player.next()

    @bindings.add('left')
    def _previous_binding(event):
        player.previous()

    def bottom_toolbar():
        media_player = player.get_media_player()
        media = media_player.get_media()
        media.parse()
        media_time = seconds_to_human(round(media_player.get_time() / 1000))
        media_length = seconds_to_human(round(media_player.get_length() / 1000))
        artist = media.get_meta(vlc.Meta.Artist)
        album = media.get_meta(vlc.Meta.Album)
        title = media.get_meta(vlc.Meta.Title)
        current = '({} / {}) {} - {} - {}'.format(media_time, media_length, artist, album, title)
        get_app().invalidate()
        return HTML('Current song: {}'.format(current))

    player.play()
    print(HTML('Bindings: q = quit | p = play | s = pause/continue | right = next song | left = previous song | l = playlist'))

    root_container = HSplit(
        [Window(FormattedTextControl(lambda: bottom_toolbar, style='class:bottom-toolbar.text'), style='class:bottom-toolbar')]
    )
    layout = Layout(root_container)
    app = Application(layout=layout, key_bindings=bindings)
    app.run()