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

###
# Copyright (c) 2004, 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.
###

__revision__ = "$Id$"

import re
import copy
import sets
import time
import types
import textwrap

import fix
import utils

class RegistryException(Exception):
    pass

class InvalidRegistryFile(RegistryException):
    pass

class InvalidRegistryValue(RegistryException):
    pass

class NonExistentRegistryEntry(RegistryException):
    pass

_cache = {}
_lastModified = 0
def open(filename):
    """Initializes the module by loading the registry file into memory."""
    global _lastModified
    _cache.clear()
    fd = utils.nonCommentNonEmptyLines(file(filename))
    for (i, line) in enumerate(fd):
        line = line.rstrip('\r\n')
        try:
            (key, value) = re.split(r':\s*', line, 1)
        except ValueError:
            raise InvalidRegistryFile, 'Error unpacking line #%s' % (i+1)
        _cache[key.lower()] = value
    _lastModified = time.time()

def close(registry, filename, annotated=True):
    first = True
    helpCache = sets.Set()
    fd = file(filename, 'w')
    for (name, value) in registry.getValues(getChildren=True):
        if annotated and \
           hasattr(value,'help') and \
           value.help and \
           value.help not in helpCache:
            helpCache.add(value.help)
            lines = textwrap.wrap(value.help)
            for (i, line) in enumerate(lines):
                lines[i] = '# %s\n' % line
            lines.insert(0, '###\n')
            if first:
                first = False
            else:
                lines.insert(0, '\n')
            lines.append('###\n')
            fd.writelines(lines)
        fd.write('%s: %s\n' % (name, value))
    fd.close()

class Value(object):
    def __init__(self, default, help):
        self.default = default
        self.help = utils.normalizeWhitespace(help.strip())
        self.setValue(default)

    def set(self, s):
        """Override this with a function to convert a string to whatever type
        you want, and call self.setValue to set the value."""
        raise NotImplementedError

    def setValue(self, v):
        """Check conditions on the actual value type here.  I.e., if you're a
        IntegerLessThanOneHundred (all your values must be integers less than
        100) convert to an integer in set() and check that the integer is less
        than 100 in this method.  You *must* call this parent method in your
        own setValue."""
        self._lastModified = time.time()
        self.value = v

    def __str__(self):
        return repr(self())

    # This is simply prettier than naming this function get(self)
    def __call__(self):
        # TODO: Check _lastModified to see if stuff needs to be reloaded from
        # the _cache.
        return self.value

class Boolean(Value):
    def set(self, s):
        s = s.lower()
        if s in ('true', 'on', 'enable', 'enabled'):
            value = True
        elif s in ('false', 'off', 'disable', 'disabled'):
            value = False
        elif s == 'toggle':
            value = not self.value
        else:
            raise InvalidRegistryValue, 'Value must be True or False.'
        self.setValue(value)

    def setValue(self, v):
        Value.setValue(self, bool(v))

class Integer(Value):
    def set(self, s):
        try:
            self.setValue(int(s))
        except ValueError:
            raise InvalidRegistryValue, 'Value must be an integer.'

class PositiveInteger(Value):
    def set(self, s):
        try:
            self.setValue(int(s))
        except ValueError:
            raise InvalidRegistryValue, 'Value must be a positive integer.'

    def setValue(self, v):
        if v <= 0:
            raise InvalidRegistryValue, 'Value must be a positive integer.'
        Value.setValue(self, v)

class Float(Value):
    def set(self, s):
        try:
            self.setValue(float(s))
        except ValueError:
            raise InvalidRegistryValue, 'Value must be a float.'

    def setValue(self, v):
        try:
            Value.setValue(self, float(v))
        except ValueError:
            raise InvalidRegistryValue, 'Value must be a float.'

class String(Value):
    def set(self, s):
        if not s:
            s = '""'
        elif s[0] != s[-1] or s[0] not in '\'"':
            s = repr(s)
        try:
            v = utils.safeEval(s)
            if not isinstance(v, basestring):
                raise ValueError
            self.value = v
        except ValueError: # This catches utils.safeEval(s) errors too.
            raise InvalidRegistryValue, 'Value must be a string.'

class NormalizedString(String):
    def set(self, s):
        s = utils.normalizeWhitespace(s.strip())
        String.set(self, s)

    def setValue(self, s):
        s = utils.normalizeWhitespace(s.strip())
        String.setValue(self, s)

class StringSurroundedBySpaces(String):
    def set(self, s):
        String.set(self, s)
        self.setValue(self.value)

    def setValue(self, v):
        if v.lstrip() == v:
            v= ' ' + v
        if v.rstrip() == v:
            v += ' '
        String.setValue(self, v)

class StringWithSpaceOnRight(String):
    def setValue(self, v):
        if v.rstrip() == v:
            v += ' '
        String.setValue(self, v)

class Regexp(Value):
    def set(self, s):
        try:
            if s:
                self.value = utils.perlReToPythonRe(s)
            else:
                self.value = None
            self.sr = s
        except ValueError, e:
            raise InvalidRegistryValue, 'Value must be a valid regexp: %s' % e

    def setValue(self, v):
        if v is None:
            self.sr = ''
            self.value = None
        else:
            raise InvalidRegistryValue, \
                  'Can\'t set to a regexp, there would be an inconsistency ' \
                  'between the regexp and the recorded string value.'

    def __str__(self):
        return self.sr

class SeparatedListOf(Value):
    Value = Value
    def splitter(self, s):
        """Override this with a function that takes a string and returns a list
        of strings."""
        raise NotImplementedError

    def joiner(self, L):
        """Override this to join the internal list for output."""
        raise NotImplementedError

    def set(self, s):
        L = self.splitter(s)
        for (i, s) in enumerate(L):
            v = self.Value(s, 'help does not matter here')
            L[i] = v()
        self.setValue(L)

    def setValue(self, v):
        Value.setValue(self, list(v))

    def __str__(self):
        return self.joiner(self.value)

class SpaceSeparatedListOfStrings(SeparatedListOf):
    Value = String
    def splitter(self, s):
        return s.split()
    joiner = ' '.join

class CommaSeparatedListOfStrings(SeparatedListOf):
    Value = String
    def splitter(self, s):
        return re.split(r'\s*,\s*', s)
    joiner = ', '.join

class CommaSeparatedSetOfStrings(CommaSeparatedListOfStrings):
    def setValue(self, v):
        CommaSeparatedListOfStrings.setValue(self, sets.Set(v))

class Group(object):
    def __init__(self):
        self.name = 'unset'
        self.values = {}
        self.children = {}
        self.originals = {}

    def __nonExistentEntry(self, attr):
        s = '%s is not a valid entry in %s' % (attr, self.name)
        raise NonExistentRegistryEntry, s

    def __getattr__(self, attr):
        original = attr
        attr = attr.lower()
        if attr in self.values:
            return self.values[attr]
        elif attr in self.children:
            return self.children[attr]
        else:
            self.__nonExistentEntry(original)

    def get(self, attr):
        return self.__getattr__(attr)

    def getChild(self, attr):
        return self.children[attr.lower()]

    def setName(self, name):
        self.name = name

    def getName(self):
        return self.name

    def register(self, name, value):
        original = name
        name = name.lower()
        self.values[name] = value
        self.originals[name] = original
        if _cache:
            fullname = '%s.%s' % (self.name, name)
            if fullname in _cache:
                value.set(_cache[fullname])

    def registerGroup(self, name, group=None):
        original = name
        name = name.lower()
        if name in self.children:
            return # Ignore redundant group inserts.
        if group is None:
            group = Group()
        self.children[name] = group
        self.originals[name] = original
        fullname = '%s.%s' % (self.name, name)
        group.setName(fullname)
        if _cache and fullname in _cache:
            group.set(_cache[fullname])

    def getValues(self, getChildren=False, fullNames=True):
        L = []
        items = self.values.items()
        # If getChildren=True, the group will insert itself into its getValues.
        if not getChildren:
            for (name, child) in self.children.items():
                if hasattr(child, 'value'):
                    items.append((name, child))
        utils.sortBy(lambda (k, _): (k.lower(), len(k), k), items)
        for (name, value) in items:
            if fullNames:
                name = '%s.%s' % (self.getName(), self.originals[name])
            else:
                name = self.originals[name]
            L.append((name, value))
        if getChildren:
            items = self.children.items()
            utils.sortBy(lambda (k, _): (k.lower(), len(k), k), items)
            for (_, child) in items:
                L.extend(child.getValues(getChildren, fullNames))
        return L

class GroupWithValue(Group):
    def __init__(self, value):
        Group.__init__(self)
        self.value = value
        self.help = value.help
        self.default = value.default

    def set(self, s):
        self.value.set(s)

    def setValue(self, v):
        self.value.setValue(v)

    def __call__(self):
        return self.value()

    def __str__(self):
        return str(self.value)

    def getValues(self, getChildren=False, fullNames=True):
        L = Group.getValues(self, getChildren, fullNames)
        if getChildren:
            L.insert(0, (self.getName(), self))
        return L

