<commit_before>"""The brains of the command system."""

from inspect import signature

from functools import wraps

import re

from logging import getLogger


def subcommand(function):
    """Decorate a subcommand."""

    params = list(signature(function).parameters.values())

    @wraps(function)
    def wrapper(self, *args, **data):
        """Parse subcommand data."""

        if not params:
            return function(self)

        args = list(args)

        args_params = tuple(
            p for p in params if p.kind is p.POSITIONAL_OR_KEYWORD)
        star_param = next(
            (p for p in params if p.kind is p.VAR_POSITIONAL), None)

        data_params = tuple(
            p for p in params if p.kind is p.KEYWORD_ONLY)
        kwargs = dict((p.name, data.get(p.annotation)) for p in data_params)


        arg_range = (
            len(tuple(p for p in args_params[1:] if p.default is p.empty)),
            float('inf') if star_param else len(args_params)-1
        )

        if not arg_range[0] <= len(args) <= arg_range[1]:

            syntax = "!{command} {subcommand} {params}".format(
                command=self.__command__, subcommand=function.__name__,
                params='<'+'> <'.join(p.name for p in args_params[1:])+'>'
            )

            if len(args) < len(args_params):
                return "Not enough arguments. ({})".format(syntax)
            elif len(args) > len(args_params):
                return "Too many arguments. ({})".format(syntax)

        for index, argument in enumerate(args_params[:len(args)]):
            annotation = argument.annotation
            if annotation is not argument.empty:
                if isinstance(annotation, str):
                    match = re.match('^'+annotation+'$', args[index])
                    if match is None:
                        return "Invalid {type}: '{value}'.".format(
                            type=argument.name, value=args[index])
                    elif match.groups():
                        args[index] = match.group(1)
                else:
                    self.logger.warning(
                        "Invalid regex: '%s.%s.%s : %s'.",
                        self.__command__, function.__name__,
                        argument.name, annotation
                    )

        return function(
            self, *args[1:],
            **kwargs
        )

    wrapper.is_subcommand = True

    return wrapper


class CommandMeta(type):
    """Manage the backend of commands via a metaclass."""

    def __new__(mcs, name, bases, attrs):
        subcommands = {}
        for value in attrs.values():
            if getattr(value, "is_subcommand", None):
                subcommands[value.__name__] = value
        attrs["_subcommands"] = subcommands
        return super().__new__(mcs, name, bases, attrs)


class Command(metaclass=CommandMeta):
    """Parent all command classes."""

    def __init__(self):
        self.logger = getLogger(__name__)

    def __call__(self, *args, **data):
        # TODO: default subcommands
        # TODO: user levels
        if args[0] in self._subcommands:
            return self._subcommands[args[0]](self, *args, **data)
        return "Invalid argument: '{}'.".format(args[0])
<commit_msg>Add ?expressions<commit_after>"""The brains of the command system."""

from inspect import signature

from functools import wraps

import re

from logging import getLogger


def subcommand(function):
    """Decorate a subcommand."""

    params = list(signature(function).parameters.values())

    @wraps(function)
    def wrapper(self, *args, **data):
        """Parse subcommand data."""

        args = list(args)

        args_params = tuple(
            p for p in params if p.kind is p.POSITIONAL_OR_KEYWORD)
        star_param = next(
            (p for p in params if p.kind is p.VAR_POSITIONAL), None)

        data_params = tuple(
            p for p in params if p.kind is p.KEYWORD_ONLY)
        kwargs = dict((p.name, data.get(p.annotation)) for p in data_params)


        arg_range = (
            len(tuple(p for p in args_params if p.default is p.empty))+1,
            float('inf') if star_param else len(args_params)-1
        )

        if not arg_range[0] <= len(args) <= arg_range[1]:

            if star_param is not None:
                args_params += (star_param,)
            syntax = "!{command} {subcommand} {params}".format(
                command=self.__command__, subcommand=function.__name__,
                params='<'+'> <'.join(p.name for p in args_params[1:])+'>'
            )

            if len(args) < arg_range[0]:
                return "Not enough arguments. ({})".format(syntax)
            elif len(args) > arg_range[1]:
                return "Too many arguments. ({})".format(syntax)

        for index, argument in enumerate(args_params[:len(args)]):
            annotation = argument.annotation
            if annotation is not argument.empty:
                if isinstance(annotation, str):
                    if annotation.startswith('?'):
                        annotation = self.expressions.get(annotation[1:], '')
                    match = re.match('^'+annotation+'$', args[index])
                    if match is None:
                        return "Invalid {type}: '{value}'.".format(
                            type=argument.name, value=args[index])
                    elif match.groups():
                        args[index] = match.group(1)
                else:
                    self.logger.warning(
                        "Invalid regex: '%s.%s.%s : %s'.",
                        self.__command__, function.__name__,
                        argument.name, annotation
                    )

        return function(
            self, *args[1:],
            **kwargs
        )

    wrapper.is_subcommand = True

    return wrapper


class CommandMeta(type):
    """Manage the backend of commands via a metaclass."""

    def __new__(mcs, name, bases, attrs):
        subcommands = {}
        for value in attrs.values():
            if getattr(value, "is_subcommand", None):
                subcommands[value.__name__] = value
        attrs["_subcommands"] = subcommands
        return super().__new__(mcs, name, bases, attrs)


class Command(metaclass=CommandMeta):
    """Parent all command classes."""

    def __init__(self):
        self.logger = getLogger(__name__)
        self.expressions = {
            "username": r'@?([A-Za-z0-9]{,32})'
        }

    def __call__(self, *args, **data):
        # TODO: default subcommands
        # TODO: user levels
        if args[0] in self._subcommands:
            return self._subcommands[args[0]](self, *args, **data)
        return "Invalid argument: '{}'.".format(args[0])

{'fraction_non_alphanumeric': 0.12099276111685625, 'fraction_numerical': 0.0039887723445117445, 'mean_word_length': 1.867429055484964, 'pattern_counts': {'":': 1, '<': 13, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, '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': '1263412', 'n_tokens_mistral': 1912, 'n_tokens_neox': 1813, 'n_words': 504}