<commit_before># -*- coding: utf-8 -*-
from unicodedata import normalize
from .compat import asyncio
from . import tags
import configparser
import importlib
import functools
import textwrap
import logging
import os
import re

try:
    BaseString = unicode
except NameError:  # pragma: no cover
    BaseString = str

def slugify(value):
    if not isinstance(value, BaseString):  # pragma: no cover
        value = value.decode('utf8', 'ignore')
    value = normalize('NFKD', value)
    value = value.encode('ascii', 'ignore').decode('ascii')
    value = re.sub('[^\w\s\.-]', '', value).strip().lower()
    value = re.sub('[-\s]+', '-', value)
    value = re.sub('-*\.+-*', '.', value)
    return value

class IrcString(BaseString):
    """Argument wrapper"""

    @property
    def nick(self):
        """return nick name:

        .. code-block:: py

            >>> print(IrcString('foo').nick)
            foo
            >>> print(IrcString('foo!user@host').nick)
            foo
            >>> IrcString('#foo').nick is None
            True
            >>> IrcString('irc.freenode.net').nick is None
            True
        """
        if '!' in self:
            return self.split('!', 1)[0]
        if not self.is_channel and not self.is_server:
            return self

    @property
    def lnick(self):
        """return nick name in lowercase:

        .. code-block:: py

            >>> print(IrcString('Foo').lnick)
            foo
        """
        nick = self.nick
        if nick:
            return nick.lower()

    @property
    def host(self):
        if '!' in self:
            return self.split('!', 1)[1]

    @property
    def is_user(self):
        return '!' in self

    @property
    def is_channel(self):
        """return True if the string is a channel:

        .. code-block:: py

            >>> IrcString('#channel').is_channel
            True
            >>> IrcString('&channel').is_channel
            True
        """
        return self.startswith(('#', '&'))

    @property
    def is_server(self):
        """return True if the string is a server:

        .. code-block:: py

            >>> IrcString('irc.freenode.net').is_server
            True
        """
        if self == '*':
            return True
        return '!' not in self and '.' in self

    @property
    def is_nick(self):
        """return True if the string is a nickname:

        .. code-block:: py

            >>> IrcString('foo').is_nick
            True
        """
        return not (self.is_server or self.is_channel)

    @property
    def tagdict(self):
        """return a dict converted from this string interpreted as a tag-string

        .. code-block:: py

            >>> from pprint import pprint
            >>> dict_ = IrcString('aaa=bbb;ccc;example.com/ddd=eee').tagdict
            >>> pprint({str(k): str(v) for k, v in dict_.items()})
            {'aaa': 'bbb', 'ccc': 'None', 'example.com/ddd': 'eee'}
        """
        tagdict = getattr(self, '_tagdict', None)
        if tagdict is None:
            try:
                self._tagdict = tags.decode(self)
            except ValueError:
                self._tagdict = {}
        return self._tagdict

STRIPPED_CHARS = '\t '

def split_message(message, max_length):
    """Split long messages"""
    if len(message) > max_length:
        for message in textwrap.wrap(message, max_length):
            yield message
    else:
        yield message.rstrip(STRIPPED_CHARS)

class Config(dict):
    """Simple dict wrapper:

    .. code-block:: python

        >>> c = Config(dict(a=True))
        >>> c.a
        True
    """

    def __getattr__(self, attr):
        return self[attr]

def parse_config(main_section, *filenames):
    """parse config files"""
    filename = filenames[-1]
    filename = os.path.abspath(filename)
    here = os.path.dirname(filename)
    defaults = dict(here=here, hash='#')
    defaults['#'] = '#'

    config = configparser.ConfigParser(
        defaults, allow_no_value=False,
        interpolation=configparser.ExtendedInterpolation(),
    )
    config.optionxform = str
    config.read([os.path.expanduser('~/.irc3/passwd.ini')] + list(filenames))

    value = {}
    for s in config.sections():
        items = {}
        for k, v in config.items(s):
            if '\n' in v:
                v = as_list(v)
            elif v.isdigit():
                v = int(v)
            elif v.lstrip('.').isdigit():
                v = float(v)
            elif v in ('true', 'false'):
                v = v == 'true' and True or False
            items[k] = v
        if s == main_section:
            value.update(items)
        else:
            for k in ('here', 'config'):
                items.pop(k, '')
            value[s] = items
    value.update(defaults)
    value['configfiles'] = filenames
    return value

def extract_config(config, prefix):
    """return all keys with the same prefix without the prefix"""
    prefix = prefix.strip('.') + '.'
    plen = len(prefix)
    value = {}
    for k, v in config.items():
        if k.startswith(prefix):
            value[k[plen:]] = v
    return value

def as_list(value):
    """clever string spliting:

    .. code-block:: python

        >>> print(as_list('value'))
        ['value']
        >>> print(as_list('v1 v2'))
        ['v1', 'v2']
        >>> print(as_list(None))
        []
        >>> print(as_list(['v1']))
        ['v1']
    """
    if isinstance(value, (list, tuple)):
        return value
    if not value:
        return []
    for c in '\n ':
        if c in value:
            value = value.split(c)
            return [v.strip() for v in value if v.strip()]
    return [value]

def as_channel(value):
    """Always return a channel name:

    .. code-block:: python

        >>> print(as_channel('chan'))
        #chan
        >>> print(as_channel('#chan'))
        #chan
        >>> print(as_channel('&chan'))
        &chan
    """
    if not value.startswith(('#', '&')):
        return '#' + value
    return value

def parse_modes(modes, targets=None, noargs=''):
    """Parse channel modes:

    .. code-block:: python

        >>> parse_modes('+c-n', noargs='cn')
        [('+', 'c', None), ('-', 'n', None)]
        >>> parse_modes('+c-v', ['gawel'], noargs='c')
        [('+', 'c', None), ('-', 'v', 'gawel')]
    """
    if not targets:
        targets = []
    cleaned = []
    for mode in modes:
        if mode in '-+':
            char = mode
            continue
        target = targets.pop(0) if mode not in noargs else None
        cleaned.append((char, mode, target))
    return cleaned

def wraps_with_context(func, context):
    """Return a wrapped partial(func, context)"""
    wrapped = functools.partial(func, context)
    wrapped = functools.wraps(func)(wrapped)
    if asyncio.iscoroutinefunction(func):
        wrapped = asyncio.coroutine(wrapped)
    return wrapped

def maybedotted(name):
    """Resolve dotted names:

    .. code-block:: python

        >>> maybedotted('irc3.config')
        <module 'irc3.config' from '...'>
        >>> maybedotted('irc3.utils.IrcString')
        <class 'irc3.utils.IrcString'>

    ..
    """
    if not name:
        raise LookupError(
            'Not able to resolve %s' % name)
    if not hasattr(name, '__name__'):
        try:
            mod = importlib.import_module(name)
        except ImportError:
            attr = None
            if '.' in name:
                names = name.split('.')
                attr = names.pop(-1)
                try:
                    mod = maybedotted('.'.join(names))
                except LookupError:
                    attr = None
                else:
                    attr = getattr(mod, attr, None)
            if attr is not None:
                return attr
            raise LookupError(
                'Not able to resolve %s' % name)
        else:
            return mod
    return name

class Handler(logging.Handler):

    def __init__(self, bot, *targets):
        super(Handler, self).__init__()
        self.bot = bot
        self.targets = targets

    def yield_records(self, record):
        for record in self.format(record).split('\n'):
            for t in self.targets:
                yield t, record

    def emit(self, record):
        self.bot.call_many('privmsg', self.yield_records(record))

class Logger(logging.getLoggerClass()):
    """Replace the default logger to add a set_irc_targets() method"""

    def set_irc_targets(self, bot, *targets):
        """Add a irc Handler using bot and log to targets (can be nicks or
        channels:

        ..
            >>> bot = None

        .. code-block:: python

            >>> log = logging.getLogger('irc.mymodule')
            >>> log.set_irc_targets(bot, '#chan', 'admin')
        """
        # get formatter initialized by config (usualy on a NullHandler)
        l = logging.getLogger('irc')
        formatter = l.handlers[0].formatter
        # add a handler for the sub logger
        handler = Handler(bot, *targets)
        handler.setFormatter(formatter)
        self.addHandler(handler)

logging.setLoggerClass(Logger)
<commit_msg>parse_config: fix parsing of floats<commit_after># -*- coding: utf-8 -*-
from unicodedata import normalize
from .compat import asyncio
from . import tags
import configparser
import importlib
import functools
import textwrap
import logging
import os
import re

try:
    BaseString = unicode
except NameError:  # pragma: no cover
    BaseString = str