class GroupWithDefault(GroupWithValue):
    def __init__(self, value):
        class X(value.__class__):
            """This class exists to differentiate those values that have been
            changed from their default to those that haven't."""
            def set(self, *args):
                self.__class__ = value.__class__
                self.set(*args)
            def setValue(self, *args):
                self.__class__ = value.__class__
                self.setValue(*args)
        self.X = X
        GroupWithValue.__init__(self, value)

    def __makeChild(self, attr, s):
        #print '***', attr, ':', repr(s)
        v = copy.copy(self.value)
        v.set(s)
        v.__class__ = self.X
        self.register(attr, v)
        return v

    def __getattr__(self, attr):
        try:
            v = GroupWithValue.__getattr__(self, attr)
            if v.__class__ is self.X:
                raise NonExistentRegistryEntry
        except NonExistentRegistryEntry:
            v = self.__makeChild(attr, str(self))
        return v

    def setName(self, name):
        GroupWithValue.setName(self, name)
        for (k, v) in _cache.iteritems():
            if k.startswith(self.name):
                (_, group) = rsplit(k, '.', 1)
                self.__makeChild(group, v)

    def getValues(self, getChildren=False, fullNames=True):
        L = GroupWithValue.getValues(self, getChildren, fullNames)
        L = [v for v in L if v[1].__class__ is not self.X]
        return L

if __name__ == '__main__':
    import sys
    sys.setrecursionlimit(40)
    supybot = Group()
    supybot.setName('supybot')
    supybot.register('throttleTime', Float(1, """Determines the minimum
    number of seconds the bot will wait between sending messages to the server.
    """))
    supybot.registerGroup('plugins')
    supybot.plugins.registerGroup('topic')
    supybot.plugins.topic.registerGroup('separator',
      GroupWithDefault(StringSurroundedBySpaces(' || ',
      'Determines what separator the bot uses to separate topic entries.')))
    supybot.plugins.topic.separator.get('#supybot').set(' |||| ')
    supybot.plugins.topic.separator.set(' <> ')

    supybot.throttleTime.set(10)

    supybot.registerGroup('log')
    supybot.log.registerGroup('stdout',
                              GroupWithValue(Boolean(False,
                                                     """Help for stdout.""")))
    supybot.log.stdout.register('colorized', Boolean(False,
                                                     'Help colorized'))
    supybot.log.stdout.setValue(True)

    for (k, v) in supybot.getValues():
        print '%s: %s' % (k, v)

    print
    print 'Asking children'
    print

    for (k, v) in supybot.getValues(getChildren=True):
        print '%s: %s' % (k, v)

    print supybot.throttleTime.help
    print supybot.plugins.topic.separator.help

# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

<commit_msg>Converted to work.<commit_after>#!/usr/bin/env python

###
# Copyright (c) 2004, 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.
###

__revision__ = "$Id$"

import re
import copy
import sets
import time
import types
import textwrap

import fix
import utils

class RegistryException(Exception):
    pass

class InvalidRegistryFile(RegistryException):
    pass

class InvalidRegistryValue(RegistryException):
    pass

class NonExistentRegistryEntry(RegistryException):
    pass

_cache = {}
_lastModified = 0
def open(filename):
    """Initializes the module by loading the registry file into memory."""
    global _lastModified
    _cache.clear()
    fd = utils.nonCommentNonEmptyLines(file(filename))
    for (i, line) in enumerate(fd):
        line = line.rstrip('\r\n')
        try:
            (key, value) = re.split(r':\s*', line, 1)
        except ValueError:
            raise InvalidRegistryFile, 'Error unpacking line #%s' % (i+1)
        _cache[key.lower()] = value
    _lastModified = time.time()

def close(registry, filename, annotated=True):
    first = True
    helpCache = sets.Set()
    fd = file(filename, 'w')
    for (name, value) in registry.getValues(getChildren=True):
        if annotated and \
           hasattr(value,'help') and \
           value.help and \
           value.help not in helpCache:
            helpCache.add(value.help)
            lines = textwrap.wrap(value.help)
            for (i, line) in enumerate(lines):
                lines[i] = '# %s\n' % line
            lines.insert(0, '###\n')
            if first:
                first = False
            else:
                lines.insert(0, '\n')
            lines.append('###\n')
            fd.writelines(lines)
        fd.write('%s: %s\n' % (name, value))
    fd.close()

class Value(object):
    def __init__(self, default, help):
        self.default = default
        self.help = utils.normalizeWhitespace(help.strip())
        self.setValue(default)

    def set(self, s):
        """Override this with a function to convert a string to whatever type
        you want, and call self.setValue to set the value."""
        raise NotImplementedError

    def setValue(self, v):
        """Check conditions on the actual value type here.  I.e., if you're a
        IntegerLessThanOneHundred (all your values must be integers less than
        100) convert to an integer in set() and check that the integer is less
        than 100 in this method.  You *must* call this parent method in your
        own setValue."""
        self._lastModified = time.time()
        self.value = v

    def __str__(self):
        return repr(self())

    # This is simply prettier than naming this function get(self)
    def __call__(self):
        # TODO: Check _lastModified to see if stuff needs to be reloaded from
        # the _cache.
        return self.value

class Boolean(Value):
    def set(self, s):
        s = s.lower()
        if s in ('true', 'on', 'enable', 'enabled'):
            value = True
        elif s in ('false', 'off', 'disable', 'disabled'):
            value = False
        elif s == 'toggle':
            value = not self.value
        else:
            raise InvalidRegistryValue, 'Value must be True or False.'
        self.setValue(value)

    def setValue(self, v):
        Value.setValue(self, bool(v))

class Integer(Value):
    def set(self, s):
        try:
            self.setValue(int(s))
        except ValueError:
            raise InvalidRegistryValue, 'Value must be an integer.'

class PositiveInteger(Value):
    def set(self, s):
        try:
            self.setValue(int(s))
        except ValueError:
            raise InvalidRegistryValue, 'Value must be a positive integer.'

    def setValue(self, v):
        if v <= 0:
            raise InvalidRegistryValue, 'Value must be a positive integer.'
        Value.setValue(self, v)

class Float(Value):
    def set(self, s):
        try:
            self.setValue(float(s))
        except ValueError:
            raise InvalidRegistryValue, 'Value must be a float.'

    def setValue(self, v):
        try:
            Value.setValue(self, float(v))
        except ValueError:
            raise InvalidRegistryValue, 'Value must be a float.'

class String(Value):
    def set(self, s):
        if not s:
            s = '""'
        elif s[0] != s[-1] or s[0] not in '\'"':
            s = repr(s)
        try:
            v = utils.safeEval(s)
            if not isinstance(v, basestring):
                raise ValueError
            self.value = v
        except ValueError: # This catches utils.safeEval(s) errors too.
            raise InvalidRegistryValue, 'Value must be a string.'

class NormalizedString(String):
    def set(self, s):
        s = utils.normalizeWhitespace(s.strip())
        String.set(self, s)

    def setValue(self, s):
        s = utils.normalizeWhitespace(s.strip())
        String.setValue(self, s)

class StringSurroundedBySpaces(String):
    def set(self, s):
        String.set(self, s)
        self.setValue(self.value)

    def setValue(self, v):
        if v.lstrip() == v:
            v= ' ' + v
        if v.rstrip() == v:
            v += ' '
        String.setValue(self, v)

class StringWithSpaceOnRight(String):
    def setValue(self, v):
        if v.rstrip() == v:
            v += ' '
        String.setValue(self, v)

class Regexp(Value):
    def set(self, s):
        try:
            if s:
                self.value = utils.perlReToPythonRe(s)
            else:
                self.value = None
            self.sr = s
        except ValueError, e:
            raise InvalidRegistryValue, 'Value must be a valid regexp: %s' % e

    def setValue(self, v):
        if v is None:
            self.sr = ''
            self.value = None
        else:
            raise InvalidRegistryValue, \
                  'Can\'t set to a regexp, there would be an inconsistency ' \
                  'between the regexp and the recorded string value.'

    def __str__(self):
        return self.sr

class SeparatedListOf(Value):
    List = list
    Value = Value
    def splitter(self, s):
        """Override this with a function that takes a string and returns a list
        of strings."""
        raise NotImplementedError

    def joiner(self, L):
        """Override this to join the internal list for output."""
        raise NotImplementedError

    def set(self, s):
        L = self.splitter(s)
        for (i, s) in enumerate(L):
            v = self.Value(s, 'help does not matter here')
            L[i] = v()
        self.setValue(L)

    def setValue(self, v):
        Value.setValue(self, self.List(v))

    def __str__(self):
        return self.joiner(self.value)

class SpaceSeparatedListOfStrings(SeparatedListOf):
    Value = String
    def splitter(self, s):
        return s.split()
    joiner = ' '.join

class CommaSeparatedListOfStrings(SeparatedListOf):
    Value = String
    def splitter(self, s):
        return re.split(r'\s*,\s*', s)
    joiner = ', '.join

class CommaSeparatedSetOfStrings(CommaSeparatedListOfStrings):
    List = sets.Set

