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

import os
import re
import errno
import logging
import yaml
import cPickle as pickle
from collections import OrderedDict

from moulinette.core import (MoulinetteError, MoulinetteLock)
from moulinette.interfaces import BaseActionsMapParser

GLOBAL_ARGUMENT = '_global'

## Extra parameters ----------------------------------------------------

# Extra parameters definition

class _ExtraParameter(object):
    """
    Argument parser for an extra parameter.

    It is a pure virtual class that each extra parameter classes must
    implement.

    """
    def __init__(self, iface):
        # TODO: Add conn argument which contains authentification object
        self.iface = iface

    ## Required variables
    # Each extra parameters classes must overwrite these variables.

    """The extra parameter name"""
    name = None

    ## Optional variables
    # Each extra parameters classes can overwrite these variables.

    """A list of interface for which the parameter doesn't apply"""
    skipped_iface = []

    ## Virtual methods
    # Each extra parameters classes can implement these methods.

    def __call__(self, parameter, arg_name, arg_value):
        """
        Parse the argument

        Keyword arguments:
            - parameter -- The value of this parameter for the action
            - arg_name -- The argument name
            - arg_value -- The argument value

        Returns:
            The new argument value

        """
        return arg_value

    @staticmethod
    def validate(value, arg_name):
        """
        Validate the parameter value for an argument

        Keyword arguments:
            - value -- The parameter value
            - arg_name -- The argument name

        Returns:
            The validated parameter value

        """
        return value

class AskParameter(_ExtraParameter):
    """
    Ask for the argument value if possible and needed.

    The value of this parameter corresponds to the message to display
    when asking the argument value.

    """
    name = 'ask'
    skipped_iface = [ 'api' ]

    def __call__(self, message, arg_name, arg_value):
        # TODO: Fix asked arguments ordering
        if arg_value:
            return arg_value

        try:
            # Ask for the argument value
            return msignals.prompt(m18n.n(message))
        except NotImplementedError:
            return arg_value

    @classmethod
    def validate(klass, value, arg_name):
        # Allow boolean or empty string
        if isinstance(value, bool) or (isinstance(value, str) and not value):
            logging.debug("value of '%s' extra parameter for '%s' argument should be a string" \
                % (klass.name, arg_name))
            value = arg_name
        elif not isinstance(value, str):
            raise TypeError("Invalid type of '%s' extra parameter for '%s' argument" \
                % (klass.name, arg_name))
        return value

class PasswordParameter(AskParameter):
    """
    Ask for the password argument value if possible and needed.

    The value of this parameter corresponds to the message to display
    when asking the password.

    """
    name = 'password'

    def __call__(self, message, arg_name, arg_value):
        if arg_value:
            return arg_value

        try:
            # Ask for the password
            return msignals.prompt(m18n.n(message), True, True)
        except NotImplementedError:
            return arg_value

class PatternParameter(_ExtraParameter):
    """
    Check if the argument value match a pattern.

    The value of this parameter corresponds to a list of the pattern and
    the message to display if it doesn't match.

    """
    name = 'pattern'

    def __call__(self, arguments, arg_name, arg_value):
        pattern, message = (arguments[0], arguments[1])

        # Use temporarly utf-8 encoded value
        try:
            v = unicode(arg_value, 'utf-8')
        except:
            v = arg_value

        if v and not re.match(pattern, v or '', re.UNICODE):
            raise MoulinetteError(errno.EINVAL, m18n.g('invalid_argument',
                                        arg_name, m18n.n(message)))
        return arg_value

    @staticmethod
    def validate(value, arg_name):
        # Tolerate string type
        if isinstance(value, str):
            logging.warning("value of 'pattern' extra parameter for '%s' argument should be a list" % arg_name)
            value = [value, _("'%s' argument is not matching the pattern") % arg_name]
        elif not isinstance(value, list) or len(value) != 2:
            raise TypeError("Invalid type of 'pattern' extra parameter for '%s' argument" % arg_name)
        return value

class RequiredParameter(_ExtraParameter):
    """
    Check if a required argument is defined or not.

    The value of this parameter must be a boolean which is set to False by
    default.
    """
    name = 'required'

    def __call__(self, required, arg_name, arg_value):
        if required and (arg_value is None or arg_value == ''):
            raise MoulinetteError(errno.EINVAL, m18n.g('argument_required',
                                                       arg_name))
        return arg_value

    @staticmethod
    def validate(value, arg_name):
        if not isinstance(value, bool):
            raise TypeError("Invalid type of 'required' extra parameter for '%s' argument" % arg_name)
        return value

"""
The list of available extra parameters classes. It will keep to this list
order on argument parsing.

"""
extraparameters_list = [ AskParameter, PasswordParameter, RequiredParameter,
                         PatternParameter ]

# Extra parameters argument Parser

class ExtraArgumentParser(object):
    """
    Argument validator and parser for the extra parameters.

    Keyword arguments:
        - iface -- The running interface

    """
    def __init__(self, iface):
        self.iface = iface
        self.extra = OrderedDict()
        self._extra_params = { GLOBAL_ARGUMENT: {} }

        # Append available extra parameters for the current interface
        for klass in extraparameters_list:
            if iface in klass.skipped_iface:
                continue
            self.extra[klass.name] = klass

    def validate(self, arg_name, parameters):
        """
        Validate values of extra parameters for an argument

        Keyword arguments:
            - arg_name -- The argument name
            - parameters -- A dict of extra parameters with their values

        """
        # Iterate over parameters to validate
        for p, v in parameters.items():
            klass = self.extra.get(p, None)
            if not klass:
                # Remove unknown parameters
                del parameters[p]
            else:
                # Validate parameter value
                parameters[p] = klass.validate(v, arg_name)

        return parameters

    def add_argument(self, tid, arg_name, parameters, validate=True):
        """
        Add extra parameters to apply on an action argument

        Keyword arguments:
            - tid -- The tuple identifier of the action or GLOBAL_ARGUMENT
                for global extra parameters
            - arg_name -- The argument name
            - parameters -- A dict of extra parameters with their values
            - validate -- False to not validate extra parameters values

        """
        if validate:
            parameters = self.validate(arg_name, parameters)
        try:
            self._extra_params[tid][arg_name] = parameters
        except KeyError:
            self._extra_params[tid] = { arg_name: parameters }

    def parse_args(self, tid, args):
        """
        Parse arguments for an action with extra parameters

        Keyword arguments:
            - tid -- The tuple identifier of the action
            - args -- A dict of argument name associated to their value

        """
        extra_args = dict(self._extra_params.get(GLOBAL_ARGUMENT, {}))
        extra_args.update(self._extra_params.get(tid, {}))

        # Iterate over action arguments with extra parameters
        for arg_name, extra_params in extra_args.items():
            # Iterate over available extra parameters
            for p, cls in self.extra.items():
                try:
                    extra_value = extra_params[p]
                except KeyError:
                    continue
                arg_value = args.get(arg_name, None)

                # Initialize the extra parser
                parser = cls(self.iface)

                # Parse the argument
                if isinstance(arg_value, list):
                    for v in arg_value:
                        r = parser(extra_value, arg_name, v)
                        if r not in arg_value:
                            arg_value.append(r)
                else:
                    arg_value = parser(extra_value, arg_name, arg_value)

                # Update argument value
                if arg_value is not None:
                    args[arg_name] = arg_value
        return args

## Main class ----------------------------------------------------------