def slugify(value):
    if not isinstance(value, BaseString):  # pragma: no cover
        value = value.decode('utf8', 'ignore')
    value = normalize('NFKD', value)
    value = value.encode('ascii', 'ignore').decode('ascii')
    value = re.sub('[^\w\s\.-]', '', value).strip().lower()
    value = re.sub('[-\s]+', '-', value)
    value = re.sub('-*\.+-*', '.', value)
    return value

class IrcString(BaseString):
    """Argument wrapper"""

    @property
    def nick(self):
        """return nick name:

        .. code-block:: py

            >>> print(IrcString('foo').nick)
            foo
            >>> print(IrcString('foo!user@host').nick)
            foo
            >>> IrcString('#foo').nick is None
            True
            >>> IrcString('irc.freenode.net').nick is None
            True
        """
        if '!' in self:
            return self.split('!', 1)[0]
        if not self.is_channel and not self.is_server:
            return self

    @property
    def lnick(self):
        """return nick name in lowercase:

        .. code-block:: py

            >>> print(IrcString('Foo').lnick)
            foo
        """
        nick = self.nick
        if nick:
            return nick.lower()

    @property
    def host(self):
        if '!' in self:
            return self.split('!', 1)[1]

    @property
    def is_user(self):
        return '!' in self

    @property
    def is_channel(self):
        """return True if the string is a channel:

        .. code-block:: py

            >>> IrcString('#channel').is_channel
            True
            >>> IrcString('&channel').is_channel
            True
        """
        return self.startswith(('#', '&'))

    @property
    def is_server(self):
        """return True if the string is a server:

        .. code-block:: py

            >>> IrcString('irc.freenode.net').is_server
            True
        """
        if self == '*':
            return True
        return '!' not in self and '.' in self

    @property
    def is_nick(self):
        """return True if the string is a nickname:

        .. code-block:: py

            >>> IrcString('foo').is_nick
            True
        """
        return not (self.is_server or self.is_channel)

    @property
    def tagdict(self):
        """return a dict converted from this string interpreted as a tag-string

        .. code-block:: py

            >>> from pprint import pprint
            >>> dict_ = IrcString('aaa=bbb;ccc;example.com/ddd=eee').tagdict
            >>> pprint({str(k): str(v) for k, v in dict_.items()})
            {'aaa': 'bbb', 'ccc': 'None', 'example.com/ddd': 'eee'}
        """
        tagdict = getattr(self, '_tagdict', None)
        if tagdict is None:
            try:
                self._tagdict = tags.decode(self)
            except ValueError:
                self._tagdict = {}
        return self._tagdict

STRIPPED_CHARS = '\t '

def split_message(message, max_length):
    """Split long messages"""
    if len(message) > max_length:
        for message in textwrap.wrap(message, max_length):
            yield message
    else:
        yield message.rstrip(STRIPPED_CHARS)

class Config(dict):
    """Simple dict wrapper:

    .. code-block:: python

        >>> c = Config(dict(a=True))
        >>> c.a
        True
    """

    def __getattr__(self, attr):
        return self[attr]

def parse_config(main_section, *filenames):
    """parse config files"""
    filename = filenames[-1]
    filename = os.path.abspath(filename)
    here = os.path.dirname(filename)
    defaults = dict(here=here, hash='#')
    defaults['#'] = '#'

    config = configparser.ConfigParser(
        defaults, allow_no_value=False,
        interpolation=configparser.ExtendedInterpolation(),
    )
    config.optionxform = str
    config.read([os.path.expanduser('~/.irc3/passwd.ini')] + list(filenames))

    value = {}
    for s in config.sections():
        items = {}
        for k, v in config.items(s):
            if '\n' in v:
                v = as_list(v)
            elif v.isdigit():
                v = int(v)
            elif v.replace('.', '').isdigit() and v.count('.') == 1:
                v = float(v)
            elif v in ('true', 'false'):
                v = v == 'true' and True or False
            items[k] = v
        if s == main_section:
            value.update(items)
        else:
            for k in ('here', 'config'):
                items.pop(k, '')
            value[s] = items
    value.update(defaults)
    value['configfiles'] = filenames
    return value

def extract_config(config, prefix):
    """return all keys with the same prefix without the prefix"""
    prefix = prefix.strip('.') + '.'
    plen = len(prefix)
    value = {}
    for k, v in config.items():
        if k.startswith(prefix):
            value[k[plen:]] = v
    return value

def as_list(value):
    """clever string spliting:

    .. code-block:: python

        >>> print(as_list('value'))
        ['value']
        >>> print(as_list('v1 v2'))
        ['v1', 'v2']
        >>> print(as_list(None))
        []
        >>> print(as_list(['v1']))
        ['v1']
    """
    if isinstance(value, (list, tuple)):
        return value
    if not value:
        return []
    for c in '\n ':
        if c in value:
            value = value.split(c)
            return [v.strip() for v in value if v.strip()]
    return [value]

def as_channel(value):
    """Always return a channel name:

    .. code-block:: python

        >>> print(as_channel('chan'))
        #chan
        >>> print(as_channel('#chan'))
        #chan
        >>> print(as_channel('&chan'))
        &chan
    """
    if not value.startswith(('#', '&')):
        return '#' + value
    return value

def parse_modes(modes, targets=None, noargs=''):
    """Parse channel modes:

    .. code-block:: python

        >>> parse_modes('+c-n', noargs='cn')
        [('+', 'c', None), ('-', 'n', None)]
        >>> parse_modes('+c-v', ['gawel'], noargs='c')
        [('+', 'c', None), ('-', 'v', 'gawel')]
    """
    if not targets:
        targets = []
    cleaned = []
    for mode in modes:
        if mode in '-+':
            char = mode
            continue
        target = targets.pop(0) if mode not in noargs else None
        cleaned.append((char, mode, target))
    return cleaned

def wraps_with_context(func, context):
    """Return a wrapped partial(func, context)"""
    wrapped = functools.partial(func, context)
    wrapped = functools.wraps(func)(wrapped)
    if asyncio.iscoroutinefunction(func):
        wrapped = asyncio.coroutine(wrapped)
    return wrapped

def maybedotted(name):
    """Resolve dotted names:

    .. code-block:: python

        >>> maybedotted('irc3.config')
        <module 'irc3.config' from '...'>
        >>> maybedotted('irc3.utils.IrcString')
        <class 'irc3.utils.IrcString'>

    ..
    """
    if not name:
        raise LookupError(
            'Not able to resolve %s' % name)
    if not hasattr(name, '__name__'):
        try:
            mod = importlib.import_module(name)
        except ImportError:
            attr = None
            if '.' in name:
                names = name.split('.')
                attr = names.pop(-1)
                try:
                    mod = maybedotted('.'.join(names))
                except LookupError:
                    attr = None
                else:
                    attr = getattr(mod, attr, None)
            if attr is not None:
                return attr
            raise LookupError(
                'Not able to resolve %s' % name)
        else:
            return mod
    return name

class Handler(logging.Handler):

    def __init__(self, bot, *targets):
        super(Handler, self).__init__()
        self.bot = bot
        self.targets = targets

    def yield_records(self, record):
        for record in self.format(record).split('\n'):
            for t in self.targets:
                yield t, record

    def emit(self, record):
        self.bot.call_many('privmsg', self.yield_records(record))

class Logger(logging.getLoggerClass()):
    """Replace the default logger to add a set_irc_targets() method"""

    def set_irc_targets(self, bot, *targets):
        """Add a irc Handler using bot and log to targets (can be nicks or
        channels:

        ..
            >>> bot = None

        .. code-block:: python

            >>> log = logging.getLogger('irc.mymodule')
            >>> log.set_irc_targets(bot, '#chan', 'admin')
        """
        # get formatter initialized by config (usualy on a NullHandler)
        l = logging.getLogger('irc')
        formatter = l.handlers[0].formatter
        # add a handler for the sub logger
        handler = Handler(bot, *targets)
        handler.setFormatter(formatter)
        self.addHandler(handler)

logging.setLoggerClass(Logger)
<|endoftext|>


<commit_before>import fcntl
import logging
from multiprocessing import Queue
import os
import random
import time

from irc import strings
from irc.bot import SingleServerIRCBot, ServerSpec

logger = logging.getLogger(__name__)