class Group(object):
    def __init__(self):
        self.name = 'unset'
        self.values = {}
        self.children = {}
        self.originals = {}

    def __nonExistentEntry(self, attr):
        s = '%s is not a valid entry in %s' % (attr, self.name)
        raise NonExistentRegistryEntry, s

    def __getattr__(self, attr):
        original = attr
        attr = attr.lower()
        if attr in self.values:
            return self.values[attr]
        elif attr in self.children:
            return self.children[attr]
        else:
            self.__nonExistentEntry(original)

    def get(self, attr):
        return self.__getattr__(attr)

    def getChild(self, attr):
        return self.children[attr.lower()]

    def setName(self, name):
        self.name = name

    def getName(self):
        return self.name

    def register(self, name, value):
        original = name
        name = name.lower()
        self.values[name] = value
        self.originals[name] = original
        if _cache:
            fullname = '%s.%s' % (self.name, name)
            if fullname in _cache:
                value.set(_cache[fullname])

    def registerGroup(self, name, group=None):
        original = name
        name = name.lower()
        if name in self.children:
            return # Ignore redundant group inserts.
        if group is None:
            group = Group()
        self.children[name] = group
        self.originals[name] = original
        fullname = '%s.%s' % (self.name, name)
        group.setName(fullname)
        if _cache and fullname in _cache:
            group.set(_cache[fullname])

    def getValues(self, getChildren=False, fullNames=True):
        L = []
        items = self.values.items()
        # If getChildren=True, the group will insert itself into its getValues.
        if not getChildren:
            for (name, child) in self.children.items():
                if hasattr(child, 'value'):
                    items.append((name, child))
        utils.sortBy(lambda (k, _): (k.lower(), len(k), k), items)
        for (name, value) in items:
            if fullNames:
                name = '%s.%s' % (self.getName(), self.originals[name])
            else:
                name = self.originals[name]
            L.append((name, value))
        if getChildren:
            items = self.children.items()
            utils.sortBy(lambda (k, _): (k.lower(), len(k), k), items)
            for (_, child) in items:
                L.extend(child.getValues(getChildren, fullNames))
        return L

class GroupWithValue(Group):
    def __init__(self, value):
        Group.__init__(self)
        self.value = value
        self.help = value.help
        self.default = value.default

    def set(self, s):
        self.value.set(s)

    def setValue(self, v):
        self.value.setValue(v)

    def __call__(self):
        return self.value()

    def __str__(self):
        return str(self.value)

    def getValues(self, getChildren=False, fullNames=True):
        L = Group.getValues(self, getChildren, fullNames)
        if getChildren:
            L.insert(0, (self.getName(), self))
        return L

class GroupWithDefault(GroupWithValue):
    def __init__(self, value):
        class X(value.__class__):
            """This class exists to differentiate those values that have been
            changed from their default to those that haven't."""
            def set(self, *args):
                self.__class__ = value.__class__
                self.set(*args)
            def setValue(self, *args):
                self.__class__ = value.__class__
                self.setValue(*args)
        self.X = X
        GroupWithValue.__init__(self, value)

    def __makeChild(self, attr, s):
        #print '***', attr, ':', repr(s)
        v = copy.copy(self.value)
        v.set(s)
        v.__class__ = self.X
        self.register(attr, v)
        return v

    def __getattr__(self, attr):
        try:
            v = GroupWithValue.__getattr__(self, attr)
            if v.__class__ is self.X:
                raise NonExistentRegistryEntry
        except NonExistentRegistryEntry:
            v = self.__makeChild(attr, str(self))
        return v

    def setName(self, name):
        GroupWithValue.setName(self, name)
        for (k, v) in _cache.iteritems():
            if k.startswith(self.name):
                (_, group) = rsplit(k, '.', 1)
                self.__makeChild(group, v)

    def getValues(self, getChildren=False, fullNames=True):
        L = GroupWithValue.getValues(self, getChildren, fullNames)
        L = [v for v in L if v[1].__class__ is not self.X]
        return L

if __name__ == '__main__':
    import sys
    sys.setrecursionlimit(40)
    supybot = Group()
    supybot.setName('supybot')
    supybot.register('throttleTime', Float(1, """Determines the minimum
    number of seconds the bot will wait between sending messages to the server.
    """))
    supybot.registerGroup('plugins')
    supybot.plugins.registerGroup('topic')
    supybot.plugins.topic.registerGroup('separator',
      GroupWithDefault(StringSurroundedBySpaces(' || ',
      'Determines what separator the bot uses to separate topic entries.')))
    supybot.plugins.topic.separator.get('#supybot').set(' |||| ')
    supybot.plugins.topic.separator.set(' <> ')

    supybot.throttleTime.set(10)

    supybot.registerGroup('log')
    supybot.log.registerGroup('stdout',
                              GroupWithValue(Boolean(False,
                                                     """Help for stdout.""")))
    supybot.log.stdout.register('colorized', Boolean(False,
                                                     'Help colorized'))
    supybot.log.stdout.setValue(True)

    for (k, v) in supybot.getValues():
        print '%s: %s' % (k, v)

    print
    print 'Asking children'
    print

    for (k, v) in supybot.getValues(getChildren=True):
        print '%s: %s' % (k, v)

    print supybot.throttleTime.help
    print supybot.plugins.topic.separator.help

# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

<|endoftext|>


<commit_before>import threading
import Queue

sqlSubmitQueue = Queue.Queue() #SQLITE3 is so thread-unsafe that they won't even let you call it from different threads using your own locks. SQL objects can only be called from one thread.
sqlReturnQueue = Queue.Queue()
sqlLock = threading.Lock()

def sqlQuery(sqlStatement, *args):
    sqlLock.acquire()
    sqlSubmitQueue.put(sqlStatement)

    if args == ():
        sqlSubmitQueue.put('')
    else:
        sqlSubmitQueue.put(args)

    queryreturn = sqlReturnQueue.get()
    sqlLock.release()

    return queryreturn

def sqlExecute(sqlStatement, *args):
    sqlLock.acquire()
    sqlSubmitQueue.put(sqlStatement)

    if args == ():
        sqlSubmitQueue.put('')
    else:
        sqlSubmitQueue.put(args)

    sqlReturnQueue.get()
    sqlSubmitQueue.put('commit')
    sqlLock.release()

def sqlStoredProcedure(procName):
    sqlLock.acquire()
    sqlSubmitQueue.put(procName)
    sqlLock.release()

class SqlBulkExecute:
    def __enter__(self):
        sqlLock.acquire()
        return self

    def __exit__(self, type, value, traceback):
        sqlSubmitQueue.put('commit')
        sqlLock.release()

    def execute(self, sqlStatement, *args):
        sqlSubmitQueue.put(sqlStatement)

        if args == ():
            sqlSubmitQueue.put('')
        else:
            sqlSubmitQueue.put(args)
        sqlReturnQueue.get()

    def query(self, sqlStatement, *args):
        sqlSubmitQueue.put(sqlStatement)

        if args == ():
            sqlSubmitQueue.put('')
        else:
            sqlSubmitQueue.put(args)
        return sqlReturnQueue.get()

<commit_msg>Allow SQL arguments as a list or tuple<commit_after>import threading
import Queue

sqlSubmitQueue = Queue.Queue() #SQLITE3 is so thread-unsafe that they won't even let you call it from different threads using your own locks. SQL objects can only be called from one thread.
sqlReturnQueue = Queue.Queue()
sqlLock = threading.Lock()

def sqlQuery(sqlStatement, *args):
    sqlLock.acquire()
    sqlSubmitQueue.put(sqlStatement)

    if args == ():
        sqlSubmitQueue.put('')
    elif type(args[0]) in [list, tuple]:
        sqlSubmitQueue.put(args[0])
    else:
        sqlSubmitQueue.put(args)

    queryreturn = sqlReturnQueue.get()
    sqlLock.release()

    return queryreturn

def sqlExecute(sqlStatement, *args):
    sqlLock.acquire()
    sqlSubmitQueue.put(sqlStatement)

    if args == ():
        sqlSubmitQueue.put('')
    else:
        sqlSubmitQueue.put(args)

    sqlReturnQueue.get()
    sqlSubmitQueue.put('commit')
    sqlLock.release()

def sqlStoredProcedure(procName):
    sqlLock.acquire()
    sqlSubmitQueue.put(procName)
    sqlLock.release()

class SqlBulkExecute:
    def __enter__(self):
        sqlLock.acquire()
        return self

    def __exit__(self, type, value, traceback):
        sqlSubmitQueue.put('commit')
        sqlLock.release()

    def execute(self, sqlStatement, *args):
        sqlSubmitQueue.put(sqlStatement)

        if args == ():
            sqlSubmitQueue.put('')
        else:
            sqlSubmitQueue.put(args)
        sqlReturnQueue.get()

    def query(self, sqlStatement, *args):
        sqlSubmitQueue.put(sqlStatement)

        if args == ():
            sqlSubmitQueue.put('')
        else:
            sqlSubmitQueue.put(args)
        return sqlReturnQueue.get()

<|endoftext|>


<commit_before>#! /usr/local/python

# ptags
#
# Create a tags file for Python programs, usable with vi.
# Tagged are:
# - functions (even inside other defs or classes)
# - classes
# - filenames
# Warns about files it cannot open.
# No warnings about duplicate tags.

import sys
import regexp
import path

tags = []	# Modified global variable!

def main():
	args = sys.argv[1:]
	for file in args: treat_file(file)
	if tags:
		fp = open('tags', 'w')
		tags.sort()
		for s in tags: fp.write(s)

matcher = regexp.compile('^[ \t]*(def|class)[ \t]+([a-zA-Z0-9_]+)[ \t]*\(')

def treat_file(file):
	try:
		fp = open(file, 'r')
	except:
		print 'Cannot open', file
		return
	base = path.basename(file)
	if base[-3:] == '.py': base = base[:-3]
	s = base + '\t' + file + '\t' + '1\n'
	tags.append(s)
	while 1:
		line = fp.readline()
		if not line: break
		res = matcher.exec(line)
		if res:
			(a, b), (a1, b1), (a2, b2) = res
			name = line[a2:b2]
			s = name + '\t' + file + '\t/^' + line[a:b] + '/\n'
			tags.append(s)