class ActionsMap(object):
    """Validate and process actions defined into an actions map

    The actions map defines the features - and their usage - of an
    application which will be available through the moulinette.
    It is composed by categories which contain one or more action(s).
    Moreover, the action can have specific argument(s).

    This class allows to manipulate one or several actions maps
    associated to a namespace. If no namespace is given, it will load
    all available namespaces.

    Keyword arguments:
        - parser -- The BaseActionsMapParser derived class to use for
            parsing the actions map
        - namespaces -- The list of namespaces to use
        - use_cache -- False if it should parse the actions map file
            instead of using the cached one.

    """
    def __init__(self, parser, namespaces=[], use_cache=True):
        self.use_cache = use_cache
        if not issubclass(parser, BaseActionsMapParser):
            raise ValueError("Invalid parser class '%s'" % parser.__name__)
        self._parser_class = parser

        logging.debug("initializing ActionsMap for the interface '%s'" % parser.interface)

        if len(namespaces) == 0:
            namespaces = self.get_namespaces()
        actionsmaps = OrderedDict()

        # Iterate over actions map namespaces
        for n in namespaces:
            logging.debug("loading '%s' actions map namespace" % n)

            if use_cache:
                try:
                    # Attempt to load cache
                    with open('%s/actionsmap/%s.pkl' % (pkg.cachedir, n)) as f:
                        actionsmaps[n] = pickle.load(f)
                # TODO: Switch to python3 and catch proper exception
                except IOError:
                    self.use_cache = False
                    actionsmaps = self.generate_cache(namespaces)
                    break
            else:
                with open('%s/actionsmap/%s.yml' % (pkg.datadir, n)) as f:
                    actionsmaps[n] = yaml.load(f)

            # Load translations
            # FIXME: Allow several namespaces in m18n
            m18n.load_namespace(n)

        # Generate parsers
        self.extraparser = ExtraArgumentParser(parser.interface)
        self._parser = self._construct_parser(actionsmaps)

    @property
    def parser(self):
        """Return the instance of the interface's actions map parser"""
        return self._parser

    def get_authenticator(self, profile='default'):
        """Get an authenticator instance

        Retrieve the authenticator for the given profile and return a
        new instance.

        Keyword arguments:
            - profile -- An authenticator profile name

        Returns:
            A new _BaseAuthenticator derived instance

        """
        try:
            auth = self.parser.get_global_conf('authenticator', profile)[1]
        except KeyError:
            raise ValueError("Unknown authenticator profile '%s'" % profile)
        else:
            return auth()

    def process(self, args, timeout=0, **kwargs):
        """
        Parse arguments and process the proper action

        Keyword arguments:
            - args -- The arguments to parse
            - timeout -- The time period before failing if the lock
                cannot be acquired for the action
            - **kwargs -- Additional interface arguments

        """
        # Parse arguments
        arguments = vars(self.parser.parse_args(args, **kwargs))

        # Retrieve tid and parse arguments with extra parameters
        tid = arguments.pop('_tid')
        arguments = self.extraparser.parse_args(tid, arguments)

        # Retrieve action information
        namespace, category, action = tid
        func_name = '%s_%s' % (category, action.replace('-', '_'))

        # Lock the moulinette for the namespace
        with MoulinetteLock(namespace, timeout):
            try:
                mod = __import__('%s.%s' % (namespace, category),
                                 globals=globals(), level=0,
                                 fromlist=[func_name])
                func = getattr(mod, func_name)
            except (AttributeError, ImportError):
                raise ImportError("Unable to load function %s.%s/%s"
                        % (namespace, category, func_name))
            else:
                # Load translation and process the action
                m18n.load_namespace(namespace)
                return func(**arguments)

    @staticmethod
    def get_namespaces():
        """
        Retrieve available actions map namespaces

        Returns:
            A list of available namespaces

        """
        namespaces = []

        for f in os.listdir('%s/actionsmap' % pkg.datadir):
            if f.endswith('.yml'):
                namespaces.append(f[:-4])
        return namespaces

    @classmethod
    def generate_cache(klass, namespaces=None):
        """
        Generate cache for the actions map's file(s)

        Keyword arguments:
            - namespaces -- A list of namespaces to generate cache for

        Returns:
            A dict of actions map for each namespaces

        """
        actionsmaps = {}
        if not namespaces:
            namespaces = klass.get_namespaces()

        # Iterate over actions map namespaces
        for n in namespaces:
            logging.debug("generating cache for '%s' actions map namespace" % n)

            # Read actions map from yaml file
            am_file = '%s/actionsmap/%s.yml' % (pkg.datadir, n)
            with open(am_file, 'r') as f:
                actionsmaps[n] = yaml.load(f)

            # Cache actions map into pickle file
            with pkg.open_cachefile('%s.pkl' % n, 'w', subdir='actionsmap') as f:
                pickle.dump(actionsmaps[n], f)

        return actionsmaps

    ## Private methods

    def _construct_parser(self, actionsmaps):
        """
        Construct the parser with the actions map

        Keyword arguments:
            - actionsmaps -- A dict of multi-level dictionnary of
                categories/actions/arguments list for each namespaces

        Returns:
            An interface relevant's parser object

        """
        ## Get extra parameters
        if not self.use_cache:
            validate_extra = True
        else:
            validate_extra = False

        ## Add arguments to the parser
        def _add_arguments(tid, parser, arguments):
            for argn, argp in arguments.items():
                names = top_parser.format_arg_names(argn,
                                                    argp.pop('full', None))
                try: argp['type'] = eval(argp['type'])
                except: pass

                try:
                    extra = argp.pop('extra')
                    arg_dest = (parser.add_argument(*names, **argp)).dest
                    self.extraparser.add_argument(tid, arg_dest, extra,
                                                  validate_extra)
                except KeyError:
                    # No extra parameters
                    parser.add_argument(*names, **argp)

        # Instantiate parser
        top_parser = self._parser_class()

        # Iterate over actions map namespaces
        for n, actionsmap in actionsmaps.items():
            # Retrieve global parameters
            _global = actionsmap.pop('_global', {})

            # -- Parse global configuration
            if 'configuration' in _global:
                # Set global configuration
                top_parser.set_global_conf(_global['configuration'])

            # -- Parse global arguments
            if 'arguments' in _global:
                try:
                    # Get global arguments parser
                    parser = top_parser.add_global_parser()
                except AttributeError:
                    # No parser for global arguments
                    pass
                else:
                    # Add arguments
                    _add_arguments(GLOBAL_ARGUMENT, parser,
                                   _global['arguments'])

            # -- Parse categories
            for cn, cp in actionsmap.items():
                try:
                    actions = cp.pop('actions')
                except KeyError:
                    # Invalid category without actions
                    logging.warning("no actions found in category '%s'" % cn)
                    continue

                # Get category parser
                cat_parser = top_parser.add_category_parser(cn, **cp)

                # -- Parse actions
                for an, ap in actions.items():
                    args = ap.pop('arguments', {})
                    tid = (n, cn, an)
                    try:
                        conf = ap.pop('configuration')
                        _set_conf = lambda p: p.set_conf(tid, conf)
                    except KeyError:
                        # No action configuration
                        _set_conf = lambda p: False

                    try:
                        # Get action parser
                        parser = cat_parser.add_action_parser(an, tid, **ap)
                    except AttributeError:
                        # No parser for the action
                        continue
                    except ValueError as e:
                        logging.warning("cannot add action (%s, %s, %s): %s" % (n, cn, an, e))
                        continue
                    else:
                        # Store action identifier and add arguments
                        parser.set_defaults(_tid=tid)
                        _add_arguments(tid, parser, args)
                        _set_conf(cat_parser)

        return top_parser
<commit_msg>[fix] Keep actions map arguments's order on parsing<commit_after># -*- coding: utf-8 -*-

import os
import re
import errno
import logging
import yaml
import cPickle as pickle
from collections import OrderedDict

from moulinette.core import (MoulinetteError, MoulinetteLock)
from moulinette.interfaces import BaseActionsMapParser

GLOBAL_ARGUMENT = '_global'

## Extra parameters ----------------------------------------------------

# Extra parameters definition

class _ExtraParameter(object):
    """
    Argument parser for an extra parameter.

    It is a pure virtual class that each extra parameter classes must
    implement.

    """
    def __init__(self, iface):
        # TODO: Add conn argument which contains authentification object
        self.iface = iface

    ## Required variables
    # Each extra parameters classes must overwrite these variables.

    """The extra parameter name"""
    name = None

    ## Optional variables
    # Each extra parameters classes can overwrite these variables.

    """A list of interface for which the parameter doesn't apply"""
    skipped_iface = []

    ## Virtual methods
    # Each extra parameters classes can implement these methods.

    def __call__(self, parameter, arg_name, arg_value):
        """
        Parse the argument

        Keyword arguments:
            - parameter -- The value of this parameter for the action
            - arg_name -- The argument name
            - arg_value -- The argument value

        Returns:
            The new argument value

        """
        return arg_value

    @staticmethod
    def validate(value, arg_name):
        """
        Validate the parameter value for an argument

        Keyword arguments:
            - value -- The parameter value
            - arg_name -- The argument name

        Returns:
            The validated parameter value

        """
        return value

class AskParameter(_ExtraParameter):
    """
    Ask for the argument value if possible and needed.

    The value of this parameter corresponds to the message to display
    when asking the argument value.

    """
    name = 'ask'
    skipped_iface = [ 'api' ]

    def __call__(self, message, arg_name, arg_value):
        # TODO: Fix asked arguments ordering
        if arg_value:
            return arg_value

        try:
            # Ask for the argument value
            return msignals.prompt(m18n.n(message))
        except NotImplementedError:
            return arg_value

    @classmethod
    def validate(klass, value, arg_name):
        # Allow boolean or empty string
        if isinstance(value, bool) or (isinstance(value, str) and not value):
            logging.debug("value of '%s' extra parameter for '%s' argument should be a string" \
                % (klass.name, arg_name))
            value = arg_name
        elif not isinstance(value, str):
            raise TypeError("Invalid type of '%s' extra parameter for '%s' argument" \
                % (klass.name, arg_name))
        return value

class PasswordParameter(AskParameter):
    """
    Ask for the password argument value if possible and needed.

    The value of this parameter corresponds to the message to display
    when asking the password.

    """
    name = 'password'

    def __call__(self, message, arg_name, arg_value):
        if arg_value:
            return arg_value

        try:
            # Ask for the password
            return msignals.prompt(m18n.n(message), True, True)
        except NotImplementedError:
            return arg_value