class IrcpdbBot(SingleServerIRCBot):
    def __init__(
        self, channel, nickname, server, port, password,
        limit_access_to, message_wait_seconds,
        **connect_params
    ):
        self.channel = channel
        self.queue = Queue()
        self.joined = False
        self.message_wait_seconds = message_wait_seconds
        self.limit_access_to = limit_access_to
        server = ServerSpec(server, port, password)
        super(IrcpdbBot, self).__init__(
            [server], nickname, nickname, **connect_params
        )

    def on_nicknameinuse(self, c, e):
        c.nick(
            u"%s-%s" % (
                c.get_nickname(),
                random.randrange(0, 9999)
            )
        )

    def on_welcome(self, c, e):
        logger.debug('Received welcome message, joining %s', self.channel)
        c.join(self.channel)
        self.joined = True

    def on_privmsg(self, c, e):
        self.send_user_message(
            e.source.nick,
            "Ircdb currently supports sending/receiving messages "
            "using only the IRC channel."
        )

    def on_pubmsg(self, c, e):
        # Check if this message is prefixed with the bot's username:
        a = e.arguments[0].split(":", 1)
        if (
            len(a) > 1
            and strings.lower(a[0]) == strings.lower(
                self.connection.get_nickname()
            )
        ):
            self.do_command(e, a[1].strip())

        # And, check if the argument was prefixed with a '!'.
        if e.arguments[0][0] == '!':
            self.do_command(e, e.arguments[0][1:].strip())
        return

    def do_command(self, e, cmd):
        logger.debug('Received command: %s', cmd)
        nickname = e.source.nick
        if self.limit_access_to and nickname not in self.limit_access_to:
            self.send_channel_message(
                "I'm sorry, %s, you are not allowed to give commands "
                "to this debugger.  Please ask one of the following "
                "users for permission to use the debugger: %s." % (
                    nickname,
                    ', '.join(self.limit_access_to)
                )
            )
            return

        if cmd.startswith("!allow"):
            allows = cmd.split(' ')
            self.limit_access_to.extend(allows[1:])
            self.send_channel_message(
                "The following users have been granted access to the debugger:"
                " %s." % (
                    ', '.join(allows[1:])
                )
            )
        else:
            self.queue.put(cmd.strip())

    def send_channel_message(self, message):
        return self.send_user_message(
            self.channel,
            message,
        )

    def send_user_message(self, username, message):
        message_stripped = message.strip()
        if not self.joined:
            logger.warning(
                'Tried to send message %s, '
                'but was not yet joined to channel.',
                message
            )
            return
        if message_stripped:
            self.connection.send_raw(
                'PRIVMSG %s :%s' % (
                    username,
                    message_stripped
                )
            )
            if self.message_wait_seconds:
                time.sleep(self.message_wait_seconds)

    def process_forever(self, inhandle, outhandle, timeout=0.1):
        self._connect()
        # Let's mark out inhandle as non-blocking
        fcntl.fcntl(inhandle, fcntl.F_SETFL, os.O_NONBLOCK)
        while True:
            try:
                messages = inhandle.read()
            except IOError:
                messages = None
            if messages:
                for message in messages.split('\n'):
                    logger.debug('>> %s', message)
                    self.send_channel_message(message.strip())

            try:
                self.manifold.process_once(timeout)
            except UnicodeDecodeError:
                # This just *happens* -- I think these are coming from
                # maybe MOTD messages?  It isn't clear.
                logger.warning(
                    'UnicodeDecodeError raised while processing messages.'
                )

            while True:
                if self.queue.empty():
                    break
                message = self.queue.get(block=False)
                logger.debug('<< %s', message)
                outhandle.write(u'%s\n' % message)
                outhandle.flush()
<commit_msg>Automatically chunk messages into 500-character blocks.  Fixes #1.<commit_after>import fcntl
import logging
from multiprocessing import Queue
import os
import random
import time

from irc import strings
from irc.bot import SingleServerIRCBot, ServerSpec

logger = logging.getLogger(__name__)

class IrcpdbBot(SingleServerIRCBot):
    def __init__(
        self, channel, nickname, server, port, password,
        limit_access_to, message_wait_seconds,
        **connect_params
    ):
        self.channel = channel
        self.queue = Queue()
        self.joined = False
        self.message_wait_seconds = message_wait_seconds
        self.limit_access_to = limit_access_to
        server = ServerSpec(server, port, password)
        super(IrcpdbBot, self).__init__(
            [server], nickname, nickname, **connect_params
        )

    def on_nicknameinuse(self, c, e):
        c.nick(
            u"%s-%s" % (
                c.get_nickname(),
                random.randrange(0, 9999)
            )
        )

    def on_welcome(self, c, e):
        logger.debug('Received welcome message, joining %s', self.channel)
        c.join(self.channel)
        self.joined = True

    def on_privmsg(self, c, e):
        self.send_user_message(
            e.source.nick,
            "Ircdb currently supports sending/receiving messages "
            "using only the IRC channel."
        )

    def on_pubmsg(self, c, e):
        # Check if this message is prefixed with the bot's username:
        a = e.arguments[0].split(":", 1)
        if (
            len(a) > 1
            and strings.lower(a[0]) == strings.lower(
                self.connection.get_nickname()
            )
        ):
            self.do_command(e, a[1].strip())

        # And, check if the argument was prefixed with a '!'.
        if e.arguments[0][0] == '!':
            self.do_command(e, e.arguments[0][1:].strip())
        return

    def do_command(self, e, cmd):
        logger.debug('Received command: %s', cmd)
        nickname = e.source.nick
        if self.limit_access_to and nickname not in self.limit_access_to:
            self.send_channel_message(
                "I'm sorry, %s, you are not allowed to give commands "
                "to this debugger.  Please ask one of the following "
                "users for permission to use the debugger: %s." % (
                    nickname,
                    ', '.join(self.limit_access_to)
                )
            )
            return

        if cmd.startswith("!allow"):
            allows = cmd.split(' ')
            self.limit_access_to.extend(allows[1:])
            self.send_channel_message(
                "The following users have been granted access to the debugger:"
                " %s." % (
                    ', '.join(allows[1:])
                )
            )
        else:
            self.queue.put(cmd.strip())

    def send_channel_message(self, message):
        return self.send_user_message(
            self.channel,
            message,
        )

    def send_user_message(self, username, message):
        message_stripped = message.strip()
        if not self.joined:
            logger.warning(
                'Tried to send message %s, '
                'but was not yet joined to channel.',
                message
            )
            return
        if message_stripped:
            chunk_size = 500
            if len(message_stripped) > 512:
                message_parts = [
                    message_stripped[i:i+chunk_size]
                    for i in range(0, len(message_stripped), chunk_size)
                ]
            else:
                message_parts = [message_stripped]
            for part in message_parts:
                self.connection.send_raw(
                    'PRIVMSG %s :%s' % (
                        username,
                        message_stripped
                    )
                )
                if self.message_wait_seconds:
                    time.sleep(self.message_wait_seconds)

    def process_forever(self, inhandle, outhandle, timeout=0.1):
        self._connect()
        # Let's mark out inhandle as non-blocking
        fcntl.fcntl(inhandle, fcntl.F_SETFL, os.O_NONBLOCK)
        while True:
            try:
                messages = inhandle.read()
            except IOError:
                messages = None
            if messages:
                for message in messages.split('\n'):
                    logger.debug('>> %s', message)
                    self.send_channel_message(message.strip())

            try:
                self.manifold.process_once(timeout)
            except UnicodeDecodeError:
                # This just *happens* -- I think these are coming from
                # maybe MOTD messages?  It isn't clear.
                logger.warning(
                    'UnicodeDecodeError raised while processing messages.'
                )

            while True:
                if self.queue.empty():
                    break
                message = self.queue.get(block=False)
                logger.debug('<< %s', message)
                outhandle.write(u'%s\n' % message)
                outhandle.flush()
<|endoftext|>


<commit_before># Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
"""Cache of the Subversion history log."""

from bzrlib import urlutils
from bzrlib.errors import NoSuchRevision
import bzrlib.ui as ui
from copy import copy

from svn.core import SubversionException, Pool
from transport import SvnRaTransport
import svn.core

from cache import sqlite3

LOG_CHUNK_LIMIT = 1000