main()
<commit_msg>Use regex and don't use path<commit_after>#! /usr/local/python

# ptags
#
# Create a tags file for Python programs, usable with vi.
# Tagged are:
# - functions (even inside other defs or classes)
# - classes
# - filenames
# Warns about files it cannot open.
# No warnings about duplicate tags.

import sys
import regex
import os

tags = []	# Modified global variable!

def main():
	args = sys.argv[1:]
	for file in args: treat_file(file)
	if tags:
		fp = open('tags', 'w')
		tags.sort()
		for s in tags: fp.write(s)

expr = '^[ \t]*\(def\|class\)[ \t]+\([a-zA-Z0-9_]+\)[ \t]*[:(]'
matcher = regex.compile(expr)

def treat_file(file):
	try:
		fp = open(file, 'r')
	except:
		print 'Cannot open', file
		return
	base = os.path.basename(file)
	if base[-3:] == '.py': base = base[:-3]
	s = base + '\t' + file + '\t' + '1\n'
	tags.append(s)
	while 1:
		line = fp.readline()
		if not line: break
		if matcher.search(line) >= 0:
			(a, b), (a1, b1), (a2, b2) = matcher.regs[:3]
			name = line[a2:b2]
			s = name + '\t' + file + '\t/^' + line[a:b] + '/\n'
			tags.append(s)

main()
<|endoftext|>


<commit_before># Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.

from UM.Job import Job
from UM.Application import Application
from UM.Message import Message
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Math.Matrix import Matrix
from UM.Math.Vector import Vector
from UM.Preferences import Preferences
import time
import os.path

from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("uranium")

##  A Job subclass that performs mesh loading.
#
#   The result of this Job is a MeshData object.
class ReadMeshJob(Job):
    def __init__(self, filename):
        super().__init__()
        self._filename = filename
        self._handler = Application.getInstance().getMeshFileHandler()

    def getFileName(self):
        return self._filename

    def run(self):
        loading_message = Message(i18n_catalog.i18nc("@info:status", "Loading <filename>{0}</filename>", self._filename), lifetime = 0, dismissable = False)
        loading_message.setProgress(-1)
        loading_message.show()

        try:
            node = self._handler.read(self._filename)
        except Exception as e:
            print(e)
        if not node:
            loading_message.hide()

            result_message = Message(i18n_catalog.i18nc("@info:status", "Failed to load <filename>{0}</filename>", self._filename))
            result_message.show()
            return

        # Scale down to maximum bounds size if that is available
        if hasattr(Application.getInstance().getController().getScene(), "_maximum_bounds"):
            max_bounds = Application.getInstance().getController().getScene()._maximum_bounds
            node._resetAABB()
            bounding_box = node.getBoundingBox()
            timeout_counter = 0
            #As the calculation of the bounding box is in a seperate thread it might be that it's not done yet.
            while bounding_box.width == 0 or bounding_box.height == 0 or bounding_box.depth == 0:
                bounding_box = node.getBoundingBox()
                time.sleep(0.1)
                timeout_counter += 1
                if timeout_counter > 10:
                    break
            if max_bounds.width < bounding_box.width or max_bounds.height < bounding_box.height or max_bounds.depth < bounding_box.depth:
                largest_dimension = max(bounding_box.width, bounding_box.height, bounding_box.depth)

                scale_factor = 1.0
                if largest_dimension == bounding_box.width:
                    scale_factor = max_bounds.width / bounding_box.width
                elif largest_dimension == bounding_box.height:
                    scale_factor = max_bounds.height / bounding_box.height
                else:
                    scale_factor = max_bounds.depth / bounding_box.depth

                scale_vector = Vector(scale_factor, scale_factor, scale_factor)
                if Preferences.getInstance().getValue("mesh/scale_to_fit") == True:
                    scale_message = Message(i18n_catalog.i18nc("", "Auto scaled object to {0} % of original size", ("%.2f" % scale_factor)))

                    try:
                        node.scale(scale_vector)
                        scale_message.show()
                    except Exception as e:
                        print(e)

        self.setResult(node)

        loading_message.hide()
        result_message = Message(i18n_catalog.i18nc("@info:status", "Loaded <filename>{0}</filename>", self._filename))
        result_message.show()
<commit_msg>Auto scale now scales to correct size.<commit_after># Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.

from UM.Job import Job
from UM.Application import Application
from UM.Message import Message
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Math.Matrix import Matrix
from UM.Math.Vector import Vector
from UM.Preferences import Preferences
import time
import os.path

from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("uranium")

##  A Job subclass that performs mesh loading.
#
#   The result of this Job is a MeshData object.
class ReadMeshJob(Job):
    def __init__(self, filename):
        super().__init__()
        self._filename = filename
        self._handler = Application.getInstance().getMeshFileHandler()

    def getFileName(self):
        return self._filename

    def run(self):
        loading_message = Message(i18n_catalog.i18nc("@info:status", "Loading <filename>{0}</filename>", self._filename), lifetime = 0, dismissable = False)
        loading_message.setProgress(-1)
        loading_message.show()

        try:
            node = self._handler.read(self._filename)
        except Exception as e:
            print(e)
        if not node:
            loading_message.hide()

            result_message = Message(i18n_catalog.i18nc("@info:status", "Failed to load <filename>{0}</filename>", self._filename))
            result_message.show()
            return

        # Scale down to maximum bounds size if that is available
        if hasattr(Application.getInstance().getController().getScene(), "_maximum_bounds"):
            max_bounds = Application.getInstance().getController().getScene()._maximum_bounds
            node._resetAABB()
            bounding_box = node.getBoundingBox()
            timeout_counter = 0
            #As the calculation of the bounding box is in a seperate thread it might be that it's not done yet.
            while bounding_box.width == 0 or bounding_box.height == 0 or bounding_box.depth == 0:
                bounding_box = node.getBoundingBox()
                time.sleep(0.1)
                timeout_counter += 1
                if timeout_counter > 10:
                    break
            if max_bounds.width < bounding_box.width or max_bounds.height < bounding_box.height or max_bounds.depth < bounding_box.depth:
                scale_factor_width = max_bounds.width / bounding_box.width
                scale_factor_height = max_bounds.height / bounding_box.height
                scale_factor_depth = max_bounds.depth / bounding_box.depth
                scale_factor = min(scale_factor_width,scale_factor_height,scale_factor_depth)

                scale_vector = Vector(scale_factor, scale_factor, scale_factor)
                if Preferences.getInstance().getValue("mesh/scale_to_fit") == True:
                    scale_message = Message(i18n_catalog.i18nc("", "Auto scaled object to {0} % of original size", ("%.2f" % scale_factor)))

                    try:
                        node.scale(scale_vector)
                        scale_message.show()
                    except Exception as e:
                        print(e)

        self.setResult(node)

        loading_message.hide()
        result_message = Message(i18n_catalog.i18nc("@info:status", "Loaded <filename>{0}</filename>", self._filename))
        result_message.show()
<|endoftext|>


<commit_before>import sys
import os
import signal
import platform

from PyQt5.QtCore import Qt, QObject, QCoreApplication, QEvent, pyqtSlot, QLocale, QTranslator, QLibraryInfo
from PyQt5.QtQml import QQmlApplicationEngine, qmlRegisterType, qmlRegisterSingletonType
from PyQt5.QtWidgets import QApplication, QSplashScreen
from PyQt5.QtGui import QGuiApplication, QPixmap
from PyQt5.QtCore import QTimer

from UM.Application import Application
from UM.Qt.QtGL2Renderer import QtGL2Renderer
from UM.Qt.Bindings.Bindings import Bindings
from UM.JobQueue import JobQueue
from UM.Signal import Signal, SignalEmitter
from UM.Resources import Resources
from UM.Logger import Logger
from UM.Preferences import Preferences

