<commit_before>import unittest
import pymongo
import bson
from bson import objectid
from pymongo import MongoClient

class MidasWithPythonDriverTest(unittest.TestCase):
    def setUp(self):
        self.client = MongoClient('localhost', 27020)
        self.document = {"_id":objectid.ObjectId() ,"name":"pythonTest"}

    def connect(self):
        assert self.client.alive() , 'Mongo Client is not connected'

    def insert(self):
        db = self.client['testDatabase']
        collection = db['testCollection']
        id = collection.insert(self.document)
        assert id != None , 'Document is not inserted'

    def read(self):
        db = self.client['testDatabase']
        collection = db['testCollection']
        readDocument = collection.find_one()
        assert readDocument != None , 'Cannot read document'

    def update(self):
        db = self.client['testDatabase']
        collection = db['testCollection']
        result = collection.update({"name":"pythonTest"}, {"name":"test1"})
        assert result['err'] != 'None' , 'Update failed'

    def delete(self):
        db = self.client['testDatabase']
        collection = db['testCollection']
        result = collection.remove({"name":"test1"})
        assert result['err'] != 'None' , 'Deletion of document failed'

    def drop(self):
        result = self.client.drop_database('testDatabase')
        assert result != 'None' , 'Cannot drop database'

    def disconnect(self):
        result = self.client.disconnect()
        assert result != 'None' , 'Cannot disconnect from mongo'