class LogWalker(object):
    """Easy way to access the history of a Subversion repository."""
    def __init__(self, transport, cache_db=None, limit=None):
        """Create a new instance.

        :param transport:   SvnRaTransport to use to access the repository.
        :param cache_db:    Optional sql database connection to use. Doesn't
                            cache if not set.
        """
        assert isinstance(transport, SvnRaTransport)

        self.url = transport.base
        self._transport = None

        if limit is not None:
            self._limit = limit
        else:
            self._limit = LOG_CHUNK_LIMIT

        if cache_db is None:
            self.db = sqlite3.connect(":memory:")
        else:
            self.db = cache_db

        self.db.executescript("""
          create table if not exists changed_path(rev integer, action text, path text, copyfrom_path text, copyfrom_rev integer);
          create index if not exists path_rev on changed_path(rev);
          create unique index if not exists path_rev_path on changed_path(rev, path);
          create unique index if not exists path_rev_path_action on changed_path(rev, path, action);
        """)
        self.db.commit()
        self.saved_revnum = self.db.execute("SELECT MAX(rev) FROM changed_path").fetchone()[0]
        if self.saved_revnum is None:
            self.saved_revnum = 0

    def _get_transport(self):
        if self._transport is not None:
            return self._transport
        self._transport = SvnRaTransport(self.url)
        return self._transport

    def fetch_revisions(self, to_revnum=None):
        """Fetch information about all revisions in the remote repository
        until to_revnum.

        :param to_revnum: End of range to fetch information for
        """
        if to_revnum <= self.saved_revnum:
            return
        latest_revnum = self._get_transport().get_latest_revnum()
        to_revnum = max(latest_revnum, to_revnum)

        pb = ui.ui_factory.nested_progress_bar()

        def rcvr(log_entry, pool):
            pb.update('fetching svn revision info', log_entry.revision, to_revnum)
            orig_paths = log_entry.changed_paths
            if orig_paths is None:
                orig_paths = {}
            for p in orig_paths:
                copyfrom_path = orig_paths[p].copyfrom_path
                if copyfrom_path is not None:
                    copyfrom_path = copyfrom_path.strip("/")

                self.db.execute(
                     "replace into changed_path (rev, path, action, copyfrom_path, copyfrom_rev) values (?, ?, ?, ?, ?)",
                     (log_entry.revision, p.strip("/"), orig_paths[p].action, copyfrom_path, orig_paths[p].copyfrom_rev))
                # Work around nasty memory leak in Subversion
                orig_paths[p]._parent_pool.destroy()

            self.saved_revnum = log_entry.revision
            if self.saved_revnum % 1000 == 0:
                self.db.commit()

        try:
            try:
                while self.saved_revnum < to_revnum:
                    pool = Pool()
                    self._get_transport().get_log("", self.saved_revnum,
                                             to_revnum, self._limit, True,
                                             True, [], rcvr, pool)
                    pool.destroy()
            finally:
                pb.finished()
        except SubversionException, (_, num):
            if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
                raise NoSuchRevision(branch=self,
                    revision="Revision number %d" % to_revnum)
            raise
        self.db.commit()

    def follow_path(self, path, revnum):
        """Return iterator over all the revisions between revnum and 0 named path or inside path.

        :param path:    Branch path to start reporting (in revnum)
        :param revnum:  Start revision.
        :return: An iterator that yields tuples with (path, paths, revnum)
            where paths is a dictionary with all changes that happened in path
            in revnum.
        """
        assert revnum >= 0

        if revnum == 0 and path == "":
            return

        recurse = (path != "")

        path = path.strip("/")

        while revnum >= 0:
            assert revnum > 0 or path == ""
            revpaths = self.get_revision_paths(revnum, path, recurse=recurse)

            if revpaths != {}:
                yield (path, copy(revpaths), revnum)

            if path == "":
                revnum -= 1
                continue

            if revpaths.has_key(path):
                if revpaths[path][1] is None:
                    if revpaths[path][0] in ('A', 'R'):
                        # this path didn't exist before this revision
                        return
                else:
                    # In this revision, this path was copied from
                    # somewhere else
                    revnum = revpaths[path][2]
                    path = revpaths[path][1]
                    assert path == "" or revnum > 0
                    continue
            revnum -= 1
            for p in sorted(revpaths.keys()):
                if path.startswith(p+"/") and revpaths[p][0] in ('A', 'R'):
                    assert revpaths[p][1]
                    path = path.replace(p, revpaths[p][1])
                    revnum = revpaths[p][2]
                    break

    def get_revision_paths(self, revnum, path=None, recurse=False):
        """Obtain dictionary with all the changes in a particular revision.

        :param revnum: Subversion revision number
        :param path: optional path under which to return all entries
        :param recurse: Report changes to parents as well
        :returns: dictionary with paths as keys and
                  (action, copyfrom_path, copyfrom_rev) as values.
        """

        if revnum == 0:
            assert path is None or path == ""
            return {'': ('A', None, -1)}

        self.fetch_revisions(revnum)

        query = "select path, action, copyfrom_path, copyfrom_rev from changed_path where rev="+str(revnum)
        if path is not None and path != "":
            query += " and (path='%s' or path like '%s/%%'" % (path, path)
            if recurse:
                query += " or ('%s' LIKE path || '/%%')" % path
            query += ")"

        paths = {}
        for p, act, cf, cr in self.db.execute(query):
            if cf is not None:
                cf = cf.encode("utf-8")
            paths[p.encode("utf-8")] = (act, cf, cr)
        return paths

    def find_latest_change(self, path, revnum, include_parents=False,
                           include_children=False):
        """Find latest revision that touched path.

        :param path: Path to check for changes
        :param revnum: First revision to check
        """
        assert isinstance(path, str)
        assert isinstance(revnum, int) and revnum >= 0
        self.fetch_revisions(revnum)

        extra = ""
        if include_children:
            if path == "":
                extra += " OR path LIKE '%'"
            else:
                extra += " OR path LIKE '%s/%%'" % path.strip("/")
        if include_parents:
            extra += " OR ('%s' LIKE (path || '/%%') AND (action = 'R' OR action = 'A'))" % path.strip("/")
        query = "SELECT rev FROM changed_path WHERE (path='%s'%s) AND rev <= %d ORDER BY rev DESC LIMIT 1" % (path.strip("/"), extra, revnum)

        row = self.db.execute(query).fetchone()
        if row is None and path == "":
            return 0

        if row is None:
            return None

        return row[0]

    def touches_path(self, path, revnum):
        """Check whether path was changed in specified revision.

        :param path:  Path to check
        :param revnum:  Revision to check
        """
        self.fetch_revisions(revnum)
        if revnum == 0:
            return (path == "")
        return (self.db.execute("select 1 from changed_path where path='%s' and rev=%d" % (path, revnum)).fetchone() is not None)

    def find_children(self, path, revnum):
        """Find all children of path in revnum.

        :param path:  Path to check
        :param revnum:  Revision to check
        """
        path = path.strip("/")
        transport = self._get_transport()
        ft = transport.check_path(path, revnum)
        if ft == svn.core.svn_node_file:
            return []
        assert ft == svn.core.svn_node_dir

        class TreeLister(svn.delta.Editor):
            def __init__(self, base):
                self.files = []
                self.base = base

            def set_target_revision(self, revnum):
                """See Editor.set_target_revision()."""
                pass

            def open_root(self, revnum, baton):
                """See Editor.open_root()."""
                return path

            def add_directory(self, path, parent_baton, copyfrom_path, copyfrom_revnum, pool):
                """See Editor.add_directory()."""
                self.files.append(urlutils.join(self.base, path))
                return path

            def change_dir_prop(self, id, name, value, pool):
                pass

            def change_file_prop(self, id, name, value, pool):
                pass

            def add_file(self, path, parent_id, copyfrom_path, copyfrom_revnum, baton):
                self.files.append(urlutils.join(self.base, path))
                return path

            def close_dir(self, id):
                pass

            def close_file(self, path, checksum):
                pass

            def close_edit(self):
                pass

            def abort_edit(self):
                pass

            def apply_textdelta(self, file_id, base_checksum):
                pass
        pool = Pool()
        editor = TreeLister(path)
        old_base = transport.base
        try:
            root_repos = transport.get_svn_repos_root()
            transport.reparent(urlutils.join(root_repos, path))
            reporter = transport.do_update(revnum, True, editor, pool)
            reporter.set_path("", revnum, True, None, pool)
            reporter.finish_report(pool)
        finally:
            transport.reparent(old_base)
        return editor.files

    def get_previous(self, path, revnum):
        """Return path,revnum pair specified pair was derived from.

        :param path:  Path to check
        :param revnum:  Revision to check
        """
        assert revnum >= 0
        self.fetch_revisions(revnum)
        if revnum == 0:
            return (None, -1)
        row = self.db.execute("select action, copyfrom_path, copyfrom_rev from changed_path where path='%s' and rev=%d" % (path, revnum)).fetchone()
        if row[2] == -1:
            if row[0] == 'A':
                return (None, -1)
            return (path, revnum-1)
        return (row[1], row[2])
<commit_msg>Don't retrieve log data in bunches.<commit_after># Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
"""Cache of the Subversion history log."""

from bzrlib import urlutils
from bzrlib.errors import NoSuchRevision
import bzrlib.ui as ui
from copy import copy

from svn.core import SubversionException, Pool
from transport import SvnRaTransport
import svn.core

from cache import sqlite3

LOG_CHUNK_LIMIT = 0