##  Application subclass that provides a Qt application object.
class QtApplication(QApplication, Application, SignalEmitter):
    def __init__(self, **kwargs):
        if hasattr(sys, 'frozen') and sys.platform == 'win32':
            plugin_path = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), 'PyQt5', 'plugins')
            Logger.log('i', 'Adding QT5 plugin path: %s' % (plugin_path))
            QCoreApplication.addLibraryPath(plugin_path)
        super().__init__(sys.argv, **kwargs)

        self._main_qml = "main.qml"
        self._engine = None
        self._renderer = None

        try:
            self._splash = QSplashScreen(QPixmap(Resources.getPath(Resources.ImagesLocation, self.getApplicationName() + ".png")))
        except FileNotFoundError:
            self._splash = None
        else:
            self._splash.show()
            self.processEvents()

        signal.signal(signal.SIGINT, signal.SIG_DFL)
        # This is done here as a lot of plugins require a correct gl context. If you want to change the framework,
        # these checks need to be done in your <framework>Application.py class __init__().

        self.showSplashMessage('Loading plugins...')
        self._loadPlugins()
        self._plugin_registry.checkRequiredPlugins(self.getRequiredPlugins())

        self.showSplashMessage('Loading machines...')
        self.loadMachines()

        self.showSplashMessage('Loading preferences...')
        try:
            file = Resources.getPath(Resources.PreferencesLocation, self.getApplicationName() + '.cfg')
            Preferences.getInstance().readFromFile(file)
        except FileNotFoundError:
            pass

        self._translators = {}

        self.showSplashMessage('Loading translations...')

        self.loadQtTranslation('uranium_qt')
        self.loadQtTranslation(self.getApplicationName() + '_qt')

    def run(self):
        pass

    def hideMessage(self, message):
        with self._message_lock:
            if message in self._visible_messages:
                self._visible_messages.remove(message)
                self.visibleMessageRemoved.emit(message)

    def showMessage(self, message):
        with self._message_lock:
            if message not in self._visible_messages:
                self._visible_messages.append(message)
                message.setTimer(QTimer())
                self.visibleMessageAdded.emit(message)

        pass

    def setMainQml(self, base_path, qml_file):
        if hasattr(sys, 'frozen'):
            self._main_qml = os.path.join(os.path.dirname(sys.executable), qml_file)
        else:
            self._main_qml = os.path.join(base_path, qml_file)

    def initializeEngine(self):
        # TODO: Document native/qml import trickery
        Bindings.register()

        self._engine = QQmlApplicationEngine()
        self.engineCreatedSignal.emit()
        if hasattr(sys, 'frozen'):
            self._engine.addImportPath(os.path.join(os.path.dirname(sys.executable), 'qml'))
        else:
            self._engine.addImportPath(os.path.join(os.path.dirname(__file__), 'qml'))

        self.registerObjects(self._engine)

        self._engine.load(self._main_qml)

    engineCreatedSignal = Signal()

    def registerObjects(self, engine):
        pass

    def getRenderer(self):
        if not self._renderer:
            self._renderer = QtGL2Renderer()

        return self._renderer

    #   Overridden from QApplication::setApplicationName to call our internal setApplicationName
    def setApplicationName(self, name):
        Application.setApplicationName(self, name)

    #   Handle a function that should be called later.
    def functionEvent(self, event):
        e = _QtFunctionEvent(event)
        QCoreApplication.postEvent(self, e)

    #   Handle Qt events
    def event(self, event):
        if event.type() == _QtFunctionEvent.QtFunctionEvent:
            event.functionEvent.call()
            return True

        return super().event(event)

    def windowClosed(self):
        self.getBackend().close()
        self.quit()
        self.saveMachines()
        Preferences.getInstance().writeToFile(Resources.getStoragePath(Resources.PreferencesLocation, self.getApplicationName() + '.cfg'))

    ##  Load a Qt translation catalog.
    #
    #   This method will locate, load and install a Qt message catalog that can be used
    #   by Qt's translation system, like qsTr() in QML files.
    #
    #   \param file The file name to load, without extension. It will be searched for in
    #               the i18nLocation Resources directory. If it can not be found a warning
    #               will be logged but no error will be thrown.
    #   \param language The language to load translations for. This can be any valid language code
    #                   or 'default' in which case the language is looked up based on system locale.
    #                   If the specified language can not be found, this method will fall back to
    #                   loading the english translations file.
    #
    #   \note When `language` is `default`, the language to load can be changed with the
    #         environment variable "LANGUAGE".
    def loadQtTranslation(self, file, language = 'default'):
        #TODO Add support for specifying a language from preferences
        path = None
        if language == 'default':
            # If we have a language set in the environment, try and use that.
            lang = os.getenv('LANGUAGE')
            if lang:
                try:
                    path = Resources.getPath(Resources.i18nLocation, lang, 'LC_MESSAGES', file + '.qm')
                except FileNotFoundError:
                    path = None

            # If looking up the language from the enviroment fails, try and use Qt's system locale instead.
            if not path:
                locale = QLocale.system()

                # First, try and find a directory for any of the provided languages
                for lang in locale.uiLanguages():
                    try:
                        path = Resources.getPath(Resources.i18nLocation, lang, "LC_MESSAGES", file + '.qm')
                        language = lang
                    except FileNotFoundError:
                        pass
                    else:
                        break

                # If that fails, see if we can extract a language "class" from the
                # preferred language. This will turn "en-GB" into "en" for example.
                if not path:
                    lang = locale.uiLanguages()[0]
                    lang = lang[0:lang.find('-')]
                    try:
                        path = Resources.getPath(Resources.i18nLocation, lang, "LC_MESSAGES", file + '.qm')
                        language = lang
                    except FileNotFoundError:
                        pass
        else:
            path = Resources.getPath(Resources.i18nLocation, language, "LC_MESSAGES", file + '.qm')

        # If all else fails, fall back to english.
        if not path:
            Logger.log('w', "Could not find any translations matching {0} for file {1}, falling back to english".format(language, file))
            try:
                path = Resources.getPath(Resources.i18nLocation, 'en', 'LC_MESSAGES', file + '.qm')
            except FileNotFoundError:
                Logger.log('w', "Could not find English translations for file {0}. Switching to developer english.".format(file))
                return

        translator = QTranslator()
        if not translator.load(path):
            Logger.log('e', "Unable to load translations %s", file)
            return

        # Store a reference to the translator.
        # This prevents the translator from being destroyed before Qt has a chance to use it.
        self._translators[file] = translator

        # Finally, install the translator so Qt can use it.
        self.installTranslator(translator)

    ##  Display text on the splash screen.
    def showSplashMessage(self, message):
        if self._splash:
            self._splash.showMessage(message , Qt.AlignHCenter | Qt.AlignBottom)
            self.processEvents()

    ##  Close the splash screen after the application has started.
    def closeSplash(self):
        if self._splash:
            self._splash.close()
            self._splash = None

##  Internal.
#
#   Wrapper around a FunctionEvent object to make Qt handle the event properly.
class _QtFunctionEvent(QEvent):
    QtFunctionEvent = QEvent.User + 1

    def __init__(self, fevent):
        super().__init__(self.QtFunctionEvent)
        self.functionEvent = fevent

<commit_msg>Change QtApplication::setMainQml to a simple setter again<commit_after>import sys
import os
import signal
import platform

from PyQt5.QtCore import Qt, QObject, QCoreApplication, QEvent, pyqtSlot, QLocale, QTranslator, QLibraryInfo
from PyQt5.QtQml import QQmlApplicationEngine, qmlRegisterType, qmlRegisterSingletonType
from PyQt5.QtWidgets import QApplication, QSplashScreen
from PyQt5.QtGui import QGuiApplication, QPixmap
from PyQt5.QtCore import QTimer

from UM.Application import Application
from UM.Qt.QtGL2Renderer import QtGL2Renderer
from UM.Qt.Bindings.Bindings import Bindings
from UM.JobQueue import JobQueue
from UM.Signal import Signal, SignalEmitter
from UM.Resources import Resources
from UM.Logger import Logger
from UM.Preferences import Preferences

