<commit_before>import dmidecode
import json
import multiprocessing
import os
import re
import subprocess
import uuid

from lxml import etree

from . import benchmark
from . import utils

def get_subsection_value(output, section_name, subsection_name):
    """Extract data from tabulated output like lshw and dmidecode."""
    section = output.find(section_name)
    ## end_section = output.find("*-", section)
    ## XXX WIP try to limit context to a section (how to detect end on dmidecode?)
    ## NOTE will be replaced with dmidecode Python2 library
    subsection = output.find(subsection_name, section)#, end_section)
    end = output.find("\n", subsection)
    return output[subsection:end].split(':')[1].strip()

class GraphicCard(object):
    def __init__(self, lshw):
        product = get_subsection_value(lshw, "display", "product")
        vendor = get_subsection_value(lshw, "display", "vendor")
        if product or vendor:
            self.model = "{vendor} {product}".format(vendor=vendor, product=product)
        else:
            self.model = get_subsection_value(lshw, "display", "description")

        # Find VGA memory
        bus_info = get_subsection_value(lshw, "display", "bus info").split("@")[1]
        mem = utils.run("lspci -v -s {bus} | grep 'prefetchable' | grep -v 'non-prefetchable' | egrep -o '[0-9]{{1,3}}[KMGT]+'".format(bus=bus_info)).splitlines()

        # Get max memory value
        max_size = 0
        for value in mem:
            unit = re.split('\d+', value)[1]
            size = int(value.rstrip(unit))

            # convert all values to KB before compare
            size_kb = utils.convert_base(size, unit, 'K', distance=1024)
            if size_kb > max_size:
                max_size = size_kb

        self.size = utils.convert_capacity(max_size, 'KB', 'MB')

class Processor(object):
    FREQ_UNIT = 'GHz'

    def __init__(self, lshw_json):
        self.number_cpus = multiprocessing.cpu_count()  # Python > 3.4 os.cpu_count()
        self.number_cores = os.popen("lscpu | grep 'Core(s) per socket'").read().split(':')[1].strip()

        cpu_data = lshw_json['children'][0]['children'][1]
        self.product = re.sub(r"\s+ ", " ", cpu_data['product'])
        self.vendor = cpu_data['vendor']  # was /proc/cpuinfo | grep vendor_id

        speed = dmidecode.processor()['0x0004']['data']['Current Speed']
        self.freq = utils.convert_frequency(speed, 'MHz', self.FREQ_UNIT)

class MemoryModule(object):
    CAPACITY_UNIT = 'MB'

    def __init__(self, lshw_json):
        ram_data = lshw_json['children'][0]['children'][0]
        dmidecode_out = utils.run("dmidecode -t 17")
        # dmidecode.QueryTypeId(7)

        # TODO optimize to only use a dmidecode call
        self.total_slots = int(utils.run("dmidecode -t 17 | grep -o BANK | wc -l"))
        self.used_slots = int(utils.run("dmidecode -t 17 | grep Size | grep MB | awk '{print $2}' | wc -l"))
        self.speed = get_subsection_value(dmidecode_out, "Memory Device", "Speed")
        self.interface = get_subsection_value(dmidecode_out, "Memory Device", "Type")

        # FIXME get total size but describe slot per slot
        size = 0
        for key, value in dmidecode.memory().iteritems():
            if value['data'].get('Size', None) is not None:
                size += int(value['data']['Size'].split()[0])

        self.size = size

class Computer(object):
    def __init__(self):
        # http://www.ezix.org/project/wiki/HardwareLiSter
        # JSON
        lshw_js = subprocess.check_output(["lshw", "-json"], universal_newlines=True)
        self.lshw_json = json.loads(lshw_js)

        # XML
        self.lshw_xml = etree.fromstring(subprocess.check_output(["lshw", "-xml"]))

        # Plain text (current)
        self.lshw = subprocess.check_output(["lshw"], universal_newlines=True)
        self.dmi = subprocess.check_output(["dmidecode"], universal_newlines=True)

        self.init_serials()

    def init_serials(self):
        # getnode attempts to obtain the hardware address, if fails it
        # chooses a random 48-bit number with its eight bit set to 1
        # Deprecated hw_addr as ID. TODO use device SN as ID
        self.ID = uuid.getnode()
        if (self.ID >> 40) % 2:
            raise OSError("The system does not seem to have a valid MAC.")

        ## Search manufacturer's serial number
        self.SERIAL1 = get_subsection_value(self.dmi, "System Information", "Serial Number")

        ## Search motherboard's serial number
        self.SERIAL2 = get_subsection_value(self.dmi, "Base Board Information", "Serial Number")

        ## Search CPU's serial number, if there are several we choose the first
        # A) dmidecode -t processor
        # FIXME Serial Number returns "To be filled by OEM"
        # http://forum.giga-byte.co.uk/index.php?topic=14167.0
        # self.SERIAL3 = get_subsection_value(self.dmi, "Processor Information", "ID")
        self.SERIAL3 = get_subsection_value(self.lshw, "*-cpu", "serial")

        # B) Try to call CPUID? https://en.wikipedia.org/wiki/CPUID
        # http://stackoverflow.com/a/4216034/1538221

        ## Search RAM's serial number, if there are several we choose the first
        dmi_memory = subprocess.check_output(["dmidecode", "-t" "memory"], universal_newlines=True)
        self.SERIAL4 = get_subsection_value(dmi_memory, "Memory Device", "Serial Number")

        ## Search hard disk's serial number, if there are several we choose the first
        # FIXME JSON loads fails because of a bug on lshw
        # https://bugs.launchpad.net/ubuntu/+source/lshw/+bug/1405873
        # lshw_disk = json.loads(subprocess.check_output(["lshw", "-json", "-class", "disk"]))
        self.SERIAL5 = get_subsection_value(self.lshw, "*-disk", "serial")

        # Deprecated: cksum CRC32 joining 5 serial numbers as secundary ID
        #ID2=`echo ${SERIAL1} ${SERIAL2} ${SERIAL3} ${SERIAL4} ${SERIAL5} | cksum | awk {'print $1'}`
        cmd = "echo {0} {1} {2} {3} {4} | cksum | awk {{'print $1'}}".format(
            self.SERIAL1, self.SERIAL2, self.SERIAL3, self.SERIAL4, self.SERIAL5)
        self.ID2 = os.popen(cmd).read().strip()

    @property
    def serials(self):
        return {
            "serial_fab": self.SERIAL1,
            "serial_mot": self.SERIAL2,
            "serial_cpu": self.SERIAL3,
            "serial_ram": self.SERIAL4,
            "serial_hdd": self.SERIAL5,
        }

    @property
    def cpu(self):
        processor = Processor(self.lshw_json)

        return {
            'nom_cpu': processor.product,
            'fab_cpu': processor.vendor,
            'speed_cpu': processor.freq,
            'unit_speed_cpu': processor.FREQ_UNIT,
            'number_cpu': processor.number_cpus,
            'number_cores': processor.number_cores,
            'score_cpu': benchmark.score_cpu(),
        }

    @property
    def ram(self):
        # TODO split computer.total_memory and MemoryModule(s) as components
        memory = MemoryModule(self.lshw_json)
        return {
            'size_ram': memory.size,
            'unit_size': memory.CAPACITY_UNIT,
            # EDO|SDRAM|DDR3|DDR2|DDR|RDRAM
            'interface_ram': memory.interface,
            'free_slots_ram': memory.total_slots - memory.used_slots,
            'used_slots_ram': memory.used_slots,
            'score_ram': benchmark.score_ram(memory.speed),
        }

    @property
    def hdd(self):
        # optimization? lshw -json -class disk
        # use dict lookup http://stackoverflow.com/a/27234926/1538221
        # NOTE only gets info of first HD
        logical_name = get_subsection_value(self.lshw, "*-disk", "logical name")
        interface = utils.run("udevadm info --query=all --name={0} | grep ID_BUS | cut -c 11-".format(logical_name))

        # TODO implement method for USB disk
        if interface == "usb":
            model = serial = size = "Unknown"

        else:
            # (S)ATA disk
            model = utils.run("hdparm -I {0} | grep 'Model\ Number' | cut -c 22-".format(logical_name))
            serial = utils.run("hdparm -I {0} | grep 'Serial\ Number' | cut -c 22-".format(logical_name))
            size = utils.run("hdparm -I {0} | grep 'device\ size\ with\ M' | head -n1 | awk '{{print $7}}'".format(logical_name))

        return {
            "model": model,
            "serial": serial,
            "size": size,
            "measure": "MB",
            "name": logical_name,
            "interface": interface,
        }

    @property
    def vga(self):
        gcard = GraphicCard(self.lshw)
        return {
            "model_vga": gcard.model,
            "size_vga": gcard.size,
            "unit_size_vga": "MB",
            "score_vga": benchmark.score_vga(gcard.model),
        }

    @property
    def audio(self):
        audio_cards = []
        for node in self.lshw_xml.xpath('//node[@id="multimedia"]'):
            product = node.xpath('product/text()')[0]
            audio_cards.append({
                "model_audio": product,
            })

        return audio_cards

    @property
    def network(self):
        net_cards = []
        for net in self.lshw_xml.xpath('//node[@id="network"]'):
            product = net.xpath('product/text()')[0]
            try:
                speed = net.xpath('capacity/text()')[0]
                units = "bps"  # net.xpath('capacity/@units')[0]
            except IndexError as e:
                speed_net = None
            else:
                # FIXME convert speed to Mbps?
                speed = utils.convert_speed(speed, units, "Mbps")
                speed_net = "{0} {1}".format(speed, "Mbps")

            net_cards.append({
                "model_net": product,
                "speed_net": speed_net,
            })
        return net_cards

    @property
    def optical_drives(self):
        drives = []
        for node in self.lshw_xml.xpath('//node[@id="cdrom"]'):
            product = node.xpath('product/text()')[0]
            description = node.xpath('description/text()')[0]
            drives.append({
                "model_uni": product,
                "tipo_uni": description,  # TODO normalize values?
            })
        return drives

    @property
    def connectors(self):
        CONNECTORS = (
            ("USB", "usb"),
            ("FireWire", "firewire"),
            ("Serial Port", "serial"),
            ("PCMCIA", "pcmcia"),
        )

        def number_of_connectors(root, name):
            for i in range(10):
                if not root.xpath('//node[@id="{0}:{1}"]'.format(name, i)):
                    return i

        result = []
        for verbose, value in CONNECTORS:
            count = number_of_connectors(self.lshw_xml, value)
            if count > 0:
                result.append({
                    "tipus_connector": verbose,
                    "nombre_connector": count,
                })

        return {"connector": result}

    @property
    def brand_info(self):
        manufacturer = get_subsection_value(self.dmi, "System Information", "Manufacturer")
        product = get_subsection_value(self.dmi, "System Information", "Product Name")
        return {
            "fab_marca": manufacturer,
            "model_marca": product,
        }