class LogWalker(object):
    """Easy way to access the history of a Subversion repository."""
    def __init__(self, transport, cache_db=None, limit=None):
        """Create a new instance.

        :param transport:   SvnRaTransport to use to access the repository.
        :param cache_db:    Optional sql database connection to use. Doesn't
                            cache if not set.
        """
        assert isinstance(transport, SvnRaTransport)

        self.url = transport.base
        self._transport = None

        if limit is not None:
            self._limit = limit
        else:
            self._limit = LOG_CHUNK_LIMIT

        if cache_db is None:
            self.db = sqlite3.connect(":memory:")
        else:
            self.db = cache_db

        self.db.executescript("""
          create table if not exists changed_path(rev integer, action text, path text, copyfrom_path text, copyfrom_rev integer);
          create index if not exists path_rev on changed_path(rev);
          create unique index if not exists path_rev_path on changed_path(rev, path);
          create unique index if not exists path_rev_path_action on changed_path(rev, path, action);
        """)
        self.db.commit()
        self.saved_revnum = self.db.execute("SELECT MAX(rev) FROM changed_path").fetchone()[0]
        if self.saved_revnum is None:
            self.saved_revnum = 0

    def _get_transport(self):
        if self._transport is not None:
            return self._transport
        self._transport = SvnRaTransport(self.url)
        return self._transport

    def fetch_revisions(self, to_revnum=None):
        """Fetch information about all revisions in the remote repository
        until to_revnum.

        :param to_revnum: End of range to fetch information for
        """
        if to_revnum <= self.saved_revnum:
            return
        latest_revnum = self._get_transport().get_latest_revnum()
        to_revnum = max(latest_revnum, to_revnum)

        pb = ui.ui_factory.nested_progress_bar()

        def rcvr(log_entry, pool):
            pb.update('fetching svn revision info', log_entry.revision, to_revnum)
            orig_paths = log_entry.changed_paths
            if orig_paths is None:
                orig_paths = {}
            for p in orig_paths:
                copyfrom_path = orig_paths[p].copyfrom_path
                if copyfrom_path is not None:
                    copyfrom_path = copyfrom_path.strip("/")

                self.db.execute(
                     "replace into changed_path (rev, path, action, copyfrom_path, copyfrom_rev) values (?, ?, ?, ?, ?)",
                     (log_entry.revision, p.strip("/"), orig_paths[p].action, copyfrom_path, orig_paths[p].copyfrom_rev))
                # Work around nasty memory leak in Subversion
                orig_paths[p]._parent_pool.destroy()

            self.saved_revnum = log_entry.revision
            if self.saved_revnum % 1000 == 0:
                self.db.commit()

        try:
            try:
                while self.saved_revnum < to_revnum:
                    pool = Pool()
                    self._get_transport().get_log("", self.saved_revnum,
                                             to_revnum, self._limit, True,
                                             True, [], rcvr, pool)
                    pool.destroy()
            finally:
                pb.finished()
        except SubversionException, (_, num):
            if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
                raise NoSuchRevision(branch=self,
                    revision="Revision number %d" % to_revnum)
            raise
        self.db.commit()

    def follow_path(self, path, revnum):
        """Return iterator over all the revisions between revnum and 0 named path or inside path.

        :param path:    Branch path to start reporting (in revnum)
        :param revnum:  Start revision.
        :return: An iterator that yields tuples with (path, paths, revnum)
            where paths is a dictionary with all changes that happened in path
            in revnum.
        """
        assert revnum >= 0

        if revnum == 0 and path == "":
            return

        recurse = (path != "")

        path = path.strip("/")

        while revnum >= 0:
            assert revnum > 0 or path == ""
            revpaths = self.get_revision_paths(revnum, path, recurse=recurse)

            if revpaths != {}:
                yield (path, copy(revpaths), revnum)

            if path == "":
                revnum -= 1
                continue

            if revpaths.has_key(path):
                if revpaths[path][1] is None:
                    if revpaths[path][0] in ('A', 'R'):
                        # this path didn't exist before this revision
                        return
                else:
                    # In this revision, this path was copied from
                    # somewhere else
                    revnum = revpaths[path][2]
                    path = revpaths[path][1]
                    assert path == "" or revnum > 0
                    continue
            revnum -= 1
            for p in sorted(revpaths.keys()):
                if path.startswith(p+"/") and revpaths[p][0] in ('A', 'R'):
                    assert revpaths[p][1]
                    path = path.replace(p, revpaths[p][1])
                    revnum = revpaths[p][2]
                    break

    def get_revision_paths(self, revnum, path=None, recurse=False):
        """Obtain dictionary with all the changes in a particular revision.

        :param revnum: Subversion revision number
        :param path: optional path under which to return all entries
        :param recurse: Report changes to parents as well
        :returns: dictionary with paths as keys and
                  (action, copyfrom_path, copyfrom_rev) as values.
        """

        if revnum == 0:
            assert path is None or path == ""
            return {'': ('A', None, -1)}

        self.fetch_revisions(revnum)

        query = "select path, action, copyfrom_path, copyfrom_rev from changed_path where rev="+str(revnum)
        if path is not None and path != "":
            query += " and (path='%s' or path like '%s/%%'" % (path, path)
            if recurse:
                query += " or ('%s' LIKE path || '/%%')" % path
            query += ")"

        paths = {}
        for p, act, cf, cr in self.db.execute(query):
            if cf is not None:
                cf = cf.encode("utf-8")
            paths[p.encode("utf-8")] = (act, cf, cr)
        return paths

    def find_latest_change(self, path, revnum, include_parents=False,
                           include_children=False):
        """Find latest revision that touched path.

        :param path: Path to check for changes
        :param revnum: First revision to check
        """
        assert isinstance(path, str)
        assert isinstance(revnum, int) and revnum >= 0
        self.fetch_revisions(revnum)

        extra = ""
        if include_children:
            if path == "":
                extra += " OR path LIKE '%'"
            else:
                extra += " OR path LIKE '%s/%%'" % path.strip("/")
        if include_parents:
            extra += " OR ('%s' LIKE (path || '/%%') AND (action = 'R' OR action = 'A'))" % path.strip("/")
        query = "SELECT rev FROM changed_path WHERE (path='%s'%s) AND rev <= %d ORDER BY rev DESC LIMIT 1" % (path.strip("/"), extra, revnum)

        row = self.db.execute(query).fetchone()
        if row is None and path == "":
            return 0

        if row is None:
            return None

        return row[0]

    def touches_path(self, path, revnum):
        """Check whether path was changed in specified revision.

        :param path:  Path to check
        :param revnum:  Revision to check
        """
        self.fetch_revisions(revnum)
        if revnum == 0:
            return (path == "")
        return (self.db.execute("select 1 from changed_path where path='%s' and rev=%d" % (path, revnum)).fetchone() is not None)

    def find_children(self, path, revnum):
        """Find all children of path in revnum.

        :param path:  Path to check
        :param revnum:  Revision to check
        """
        path = path.strip("/")
        transport = self._get_transport()
        ft = transport.check_path(path, revnum)
        if ft == svn.core.svn_node_file:
            return []
        assert ft == svn.core.svn_node_dir

        class TreeLister(svn.delta.Editor):
            def __init__(self, base):
                self.files = []
                self.base = base

            def set_target_revision(self, revnum):
                """See Editor.set_target_revision()."""
                pass

            def open_root(self, revnum, baton):
                """See Editor.open_root()."""
                return path

            def add_directory(self, path, parent_baton, copyfrom_path, copyfrom_revnum, pool):
                """See Editor.add_directory()."""
                self.files.append(urlutils.join(self.base, path))
                return path

            def change_dir_prop(self, id, name, value, pool):
                pass

            def change_file_prop(self, id, name, value, pool):
                pass

            def add_file(self, path, parent_id, copyfrom_path, copyfrom_revnum, baton):
                self.files.append(urlutils.join(self.base, path))
                return path

            def close_dir(self, id):
                pass

            def close_file(self, path, checksum):
                pass

            def close_edit(self):
                pass

            def abort_edit(self):
                pass

            def apply_textdelta(self, file_id, base_checksum):
                pass
        pool = Pool()
        editor = TreeLister(path)
        old_base = transport.base
        try:
            root_repos = transport.get_svn_repos_root()
            transport.reparent(urlutils.join(root_repos, path))
            reporter = transport.do_update(revnum, True, editor, pool)
            reporter.set_path("", revnum, True, None, pool)
            reporter.finish_report(pool)
        finally:
            transport.reparent(old_base)
        return editor.files

    def get_previous(self, path, revnum):
        """Return path,revnum pair specified pair was derived from.

        :param path:  Path to check
        :param revnum:  Revision to check
        """
        assert revnum >= 0
        self.fetch_revisions(revnum)
        if revnum == 0:
            return (None, -1)
        row = self.db.execute("select action, copyfrom_path, copyfrom_rev from changed_path where path='%s' and rev=%d" % (path, revnum)).fetchone()
        if row[2] == -1:
            if row[0] == 'A':
                return (None, -1)
            return (path, revnum-1)
        return (row[1], row[2])
<|endoftext|>


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

