<commit_before># -*- coding: utf-8 -*-


import sys
from optparse import OptionParser, Option
from xmlrpclib import Fault

from kobo.plugins import Plugin, PluginContainer


__all__ = (
    "Command",
    "CommandContainer",
    "CommandOptionParser",
    "Option",
    "username_prompt",
    "password_prompt",
    "yes_no_prompt",
    "are_you_sure_prompt",
)


def username_prompt(prompt=None, default_value=None):
    """Ask for a username."""
    if default_value is not None:
        return default_value

    prompt = prompt or "Enter your username: "
    print >>sys.stderr, prompt,
    return sys.stdin.readline()


def password_prompt(prompt=None, default_value=None):
    """Ask for a password."""
    import getpass

    if default_value is not None:
        return default_value

    prompt = prompt or "Enter your password: "
    try:
        # try to use stderr stream
        result = getpass.getpass(prompt, stream=sys.stderr)
    except TypeError:
        # fall back to stdout
        result = getpass.getpass(prompt)
    return result


def yes_no_prompt(prompt, default_value=None):
    """Give a yes/no (y/n) question."""
    if default_value is not None:
        if default_value not in ("Y", "N"):
            raise ValueError("Invalid default value: %s" % default_value)
        default_value = default_value.upper()

    prompt = "%s [%s/%s]: " % (prompt, ("y", "Y")[default_value == "Y"], ("n", "N")[default_value == "N"])
    print >>sys.stderr, prompt,

    while True:
        user_input = sys.stdin.readline().strip().upper()
        if user_input == "" and default_value is not None:
            user_input = default_value

        if user_input == "Y":
            return True
        if user_input == "N":
            return False


def are_you_sure_prompt(prompt=None):
    """Give a yes/no (y/n) question."""
    prompt = prompt or "Are you sure? Enter 'YES' to continue: "
    print >>sys.stderr, prompt,
    user_input = sys.stdin.readline().strip()

    if user_input == "YES":
        return True

    return False


class Command(Plugin):
    """An abstract class representing a command for CommandOptionParser."""

    __slots__ = (
        "container",
        "parser",
        "normalized_name",
        "admin",
    )

    enabled = False
    admin = False


    username_prompt = staticmethod(username_prompt)
    password_prompt = staticmethod(password_prompt)
    yes_no_prompt = staticmethod(yes_no_prompt)
    are_you_sure_prompt = staticmethod(are_you_sure_prompt)


    def __init__(self, container, parser):
        Plugin.__init__(self)
        self.container = container
        self.parser = parser
        self.normalized_name = self.container.normalize_name(self.__class__.__name__)


    def options(self):
        """Add options to self.parser."""
        pass


    def run(self, *args, **kwargs):
        """Run a command. Arguments contain parsed options."""
        raise NotImplementedError()


class CommandContainer(PluginContainer):
    """Container for Command classes."""

    @classmethod
    def normalize_name(cls, name):
        """Replace some characters in command names."""
        return name.lower().replace('_', '-').replace(' ', '-')


class CommandOptionParser(OptionParser):
    """Enhanced OptionParser with plugin support."""
    def __init__(self,
            usage=None,
            option_list=None,
            option_class=Option,
            version=None,
            conflict_handler="error",
            description=None,
            formatter=None,
            add_help_option=True,
            prog=None,
            command_container=None,
            default_command=None):

        usage = usage or "%prog <command> [args] [--help]"
        self.container = command_container
        self.default_command = default_command or "help"
        self.command = None
        OptionParser.__init__(self, usage, option_list, option_class, version, conflict_handler, description, formatter, add_help_option, prog)


    def print_help(self, file=None):
        if file is None:
            file = sys.stdout
        file.write(self.format_help())
        if self.command is None or self.command == "help":
            file.write("\n")
            file.write(self.format_help_commands())


    def format_help_commands(self):
        result = []
        result.append("commands:")
        for name, plugin in sorted(self.container.plugins.iteritems()):
            admin = (" ", "A")[getattr(plugin, "admin", False)]
            result.append("%s %-30s %s" % (admin, name, plugin.__doc__ or ""))
        result.append("")
        return "\n".join(result)


    def parse_args(self, args=None, values=None):
        """return (command_instance, opts, args)"""
        args = self._get_args(args)
        command = None

        if len(args) > 0 and not args[0].startswith("-"):
            command = args[0]
            args = args[1:]
        else:
            command = self.default_command
            # keep args as is

        if not command in self.container.plugins:
            self.error("unknown command: %s" % command)

        CommandClass = self.container[command]
        cmd = CommandClass(self.container, self)
        if self.command != cmd.normalized_name:
            self.command = cmd.normalized_name
            cmd.options()<commit_msg>Make CommandOptionParser compatible with latest plugins. Add a HOWTO.<commit_after># -*- coding: utf-8 -*-


"""
CommandOptionParser HOWTO
=========================

1) setup CommandContainer
-------------------------
# In case you don't need any special functionality, just import default CommandContainer.
# Otherwise it's recommended to inherit your own container and extend it's functionality.
# Typical use cases are shared configuration or shared XML-RPC connection.

from kobo.cli import CommandContainer
class MyCommandContainer(CommandContainer):
    def __init__(self, *args, **kwargs):
        CommandContainer.__init__(self, *args, **kwargs)
        self.xmlrpc_client = ...


2) write your own Commands
--------------------------
# It usually makes sense to inherit directly from Command class.
# All common methods and attributes should be in the container.
# Specify any OptionParser options in options() method.
# OptionParser.parse_args() result is automatically passed to run(*args, **kwargs) method.
# A OptionParser instance os available in self.parser attribute.

class Make_Dirs(Command):
    '''create directories'''
    enabled = True
    admin = False

    def options(self):
        self.parser.usage = "%%prog %s [options] <user>" % self.normalized_name
        self.parser.add_option("-m", "--mode", help="set directory perms (0xxx)")

    def run(self, *args, **kwargs):
        if len(args) < 1:
            self.parser.error("Please specify a directory")
        mode = kwargs.pop("mode", "0755")
        mode = int(mode, 0) # convert oct string to int

        import os
        for directory in args:
            os.makedirs(directory, mode=mode)


3) register commands to a container
-----------------------------------
# Register either either all plugins (register_plugin)
# or all plugins in a module (register_module) to a container.
# All plugins must have enabled=True otherwise they won't be registered.

(My)CommandContainer.register_plugin(plugin_class)
(My)CommandContainer.register_module(module_with_plugins)


4) Use CommandOptionParser
--------------------------
command_container = (My)CommandContainer()
parser = CommandOptionParser(command_container=command_container)
parser.run()

# See kobo.client.main for slightly advanced example.
"""