<commit_msg>Refactor: encapsulate SoundCard.<commit_after>import dmidecode
import json
import multiprocessing
import os
import re
import subprocess
import uuid

from lxml import etree

from . import benchmark
from . import utils

def get_subsection_value(output, section_name, subsection_name):
    """Extract data from tabulated output like lshw and dmidecode."""
    section = output.find(section_name)
    ## end_section = output.find("*-", section)
    ## XXX WIP try to limit context to a section (how to detect end on dmidecode?)
    ## NOTE will be replaced with dmidecode Python2 library
    subsection = output.find(subsection_name, section)#, end_section)
    end = output.find("\n", subsection)
    return output[subsection:end].split(':')[1].strip()

class GraphicCard(object):
    def __init__(self, lshw):
        product = get_subsection_value(lshw, "display", "product")
        vendor = get_subsection_value(lshw, "display", "vendor")
        if product or vendor:
            self.model = "{vendor} {product}".format(vendor=vendor, product=product)
        else:
            self.model = get_subsection_value(lshw, "display", "description")

        # Find VGA memory
        bus_info = get_subsection_value(lshw, "display", "bus info").split("@")[1]
        mem = utils.run("lspci -v -s {bus} | grep 'prefetchable' | grep -v 'non-prefetchable' | egrep -o '[0-9]{{1,3}}[KMGT]+'".format(bus=bus_info)).splitlines()

        # Get max memory value
        max_size = 0
        for value in mem:
            unit = re.split('\d+', value)[1]
            size = int(value.rstrip(unit))

            # convert all values to KB before compare
            size_kb = utils.convert_base(size, unit, 'K', distance=1024)
            if size_kb > max_size:
                max_size = size_kb

        self.size = utils.convert_capacity(max_size, 'KB', 'MB')

class Processor(object):
    FREQ_UNIT = 'GHz'

    def __init__(self, lshw_json):
        self.number_cpus = multiprocessing.cpu_count()  # Python > 3.4 os.cpu_count()
        self.number_cores = os.popen("lscpu | grep 'Core(s) per socket'").read().split(':')[1].strip()

        cpu_data = lshw_json['children'][0]['children'][1]
        self.product = re.sub(r"\s+ ", " ", cpu_data['product'])
        self.vendor = cpu_data['vendor']  # was /proc/cpuinfo | grep vendor_id

        speed = dmidecode.processor()['0x0004']['data']['Current Speed']
        self.freq = utils.convert_frequency(speed, 'MHz', self.FREQ_UNIT)

class MemoryModule(object):
    CAPACITY_UNIT = 'MB'

    def __init__(self, lshw_json):
        ram_data = lshw_json['children'][0]['children'][0]
        dmidecode_out = utils.run("dmidecode -t 17")
        # dmidecode.QueryTypeId(7)

        # TODO optimize to only use a dmidecode call
        self.total_slots = int(utils.run("dmidecode -t 17 | grep -o BANK | wc -l"))
        self.used_slots = int(utils.run("dmidecode -t 17 | grep Size | grep MB | awk '{print $2}' | wc -l"))
        self.speed = get_subsection_value(dmidecode_out, "Memory Device", "Speed")
        self.interface = get_subsection_value(dmidecode_out, "Memory Device", "Type")

        # FIXME get total size but describe slot per slot
        size = 0
        for key, value in dmidecode.memory().iteritems():
            if value['data'].get('Size', None) is not None:
                size += int(value['data']['Size'].split()[0])

        self.size = size

class SoundCard(object):
    def __init__(self, product):
        self.product = product

class Computer(object):
    def __init__(self):
        # http://www.ezix.org/project/wiki/HardwareLiSter
        # JSON
        lshw_js = subprocess.check_output(["lshw", "-json"], universal_newlines=True)
        self.lshw_json = json.loads(lshw_js)

        # XML
        self.lshw_xml = etree.fromstring(subprocess.check_output(["lshw", "-xml"]))

        # Plain text (current)
        self.lshw = subprocess.check_output(["lshw"], universal_newlines=True)
        self.dmi = subprocess.check_output(["dmidecode"], universal_newlines=True)

        self.init_serials()

    def init_serials(self):
        # getnode attempts to obtain the hardware address, if fails it
        # chooses a random 48-bit number with its eight bit set to 1
        # Deprecated hw_addr as ID. TODO use device SN as ID
        self.ID = uuid.getnode()
        if (self.ID >> 40) % 2:
            raise OSError("The system does not seem to have a valid MAC.")

        ## Search manufacturer's serial number
        self.SERIAL1 = get_subsection_value(self.dmi, "System Information", "Serial Number")

        ## Search motherboard's serial number
        self.SERIAL2 = get_subsection_value(self.dmi, "Base Board Information", "Serial Number")

        ## Search CPU's serial number, if there are several we choose the first
        # A) dmidecode -t processor
        # FIXME Serial Number returns "To be filled by OEM"
        # http://forum.giga-byte.co.uk/index.php?topic=14167.0
        # self.SERIAL3 = get_subsection_value(self.dmi, "Processor Information", "ID")
        self.SERIAL3 = get_subsection_value(self.lshw, "*-cpu", "serial")

        # B) Try to call CPUID? https://en.wikipedia.org/wiki/CPUID
        # http://stackoverflow.com/a/4216034/1538221

        ## Search RAM's serial number, if there are several we choose the first
        dmi_memory = subprocess.check_output(["dmidecode", "-t" "memory"], universal_newlines=True)
        self.SERIAL4 = get_subsection_value(dmi_memory, "Memory Device", "Serial Number")

        ## Search hard disk's serial number, if there are several we choose the first
        # FIXME JSON loads fails because of a bug on lshw
        # https://bugs.launchpad.net/ubuntu/+source/lshw/+bug/1405873
        # lshw_disk = json.loads(subprocess.check_output(["lshw", "-json", "-class", "disk"]))
        self.SERIAL5 = get_subsection_value(self.lshw, "*-disk", "serial")

        # Deprecated: cksum CRC32 joining 5 serial numbers as secundary ID
        #ID2=`echo ${SERIAL1} ${SERIAL2} ${SERIAL3} ${SERIAL4} ${SERIAL5} | cksum | awk {'print $1'}`
        cmd = "echo {0} {1} {2} {3} {4} | cksum | awk {{'print $1'}}".format(
            self.SERIAL1, self.SERIAL2, self.SERIAL3, self.SERIAL4, self.SERIAL5)
        self.ID2 = os.popen(cmd).read().strip()

    @property
    def serials(self):
        return {
            "serial_fab": self.SERIAL1,
            "serial_mot": self.SERIAL2,
            "serial_cpu": self.SERIAL3,
            "serial_ram": self.SERIAL4,
            "serial_hdd": self.SERIAL5,
        }

    @property
    def cpu(self):
        processor = Processor(self.lshw_json)

        return {
            'nom_cpu': processor.product,
            'fab_cpu': processor.vendor,
            'speed_cpu': processor.freq,
            'unit_speed_cpu': processor.FREQ_UNIT,
            'number_cpu': processor.number_cpus,
            'number_cores': processor.number_cores,
            'score_cpu': benchmark.score_cpu(),
        }

    @property
    def ram(self):
        # TODO split computer.total_memory and MemoryModule(s) as components
        memory = MemoryModule(self.lshw_json)
        return {
            'size_ram': memory.size,
            'unit_size': memory.CAPACITY_UNIT,
            # EDO|SDRAM|DDR3|DDR2|DDR|RDRAM
            'interface_ram': memory.interface,
            'free_slots_ram': memory.total_slots - memory.used_slots,
            'used_slots_ram': memory.used_slots,
            'score_ram': benchmark.score_ram(memory.speed),
        }

    @property
    def hdd(self):
        # optimization? lshw -json -class disk
        # use dict lookup http://stackoverflow.com/a/27234926/1538221
        # NOTE only gets info of first HD
        logical_name = get_subsection_value(self.lshw, "*-disk", "logical name")
        interface = utils.run("udevadm info --query=all --name={0} | grep ID_BUS | cut -c 11-".format(logical_name))

        # TODO implement method for USB disk
        if interface == "usb":
            model = serial = size = "Unknown"

        else:
            # (S)ATA disk
            model = utils.run("hdparm -I {0} | grep 'Model\ Number' | cut -c 22-".format(logical_name))
            serial = utils.run("hdparm -I {0} | grep 'Serial\ Number' | cut -c 22-".format(logical_name))
            size = utils.run("hdparm -I {0} | grep 'device\ size\ with\ M' | head -n1 | awk '{{print $7}}'".format(logical_name))

        return {
            "model": model,
            "serial": serial,
            "size": size,
            "measure": "MB",
            "name": logical_name,
            "interface": interface,
        }

    @property
    def vga(self):
        gcard = GraphicCard(self.lshw)
        return {
            "model_vga": gcard.model,
            "size_vga": gcard.size,
            "unit_size_vga": "MB",
            "score_vga": benchmark.score_vga(gcard.model),
        }

    @property
    def audio(self):
        audio_cards = []
        for node in self.lshw_xml.xpath('//node[@id="multimedia"]'):
            product = node.xpath('product/text()')[0]
            audio_cards.append(SoundCard(product))

        return [{"model_audio": card.product} for card in audio_cards]

    @property
    def network(self):
        net_cards = []
        for net in self.lshw_xml.xpath('//node[@id="network"]'):
            product = net.xpath('product/text()')[0]
            try:
                speed = net.xpath('capacity/text()')[0]
                units = "bps"  # net.xpath('capacity/@units')[0]
            except IndexError as e:
                speed_net = None
            else:
                # FIXME convert speed to Mbps?
                speed = utils.convert_speed(speed, units, "Mbps")
                speed_net = "{0} {1}".format(speed, "Mbps")

            net_cards.append({
                "model_net": product,
                "speed_net": speed_net,
            })
        return net_cards

    @property
    def optical_drives(self):
        drives = []
        for node in self.lshw_xml.xpath('//node[@id="cdrom"]'):
            product = node.xpath('product/text()')[0]
            description = node.xpath('description/text()')[0]
            drives.append({
                "model_uni": product,
                "tipo_uni": description,  # TODO normalize values?
            })
        return drives

    @property
    def connectors(self):
        CONNECTORS = (
            ("USB", "usb"),
            ("FireWire", "firewire"),
            ("Serial Port", "serial"),
            ("PCMCIA", "pcmcia"),
        )

        def number_of_connectors(root, name):
            for i in range(10):
                if not root.xpath('//node[@id="{0}:{1}"]'.format(name, i)):
                    return i

        result = []
        for verbose, value in CONNECTORS:
            count = number_of_connectors(self.lshw_xml, value)
            if count > 0:
                result.append({
                    "tipus_connector": verbose,
                    "nombre_connector": count,
                })

        return {"connector": result}

    @property
    def brand_info(self):
        manufacturer = get_subsection_value(self.dmi, "System Information", "Manufacturer")
        product = get_subsection_value(self.dmi, "System Information", "Product Name")
        return {
            "fab_marca": manufacturer,
            "model_marca": product,
        }