from __future__ import absolute_import

import argparse

__version__ = '0.0.2'

import os
import sys
import random
import difflib
import subprocess

import pkgtools.pypi as pp
import emoji

from travispy import TravisPy
from withlog import Info, Message

import withlog

from time import sleep
# from cookiecutter.main import generate_context, generate_files

import logging

from .github import setup_github_credentials, setup_github_repository
from .flit import python_requires

heart = """

                L       OOO    V   V  EEEEE
                L      O   O   V   V  E
                L      O   O   V   V  EEE
                L      O   O    V V   E
    oo          LLLLL   OOO      V    EEEEE
       oo
          oo     OOOOOOOO:       OOOOOOOO!
             oOOOO!!!!;;;;O    OO.......:;!O
            'OOO!!!;;;;;;;;O  O.......:   ;!O
            OOO!!!!;;::::::.OO........:    ;!O
            OO!!!!;;:::::..............:   ;!O
            OOO!!!;::::::..............:   ;!O
             OO!!;;::::::.............:   ;!O           Made With Love
              OO!;;::::::......oo.....::::!O
                O!!;::::::........oo..:::O                  - M -
                  !!!;:::::..........ooO
                     !!;:::::.......O   oo              version : {version}
                       ;;::::.....O        oo  ,o
                          :::..O              ooo
                            ::.              oooo
                             :
""".format(version=__version__)

from colored import fg, attr, bg

cheart = '\n'.join(map(
        lambda l : "%s%s%s" %(fg('deep_pink_3b'),l, attr(0)),
        heart.split('\n')
        ))

class WLove(Message):
   def __init__(self, title):
        super().__init__(title,1, fg('white') + bg('deep_pink_3b') )

try:
    import curses
except ImportError:
    curses = None

def _stderr_supports_color():
    color = False
    if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
        try:
            curses.setupterm()
            if curses.tigetnum("colors") > 0:
                color = True
        except Exception:
            pass
    return color

emoji_level = {
        10:emoji.emojize(":broken_heart:"),
        20:emoji.emojize(":growing_heart:"),
        30:emoji.emojize(":heart_with_arrow:")
}

class LogFormatter(logging.Formatter):
    """Log formatter with colour support
    """
    DEFAULT_COLORS = {
        logging.INFO: 2, # Green
        logging.WARNING: 3, # Yellow
        logging.ERROR: 1, # Red
        logging.CRITICAL: 1,
    }

    def __init__(self, color=True, datefmt=None):
        r"""
        :arg bool color: Enables color support.
        :arg string fmt: Log message format.
        It will be applied to the attributes dict of log records. The
        text between ``%(color)s`` and ``%(end_color)s`` will be colored
        depending on the level if color support is on.
        :arg dict colors: color mappings from logging level to terminal color
        code
        :arg string datefmt: Datetime format.
        Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``.
        .. versionchanged:: 3.2
        Added ``fmt`` and ``datefmt`` arguments.
        """
        logging.Formatter.__init__(self, datefmt=datefmt)
        self._colors = {}
        if color and _stderr_supports_color():
            fg_color = (curses.tigetstr("setaf") or
                        curses.tigetstr("setf") or "")
            if (3, 0) < sys.version_info < (3, 2, 3):
                fg_color = str(fg_color, "ascii")

            for levelno, code in self.DEFAULT_COLORS.items():
                self._colors[levelno] = str(curses.tparm(fg_color, code), "ascii")
            self._normal = str(curses.tigetstr("sgr0"), "ascii")

            scr = curses.initscr()
            self.termwidth = scr.getmaxyx()[1]
            curses.endwin()
        else:
            self._normal = ''
            # Default width is usually 80, but too wide is worse than too narrow
            self.termwidth = 70

    def formatMessage(self, record):
        right_text = '{initial}-{name}'.format(initial=record.levelname[0],
                                               name=record.name)
        if record.levelno in self._colors:
            start_color = self._colors[record.levelno]
            end_color = self._normal
        else:
            start_color = end_color = ''
        emo = emoji_level[record.levelno]

        return emo +' '+start_color + '[' +right_text + '] ' +  record.message + end_color + ' '+emo

def main():
    with WLove('Love') as p:
        submain(p)

def submain(p):
    """
    completly set-up a package in the target dir (using cookie cutter)

    """
    parser = argparse.ArgumentParser(description='Bootstrap a Python Package with Love.')
    parser.add_argument('name', metavar='name', type=str, nargs='?',
                       help='a potential package name')
    parser.add_argument('target_dir', type=str, nargs='?',
                       help='target directory in which to create the package')
    parser.add_argument('--version', help='print version, logo and exist', action='store_true')
    parser.add_argument('--no-banner',  help='do not print banner', action='store_false')

    parser.add_argument('--no-travis', help='prevent Travis from being set-up', action='store_true')
    parser.add_argument('--no-flit', help='prevent usage of Flit to set-up-package', action='store_true')
    parser.add_argument('--no-similar', help='prevent search of similar project on PyPI', action='store_true')
    parser.add_argument('--python', help='set-up the minimal required python', action='store_true')

    args = parser.parse_args()
    proposal = args.name
    target_dir = args.target_dir

    LOGLEVEL = 50
    log = logging.getLogger(__name__)
    handler = logging.StreamHandler()
    handler.setFormatter(LogFormatter())
    logging.root.addHandler(handler)
    logging.root.setLevel(LOGLEVEL)
    log.setLevel(LOGLEVEL)

    log.info = withlog.glog

    if args.no_banner:
        p(cheart)
    if args.version :
       p('Version : '+ __version__)
       sys.exit(0)

    if args.python :
        python_requires()
        sys.exit(0)

    if not target_dir:
        with Info('Target directory'):
            target_dir = os.getcwd()
            log.info('Will use target dir : %s', target_dir)

    # generate a python package name

    adjectives = ['red','green','blue','purple','fluffy','soft','hard','golden','silver', 'pathetic', 'intrepid', 'jovial' , 'long']
    nouns = ['moon', 'frog', 'lake','orchid','sapphire','gem','sun','lily','ocean','lampshade','fish', 'koala', 'shark', 'kangaroo']
    if proposal and not proposal.isidentifier() and len(proposal)>3:
        log.info('package names should be valid python identifiers, and at least 3 letters long : %s', proposal)
        sys.exit(-1)

    if not proposal:
        proposal = random.choice(adjectives).capitalize()+random.choice(nouns).capitalize()
    plist = None

    #  compare name with existing package name, warn if too close
    if not args.no_similar:
        with Info('Similar projects'):
            log.info('Comparing "%s" to other existing package names...' % proposal)
            pypi = pp.PyPIXmlRpc()
            # cache that on a weekly basis ?
            if plist is None:
                plist = pypi.list_packages()
            closest = difflib.get_close_matches(proposal.lower(), map(str.lower, plist), cutoff=0.8)
            if closest:
                if proposal in closest:
                    log.info(proposal + ' already exists, maybe you would prefer to contribute to this package?')
                else:
                    log.info('%s name is close to the following package name: %s', proposal,  closest)
                response = input('Do you still want to continue ? [Y/n]')
                if not response:
                    response = 'y'
                if not response.lower() == 'y':
                    sys.exit('Aborting')
            else:
                log.info('"%s" seems like a great name !', proposal)

    #  Actually authenticate with github
    #  Create (if do not exist) the named repo, and and clone URL.
    with Info('Github'):
        token, user = setup_github_credentials(log=log)
        slug = setup_github_repository(user, proposal, log)

    os.chdir(proposal)
    log.debug('I am now in %s', os.getcwd())

    if not args.no_travis:
        with Info('Continuous Integration: Configuring Travis-CI'):
            enable_travis(token, slug, log)

    with Info('Setting up Layout'):
        project_layout(proposal, None, None, log)

    if not args.no_flit:
        with Info('Setting up Package with Flit.') as p, withlog.print_statement(p), withlog.input():
            packaging_init(log)
            with Info('Python Minimal version') as p, withlog.print_statement(p):
                python_requires('>=3.4')
                p('Package marked as requiring python >= 3.4')