class PatternParameter(_ExtraParameter):
    """
    Check if the argument value match a pattern.

    The value of this parameter corresponds to a list of the pattern and
    the message to display if it doesn't match.

    """
    name = 'pattern'

    def __call__(self, arguments, arg_name, arg_value):
        pattern, message = (arguments[0], arguments[1])

        # Use temporarly utf-8 encoded value
        try:
            v = unicode(arg_value, 'utf-8')
        except:
            v = arg_value

        if v and not re.match(pattern, v or '', re.UNICODE):
            raise MoulinetteError(errno.EINVAL, m18n.g('invalid_argument',
                                        arg_name, m18n.n(message)))
        return arg_value

    @staticmethod
    def validate(value, arg_name):
        # Tolerate string type
        if isinstance(value, str):
            logging.warning("value of 'pattern' extra parameter for '%s' argument should be a list" % arg_name)
            value = [value, _("'%s' argument is not matching the pattern") % arg_name]
        elif not isinstance(value, list) or len(value) != 2:
            raise TypeError("Invalid type of 'pattern' extra parameter for '%s' argument" % arg_name)
        return value

class RequiredParameter(_ExtraParameter):
    """
    Check if a required argument is defined or not.

    The value of this parameter must be a boolean which is set to False by
    default.
    """
    name = 'required'

    def __call__(self, required, arg_name, arg_value):
        if required and (arg_value is None or arg_value == ''):
            raise MoulinetteError(errno.EINVAL, m18n.g('argument_required',
                                                       arg_name))
        return arg_value

    @staticmethod
    def validate(value, arg_name):
        if not isinstance(value, bool):
            raise TypeError("Invalid type of 'required' extra parameter for '%s' argument" % arg_name)
        return value

"""
The list of available extra parameters classes. It will keep to this list
order on argument parsing.

"""
extraparameters_list = [ AskParameter, PasswordParameter, RequiredParameter,
                         PatternParameter ]

# Extra parameters argument Parser

class ExtraArgumentParser(object):
    """
    Argument validator and parser for the extra parameters.

    Keyword arguments:
        - iface -- The running interface

    """
    def __init__(self, iface):
        self.iface = iface
        self.extra = OrderedDict()
        self._extra_params = { GLOBAL_ARGUMENT: {} }

        # Append available extra parameters for the current interface
        for klass in extraparameters_list:
            if iface in klass.skipped_iface:
                continue
            self.extra[klass.name] = klass

    def validate(self, arg_name, parameters):
        """
        Validate values of extra parameters for an argument

        Keyword arguments:
            - arg_name -- The argument name
            - parameters -- A dict of extra parameters with their values

        """
        # Iterate over parameters to validate
        for p, v in parameters.items():
            klass = self.extra.get(p, None)
            if not klass:
                # Remove unknown parameters
                del parameters[p]
            else:
                # Validate parameter value
                parameters[p] = klass.validate(v, arg_name)

        return parameters

    def add_argument(self, tid, arg_name, parameters, validate=True):
        """
        Add extra parameters to apply on an action argument

        Keyword arguments:
            - tid -- The tuple identifier of the action or GLOBAL_ARGUMENT
                for global extra parameters
            - arg_name -- The argument name
            - parameters -- A dict of extra parameters with their values
            - validate -- False to not validate extra parameters values

        """
        if validate:
            parameters = self.validate(arg_name, parameters)
        try:
            self._extra_params[tid][arg_name] = parameters
        except KeyError:
            self._extra_params[tid] = OrderedDict({ arg_name: parameters })

    def parse_args(self, tid, args):
        """
        Parse arguments for an action with extra parameters

        Keyword arguments:
            - tid -- The tuple identifier of the action
            - args -- A dict of argument name associated to their value

        """
        extra_args = OrderedDict(self._extra_params.get(GLOBAL_ARGUMENT, {}))
        extra_args.update(self._extra_params.get(tid, {}))

        # Iterate over action arguments with extra parameters
        for arg_name, extra_params in extra_args.items():
            # Iterate over available extra parameters
            for p, cls in self.extra.items():
                try:
                    extra_value = extra_params[p]
                except KeyError:
                    continue
                arg_value = args.get(arg_name, None)

                # Initialize the extra parser
                parser = cls(self.iface)

                # Parse the argument
                if isinstance(arg_value, list):
                    for v in arg_value:
                        r = parser(extra_value, arg_name, v)
                        if r not in arg_value:
                            arg_value.append(r)
                else:
                    arg_value = parser(extra_value, arg_name, arg_value)

                # Update argument value
                if arg_value is not None:
                    args[arg_name] = arg_value
        return args

## Main class ----------------------------------------------------------

def ordered_yaml_load(stream):
    class OrderedLoader(yaml.Loader):
        pass
    OrderedLoader.add_constructor(
        yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
        lambda loader, node: OrderedDict(loader.construct_pairs(node)))
    return yaml.load(stream, OrderedLoader)

class ActionsMap(object):
    """Validate and process actions defined into an actions map

    The actions map defines the features - and their usage - of an
    application which will be available through the moulinette.
    It is composed by categories which contain one or more action(s).
    Moreover, the action can have specific argument(s).

    This class allows to manipulate one or several actions maps
    associated to a namespace. If no namespace is given, it will load
    all available namespaces.

    Keyword arguments:
        - parser -- The BaseActionsMapParser derived class to use for
            parsing the actions map
        - namespaces -- The list of namespaces to use
        - use_cache -- False if it should parse the actions map file
            instead of using the cached one.

    """
    def __init__(self, parser, namespaces=[], use_cache=True):
        self.use_cache = use_cache
        if not issubclass(parser, BaseActionsMapParser):
            raise ValueError("Invalid parser class '%s'" % parser.__name__)
        self._parser_class = parser

        logging.debug("initializing ActionsMap for the interface '%s'" % parser.interface)

        if len(namespaces) == 0:
            namespaces = self.get_namespaces()
        actionsmaps = OrderedDict()

        # Iterate over actions map namespaces
        for n in namespaces:
            logging.debug("loading '%s' actions map namespace" % n)

            if use_cache:
                try:
                    # Attempt to load cache
                    with open('%s/actionsmap/%s.pkl' % (pkg.cachedir, n)) as f:
                        actionsmaps[n] = pickle.load(f)
                # TODO: Switch to python3 and catch proper exception
                except IOError:
                    self.use_cache = False
                    actionsmaps = self.generate_cache(namespaces)
                    break
            else:
                with open('%s/actionsmap/%s.yml' % (pkg.datadir, n)) as f:
                    actionsmaps[n] = ordered_yaml_load(f)

            # Load translations
            # FIXME: Allow several namespaces in m18n
            m18n.load_namespace(n)

        # Generate parsers
        self.extraparser = ExtraArgumentParser(parser.interface)
        self._parser = self._construct_parser(actionsmaps)

    @property
    def parser(self):
        """Return the instance of the interface's actions map parser"""
        return self._parser

    def get_authenticator(self, profile='default'):
        """Get an authenticator instance

        Retrieve the authenticator for the given profile and return a
        new instance.

        Keyword arguments:
            - profile -- An authenticator profile name

        Returns:
            A new _BaseAuthenticator derived instance

        """
        try:
            auth = self.parser.get_global_conf('authenticator', profile)[1]
        except KeyError:
            raise ValueError("Unknown authenticator profile '%s'" % profile)
        else:
            return auth()

    def process(self, args, timeout=0, **kwargs):
        """
        Parse arguments and process the proper action

        Keyword arguments:
            - args -- The arguments to parse
            - timeout -- The time period before failing if the lock
                cannot be acquired for the action
            - **kwargs -- Additional interface arguments

        """
        # Parse arguments
        arguments = vars(self.parser.parse_args(args, **kwargs))

        # Retrieve tid and parse arguments with extra parameters
        tid = arguments.pop('_tid')
        arguments = self.extraparser.parse_args(tid, arguments)

        # Retrieve action information
        namespace, category, action = tid
        func_name = '%s_%s' % (category, action.replace('-', '_'))

        # Lock the moulinette for the namespace
        with MoulinetteLock(namespace, timeout):
            try:
                mod = __import__('%s.%s' % (namespace, category),
                                 globals=globals(), level=0,
                                 fromlist=[func_name])
                func = getattr(mod, func_name)
            except (AttributeError, ImportError):
                raise ImportError("Unable to load function %s.%s/%s"
                        % (namespace, category, func_name))
            else:
                # Load translation and process the action
                m18n.load_namespace(namespace)
                return func(**arguments)

    @staticmethod
    def get_namespaces():
        """
        Retrieve available actions map namespaces

        Returns:
            A list of available namespaces

        """
        namespaces = []

        for f in os.listdir('%s/actionsmap' % pkg.datadir):
            if f.endswith('.yml'):
                namespaces.append(f[:-4])
        return namespaces

    @classmethod
    def generate_cache(klass, namespaces=None):
        """
        Generate cache for the actions map's file(s)

        Keyword arguments:
            - namespaces -- A list of namespaces to generate cache for

        Returns:
            A dict of actions map for each namespaces

        """
        actionsmaps = {}
        if not namespaces:
            namespaces = klass.get_namespaces()

        # Iterate over actions map namespaces
        for n in namespaces:
            logging.debug("generating cache for '%s' actions map namespace" % n)

            # Read actions map from yaml file
            am_file = '%s/actionsmap/%s.yml' % (pkg.datadir, n)
            with open(am_file, 'r') as f:
                actionsmaps[n] = yaml.load(f)

            # Cache actions map into pickle file
            with pkg.open_cachefile('%s.pkl' % n, 'w', subdir='actionsmap') as f:
                pickle.dump(actionsmaps[n], f)

        return actionsmaps

    ## Private methods

    def _construct_parser(self, actionsmaps):
        """
        Construct the parser with the actions map

        Keyword arguments:
            - actionsmaps -- A dict of multi-level dictionnary of
                categories/actions/arguments list for each namespaces

        Returns:
            An interface relevant's parser object

        """
        ## Get extra parameters
        if not self.use_cache:
            validate_extra = True
        else:
            validate_extra = False

        ## Add arguments to the parser
        def _add_arguments(tid, parser, arguments):
            for argn, argp in arguments.items():
                names = top_parser.format_arg_names(argn,
                                                    argp.pop('full', None))
                try: argp['type'] = eval(argp['type'])
                except: pass

                try:
                    extra = argp.pop('extra')
                    arg_dest = (parser.add_argument(*names, **argp)).dest
                    self.extraparser.add_argument(tid, arg_dest, extra,
                                                  validate_extra)
                except KeyError:
                    # No extra parameters
                    parser.add_argument(*names, **argp)

        # Instantiate parser
        top_parser = self._parser_class()

        # Iterate over actions map namespaces
        for n, actionsmap in actionsmaps.items():
            # Retrieve global parameters
            _global = actionsmap.pop('_global', {})

            # -- Parse global configuration
            if 'configuration' in _global:
                # Set global configuration
                top_parser.set_global_conf(_global['configuration'])

            # -- Parse global arguments
            if 'arguments' in _global:
                try:
                    # Get global arguments parser
                    parser = top_parser.add_global_parser()
                except AttributeError:
                    # No parser for global arguments
                    pass
                else:
                    # Add arguments
                    _add_arguments(GLOBAL_ARGUMENT, parser,
                                   _global['arguments'])

            # -- Parse categories
            for cn, cp in actionsmap.items():
                try:
                    actions = cp.pop('actions')
                except KeyError:
                    # Invalid category without actions
                    logging.warning("no actions found in category '%s'" % cn)
                    continue

                # Get category parser
                cat_parser = top_parser.add_category_parser(cn, **cp)

                # -- Parse actions
                for an, ap in actions.items():
                    args = ap.pop('arguments', {})
                    tid = (n, cn, an)
                    try:
                        conf = ap.pop('configuration')
                        _set_conf = lambda p: p.set_conf(tid, conf)
                    except KeyError:
                        # No action configuration
                        _set_conf = lambda p: False

                    try:
                        # Get action parser
                        parser = cat_parser.add_action_parser(an, tid, **ap)
                    except AttributeError:
                        # No parser for the action
                        continue
                    except ValueError as e:
                        logging.warning("cannot add action (%s, %s, %s): %s" % (n, cn, an, e))
                        continue
                    else:
                        # Store action identifier and add arguments
                        parser.set_defaults(_tid=tid)
                        _add_arguments(tid, parser, args)
                        _set_conf(cat_parser)

        return top_parser