MidasWithPythonDriverSuite = unittest.TestSuite()
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("connect"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("insert"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("read"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("update"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("delete"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("drop"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("disconnect"))

runner = unittest.TextTestRunner()
runner.run(MidasWithPythonDriverSuite)<commit_msg>Added tearDown for python driver test<commit_after>import unittest
import pymongo
import bson
from bson import objectid
from pymongo import MongoClient

class MidasWithPythonDriverTest(unittest.TestCase):
    def setUp(self):
        self.client = MongoClient('localhost', 27020)
        self.document = {"_id":objectid.ObjectId() ,"name":"pythonTest"}

    def connect(self):
        assert self.client.alive() , 'Mongo Client is not connected'

    def insert(self):
        db = self.client['testDatabase']
        collection = db['testCollection']
        id = collection.insert(self.document)
        assert id != None , 'Document is not inserted'

    def read(self):
        db = self.client['testDatabase']
        collection = db['testCollection']
        readDocument = collection.find_one()
        assert readDocument != None , 'Cannot read document'

    def update(self):
        db = self.client['testDatabase']
        collection = db['testCollection']
        result = collection.update({"name":"pythonTest"}, {"name":"test1"})
        assert result['err'] != 'None' , 'Update failed'

    def delete(self):
        db = self.client['testDatabase']
        collection = db['testCollection']
        result = collection.remove({"name":"test1"})
        assert result['err'] != 'None' , 'Deletion of document failed'

    def drop(self):
        result = self.client.drop_database('testDatabase')
        assert result != 'None' , 'Cannot drop database'

    def disconnect(self):
        result = self.client.disconnect()
        assert result != 'None' , 'Cannot disconnect from mongo'

    def tearDown(self):
        self.client.close()

MidasWithPythonDriverSuite = unittest.TestSuite()
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("connect"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("insert"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("read"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("update"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("delete"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("drop"))
MidasWithPythonDriverSuite.addTest(MidasWithPythonDriverTest("disconnect"))

runner = unittest.TextTestRunner()
runner.run(MidasWithPythonDriverSuite)<|endoftext|>


<commit_before>#!/usr/bin/env python

###
# Copyright (c) 2002, Jeremiah Fincher
# 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 author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# 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 OWNER 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.
###

from fix import *

import os
import sys
import imp

import conf
import debug
import world
import ircdb
import ircmsgs
import drivers
import ircutils
import callbacks

def getChannel(msg, args):
    """Returns the channel the msg came over or the channel given in args.

    If the channel was given in args, args is modified (the channel is
    removed).
    """
    if args and ircutils.isChannel(args[0]):
        return args.pop(0)
    elif ircutils.isChannel(msg.args[0]):
        return msg.args[0]
    else:
        raise callbacks.Error, 'Command must be sent in a channel or ' \
                               'include a channel in its arguments.'

def getArgs(args, needed=1, optional=0):
    """Take the needed arguments from args.

    Always returns a list of size needed + optional, filling it with however
    many empty strings is necessary to fill the tuple to the right size.

    If there aren't enough args even to satisfy needed, raise an error and
    let the caller handle sending the help message.
    """
    if len(args) < needed:
        raise callbacks.ArgumentError
    if len(args) < needed + optional:
        ret = list(args) + ([''] * (needed + optional - len(args)))
    elif len(args) >= needed + optional:
        ret = list(args[:needed + optional - 1])
        ret.append(' '.join(args[needed + optional - 1:]))
    if len(ret) == 1:
        return ret[0]
    else:
        return ret

def getKeywordArgs(irc, msg, d=None):
    if d is None:
        d = {}
    args = []
    tokenizer = callbacks.Tokenizer('=')
    s = callbacks.addressed(irc.nick, msg)
    tokens = tokenizer.tokenize(s) + [None, None]
    counter = 0
    for (left, middle, right) in window(tokens, 3):
        if counter:
            counter -= 1
            continue
        elif middle == '=':
            d[callbacks.canonicalName(left)] = right
            counter = 2
        else:
            args.append(left)
    del args[0] # The command name itself.
    return (args, d)

def checkCapability(f, capability):
    def newf(self, irc, msg, args):
        if ircdb.checkCapability(msg.prefix, capability):
            f(self, irc, msg, args)
        else:
            irc.error(msg, conf.replyNoCapability % capability)
    newf.__doc__ = f.__doc__
    return newf

class CapabilityCheckingPrivmsg(callbacks.Privmsg):
    capability = '' # To satisfy PyChecker
    def callCommand(self, f, irc, msg, args):
        if ircdb.checkCapability(msg.prefix, self.capability):
            callbacks.Privmsg.callCommand(self, f, irc, msg, args)
        else:
            irc.error(msg, conf.replyNoCapability % self.capability)

class OwnerCommands(CapabilityCheckingPrivmsg):
    capability = 'owner'
    def __init__(self):
        callbacks.Privmsg.__init__(self)
        setattr(self.__class__, 'exec', self._exec)

    def eval(self, irc, msg, args):
        """<string to be evaluated by the Python interpreter>"""
        if conf.allowEval:
            s = getArgs(args)
            try:
                irc.reply(msg, repr(eval(s)))
            except Exception, e:
                irc.reply(msg, debug.exnToString(e))
        else:
            irc.error(msg, conf.replyEvalNotAllowed)

    def _exec(self, irc, msg, args):
        """<code to exec>"""
        if conf.allowEval:
            s = getArgs(args)
            try:
                exec s
                irc.reply(msg, conf.replySuccess)
            except Exception, e:
                irc.reply(msg, debug.exnToString(e))
        else:
            irc.error(msg, conf.replyEvalNotAllowed)

    def setdefaultcapability(self, irc, msg, args):
        """<capability>

        Sets the default capability to be allowed for any command.
        """
        capability = getArgs(args)
        conf.defaultCapabilities[capability] = True
        irc.reply(msg, conf.replySuccess)

    def unsetdefaultcapability(self, irc, msg, args):
        """<capability>

        Unsets the default capability for any command.
        """
        capability = getArgs(args)
        del conf.defaultCapabilities[capability]
        irc.reply(msg, conf.replySuccess)

    def settrace(self, irc, msg, args):
        """takes no arguments

        Starts the function-tracing debug mode; beware that this makes *huge*
        logfiles.
        """
        sys.settrace(debug.tracer)
        irc.reply(msg, conf.replySuccess)

    def unsettrace(self, irc, msg, args):
        """takes no arguments

        Stops the function-tracing debug mode."""
        sys.settrace(None)
        irc.reply(msg, conf.replySuccess)

    def ircquote(self, irc, msg, args):
        """<string to be sent to the server>

        Sends the raw string given to the server.
        """
        s = getArgs(args)
        try:
            m = ircmsgs.IrcMsg(s)
            irc.queueMsg(m)
        except Exception:
            debug.recoverableException()
            irc.error(msg, conf.replyError)

    def quit(self, irc, msg, args):
        """[<int return value>]

        Exits the program with the given return value (the default is 0)
        """
        try:
            i = int(args[0])
        except (ValueError, IndexError):
            i = 0
        for driver in drivers._drivers.itervalues():
            driver.die()
        for irc in world.ircs:
            irc.die()
        debug.exit(i)

    def flush(self, irc, msg, args):
        """takes no arguments

        Runs all the periodic flushers in world.flushers.
        """
        world.flush()
        irc.reply(msg, conf.replySuccess)

    def set(self, irc, msg, args):
        """<name> <value>

        Sets the runtime variable <name> to <value>.  Currently used variables
        include "noflush" which, if set to true value, will prevent the
        periodic flushing that normally occurs.
        """
        (name, value) = getArgs(args, optional=1)
        world.tempvars[name] = value
        irc.reply(msg, conf.replySuccess)

    def unset(self, irc, msg, args):
        """<name>

        Unsets the value of variables set via the 'set' command.
        """
        name = getArgs(args)
        try:
            del world.tempvars[name]
            irc.reply(msg, conf.replySuccess)
        except KeyError:
            irc.error(msg, 'That variable wasn\'t set.')

    def load(self, irc, msg, args):
        """<plugin>

        Loads the plugin <plugin> from the plugins/ directory.
        """
        name = getArgs(args)
        if name in [cb.name() for cb in irc.callbacks]:
            irc.error(msg, 'That module is already loaded.')
            return
        try:
            moduleInfo = imp.find_module(name)
        except ImportError:
            irc.error(msg, 'No plugin %s exists.' % name)
            return
        module = imp.load_module(name, *moduleInfo)
        callback = module.Class()
        irc.addCallback(callback)
        irc.reply(msg, conf.replySuccess)

    '''
    def superreload(self, irc, msg, args):
        """<module name>

        Reloads a module, hopefully such that all vestiges of the old module
        are gone.
        """
        name = getArgs(args)
        world.superReload(__import__(name))
        irc.reply(msg, conf.replySuccess)
    '''

    def reload(self, irc, msg, args):
        """<callback name>

        Unloads and subsequently reloads the callback by name; use the 'list'
        command to see a list of the currently loaded callbacks.
        """
        name = getArgs(args)
        callbacks = irc.removeCallback(name)
        if callbacks:
            try:
                moduleInfo = imp.find_module(name)
                module = imp.load_module(name, *moduleInfo)
                callback = module.Class()
                irc.addCallback(callback)
                for callback in callbacks:
                    callback.die()
                irc.reply(msg, conf.replySuccess)
            except ImportError:
                for callback in callbacks:
                    irc.addCallback(callback)
                irc.error(msg, 'No plugin %s exists.' % name)
        else:
            irc.error(msg, 'There was no callback %s.' % name)

    def unload(self, irc, msg, args):
        """<callback name>

        Unloads the callback by name; use the 'list' command to see a list
        of the currently loaded callbacks.
        """
        name = getArgs(args)
        callbacks = irc.removeCallback(name)
        if callbacks:
            for callback in callbacks:
                callback.die()
            irc.reply(msg, conf.replySuccess)
        else:
            irc.error(msg, 'There was no callback %s' % name)

    def cvsup(self, irc, msg, args):
        """takes no arguments"""
        irc.reply(msg, str(os.system('cvs up')))

    def say(self, irc, msg, args):
        """<channel> <text>"""
        (channel, text) = getArgs(args, needed=2)
        irc.queueMsg(ircmsgs.privmsg(channel, text))

standardPrivmsgModules = [OwnerCommands]

# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
<commit_msg>Made the old callbacks die before the new callbacks are loaded.<commit_after>#!/usr/bin/env python

###
# Copyright (c) 2002, Jeremiah Fincher
# 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 author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# 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 OWNER 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.
###

from fix import *

import os
import sys
import imp

import conf
import debug
import world
import ircdb
import ircmsgs
import drivers
import ircutils
import callbacks

def getChannel(msg, args):
    """Returns the channel the msg came over or the channel given in args.

    If the channel was given in args, args is modified (the channel is
    removed).
    """
    if args and ircutils.isChannel(args[0]):
        return args.pop(0)
    elif ircutils.isChannel(msg.args[0]):
        return msg.args[0]
    else:
        raise callbacks.Error, 'Command must be sent in a channel or ' \
                               'include a channel in its arguments.'

def getArgs(args, needed=1, optional=0):
    """Take the needed arguments from args.

    Always returns a list of size needed + optional, filling it with however
    many empty strings is necessary to fill the tuple to the right size.

    If there aren't enough args even to satisfy needed, raise an error and
    let the caller handle sending the help message.
    """
    if len(args) < needed:
        raise callbacks.ArgumentError
    if len(args) < needed + optional:
        ret = list(args) + ([''] * (needed + optional - len(args)))
    elif len(args) >= needed + optional:
        ret = list(args[:needed + optional - 1])
        ret.append(' '.join(args[needed + optional - 1:]))
    if len(ret) == 1:
        return ret[0]
    else:
        return ret

def getKeywordArgs(irc, msg, d=None):
    if d is None:
        d = {}
    args = []
    tokenizer = callbacks.Tokenizer('=')
    s = callbacks.addressed(irc.nick, msg)
    tokens = tokenizer.tokenize(s) + [None, None]
    counter = 0
    for (left, middle, right) in window(tokens, 3):
        if counter:
            counter -= 1
            continue
        elif middle == '=':
            d[callbacks.canonicalName(left)] = right
            counter = 2
        else:
            args.append(left)
    del args[0] # The command name itself.
    return (args, d)

def checkCapability(f, capability):
    def newf(self, irc, msg, args):
        if ircdb.checkCapability(msg.prefix, capability):
            f(self, irc, msg, args)
        else:
            irc.error(msg, conf.replyNoCapability % capability)
    newf.__doc__ = f.__doc__
    return newf

class CapabilityCheckingPrivmsg(callbacks.Privmsg):
    capability = '' # To satisfy PyChecker
    def callCommand(self, f, irc, msg, args):
        if ircdb.checkCapability(msg.prefix, self.capability):
            callbacks.Privmsg.callCommand(self, f, irc, msg, args)
        else:
            irc.error(msg, conf.replyNoCapability % self.capability)

class OwnerCommands(CapabilityCheckingPrivmsg):
    capability = 'owner'
    def __init__(self):
        callbacks.Privmsg.__init__(self)
        setattr(self.__class__, 'exec', self._exec)

    def eval(self, irc, msg, args):
        """<string to be evaluated by the Python interpreter>"""
        if conf.allowEval:
            s = getArgs(args)
            try:
                irc.reply(msg, repr(eval(s)))
            except Exception, e:
                irc.reply(msg, debug.exnToString(e))
        else:
            irc.error(msg, conf.replyEvalNotAllowed)

    def _exec(self, irc, msg, args):
        """<code to exec>"""
        if conf.allowEval:
            s = getArgs(args)
            try:
                exec s
                irc.reply(msg, conf.replySuccess)
            except Exception, e:
                irc.reply(msg, debug.exnToString(e))
        else:
            irc.error(msg, conf.replyEvalNotAllowed)

    def setdefaultcapability(self, irc, msg, args):
        """<capability>

        Sets the default capability to be allowed for any command.
        """
        capability = getArgs(args)
        conf.defaultCapabilities[capability] = True
        irc.reply(msg, conf.replySuccess)

    def unsetdefaultcapability(self, irc, msg, args):
        """<capability>

        Unsets the default capability for any command.
        """
        capability = getArgs(args)
        del conf.defaultCapabilities[capability]
        irc.reply(msg, conf.replySuccess)

    def settrace(self, irc, msg, args):
        """takes no arguments

        Starts the function-tracing debug mode; beware that this makes *huge*
        logfiles.
        """
        sys.settrace(debug.tracer)
        irc.reply(msg, conf.replySuccess)

    def unsettrace(self, irc, msg, args):
        """takes no arguments

        Stops the function-tracing debug mode."""
        sys.settrace(None)
        irc.reply(msg, conf.replySuccess)

    def ircquote(self, irc, msg, args):
        """<string to be sent to the server>

        Sends the raw string given to the server.
        """
        s = getArgs(args)
        try:
            m = ircmsgs.IrcMsg(s)
            irc.queueMsg(m)
        except Exception:
            debug.recoverableException()
            irc.error(msg, conf.replyError)

    def quit(self, irc, msg, args):
        """[<int return value>]

        Exits the program with the given return value (the default is 0)
        """
        try:
            i = int(args[0])
        except (ValueError, IndexError):
            i = 0
        for driver in drivers._drivers.itervalues():
            driver.die()
        for irc in world.ircs:
            irc.die()
        debug.exit(i)

    def flush(self, irc, msg, args):
        """takes no arguments

        Runs all the periodic flushers in world.flushers.
        """
        world.flush()
        irc.reply(msg, conf.replySuccess)

    def set(self, irc, msg, args):
        """<name> <value>

        Sets the runtime variable <name> to <value>.  Currently used variables
        include "noflush" which, if set to true value, will prevent the
        periodic flushing that normally occurs.
        """
        (name, value) = getArgs(args, optional=1)
        world.tempvars[name] = value
        irc.reply(msg, conf.replySuccess)

    def unset(self, irc, msg, args):
        """<name>

        Unsets the value of variables set via the 'set' command.
        """
        name = getArgs(args)
        try:
            del world.tempvars[name]
            irc.reply(msg, conf.replySuccess)
        except KeyError:
            irc.error(msg, 'That variable wasn\'t set.')

    def load(self, irc, msg, args):
        """<plugin>

        Loads the plugin <plugin> from the plugins/ directory.
        """
        name = getArgs(args)
        if name in [cb.name() for cb in irc.callbacks]:
            irc.error(msg, 'That module is already loaded.')
            return
        try:
            moduleInfo = imp.find_module(name)
        except ImportError:
            irc.error(msg, 'No plugin %s exists.' % name)
            return
        module = imp.load_module(name, *moduleInfo)
        callback = module.Class()
        irc.addCallback(callback)
        irc.reply(msg, conf.replySuccess)

    '''
    def superreload(self, irc, msg, args):
        """<module name>

        Reloads a module, hopefully such that all vestiges of the old module
        are gone.
        """
        name = getArgs(args)
        world.superReload(__import__(name))
        irc.reply(msg, conf.replySuccess)
    '''

    def reload(self, irc, msg, args):
        """<callback name>

        Unloads and subsequently reloads the callback by name; use the 'list'
        command to see a list of the currently loaded callbacks.
        """
        name = getArgs(args)
        callbacks = irc.removeCallback(name)
        if callbacks:
            for callback in callbacks:
                callback.die()
            try:
                moduleInfo = imp.find_module(name)
                module = imp.load_module(name, *moduleInfo)
                callback = module.Class()
                irc.addCallback(callback)
                irc.reply(msg, conf.replySuccess)
            except ImportError:
                for callback in callbacks:
                    irc.addCallback(callback)
                irc.error(msg, 'No plugin %s exists.' % name)
        else:
            irc.error(msg, 'There was no callback %s.' % name)

    def unload(self, irc, msg, args):
        """<callback name>

        Unloads the callback by name; use the 'list' command to see a list
        of the currently loaded callbacks.
        """
        name = getArgs(args)
        callbacks = irc.removeCallback(name)
        if callbacks:
            for callback in callbacks:
                callback.die()
            irc.reply(msg, conf.replySuccess)
        else:
            irc.error(msg, 'There was no callback %s' % name)

    def cvsup(self, irc, msg, args):
        """takes no arguments"""
        irc.reply(msg, str(os.system('cvs up')))

    def say(self, irc, msg, args):
        """<channel> <text>"""
        (channel, text) = getArgs(args, needed=2)
        irc.queueMsg(ircmsgs.privmsg(channel, text))

standardPrivmsgModules = [OwnerCommands]

# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
<|endoftext|>


<commit_before>#!/usr/bin/env python

###
# Copyright (c) 2002, Jeremiah Fincher
# 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 author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# 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 OWNER 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.
###

"""
Includes various accessories for callbacks.Privmsg based callbacks.
"""

__revision__ = "$Id$"

import fix

import time
import types
import threading

import conf
import ircdb
import world
import ircutils
import callbacks
import structures

def getChannel(msg, args):
    """Returns the channel the msg came over or the channel given in args.

    If the channel was given in args, args is modified (the channel is
    removed).
    """
    if args and ircutils.isChannel(args[0]):
        return args.pop(0)
    elif ircutils.isChannel(msg.args[0]):
        return msg.args[0]
    else:
        raise callbacks.Error, 'Command must be sent in a channel or ' \
                               'include a channel in its arguments.'

def getArgs(args, required=1, optional=0):
    """Take the required/optional arguments from args.

    Always returns a list of size required + optional, filling it with however
    many empty strings is necessary to fill the tuple to the right size.

    If there aren't enough args even to satisfy required, raise an error and
    let the caller handle sending the help message.
    """
    if len(args) < required:
        raise callbacks.ArgumentError
    if len(args) < required + optional:
        ret = list(args) + ([''] * (required + optional - len(args)))
    elif len(args) >= required + optional:
        ret = list(args[:required + optional - 1])
        ret.append(' '.join(args[required + optional - 1:]))
    if len(ret) == 1:
        return ret[0]
    else:
        return ret

def checkCapability(f, capability):
    """Makes sure a user has a certain capability before a command will run."""
    def newf(self, irc, msg, args):
        if ircdb.checkCapability(msg.prefix, capability):
            f(self, irc, msg, args)
        else:
            self.log.warning('%r attempted %s without %s.',
                             msg.prefix, f.func_name, capability)
            irc.error(msg, conf.replyNoCapability % capability)
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

def checkChannelCapability(f, capability):
    """Makes sure a user has a certain channel capability before running f.

    Do note that you need to add a "channel" argument to your argument list.
    """
    def newf(self, irc, msg, args, *L):
        channel = getChannel(msg, args)
        chancap = ircdb.makeChannelCapability(channel, capability)
        if ircdb.checkCapability(msg.prefix, chancap):
            L += (channel,)
            ff = types.MethodType(f, self, self.__class__)
            ff(irc, msg, args, *L)
        else:
            self.log.warning('%r attempted %s without %s.',
                             msg.prefix, f.func_name, capability)
            irc.error(msg, conf.replyNoCapability % chancap)
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

def thread(f):
    """Makes sure a command spawns a thread when called."""
    def newf(self, irc, msg, args, *L):
        ff = types.MethodType(f, self, self.__class__)
        t = callbacks.CommandThread(self.callCommand, ff, irc, msg, args, *L)
        t.start()
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

def name(f):
    """Makes sure a name is available based on conf.requireRegistration."""
    def newf(self, irc, msg, args, *L):
        try:
            name = ircdb.users.getUser(msg.prefix).name
        except KeyError:
            if conf.requireRegistration:
                irc.error(msg, conf.replyNotRegistered)
                return
            else:
                name = msg.prefix
        L = (name,) + L
        ff = types.MethodType(f, self, self.__class__)
        ff(irc, msg, args, *L)
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

def channel(f):
    """Gives the command an extra channel arg as if it had called getChannel"""
    def newf(self, irc, msg, args, *L):
        channel = getChannel(msg, args)
        L = (channel,) + L
        ff = types.MethodType(f, self, self.__class__)
        ff(irc, msg, args, *L)
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

def urlSnarfer(f):
    """Protects the snarfer from loops and whatnot."""
    def newf(self, irc, msg, match, *L):
        now = time.time()
        cutoff = now - conf.snarfThrottle
        q = getattr(self, '_snarfedUrls', None)
        if q is None:
            q = structures.smallqueue()
            self._snarfedUrls = q
        while q and q[0][2] < cutoff:
            q.dequeue()
        url = match.group(0)
        if any(lambda t: t[0] == url and t[1] == msg.args[0], q) and \
               not world.testing:
            self.log.warning('Refusing to snarf %s from %r.', url, msg.prefix)
        else:
            q.enqueue((url, msg.args[0], now))
            if self.threaded:
                f(self, irc, msg, match, *L)
            else:
                L = list(L)
                t = threading.Thread(target=f, args=[self, irc, msg, match]+L)
                t.setDaemon(True)
                t.start()
                world.threadsSpawned += 1
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

class CapabilityCheckingPrivmsg(callbacks.Privmsg):
    """A small subclass of callbacks.Privmsg that checks self.capability
    before allowing any command to be called.
    """
    capability = '' # To satisfy PyChecker
    def callCommand(self, f, irc, msg, args):
        if ircdb.checkCapability(msg.prefix, self.capability):
            callbacks.Privmsg.callCommand(self, f, irc, msg, args)
        else:
            self.log.warning('%r tried to call %s without %s.',
                             msg.prefix, f.im_func.func_name, self.capability)
            irc.error(msg, conf.replyNoCapability % self.capability)

# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
<commit_msg>Added an assert to prevent improper usage by inexperienced developers.<commit_after>#!/usr/bin/env python

###
# Copyright (c) 2002, Jeremiah Fincher
# 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 author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# 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 OWNER 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.
###

"""
Includes various accessories for callbacks.Privmsg based callbacks.
"""

__revision__ = "$Id$"

import fix

import time
import types
import threading

import conf
import ircdb
import world
import ircutils
import callbacks
import structures

def getChannel(msg, args):
    """Returns the channel the msg came over or the channel given in args.

    If the channel was given in args, args is modified (the channel is
    removed).
    """
    if args and ircutils.isChannel(args[0]):
        return args.pop(0)
    elif ircutils.isChannel(msg.args[0]):
        return msg.args[0]
    else:
        raise callbacks.Error, 'Command must be sent in a channel or ' \
                               'include a channel in its arguments.'

def getArgs(args, required=1, optional=0):
    """Take the required/optional arguments from args.

    Always returns a list of size required + optional, filling it with however
    many empty strings is necessary to fill the tuple to the right size.

    If there aren't enough args even to satisfy required, raise an error and
    let the caller handle sending the help message.
    """
    assert not isinstance(args, str), 'args should be a list.'
    if len(args) < required:
        raise callbacks.ArgumentError
    if len(args) < required + optional:
        ret = list(args) + ([''] * (required + optional - len(args)))
    elif len(args) >= required + optional:
        ret = list(args[:required + optional - 1])
        ret.append(' '.join(args[required + optional - 1:]))
    if len(ret) == 1:
        return ret[0]
    else:
        return ret

def checkCapability(f, capability):
    """Makes sure a user has a certain capability before a command will run."""
    def newf(self, irc, msg, args):
        if ircdb.checkCapability(msg.prefix, capability):
            f(self, irc, msg, args)
        else:
            self.log.warning('%r attempted %s without %s.',
                             msg.prefix, f.func_name, capability)
            irc.error(msg, conf.replyNoCapability % capability)
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

def checkChannelCapability(f, capability):
    """Makes sure a user has a certain channel capability before running f.

    Do note that you need to add a "channel" argument to your argument list.
    """
    def newf(self, irc, msg, args, *L):
        channel = getChannel(msg, args)
        chancap = ircdb.makeChannelCapability(channel, capability)
        if ircdb.checkCapability(msg.prefix, chancap):
            L += (channel,)
            ff = types.MethodType(f, self, self.__class__)
            ff(irc, msg, args, *L)
        else:
            self.log.warning('%r attempted %s without %s.',
                             msg.prefix, f.func_name, capability)
            irc.error(msg, conf.replyNoCapability % chancap)
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

def thread(f):
    """Makes sure a command spawns a thread when called."""
    def newf(self, irc, msg, args, *L):
        ff = types.MethodType(f, self, self.__class__)
        t = callbacks.CommandThread(self.callCommand, ff, irc, msg, args, *L)
        t.start()
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

def name(f):
    """Makes sure a name is available based on conf.requireRegistration."""
    def newf(self, irc, msg, args, *L):
        try:
            name = ircdb.users.getUser(msg.prefix).name
        except KeyError:
            if conf.requireRegistration:
                irc.error(msg, conf.replyNotRegistered)
                return
            else:
                name = msg.prefix
        L = (name,) + L
        ff = types.MethodType(f, self, self.__class__)
        ff(irc, msg, args, *L)
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

def channel(f):
    """Gives the command an extra channel arg as if it had called getChannel"""
    def newf(self, irc, msg, args, *L):
        channel = getChannel(msg, args)
        L = (channel,) + L
        ff = types.MethodType(f, self, self.__class__)
        ff(irc, msg, args, *L)
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

def urlSnarfer(f):
    """Protects the snarfer from loops and whatnot."""
    def newf(self, irc, msg, match, *L):
        now = time.time()
        cutoff = now - conf.snarfThrottle
        q = getattr(self, '_snarfedUrls', None)
        if q is None:
            q = structures.smallqueue()
            self._snarfedUrls = q
        while q and q[0][2] < cutoff:
            q.dequeue()
        url = match.group(0)
        if any(lambda t: t[0] == url and t[1] == msg.args[0], q) and \
               not world.testing:
            self.log.warning('Refusing to snarf %s from %r.', url, msg.prefix)
        else:
            q.enqueue((url, msg.args[0], now))
            if self.threaded:
                f(self, irc, msg, match, *L)
            else:
                L = list(L)
                t = threading.Thread(target=f, args=[self, irc, msg, match]+L)
                t.setDaemon(True)
                t.start()
                world.threadsSpawned += 1
    newf = types.FunctionType(newf.func_code, newf.func_globals,
                              f.func_name, closure=newf.func_closure)
    newf.__doc__ = f.__doc__
    return newf

class CapabilityCheckingPrivmsg(callbacks.Privmsg):
    """A small subclass of callbacks.Privmsg that checks self.capability
    before allowing any command to be called.
    """
    capability = '' # To satisfy PyChecker
    def callCommand(self, f, irc, msg, args):
        if ircdb.checkCapability(msg.prefix, self.capability):
            callbacks.Privmsg.callCommand(self, f, irc, msg, args)
        else:
            self.log.warning('%r tried to call %s without %s.',
                             msg.prefix, f.im_func.func_name, self.capability)
            irc.error(msg, conf.replyNoCapability % self.capability)

# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
<|endoftext|>


<commit_before>#! /usr/bin/python

from __future__ import print_function

import sys

with open(sys.argv[1], "r") as f:
    lines = f.readlines()

class Player:
    def __init__(self, name):
        self.name = name
        self.opponent = {}
        self.vs_opponent = {}
        self.games_vs = {}
        self.points_for = 0
        self.points_against = 0

    def won_game(self, rslt):
        sc1, sc2, turn = rslt
        return (sc1 > sc2) or (sc1 == sc2 and turn == 2)

    # op -> name of opponent
    # game -> game number against this opponent
    # myscore -> score for this player for this game
    # opscore -> score for opponent for this game
    # turn -> 1 if first player, 2 if second player
    def add_result(self, op, game, myscore, opscore, turn):
        self.points_for += myscore
        self.points_against += opscore
        if op not in self.opponent:
            self.opponent[op] = [(), (), (), (), ()]
            self.vs_opponent[op] = 0
            self.games_vs[op] = 0
        self.opponent[op][game - 1] = (myscore, opscore, turn)
        self.games_vs[op] += 1
        if self.won_game(self.opponent[op][game - 1]):
            self.vs_opponent[op] += 1

    def fmt_result(self, oplist):
        won = 0
        for i in oplist:
            if i in self.opponent:
                for score in self.opponent[i]:
                    if score:
                        if self.won_game(score):
                            print("1  ", end="")
                            won += 1
                        else:
                            print("0  ", end="")
                    else:
                        print("   ", end="")
            elif i == self.name:
                print("-  -  -  -  -  ", end="")
            else:
                print("               ", end="")
            print("| ", end="")
        print("%2s" % won)

#
# Format of the file we're reading in:
# - lines which start with "p " give the player name
# - lines which start with "g " provide game results
# - all other lines are ignored
#
# Example of p:
# p player1
# p player2
#
# Example of g:
# g 3 player1:23,player2:45
#
# Format is: "game_number player:score,player:score"
#

player = {}
maxlen = 0
for l in lines:
    l = l.rstrip()
    if not l:
        continue
    v = l.split()
    if v[0] == "p":
        player[v[1]] = (Player(v[1]))
        maxlen = max(maxlen, len(v[1]))
    elif v[0] == "g":
        game = int(v[1])
        rslt = v[2].split(",")
        p1 = rslt[0].split(":")
        p2 = rslt[1].split(":")
        p1sc = int(p1[1])
        p2sc = int(p2[1])

        if p1[0] not in player:
            print("Player %s not defined" % p1[0])
            sys.exit(1)
        if p2[0] not in player:
            print("Player %s not defined" % p2[0])
            sys.exit(1)

        player[p1[0]].add_result(p2[0], game, p1sc, p2sc, 1)
        player[p2[0]].add_result(p1[0], game, p2sc, p1sc, 2)

fmt = "%%%ss" % maxlen
lfmt = "  %%-%ss" % maxlen
pad = 15 - maxlen  # 15 magic number comes from 5 games * 3 spaces per game
plist = sorted(player, key=lambda s: s.lower())

print("Game Results:")
print(fmt % "", end="")
for p in plist:
    print(lfmt % p, end="")
    print(" " * pad, end="")
print("")

print(fmt % "", end="")
print(" ", end="")
for p in plist:
    print(" 1  2  3  4  5  |", end="")
print(" Total Games Won")

for p in plist:
    print(fmt % p, end="")
    print(": ", end="")
    player[p].fmt_result(plist)

print("---")

print("Match Results:")
for p in plist:
    wins = 0
    losses = 0
    for op in player[p].vs_opponent:
        if player[p].games_vs[op] < 5:
            continue
        rslt = player[p].vs_opponent[op]
        if rslt > 2:
            wins += 1
        elif rslt <= 2:
            losses += 1
    if wins > 0 or losses > 0:
        print("%s%s: " % (" " * (pad - len(p)), p), end="")
        match = list()

        def sing_or_pl(score, sing, pl):
            if score == 1:
                return "%s %s" % (score, sing)
            return "%s %s" % (score, pl)

        if wins > 0:
            match.append(sing_or_pl(wins, "win", "wins"))
        if losses > 0:
            match.append(sing_or_pl(losses, "loss", "losses"))
        print(", ".join(match))

print("---")

print("Total Points:")
for p in plist:
    print(lfmt % p, end="")
    print(" %10s points for / %10s points against" % \
            (player[p].points_for, player[p].points_against))
<commit_msg>Fail fatally if overwriting an existing score.<commit_after>#! /usr/bin/python

from __future__ import print_function

import sys

with open(sys.argv[1], "r") as f:
    lines = f.readlines()

class Player:
    def __init__(self, name):
        self.name = name
        self.opponent = {}
        self.vs_opponent = {}
        self.games_vs = {}
        self.points_for = 0
        self.points_against = 0

    def won_game(self, rslt):
        sc1, sc2, turn = rslt
        return (sc1 > sc2) or (sc1 == sc2 and turn == 2)

    # op -> name of opponent
    # game -> game number against this opponent
    # myscore -> score for this player for this game
    # opscore -> score for opponent for this game
    # turn -> 1 if first player, 2 if second player
    def add_result(self, op, game, myscore, opscore, turn):
        self.points_for += myscore
        self.points_against += opscore
        if op not in self.opponent:
            self.opponent[op] = [None, None, None, None, None]
            self.vs_opponent[op] = 0
            self.games_vs[op] = 0
        if self.opponent[op][game - 1] is None:
            self.opponent[op][game - 1] = (myscore, opscore, turn)
        else:
            print("ERROR: Overwriting %s vs %s, game %s" %
                    (self.name, op, game), file=sys.stderr)
            sys.exit(1)
        self.games_vs[op] += 1
        if self.won_game(self.opponent[op][game - 1]):
            self.vs_opponent[op] += 1

    def fmt_result(self, oplist):
        won = 0
        for i in oplist:
            if i in self.opponent:
                for score in self.opponent[i]:
                    if score:
                        if self.won_game(score):
                            print("1  ", end="")
                            won += 1
                        else:
                            print("0  ", end="")
                    else:
                        print("   ", end="")
            elif i == self.name:
                print("-  -  -  -  -  ", end="")
            else:
                print("               ", end="")
            print("| ", end="")
        print("%2s" % won)

#
# Format of the file we're reading in:
# - lines which start with "p " give the player name
# - lines which start with "g " provide game results
# - all other lines are ignored
#
# Example of p:
# p player1
# p player2
#
# Example of g:
# g 3 player1:23,player2:45
#
# Format is: "game_number player:score,player:score"
#

player = {}
maxlen = 0
for l in lines:
    l = l.rstrip()
    if not l:
        continue
    v = l.split()
    if v[0] == "p":
        player[v[1]] = (Player(v[1]))
        maxlen = max(maxlen, len(v[1]))
    elif v[0] == "g":
        game = int(v[1])
        rslt = v[2].split(",")
        p1 = rslt[0].split(":")
        p2 = rslt[1].split(":")
        p1sc = int(p1[1])
        p2sc = int(p2[1])

        if p1[0] not in player:
            print("Player %s not defined" % p1[0])
            sys.exit(1)
        if p2[0] not in player:
            print("Player %s not defined" % p2[0])
            sys.exit(1)

        player[p1[0]].add_result(p2[0], game, p1sc, p2sc, 1)
        player[p2[0]].add_result(p1[0], game, p2sc, p1sc, 2)

fmt = "%%%ss" % maxlen
lfmt = "  %%-%ss" % maxlen
pad = 15 - maxlen  # 15 magic number comes from 5 games * 3 spaces per game
plist = sorted(player, key=lambda s: s.lower())

print("Game Results:")
print(fmt % "", end="")
for p in plist:
    print(lfmt % p, end="")
    print(" " * pad, end="")
print("")

print(fmt % "", end="")
print(" ", end="")
for p in plist:
    print(" 1  2  3  4  5  |", end="")
print(" Total Games Won")

for p in plist:
    print(fmt % p, end="")
    print(": ", end="")
    player[p].fmt_result(plist)

print("---")

print("Match Results:")
for p in plist:
    wins = 0
    losses = 0
    for op in player[p].vs_opponent:
        if player[p].games_vs[op] < 5:
            continue
        rslt = player[p].vs_opponent[op]
        if rslt > 2:
            wins += 1
        elif rslt <= 2:
            losses += 1
    if wins > 0 or losses > 0:
        print("%s%s: " % (" " * (pad - len(p)), p), end="")
        match = list()

        def sing_or_pl(score, sing, pl):
            if score == 1:
                return "%s %s" % (score, sing)
            return "%s %s" % (score, pl)

        if wins > 0:
            match.append(sing_or_pl(wins, "win", "wins"))
        if losses > 0:
            match.append(sing_or_pl(losses, "loss", "losses"))
        print(", ".join(match))

print("---")

print("Total Points:")
for p in plist:
    print(lfmt % p, end="")
    print(" %10s points for / %10s points against" % \
            (player[p].points_for, player[p].points_against))
<|endoftext|>


<commit_before># Giles: utils.py
# Copyright 2012 Phil Bordelon
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

class Struct(object):
    # Empty class, useful for making "structs."
    pass

def booleanize(msg):
    # This returns:
    # -1 for False
    # 1 for True
    # 0 for invalid input.

    if type(msg) != str:
        return 0

    msg = msg.strip().lower()
    if msg in ('1', 'true', 't', 'yes', 'y', 'on'):
        return 1
    elif msg in ('0', 'false', 'f', 'no', 'n', 'off'):
        return -1

    return 0

LETTERS = "abcdefghijklmnopqrstuvwxyz"
def demangle_move(move_list):

    # Most games take a move in the format Ln, where L is a letter
    # and n is some number.  They make take more than one such move,
    # for games where you move a piece from a to b.  Rather than
    # force every game to reimplement all the various ways players
    # might want to enter this, this function does the best it can
    # to return a valid set of coordinates.  It assumes zero-based
    # indexing, letter first; you may have to reverse entries, etc.
    # to get it how you want, but at least you don't have to parse
    # wacky strings.  This function will properly handle:
    # * a2
    # * a2 b2
    # * a2-b2
    # * a2,b2
    # * a2/b2
    # * a2, b2
    # ...and further coordinated versions of the same, including
    # mixed versions, ones with too many dashes, and so on.  It
    # returns a list of tuples representing the 0-indexed
    # letter/number sets, and a None if any of the entries were
    # nonsense ("aa5", "0", "b", et cetera).

    # Okay, so, to start, let's concatenate whatever list of
    # strings we got...
    move_str = " ".join(move_list)

    # ...and then replace every random character we see in the
    # string with a space.
    new_str = ""
    for char in move_str:
        if char.isalnum():
            new_str += char
        else:
            new_str += " "

    # Now we lowercase it and split it back out into components.
    new_str_bits = new_str.lower().split()

    # Now let's work on each of the bits...
    to_return = []
    for bit in new_str_bits:

        # Is it at least two and at most three characters long?
        if len(bit) < 2 or len(bit) > 3:
            return None

        # Is the first bit a letter and the rest a number?
        if not bit[0].isalpha() or not bit[1:].isdigit():
            return None

        # Okay, good.  Let's get the values.  Remember that it's zero-based,
        # so the number needs to be decremented by one.
        letter_val = LETTERS.index(bit[0])
        number_val = int(bit[1:]) - 1

        to_return.append((letter_val, number_val))

    # Since we never bailed due to bad data, let's return the list.
    return to_return
<commit_msg>utils: New utility function, name_is_valid().<commit_after># Giles: utils.py
# Copyright 2012 Phil Bordelon
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

class Struct(object):
    # Empty class, useful for making "structs."
    pass

def booleanize(msg):
    # This returns:
    # -1 for False
    # 1 for True
    # 0 for invalid input.

    if type(msg) != str:
        return 0

    msg = msg.strip().lower()
    if msg in ('1', 'true', 't', 'yes', 'y', 'on'):
        return 1
    elif msg in ('0', 'false', 'f', 'no', 'n', 'off'):
        return -1

    return 0

LETTERS = "abcdefghijklmnopqrstuvwxyz"
def demangle_move(move_list):

    # Most games take a move in the format Ln, where L is a letter
    # and n is some number.  They make take more than one such move,
    # for games where you move a piece from a to b.  Rather than
    # force every game to reimplement all the various ways players
    # might want to enter this, this function does the best it can
    # to return a valid set of coordinates.  It assumes zero-based
    # indexing, letter first; you may have to reverse entries, etc.
    # to get it how you want, but at least you don't have to parse
    # wacky strings.  This function will properly handle:
    # * a2
    # * a2 b2
    # * a2-b2
    # * a2,b2
    # * a2/b2
    # * a2, b2
    # ...and further coordinated versions of the same, including
    # mixed versions, ones with too many dashes, and so on.  It
    # returns a list of tuples representing the 0-indexed
    # letter/number sets, and a None if any of the entries were
    # nonsense ("aa5", "0", "b", et cetera).

    # Okay, so, to start, let's concatenate whatever list of
    # strings we got...
    move_str = " ".join(move_list)

    # ...and then replace every random character we see in the
    # string with a space.
    new_str = ""
    for char in move_str:
        if char.isalnum():
            new_str += char
        else:
            new_str += " "

    # Now we lowercase it and split it back out into components.
    new_str_bits = new_str.lower().split()

    # Now let's work on each of the bits...
    to_return = []
    for bit in new_str_bits:

        # Is it at least two and at most three characters long?
        if len(bit) < 2 or len(bit) > 3:
            return None

        # Is the first bit a letter and the rest a number?
        if not bit[0].isalpha() or not bit[1:].isdigit():
            return None

        # Okay, good.  Let's get the values.  Remember that it's zero-based,
        # so the number needs to be decremented by one.
        letter_val = LETTERS.index(bit[0])
        number_val = int(bit[1:]) - 1

        to_return.append((letter_val, number_val))

    # Since we never bailed due to bad data, let's return the list.
    return to_return

MAX_NAME_LENGTH = 16
def name_is_valid(name_str):

    # A name in Giles, whether it's a player, table, or channel, must follow
    # a specific pattern:
    # - It must be alphanumeric only;
    # - It must start with a letter, not a number (aliases are numerical);
    # - It must be at most MAX_NAME_LENGTH long.

    # Bail on the easy duds.
    if not name_str or type(name_str) != str or len(name_str) > MAX_NAME_LENGTH:
        return False

    # Is it fully alphanumeric and is the first character not a digit?
    if not name_str.isalnum() or name_str[0].isdigit():
        return False

    # Passed the tests.
    return True
<|endoftext|>


<commit_before># Giles: utils.py
# Copyright 2012 Phil Bordelon
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

class Struct(object):
    # Empty class, useful for making "structs."

    def __init__(self, attributes = {}):

        # For convenience, it supports getting a dictionary of attributes to
        # set.
        for attribute in attributes:
            setattr(self, attribute, attributes[attribute])

def booleanize(msg):
    # This returns:
    # -1 for False
    # 1 for True
    # 0 for invalid input.

    if type(msg) != str:
        return 0

    msg = msg.strip().lower()
    if msg in ('1', 'true', 't', 'yes', 'y', 'on'):
        return 1
    elif msg in ('0', 'false', 'f', 'no', 'n', 'off'):
        return -1

    return 0

LETTERS = "abcdefghijklmnopqrstuvwxyz"
def demangle_move(move_list):

    # Most games take a move in the format Ln, where L is a letter
    # and n is some number.  They make take more than one such move,
    # for games where you move a piece from a to b.  Rather than
    # force every game to reimplement all the various ways players
    # might want to enter this, this function does the best it can
    # to return a valid set of coordinates.  It assumes zero-based
    # indexing, letter first; you may have to reverse entries, etc.
    # to get it how you want, but at least you don't have to parse
    # wacky strings.  This function will properly handle:
    # * a2
    # * a2 b2
    # * a2-b2
    # * a2,b2
    # * a2/b2
    # * a2, b2
    # ...and further coordinated versions of the same, including
    # mixed versions, ones with too many dashes, and so on.  It
    # returns a list of tuples representing the 0-indexed
    # letter/number sets, and a None if any of the entries were
    # nonsense ("aa5", "0", "b", et cetera).

    # Okay, so, to start, let's concatenate whatever list of
    # strings we got...
    move_str = " ".join(move_list)

    # ...and then replace every random character we see in the
    # string with a space.
    new_str = ""
    for char in move_str:
        if char.isalnum():
            new_str += char
        else:
            new_str += " "

    # Now we lowercase it and split it back out into components.
    new_str_bits = new_str.lower().split()

    # Now let's work on each of the bits...
    to_return = []
    for bit in new_str_bits:

        # Is it at least two and at most three characters long?
        if len(bit) < 2 or len(bit) > 3:
            return None

        # Is the first bit a letter and the rest a number?
        if not bit[0].isalpha() or not bit[1:].isdigit():
            return None

        # Okay, good.  Let's get the values.  Remember that it's zero-based,
        # so the number needs to be decremented by one.
        letter_val = LETTERS.index(bit[0])
        number_val = int(bit[1:]) - 1

        to_return.append((letter_val, number_val))

    # Since we never bailed due to bad data, let's return the list.
    return to_return

MAX_NAME_LENGTH = 16
def name_is_valid(name_str):

    # A name in Giles, whether it's a player, table, or channel, must follow
    # a specific pattern:
    # - It must be alphanumeric only;
    # - It must start with a letter, not a number (aliases are numerical);
    # - It must be at most MAX_NAME_LENGTH long.

    # Bail on the easy duds.
    if not name_str or type(name_str) != str or len(name_str) > MAX_NAME_LENGTH:
        return False

    # Is it fully alphanumeric and is the first character not a digit?
    if not name_str.isalnum() or name_str[0].isdigit():
        return False

    # Passed the tests.
    return True
<commit_msg>utils: new function, get_plural_str().<commit_after># Giles: utils.py
# Copyright 2012 Phil Bordelon
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

class Struct(object):
    # Empty class, useful for making "structs."

    def __init__(self, attributes = {}):

        # For convenience, it supports getting a dictionary of attributes to
        # set.
        for attribute in attributes:
            setattr(self, attribute, attributes[attribute])

def booleanize(msg):
    # This returns:
    # -1 for False
    # 1 for True
    # 0 for invalid input.

    if type(msg) != str:
        return 0

    msg = msg.strip().lower()
    if msg in ('1', 'true', 't', 'yes', 'y', 'on'):
        return 1
    elif msg in ('0', 'false', 'f', 'no', 'n', 'off'):
        return -1

    return 0

LETTERS = "abcdefghijklmnopqrstuvwxyz"
def demangle_move(move_list):

    # Most games take a move in the format Ln, where L is a letter
    # and n is some number.  They make take more than one such move,
    # for games where you move a piece from a to b.  Rather than
    # force every game to reimplement all the various ways players
    # might want to enter this, this function does the best it can
    # to return a valid set of coordinates.  It assumes zero-based
    # indexing, letter first; you may have to reverse entries, etc.
    # to get it how you want, but at least you don't have to parse
    # wacky strings.  This function will properly handle:
    # * a2
    # * a2 b2
    # * a2-b2
    # * a2,b2
    # * a2/b2
    # * a2, b2
    # ...and further coordinated versions of the same, including
    # mixed versions, ones with too many dashes, and so on.  It
    # returns a list of tuples representing the 0-indexed
    # letter/number sets, and a None if any of the entries were
    # nonsense ("aa5", "0", "b", et cetera).

    # Okay, so, to start, let's concatenate whatever list of
    # strings we got...
    move_str = " ".join(move_list)

    # ...and then replace every random character we see in the
    # string with a space.
    new_str = ""
    for char in move_str:
        if char.isalnum():
            new_str += char
        else:
            new_str += " "

    # Now we lowercase it and split it back out into components.
    new_str_bits = new_str.lower().split()

    # Now let's work on each of the bits...
    to_return = []
    for bit in new_str_bits:

        # Is it at least two and at most three characters long?
        if len(bit) < 2 or len(bit) > 3:
            return None

        # Is the first bit a letter and the rest a number?
        if not bit[0].isalpha() or not bit[1:].isdigit():
            return None

        # Okay, good.  Let's get the values.  Remember that it's zero-based,
        # so the number needs to be decremented by one.
        letter_val = LETTERS.index(bit[0])
        number_val = int(bit[1:]) - 1

        to_return.append((letter_val, number_val))

    # Since we never bailed due to bad data, let's return the list.
    return to_return

MAX_NAME_LENGTH = 16
def name_is_valid(name_str):

    # A name in Giles, whether it's a player, table, or channel, must follow
    # a specific pattern:
    # - It must be alphanumeric only;
    # - It must start with a letter, not a number (aliases are numerical);
    # - It must be at most MAX_NAME_LENGTH long.

    # Bail on the easy duds.
    if not name_str or type(name_str) != str or len(name_str) > MAX_NAME_LENGTH:
        return False

    # Is it fully alphanumeric and is the first character not a digit?
    if not name_str.isalnum() or name_str[0].isdigit():
        return False

    # Passed the tests.
    return True

def get_plural_str(count, base):

    if count == 1:
        return "1 " + base
    return "%s %ss" % (count, base)
<|endoftext|>


<commit_before>import json
import logging
import os
import pickle
from ast import literal_eval

import networkx as nx
import py2neo
import requests
from networkx import GraphMLReader
from networkx.readwrite import json_graph
from requests_file import FileAdapter

from .canonicalize import decanonicalize_node
from .constants import PYBEL_CONTEXT_TAG, FUNCTION, NAME
from .graph import BELGraph, expand_edges
from .utils import flatten, flatten_graph_data

__all__ = [
    'from_lines',
    'from_path',
    'from_url',
    'to_pickle',
    'to_bytes',
    'from_bytes',
    'from_pickle',
    'to_json',
    'from_json',
    'to_graphml',
    'from_graphml',
    'to_csv',
    'to_neo4j'
]

log = logging.getLogger('pybel')

def from_lines(lines, **kwargs):
    """Loads a BEL graph from an iterable over the lines of a BEL script. This can be a list of strings, file, or other.
    This function is a *very* thin wrapper around :py:meth:`BELGraph`.

    :param lines: an iterable of strings (the lines in a BEL script)
    :type lines: iter
    :param kwargs: keyword arguments to pass to :py:meth:`BELGraph`
    :return: a parsed BEL graph
    :rtype: :class:`BELGraph`
    """
    return BELGraph(lines=lines, **kwargs)

def from_path(path, **kwargs):
    """Loads a BEL graph from a file resource

    :param path: a file path
    :type path: str
    :param kwargs: keyword arguments to pass to :py:meth:`BELGraph`
    :return: a parsed BEL graph
    :rtype: :class:`BELGraph`
    """
    log.info('Loading from path: %s', path)
    with open(os.path.expanduser(path)) as f:
        return BELGraph(lines=f, **kwargs)

def from_url(url, **kwargs):
    """Loads a BEL graph from a URL resource

    :param url: a valid URL pointing to a BEL resource
    :type url: str
    :param kwargs: keyword arguments to pass to :py:meth:`BELGraph`
    :return: a parsed BEL graph
    :rtype: :class:`BELGraph`
    """
    log.info('Loading from url: %s', url)

    session = requests.session()
    session.mount('file://', FileAdapter())

    response = session.get(url)
    response.raise_for_status()

    lines = (line.decode('utf-8') for line in response.iter_lines())

    return BELGraph(lines=lines, **kwargs)

def to_bytes(graph):
    """Converts a graph to bytes (as BytesIO object)

    :param graph: a BEL graph
    :type graph: BELGraph
    :rtype: bytes
    """
    return pickle.dumps(nx.MultiDiGraph(graph), protocol=pickle.HIGHEST_PROTOCOL)

def from_bytes(bytes_graph):
    """Reads a graph from bytes (BytesIO objet)

    :param bytes_graph: File or filename to write
    :type bytes_graph: bytes
    :rtype: :class:`BELGraph`
    """
    return BELGraph(data=pickle.loads(bytes_graph))

def to_pickle(graph, output):
    """Writes this graph to a pickle object with nx.write_gpickle

    Cast as a nx.MultiDiGraph before outputting because the pickle serializer can't handle the PyParsing elements
    within the BELGraph class.

    :param graph: a BEL graph
    :type graph: BELGraph
    :param output: a file or filename to write to
    """
    nx.write_gpickle(nx.MultiDiGraph(graph), output)

def from_pickle(path):
    """Reads a graph from a gpickle file

    :param path: File or filename to read. Filenames ending in .gz or .bz2 will be uncompressed.
    :type path: file or str
    :rtype: :class:`BELGraph`
    """
    return BELGraph(data=nx.read_gpickle(path))

def to_json(graph, output):
    """Writes this graph to a node-link JSON object

    :param graph: a BEL graph
    :type graph: BELGraph
    :param output: a write-supporting file-like object
    """
    data = json_graph.node_link_data(graph)
    data['graph']['annotation_list'] = {k: list(sorted(v)) for k, v in data['graph']['annotation_list'].items()}
    json.dump(data, output, ensure_ascii=False)

def from_json(path):
    """Reads graph from node-link JSON Object

    :param path: file path to read
    :type path: str
    :rtype: :class:`BELGraph`
    """
    with open(os.path.expanduser(path)) as f:
        data = json.load(f)

    for i, node in enumerate(data['nodes']):
        data['nodes'][i]['id'] = tuple(node['id'])

    g = json_graph.node_link_graph(data, directed=True, multigraph=True)
    return BELGraph(data=g)

def to_graphml(graph, output):
    """Writes this graph to GraphML file. Use .graphml extension so Cytoscape can recognize it

    :param graph: a BEL graph
    :type graph: BELGraph
    :param output: a file or filelike object
    """
    g = nx.MultiDiGraph()

    for node, data in graph.nodes(data=True):
        g.add_node(node, json=json.dumps(data))

    for u, v, key, data in graph.edges(data=True, keys=True):
        g.add_edge(u, v, key=key, attr_dict=flatten(data))

    nx.write_graphml(g, output)

def from_graphml(path):
    """Reads a graph from a graphml file

    :param path: File or filename to write
    :type path: file or str
    :rtype: :class:`BELGraph`
    """
    reader = GraphMLReader(node_type=str)
    reader.multigraph = True
    g = list(reader(path=path))[0]
    g = expand_edges(g)
    for n in g.nodes_iter():
        g.node[n] = json.loads(g.node[n]['json'])
    nx.relabel_nodes(g, literal_eval, copy=False)  # shh don't tell anyone
    return BELGraph(data=g)

def to_csv(graph, output):
    """Writes graph to edge list csv

    :param graph: a BEL graph
    :type graph: BELGraph
    :param output: a file or filelike object
    """
    nx.write_edgelist(flatten_graph_data(graph), output, data=True)

def to_neo4j(graph, neo_graph, context=None):
    """Uploads a BEL graph to Neo4J graph database using `py2neo`

    :param graph: a BEL Graph
    :type graph: BELGraph
    :param neo_graph: a py2neo graph object, Refer to the
                        `py2neo documentation <http://py2neo.org/v3/database.html#the-graph>`_
                        for how to build this object.
    :type neo_graph: :class:`py2neo.Graph`
    :param context: a disease context to allow for multiple disease models in one neo4j instance.
                    Each edge will be assigned an attribute :code:`pybel_context` with this value
    :type context: str
    """
    if graph.context is not None:
        context = graph.context

    tx = neo_graph.begin()

    node_map = {}
    for node, data in graph.nodes(data=True):
        node_type = data[FUNCTION]
        attrs = {k: v for k, v in data.items() if k != FUNCTION}

        if NAME in data:
            attrs['value'] = data[NAME]

        node_map[node] = py2neo.Node(node_type, bel=decanonicalize_node(graph, node), **attrs)

        tx.create(node_map[node])

    for u, v, data in graph.edges(data=True):
        neo_u = node_map[u]
        neo_v = node_map[v]
        rel_type = data['relation']
        attrs = flatten(data)
        if context is not None:
            attrs[PYBEL_CONTEXT_TAG] = str(context)
        rel = py2neo.Relationship(neo_u, rel_type, neo_v, **attrs)
        tx.create(rel)

    tx.commit()
<commit_msg>Add encoding support with codecs module<commit_after>import codecs
import json
import logging
import os
import pickle
from ast import literal_eval

import networkx as nx
import py2neo
import requests
from networkx import GraphMLReader
from networkx.readwrite import json_graph
from requests_file import FileAdapter

from .canonicalize import decanonicalize_node
from .constants import PYBEL_CONTEXT_TAG, FUNCTION, NAME
from .graph import BELGraph, expand_edges
from .utils import flatten, flatten_graph_data

__all__ = [
    'from_lines',
    'from_path',
    'from_url',
    'to_pickle',
    'to_bytes',
    'from_bytes',
    'from_pickle',
    'to_json',
    'from_json',
    'to_graphml',
    'from_graphml',
    'to_csv',
    'to_neo4j'
]

log = logging.getLogger('pybel')

def from_lines(lines, **kwargs):
    """Loads a BEL graph from an iterable over the lines of a BEL script. This can be a list of strings, file, or other.
    This function is a *very* thin wrapper around :py:meth:`BELGraph`.

    :param lines: an iterable of strings (the lines in a BEL script)
    :type lines: iter
    :param kwargs: keyword arguments to pass to :py:meth:`BELGraph`
    :return: a parsed BEL graph
    :rtype: :class:`BELGraph`
    """
    return BELGraph(lines=lines, **kwargs)

def from_path(path, encoding='utf-8', **kwargs):
    """Loads a BEL graph from a file resource

    :param path: a file path
    :type path: str
    :param encoding: the encoding to use when reading this file. Is passed to codecs.open.
                     See https://docs.python.org/3/library/codecs.html#standard-encodings for a list of standard
                     encodings. For example, files starting with a UTF-8 BOM should use 'utf_8_sig'
    :type encoding: str
    :param kwargs: keyword arguments to pass to :py:meth:`BELGraph`
    :return: a parsed BEL graph
    :rtype: :class:`BELGraph`
    """
    log.info('Loading from path: %s', path)
    with codecs.open(os.path.expanduser(path), encoding=encoding) as f:
        return BELGraph(lines=f, **kwargs)

def from_url(url, **kwargs):
    """Loads a BEL graph from a URL resource

    :param url: a valid URL pointing to a BEL resource
    :type url: str
    :param kwargs: keyword arguments to pass to :py:meth:`BELGraph`
    :return: a parsed BEL graph
    :rtype: :class:`BELGraph`
    """
    log.info('Loading from url: %s', url)

    session = requests.session()
    session.mount('file://', FileAdapter())

    response = session.get(url)
    response.raise_for_status()

    lines = (line.decode('utf-8') for line in response.iter_lines())

    return BELGraph(lines=lines, **kwargs)

def to_bytes(graph):
    """Converts a graph to bytes (as BytesIO object)

    :param graph: a BEL graph
    :type graph: BELGraph
    :rtype: bytes
    """
    return pickle.dumps(nx.MultiDiGraph(graph), protocol=pickle.HIGHEST_PROTOCOL)

def from_bytes(bytes_graph):
    """Reads a graph from bytes (BytesIO objet)

    :param bytes_graph: File or filename to write
    :type bytes_graph: bytes
    :rtype: :class:`BELGraph`
    """
    return BELGraph(data=pickle.loads(bytes_graph))

def to_pickle(graph, output):
    """Writes this graph to a pickle object with nx.write_gpickle

    Cast as a nx.MultiDiGraph before outputting because the pickle serializer can't handle the PyParsing elements
    within the BELGraph class.

    :param graph: a BEL graph
    :type graph: BELGraph
    :param output: a file or filename to write to
    """
    nx.write_gpickle(nx.MultiDiGraph(graph), output)

def from_pickle(path):
    """Reads a graph from a gpickle file

    :param path: File or filename to read. Filenames ending in .gz or .bz2 will be uncompressed.
    :type path: file or str
    :rtype: :class:`BELGraph`
    """
    return BELGraph(data=nx.read_gpickle(path))

def to_json(graph, output):
    """Writes this graph to a node-link JSON object

    :param graph: a BEL graph
    :type graph: BELGraph
    :param output: a write-supporting file-like object
    """
    data = json_graph.node_link_data(graph)
    data['graph']['annotation_list'] = {k: list(sorted(v)) for k, v in data['graph']['annotation_list'].items()}
    json.dump(data, output, ensure_ascii=False)

def from_json(path):
    """Reads graph from node-link JSON Object

    :param path: file path to read
    :type path: str
    :rtype: :class:`BELGraph`
    """
    with open(os.path.expanduser(path)) as f:
        data = json.load(f)

    for i, node in enumerate(data['nodes']):
        data['nodes'][i]['id'] = tuple(node['id'])

    g = json_graph.node_link_graph(data, directed=True, multigraph=True)
    return BELGraph(data=g)

def to_graphml(graph, output):
    """Writes this graph to GraphML file. Use .graphml extension so Cytoscape can recognize it

    :param graph: a BEL graph
    :type graph: BELGraph
    :param output: a file or filelike object
    """
    g = nx.MultiDiGraph()

    for node, data in graph.nodes(data=True):
        g.add_node(node, json=json.dumps(data))

    for u, v, key, data in graph.edges(data=True, keys=True):
        g.add_edge(u, v, key=key, attr_dict=flatten(data))

    nx.write_graphml(g, output)

def from_graphml(path):
    """Reads a graph from a graphml file

    :param path: File or filename to write
    :type path: file or str
    :rtype: :class:`BELGraph`
    """
    reader = GraphMLReader(node_type=str)
    reader.multigraph = True
    g = list(reader(path=path))[0]
    g = expand_edges(g)
    for n in g.nodes_iter():
        g.node[n] = json.loads(g.node[n]['json'])
    nx.relabel_nodes(g, literal_eval, copy=False)  # shh don't tell anyone
    return BELGraph(data=g)

def to_csv(graph, output):
    """Writes graph to edge list csv

    :param graph: a BEL graph
    :type graph: BELGraph
    :param output: a file or filelike object
    """
    nx.write_edgelist(flatten_graph_data(graph), output, data=True)

def to_neo4j(graph, neo_graph, context=None):
    """Uploads a BEL graph to Neo4J graph database using `py2neo`

    :param graph: a BEL Graph
    :type graph: BELGraph
    :param neo_graph: a py2neo graph object, Refer to the
                        `py2neo documentation <http://py2neo.org/v3/database.html#the-graph>`_
                        for how to build this object.
    :type neo_graph: :class:`py2neo.Graph`
    :param context: a disease context to allow for multiple disease models in one neo4j instance.
                    Each edge will be assigned an attribute :code:`pybel_context` with this value
    :type context: str
    """
    if graph.context is not None:
        context = graph.context

    tx = neo_graph.begin()

    node_map = {}
    for node, data in graph.nodes(data=True):
        node_type = data[FUNCTION]
        attrs = {k: v for k, v in data.items() if k != FUNCTION}

        if NAME in data:
            attrs['value'] = data[NAME]

        node_map[node] = py2neo.Node(node_type, bel=decanonicalize_node(graph, node), **attrs)

        tx.create(node_map[node])

    for u, v, data in graph.edges(data=True):
        neo_u = node_map[u]
        neo_v = node_map[v]
        rel_type = data['relation']
        attrs = flatten(data)
        if context is not None:
            attrs[PYBEL_CONTEXT_TAG] = str(context)
        rel = py2neo.Relationship(neo_u, rel_type, neo_v, **attrs)
        tx.create(rel)

    tx.commit()
<|endoftext|>


<commit_before>"""

gitdl - download github repositories easily!

Arguments:
   REPO   Repository to download

Usage:
  gitdl search <REPO> [--sort <sort_field> ( --asc | --desc )]
  gitdl search <REPO> [--per_page <display>]
  gitdl <REPO> [-e]
  gitdl -h | --help
  gitdl --version

 Options:
   -e, --exact  Download exact repository
   -h, --help   Show this message.
   -v, --version    Show version
"""

import requests
import os
import zipfile

from docopt import docopt
from tabulate import tabulate
from tqdm import tqdm

from . import __version__

API_TOKEN = os.environ.get('GITHUB_API_TOKEN')

def get_params(API_TOKEN):
    """
    Set parameters of the request using GITHUB_API_TOKEN environment variable
    """
    if API_TOKEN is None:
        raise Exception('GITHUB_API_TOKEN not found')
    params = {'API_TOKEN': API_TOKEN}  # create a dict to be passed by request
    return params

def get_size(request):
    """
    Retrieves a size of a request object in KB.
    """
    # get the 'Content-Length' response and convert it to int
    size = int(request.headers.get('Content-Length'))
    # convert bytes to KB
    size /= 1024
    # round to two decimal places
    size = round(size, 2)
    return size

def urlretrieve(url, path):
    """
    Retrieves a zipfile and writes it to a local disk
    """
    with open(path, 'wb') as f:
        r = requests.get(url, stream=True)
        for chunk in tqdm(r.iter_content(1024), unit='k',
                          total=get_size(r), ncols=100):
            f.write(chunk)

def extractfiles(zipf):
    """
    Extract a zipfile to the current directory
    """
    with zipfile.ZipFile(zipf, "r") as z:
        z.extractall()

def get_first_search_result(resp):
    """
    Gets first search result from json response
    """
    try:
        first_result = resp['items'][0]
        return first_result
    except IndexError:
        raise Exception("Repository Not Found.")

def work_them_files(repo_name, branch):
    """
    Extract, rename and delete.
    """
    extractfiles("{}.zip".format(repo_name))
    os.rename("{}-{}".format(repo_name, branch), "{}".format(repo_name))
    os.unlink("{}.zip".format(repo_name))

def get_repo_names(response):
    items = response['items']
    repo_names = [item['full_name'] for item in items]
    return repo_names

def get_search_results(search_term, sort_field="", sort_order="desc",
                       per_page="", only_first=False):
    # send a GET to search url in GitHub API
    url = "https://api.github.com/search/repositories?"\
        "q={}&sort={}&order={}&per_page={}".format(search_term, sort_field,
                                                   sort_order, per_page)
    print(url)
    response = requests.get(url, params=get_params(API_TOKEN)).json()
    if only_first:
        result = get_first_search_result(response)
        return result
    else:
        # return the whole json
        return response

def download_zip_and_extract(repo_json):
    default_branch = repo_json['default_branch']
    download_url = "{}/archive/{}.zip".format(
        repo_json['html_url'], default_branch)
    repo_name = repo_json['name']  # stores the repository name
    print(download_url)

    urlretrieve(download_url, "{}.zip".format(repo_name))

    work_them_files(repo_name, default_branch)

def download_exact_repo(repo):
    url = "https://api.github.com/repos/{}".format(repo)
    response = requests.get(url, params=get_params(API_TOKEN))
    if response.status_code == 404:
        raise Exception("Repository Not Found.")
    response = response.json()
    download_zip_and_extract(response)

def tabulate_view(search_results):
    table = []
    # headers for the table
    headers = ['Name', 'Stars', 'Forks', 'Language', 'Last Pushed']
    # loop through each of the items containing the repo data
    for repo_data in search_results["items"]:

        # collect necessary data
        repo_name = repo_data['full_name']
        repo_stars = repo_data['stargazers_count']
        repo_forks = repo_data['forks_count']
        repo_language = repo_data['language']
        updated_at = repo_data['pushed_at']

        repo_row = [
            repo_name, repo_stars, repo_forks,
            repo_language, updated_at
        ]

        table.append(repo_row)

    # display in tabulate format
    print(tabulate(table, headers, tablefmt="grid"))

def main():

    args = docopt(__doc__, version=__version__)

    repo = args['<REPO>']
    # if search is used, then print the results
    if args['search']:
        sort_order = 'asc' if args.get('--asc') else 'desc'
        sort_field = args['<field>'] if args['--sort'] else None
        per_page = args['<display>'] if args['--per_page'] else None
        results = get_search_results(repo, sort_field, sort_order, per_page)

        tabulate_view(results)
    elif args["--exact"]:
        download_exact_repo(args["<REPO>"])
    else:
        first_result = get_search_results(repo, only_first=True)
        download_zip_and_extract(first_result)

if __name__ == "__main__":
    main()
<commit_msg>remove default arguments from functions and instead set them<commit_after>"""

gitdl - download github repositories easily!

Arguments:
   REPO   Repository to download

Usage:
  gitdl search <REPO> [--sort <sort_field> ( --asc | --desc )]
  gitdl search <REPO> [--per_page <display>]
  gitdl <REPO> [-e]
  gitdl -h | --help
  gitdl --version

 Options:
   -e, --exact  Download exact repository
   -h, --help   Show this message.
   -v, --version    Show version
"""

import requests
import os
import zipfile

from docopt import docopt
from tabulate import tabulate
from tqdm import tqdm

from . import __version__

API_TOKEN = os.environ.get('GITHUB_API_TOKEN')

def get_params(API_TOKEN):
    """
    Set parameters of the request using GITHUB_API_TOKEN environment variable
    """
    if API_TOKEN is None:
        raise Exception('GITHUB_API_TOKEN not found')
    params = {'API_TOKEN': API_TOKEN}  # create a dict to be passed by request
    return params

def get_size(request):
    """
    Retrieves a size of a request object in KB.
    """
    # get the 'Content-Length' response and convert it to int
    size = int(request.headers.get('Content-Length'))
    # convert bytes to KB
    size /= 1024
    # round to two decimal places
    size = round(size, 2)
    return size

def urlretrieve(url, path):
    """
    Retrieves a zipfile and writes it to a local disk
    """
    with open(path, 'wb') as f:
        r = requests.get(url, stream=True)
        for chunk in tqdm(r.iter_content(1024), unit='k',
                          total=get_size(r), ncols=100):
            f.write(chunk)

def extractfiles(zipf):
    """
    Extract a zipfile to the current directory
    """
    with zipfile.ZipFile(zipf, "r") as z:
        z.extractall()

def get_first_search_result(resp):
    """
    Gets first search result from json response
    """
    try:
        first_result = resp['items'][0]
        return first_result
    except IndexError:
        raise Exception("Repository Not Found.")

def work_them_files(repo_name, branch):
    """
    Extract, rename and delete.
    """
    extractfiles("{}.zip".format(repo_name))
    os.rename("{}-{}".format(repo_name, branch), "{}".format(repo_name))
    os.unlink("{}.zip".format(repo_name))

def get_repo_names(response):
    items = response['items']
    repo_names = [item['full_name'] for item in items]
    return repo_names

def get_search_results(search_term, sort_field, sort_order,
                       per_page, only_first=False):
    # send a GET to search url in GitHub API
    url = "https://api.github.com/search/repositories?"\
        "q={}&sort={}&order={}&per_page={}".format(search_term, sort_field,
                                                   sort_order, per_page)
    print(url)
    response = requests.get(url, params=get_params(API_TOKEN)).json()
    if only_first:
        result = get_first_search_result(response)
        return result
    else:
        # return the whole json
        return response

def download_zip_and_extract(repo_json):
    default_branch = repo_json['default_branch']
    download_url = "{}/archive/{}.zip".format(
        repo_json['html_url'], default_branch)
    repo_name = repo_json['name']  # stores the repository name
    print(download_url)

    urlretrieve(download_url, "{}.zip".format(repo_name))

    work_them_files(repo_name, default_branch)

def download_exact_repo(repo):
    url = "https://api.github.com/repos/{}".format(repo)
    response = requests.get(url, params=get_params(API_TOKEN))
    if response.status_code == 404:
        raise Exception("Repository Not Found.")
    response = response.json()
    download_zip_and_extract(response)

def tabulate_view(search_results):
    table = []
    # headers for the table
    headers = ['Name', 'Stars', 'Forks', 'Language', 'Last Pushed']
    # loop through each of the items containing the repo data
    for repo_data in search_results["items"]:

        # collect necessary data
        repo_name = repo_data['full_name']
        repo_stars = repo_data['stargazers_count']
        repo_forks = repo_data['forks_count']
        repo_language = repo_data['language']
        updated_at = repo_data['pushed_at']

        repo_row = [
            repo_name, repo_stars, repo_forks,
            repo_language, updated_at
        ]

        table.append(repo_row)

    # display in tabulate format
    print(tabulate(table, headers, tablefmt="grid"))

def main():

    args = docopt(__doc__, version=__version__)

    repo = args['<REPO>']
    # if search is used, then print the results
    if args['search']:
        sort_order = 'asc' if args.get('--asc') else 'desc'
        sort_field = args['<field>'] if args['--sort'] else ""
        per_page = args['<display>'] if args['--per_page'] else ""
        results = get_search_results(repo, sort_field, sort_order, per_page)

        tabulate_view(results)
    elif args["--exact"]:
        download_exact_repo(args["<REPO>"])
    else:
        first_result = get_search_results(repo, only_first=True)
        download_zip_and_extract(first_result)

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>import requests
import os
import zipfile
import sys

API_TOKEN = os.environ.get('GITHUB_API_TOKEN')

url = "https://api.github.com/search/repositories?q={}".format(sys.argv[1])

def get_params():
    if API_TOKEN is None:
        raise Exception('GITHUB_API_TOKEN not found')
    params = {'API_TOKEN': API_TOKEN}  # create a dict to be passed by request
    return params

def urlretrieve(url, path):
    """Retrieves a zipfile and writes it to a local disk"""
    with open(path, 'wb') as f:
        r = requests.get(url, stream=True)
        for chunk in r.iter_content(1024):
            f.write(chunk)

def extractfiles(zipf):
    """Extract a zipfile to the current directory"""
    with zipfile.ZipFile(zipf, "r") as z:
        z.extractall()

def main():
    response = requests.get(url, params=get_params()).json()
    first_result = response['items'][0]  # takes out the first result
    download_url = first_result['html_url'] + '/archive/master.zip'
    repo_name = first_result['name']  # stores the repository name
    print(download_url)
    urlretrieve(download_url, "{}.zip".format(repo_name))
    extractfiles("{}.zip".format(repo_name))
    os.rename("{}-master".format(repo_name), "{}".format(repo_name))
    os.unlink("{}.zip".format(repo_name))

if __name__ == "__main__":
    main()
<commit_msg>add proper exceptions, docstrings and refactor code<commit_after>import requests
import os
import zipfile
import sys

API_TOKEN = os.environ.get('GITHUB_API_TOKEN')

def get_params():
    """Set parameters of the request"""
    if API_TOKEN is None:
        raise Exception('GITHUB_API_TOKEN not found')
    params = {'API_TOKEN': API_TOKEN}  # create a dict to be passed by request
    return params

def urlretrieve(url, path):
    """Retrieves a zipfile and writes it to a local disk"""
    with open(path, 'wb') as f:
        r = requests.get(url, stream=True)
        for chunk in r.iter_content(1024):
            f.write(chunk)

def extractfiles(zipf):
    """Extract a zipfile to the current directory"""
    with zipfile.ZipFile(zipf, "r") as z:
        z.extractall()

def get_first_search_result(resp):
    "Gets first search result from json response"
    try:
        first_result = resp['items'][0]
        return first_result
    except IndexError:
        print("No such repository exists.")
        exit(1)

def work_them_files(repo_name):
    "Extract, rename and delete."
    extractfiles("{}.zip".format(repo_name))
    os.rename("{}-master".format(repo_name), "{}".format(repo_name))
    os.unlink("{}.zip".format(repo_name))

def main():
    # send a GET to search url in GitHub API
    url = "https://api.github.com/search/repositories?q={}".format(sys.argv[1])
    response = requests.get(url, params=get_params()).json()

    first_result = get_first_search_result(response)  # check for empty response

    download_url = first_result['html_url'] + '/archive/master.zip'
    repo_name = first_result['name']  # stores the repository name
    print(download_url)

    urlretrieve(download_url, "{}.zip".format(repo_name))

    work_them_files(repo_name)

if __name__ == "__main__":
    main()
<|endoftext|>