<|endoftext|>


<commit_before>try:
    __version__ = get_distribution(__name__).version
except DistributionNotFound:
    # package is not installed
    pass
default_app_config = "teryt_tree.apps.TerytTreeConfig"
<commit_msg>Fix import in __init__.py<commit_after>from pkg_resources import get_distribution, DistributionNotFound

try:
    __version__ = get_distribution(__name__).version
except DistributionNotFound:
    # package is not installed
    pass
default_app_config = "teryt_tree.apps.TerytTreeConfig"
<|endoftext|>


<commit_before>import argparse
import codecs
import sys
from unidump import version, unidump
from unidump.env import env
from unicodedata import unidata_version

description = '''  A Unicode code point dump.

  Think of it as  hexdump(1)  for Unicode.  The command analyses  the input and
  then prints three columns: the raw byte index of the first code point in this
  row, code points in their hex notation,  and finally the raw input characters
  with control and whitespace replaced by a dot.

  Invalid byte sequences are represented with an “X” and with the hex value en-
  closed in question marks, e.g., “?F5?”.

  You can pipe in  data from stdin,  select several files at once,  or even mix
  all those input methods together.'''

epilog = '''Examples:

* Basic usage with stdin:

      echo -n 'ABCDEFGHIJKLMNOP' | unidump -n 4
            0    0041 0042 0043 0044    ABCD
            4    0045 0046 0047 0048    EFGH
            8    0049 004A 004B 004C    IJKL
           12    004D 004E 004F 0050    MNOP

* Dump the code points translated from another encoding:

      unidump -c latin-1 some-legacy-file

* Dump many files at the same time:

      unidump foo-*.txt

* Control characters and whitespace are safely rendered:

      echo -n -e '\\x01' | unidump -n 1
           0    0001    .

* Finally learn what your favorite Emoji is composed of:

      ( echo -n -e '\\xf0\\x9f\\xa7\\x9d\\xf0\\x9f\\x8f\\xbd\\xe2' ; \\
        echo -n -e '\\x80\\x8d\\xe2\\x99\\x82\\xef\\xb8\\x8f' ; ) | \\
      unidump -n 5
           0    1F9DD 1F3FD 200D 2642 FE0F    .🏽.♂

  See  <http://emojipedia.org/man-elf-medium-skin-tone/> for images.  The “elf”
  emoji (the first character) is replaced with a dot here,  because the current
  version of Python’s unicodedata doesn’t know of this character yet.

* Use it like strings(1):

      unidump -e '{data}' some-file.bin

  This will replace  every unknown byte from the input file  with “X” and every
  control and whitespace character with “.”.

* Only print the code points of the input:

      unidump -e '{repr}'$'\\n' -n 1 some-file.txt

  This results in a stream of code points in hex notation,  each on a new line,
  without byte counter  or rendering of actual data.  You can use this to count
  the total amount of characters  (as opposed to raw bytes)  in a file,  if you
  pipe it through `wc -l`.
'''+'''
This is version {} of unidump, using Unicode {} data.
'''.format(version, unidata_version)

def main(args=None):
    # force stdout to be UTF-8 encoded, disregarding locale
    sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())

    if args is None:
        args = sys.argv[1:]

    parser = argparse.ArgumentParser(
        prog='unidump',
        description=description,
        epilog=epilog,
        formatter_class=argparse.RawDescriptionHelpFormatter
    )

    parser.add_argument('files', nargs='*', metavar='FILE', default=('-',),
                        help='input files. Use `-\' or keep empty for stdin.')
    parser.add_argument('-n', '--length', type=int, default=16,
                        dest='linelength', metavar='LENGTH',
                        help='format output using this much input characters. '
                        'Default is %(default)s characters.')
    parser.add_argument('-c', '--encoding', type=str, default='utf-8',
                        metavar='ENC',
                        help='interpret input in this encoding. Default is '
                        '%(default)s. You can choose any encoding that Python '
                        'supports, e.g. “latin-1”.')
    parser.add_argument('-e', '--format', type=str, default=None,
                        dest='lineformat', metavar='FORMAT',
                        help=(
                            'specify a custom format in Python’s {} notation. '
                            'Default is “%s”. '
                            'See examples below on how to use this option.'
                        ) % env.lineformat.replace('\n', '\\n'))
    parser.add_argument('-v', '--version', action='version',
                        version='%(prog)s {} using Unicode {} data'.format(version, unidata_version))

    a = parser.parse_args(args)

    try:
        for filename in a.files:
            if filename == '-':
                infile = sys.stdin.buffer
            else:
                try:
                    infile = open(filename, 'rb')
                except FileNotFoundError:
                    sys.stdout.flush()
                    sys.stderr.write('File {} not found.\n'.format(filename))
                    continue
                except IsADirectoryError:
                    sys.stdout.flush()
                    sys.stderr.write('{} is a directory.\n'.format(filename))
                    continue
            unidump(infile, env=env(linelength=a.linelength, encoding=a.encoding, lineformat=a.lineformat))
    except KeyboardInterrupt:
        sys.stdout.flush()
        return 1
    else:
        return 0
<commit_msg>fix pep8 warnings<commit_after>import argparse
import codecs
import sys
from unidump import version, unidump
from unidump.env import env
from unicodedata import unidata_version

description = '''  A Unicode code point dump.

  Think of it as  hexdump(1)  for Unicode.  The command analyses  the input and
  then prints three columns: the raw byte index of the first code point in this
  row, code points in their hex notation,  and finally the raw input characters
  with control and whitespace replaced by a dot.

  Invalid byte sequences are represented with an “X” and with the hex value en-
  closed in question marks, e.g., “?F5?”.

  You can pipe in  data from stdin,  select several files at once,  or even mix
  all those input methods together.'''

epilog = '''Examples:

* Basic usage with stdin:

      echo -n 'ABCDEFGHIJKLMNOP' | unidump -n 4
            0    0041 0042 0043 0044    ABCD
            4    0045 0046 0047 0048    EFGH
            8    0049 004A 004B 004C    IJKL
           12    004D 004E 004F 0050    MNOP

* Dump the code points translated from another encoding:

      unidump -c latin-1 some-legacy-file

* Dump many files at the same time:

      unidump foo-*.txt

* Control characters and whitespace are safely rendered:

      echo -n -e '\\x01' | unidump -n 1
           0    0001    .

* Finally learn what your favorite Emoji is composed of:

      ( echo -n -e '\\xf0\\x9f\\xa7\\x9d\\xf0\\x9f\\x8f\\xbd\\xe2' ; \\
        echo -n -e '\\x80\\x8d\\xe2\\x99\\x82\\xef\\xb8\\x8f' ; ) | \\
      unidump -n 5
           0    1F9DD 1F3FD 200D 2642 FE0F    .🏽.♂

  See  <http://emojipedia.org/man-elf-medium-skin-tone/> for images.  The “elf”
  emoji (the first character) is replaced with a dot here,  because the current
  version of Python’s unicodedata doesn’t know of this character yet.

* Use it like strings(1):

      unidump -e '{data}' some-file.bin

  This will replace  every unknown byte from the input file  with “X” and every
  control and whitespace character with “.”.

* Only print the code points of the input:

      unidump -e '{repr}'$'\\n' -n 1 some-file.txt

  This results in a stream of code points in hex notation,  each on a new line,
  without byte counter  or rendering of actual data.  You can use this to count
  the total amount of characters  (as opposed to raw bytes)  in a file,  if you
  pipe it through `wc -l`.
'''+'''
This is version {} of unidump, using Unicode {} data.
'''.format(version, unidata_version)

