pax_global_header00006660000000000000000000000064141417620610014514gustar00rootroot0000000000000052 comment=9d3936283ae9ca45f09686a31efa739f0cd068fd python-click-repl-0.2.0/000077500000000000000000000000001414176206100150575ustar00rootroot00000000000000python-click-repl-0.2.0/LICENSE000066400000000000000000000020741414176206100160670ustar00rootroot00000000000000Copyright (c) 2014-2015 Markus Unterwaditzer & contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. python-click-repl-0.2.0/MANIFEST.in000066400000000000000000000001001414176206100166040ustar00rootroot00000000000000global-exclude *.py[cdo] __pycache__ *.so *.pyd include LICENSE python-click-repl-0.2.0/PKG-INFO000066400000000000000000000004001414176206100161460ustar00rootroot00000000000000Metadata-Version: 1.0 Name: click-repl Version: 0.2.0 Summary: REPL plugin for Click Home-page: https://github.com/untitaker/click-repl Author: Markus Unterwaditzer Author-email: markus@unterwaditzer.net License: MIT Description: UNKNOWN Platform: UNKNOWN python-click-repl-0.2.0/README.rst000066400000000000000000000042761414176206100165570ustar00rootroot00000000000000========== click-repl ========== .. image:: https://travis-ci.org/click-contrib/click-repl.svg?branch=master :target: https://travis-ci.org/click-contrib/click-repl In your click_ app: .. code:: python import click from click_repl import register_repl @click.group() def cli(): pass @cli.command() def hello(): click.echo("Hello world!") register_repl(cli) cli() In the shell:: $ my_app repl > hello Hello world! > ^C $ echo hello | my_app repl Hello world! Features not shown: * Tab-completion. * The parent context is reused, which means ``ctx.obj`` persists between subcommands. If you're keeping caches on that object (like I do), using the app's repl instead of the shell is a huge performance win. * ``!``-prefix executes shell commands. You can use the internal ``:help`` command to explain usage. PyPI: ``_ .. _click: http://click.pocoo.org/ How to install ============== Installation is done with pip: $ pip install click-repl Advanced Usage ============== For more flexibility over how your REPL works you can use the ``repl`` function directly instead of ``register_repl``. For example, in your app: .. code:: python import click from click_repl import repl from prompt_toolkit.history import FileHistory @click.group() def cli(): pass @cli.command() def myrepl(): prompt_kwargs = { 'history': FileHistory('/etc/myrepl/myrepl-history'), } repl(click.get_current_context(), prompt_kwargs=prompt_kwargs) cli() And then your custom ``myrepl`` command will be available on your CLI, which will start a REPL which has its history stored in ``/etc/myrepl/myrepl-history`` and persist between sessions. Any arguments that can be passed to the ``python-prompt-toolkit`` Prompt_ class can be passed in the ``prompt_kwargs`` argument and will be used when instantiating your ``Prompt``. .. _Prompt: http://python-prompt-toolkit.readthedocs.io/en/stable/pages/reference.html?prompt_toolkit.shortcuts.Prompt#prompt_toolkit.shortcuts.Prompt License ======= Licensed under the MIT, see ``LICENSE``. python-click-repl-0.2.0/click_repl.egg-info/000077500000000000000000000000001414176206100206605ustar00rootroot00000000000000python-click-repl-0.2.0/click_repl.egg-info/PKG-INFO000066400000000000000000000004001414176206100217470ustar00rootroot00000000000000Metadata-Version: 1.0 Name: click-repl Version: 0.2.0 Summary: REPL plugin for Click Home-page: https://github.com/untitaker/click-repl Author: Markus Unterwaditzer Author-email: markus@unterwaditzer.net License: MIT Description: UNKNOWN Platform: UNKNOWN python-click-repl-0.2.0/click_repl.egg-info/SOURCES.txt000066400000000000000000000004001414176206100225360ustar00rootroot00000000000000LICENSE MANIFEST.in README.rst setup.py click_repl/__init__.py click_repl/exceptions.py click_repl.egg-info/PKG-INFO click_repl.egg-info/SOURCES.txt click_repl.egg-info/dependency_links.txt click_repl.egg-info/requires.txt click_repl.egg-info/top_level.txtpython-click-repl-0.2.0/click_repl.egg-info/dependency_links.txt000066400000000000000000000000011414176206100247260ustar00rootroot00000000000000 python-click-repl-0.2.0/click_repl.egg-info/requires.txt000066400000000000000000000000311414176206100232520ustar00rootroot00000000000000click prompt_toolkit six python-click-repl-0.2.0/click_repl.egg-info/top_level.txt000066400000000000000000000000131414176206100234040ustar00rootroot00000000000000click_repl python-click-repl-0.2.0/click_repl/000077500000000000000000000000001414176206100171665ustar00rootroot00000000000000python-click-repl-0.2.0/click_repl/__init__.py000066400000000000000000000213121414176206100212760ustar00rootroot00000000000000from collections import defaultdict from prompt_toolkit.completion import Completer, Completion from prompt_toolkit.history import InMemoryHistory from prompt_toolkit.shortcuts import prompt import click import click.parser import os import shlex import sys import six from .exceptions import InternalCommandException, ExitReplException # noqa # Handle backwards compatibility between Click 7.0 and 8.0 try: import click.shell_completion HAS_C8 = True except ImportError: import click._bashcomplete HAS_C8 = False # Handle click.exceptions.Exit introduced in Click 7.0 try: from click.exceptions import Exit as ClickExit except ImportError: class ClickExit(RuntimeError): pass PY2 = sys.version_info[0] == 2 if PY2: text_type = unicode # noqa else: text_type = str # noqa __version__ = "0.2.0" _internal_commands = dict() def _register_internal_command(names, target, description=None): if not hasattr(target, "__call__"): raise ValueError("Internal command must be a callable") if isinstance(names, six.string_types): names = [names] elif not isinstance(names, (list, tuple)): raise ValueError('"names" must be a string or a list / tuple') for name in names: _internal_commands[name] = (target, description) def _get_registered_target(name, default=None): target_info = _internal_commands.get(name) if target_info: return target_info[0] return default def _exit_internal(): raise ExitReplException() def _help_internal(): formatter = click.HelpFormatter() formatter.write_heading("REPL help") formatter.indent() with formatter.section("External Commands"): formatter.write_text('prefix external commands with "!"') with formatter.section("Internal Commands"): formatter.write_text('prefix internal commands with ":"') info_table = defaultdict(list) for mnemonic, target_info in six.iteritems(_internal_commands): info_table[target_info[1]].append(mnemonic) formatter.write_dl( ( ", ".join((":{0}".format(mnemonic) for mnemonic in sorted(mnemonics))), description, ) for description, mnemonics in six.iteritems(info_table) ) return formatter.getvalue() _register_internal_command(["q", "quit", "exit"], _exit_internal, "exits the repl") _register_internal_command( ["?", "h", "help"], _help_internal, "displays general help information" ) class ClickCompleter(Completer): def __init__(self, cli): self.cli = cli def get_completions(self, document, complete_event=None): # Code analogous to click._bashcomplete.do_complete try: args = shlex.split(document.text_before_cursor) except ValueError: # Invalid command, perhaps caused by missing closing quotation. return cursor_within_command = ( document.text_before_cursor.rstrip() == document.text_before_cursor ) if args and cursor_within_command: # We've entered some text and no space, give completions for the # current word. incomplete = args.pop() else: # We've not entered anything, either at all or for the current # command, so give all relevant completions for this context. incomplete = "" # Resolve context based on click version if HAS_C8: ctx = click.shell_completion._resolve_context(self.cli, {}, "", args) else: ctx = click._bashcomplete.resolve_ctx(self.cli, "", args) if ctx is None: return choices = [] for param in ctx.command.params: if isinstance(param, click.Option): for options in (param.opts, param.secondary_opts): for o in options: choices.append( Completion( text_type(o), -len(incomplete), display_meta=param.help ) ) elif isinstance(param, click.Argument): if isinstance(param.type, click.Choice): for choice in param.type.choices: choices.append(Completion(text_type(choice), -len(incomplete))) if isinstance(ctx.command, click.MultiCommand): for name in ctx.command.list_commands(ctx): command = ctx.command.get_command(ctx, name) choices.append( Completion( text_type(name), -len(incomplete), display_meta=getattr(command, "short_help"), ) ) for item in choices: if item.text.startswith(incomplete): yield item def bootstrap_prompt(prompt_kwargs, group): """ Bootstrap prompt_toolkit kwargs or use user defined values. :param prompt_kwargs: The user specified prompt kwargs. """ prompt_kwargs = prompt_kwargs or {} defaults = { "history": InMemoryHistory(), "completer": ClickCompleter(group), "message": u"> ", } for key in defaults: default_value = defaults[key] if key not in prompt_kwargs: prompt_kwargs[key] = default_value return prompt_kwargs def repl( # noqa: C901 old_ctx, prompt_kwargs=None, allow_system_commands=True, allow_internal_commands=True, ): """ Start an interactive shell. All subcommands are available in it. :param old_ctx: The current Click context. :param prompt_kwargs: Parameters passed to :py:func:`prompt_toolkit.shortcuts.prompt`. If stdin is not a TTY, no prompt will be printed, but only commands read from stdin. """ # parent should be available, but we're not going to bother if not group_ctx = old_ctx.parent or old_ctx group = group_ctx.command isatty = sys.stdin.isatty() # Delete the REPL command from those available, as we don't want to allow # nesting REPLs (note: pass `None` to `pop` as we don't want to error if # REPL command already not present for some reason). repl_command_name = old_ctx.command.name if isinstance(group_ctx.command, click.CommandCollection): available_commands = { cmd_name: cmd_obj for source in group_ctx.command.sources for cmd_name, cmd_obj in source.commands.items() } else: available_commands = group_ctx.command.commands available_commands.pop(repl_command_name, None) prompt_kwargs = bootstrap_prompt(prompt_kwargs, group) if isatty: def get_command(): return prompt(**prompt_kwargs) else: get_command = sys.stdin.readline while True: try: command = get_command() except KeyboardInterrupt: continue except EOFError: break if not command: if isatty: continue else: break if allow_system_commands and dispatch_repl_commands(command): continue if allow_internal_commands: try: result = handle_internal_commands(command) if isinstance(result, six.string_types): click.echo(result) continue except ExitReplException: break try: args = shlex.split(command) except ValueError as e: click.echo("{}: {}".format(type(e).__name__, e)) continue try: with group.make_context(None, args, parent=group_ctx) as ctx: group.invoke(ctx) ctx.exit() except click.ClickException as e: e.show() except ClickExit: pass except SystemExit: pass except ExitReplException: break def register_repl(group, name="repl"): """Register :func:`repl()` as sub-command *name* of *group*.""" group.command(name=name)(click.pass_context(repl)) def exit(): """Exit the repl""" _exit_internal() def dispatch_repl_commands(command): """Execute system commands entered in the repl. System commands are all commands starting with "!". """ if command.startswith("!"): os.system(command[1:]) return True return False def handle_internal_commands(command): """Run repl-internal commands. Repl-internal commands are all commands starting with ":". """ if command.startswith(":"): target = _get_registered_target(command[1:], default=None) if target: return target() python-click-repl-0.2.0/click_repl/exceptions.py000066400000000000000000000001621414176206100217200ustar00rootroot00000000000000class InternalCommandException(Exception): pass class ExitReplException(InternalCommandException): pass python-click-repl-0.2.0/setup.cfg000066400000000000000000000000461414176206100167000ustar00rootroot00000000000000[egg_info] tag_build = tag_date = 0 python-click-repl-0.2.0/setup.py000066400000000000000000000011461414176206100165730ustar00rootroot00000000000000#!/usr/bin/env python import ast import re from setuptools import setup _version_re = re.compile(r"__version__\s+=\s+(.*)") with open("click_repl/__init__.py", "rb") as f: version = str( ast.literal_eval(_version_re.search(f.read().decode("utf-8")).group(1)) ) setup( name="click-repl", version=version, description="REPL plugin for Click", author="Markus Unterwaditzer", author_email="markus@unterwaditzer.net", url="https://github.com/untitaker/click-repl", license="MIT", packages=["click_repl"], install_requires=["click", "prompt_toolkit", "six"], )