##  Application subclass that provides a Qt application object.
class QtApplication(QApplication, Application, SignalEmitter):
    def __init__(self, **kwargs):
        if hasattr(sys, 'frozen') and sys.platform == 'win32':
            plugin_path = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), 'PyQt5', 'plugins')
            Logger.log('i', 'Adding QT5 plugin path: %s' % (plugin_path))
            QCoreApplication.addLibraryPath(plugin_path)
        super().__init__(sys.argv, **kwargs)

        self._main_qml = "main.qml"
        self._engine = None
        self._renderer = None

        try:
            self._splash = QSplashScreen(QPixmap(Resources.getPath(Resources.ImagesLocation, self.getApplicationName() + ".png")))
        except FileNotFoundError:
            self._splash = None
        else:
            self._splash.show()
            self.processEvents()

        signal.signal(signal.SIGINT, signal.SIG_DFL)
        # This is done here as a lot of plugins require a correct gl context. If you want to change the framework,
        # these checks need to be done in your <framework>Application.py class __init__().

        self.showSplashMessage('Loading plugins...')
        self._loadPlugins()
        self._plugin_registry.checkRequiredPlugins(self.getRequiredPlugins())

        self.showSplashMessage('Loading machines...')
        self.loadMachines()

        self.showSplashMessage('Loading preferences...')
        try:
            file = Resources.getPath(Resources.PreferencesLocation, self.getApplicationName() + '.cfg')
            Preferences.getInstance().readFromFile(file)
        except FileNotFoundError:
            pass

        self._translators = {}

        self.showSplashMessage('Loading translations...')

        self.loadQtTranslation('uranium_qt')
        self.loadQtTranslation(self.getApplicationName() + '_qt')

    def run(self):
        pass

    def hideMessage(self, message):
        with self._message_lock:
            if message in self._visible_messages:
                self._visible_messages.remove(message)
                self.visibleMessageRemoved.emit(message)

    def showMessage(self, message):
        with self._message_lock:
            if message not in self._visible_messages:
                self._visible_messages.append(message)
                message.setTimer(QTimer())
                self.visibleMessageAdded.emit(message)

        pass

    def setMainQml(self, path):
        self._main_qml = path

    def initializeEngine(self):
        # TODO: Document native/qml import trickery
        Bindings.register()

        self._engine = QQmlApplicationEngine()
        self.engineCreatedSignal.emit()
        if hasattr(sys, 'frozen'):
            self._engine.addImportPath(os.path.join(os.path.dirname(sys.executable), 'qml'))
        else:
            self._engine.addImportPath(os.path.join(os.path.dirname(__file__), 'qml'))

        self.registerObjects(self._engine)

        self._engine.load(self._main_qml)

    engineCreatedSignal = Signal()

    def registerObjects(self, engine):
        pass

    def getRenderer(self):
        if not self._renderer:
            self._renderer = QtGL2Renderer()

        return self._renderer

    #   Overridden from QApplication::setApplicationName to call our internal setApplicationName
    def setApplicationName(self, name):
        Application.setApplicationName(self, name)

    #   Handle a function that should be called later.
    def functionEvent(self, event):
        e = _QtFunctionEvent(event)
        QCoreApplication.postEvent(self, e)

    #   Handle Qt events
    def event(self, event):
        if event.type() == _QtFunctionEvent.QtFunctionEvent:
            event.functionEvent.call()
            return True

        return super().event(event)

    def windowClosed(self):
        self.getBackend().close()
        self.quit()
        self.saveMachines()
        Preferences.getInstance().writeToFile(Resources.getStoragePath(Resources.PreferencesLocation, self.getApplicationName() + '.cfg'))

    ##  Load a Qt translation catalog.
    #
    #   This method will locate, load and install a Qt message catalog that can be used
    #   by Qt's translation system, like qsTr() in QML files.
    #
    #   \param file The file name to load, without extension. It will be searched for in
    #               the i18nLocation Resources directory. If it can not be found a warning
    #               will be logged but no error will be thrown.
    #   \param language The language to load translations for. This can be any valid language code
    #                   or 'default' in which case the language is looked up based on system locale.
    #                   If the specified language can not be found, this method will fall back to
    #                   loading the english translations file.
    #
    #   \note When `language` is `default`, the language to load can be changed with the
    #         environment variable "LANGUAGE".
    def loadQtTranslation(self, file, language = 'default'):
        #TODO Add support for specifying a language from preferences
        path = None
        if language == 'default':
            # If we have a language set in the environment, try and use that.
            lang = os.getenv('LANGUAGE')
            if lang:
                try:
                    path = Resources.getPath(Resources.i18nLocation, lang, 'LC_MESSAGES', file + '.qm')
                except FileNotFoundError:
                    path = None

            # If looking up the language from the enviroment fails, try and use Qt's system locale instead.
            if not path:
                locale = QLocale.system()

                # First, try and find a directory for any of the provided languages
                for lang in locale.uiLanguages():
                    try:
                        path = Resources.getPath(Resources.i18nLocation, lang, "LC_MESSAGES", file + '.qm')
                        language = lang
                    except FileNotFoundError:
                        pass
                    else:
                        break

                # If that fails, see if we can extract a language "class" from the
                # preferred language. This will turn "en-GB" into "en" for example.
                if not path:
                    lang = locale.uiLanguages()[0]
                    lang = lang[0:lang.find('-')]
                    try:
                        path = Resources.getPath(Resources.i18nLocation, lang, "LC_MESSAGES", file + '.qm')
                        language = lang
                    except FileNotFoundError:
                        pass
        else:
            path = Resources.getPath(Resources.i18nLocation, language, "LC_MESSAGES", file + '.qm')

        # If all else fails, fall back to english.
        if not path:
            Logger.log('w', "Could not find any translations matching {0} for file {1}, falling back to english".format(language, file))
            try:
                path = Resources.getPath(Resources.i18nLocation, 'en', 'LC_MESSAGES', file + '.qm')
            except FileNotFoundError:
                Logger.log('w', "Could not find English translations for file {0}. Switching to developer english.".format(file))
                return

        translator = QTranslator()
        if not translator.load(path):
            Logger.log('e', "Unable to load translations %s", file)
            return

        # Store a reference to the translator.
        # This prevents the translator from being destroyed before Qt has a chance to use it.
        self._translators[file] = translator

        # Finally, install the translator so Qt can use it.
        self.installTranslator(translator)

    ##  Display text on the splash screen.
    def showSplashMessage(self, message):
        if self._splash:
            self._splash.showMessage(message , Qt.AlignHCenter | Qt.AlignBottom)
            self.processEvents()

    ##  Close the splash screen after the application has started.
    def closeSplash(self):
        if self._splash:
            self._splash.close()
            self._splash = None

##  Internal.
#
#   Wrapper around a FunctionEvent object to make Qt handle the event properly.
class _QtFunctionEvent(QEvent):
    QtFunctionEvent = QEvent.User + 1

    def __init__(self, fevent):
        super().__init__(self.QtFunctionEvent)
        self.functionEvent = fevent

<|endoftext|>


<commit_before><commit_msg>Final version for presentation of the demo<commit_after><|endoftext|>


<commit_before><commit_msg>added some logic in bridge pattern<commit_after>from abc import ABCMeta, abstractmethod

class DrawingApi:
    __metaclass__ = ABCMeta

    @abstractmethod
    def draw_circle(x, y, radius):
        pass

class DrawingApi1(DrawingApi):

    def draw_circle(x, y, radius):
        print "Api1 ",x,y,radius

class DrawingApi2(DrawingApi):

    def draw_circle(x, y, radius):
        print "Api1 ",x,y,radius

class Shape:
    __metaclass__ = ABCMeta

    def __init__(self, drawing_api):
        self.drawing_api = DrawingApi()

    def draw(self):
        pass

    def resize_by_percentage(pct):
        pass

<|endoftext|>


<commit_before>import discord
from discord.ext import commands
from .utils import checks
from cogs.utils.dataIO import dataIO, fileIO
from __main__ import send_cmd_help

import json
import os
import asyncio
import aiohttp
import datetime
import random
import time

try: # check if BeautifulSoup4 is installed
	from bs4 import BeautifulSoup
	soupAvailable = True
except:
	soupAvailable = False

class APIError(Exception):
	pass

class ShinyAPIError(Exception):
	pass

class APIKeyError(Exception):
	pass