def main(args=None):
    # force stdout to be UTF-8 encoded, disregarding locale
    sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())

    if args is None:
        args = sys.argv[1:]

    parser = argparse.ArgumentParser(
        prog='unidump',
        description=description,
        epilog=epilog,
        formatter_class=argparse.RawDescriptionHelpFormatter
    )

    parser.add_argument('files', nargs='*', metavar='FILE', default=('-',),
                        help='input files. Use `-\' or keep empty for stdin.')
    parser.add_argument('-n', '--length', type=int, default=16,
                        dest='linelength', metavar='LENGTH',
                        help='format output using this much input characters. '
                        'Default is %(default)s characters.')
    parser.add_argument('-c', '--encoding', type=str, default='utf-8',
                        metavar='ENC',
                        help='interpret input in this encoding. Default is '
                        '%(default)s. You can choose any encoding that Python '
                        'supports, e.g. “latin-1”.')
    parser.add_argument('-e', '--format', type=str, default=None,
                        dest='lineformat', metavar='FORMAT',
                        help=(
                            'specify a custom format in Python’s {} notation. '
                            'Default is “%s”. '
                            'See examples below on how to use this option.'
                        ) % env.lineformat.replace('\n', '\\n'))
    parser.add_argument('-v', '--version', action='version',
                        version='%(prog)s {} using Unicode {} data'.format(
                            version, unidata_version))

    a = parser.parse_args(args)

    try:
        for filename in a.files:
            if filename == '-':
                infile = sys.stdin.buffer
            else:
                try:
                    infile = open(filename, 'rb')
                except FileNotFoundError:
                    sys.stdout.flush()
                    sys.stderr.write('File {} not found.\n'.format(filename))
                    continue
                except IsADirectoryError:
                    sys.stdout.flush()
                    sys.stderr.write('{} is a directory.\n'.format(filename))
                    continue
            unidump(infile, env=env(linelength=a.linelength,
                    encoding=a.encoding, lineformat=a.lineformat))
    except KeyboardInterrupt:
        sys.stdout.flush()
        return 1
    else:
        return 0
<|endoftext|>


<commit_before>from django import forms
import fields

def _create_seachform(cls):
    class SearchForm(forms.Form):
        query = forms.CharField(required=False)
        limit = fields.PositiveIntegerWithFallbackField(fallbackvalue=50)
        start = fields.PositiveIntegerWithFallbackField()
        orderby = fields.CharListWithFallbackField(
                fallbackvalue=cls._meta.simplified._meta.ordering)
        result_fieldgroups = fields.CharListWithFallbackField()
        search_fieldgroups = fields.CharListWithFallbackField()
    cls.SearchForm = SearchForm
<commit_msg>restful: Docs<commit_after>from django import forms
import fields

def _create_seachform(cls):
    """
    Create the form used on search(). Needs to be created for each
    restful class since orderby needs a fallbackvalue.
    """
    class SearchForm(forms.Form):
        query = forms.CharField(required=False)
        limit = fields.PositiveIntegerWithFallbackField(fallbackvalue=50)
        start = fields.PositiveIntegerWithFallbackField()
        orderby = fields.CharListWithFallbackField(
                fallbackvalue=cls._meta.simplified._meta.ordering)
        result_fieldgroups = fields.CharListWithFallbackField()
        search_fieldgroups = fields.CharListWithFallbackField()
    cls.SearchForm = SearchForm
<|endoftext|>


<commit_before># -*- mode:python -*-

import logging
import os

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

log = logging.getLogger(__name__)

app = Flask(__name__)

# Load internal defaults (inside application package)
app.config.from_pyfile('config.py')
# Allow overriding from an external file, pathwise (near project, not app,
# root - is typically one level above app root.)
local = os.path.join(os.getcwd(), 'etc', 'config.py')
try:
    app.config.from_pyfile(local)
except IOError as e:
    # It's fine to not have this file, just log for debugging.
    log.debug("Local override config {0!r} not found, skipping".format(local))
# Final wholly configurable config file location
# Only load if set, and then let it explode on its own if target not found.
if os.environ.get('TESSERA_CONFIG', False):
    app.config.from_envvar('TESSERA_CONFIG')

db = SQLAlchemy(app)

from views import *
<commit_msg>Comment tweak<commit_after># -*- mode:python -*-

import logging
import os

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

log = logging.getLogger(__name__)

app = Flask(__name__)

# Load internal defaults (inside application package)
app.config.from_pyfile('config.py')
# Allow overriding from an external file, pathwise (near project, not app,
# root - is typically one level above app root.)
local = os.path.join(os.getcwd(), 'etc', 'config.py')
try:
    app.config.from_pyfile(local)
except IOError as e:
    # It's fine to not have this file, just log for debugging.
    log.debug("Local override config {0!r} not found, skipping".format(local))
# Wholly configurable config file location via env var.
# Only load if set, and then let it explode on its own if target not found.
if os.environ.get('TESSERA_CONFIG', False):
    app.config.from_envvar('TESSERA_CONFIG')

db = SQLAlchemy(app)

from views import *
<|endoftext|>


<commit_before>#!/usr/bin/env python
import argparse
import sys

import logging
log = logging.getLogger()

from display import World
from display.atmosphere import Sky, Stars, Ground, Rain, CloudCover
from display.atmosphere import ExpandingSplotches

import signal

def world_callback(world):
    return False

PIXELS = 90

if __name__ == "__main__":
    log.setLevel(logging.INFO)
    logging.basicConfig(format="%(thread)d:%(asctime)s:%(levelname)s:%(module)s :: %(message)s")

    parser = argparse.ArgumentParser()
    parser.add_argument('--virtual', action="store_true", dest="virtual", default=False,
                        help='Use virtual display')
    parser.add_argument('--led', action="store_true", dest="led", default=False,
                        help='Use led display')
    parser.add_argument('--text-color', action="store_true", dest="text_color", default=False,
                        help='Use ansi color text display')
    parser.add_argument('--text', action="store_true", dest="text", default=False,
                        help='Use text display')

    parser.add_argument('--fps', action="store_true", dest="fps", default=False,
                        help='Show FPS')
    parser.add_argument('--profiler', action="store_true", dest="profiler", default=False,
                        help='Run timing profiler')

    parser.add_argument('scene', default="grass,clouds", nargs='?',
                        help='Which scenes to composite.  Choices include: sky, grass, dirt, night, rain, clouds, stars')

    args = parser.parse_args(sys.argv[1:])

    scene = World(PIXELS, print_fps=args.fps)

    if args.virtual:
        from display.renderers.virtual import PyGameRenderer
        scene.add_renderer( PyGameRenderer() )
    if args.led:
        from display.renderers.led import NeoPixelRenderer
        scene.add_renderer( NeoPixelRenderer() )
    if args.text_color:
        from display.renderers.text import ConsoleColorRenderer
        scene.add_renderer( ConsoleColorRenderer(clear_on_render=True) )
    if args.text:
        from display.renderers.text import ConsoleRenderer
        scene.add_renderer( ConsoleRenderer(clear_on_render=True) )

    if "sky" in args.scene:
        scene.add_sprite( Sky(clouds=2, world_size=PIXELS) )

    if "grass" in args.scene:
        scene.add_sprite( Ground(ground_color=Ground.MEADOW_COLOR, brightness_variance=0.10) )

    if "dirt" in args.scene:
        scene.add_sprite( Ground(ground_color=Ground.DIRT_COLOR, brightness_variance=0.05) )

    if "night" in args.scene:
        scene.add_sprite( Ground(ground_color=Ground.NIGHT_COLOR, brightness_variance=0.01) )

    if "rain" in args.scene:
        scene.add_sprite( Rain(max_drops=10, drop_rate=.1, world_size=PIXELS) )

    if "clouds" in args.scene:
        scene.add_sprite( CloudCover(clouds=2, world_size=PIXELS, cloud_min_radius=2, cloud_max_radius=int(PIXELS*0.2)) )

    if "stars" in args.scene:
        scene.add_sprite( Stars(stars=5, world_size=PIXELS) )

    if "expand" in args.scene:
        scene.add_sprite( ExpandingSplotches() )

    profile = None
    if args.profiler:
        print "starting profiler"
        import cProfile as profile

        profile = profile.Profile(timeunit=100)
        profile.enable()

    def signal_handler(signal, frame):
        print('\nStopping world run loop\n')
        scene.stop()

    signal.signal(signal.SIGINT, signal_handler)

    scene.run( world_callback )

    if profile:
        import pstats
        profile.disable()

        sortby = 'cumulative'
        ps = pstats.Stats(profile).sort_stats(sortby)

        ps.print_stats(0.25)
        ps.dump_stats("./profile.pf")
<commit_msg>remove any variance in color for night atmosphere<commit_after>#!/usr/bin/env python
import argparse
import sys

import logging
log = logging.getLogger()

from display import World
from display.atmosphere import Sky, Stars, Ground, Rain, CloudCover
from display.atmosphere import ExpandingSplotches

import signal

def world_callback(world):
    return False

PIXELS = 90