def enable_travis(token, slug, log):
    """
    Enable Travis automatically for the given repo.

    this need to have access to the GitHub token.
    """

    # Done with github directly. Login to travis

    travis = TravisPy.github_auth(token, uri='https://api.travis-ci.org')
    user = travis.user()
    log.info('Travis user: %s', user.name)

    # Ask travis to sync with github, try to fetch created repo with exponentially decaying time.

    last_sync = user.synced_at
    log.info('syncing Travis with Github, this can take a while...')
    repo = travis._session.post(travis._session.uri+'/users/sync')
    for i in range(10):
        try:
            sleep((1.5)**i)
            repo = travis.repo(slug)
            if travis.user().synced_at == last_sync:
                raise ValueError('synced not really done, travis.repo() can be a duplicate')
            log.info('\nsyncing done')
            break
        # TODO: find the right exception here
        except Exception:
            pass
    ## todo , warn if not found

    #  Enable travis hook for this repository

    log.info('Enabling Travis-CI hook for this repository')
    resp = travis._session.put(travis._session.uri+"/hooks/",
                        json={
                            "hook": {
                                "id": repo.id ,
                                "active": True
                            }
                        },
                      )
    if resp.json()['result'] is True:
        log.info('Travis hook for this repository is now enabled.')
        log.info('Continuous integration test should be triggered every time you push code to github')
    else:
        log.info("I was not able to set up Travis hooks... something went wrong.")

    return user

def codecov():
    pass

def travis_yml():
    with open('.travis.yml', 'w') as f:
        f.write(
"""
language: python
python:
    - 3.5
    - 3.4
before_install:
    - pip install flit pytest
install:
    - flit install --symlink
script:
    - py.test
"""
        )

def project_layout(proposal, user=None, repo=None, log=None):
    """
    generate the project template

    proposal is the name of the project,
    user is an object containing some information about the user.
        - full name,
        - github username
        - email

    """

    proposal = proposal.lower()

    #context_file = os.path.expanduser('~/.cookiecutters/cookiecutter-pypackage/cookiecutter.json')
    #context = generate_context(context_file)

    # os.chdir('..')
    # context['cookiecutter']['full_name'] = user.name
    # context['cookiecutter']['email'] = user.email
    # context['cookiecutter']['github_username'] = user.login
    # context['cookiecutter']['project_name'] = proposal
    # context['cookiecutter']['repo_name'] = proposal.lower()

    try:
        os.mkdir(proposal)
    except FileExistsError:
        log.info('Skip directory structure, as project seem to already exists')

    with open( '/'.join([proposal, '__init__.py']), 'w') as f:
        f.write('''
"""
a simple package
"""

__version__ = '0.0.1'

        ''')

    travis_yml()

    #generate_files(
    #        repo_dir=os.path.expanduser('~/.cookiecutters/cookiecutter-pypackage/'),
    #        context=context
    #    )

    log.info('Workig in %s', os.getcwd())
    os.listdir('.')

    subprocess.call(['git','add','.'], )

    subprocess.call(['git','commit',"-am'initial commit of %s'" % proposal])

    subprocess.call(['git', "push", "origin", "master:master"])

    #webbrowser.open('https://travis-ci.org/{slug}'.format(slug=repo.slug))

def packaging_init(log):
    log.info('Please answer the following questions: ')
    from flit.init import TerminalIniter
    TerminalIniter().initialise()

<commit_msg>add default .gitigore<commit_after># -*- coding: utf-8 -*-

from __future__ import absolute_import

import argparse

__version__ = '0.0.2'

import os
import sys
import random
import difflib
import subprocess

import pkgtools.pypi as pp
import emoji

from travispy import TravisPy
from withlog import Info, Message

import withlog

from time import sleep
# from cookiecutter.main import generate_context, generate_files

import logging

from .github import setup_github_credentials, setup_github_repository
from .flit import python_requires

heart = """

                L       OOO    V   V  EEEEE
                L      O   O   V   V  E
                L      O   O   V   V  EEE
                L      O   O    V V   E
    oo          LLLLL   OOO      V    EEEEE
       oo
          oo     OOOOOOOO:       OOOOOOOO!
             oOOOO!!!!;;;;O    OO.......:;!O
            'OOO!!!;;;;;;;;O  O.......:   ;!O
            OOO!!!!;;::::::.OO........:    ;!O
            OO!!!!;;:::::..............:   ;!O
            OOO!!!;::::::..............:   ;!O
             OO!!;;::::::.............:   ;!O           Made With Love
              OO!;;::::::......oo.....::::!O
                O!!;::::::........oo..:::O                  - M -
                  !!!;:::::..........ooO
                     !!;:::::.......O   oo              version : {version}
                       ;;::::.....O        oo  ,o
                          :::..O              ooo
                            ::.              oooo
                             :
""".format(version=__version__)

from colored import fg, attr, bg

cheart = '\n'.join(map(
        lambda l : "%s%s%s" %(fg('deep_pink_3b'),l, attr(0)),
        heart.split('\n')
        ))

class WLove(Message):
   def __init__(self, title):
        super().__init__(title,1, fg('white') + bg('deep_pink_3b') )

try:
    import curses
except ImportError:
    curses = None

def _stderr_supports_color():
    color = False
    if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
        try:
            curses.setupterm()
            if curses.tigetnum("colors") > 0:
                color = True
        except Exception:
            pass
    return color

emoji_level = {
        10:emoji.emojize(":broken_heart:"),
        20:emoji.emojize(":growing_heart:"),
        30:emoji.emojize(":heart_with_arrow:")
}

class LogFormatter(logging.Formatter):
    """Log formatter with colour support
    """
    DEFAULT_COLORS = {
        logging.INFO: 2, # Green
        logging.WARNING: 3, # Yellow
        logging.ERROR: 1, # Red
        logging.CRITICAL: 1,
    }

    def __init__(self, color=True, datefmt=None):
        r"""
        :arg bool color: Enables color support.
        :arg string fmt: Log message format.
        It will be applied to the attributes dict of log records. The
        text between ``%(color)s`` and ``%(end_color)s`` will be colored
        depending on the level if color support is on.
        :arg dict colors: color mappings from logging level to terminal color
        code
        :arg string datefmt: Datetime format.
        Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``.
        .. versionchanged:: 3.2
        Added ``fmt`` and ``datefmt`` arguments.
        """
        logging.Formatter.__init__(self, datefmt=datefmt)
        self._colors = {}
        if color and _stderr_supports_color():
            fg_color = (curses.tigetstr("setaf") or
                        curses.tigetstr("setf") or "")
            if (3, 0) < sys.version_info < (3, 2, 3):
                fg_color = str(fg_color, "ascii")

            for levelno, code in self.DEFAULT_COLORS.items():
                self._colors[levelno] = str(curses.tparm(fg_color, code), "ascii")
            self._normal = str(curses.tigetstr("sgr0"), "ascii")

            scr = curses.initscr()
            self.termwidth = scr.getmaxyx()[1]
            curses.endwin()
        else:
            self._normal = ''
            # Default width is usually 80, but too wide is worse than too narrow
            self.termwidth = 70

    def formatMessage(self, record):
        right_text = '{initial}-{name}'.format(initial=record.levelname[0],
                                               name=record.name)
        if record.levelno in self._colors:
            start_color = self._colors[record.levelno]
            end_color = self._normal
        else:
            start_color = end_color = ''
        emo = emoji_level[record.levelno]

        return emo +' '+start_color + '[' +right_text + '] ' +  record.message + end_color + ' '+emo

def main():
    with WLove('Love') as p:
        submain(p)