class Gw2:
	"""This cog finds tp prices"""

	def __init__(self, bot):
		self.bot = bot
		self.session = aiohttp.ClientSession(loop=self.bot.loop)
		self.gemtrack = dataIO.load_json("data/gw2/gemtrack.json")

	def save_gemtrack(self):
        dataIO.save_json("data/gw2/gemtrack.json", self.gemtrack)

    @commands.command(pass_context=True)
	async def gemtrack(self, ctx, price : int):
		"""This requests to be notified when the cost of 400 gems drops below a specified price"""
		user = ctx.message.author
		color = self.getColor(user)

		self.gemtrack[user.id] = { "user_id": user.id, "price": price }
		self.save_gemtrack();

		await self.bot.say("{0.mention}, you'll be notified when the price of 400 gems drops below {1}".format(user, self.gold_to_coins(threshold)))

	# tracks gemprices and notifies people
	async def _gemprice_tracker(self):
        while self is self.bot.get_cog("Gw2"):
        	gemPrice = getGemPrice()
        	for user_id, data in self.gemtrack:
        		if (gemPrice < data["price"])
        			user = get_user_info(user_id)
        			await self.bot.send_message(user, "Hey, {0}. Gem prices have dropped below {1}!".format(user.name, data["price"]))
        			self.gemtrack.pop(user_id)
        			self.save_gemtrack()

            await asyncio.sleep(60)

	@commands.command(pass_context=True)
	async def tpdata(self, ctx, *, tpitemname: str):
		"""This finds the current buy and sell prices of an item
		If multiple matches are found, displays the first"""
		user = ctx.message.author
		tpitemname = tpitemname.replace(" ", "%20")
		color = self.getColor(user)
		try:
			shiniesendpoint = tpitemname
			shiniesresults = await self.call_shiniesapi(shiniesendpoint)
			itemnameresult = shiniesresults[0]["name"]
			tpbuyid = shiniesresults[0]["item_id"]
			commerce = 'commerce/prices/'
			endpoint = commerce + tpbuyid
			results = await self.call_api(endpoint)
		except APIKeyError as e:
			await self.bot.say(e)
			return
		except ShinyAPIError as e:
			await self.bot.say("{0.mention}, API has responded with the following error: "
							   "`{1}`".format(user, e))
			return
		except APIError as e:
			data = discord.Embed(description='I was unable to match that to an item on the TP , listing all - use !tpid (id) to select one', colour=color)
			#For each item returned, add to the data table
			counter = 0
			for name in shiniesresults:
				if counter < 10:
					data.add_field(name=name['name'], value=name['item_id'])
					counter += 1
			try:
				await self.bot.say(embed=data)
				if counter > 9:
					await self.bot.say("More than 10 entries, try to refine your search")
			except discord.HTTPException:
				await self.bot.say("Issue embedding data into discord - EC1")
			return
		buyprice = results["buys"]["unit_price"]
		sellprice = results ["sells"]["unit_price"]
		if buyprice != 0:
			buyprice = self.gold_to_coins(buyprice)
		if sellprice != 0:
			sellprice = self.gold_to_coins(sellprice)
		if buyprice == 0:
			buyprice = 'No buy orders'
		if sellprice == 0:
			sellprice = 'No sell orders'
		data = discord.Embed(title=itemnameresult, description='Not the item you wanted? Try !tplist (name) instead', colour=color)
		data.add_field(name="Buy price", value=buyprice)
		data.add_field(name="Sell price", value=sellprice)

		try:
			await self.bot.say(embed=data)
		except discord.HTTPException:
			await self.bot.say("Issue embedding data into discord - EC2")

	@commands.command(pass_context=True)
	async def tpid(self, ctx, *, tpdataid: str):
		"""This finds the current buy and sell prices of an item
		If multiple matches are found, displays the first"""
		user = ctx.message.author
		color = self.getColor(user)
		try:
			commerce = 'commerce/prices/'
			endpoint = commerce + tpdataid
			results = await self.call_api(endpoint)
			items = 'items/'
			endpoint = items + tpdataid
			itemsresult = await self.call_api(endpoint)
			itemnameresult = itemsresult["name"]
		except APIKeyError as e:
			await self.bot.say(e)
			return
		except APIError as e:
			await self.bot.say("{0.mention}, This item isn't on the TP "
							   "`{1}`".format(user, e))
			return
		buyprice = results["buys"]["unit_price"]
		sellprice = results ["sells"]["unit_price"]
		if buyprice != 0:
			buyprice = self.gold_to_coins(buyprice)
		if sellprice != 0:
			sellprice = self.gold_to_coins(sellprice)
		if buyprice == 0:
			buyprice = 'No buy orders'
		if sellprice == 0:
			sellprice = 'No sell orders'
		data = discord.Embed(title=itemnameresult, colour=color)
		data.add_field(name="Buy price", value=buyprice)
		data.add_field(name="Sell price", value=sellprice)

		try:
			await self.bot.say(embed=data)
		except discord.HTTPException:
			await self.bot.say("Iss")

	@commands.command(pass_context=True)
	async def tplist(self, ctx, *, tpitemname: str):
		"""This lists the ids and names of all matching items to the entered name"""
		user = ctx.message.author
		color = self.getColor(user)
		tpitemname = tpitemname.replace(" ", "%20")
		try:
			shiniesendpoint = tpitemname
			shiniesresults = await self.call_shiniesapi(shiniesendpoint)
		except APIKeyError as e:
			await self.bot.say(e)
			return
		except ShinyAPIError as e:
			await self.bot.say("{0.mention}, API has responded with the following error: "
							   "`{1}`".format(user, e))
			return
		data = discord.Embed(description='Matching IDs, use !tpid (id) to see prices for a specific item', colour=color)
		#For each item returned, add to the data table
		counter = 0
		for name in shiniesresults:
			if counter < 10:
				data.add_field(name=name['name'], value=name['item_id'])
				counter += 1
		try:
			await self.bot.say(embed=data)
			if counter > 9:
				await self.bot.say("More than 10 entries, try to refine your search")
		except discord.HTTPException:
			await self.bot.say("Issue embedding data into discord - EC3")

	@commands.command(pass_context=True)
	async def quaggan(self, ctx, *, quaggan_name : str = 'random'):
		"""This displays a quaggan"""
		user = ctx.message.author
		color = self.getColor(user)
		endpoint = 'quaggans'
		base_quaggan = 'https://static.staticwars.com/quaggans/'
		try:
			l_quaggans = await self.call_api(endpoint)
			if quaggan_name == 'random':
				quaggan_name = random.choice(l_quaggans)
				URL_quaggan = base_quaggan + quaggan_name + '.jpg'
				await self.bot.say(URL_quaggan)
			elif quaggan_name == 'list':
				data = discord.Embed(title='Available quaggans')
				data.add_field(name="List of quaggans", value=', '.join(l_quaggans))
				try:
					await self.bot.say(embed=data)
				except discord.HTTPException:
					await self.bot.say("Issue embedding data into discord - EC4")
			elif quaggan_name in l_quaggans:
				URL_quaggan = base_quaggan + quaggan_name + '.jpg'
				await self.bot.say(URL_quaggan)
			else:
				await self.bot.say("I couldn't find the requested quaggan. List of all available quaggans:")
				data = discord.Embed(title='Available quaggans')
				data.add_field(name="List of quaggans", value=', '.join(l_quaggans))
				try:
					await self.bot.say(embed=data)
				except discord.HTTPException:
					await self.bot.say("Issue embedding data into discord - EC5")
		except APIError as e:
			await self.bot.say("{0.mention}, API returned the following error:  "
							"`{1}`".format(user, e))
			return

	def getGemPrice(numberOfGems : int = 400):
		try:
			endpoint = "commerce/exchange/coins?quantity=10000000"
			gemsresult = await self.call_api(endpoint)
			return gemsresult['coins_per_gem']*numberOfGems
		except APIKeyError as e:
			await self.bot.say(e)
			return 0

	@commands.command(pass_context=True)
	async def gemprice(self, ctx, numberOfGems : int = 400):
		"""This lists current gold/gem prices"""
		user = ctx.message.author
		color = self.getColor(user)

		gemCost = getGemPrice(numberOfGems)

		# If this is zero then the API is down (OR GEMS ARE FREE!!! OMG \o/)
		if (gemCost == 0)
			return

		# Display data
		data = discord.Embed(title="Gem / Gold price", colour=color)
		data.add_field(name=str(numberOfGems) + " Gems",value=self.gold_to_coins(gemCost))

		try:
			await self.bot.say(embed=data)
		except discord.HTTPException:
			await self.bot.say("Issue embedding data into discord - EC3")

	async def call_api(self, endpoint):
		apiserv = 'https://api.guildwars2.com/v2/'
		url = apiserv + endpoint
		async with self.session.get(url) as r:
			results = await r.json()
		if "error" in results:
			raise APIError("The API is dead!")
		if "text" in results:
			raise APIError(results["text"])
		return results
	async def call_shiniesapi(self, shiniesendpoint):
		shinyapiserv = 'https://www.gw2shinies.com/api/json/idbyname/'
		url = shinyapiserv + shiniesendpoint
		async with self.session.get(url) as r:
			shiniesresults = await r.json()
		if shiniesresults is None:
			raise ShinyAPIError("Could not find an item by that name")
		if "error" in shiniesresults:
			raise ShinyAPIError("The API is dead!")
		if "text" in shiniesresults:
			raise ShinyAPIError(shiniesresults["text"])
		return shiniesresults

	def gold_to_coins(self, money):
		gold, remainder = divmod(money, 10000)
		silver, copper = divmod(remainder, 100)
		if gold == 0:
			gold_string = ""
		else:
			gold_string = str(gold) + "g"
		if silver == 0:
			silver_string = ""
		else:
			silver_string = " " + str(silver) + "s"
		if copper == 0:
			copper_string = ""
		else:
			copper_string = " " + str(copper) + "c"
		return gold_string + silver_string + copper_string

	def getColor(self, user):
		try:
			color = user.colour
		except:
			color = discord.Embed.Empty
		return color

def check_folders():
	if not os.path.exists("data/guildwars2"):
		print("Creating data/guildwars2")
		os.makedirs("data/guildwars2")

def check_files():
	files = {
		"gamedata.json": {},
		"settings.json": {"ENABLED": False},
		"language.json": {},
		"keys.json": {},
		"build.json": {"id": None}  # Yay legacy support
	}

	for filename, value in files.items():
		if not os.path.isfile("data/guildwars2/{}".format(filename)):
			print("Creating empty {}".format(filename))
			dataIO.save_json("data/guildwars2/{}".format(filename), value)

def setup(bot):
	if soupAvailable:
		bot.add_cog(Gw2(bot))
	else:
		raise RuntimeError("You need to run `pip3 install beautifulsoup4`")
	check_folders()
	check_files()
	n = Gw2(bot)
	loop = asyncio.get_event_loop()
	loop.create_task(n._gemprice_tracker())
	bot.add_cog(n)<commit_msg>Fixed spaces<commit_after>import discord
from discord.ext import commands
from .utils import checks
from cogs.utils.dataIO import dataIO, fileIO
from __main__ import send_cmd_help

import json
import os
import asyncio
import aiohttp
import datetime
import random
import time

try: # check if BeautifulSoup4 is installed
	from bs4 import BeautifulSoup
	soupAvailable = True
except:
	soupAvailable = False

class APIError(Exception):
	pass

class ShinyAPIError(Exception):
	pass

class APIKeyError(Exception):
	pass