if __name__ == "__main__":
    log.setLevel(logging.INFO)
    logging.basicConfig(format="%(thread)d:%(asctime)s:%(levelname)s:%(module)s :: %(message)s")

    parser = argparse.ArgumentParser()
    parser.add_argument('--virtual', action="store_true", dest="virtual", default=False,
                        help='Use virtual display')
    parser.add_argument('--led', action="store_true", dest="led", default=False,
                        help='Use led display')
    parser.add_argument('--text-color', action="store_true", dest="text_color", default=False,
                        help='Use ansi color text display')
    parser.add_argument('--text', action="store_true", dest="text", default=False,
                        help='Use text display')

    parser.add_argument('--fps', action="store_true", dest="fps", default=False,
                        help='Show FPS')
    parser.add_argument('--profiler', action="store_true", dest="profiler", default=False,
                        help='Run timing profiler')

    parser.add_argument('scene', default="grass,clouds", nargs='?',
                        help='Which scenes to composite.  Choices include: sky, grass, dirt, night, rain, clouds, stars')

    args = parser.parse_args(sys.argv[1:])

    scene = World(PIXELS, print_fps=args.fps)

    if args.virtual:
        from display.renderers.virtual import PyGameRenderer
        scene.add_renderer( PyGameRenderer() )
    if args.led:
        from display.renderers.led import NeoPixelRenderer
        scene.add_renderer( NeoPixelRenderer() )
    if args.text_color:
        from display.renderers.text import ConsoleColorRenderer
        scene.add_renderer( ConsoleColorRenderer(clear_on_render=True) )
    if args.text:
        from display.renderers.text import ConsoleRenderer
        scene.add_renderer( ConsoleRenderer(clear_on_render=True) )

    if "sky" in args.scene:
        scene.add_sprite( Sky(clouds=2, world_size=PIXELS) )

    if "grass" in args.scene:
        scene.add_sprite( Ground(ground_color=Ground.MEADOW_COLOR, brightness_variance=0.10) )

    if "dirt" in args.scene:
        scene.add_sprite( Ground(ground_color=Ground.DIRT_COLOR, brightness_variance=0.05) )

    if "night" in args.scene:
        scene.add_sprite( Ground(ground_color=Ground.NIGHT_COLOR, brightness_variance=0.0) )

    if "rain" in args.scene:
        scene.add_sprite( Rain(max_drops=10, drop_rate=.1, world_size=PIXELS) )

    if "clouds" in args.scene:
        scene.add_sprite( CloudCover(clouds=2, world_size=PIXELS, cloud_min_radius=2, cloud_max_radius=int(PIXELS*0.2)) )

    if "stars" in args.scene:
        scene.add_sprite( Stars(stars=5, world_size=PIXELS) )

    if "expand" in args.scene:
        scene.add_sprite( ExpandingSplotches() )

    profile = None
    if args.profiler:
        print "starting profiler"
        import cProfile as profile

        profile = profile.Profile(timeunit=100)
        profile.enable()

    def signal_handler(signal, frame):
        print('\nStopping world run loop\n')
        scene.stop()

    signal.signal(signal.SIGINT, signal_handler)

    scene.run( world_callback )

    if profile:
        import pstats
        profile.disable()

        sortby = 'cumulative'
        ps = pstats.Stats(profile).sort_stats(sortby)

        ps.print_stats(0.25)
        ps.dump_stats("./profile.pf")
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
import logging
from testfixtures import LogCapture
from SpiffyWorld import Battle, Battlemaster, ItemGenerator, \
    UnitGenerator, Dungeon, DungeonAnnouncer
import supybot.ircmsgs as ircmsgs
import supybot.ircutils as ircutils

class TestBattle(unittest.TestCase):
    """
    Tests battle interactions between units
    """
    def _make_item(self, **kwargs):
        item_type = kwargs["item_type"]

        item_generator = ItemGenerator()

        return item_generator.generate(item_type=item_type)

    def _make_unit(self, **kwargs):
        level = kwargs["level"]
        unit_generator = UnitGenerator()

        return unit_generator.generate(level=level)

    def _make_dungeon(self):
        dungeon_model = {
            "id": 1,
            "name": "TestDungeon",
            "min_level": 1,
            "max_level": 100,
            "description": "foo",
            "channel": "#SpiffyRPG",
            "units": []
        }

        dungeon_announcer = DungeonAnnouncer(ircmsgs=ircmsgs,
                                             ircutils=ircutils,
                                             testing=True,
                                             destination=dungeon_model["channel"],
                                             irc="quux")

        with LogCapture():
            logger = logging.getLogger()
            dungeon = Dungeon(announcer=dungeon_announcer,
                              dungeon=dungeon_model,
                              log=logger)

        return dungeon

    def test_start_round(self):
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=13)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Lizard poisons Spock
        attacker_weapon = self._make_item(item_type="lizard")
        target_weapon = self._make_item(item_type="spock")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)
        dungeon = self._make_dungeon()

        battle.start_round(battle=battle,
                           irc="quux",
                           ircmsgs=ircmsgs,
                           dungeon=dungeon,
                           ircutils=ircutils)

        self.assertFalse(unit_omega.has_full_hp())
        self.assertTrue(unit_charlie.has_full_hp())
        self.assertTrue(len(battle.rounds), 2)

    def test_target_retaliation(self):
        """
        Tests the the following use case:
        1. Attacker strikes
        2. Target is equipped with a counter item type
        3. Attacker should miss and then take damage from
           the target unit's attack
        """
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=13)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Target's Scissors cut Attacker's Paper
        attacker_weapon = self._make_item(item_type="paper")
        target_weapon = self._make_item(item_type="scissors")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)

        dungeon = self._make_dungeon()

        battle.start_round(battle=battle,
                           irc="quux",
                           ircmsgs="foo",
                           dungeon=dungeon,
                           ircutils="quux")

        """
        Since Unit Omega had scissors equipped they should
        have dealt damage to Unit Charlie
        """
        self.assertFalse(unit_charlie.has_full_hp())
        self.assertTrue(unit_omega.has_full_hp())
        self.assertTrue(len(battle.rounds), 2)

    def test_not_your_turn(self):
        """
        Unit should not be able to attack twice in a row
        """
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=99)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Target's Scissors cut Attacker's Paper
        attacker_weapon = self._make_item(item_type="lizard")
        target_weapon = self._make_item(item_type="spock")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)

        dungeon = self._make_dungeon()

        battle.start_round(battle=battle,
                           irc="quux",
                           ircmsgs="foo",
                           dungeon=dungeon,
                           ircutils="quux")

        """
        unit_charle's Lizard poisons unit_omega's Spock
        """
        self.assertFalse(unit_omega.has_full_hp())
        self.assertTrue(unit_charlie.has_full_hp())
        self.assertTrue(len(battle.rounds), 2)

        """
        Try to attack again (should fail)
        """
        battle.start_round(battle=battle,
                           irc="quux",
                           ircmsgs="foo",
                           dungeon=dungeon,
                           ircutils="quux")

        # No round should have been added
        self.assertTrue(len(battle.rounds), 2)

    def test_can_add_round(self):
        """
        Test that:
        1. We have not exceeded the total rounds for this battle
        2. All combatants are alive
        3. No combatant can attack the same target twice in a row
        """
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger, total_rounds=1)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=99)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Target's Scissors cut Attacker's Paper
        attacker_weapon = self._make_item(item_type="lizard")
        target_weapon = self._make_item(item_type="spock")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)

        """
        Sunny day scenario
        """
        can_add_round = battle.can_add_round(attacker=unit_charlie,
                                             target=unit_omega)

        self.assertTrue(can_add_round)

        """
        Test that we cannot exceed total rounds
        """
        dungeon = self._make_dungeon()
        battle.start_round(battle=battle,
                           ircutils="quux",
                           ircmsgs="quux",
                           irc="quux",
                           dungeon=dungeon)

        self.assertEqual(len(battle.rounds), 1)

        # Swap combatant order so it is someone else's turn
        battle.combatants = list(reversed(battle.combatants))

        cannot_exceed_rounds_error = battle.can_add_round(attacker=unit_charlie,
                                                          target=unit_omega)

        self.assertEqual(cannot_exceed_rounds_error, "Cannot add round: maximum rounds reached.")

if __name__ == '__main__':
    unittest.main()
<commit_msg>Adds assertion for exact error message in the case of Not Your Turn<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
import logging
from testfixtures import LogCapture
from SpiffyWorld import Battle, Battlemaster, ItemGenerator, \
    UnitGenerator, Dungeon, DungeonAnnouncer
import supybot.ircmsgs as ircmsgs
import supybot.ircutils as ircutils

class TestBattle(unittest.TestCase):
    """
    Tests battle interactions between units
    """
    def _make_item(self, **kwargs):
        item_type = kwargs["item_type"]

        item_generator = ItemGenerator()

        return item_generator.generate(item_type=item_type)

    def _make_unit(self, **kwargs):
        level = kwargs["level"]
        unit_generator = UnitGenerator()

        return unit_generator.generate(level=level)

    def _make_dungeon(self):
        dungeon_model = {
            "id": 1,
            "name": "TestDungeon",
            "min_level": 1,
            "max_level": 100,
            "description": "foo",
            "channel": "#SpiffyRPG",
            "units": []
        }

        dungeon_announcer = DungeonAnnouncer(ircmsgs=ircmsgs,
                                             ircutils=ircutils,
                                             testing=True,
                                             destination=dungeon_model["channel"],
                                             irc="quux")

        with LogCapture():
            logger = logging.getLogger()
            dungeon = Dungeon(announcer=dungeon_announcer,
                              dungeon=dungeon_model,
                              log=logger)

        return dungeon

    def test_start_round(self):
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=13)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Lizard poisons Spock
        attacker_weapon = self._make_item(item_type="lizard")
        target_weapon = self._make_item(item_type="spock")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)
        dungeon = self._make_dungeon()

        battle.start_round(battle=battle,
                           irc="quux",
                           ircmsgs=ircmsgs,
                           dungeon=dungeon,
                           ircutils=ircutils)

        self.assertFalse(unit_omega.has_full_hp())
        self.assertTrue(unit_charlie.has_full_hp())
        self.assertTrue(len(battle.rounds), 2)

    def test_target_retaliation(self):
        """
        Tests the the following use case:
        1. Attacker strikes
        2. Target is equipped with a counter item type
        3. Attacker should miss and then take damage from
           the target unit's attack
        """
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=13)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Target's Scissors cut Attacker's Paper
        attacker_weapon = self._make_item(item_type="paper")
        target_weapon = self._make_item(item_type="scissors")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)

        dungeon = self._make_dungeon()

        battle.start_round(battle=battle,
                           irc="quux",
                           ircmsgs="foo",
                           dungeon=dungeon,
                           ircutils="quux")

        """
        Since Unit Omega had scissors equipped they should
        have dealt damage to Unit Charlie
        """
        self.assertFalse(unit_charlie.has_full_hp())
        self.assertTrue(unit_omega.has_full_hp())
        self.assertTrue(len(battle.rounds), 2)

    def test_not_your_turn(self):
        """
        Unit should not be able to attack twice in a row
        """
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=99)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Target's Scissors cut Attacker's Paper
        attacker_weapon = self._make_item(item_type="lizard")
        target_weapon = self._make_item(item_type="spock")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)

        dungeon = self._make_dungeon()

        battle.start_round(battle=battle,
                           irc="quux",
                           ircmsgs="foo",
                           dungeon=dungeon,
                           ircutils="quux")

        """
        unit_charle's Lizard poisons unit_omega's Spock
        """
        self.assertFalse(unit_omega.has_full_hp())
        self.assertTrue(unit_charlie.has_full_hp())
        self.assertTrue(len(battle.rounds), 2)

        """
        Try to attack again (should fail)
        """
        cant_start_reason = battle.start_round(battle=battle,
                                               irc="quux",
                                               ircmsgs="foo",
                                               dungeon=dungeon,
                                               ircutils="quux")

        self.assertEqual(cant_start_reason, "Cannot add round: not your turn.")
        self.assertTrue(len(battle.rounds), 2)

    def test_can_add_round(self):
        """
        Test that:
        1. We have not exceeded the total rounds for this battle
        2. All combatants are alive
        3. No combatant can attack the same target twice in a row
        """
        with LogCapture():
            logger = logging.getLogger()
            battle = Battle(log=logger, total_rounds=1)

        battlemaster = Battlemaster()

        unit_charlie = self._make_unit(is_player=True, level=99)
        unit_omega = self._make_unit(is_player=True, level=13)

        battle.add_combatant(combatant=unit_charlie)
        battle.add_combatant(combatant=unit_omega)

        # Make sure battle was added
        battlemaster.add_battle(battle=battle)

        # Target's Scissors cut Attacker's Paper
        attacker_weapon = self._make_item(item_type="lizard")
        target_weapon = self._make_item(item_type="spock")

        unit_charlie.equip_item(item=attacker_weapon)
        unit_omega.equip_item(item=target_weapon)

        """
        Sunny day scenario
        """
        can_add_round = battle.can_add_round(attacker=unit_charlie,
                                             target=unit_omega)

        self.assertTrue(can_add_round)

        """
        Test that we cannot exceed total rounds
        """
        dungeon = self._make_dungeon()
        battle.start_round(battle=battle,
                           ircutils="quux",
                           ircmsgs="quux",
                           irc="quux",
                           dungeon=dungeon)

        self.assertEqual(len(battle.rounds), 1)

        # Swap combatant order so it is someone else's turn
        battle.combatants = list(reversed(battle.combatants))

        cannot_exceed_rounds_error = battle.can_add_round(attacker=unit_charlie,
                                                          target=unit_omega)

        self.assertEqual(cannot_exceed_rounds_error, "Cannot add round: maximum rounds reached.")