<|endoftext|>


<commit_before>from datetime import date
from django.test import TestCase
from django.conf import settings
from mpr import models

class TestProduct(TestCase):
    fixtures = ["mpr_models.json"]

    def setUp(self):
        self.ing1 = models.Ingredient.objects.get(pk=1)
        self.ing2 = models.Ingredient.objects.get(pk=2)
        self.p1 = models.Product.objects.get(pk=1)
        self.p2 = models.Product.objects.get(pk=2)
        self.p3 = models.Product.objects.get(pk=3)

        settings.PRICE_PARAMETERS = {
            "VAT" : 2,
            "prices" : [
                (100, 0.10, 10),
                (200, 0.20, 20),
                (300, 0.30, 30),
                (float('inf'), 0.40, 40),
            ]
        }

    def testDispensingFee(self):
        test_data = [(100, 40), (150, 50), (250, 105), (1000, 440)]

        for price, fee in test_data:
            self.p1.sep = price
            self.assertEquals(self.p1.dispensing_fee, fee)

    def testCostPerUnit(self):
        test_data = [(100, 28), (150, 40), (250, 71), (1000, 288)]

        for price, cost in test_data:
            self.p1.sep = price
            self.assertEquals(self.p1.cost_per_unit, cost)

    def testMaxFee(self):
        test_data = [(100, 140), (150, 200), (250, 355), (1000, 1440)]

        for price, fee in test_data:
            self.p1.sep = price
            self.assertEquals(self.p1.max_fee, fee)

    def testRelatedProducts(self):
        p = self.p1.related_products
        self.assertEquals(len(p), 2)
        self.assertTrue(self.p1 in p)
        self.assertTrue(self.p2 in p)

        p = self.p2.related_products
        self.assertEquals(len(p), 2)
        self.assertTrue(self.p1 in p)
        self.assertTrue(self.p2 in p)

        p = self.p3.related_products
        self.assertEquals(len(p), 1)
        self.assertTrue(self.p3 in p)

class TestProductManager(TestCase):
    fixtures = ["mpr_models.json"]

    def setUp(self):
        self.p1 = models.Product.objects.get(pk=1)
        self.p2 = models.Product.objects.get(pk=2)
        self.p3 = models.Product.objects.get(pk=3)

    def testSearchByIngredient(self):
        ingredients = models.Product.objects.search_by_ingredient("Ingredient 1")
        self.assertEquals(len(ingredients), 3)
        self.assertTrue(self.p1 in ingredients)
        self.assertTrue(self.p2 in ingredients)
        self.assertTrue(self.p3 in ingredients)

        seps = [i.sep for i in ingredients]
        self.assertEquals(seps, sorted(seps))

        ingredients = models.Product.objects.search_by_ingredient("Ingredient 2")
        self.assertEquals(len(ingredients), 2)
        self.assertTrue(self.p1 in ingredients)
        self.assertTrue(self.p2 in ingredients)

        ingredients = models.Product.objects.search_by_ingredient("Ingredien")
        self.assertEquals(len(ingredients), 3)
        self.assertTrue(self.p1 in ingredients)
        self.assertTrue(self.p2 in ingredients)
        self.assertTrue(self.p3 in ingredients)

        ingredients = models.Product.objects.search_by_ingredient("Nothing")
        self.assertEquals(len(ingredients), 0)

    def testSearchByNappi(self):
        p = models.Product.objects.search_by_nappi("111")
        self.assertEquals(len(p), 1)
        self.assertTrue(self.p1 in p)

        p = models.Product.objects.search_by_nappi("0")
        self.assertEquals(len(p), 0)

    def testSearchByProductName(self):
        p = models.Product.objects.search_by_product_name("roduct")
        self.assertEquals(len(p), 3)

        p = models.Product.objects.search_by_product_name("Product 1")
        self.assertEquals(len(p), 1)

class TestLastUpdated(TestCase):
    fixtures = ["mpr_models.json"]

    def testLastUpdated(self):
        lu = models.LastUpdated.objects.last_updated()
        self.assertEquals(lu.update_date, date(2014, 9, 29))

<commit_msg>Fixed models test<commit_after>from datetime import date
from django.test import TestCase
from django.conf import settings
from mpr import models

class TestProduct(TestCase):
    fixtures = ["mpr_models.json"]

    def setUp(self):
        self.ing1 = models.Ingredient.objects.get(pk=1)
        self.ing2 = models.Ingredient.objects.get(pk=2)
        self.p1 = models.Product.objects.get(pk=1)
        self.p2 = models.Product.objects.get(pk=2)
        self.p3 = models.Product.objects.get(pk=3)

        settings.PRICE_PARAMETERS = {
            "VAT" : 2,
            "prices" : [
                (100, 0.10, 10),
                (200, 0.20, 20),
                (300, 0.30, 30),
                (float('inf'), 0.40, 40),
            ]
        }

    def testDispensingFee(self):
        test_data = [(100, 80), (150, 100), (250, 210), (1000, 880)]

        for price, fee in test_data:
            self.p1.sep = price
            self.assertEquals(self.p1.dispensing_fee, fee)

    def testCostPerUnit(self):
        test_data = [(100, 36), (150, 50), (250, 92), (1000, 376)]

        for price, cost in test_data:
            self.p1.sep = price
            self.assertEquals(self.p1.cost_per_unit, cost)

    def testMaxFee(self):
        test_data = [(100, 180), (150, 250), (250, 460), (1000, 1880)]

        for price, fee in test_data:
            self.p1.sep = price
            self.assertEquals(self.p1.max_fee, fee)

    def testRelatedProducts(self):
        p = self.p1.related_products
        self.assertEquals(len(p), 2)
        self.assertTrue(self.p1 in p)
        self.assertTrue(self.p2 in p)

        p = self.p2.related_products
        self.assertEquals(len(p), 2)
        self.assertTrue(self.p1 in p)
        self.assertTrue(self.p2 in p)

        p = self.p3.related_products
        self.assertEquals(len(p), 1)
        self.assertTrue(self.p3 in p)