def submain(p):
    """
    completly set-up a package in the target dir (using cookie cutter)

    """
    parser = argparse.ArgumentParser(description='Bootstrap a Python Package with Love.')
    parser.add_argument('name', metavar='name', type=str, nargs='?',
                       help='a potential package name')
    parser.add_argument('target_dir', type=str, nargs='?',
                       help='target directory in which to create the package')
    parser.add_argument('--version', help='print version, logo and exist', action='store_true')
    parser.add_argument('--no-banner',  help='do not print banner', action='store_false')

    parser.add_argument('--no-travis', help='prevent Travis from being set-up', action='store_true')
    parser.add_argument('--no-flit', help='prevent usage of Flit to set-up-package', action='store_true')
    parser.add_argument('--no-similar', help='prevent search of similar project on PyPI', action='store_true')
    parser.add_argument('--python', help='set-up the minimal required python', action='store_true')

    args = parser.parse_args()
    proposal = args.name
    target_dir = args.target_dir

    LOGLEVEL = 50
    log = logging.getLogger(__name__)
    handler = logging.StreamHandler()
    handler.setFormatter(LogFormatter())
    logging.root.addHandler(handler)
    logging.root.setLevel(LOGLEVEL)
    log.setLevel(LOGLEVEL)

    log.info = withlog.glog

    if args.no_banner:
        p(cheart)
    if args.version :
       p('Version : '+ __version__)
       sys.exit(0)

    if args.python :
        python_requires()
        sys.exit(0)

    if not target_dir:
        with Info('Target directory'):
            target_dir = os.getcwd()
            log.info('Will use target dir : %s', target_dir)

    # generate a python package name

    adjectives = ['red','green','blue','purple','fluffy','soft','hard','golden','silver', 'pathetic', 'intrepid', 'jovial' , 'long']
    nouns = ['moon', 'frog', 'lake','orchid','sapphire','gem','sun','lily','ocean','lampshade','fish', 'koala', 'shark', 'kangaroo']
    if proposal and not proposal.isidentifier() and len(proposal)>3:
        log.info('package names should be valid python identifiers, and at least 3 letters long : %s', proposal)
        sys.exit(-1)

    if not proposal:
        proposal = random.choice(adjectives).capitalize()+random.choice(nouns).capitalize()
    plist = None

    #  compare name with existing package name, warn if too close
    if not args.no_similar:
        with Info('Similar projects'):
            log.info('Comparing "%s" to other existing package names...' % proposal)
            pypi = pp.PyPIXmlRpc()
            # cache that on a weekly basis ?
            if plist is None:
                plist = pypi.list_packages()
            closest = difflib.get_close_matches(proposal.lower(), map(str.lower, plist), cutoff=0.8)
            if closest:
                if proposal in closest:
                    log.info(proposal + ' already exists, maybe you would prefer to contribute to this package?')
                else:
                    log.info('%s name is close to the following package name: %s', proposal,  closest)
                response = input('Do you still want to continue ? [Y/n]')
                if not response:
                    response = 'y'
                if not response.lower() == 'y':
                    sys.exit('Aborting')
            else:
                log.info('"%s" seems like a great name !', proposal)

    #  Actually authenticate with github
    #  Create (if do not exist) the named repo, and  clone URL.
    with Info('Github'):
        token, user = setup_github_credentials(log=log)
        slug = setup_github_repository(user, proposal, log)

    os.chdir(proposal)
    log.debug('I am now in %s', os.getcwd())

    if not args.no_travis:
        with Info('Continuous Integration: Configuring Travis-CI'):
            enable_travis(token, slug, log)

    with Info('Setting up Layout'):
        project_layout(proposal, None, None, log)

    if not args.no_flit:
        with Info('Setting up Package with Flit.') as p, withlog.print_statement(p), withlog.input():
            packaging_init(log)
            with Info('Python Minimal version') as p, withlog.print_statement(p):
                python_requires('>=3.4')
                p('Package marked as requiring python >= 3.4')

def enable_travis(token, slug, log):
    """
    Enable Travis automatically for the given repo.

    this need to have access to the GitHub token.
    """

    # Done with github directly. Login to travis

    travis = TravisPy.github_auth(token, uri='https://api.travis-ci.org')
    user = travis.user()
    log.info('Travis user: %s', user.name)

    # Ask travis to sync with github, try to fetch created repo with exponentially decaying time.

    last_sync = user.synced_at
    log.info('syncing Travis with Github, this can take a while...')
    repo = travis._session.post(travis._session.uri+'/users/sync')
    for i in range(10):
        try:
            sleep((1.5)**i)
            repo = travis.repo(slug)
            if travis.user().synced_at == last_sync:
                raise ValueError('synced not really done, travis.repo() can be a duplicate')
            log.info('\nsyncing done')
            break
        # TODO: find the right exception here
        except Exception:
            pass
    ## todo , warn if not found

    #  Enable travis hook for this repository

    log.info('Enabling Travis-CI hook for this repository')
    resp = travis._session.put(travis._session.uri+"/hooks/",
                        json={
                            "hook": {
                                "id": repo.id ,
                                "active": True
                            }
                        },
                      )
    if resp.json()['result'] is True:
        log.info('Travis hook for this repository is now enabled.')
        log.info('Continuous integration test should be triggered every time you push code to github')
    else:
        log.info("I was not able to set up Travis hooks... something went wrong.")

    return user

def codecov():
    pass

def travis_yml():
    with open('.travis.yml', 'w') as f:
        f.write(
"""
language: python
python:
    - 3.5
    - 3.4
before_install:
    - pip install flit pytest
install:
    - flit install --symlink
script:
    - py.test
"""
        )

def project_layout(proposal, user=None, repo=None, log=None):
    """
    generate the project template

    proposal is the name of the project,
    user is an object containing some information about the user.
        - full name,
        - github username
        - email

    """

    proposal = proposal.lower()

    #context_file = os.path.expanduser('~/.cookiecutters/cookiecutter-pypackage/cookiecutter.json')
    #context = generate_context(context_file)

    # os.chdir('..')
    # context['cookiecutter']['full_name'] = user.name
    # context['cookiecutter']['email'] = user.email
    # context['cookiecutter']['github_username'] = user.login
    # context['cookiecutter']['project_name'] = proposal
    # context['cookiecutter']['repo_name'] = proposal.lower()

    try:
        os.mkdir(proposal)
    except FileExistsError:
        log.info('Skip directory structure, as project seem to already exists')

    with open('.gitignore') as f:
        f.write('''
*.pyc
__pycache__
/build/
/dist/
''')

    with open( '/'.join([proposal, '__init__.py']), 'w') as f:
        f.write('''
"""
a simple package
"""

__version__ = '0.0.1'

        ''')

    travis_yml()

    #generate_files(
    #        repo_dir=os.path.expanduser('~/.cookiecutters/cookiecutter-pypackage/'),
    #        context=context
    #    )

    log.info('Workig in %s', os.getcwd())
    os.listdir('.')

    subprocess.call(['git','add','.'], )

    subprocess.call(['git','commit',"-am'initial commit of %s'" % proposal])

    subprocess.call(['git', "push", "origin", "master:master"])

    #webbrowser.open('https://travis-ci.org/{slug}'.format(slug=repo.slug))

def packaging_init(log):
    log.info('Please answer the following questions: ')
    from flit.init import TerminalIniter
    TerminalIniter().initialise()

<|endoftext|>


<commit_before>class LRUCache:

    # @param capacity, an integer
    def __init__(self, capacity):
        self.q = []
        self.capacity = capacity;
        self.store = {}

    # @return an integer
    def get(self, key):
        if key in self.store:
            self.lifttofirst(key)
            return self.store[key]
        else:
            return -1

    def lifttofirst(self, key):
        # q[i] <==> q[end]
        for i in range(len(self.q)):
            if self.q[i] == key:
                break
        self.q.pop(i)
        self.q.append(key)

    # @param key, an integer
    # @param value, an integer
    # @return nothing
    def set(self, key, value):
        if key in self.store:
            self.lifttofirst(key)
        else:
            if len(self.q) == self.capacity:
                discard = self.q.pop(0)
                del self.store[discard]
            self.q.append(key)
        self.store[key] = value

s = LRUCache(3)
s.set(1, 1)
s.set(2, 2)
s.set(3, 3)
s.set(2, 4)
s.set(4, 5)
assert s.get(1) == -1
assert s.get(2) == 4

s = LRUCache(2)
s.set(2,1)
s.set(1,1)
assert s.get(2) == 1
s.set(4,1)
assert s.get(1) == -1
assert s.get(2) == 1
<commit_msg>AC O(1) both set and get with doubly linked list<commit_after>class LRUCache:

    # @param capacity, an integer
    def __init__(self, capacity):
        self.q = []
        self.capacity = capacity;
        self.store = {}
        self.dbl = DBLinkedList()

    # @return an integer
    def get(self, key):
        if key in self.store:
            self.dbl.lift(self.store[key])
            return self.store[key].value
        return -1

    # @param key, an integer
    # @param value, an integer
    # @return nothing
    def set(self, key, value):
        if key in self.store:
            n = self.store[key]
            n.value = value
            self.dbl.remove(n)
            self.dbl.append(n)
        else:
            if self.capacity == len(self.store):
                k = self.dbl.pop()
                del self.store[k]
            n = Node(key, value)
            self.dbl.append(n)
            self.store[key] = n

class Node:
    def __init__(self, key, value):
        self.value = value
        self.key = key
        self.pre = None
        self.next = None

class DBLinkedList:
    def __init__(self):
        self.head = Node(None, None)
        self.tail = Node(None, None)
        self.head.next = self.tail
        self.tail.pre = self.head

    def append(self, node):
        tmp = self.tail.pre
        tmp.next = node
        node.pre = tmp
        node.next = self.tail
        self.tail.pre = node
    def remove(self, node):
        node.pre.next = node.next
        node.next.pre = node.pre
    def pop(self):
        tmp = self.head.next
        self.head.next = tmp.next
        tmp.next.pre = self.head
        return tmp.key

    def lift(self, node):
        self.remove(node)
        self.append(node)

s = LRUCache(3)
s.set(1, 1)
s.set(2, 2)
s.set(3, 3)
s.set(2, 4)
s.set(4, 5)
assert s.get(1) == -1
assert s.get(2) == 4

s = LRUCache(2)
s.set(2,1)
s.set(1,1)
assert s.get(2) == 1
s.set(4,1)
assert s.get(1) == -1
assert s.get(2) == 1

s = LRUCache(1)
s.set(2,1)
assert s.get(2) == 1
<|endoftext|>