import sys
from optparse import OptionParser, Option
from xmlrpclib import Fault

from kobo.plugins import Plugin, PluginContainer


__all__ = (
    "Command",
    "CommandContainer",
    "CommandOptionParser",
    "Option",
    "username_prompt",
    "password_prompt",
    "yes_no_prompt",
    "are_you_sure_prompt",
)


def username_prompt(prompt=None, default_value=None):
    """Ask for a username."""
    if default_value is not None:
        return default_value

    prompt = prompt or "Enter your username: "
    print >>sys.stderr, prompt,
    return sys.stdin.readline()


def password_prompt(prompt=None, default_value=None):
    """Ask for a password."""
    import getpass

    if default_value is not None:
        return default_value

    prompt = prompt or "Enter your password: "
    try:
        # try to use stderr stream
        result = getpass.getpass(prompt, stream=sys.stderr)
    except TypeError:
        # fall back to stdout
        result = getpass.getpass(prompt)
    return result


def yes_no_prompt(prompt, default_value=None):
    """Give a yes/no (y/n) question."""
    if default_value is not None:
        if default_value not in ("Y", "N"):
            raise ValueError("Invalid default value: %s" % default_value)
        default_value = default_value.upper()

    prompt = "%s [%s/%s]: " % (prompt, ("y", "Y")[default_value == "Y"], ("n", "N")[default_value == "N"])
    print >>sys.stderr, prompt,

    while True:
        user_input = sys.stdin.readline().strip().upper()
        if user_input == "" and default_value is not None:
            user_input = default_value

        if user_input == "Y":
            return True
        if user_input == "N":
            return False


def are_you_sure_prompt(prompt=None):
    """Give a yes/no (y/n) question."""
    prompt = prompt or "Are you sure? Enter 'YES' to continue: "
    print >>sys.stderr, prompt,
    user_input = sys.stdin.readline().strip()

    if user_input == "YES":
        return True

    return False


class Command(Plugin):
    """An abstract class representing a command for CommandOptionParser."""

    __slots__ = (
        "parser",
        "admin",
    )

    enabled = False
    admin = False


    username_prompt = staticmethod(username_prompt)
    password_prompt = staticmethod(password_prompt)
    yes_no_prompt = staticmethod(yes_no_prompt)
    are_you_sure_prompt = staticmethod(are_you_sure_prompt)


    def __init__(self, parser):
        Plugin.__init__(self)
        self.parser = parser


    def options(self):
        """Add options to self.parser."""
        pass


    def run(self, *args, **kwargs):
        """Run a command. Arguments contain parsed options."""
        raise NotImplementedError()


class CommandContainer(PluginContainer):
    """Container for Command classes."""

    @classmethod
    def normalize_name(cls, name):
        """Replace some characters in command names."""
        return name.lower().replace('_', '-').replace(' ', '-')


class CommandOptionParser(OptionParser):
    """Enhanced OptionParser with plugin support."""
    def __init__(self,
            usage=None,
            option_list=None,
            option_class=Option,
            version=None,
            conflict_handler="error",
            description=None,
            formatter=None,
            add_help_option=True,
            prog=None,
            command_container=None,
            default_command=None):

        usage = usage or "%prog <command> [args] [--help]"
        self.container = command_container
        self.default_command = default_command or "help"
        self.command = None
        OptionParser.__init__(self, usage, option_list, option_class, version, conflict_handler, description, formatter, add_help_option, prog)


    def print_help(self, file=None):
        if file is None:
            file = sys.stdout
        file.write(self.format_help())
        if self.command is None or self.command == "help":
            file.write("\n")
            file.write(self.format_help_commands())


    def format_help_commands(self):
        result = []
        result.append("commands:")
        for name, plugin in sorted(self.container.plugins.iteritems()):
            admin = (" ", "A")[getattr(plugin, "admin", False)]
            result.append("%s %-30s %s" % (admin, name, plugin.__doc__ or ""))
        result.append("")
        return "\n".join(result)


    def parse_args(self, args=None, values=None):
        """return (command_instance, opts, args)"""
        args = self._get_args(args)
        command = None

        if len(args) > 0 and not args[0].startswith("-"):
            command = args[0]
            args = args[1:]
        else:
            command = self.default_command
            # keep args as is

        if not command in self.container.plugins:
            self.error("unknown command: %s" % command)

        CommandClass = self.container[command]
        cmd = CommandClass(self)
        if self.command != cmd.normalized_name:
            self.command = cmd.normalized_name
            cmd.options()
{'fraction_non_alphanumeric': 0.12253709082345553, 'fraction_numerical': 0.0019624774315095375, 'mean_word_length': 2.607021517553794, 'pattern_counts': {'":': 8, '<': 7, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 3}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1139119', 'n_tokens_mistral': 3664, 'n_tokens_neox': 3487, 'n_words': 999}