class TestProductManager(TestCase):
    fixtures = ["mpr_models.json"]

    def setUp(self):
        self.p1 = models.Product.objects.get(pk=1)
        self.p2 = models.Product.objects.get(pk=2)
        self.p3 = models.Product.objects.get(pk=3)

    def testSearchByIngredient(self):
        ingredients = models.Product.objects.search_by_ingredient("Ingredient 1")
        self.assertEquals(len(ingredients), 3)
        self.assertTrue(self.p1 in ingredients)
        self.assertTrue(self.p2 in ingredients)
        self.assertTrue(self.p3 in ingredients)

        seps = [i.sep for i in ingredients]
        self.assertEquals(seps, sorted(seps))

        ingredients = models.Product.objects.search_by_ingredient("Ingredient 2")
        self.assertEquals(len(ingredients), 2)
        self.assertTrue(self.p1 in ingredients)
        self.assertTrue(self.p2 in ingredients)

        ingredients = models.Product.objects.search_by_ingredient("Ingredien")
        self.assertEquals(len(ingredients), 3)
        self.assertTrue(self.p1 in ingredients)
        self.assertTrue(self.p2 in ingredients)
        self.assertTrue(self.p3 in ingredients)

        ingredients = models.Product.objects.search_by_ingredient("Nothing")
        self.assertEquals(len(ingredients), 0)

    def testSearchByNappi(self):
        p = models.Product.objects.search_by_nappi("111")
        self.assertEquals(len(p), 1)
        self.assertTrue(self.p1 in p)

        p = models.Product.objects.search_by_nappi("0")
        self.assertEquals(len(p), 0)

    def testSearchByProductName(self):
        p = models.Product.objects.search_by_product_name("roduct")
        self.assertEquals(len(p), 3)

        p = models.Product.objects.search_by_product_name("Product 1")
        self.assertEquals(len(p), 1)

class TestLastUpdated(TestCase):
    fixtures = ["mpr_models.json"]

    def testLastUpdated(self):
        lu = models.LastUpdated.objects.last_updated()
        self.assertEquals(lu.update_date, date(2014, 9, 29))

<|endoftext|>


<commit_before>#Impot ggame stuff

import random, time
from ggame import App, RectangleAsset, ImageAsset, Sprite, LineStyle, Color, Frame, TextAsset
SCREEN_WIDTH = 0
SCREEN_HEIGHT = 0
a= random.choice('abcdefghijklmnopqrstuvwxyz')
wordList = []
Quest=0
def createWord():
    global wordList
    wordList.append(random.choice('abcdefghijklmnopqrstuvwxyz'))
    print(wordList)
def createWord2():
    global wordList, Quest
    wordList=[]

    for _ in range(10):
        createWord()
    wordList= ("".join(wordList))
    print(wordList)
    Quest=TextAsset(wordList, style='100px Arial')
    print(Quest)
b = list(a)
createWord2()