if __name__ == '__main__':
    unittest.main()
<|endoftext|>


<commit_before>from data_collection.management.commands import BaseXpressDemocracyClubCsvImporter

class Command(BaseXpressDemocracyClubCsvImporter):
    council_id = "E07000220"
    addresses_name = "local.2018-05-03/Version 1/Democracy_Club__03May2018 Rugby.tsv"
    stations_name = "local.2018-05-03/Version 1/Democracy_Club__03May2018 Rugby.tsv"
    elections = ["local.2018-05-03"]
    csv_delimiter = "\t"

    def address_record_to_dict(self, record):
        uprn = record.property_urn.strip().lstrip("0")

        if uprn == "10010523748":
            rec = super().address_record_to_dict(record)
            rec["postcode"] = "CV22 7EX"
            return rec

        return super().address_record_to_dict(record)
<commit_msg>import Rugby (closes #1671)<commit_after>from data_collection.management.commands import BaseXpressDemocracyClubCsvImporter

class Command(BaseXpressDemocracyClubCsvImporter):
    council_id = "E07000220"
    addresses_name = "local.2019-05-02/Version 1/Democracy_Club__02May2019ruggers.tsv"
    stations_name = "local.2019-05-02/Version 1/Democracy_Club__02May2019ruggers.tsv"
    elections = ["local.2019-05-02"]
    csv_delimiter = "\t"

    def address_record_to_dict(self, record):
        rec = super().address_record_to_dict(record)
        uprn = record.property_urn.strip().lstrip("0")

        if uprn == "10010524278":
            rec["postcode"] = "CV7 9LX"

        if uprn in [
            "100070189342"  # CV227JT -> CV227JS : 83 Lawford Lane, Bilton, Rugby
        ]:
            rec["accept_suggestion"] = False

        return rec
<|endoftext|>


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

#   Copyright 2010-2014 Tuukka Turto
#
#   This file is part of pyherc.
#
#   pyherc is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   pyherc is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with pyherc.  If not, see <http://www.gnu.org/licenses/>.

"""
Module for magic related tests
"""
#pylint: disable=W0614
from pyherc.test.builders import CharacterBuilder, HealBuilder, DamageBuilder
from pyherc.test.builders import ActionFactoryBuilder
from pyherc.test.builders import SpellCastingFactoryBuilder
from pyherc.rules import cast

from hamcrest import assert_that, is_, equal_to #pylint: disable-msg=E0611
from mockito import mock, when, any, verify

class TestMagic:
    """
    Tests for magic
    """

    def __init__(self):
        """
        Default constructor
        """
        pass

    def test_damage_effect(self):
        """
        Test that a damage effect can be applied on a character
        """
        character = (CharacterBuilder()
                        .with_hit_points(15)
                        .with_max_hp(15)
                        .build())

        effect = (DamageBuilder()
                    .with_duration(0)
                    .with_frequency(0)
                    .with_tick(0)
                    .with_damage(10)
                    .with_target(character)
                    .build())

        effect.trigger(mock())

        assert_that(character.hit_points, is_(equal_to(5)))

    def test_healing_effect(self):
        """
        Test that a healing effect can be applied on a character
        """
        character = (CharacterBuilder()
                        .with_hit_points(1)
                        .with_max_hp(15)
                        .build())

        effect = (HealBuilder()
                    .with_duration(0)
                    .with_frequency(0)
                    .with_tick(0)
                    .with_healing(10)
                    .with_target(character)
                    .build())

        effect.trigger(mock())

        assert_that(character.hit_points, is_(equal_to(11)))

    def test_healing_does_not_heal_over_max_hp(self):
        """
        Test that character does not get healed over his maximum hp when getting healing effect
        """
        character = (CharacterBuilder()
                        .with_hit_points(1)
                        .with_max_hp(5)
                        .build())

        effect = (HealBuilder()
                    .with_duration(0)
                    .with_frequency(0)
                    .with_tick(0)
                    .with_healing(10)
                    .with_target(character)
                    .build())

        effect.trigger(mock())

        assert_that(character.hit_points, is_(equal_to(5)))

class TestSpellCasting:
    """
    Test spell casting
    """
    def __init__(self):
        """
        Default constructor
        """
        pass

    def test_spell_casting_executes_action(self):
        """
        Casting a spell should activate the action
        """
        magic_factory = SpellCastingFactoryBuilder().build()
        action = mock()
        when(action).is_legal().thenReturn(True)

        when(magic_factory).get_action(any()).thenReturn(action) #pylint: disable-msg=E1103

        action_factory = (ActionFactoryBuilder()
                                    .with_spellcasting_factory(magic_factory)
                                    .build())

        caster = (CharacterBuilder()
                        .build())

        cast(caster,
             direction = 1,
             spell_name = 'healing wind',
             action_factory = action_factory)

        verify(action).execute()

class TestDomains():
    """
    Tests for gaining domains
    """
    def __init__(self):
        """
        Default constructor
        """
        super().__init__()

    def test_add_spell_level(self):
        """
        Test that a spell level can be added
        """
        caster = (CharacterBuilder()
                        .with_domain('fire', 1)
                        .build())

        caster.add_spell_level('fire')

        assert_that(caster.get_spell_level('fire'), is_(equal_to(2)))
<commit_msg>working on gaining domain levels relates #49<commit_after># -*- coding: utf-8 -*-

#   Copyright 2010-2014 Tuukka Turto
#
#   This file is part of pyherc.
#
#   pyherc is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   pyherc is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with pyherc.  If not, see <http://www.gnu.org/licenses/>.

"""
Module for magic related tests
"""
#pylint: disable=W0614
from pyherc.test.builders import CharacterBuilder, HealBuilder, DamageBuilder
from pyherc.test.builders import ActionFactoryBuilder
from pyherc.test.builders import SpellCastingFactoryBuilder
from pyherc.rules import cast

from hamcrest import assert_that, is_, equal_to #pylint: disable-msg=E0611
from mockito import mock, when, any, verify

class TestMagic:
    """
    Tests for magic
    """

    def __init__(self):
        """
        Default constructor
        """
        pass

    def test_damage_effect(self):
        """
        Test that a damage effect can be applied on a character
        """
        character = (CharacterBuilder()
                        .with_hit_points(15)
                        .with_max_hp(15)
                        .build())

        effect = (DamageBuilder()
                    .with_duration(0)
                    .with_frequency(0)
                    .with_tick(0)
                    .with_damage(10)
                    .with_target(character)
                    .build())

        effect.trigger(mock())

        assert_that(character.hit_points, is_(equal_to(5)))

    def test_healing_effect(self):
        """
        Test that a healing effect can be applied on a character
        """
        character = (CharacterBuilder()
                        .with_hit_points(1)
                        .with_max_hp(15)
                        .build())

        effect = (HealBuilder()
                    .with_duration(0)
                    .with_frequency(0)
                    .with_tick(0)
                    .with_healing(10)
                    .with_target(character)
                    .build())

        effect.trigger(mock())

        assert_that(character.hit_points, is_(equal_to(11)))

    def test_healing_does_not_heal_over_max_hp(self):
        """
        Test that character does not get healed over his maximum hp when getting healing effect
        """
        character = (CharacterBuilder()
                        .with_hit_points(1)
                        .with_max_hp(5)
                        .build())

        effect = (HealBuilder()
                    .with_duration(0)
                    .with_frequency(0)
                    .with_tick(0)
                    .with_healing(10)
                    .with_target(character)
                    .build())

        effect.trigger(mock())

        assert_that(character.hit_points, is_(equal_to(5)))

class TestSpellCasting:
    """
    Test spell casting
    """
    def __init__(self):
        """
        Default constructor
        """
        pass

    def test_spell_casting_executes_action(self):
        """
        Casting a spell should activate the action
        """
        magic_factory = SpellCastingFactoryBuilder().build()
        action = mock()
        when(action).is_legal().thenReturn(True)

        when(magic_factory).get_action(any()).thenReturn(action) #pylint: disable-msg=E1103

        action_factory = (ActionFactoryBuilder()
                                    .with_spellcasting_factory(magic_factory)
                                    .build())

        caster = (CharacterBuilder()
                        .build())

        cast(caster,
             direction = 1,
             spell_name = 'healing wind',
             action_factory = action_factory)

        verify(action).execute()

class TestDomains():
    """
    Tests for gaining domains
    """
    def __init__(self):
        """
        Default constructor
        """
        super().__init__()

    def test_add_spell_level(self):
        """
        Test that a spell level can be added
        """
        caster = (CharacterBuilder()
                        .with_domain('fire', 1)
                        .build())

        caster.add_spell_level('fire')

        assert_that(caster.get_spell_level('fire'), is_(equal_to(2)))

    def test_add_multiple_levels(self):
        """
        Adding more than one level should be possible with a single call
        """
        caster = (CharacterBuilder()
                        .with_domain('fire', 1)
                        .build())

        caster.add_spell_level('fire', 5)

        assert_that(caster.get_spell_level('fire'), is_(equal_to(6)))
<|endoftext|>


<commit_before>from datetime import datetime
import os
from stat import S_ISLNK, S_ISDIR, S_ISREG, S_IFDIR, S_IRWXU, S_IRWXG, S_IRWXO
import time

from dateutil.tz import tzlocal
from dulwich.objects import S_ISGITLINK, Blob, Commit, Tree
import logbook
from stuf.collects import ChainMap

log = logbook.Logger('git')

def export_tree(lookup, tree, path):
    """Exports the given tree object to path.

    :param lookup: Function to retrieve objects for SHA1 hashes.
    :param tree: Tree to export.
    :param path: Output path.
    """
    FILE_PERM = S_IRWXU | S_IRWXG | S_IRWXO

    for name, mode, hexsha in tree.iteritems():
        dest = os.path.join(path, name)

        if S_ISGITLINK(mode):
            log.error('Ignored submodule {}; submodules are not yet supported.'
                      .format(name))
            # raise ValueError('Does not support submodules')
        elif S_ISDIR(mode):
            os.mkdir(dest)
            os.chmod(dest, 0o0755)
            export_tree(lookup, lookup(hexsha), dest)
        elif S_ISLNK(mode):
            os.symlink(lookup(hexsha).data, dest)
        elif S_ISREG(mode):
            with open(dest, 'wb') as out:
                for chunk in lookup(hexsha).chunked:
                    out.write(chunk)
            os.chmod(dest, mode & FILE_PERM)
        else:
            raise ValueError('Cannot deal with mode of {:o} from {}'.format
                             (mode, name))

def get_local_timezone(now=None):
    if now is None:
        now = int(time.time())

    # calculate local timezone. must be done each time, due to DST
    _tz_offset = tzlocal().utcoffset(datetime.utcfromtimestamp(now))

    return _tz_offset.days * 24 * 60 * 60 + _tz_offset.seconds

def one_or_many(f):
    def _(self, *args, **kwargs):
        if len(self.candidates) == 1:
            return f(self, self.candidates[0], *args, **kwargs)
        if len(self.candidates) > 1:
            return [f(self, c) for c in self.candidates]
    return _

class ResolvedRef(object):
    """Resolves a commit-ish/tree-ish to an object.

    Correct way is described at `here <http://stackoverflow.com/questions/
    23303549/what-are-commit-ish-and-tree-ish-in-git>`_.

    Resolution in this function is much simpler, no ``@{}~:/^ `` are supported.
    """

    SYM_PREFIX = 'ref: '
    TAG_PREFIX = 'refs/tags/'

    def __init__(self, repo, ref, lookup=None):
        self.repo = repo
        self.ref = ref
        self.lookup = lookup or repo.object_store.__getitem__

        full_names = [
            ref,
            'refs/tags/{}'.format(ref),
            'refs/heads/{}'.format(ref),
            'refs/remotes/{}'.format(ref),
            'refs/{}'.format(ref),
        ]

        candidates = []

        if ref in self.repo.object_store:
            candidates.append((ref, 'object', None))

        for name in full_names:
            if name in self.repo.refs:
                # store symbolic ref targets
                target = self.repo.refs.read_ref(name)
                if not target.startswith(self.SYM_PREFIX):
                    target = None
                else:
                    target = target[len(self.SYM_PREFIX):]

                candidates.append((name, 'ref', target))

        self.candidates = candidates

    @property
    def is_definite(self):
        return len(self.candidates) < 2

    @property
    def is_ref(self):
        if not self.candidates:
            return False

        return all(c[1] == 'ref' for c in self.candidates)

    @property
    def is_symbolic(self):
        return len(self.candidates) == 1 and self.target is not None

    @property
    @one_or_many
    def target(self, candidate):
        return candidate[2]

    @property
    def is_object(self):
        if not self.candidates:
            return False

        return all(c[1] == 'object' for c in self.candidates)

    @property
    @one_or_many
    def id(self, candidate):
        if candidate[1] == 'object':
            return candidate[0]

        return self.repo.refs[candidate[0]]

    @property
    def found(self):
        return bool(self.candidates)

    @property
    @one_or_many
    def full_name(self, candidate):
        return candidate[0]

    @property
    def tag_name(self):
        fn = self.full_name

        if fn.startswith(self.TAG_PREFIX):
            return fn[len(self.TAG_PREFIX):]

    @property
    @one_or_many
    def type(self, candidate):
        return candidate[1]

    @one_or_many
    def get_object(self, candidate):
        return self.repo[candidate[0]]

class MalleableCommit(object):
    def __init__(self, repo, author=u'', message=u'', parent_ids=[], tree=None,
                 committer=None, commit_time=None, author_time=None,
                 commit_timezone=None, author_timezone=None,
                 encoding='UTF-8'):
        now = int(time.time())
        local_timezone = get_local_timezone(now)

        self.repo = repo
        self.encoding = encoding
        self.parent_ids = parent_ids
        self.message = message

        self.author = author
        self.committer = author if committer is None else committer

        # times default to now
        self.author_time = author_time if author_time is not None else now
        self.commit_time = commit_time if commit_time is not None else now

        # the default timezone is the local timezone
        self.commit_timezone = (commit_timezone if commit_timezone is not None
                                else local_timezone)
        self.author_timezone = (author_timezone if author_timezone is not None
                                else local_timezone)

        self.tree = tree

        self.new_objects = {}

        # chain used for looking up items, may include uncommitted ones
        self._lookup_chain = ChainMap(self.new_objects, self.repo.object_store)

    @classmethod
    def from_parent(cls, repo, parent_id):
        parent = repo[parent_id]

        nc = cls(repo, parent_ids=[parent_id])
        nc.tree = repo[parent.id]

        return nc

    @classmethod
    def from_existing(cls, repo, commit_id):
        commit = repo[commit_id]
        encoding = commit.encoding or 'UTF-8'
        return cls(
            repo,
            author=commit.author,
            message=commit.message.decode(encoding),
            parent_ids=commit.parents,
            committer=commit.committer,
            commit_time=commit.commit_time,
            author_time=commit.author_time,
            commit_timezone=commit.commit_timezone,
            author_timezone=commit.author_timezone,
            encoding=encoding,
            tree=repo[commit.tree],
        )

    def to_commit(self):
        new_commit = Commit()
        new_commit.parents = self.parent_ids

        new_commit.tree = self.tree.id
        new_commit.author = self.author
        new_commit.committer = self.committer

        new_commit.author_time = self.author_time
        new_commit.commit_time = self.commit_time

        new_commit.commit_timezone = self.commit_timezone
        new_commit.author_timezone = self.author_timezone

        new_commit.encoding = self.encoding
        new_commit.message = self.message.encode(self.encoding)

        return new_commit

    def __unicode__(self):
        return self.to_commit().as_pretty_string().decode(self.encoding)

    def set_path_data(self, path, data, mode=0o0100644):
        blob = Blob.from_string(data)
        self.new_objects[blob.id] = blob

        return self.set_path_id(path, blob.id, mode)

    def set_path_id(self, path, id, mode=0o0100644):
        # we use regular "/" split here, as dulwich uses the same method
        parts = path.split('/')

        self.tree = self._path_add(self.tree, parts, id, mode)
        self.new_objects[self.tree.id] = self.tree

    def _path_add(self, tree, parts, id, mode):
        if len(parts) == 1:
            fn = parts[0]

            # add id and remember altered tree
            tree.add(fn, mode, id)
            return tree

        # there are more parts left
        subtree_name = parts[0]

        try:
            subtree_mode, subtree_id = tree[subtree_name]
            if not S_ISDIR(subtree_mode):
                subtree = None  # if it's a regular file, we overwrite it
            else:
                subtree = self._lookup_chain[subtree_id]
        except KeyError:
            subtree = None
            subtree_mode = S_IFDIR

        if subtree is None:
            subtree = Tree()

        # alter subtree and add to our tree
        old_subtree_id = subtree.id
        subtree = self._path_add(subtree, parts[1:], id, mode)

        # only store if the subtree actually changed
        if subtree.id != old_subtree_id:
            self.new_objects[subtree.id] = subtree
            tree.add(subtree_name, subtree_mode, subtree.id)

        return tree

    def export_to(self, path):
        export_tree(self._lookup_chain.__getitem__, self.tree, path)

    def path_exists(self, path):
        try:
            self.get_path_data(path)
            return True
        except KeyError:
            return False

    def get_path_id(self, path):
        return self._lookup(path)[1]

    def get_path_data(self, path):
        obj = self._lookup_chain[self.get_path_id(path)]

        if hasattr(obj, 'data'):
            return obj.data

    def get_path_mode(self, path):
        return self._lookup(path)[0]

    def save(self):
        # generate the commit
        commit = self.to_commit()
        self.new_objects[commit.id] = commit

        # instead of adding all new objects, we just add reachable ones
        ids_to_add = set()
        ids_to_add.add(commit.id)

        q = [commit.tree]
        while q:
            cur = q.pop()
            if not cur in self.new_objects:
                # already in repo
                continue

            ids_to_add.add(cur)

            # if it is a tree, also add children
            cur_obj = self.new_objects[cur]
            if isinstance(cur_obj, Tree):
                # add children
                for name, mode, sha in cur_obj.iteritems():
                    q.append(sha)

        # add all collected objects
        for id in ids_to_add:
            self.repo.object_store.add_object(self.new_objects[id])

        return commit.id

    def _lookup(self, path):
        # construct a lookup chain that
        return self.tree.lookup_path(self._lookup_chain.__getitem__, path)
<commit_msg>Check hash is valid before rummaging in object store.<commit_after>from datetime import datetime
import os
import re
from stat import S_ISLNK, S_ISDIR, S_ISREG, S_IFDIR, S_IRWXU, S_IRWXG, S_IRWXO
import time

from dateutil.tz import tzlocal
from dulwich.objects import S_ISGITLINK, Blob, Commit, Tree
import logbook
from stuf.collects import ChainMap

log = logbook.Logger('git')
HASH_RE = re.compile('^[a-zA-Z0-9]{40}$')

def export_tree(lookup, tree, path):
    """Exports the given tree object to path.

    :param lookup: Function to retrieve objects for SHA1 hashes.
    :param tree: Tree to export.
    :param path: Output path.
    """
    FILE_PERM = S_IRWXU | S_IRWXG | S_IRWXO

    for name, mode, hexsha in tree.iteritems():
        dest = os.path.join(path, name)

        if S_ISGITLINK(mode):
            log.error('Ignored submodule {}; submodules are not yet supported.'
                      .format(name))
            # raise ValueError('Does not support submodules')
        elif S_ISDIR(mode):
            os.mkdir(dest)
            os.chmod(dest, 0o0755)
            export_tree(lookup, lookup(hexsha), dest)
        elif S_ISLNK(mode):
            os.symlink(lookup(hexsha).data, dest)
        elif S_ISREG(mode):
            with open(dest, 'wb') as out:
                for chunk in lookup(hexsha).chunked:
                    out.write(chunk)
            os.chmod(dest, mode & FILE_PERM)
        else:
            raise ValueError('Cannot deal with mode of {:o} from {}'.format
                             (mode, name))

def get_local_timezone(now=None):
    if now is None:
        now = int(time.time())

    # calculate local timezone. must be done each time, due to DST
    _tz_offset = tzlocal().utcoffset(datetime.utcfromtimestamp(now))

    return _tz_offset.days * 24 * 60 * 60 + _tz_offset.seconds

def one_or_many(f):
    def _(self, *args, **kwargs):
        if len(self.candidates) == 1:
            return f(self, self.candidates[0], *args, **kwargs)
        if len(self.candidates) > 1:
            return [f(self, c) for c in self.candidates]
    return _

class ResolvedRef(object):
    """Resolves a commit-ish/tree-ish to an object.

    Correct way is described at `here <http://stackoverflow.com/questions/
    23303549/what-are-commit-ish-and-tree-ish-in-git>`_.

    Resolution in this function is much simpler, no ``@{}~:/^ `` are supported.
    """

    SYM_PREFIX = 'ref: '
    TAG_PREFIX = 'refs/tags/'

    def __init__(self, repo, ref, lookup=None):
        self.repo = repo
        self.ref = ref
        self.lookup = lookup or repo.object_store.__getitem__

        candidates = []

        # only check sha1 hashes
        if HASH_RE.match(ref) and ref in self.repo.object_store:
            candidates.append((ref, 'object', None))

        full_names = [
            ref,
            'refs/tags/{}'.format(ref),
            'refs/heads/{}'.format(ref),
            'refs/remotes/{}'.format(ref),
            'refs/{}'.format(ref),
        ]

        for name in full_names:
            if name in self.repo.refs:
                # store symbolic ref targets
                target = self.repo.refs.read_ref(name)
                if not target.startswith(self.SYM_PREFIX):
                    target = None
                else:
                    target = target[len(self.SYM_PREFIX):]

                candidates.append((name, 'ref', target))

        self.candidates = candidates

    @property
    def is_definite(self):
        return len(self.candidates) < 2

    @property
    def is_ref(self):
        if not self.candidates:
            return False

        return all(c[1] == 'ref' for c in self.candidates)

    @property
    def is_symbolic(self):
        return len(self.candidates) == 1 and self.target is not None

    @property
    @one_or_many
    def target(self, candidate):
        return candidate[2]

    @property
    def is_object(self):
        if not self.candidates:
            return False

        return all(c[1] == 'object' for c in self.candidates)

    @property
    @one_or_many
    def id(self, candidate):
        if candidate[1] == 'object':
            return candidate[0]

        return self.repo.refs[candidate[0]]

    @property
    def found(self):
        return bool(self.candidates)

    @property
    @one_or_many
    def full_name(self, candidate):
        return candidate[0]

    @property
    def tag_name(self):
        fn = self.full_name

        if fn.startswith(self.TAG_PREFIX):
            return fn[len(self.TAG_PREFIX):]

    @property
    @one_or_many
    def type(self, candidate):
        return candidate[1]

    @one_or_many
    def get_object(self, candidate):
        return self.repo[candidate[0]]

class MalleableCommit(object):
    def __init__(self, repo, author=u'', message=u'', parent_ids=[], tree=None,
                 committer=None, commit_time=None, author_time=None,
                 commit_timezone=None, author_timezone=None,
                 encoding='UTF-8'):
        now = int(time.time())
        local_timezone = get_local_timezone(now)

        self.repo = repo
        self.encoding = encoding
        self.parent_ids = parent_ids
        self.message = message

        self.author = author
        self.committer = author if committer is None else committer

        # times default to now
        self.author_time = author_time if author_time is not None else now
        self.commit_time = commit_time if commit_time is not None else now

        # the default timezone is the local timezone
        self.commit_timezone = (commit_timezone if commit_timezone is not None
                                else local_timezone)
        self.author_timezone = (author_timezone if author_timezone is not None
                                else local_timezone)

        self.tree = tree

        self.new_objects = {}

        # chain used for looking up items, may include uncommitted ones
        self._lookup_chain = ChainMap(self.new_objects, self.repo.object_store)

    @classmethod
    def from_parent(cls, repo, parent_id):
        parent = repo[parent_id]

        nc = cls(repo, parent_ids=[parent_id])
        nc.tree = repo[parent.id]

        return nc

    @classmethod
    def from_existing(cls, repo, commit_id):
        commit = repo[commit_id]
        encoding = commit.encoding or 'UTF-8'
        return cls(
            repo,
            author=commit.author,
            message=commit.message.decode(encoding),
            parent_ids=commit.parents,
            committer=commit.committer,
            commit_time=commit.commit_time,
            author_time=commit.author_time,
            commit_timezone=commit.commit_timezone,
            author_timezone=commit.author_timezone,
            encoding=encoding,
            tree=repo[commit.tree],
        )

    def to_commit(self):
        new_commit = Commit()
        new_commit.parents = self.parent_ids

        new_commit.tree = self.tree.id
        new_commit.author = self.author
        new_commit.committer = self.committer

        new_commit.author_time = self.author_time
        new_commit.commit_time = self.commit_time

        new_commit.commit_timezone = self.commit_timezone
        new_commit.author_timezone = self.author_timezone

        new_commit.encoding = self.encoding
        new_commit.message = self.message.encode(self.encoding)

        return new_commit

    def __unicode__(self):
        return self.to_commit().as_pretty_string().decode(self.encoding)

    def set_path_data(self, path, data, mode=0o0100644):
        blob = Blob.from_string(data)
        self.new_objects[blob.id] = blob

        return self.set_path_id(path, blob.id, mode)

    def set_path_id(self, path, id, mode=0o0100644):
        # we use regular "/" split here, as dulwich uses the same method
        parts = path.split('/')

        self.tree = self._path_add(self.tree, parts, id, mode)
        self.new_objects[self.tree.id] = self.tree

    def _path_add(self, tree, parts, id, mode):
        if len(parts) == 1:
            fn = parts[0]

            # add id and remember altered tree
            tree.add(fn, mode, id)
            return tree

        # there are more parts left
        subtree_name = parts[0]

        try:
            subtree_mode, subtree_id = tree[subtree_name]
            if not S_ISDIR(subtree_mode):
                subtree = None  # if it's a regular file, we overwrite it
            else:
                subtree = self._lookup_chain[subtree_id]
        except KeyError:
            subtree = None
            subtree_mode = S_IFDIR

        if subtree is None:
            subtree = Tree()

        # alter subtree and add to our tree
        old_subtree_id = subtree.id
        subtree = self._path_add(subtree, parts[1:], id, mode)

        # only store if the subtree actually changed
        if subtree.id != old_subtree_id:
            self.new_objects[subtree.id] = subtree
            tree.add(subtree_name, subtree_mode, subtree.id)

        return tree

    def export_to(self, path):
        export_tree(self._lookup_chain.__getitem__, self.tree, path)

    def path_exists(self, path):
        try:
            self.get_path_data(path)
            return True
        except KeyError:
            return False

    def get_path_id(self, path):
        return self._lookup(path)[1]

    def get_path_data(self, path):
        obj = self._lookup_chain[self.get_path_id(path)]

        if hasattr(obj, 'data'):
            return obj.data

    def get_path_mode(self, path):
        return self._lookup(path)[0]

    def save(self):
        # generate the commit
        commit = self.to_commit()
        self.new_objects[commit.id] = commit

        # instead of adding all new objects, we just add reachable ones
        ids_to_add = set()
        ids_to_add.add(commit.id)

        q = [commit.tree]
        while q:
            cur = q.pop()
            if not cur in self.new_objects:
                # already in repo
                continue

            ids_to_add.add(cur)

            # if it is a tree, also add children
            cur_obj = self.new_objects[cur]
            if isinstance(cur_obj, Tree):
                # add children
                for name, mode, sha in cur_obj.iteritems():
                    q.append(sha)

        # add all collected objects
        for id in ids_to_add:
            self.repo.object_store.add_object(self.new_objects[id])

        return commit.id

    def _lookup(self, path):
        # construct a lookup chain that
        return self.tree.lookup_path(self._lookup_chain.__getitem__, path)
<|endoftext|>