class Gw2:
	"""This cog finds tp prices"""

	def __init__(self, bot):
		self.bot = bot
		self.session = aiohttp.ClientSession(loop=self.bot.loop)
		self.gemtrack = dataIO.load_json("data/gw2/gemtrack.json")

	def save_gemtrack(self):
		dataIO.save_json("data/gw2/gemtrack.json", self.gemtrack)

	@commands.command(pass_context=True)
	async def gemtrack(self, ctx, price : int):
		"""This requests to be notified when the cost of 400 gems drops below a specified price"""
		user = ctx.message.author
		color = self.getColor(user)

		self.gemtrack[user.id] = { "user_id": user.id, "price": price }
		self.save_gemtrack();

		await self.bot.say("{0.mention}, you'll be notified when the price of 400 gems drops below {1}".format(user, self.gold_to_coins(threshold)))

	# tracks gemprices and notifies people
	async def _gemprice_tracker(self):
        while self is self.bot.get_cog("Gw2"):
        	gemPrice = getGemPrice()
        	for user_id, data in self.gemtrack:
        		if (gemPrice < data["price"])
        			user = get_user_info(user_id)
        			await self.bot.send_message(user, "Hey, {0}. Gem prices have dropped below {1}!".format(user.name, data["price"]))
        			self.gemtrack.pop(user_id)
        			self.save_gemtrack()

            await asyncio.sleep(60)

	@commands.command(pass_context=True)
	async def tpdata(self, ctx, *, tpitemname: str):
		"""This finds the current buy and sell prices of an item
		If multiple matches are found, displays the first"""
		user = ctx.message.author
		tpitemname = tpitemname.replace(" ", "%20")
		color = self.getColor(user)
		try:
			shiniesendpoint = tpitemname
			shiniesresults = await self.call_shiniesapi(shiniesendpoint)
			itemnameresult = shiniesresults[0]["name"]
			tpbuyid = shiniesresults[0]["item_id"]
			commerce = 'commerce/prices/'
			endpoint = commerce + tpbuyid
			results = await self.call_api(endpoint)
		except APIKeyError as e:
			await self.bot.say(e)
			return
		except ShinyAPIError as e:
			await self.bot.say("{0.mention}, API has responded with the following error: "
							   "`{1}`".format(user, e))
			return
		except APIError as e:
			data = discord.Embed(description='I was unable to match that to an item on the TP , listing all - use !tpid (id) to select one', colour=color)
			#For each item returned, add to the data table
			counter = 0
			for name in shiniesresults:
				if counter < 10:
					data.add_field(name=name['name'], value=name['item_id'])
					counter += 1
			try:
				await self.bot.say(embed=data)
				if counter > 9:
					await self.bot.say("More than 10 entries, try to refine your search")
			except discord.HTTPException:
				await self.bot.say("Issue embedding data into discord - EC1")
			return
		buyprice = results["buys"]["unit_price"]
		sellprice = results ["sells"]["unit_price"]
		if buyprice != 0:
			buyprice = self.gold_to_coins(buyprice)
		if sellprice != 0:
			sellprice = self.gold_to_coins(sellprice)
		if buyprice == 0:
			buyprice = 'No buy orders'
		if sellprice == 0:
			sellprice = 'No sell orders'
		data = discord.Embed(title=itemnameresult, description='Not the item you wanted? Try !tplist (name) instead', colour=color)
		data.add_field(name="Buy price", value=buyprice)
		data.add_field(name="Sell price", value=sellprice)

		try:
			await self.bot.say(embed=data)
		except discord.HTTPException:
			await self.bot.say("Issue embedding data into discord - EC2")

	@commands.command(pass_context=True)
	async def tpid(self, ctx, *, tpdataid: str):
		"""This finds the current buy and sell prices of an item
		If multiple matches are found, displays the first"""
		user = ctx.message.author
		color = self.getColor(user)
		try:
			commerce = 'commerce/prices/'
			endpoint = commerce + tpdataid
			results = await self.call_api(endpoint)
			items = 'items/'
			endpoint = items + tpdataid
			itemsresult = await self.call_api(endpoint)
			itemnameresult = itemsresult["name"]
		except APIKeyError as e:
			await self.bot.say(e)
			return
		except APIError as e:
			await self.bot.say("{0.mention}, This item isn't on the TP "
							   "`{1}`".format(user, e))
			return
		buyprice = results["buys"]["unit_price"]
		sellprice = results ["sells"]["unit_price"]
		if buyprice != 0:
			buyprice = self.gold_to_coins(buyprice)
		if sellprice != 0:
			sellprice = self.gold_to_coins(sellprice)
		if buyprice == 0:
			buyprice = 'No buy orders'
		if sellprice == 0:
			sellprice = 'No sell orders'
		data = discord.Embed(title=itemnameresult, colour=color)
		data.add_field(name="Buy price", value=buyprice)
		data.add_field(name="Sell price", value=sellprice)

		try:
			await self.bot.say(embed=data)
		except discord.HTTPException:
			await self.bot.say("Iss")

	@commands.command(pass_context=True)
	async def tplist(self, ctx, *, tpitemname: str):
		"""This lists the ids and names of all matching items to the entered name"""
		user = ctx.message.author
		color = self.getColor(user)
		tpitemname = tpitemname.replace(" ", "%20")
		try:
			shiniesendpoint = tpitemname
			shiniesresults = await self.call_shiniesapi(shiniesendpoint)
		except APIKeyError as e:
			await self.bot.say(e)
			return
		except ShinyAPIError as e:
			await self.bot.say("{0.mention}, API has responded with the following error: "
							   "`{1}`".format(user, e))
			return
		data = discord.Embed(description='Matching IDs, use !tpid (id) to see prices for a specific item', colour=color)
		#For each item returned, add to the data table
		counter = 0
		for name in shiniesresults:
			if counter < 10:
				data.add_field(name=name['name'], value=name['item_id'])
				counter += 1
		try:
			await self.bot.say(embed=data)
			if counter > 9:
				await self.bot.say("More than 10 entries, try to refine your search")
		except discord.HTTPException:
			await self.bot.say("Issue embedding data into discord - EC3")

	@commands.command(pass_context=True)
	async def quaggan(self, ctx, *, quaggan_name : str = 'random'):
		"""This displays a quaggan"""
		user = ctx.message.author
		color = self.getColor(user)
		endpoint = 'quaggans'
		base_quaggan = 'https://static.staticwars.com/quaggans/'
		try:
			l_quaggans = await self.call_api(endpoint)
			if quaggan_name == 'random':
				quaggan_name = random.choice(l_quaggans)
				URL_quaggan = base_quaggan + quaggan_name + '.jpg'
				await self.bot.say(URL_quaggan)
			elif quaggan_name == 'list':
				data = discord.Embed(title='Available quaggans')
				data.add_field(name="List of quaggans", value=', '.join(l_quaggans))
				try:
					await self.bot.say(embed=data)
				except discord.HTTPException:
					await self.bot.say("Issue embedding data into discord - EC4")
			elif quaggan_name in l_quaggans:
				URL_quaggan = base_quaggan + quaggan_name + '.jpg'
				await self.bot.say(URL_quaggan)
			else:
				await self.bot.say("I couldn't find the requested quaggan. List of all available quaggans:")
				data = discord.Embed(title='Available quaggans')
				data.add_field(name="List of quaggans", value=', '.join(l_quaggans))
				try:
					await self.bot.say(embed=data)
				except discord.HTTPException:
					await self.bot.say("Issue embedding data into discord - EC5")
		except APIError as e:
			await self.bot.say("{0.mention}, API returned the following error:  "
							"`{1}`".format(user, e))
			return

	def getGemPrice(numberOfGems : int = 400):
		try:
			endpoint = "commerce/exchange/coins?quantity=10000000"
			gemsresult = await self.call_api(endpoint)
			return gemsresult['coins_per_gem']*numberOfGems
		except APIKeyError as e:
			await self.bot.say(e)
			return 0

	@commands.command(pass_context=True)
	async def gemprice(self, ctx, numberOfGems : int = 400):
		"""This lists current gold/gem prices"""
		user = ctx.message.author
		color = self.getColor(user)

		gemCost = getGemPrice(numberOfGems)

		# If this is zero then the API is down (OR GEMS ARE FREE!!! OMG \o/)
		if (gemCost == 0)
			return

		# Display data
		data = discord.Embed(title="Gem / Gold price", colour=color)
		data.add_field(name=str(numberOfGems) + " Gems",value=self.gold_to_coins(gemCost))

		try:
			await self.bot.say(embed=data)
		except discord.HTTPException:
			await self.bot.say("Issue embedding data into discord - EC3")

	async def call_api(self, endpoint):
		apiserv = 'https://api.guildwars2.com/v2/'
		url = apiserv + endpoint
		async with self.session.get(url) as r:
			results = await r.json()
		if "error" in results:
			raise APIError("The API is dead!")
		if "text" in results:
			raise APIError(results["text"])
		return results
	async def call_shiniesapi(self, shiniesendpoint):
		shinyapiserv = 'https://www.gw2shinies.com/api/json/idbyname/'
		url = shinyapiserv + shiniesendpoint
		async with self.session.get(url) as r:
			shiniesresults = await r.json()
		if shiniesresults is None:
			raise ShinyAPIError("Could not find an item by that name")
		if "error" in shiniesresults:
			raise ShinyAPIError("The API is dead!")
		if "text" in shiniesresults:
			raise ShinyAPIError(shiniesresults["text"])
		return shiniesresults

	def gold_to_coins(self, money):
		gold, remainder = divmod(money, 10000)
		silver, copper = divmod(remainder, 100)
		if gold == 0:
			gold_string = ""
		else:
			gold_string = str(gold) + "g"
		if silver == 0:
			silver_string = ""
		else:
			silver_string = " " + str(silver) + "s"
		if copper == 0:
			copper_string = ""
		else:
			copper_string = " " + str(copper) + "c"
		return gold_string + silver_string + copper_string

	def getColor(self, user):
		try:
			color = user.colour
		except:
			color = discord.Embed.Empty
		return color

def check_folders():
	if not os.path.exists("data/guildwars2"):
		print("Creating data/guildwars2")
		os.makedirs("data/guildwars2")

def check_files():
	files = {
		"gamedata.json": {},
		"settings.json": {"ENABLED": False},
		"language.json": {},
		"keys.json": {},
		"build.json": {"id": None}  # Yay legacy support
	}

	for filename, value in files.items():
		if not os.path.isfile("data/guildwars2/{}".format(filename)):
			print("Creating empty {}".format(filename))
			dataIO.save_json("data/guildwars2/{}".format(filename), value)

def setup(bot):
	if soupAvailable:
		bot.add_cog(Gw2(bot))
	else:
		raise RuntimeError("You need to run `pip3 install beautifulsoup4`")
	check_folders()
	check_files()
	n = Gw2(bot)
	loop = asyncio.get_event_loop()
	loop.create_task(n._gemprice_tracker())
	bot.add_cog(n)<|endoftext|>