class FindGame(App):
    def __init__(self, width, height):
        self.width=width
        self.height=height
        super().__init__(width, height)
        black = Color(0, 1)
        noline = LineStyle(0, black)
        asset = ImageAsset("blue.png")
        self.index = 0
        self.point = 0
        self.pointList = []
        self.end=False

        self.timerList=[]
        self.timer=60
        self.currentTime = time.time()
        for x in range(self.width//600 + 1):
            for y in range(self.height//360 + 1):
                Sprite(asset,(x*600, y*360))
        global SCREEN_WIDTH,SCREEN_HEIGHT
        SCREEN_WIDTH = self.width
        SCREEN_HEIGHT = self.height
        for c in 'abcdefghijklmnopqrstuvwxyz':
            FindGame.listenKeyEvent("keydown", c, self.key)
    def makegreensprite(self):
        checker((200+(100*self.index),100))
        if self.index == 10:
            self.point += 10
            self.update()
            print(self.getSpritesbyClass(checker), len(self.getSpritesbyClass(checker)))
            for x in self.getSpritesbyClass(checker)[:]:
                x.destroy()
            for x in self.getSpritesbyClass(quest):
                x.destroy()
                print('fun')
            createWord2()
            quest((612,342))
            print(self.point)
            self.index = 0

        print('Makegreensprite')

    def makegreenspritegoaway(self):
        for x in self.getSpritesbyClass(checker):
            x.destroy()
        print('MakeGreenSpriteGoAway')
        pass
    def key(self,event):
        if self.end==False:
            if event.key == wordList[self.index]:
                self.index += 1
                self.makegreensprite()

        else:
            self.index = 0
            self.point -= 2
            self.update()
            self.makegreenspritegoaway()
            self.makegreenspritegoaway()
            self.makegreenspritegoaway()
            self.makegreenspritegoaway()
            self.makegreenspritegoaway()
            self.makegreenspritegoaway()
            self.makegreenspritegoaway()

    def step(self):
        if time.time()-self.currentTime>1:
            self.currentTime=time.time()
            timerAsset= TextAsset(self.timer, color=Color(0x9acd32, 1))
            self.timer-=1
            if len(self.timerList)>0:
                for x in self.timerList:
                    x.destroy()
            self.timerList=[]
            self.timerList.append(Sprite(timerAsset, (self.width/2, 20)))
        if self.timer<1:
            self.end=True

    def update(self):
        tadaAsset = TextAsset(self.point, style='100px Arial')
        if len(self.pointList)>0:
            for x in self.pointList:
                x.destroy()
        self.pointList=[]
        self.pointList.append(Sprite(tadaAsset, (self.width/2, 50)))

myapp = FindGame(0,0)
startTime = time.time()
class Timer(Sprite):
    def __init__(self, startvalue, position, expired):
        self.startvalue = startvalue
        self.starttime = time.time()
        startasset = TextAsset("{0}".format(startvalue), style='100px Arial')
        super.__init__(startasset,position)
        self.expired = expired
        "{0}".format(startvalue)

    def step(self):
        pass
        if time.timer() > self.starttime+1:
            if self.startvalue == 1:
                self.expire()
            else:
                Timer(self.startvalue-1,self.position,self.expired)
            self.destroy()
class checker(Sprite):
    asset = ImageAsset("green-dot-hi.png")
    width = 50
    height = 50
    def __init__(self, position):
        super().__init__(checker.asset, position)
        self.scale = .05

class quest(Sprite):
    global Quest
    print(Quest)
    asset = Quest
    width = 70
    height = 50
    def __init__(self, position):
        super().__init__(Quest, position)
        quest1 = b

#class questy(quest):

quest((612,342))
#checker((100,100))
#checker((200+(50*myapp.index),100))

def done():
    print('Timer Expired!')

print(Timer)
timer = Timer(60,(150,150), done)
#myapp.run(timer.step)
myapp.update()
myapp.run()<commit_msg>Updated from Brython Server: 6/14/2017 10:48:59 AM<commit_after>#Impot ggame stuff

import random, time
from ggame import App, RectangleAsset, ImageAsset, Sprite, LineStyle, Color, Frame, TextAsset
SCREEN_WIDTH = 0
SCREEN_HEIGHT = 0
a= random.choice('abcdefghijklmnopqrstuvwxyz')
wordList = []
Quest=0
def createWord():
    global wordList
    wordList.append(random.choice('abcdefghijklmnopqrstuvwxyz'))
    print(wordList)
def createWord2():
    global wordList, Quest
    wordList=[]

    for _ in range(10):
        createWord()
    wordList= ("".join(wordList))
    print(wordList)
    Quest=TextAsset(wordList, style='100px Arial')
    print(Quest)
b = list(a)
createWord2()

class FindGame(App):
    def __init__(self, width, height):
        self.width=width
        self.height=height
        super().__init__(width, height)
        black = Color(0, 1)
        noline = LineStyle(0, black)
        asset = ImageAsset("blue.png")
        self.index = 0
        self.point = 0
        self.pointList = []
        self.end=False

        self.timerList=[]
        self.timer=60
        self.currentTime = time.time()
        for x in range(self.width//600 + 1):
            for y in range(self.height//360 + 1):
                Sprite(asset,(x*600, y*360))
        global SCREEN_WIDTH,SCREEN_HEIGHT
        SCREEN_WIDTH = self.width
        SCREEN_HEIGHT = self.height
        for c in 'abcdefghijklmnopqrstuvwxyz':
            FindGame.listenKeyEvent("keydown", c, self.key)
    def makegreensprite(self):
        checker((200+(100*self.index),100))
        if self.index == 10:
            self.point += 10
            self.update()
            print(self.getSpritesbyClass(checker), len(self.getSpritesbyClass(checker)))
            for x in self.getSpritesbyClass(checker)[:]:
                x.destroy()
            for x in self.getSpritesbyClass(quest):
                x.destroy()
                print('fun')
            createWord2()
            quest((612,342))
            print(self.point)
            self.index = 0

        print('Makegreensprite')

    def makegreenspritegoaway(self):
        for x in self.getSpritesbyClass(checker):
            x.destroy()
        print('MakeGreenSpriteGoAway')
        pass
    def key(self,event):
        if self.end==False:
            if event.key == wordList[self.index]:
                self.index += 1
                self.makegreensprite()

            else:
                self.index = 0
                self.point -= 2
                self.update()
                self.makegreenspritegoaway()
                self.makegreenspritegoaway()
                self.makegreenspritegoaway()
                self.makegreenspritegoaway()
                self.makegreenspritegoaway()
                self.makegreenspritegoaway()
                self.makegreenspritegoaway()

    def step(self):
        if self.timer<1:
            self.end=True
            if time.time()-self.currentTime>1:
                self.currentTime=time.time()
                timerAsset= TextAsset(self.timer, color=Color(0x9acd32, 1))
                self.timer-=1
                if len(self.timerList)>0:
                    for x in self.timerList:
                        x.destroy()
                self.timerList=[]
                self.timerList.append(Sprite(timerAsset, (self.width/2, 20)))

    def update(self):
        tadaAsset = TextAsset(self.point, style='100px Arial')
        if len(self.pointList)>0:
            for x in self.pointList:
                x.destroy()
        self.pointList=[]
        self.pointList.append(Sprite(tadaAsset, (self.width/2, 50)))

myapp = FindGame(0,0)
startTime = time.time()
class Timer(Sprite):
    def __init__(self, startvalue, position, expired):
        self.startvalue = startvalue
        self.starttime = time.time()
        startasset = TextAsset("{0}".format(startvalue), style='100px Arial')
        super.__init__(startasset,position)
        self.expired = expired
        "{0}".format(startvalue)

    def step(self):
        pass
        if time.timer() > self.starttime+1:
            if self.startvalue == 1:
                self.expire()
            else:
                Timer(self.startvalue-1,self.position,self.expired)
            self.destroy()
class checker(Sprite):
    asset = ImageAsset("green-dot-hi.png")
    width = 50
    height = 50
    def __init__(self, position):
        super().__init__(checker.asset, position)
        self.scale = .05

class quest(Sprite):
    global Quest
    print(Quest)
    asset = Quest
    width = 70
    height = 50
    def __init__(self, position):
        super().__init__(Quest, position)
        quest1 = b

#class questy(quest):

quest((612,342))
#checker((100,100))
#checker((200+(50*myapp.index),100))

def done():
    print('Timer Expired!')

print(Timer)
timer = Timer(60,(150,150), done)
#myapp.run(timer.step)
myapp.update()
myapp.run()<|endoftext|>


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

"""

  Copyright (c) 2012, Davyd McColl; 2013, Jaime Soffer

   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:

   Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

   Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

   Neither the name of the involved organizations nor the names of its
   contributors may be used to endorse or promote products derived from this
   software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   THE POSSIBILITY OF SUCH DAMAGE.

"""

import PyQt4.QtGui as QtGui
from PyQt4.QtWebKit import QWebPage, QWebSettings
from PyQt4.QtCore import QUrl

# local
from WebView import WebView
from libeilat import log, fix_url

class WebTab(QtGui.QWidget):
    """ Cada tab contiene una página web """
    def __init__(self, browser, netmanager, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.browser = browser

        # layout
        self.grid = QtGui.QGridLayout(self)
        self.grid.setSpacing(0)
        self.grid.setVerticalSpacing(0)
        self.grid.setContentsMargins(0, 0, 0, 0)

        # webkit: la parte que entra a internet
        # aquí se configura, cada tab tiene opciones independientes
        self.webkit = WebView(self)
        self.webkit.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
        self.webkit.linkClicked.connect(self.on_link_click)
        self.webkit.settings().setAttribute(
                QWebSettings.PluginsEnabled, False)
        self.webkit.settings().setAttribute(
                QWebSettings.JavascriptEnabled, False)
        #self.webkit.settings().setAttribute(
        #       QWebSettings.SpatialNavigationEnabled, True)
        #self.webkit.settings().setAttribute(
        #       QWebSettings.DeveloperExtrasEnabled, True)

        # address bar
        self.cmb = QtGui.QComboBox()
        self.cmb.setEditable(True)

        # progress bar
        self.pbar = QtGui.QProgressBar()
        self.pbar.setRange(0, 100)
        self.pbar.setTextVisible(False)
        self.pbar.setVisible(False)
        self.pbar.setMaximumHeight(7)

        self.search_frame = SearchFrame()

        self.statusbar = QtGui.QStatusBar()
        self.statusbar.setVisible(False)
        self.statusbar.setMaximumHeight(25)

        self.grid.addWidget(self.webkit, 1, 0)
        self.grid.setRowStretch(1, 1)
        self.grid.addWidget(self.search_frame, 2, 0)
        self.grid.addWidget(self.cmb, 0, 0)
        self.grid.addWidget(self.pbar, 3, 0)
        self.grid.addWidget(self.statusbar, 4, 0)

        self.webkit.loadStarted.connect(self.show_progress_bar)
        self.webkit.loadFinished.connect(self.load_finished)
        self.webkit.titleChanged.connect(self.set_title)
        self.webkit.loadProgress.connect(self.load_progress)
        self.webkit.urlChanged.connect(
                lambda url: self.cmb.setEditText(url.toString()))
        self.webkit.page().linkHovered.connect(self.on_link_hovered)

        # el contenido de la tab (los datos, no el contenedor)
        page = self.webkit.page()

        page.downloadRequested.connect(
                lambda k: log("D: " + k.url().toString()))
        page.unsupportedContent.connect(
                lambda k: log("U: " + k.url().toString()))
        self.search_frame.search_line.textChanged.connect(self.do_search)

        page.setForwardUnsupportedContent(True)

        def toggle_status():
            """ One-time callback for QShortcut """
            self.statusbar.setVisible(not self.statusbar.isVisible())

        def show_search():
            """ One-time callback for QShortcut """
            self.search_frame.search("")
            self.search_frame.setVisible(True)
            self.search_frame.search_line.setFocus()

        def hide_search():
            """ One-time callback for QShortcut """
            if self.search_frame.isVisible():
                self.search_frame.setVisible(False)
                self.webkit.setFocus()
            else:
                self.webkit.stop()

        def scroll(delta):
            """ One-time callback for QShortcut """
            def ret():
                """ return a lambda to pass argument to callback """
                self.webkit.page().mainFrame().scroll(0, delta)
            return ret

        def zoom(lvl):
            """ One-time callback for QShortcut """
            def ret():
                """ return a lambda to pass argument to callback """
                factor = self.webkit.zoomFactor() + (lvl * 0.25)
                self.webkit.setZoomFactor(factor)
            return ret

        for (shortcut, owner, callback) in [
                ("Ctrl+L", self, self.cmb.setFocus),
                ("Ctrl+J", self.cmb, self.navigate),
                ("F5", self, self.webkit.reload),
                ("R", self, self.webkit.reload),
                ("Alt+Left", self, self.webkit.history().back),
                ("Alt+Right", self, self.webkit.history().forward),
                ("Ctrl+Space", self, toggle_status),
                ("Q", self, self.toggle_script),
                ("J", self, scroll(40)),
                ("K", self, scroll(-40)),
                ("Ctrl+Up", self, zoom(1)),
                ("Ctrl+Down", self, zoom(-1)),
                ("G", self, show_search),
                ("Return", self.search_frame, self.do_search),
                ("Escape", self.search_frame, hide_search)
                ]:
            QtGui.QShortcut(shortcut, owner, callback)

        self.show_hide_message()

        # replace the Network Access Manager (log connections)
        page.setNetworkAccessManager(netmanager)

    def toggle_script(self):
        """ Activa o desactiva javascript, y notifica cambiando el color
        del address bar

        """
        if self.webkit.settings().testAttribute(
                QWebSettings.JavascriptEnabled):
            self.webkit.settings().setAttribute(
                    QWebSettings.JavascriptEnabled, False)
            self.cmb.setStyleSheet("QComboBox { background-color: #fff; }")
        else:
            self.webkit.settings().setAttribute(
                    QWebSettings.JavascriptEnabled, True)
            self.cmb.setStyleSheet("QComboBox { background-color: #ddf; }")

    def load_progress(self, val):
        """ Callback for connection """
        if self.pbar.isVisible():
            self.pbar.setValue(val)

    # connect (en constructor)
    def show_progress_bar(self):
        """ Callback for connection """
        self.pbar.setValue(0)
        self.pbar.setVisible(True)

    # connect (en constructor)
    def load_finished(self, success):
        """ Callback for connection """
        self.pbar.setVisible(False)
        if self.cmb.hasFocus():
            self.webkit.setFocus()
        if not success:
            print "loadFinished: failed"

    # connect (en constructor)
    def on_link_click(self, qurl):
        """ Callback for connection. Reads the 'paste' attribute from
        the extended QWebView to know if a middle click requested open
        on new tab.

        """
        if self.webkit.paste:
            self.browser.add_tab(qurl)
        else:
            self.navigate(qurl)

    # connect (en constructor)
    def on_link_hovered(self, link, title, _):
        """ The mouse is over an image or link. Does it have href?
        Does it have a title? Display on status bar.

        """
        if link or title:
            if title and not link:
                self.statusbar.showMessage(title)
            elif link and not title:
                self.statusbar.showMessage(link)
            elif link and title:
                self.statusbar.showMessage("%s (%s)" % (title, link))
        else:
            self.show_hide_message()

    # en constructor
    def show_hide_message(self):
        """ Shown in status bar when nothing is happening """
        message = "(press %s to hide this)"
        self.statusbar.showMessage(message)

    # action (en register_actions)
    def navigate(self, url = None):
        """ Send this tab to the url. If 'url' is already a QUrl (for example
        if it comes from a href click), just send it. Otherwise, check if the
        "url" is actually one, partial or otherwise; if it's not, construct
        a web search.

        If 'url' is None, extract it directly from the address bar.

        """
        if isinstance(url, QUrl):
            qurl = url
        else:
            if not url:
                url = unicode(self.cmb.currentText())
            qurl = fix_url(url)
        self.set_title("Loading...")
        self.webkit.load(qurl)
        self.webkit.setFocus()

    # connect (en constructor)
    def set_title(self, title):
        """ Go upwards to the web browser's tab widget and set this
        tab's title
        """
        if self.browser:
            self.browser.tab_widget.setTabText(
                    self.browser.tab_widget.indexOf(self), title[:40])

    # connection in constructor and action
    def do_search(self, search = None):
        """ Find text on the currently loaded web page. If no text
        is provided, it's extracted from the search widget.

        """
        if search is None:
            search = self.search_frame.search()
        self.webkit.findText(search, QWebPage.FindWrapsAroundDocument)

class SearchFrame(QtGui.QFrame):
    """ A frame with a label and a text entry. The text is provided to
    the application upwards to perform in-page search.

    """

    def __init__(self):
        super(SearchFrame, self).__init__()

        self.search_grid = QtGui.QGridLayout(self)
        self.search_grid.setSpacing(0)
        self.search_grid.setContentsMargins(0, 0, 0, 0)
        self.label = QtGui.QLabel("Find in page:")
        self.search_line = QtGui.QLineEdit()
        self.search_grid.addWidget(self.label, 0, 0)
        self.search_grid.addWidget(self.search_line, 0, 1)
        self.setVisible(False)

    def search(self, text = None):
        """ Sets or gets the content of the line edit, depending on
        if it was called with or without arguments.

        """
        if text:
            self.search_line.setText(text)
        else:
            return self.search_line.text()
<commit_msg>linting; overall, 98.7% with pylint, as good as it gets without heavy refactor<commit_after># -*- coding: utf-8 -*-

"""

  Copyright (c) 2012, Davyd McColl; 2013, Jaime Soffer

   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:

   Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

   Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

   Neither the name of the involved organizations nor the names of its
   contributors may be used to endorse or promote products derived from this
   software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   THE POSSIBILITY OF SUCH DAMAGE.

"""

import PyQt4.QtGui as QtGui
from PyQt4.QtWebKit import QWebPage, QWebSettings
from PyQt4.QtCore import QUrl

# local
from WebView import WebView
from libeilat import log, fix_url

class WebTab(QtGui.QWidget):
    """ Cada tab contiene una página web """
    def __init__(self, browser, netmanager, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.browser = browser

        # webkit: la parte que entra a internet
        # aquí se configura, cada tab tiene opciones independientes
        self.webkit = WebView(self)
        self.webkit.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
        self.webkit.linkClicked.connect(self.on_link_click)
        self.webkit.settings().setAttribute(
                QWebSettings.PluginsEnabled, False)
        self.webkit.settings().setAttribute(
                QWebSettings.JavascriptEnabled, False)
        #self.webkit.settings().setAttribute(
        #       QWebSettings.SpatialNavigationEnabled, True)
        #self.webkit.settings().setAttribute(
        #       QWebSettings.DeveloperExtrasEnabled, True)

        # address bar
        self.cmb = QtGui.QComboBox()
        self.cmb.setEditable(True)

        # progress bar
        self.pbar = QtGui.QProgressBar()
        self.pbar.setRange(0, 100)
        self.pbar.setTextVisible(False)
        self.pbar.setVisible(False)
        self.pbar.setMaximumHeight(7)

        self.search_frame = SearchFrame()

        self.statusbar = QtGui.QStatusBar()
        self.statusbar.setVisible(False)
        self.statusbar.setMaximumHeight(25)

        self.webkit.loadStarted.connect(self.show_progress_bar)
        self.webkit.loadFinished.connect(self.load_finished)
        self.webkit.titleChanged.connect(self.set_title)
        self.webkit.loadProgress.connect(self.load_progress)
        self.webkit.urlChanged.connect(
                lambda url: self.cmb.setEditText(url.toString()))
        self.webkit.page().linkHovered.connect(self.on_link_hovered)

        # el contenido de la tab (los datos, no el contenedor)
        page = self.webkit.page()

        page.downloadRequested.connect(
                lambda k: log("D: " + k.url().toString()))
        page.unsupportedContent.connect(
                lambda k: log("U: " + k.url().toString()))
        self.search_frame.search_line.textChanged.connect(self.do_search)

        page.setForwardUnsupportedContent(True)

        def toggle_status():
            """ One-time callback for QShortcut """
            self.statusbar.setVisible(not self.statusbar.isVisible())

        def show_search():
            """ One-time callback for QShortcut """
            self.search_frame.search("")
            self.search_frame.setVisible(True)
            self.search_frame.search_line.setFocus()

        def hide_search():
            """ One-time callback for QShortcut """
            if self.search_frame.isVisible():
                self.search_frame.setVisible(False)
                self.webkit.setFocus()
            else:
                self.webkit.stop()

        def scroll(delta):
            """ One-time callback for QShortcut """
            def ret():
                """ return a lambda to pass argument to callback """
                self.webkit.page().mainFrame().scroll(0, delta)
            return ret

        def zoom(lvl):
            """ One-time callback for QShortcut """
            def ret():
                """ return a lambda to pass argument to callback """
                factor = self.webkit.zoomFactor() + (lvl * 0.25)
                self.webkit.setZoomFactor(factor)
            return ret

        for (shortcut, owner, callback) in [
                ("Ctrl+L", self, self.cmb.setFocus),
                ("Ctrl+J", self.cmb, self.navigate),
                ("F5", self, self.webkit.reload),
                ("R", self, self.webkit.reload),
                ("Alt+Left", self, self.webkit.history().back),
                ("Alt+Right", self, self.webkit.history().forward),
                ("Ctrl+Space", self, toggle_status),
                ("Q", self, self.toggle_script),
                ("J", self, scroll(40)),
                ("K", self, scroll(-40)),
                ("Ctrl+Up", self, zoom(1)),
                ("Ctrl+Down", self, zoom(-1)),
                ("G", self, show_search),
                ("Return", self.search_frame, self.do_search),
                ("Escape", self.search_frame, hide_search)
                ]:
            QtGui.QShortcut(shortcut, owner, callback)

        # layout
        grid = QtGui.QGridLayout(self)
        grid.setSpacing(0)
        grid.setVerticalSpacing(0)
        grid.setContentsMargins(0, 0, 0, 0)

        grid.addWidget(self.webkit, 1, 0)
        grid.setRowStretch(1, 1)
        grid.addWidget(self.search_frame, 2, 0)
        grid.addWidget(self.cmb, 0, 0)
        grid.addWidget(self.pbar, 3, 0)
        grid.addWidget(self.statusbar, 4, 0)

        # replace the Network Access Manager (log connections)
        page.setNetworkAccessManager(netmanager)

    def toggle_script(self):
        """ Activa o desactiva javascript, y notifica cambiando el color
        del address bar

        """
        if self.webkit.settings().testAttribute(
                QWebSettings.JavascriptEnabled):
            self.webkit.settings().setAttribute(
                    QWebSettings.JavascriptEnabled, False)
            self.cmb.setStyleSheet("QComboBox { background-color: #fff; }")
        else:
            self.webkit.settings().setAttribute(
                    QWebSettings.JavascriptEnabled, True)
            self.cmb.setStyleSheet("QComboBox { background-color: #ddf; }")

    def load_progress(self, val):
        """ Callback for connection """
        if self.pbar.isVisible():
            self.pbar.setValue(val)

    # connect (en constructor)
    def show_progress_bar(self):
        """ Callback for connection """
        self.pbar.setValue(0)
        self.pbar.setVisible(True)

    # connect (en constructor)
    def load_finished(self, success):
        """ Callback for connection """
        self.pbar.setVisible(False)
        if self.cmb.hasFocus():
            self.webkit.setFocus()
        if not success:
            print "loadFinished: failed"

    # connect (en constructor)
    def on_link_click(self, qurl):
        """ Callback for connection. Reads the 'paste' attribute from
        the extended QWebView to know if a middle click requested open
        on new tab.

        """
        if self.webkit.paste:
            self.browser.add_tab(qurl)
        else:
            self.navigate(qurl)

    # connect (en constructor)
    def on_link_hovered(self, link, title, _):
        """ The mouse is over an image or link. Does it have href?
        Does it have a title? Display on status bar.

        """
        if link or title:
            if title and not link:
                self.statusbar.showMessage(title)
            elif link and not title:
                self.statusbar.showMessage(link)
            elif link and title:
                self.statusbar.showMessage("%s (%s)" % (title, link))
        else:
            self.statusbar.showMessage("")

    # en constructor
    def show_hide_message(self):
        """ Shown in status bar when nothing is happening """
        message = "(press %s to hide this)"
        self.statusbar.showMessage(message)

    # action (en register_actions)
    def navigate(self, url = None):
        """ Send this tab to the url. If 'url' is already a QUrl (for example
        if it comes from a href click), just send it. Otherwise, check if the
        "url" is actually one, partial or otherwise; if it's not, construct
        a web search.

        If 'url' is None, extract it directly from the address bar.

        """
        if isinstance(url, QUrl):
            qurl = url
        else:
            if not url:
                url = unicode(self.cmb.currentText())
            qurl = fix_url(url)
        self.set_title("Loading...")
        self.webkit.load(qurl)
        self.webkit.setFocus()

    # connect (en constructor)
    def set_title(self, title):
        """ Go upwards to the web browser's tab widget and set this
        tab's title
        """
        if self.browser:
            self.browser.tab_widget.setTabText(
                    self.browser.tab_widget.indexOf(self), title[:40])

    # connection in constructor and action
    def do_search(self, search = None):
        """ Find text on the currently loaded web page. If no text
        is provided, it's extracted from the search widget.

        """
        if search is None:
            search = self.search_frame.search()
        self.webkit.findText(search, QWebPage.FindWrapsAroundDocument)

class SearchFrame(QtGui.QFrame):
    """ A frame with a label and a text entry. The text is provided to
    the application upwards to perform in-page search.

    """

    def __init__(self):
        super(SearchFrame, self).__init__()

        self.search_grid = QtGui.QGridLayout(self)
        self.search_grid.setSpacing(0)
        self.search_grid.setContentsMargins(0, 0, 0, 0)
        self.label = QtGui.QLabel("Find in page:")
        self.search_line = QtGui.QLineEdit()
        self.search_grid.addWidget(self.label, 0, 0)
        self.search_grid.addWidget(self.search_line, 0, 1)
        self.setVisible(False)

    def search(self, text = None):
        """ Sets or gets the content of the line edit, depending on
        if it was called with or without arguments.

        """
        if text:
            self.search_line.setText(text)
        else:
            return self.search_line.text()
<|endoftext|>


<commit_before>import threading
import time

#This is what gets spawned by the server when it receives a connection.

class worker(threading.Thread):
    def __init__(self, sock):
        threading.Thread.__init__(self)
        self.sock = sock

    def run(self):

        print "Worker thread %s has started." % threading.current_thread()
        #print threading.current_thread()

        data = self.sock.recv(1024)
        print "Received [%s]" % data

        self.sock.send("This note is from the worker thread %s on the server!" % threading.current_thread())
        time.sleep(600)

        self.sock.close()<commit_msg>minor cleanup<commit_after>#!/usr/bin/env python

import threading
import time

#This is what gets spawned by the server when it receives a connection.
class worker(threading.Thread):
    def __init__(self, sock):
        threading.Thread.__init__(self)
        self.sock = sock

    def run(self):
        print "Worker thread %s has started." % threading.current_thread()

        data = self.sock.recv(1024)
        print "Received [%s]" % data

        self.sock.send("This note is from the worker thread %s on the server!" % threading.current_thread())

        self.sock.close()<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding:utf-8 -*-

#
# The most stupid IRC bot
#

import socket
from time import ctime
import feedparser

# Configuration
# -------------

server = "irc.freenode.net"
channel = "#zdroid"
nick = "XDroid"

# Connect to server
# -----------------

irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
irc.connect((server, 6667))
irc.send("USER " + nick + " " + nick + " " + nick + " :" + nick + "\n")
irc.send("NICK :" + nick + "\n")
irc.send("JOIN :" + channel + "\n")

# Commands
# --------

while 1:
  msg = irc.recv(2048).strip("\n\r")
  print(msg)

  if msg.find("PING :") != -1:
    irc.send("PONG :pingis\n")

  if msg.find("+hi") != -1:
    irc.send("PRIVMSG " + channel + " :Hi!\n")

  if msg.find("+time") != -1:
    irc.send("PRIVMSG " + channel + " :It's " + ctime() + ".\n")

  if msg.find("+channel") != -1:
    irc.send("PRIVMSG " + channel + " :We're on " + channel + ".\n")

  if msg.find("+pi") != -1:
    irc.send("PRIVMSG " + channel + " :The value of pi is 3.14159265359.\n")

  if msg.find("+note") != -1:
    irc.send("NOTICE " + channel + " :Something failed. Sorry.\n")

  if msg.find("+tux") != -1:
    irc.send("PRIVMSG " + channel + " :    .--.\n")
    irc.send("PRIVMSG " + channel + " :   |o o |\n")
    irc.send("PRIVMSG " + channel + " :   |:_/ |\n")
    irc.send("PRIVMSG " + channel + " :  //   \ \ \n")
    irc.send("PRIVMSG " + channel + " : (|     | )\n")
    irc.send("PRIVMSG " + channel + " :/^\     /^\ \n")
    irc.send("PRIVMSG " + channel + " :\___)-(___/\n")

  if msg.find("+feed") != -1:
    url = "http://zdroid.roon.io/feed"
    for i in range(1,5):
      irc.send("PRIVMSG " + channel + " :" +
               feedparser.parse(url)["items"][i].title + " " +
               feedparser.parse(url)["items"][i].link + "\n")

  if msg.find("+quit") != -1:
    irc.send("QUIT\n")
    exit()

  if msg.find("+rcn") != -1:
    irc.send("QUIT\n")
    irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    irc.connect((server, 6667))
    irc.send("USER " + nick + " " + nick + " " + nick + " :" + nick + "\n")
    irc.send("NICK :" + nick + "\n")
    irc.send("JOIN :" + channel + "\n")<commit_msg>Fix description<commit_after>#!/usr/bin/env python
# -*- coding:utf-8 -*-

#
# Potentially good IRC bot
#

import socket
from time import ctime
import feedparser

# Configuration
# -------------

server = "irc.freenode.net"
channel = "#zdroid"
nick = "XDroid"

# Connect to server
# -----------------

irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
irc.connect((server, 6667))
irc.send("USER " + nick + " " + nick + " " + nick + " :" + nick + "\n")
irc.send("NICK :" + nick + "\n")
irc.send("JOIN :" + channel + "\n")

# Commands
# --------

while 1:
  msg = irc.recv(2048).strip("\n\r")
  print(msg)

  if msg.find("PING :") != -1:
    irc.send("PONG :pingis\n")

  if msg.find("+hi") != -1:
    irc.send("PRIVMSG " + channel + " :Hi!\n")

  if msg.find("+time") != -1:
    irc.send("PRIVMSG " + channel + " :It's " + ctime() + ".\n")

  if msg.find("+channel") != -1:
    irc.send("PRIVMSG " + channel + " :We're on " + channel + ".\n")

  if msg.find("+pi") != -1:
    irc.send("PRIVMSG " + channel + " :The value of pi is 3.14159265359.\n")

  if msg.find("+note") != -1:
    irc.send("NOTICE " + channel + " :Something failed. Sorry.\n")

  if msg.find("+tux") != -1:
    irc.send("PRIVMSG " + channel + " :    .--.\n")
    irc.send("PRIVMSG " + channel + " :   |o o |\n")
    irc.send("PRIVMSG " + channel + " :   |:_/ |\n")
    irc.send("PRIVMSG " + channel + " :  //   \ \ \n")
    irc.send("PRIVMSG " + channel + " : (|     | )\n")
    irc.send("PRIVMSG " + channel + " :/^\     /^\ \n")
    irc.send("PRIVMSG " + channel + " :\___)-(___/\n")

  if msg.find("+feed") != -1:
    url = "http://zdroid.roon.io/feed"
    for i in range(1,5):
      irc.send("PRIVMSG " + channel + " :" +
               feedparser.parse(url)["items"][i].title + " " +
               feedparser.parse(url)["items"][i].link + "\n")

  if msg.find("+quit") != -1:
    irc.send("QUIT\n")
    exit()

  if msg.find("+rcn") != -1:
    irc.send("QUIT\n")
    irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    irc.connect((server, 6667))
    irc.send("USER " + nick + " " + nick + " " + nick + " :" + nick + "\n")
    irc.send("NICK :" + nick + "\n")
    irc.send("JOIN :" + channel + "\n")<|endoftext|>


<commit_before>print """
The terminal code didn't work, so I just used github that is on my desk and manually changed everything.
Hopefully I can use terminal next time to do this."""
<commit_msg>made some changes<commit_after>print """
Now i'm using the terminal. let's hope this works!"""
<|endoftext|>