<commit_before>import datetime

__version__ = "0.2.1"

class BusinessTime(object):
    """
    BusinessTime is essentially a calendar that can be queried for
    business time aware timedeltas between two datetimes.
    """

    def __init__(self, business_hours=None, weekends=(5, 6), holidays=None):
        if business_hours is None:
            business_hours = (datetime.time(9), datetime.time(17))
        self.business_hours = business_hours

        # TODO: weekends should maybe be a generator or a callable returning True/False
        self.weekends = weekends

        self.holidays = holidays
        if callable(self.holidays) or self.holidays is None:
            self._holidaysGeneratorStart = None
            self._holidaysGenerator = None
            self._holidays = []
        else:
            self._holidays = self.holidays

        # HACK: pick an arbitrary date so we can do math with datetime.time objects
        arbitrary_date = datetime.datetime(2014, 1, 26)
        start = datetime.datetime.combine(arbitrary_date, business_hours[0])
        end = datetime.datetime.combine(arbitrary_date, business_hours[1])
        self.open_hours = end - start

    def isweekend(self, dt):
        return dt.weekday() in self.weekends

    def _ensure_holidays_span_datetime(self, dt):
        if callable(self.holidays):
            if self._holidaysGeneratorStart is None or dt < self._holidaysGeneratorStart:
                self._holidaysGeneratorStart = dt
                self._holidaysGenerator = self.holidays(dt)
            while len(self._holidays) == 0 or dt > self._holidays[-1]:
                self._holidays.append(next(self._holidaysGenerator))

    def isholiday(self, dt):
        if type(dt) == datetime.datetime:
            dt = dt.date()
        self._ensure_holidays_span_datetime(dt)
        return dt in self._holidays

    def isbusinessday(self, dt):
        return not self.isweekend(dt) and not self.isholiday(dt)

    def isduringbusinesshours(self, dt):
        return self.isbusinessday(dt) and self.business_hours[0] <= dt.time(
        ) < self.business_hours[1]

    def iterdays(self, d1, d2):
        """
        Date iterator returning dates in d1 <= x < d2
        """
        curr = datetime.datetime.combine(d1, datetime.time())
        end = datetime.datetime.combine(d2, datetime.time())
        if d1.date() == d2.date():
            yield curr
            return
        while curr < end:
            yield curr
            curr = curr + datetime.timedelta(days=1)

    def iterweekdays(self, d1, d2):
        """
        Date iterator returning dates in d1 <= x < d2, excluding weekends
        """
        for dt in self.iterdays(d1, d2):
            if not self.isweekend(dt):
                yield dt

    def iterbusinessdays(self, d1, d2):
        """
        Date iterator returning dates in d1 <= x < d2, excluding weekends and holidays
        """
        assert d2 >= d1
        if d1.date() == d2.date() and d2.time() < self.business_hours[0]:
            return
        first = True
        for dt in self.iterdays(d1, d2):
            if first and d1.time() > self.business_hours[1]:
                first = False
                continue
            first = False
            if not self.isweekend(dt) and not self.isholiday(dt):
                yield dt

    def _build_spanning_datetimes(self, d1, d2):
        businessdays = list(self.iterbusinessdays(d1, d2))
        if len(businessdays) == 0:
            return businessdays

        businessdays = [
            datetime.datetime.combine(d, self.business_hours[0])
            for d in businessdays
        ]

        if d1 > businessdays[0]:
            businessdays[0] = d1

        if self.isbusinessday(d2) and d2 >= datetime.datetime.combine(
                d2, self.business_hours[0]):
            businessdays.append(
                datetime.datetime.combine(d2, self.business_hours[1]))
            if d2 < businessdays[-1]:
                businessdays[-1] = datetime.datetime.combine(
                    businessdays[-1], d2.time())
        else:
            if len(businessdays) == 1:
                businessdays.append(
                    datetime.datetime.combine(businessdays[0],
                                              self.business_hours[1]))
            else:
                businessdays[-1] = datetime.datetime.combine(
                    businessdays[-1], self.business_hours[1])

        return businessdays

    def businesstimedelta(self, d1, d2):
        """
        Returns a datetime.timedelta with the number of full business days
        and business time between d1 and d2
        """

        if d1 > d2:
            d1, d2, timedelta_direction = d2, d1, -1
        else:
            timedelta_direction = 1
        businessdays = self._build_spanning_datetimes(d1, d2)
        time = datetime.timedelta()

        if len(businessdays) == 0:
            # HACK: manually handle the case when d1 is after business hours while d2 is during
            if self.isduringbusinesshours(d2):
                time += d2 - datetime.datetime.combine(d2,
                                                       self.business_hours[0])

            # HACK: manually handle the case where d1 is on an earlier non-business day and d2 is after hours on a business day
            elif not self.isbusinessday(d1) and self.isbusinessday(d2):
                if d2.time() > self.business_hours[1]:
                    time += datetime.datetime.combine(
                        d2,
                        self.business_hours[1]) - datetime.datetime.combine(
                            d2, self.business_hours[0])
                elif d2.time() > self.business_hours[0]:
                    time += d2 - datetime.datetime.combine(
                        d2, self.business_hours[0])

        else:
            prev = None
            current = None
            count = 0
            for d in businessdays:
                if current is None:
                    current = d
                current = datetime.datetime.combine(d, current.time())
                if prev is not None:
                    if prev.date() != current.date():
                        time += datetime.timedelta(days=1)
                    if count == len(businessdays) - 1:
                        if current > d:
                            # We went too far
                            time -= datetime.timedelta(days=1)
                            time += self.open_hours - (current - d)
                        else:
                            time += d - current
                count += 1
                prev = current

        return time * timedelta_direction

    def businesstime_hours(self, d1, d2):
        """
        Returns a datetime.timedelta of business hours between d1 and d2,
        based on the length of the businessday
        """
        open_hours = self.open_hours.seconds / 3600
        btd = self.businesstimedelta(d1, d2)
        btd_hours = btd.seconds / 3600
        return datetime.timedelta(hours=(btd.days * open_hours + btd_hours))
<commit_msg>when a date is before the start of the holiday generator, need to re-set the holidays to an empty list<commit_after>import datetime

__version__ = "0.2.1"

class BusinessTime(object):
    """
    BusinessTime is essentially a calendar that can be queried for
    business time aware timedeltas between two datetimes.
    """

    def __init__(self, business_hours=None, weekends=(5, 6), holidays=None):
        if business_hours is None:
            business_hours = (datetime.time(9), datetime.time(17))
        self.business_hours = business_hours

        # TODO: weekends should maybe be a generator or a callable returning True/False
        self.weekends = weekends

        self.holidays = holidays
        if callable(self.holidays) or self.holidays is None:
            self._holidaysGeneratorStart = None
            self._holidaysGenerator = None
            self._holidays = []
        else:
            self._holidays = self.holidays

        # HACK: pick an arbitrary date so we can do math with datetime.time objects
        arbitrary_date = datetime.datetime(2014, 1, 26)
        start = datetime.datetime.combine(arbitrary_date, business_hours[0])
        end = datetime.datetime.combine(arbitrary_date, business_hours[1])
        self.open_hours = end - start

    def isweekend(self, dt):
        return dt.weekday() in self.weekends

    def _ensure_holidays_span_datetime(self, dt):
        if callable(self.holidays):
            if self._holidaysGeneratorStart is None or dt < self._holidaysGeneratorStart:
                self._holidaysGeneratorStart = dt
                self._holidays = []
                self._holidaysGenerator = self.holidays(dt)
            while len(self._holidays) == 0 or dt > self._holidays[-1]:
                self._holidays.append(next(self._holidaysGenerator))

    def isholiday(self, dt):
        if type(dt) == datetime.datetime:
            dt = dt.date()
        self._ensure_holidays_span_datetime(dt)
        return dt in self._holidays

    def isbusinessday(self, dt):
        return not self.isweekend(dt) and not self.isholiday(dt)

    def isduringbusinesshours(self, dt):
        return self.isbusinessday(dt) and self.business_hours[0] <= dt.time(
        ) < self.business_hours[1]

    def iterdays(self, d1, d2):
        """
        Date iterator returning dates in d1 <= x < d2
        """
        curr = datetime.datetime.combine(d1, datetime.time())
        end = datetime.datetime.combine(d2, datetime.time())
        if d1.date() == d2.date():
            yield curr
            return
        while curr < end:
            yield curr
            curr = curr + datetime.timedelta(days=1)

    def iterweekdays(self, d1, d2):
        """
        Date iterator returning dates in d1 <= x < d2, excluding weekends
        """
        for dt in self.iterdays(d1, d2):
            if not self.isweekend(dt):
                yield dt

    def iterbusinessdays(self, d1, d2):
        """
        Date iterator returning dates in d1 <= x < d2, excluding weekends and holidays
        """
        assert d2 >= d1
        if d1.date() == d2.date() and d2.time() < self.business_hours[0]:
            return
        first = True
        for dt in self.iterdays(d1, d2):
            if first and d1.time() > self.business_hours[1]:
                first = False
                continue
            first = False
            if not self.isweekend(dt) and not self.isholiday(dt):
                yield dt

    def _build_spanning_datetimes(self, d1, d2):
        businessdays = list(self.iterbusinessdays(d1, d2))
        if len(businessdays) == 0:
            return businessdays

        businessdays = [
            datetime.datetime.combine(d, self.business_hours[0])
            for d in businessdays
        ]

        if d1 > businessdays[0]:
            businessdays[0] = d1

        if self.isbusinessday(d2) and d2 >= datetime.datetime.combine(
                d2, self.business_hours[0]):
            businessdays.append(
                datetime.datetime.combine(d2, self.business_hours[1]))
            if d2 < businessdays[-1]:
                businessdays[-1] = datetime.datetime.combine(
                    businessdays[-1], d2.time())
        else:
            if len(businessdays) == 1:
                businessdays.append(
                    datetime.datetime.combine(businessdays[0],
                                              self.business_hours[1]))
            else:
                businessdays[-1] = datetime.datetime.combine(
                    businessdays[-1], self.business_hours[1])

        return businessdays

    def businesstimedelta(self, d1, d2):
        """
        Returns a datetime.timedelta with the number of full business days
        and business time between d1 and d2
        """

        if d1 > d2:
            d1, d2, timedelta_direction = d2, d1, -1
        else:
            timedelta_direction = 1
        businessdays = self._build_spanning_datetimes(d1, d2)
        time = datetime.timedelta()

        if len(businessdays) == 0:
            # HACK: manually handle the case when d1 is after business hours while d2 is during
            if self.isduringbusinesshours(d2):
                time += d2 - datetime.datetime.combine(d2,
                                                       self.business_hours[0])

            # HACK: manually handle the case where d1 is on an earlier non-business day and d2 is after hours on a business day
            elif not self.isbusinessday(d1) and self.isbusinessday(d2):
                if d2.time() > self.business_hours[1]:
                    time += datetime.datetime.combine(
                        d2,
                        self.business_hours[1]) - datetime.datetime.combine(
                            d2, self.business_hours[0])
                elif d2.time() > self.business_hours[0]:
                    time += d2 - datetime.datetime.combine(
                        d2, self.business_hours[0])

        else:
            prev = None
            current = None
            count = 0
            for d in businessdays:
                if current is None:
                    current = d
                current = datetime.datetime.combine(d, current.time())
                if prev is not None:
                    if prev.date() != current.date():
                        time += datetime.timedelta(days=1)
                    if count == len(businessdays) - 1:
                        if current > d:
                            # We went too far
                            time -= datetime.timedelta(days=1)
                            time += self.open_hours - (current - d)
                        else:
                            time += d - current
                count += 1
                prev = current

        return time * timedelta_direction

    def businesstime_hours(self, d1, d2):
        """
        Returns a datetime.timedelta of business hours between d1 and d2,
        based on the length of the businessday
        """
        open_hours = self.open_hours.seconds / 3600
        btd = self.businesstimedelta(d1, d2)
        btd_hours = btd.seconds / 3600
        return datetime.timedelta(hours=(btd.days * open_hours + btd_hours))
<|endoftext|>


<commit_before>from furl import furl
import os
import tempfile

from mediawords.util.log import create_logger
from mediawords.util.perl import decode_object_from_bytes_if_needed
from mediawords.util.process import run_command_in_foreground, McRunCommandInForegroundException

log = create_logger(__name__)

class McDownloadFileException(Exception):
    """download_file() exception."""
    pass

class McDownloadFileToTempPathException(McDownloadFileException):
    """download_file_to_temp_path() exception."""
    pass

def download_file(source_url: str, target_path: str) -> None:
    """Download URL to path."""

    # FIXME reimplement using Python's "requests", don't use cURL

    source_url = decode_object_from_bytes_if_needed(source_url)
    target_path = decode_object_from_bytes_if_needed(target_path)

    args = ["curl",
            "--silent",
            "--show-error",
            "--fail",
            "--retry", "3",
            "--retry-delay", "5",
            "--output", target_path,
            source_url]

    try:
        run_command_in_foreground(args)
    except McRunCommandInForegroundException as ex:
        raise McDownloadFileException(
            "Error while downloading file from '%(source_url)s' to '%(target_path)s': %(exception)s" % {
                'source_url': source_url,
                'target_path': target_path,
                'exception': str(ex),
            })

def download_file_to_temp_path(source_url: str) -> str:
    """Download URL to temporary path, return that path."""

    # FIXME reimplement using Python's "requests", don't use cURL

    source_url = decode_object_from_bytes_if_needed(source_url)

    dest_dir = tempfile.mkdtemp()

    # Try to figure out a sensible name for the file
    # noinspection PyBroadException
    try:
        uri = furl(source_url)
        url_path = str(uri.path)
        temp_filename = os.path.basename(url_path)
    except Exception as ex:
        log.warning("Unable to come up with filename for URL %s: %s" % (source_url, str(ex),))
        temp_filename = "temp.dat"

    dest_path = os.path.join(dest_dir, temp_filename)
    try:
        download_file(source_url=source_url, target_path=dest_path)
    except McDownloadFileException as ex:
        raise McDownloadFileToTempPathException(
            "Error while downloading file from '%(source_url)s' to temp. location '%(target_path)s': %(exception)s" % {
                'source_url': source_url,
                'target_path': dest_path,
                'exception': str(ex),
            })

    return dest_path
<commit_msg>Remove obsolete FIXMEs<commit_after>from furl import furl
import os
import tempfile

from mediawords.util.log import create_logger
from mediawords.util.perl import decode_object_from_bytes_if_needed
from mediawords.util.process import run_command_in_foreground, McRunCommandInForegroundException

log = create_logger(__name__)

class McDownloadFileException(Exception):
    """download_file() exception."""
    pass

class McDownloadFileToTempPathException(McDownloadFileException):
    """download_file_to_temp_path() exception."""
    pass

def download_file(source_url: str, target_path: str) -> None:
    """Download URL to path."""

    source_url = decode_object_from_bytes_if_needed(source_url)
    target_path = decode_object_from_bytes_if_needed(target_path)

    args = ["curl",
            "--silent",
            "--show-error",
            "--fail",
            "--retry", "3",
            "--retry-delay", "5",
            "--output", target_path,
            source_url]

    try:
        run_command_in_foreground(args)
    except McRunCommandInForegroundException as ex:
        raise McDownloadFileException(
            "Error while downloading file from '%(source_url)s' to '%(target_path)s': %(exception)s" % {
                'source_url': source_url,
                'target_path': target_path,
                'exception': str(ex),
            })

def download_file_to_temp_path(source_url: str) -> str:
    """Download URL to temporary path, return that path."""

    source_url = decode_object_from_bytes_if_needed(source_url)

    dest_dir = tempfile.mkdtemp()

    # Try to figure out a sensible name for the file
    # noinspection PyBroadException
    try:
        uri = furl(source_url)
        url_path = str(uri.path)
        temp_filename = os.path.basename(url_path)
    except Exception as ex:
        log.warning("Unable to come up with filename for URL %s: %s" % (source_url, str(ex),))
        temp_filename = "temp.dat"

    dest_path = os.path.join(dest_dir, temp_filename)
    try:
        download_file(source_url=source_url, target_path=dest_path)
    except McDownloadFileException as ex:
        raise McDownloadFileToTempPathException(
            "Error while downloading file from '%(source_url)s' to temp. location '%(target_path)s': %(exception)s" % {
                'source_url': source_url,
                'target_path': dest_path,
                'exception': str(ex),
            })

    return dest_path
<|endoftext|>


<commit_before>#! /usr/bin/python3

import struct
import json
import logging
import binascii
import math
import itertools
import operator
from bitcoin import base58
from bitcoin.core import key
from functools import reduce

from bitstring import BitArray, BitStream, ConstBitStream, ReadError

logger = logging.getLogger(__name__)

from counterpartylib.lib import (config, util, exceptions, util, message_type, address)

ID = 3 # 0x03 is this specific message type

## address manipulation functions

def _addrToBytes(addr):
    return base58.decode(addr)[:-4]

def _addressFrom21Bytes(b):
    sha256_1 = key.hashlib.new('sha256')
    sha256_1.update(b)

    sha256_2 = key.hashlib.new('sha256')
    sha256_2.update(sha256_1.digest())

    chk = sha256_2.digest()[:4]

    return base58.encode(b''.join([b, chk]))

## encoding functions

def _encode_constructBaseLUT(snds):
    # t is a tuple of the form (asset, addr, amnt, memo, is_hex)
    return sorted(list(set([t[1] for t in snds]))) # Sorted to make list determinist

def _encode_constructBaseAssets(snds):
    # t is a tuple of the form (asset, addr, amnt [, memo, is_hex])
    return sorted(list(set([t[0] for t in snds]))) # Sorted to make list determinist

def _encode_constructLUT(snds):
    baseLUT = _encode_constructBaseLUT(snds)
    lutNbits = math.ceil(math.log2(len(baseLUT)))
    return {
        "nbits": lutNbits,
        "addrs": baseLUT
    }

def _encode_compressLUT(lut):
    return b''.join([struct.pack('>H', len(lut['addrs']))] +
        [
            _addrToBytes(addr)
            for addr in lut['addrs']
        ])

def _encode_memo(memo=None, is_hex=False):
    if memo is not None:
        barr = BitArray('0b1')

        if is_hex:
            barr.append('0b1')
            if type(memo) is str: # append the string as hex-string
                barr.append('uint:6=%i' % (len(memo) >> 1))
                memo = '0x%s' % memo
            else:
                barr.append('uint:6=%i' % len(memo))

            barr.append(memo)
        else:
            barr.append('0b0')
            barr.append('uint:6=%i' % len(memo))
            barr.append(BitArray(memo.encode('utf-8')))

        return barr
    else:
        return BitArray('0b0')

def _safe_tuple_index(t, i):
    if len(t) <= i:
        return None
    else:
        return t[i]

def _encode_constructSendList(send_asset, lut, sends):
    # t is a tuple of the form (asset, addr, amnt, memo, is_hex)
    return [
        (lut['addrs'].index(t[1]), t[2], _safe_tuple_index(t, 3), _safe_tuple_index(t, 4))
        for t in sends
        if t[0] == send_asset
    ]

def _solve_asset(db, assetName, block_index):
    asset = util.resolve_subasset_longname(db, assetName)
    return util.get_asset_id(db, asset, block_index)

def _encode_compressSendList(db, nbits, send, block_index):
    r = BitArray()
    r.append('uintbe:64=%i' % _solve_asset(db, send['assetName'], block_index))
    r.append('uint:%i=%i' % (nbits, len(send['sendList'])-1))
    for sendItem in send['sendList']:
        idx = sendItem[0]
        amnt = sendItem[1]
        r.append('uint:%i=%i' % (nbits, idx))
        r.append('uintbe:64=%i' % amnt)

        try:
            memoStr = _encode_memo(memo=sendItem[2], is_hex=sendItem[3])
        except:
            memoStr = BitArray('0b0')

        r.append(memoStr)
    return r

def _encode_constructSends(sends):
    lut = _encode_constructLUT(sends)
    assets = _encode_constructBaseAssets(sends)

    sendLists = [
        {
            "assetName": asset,
            "sendList": _encode_constructSendList(asset, lut, sends)
        }
        for asset in assets
    ]

    return {
        "lut": lut,
        "sendLists": sendLists
    }

def _encode_compressSends(db, mpmaSend, block_index, memo=None, memo_is_hex=False):
    compressedLUT = _encode_compressLUT(mpmaSend['lut'])
    memo_arr = _encode_memo(memo, memo_is_hex).bin

    isends = '0b' + memo_arr + ''.join([
        ''.join(['1', _encode_compressSendList(db, mpmaSend['lut']['nbits'], sendList, block_index).bin])
        for sendList in mpmaSend['sendLists']
    ])
    bstr = ''.join([isends, '0'])
    pad = '0' * ((8 - (len(bstr) - 2)) % 8) # That -2 is because the prefix 0b is there
    barr = BitArray(bstr + pad)
    return b''.join([
        compressedLUT,
        barr.bytes
    ])

def _encode_mpmaSend(db, sends, block_index, memo=None, memo_is_hex=False):
    mpma = _encode_constructSends(sends)
    send = _encode_compressSends(db, mpma, block_index, memo=memo, memo_is_hex=memo_is_hex)

    return send

## decoding functions

def _decode_decodeLUT(data):
    (numAddresses,) = struct.unpack('>H', data[0:2])
    if numAddresses == 0:
        raise exceptions.DecodeError('address list can\'t be empty')
    p = 2
    addressList = []
    bytesPerAddress = 21

    for i in range(0, numAddresses):
        addr_raw = data[p:p+bytesPerAddress]

        addressList.append(_addressFrom21Bytes(addr_raw))
        p += bytesPerAddress

    lutNbits = math.ceil(math.log2(numAddresses))

    return addressList, lutNbits, data[p:]

def _decode_decodeSendList(stream, nbits, lut, block_index):
    asset_id = stream.read('uintbe:64')
    numRecipients = stream.read('uint:%i' % nbits)
    sendList = []
    asset = util.generate_asset_name(asset_id, block_index)
    for i in range(0, numRecipients + 1):
        idx = stream.read('uint:%i' % nbits)
        addr = lut[idx]
        amount = stream.read('uintbe:64')

        memo, is_hex = _decode_memo(stream)
        if memo is not None:
            sendList.append((addr, amount, memo, is_hex))
        else:
            sendList.append((addr, amount))

    return asset, sendList

def _decode_decodeSends(stream, nbits, lut, block_index):
    #stream = ConstBitStream(data)
    sends = {}

    while stream.read('bool'):
        asset, sendList = _decode_decodeSendList(stream, nbits, lut, block_index)
        sends[asset] = sendList

    return sends

def _decode_memo(stream):
    if stream.read('bool'):
        is_hex = stream.read('bool')
        mlen = stream.read('uint:6')
        data = stream.read('bytes:%i' % mlen)

        if not(is_hex):
            # is an utf8 string
            data = data.decode('utf-8')

        return data, is_hex
    else:
        return None, None

def _decode_mpmaSendDecode(data, block_index):
    lut, nbits, remain = _decode_decodeLUT(data)
    stream = ConstBitStream(remain)
    memo, is_hex = _decode_memo(stream)
    sends = _decode_decodeSends(stream, nbits, lut, block_index)
    if memo is not None:
        for asset in sends:
            sendList = sends[asset]
            for idx, send in enumerate(sendList):
                if len(send) == 2:
                    sendList[idx] = (send[0], send[1], memo, is_hex)

    return sends

## misc funcs

def _accumulate(l):
    it = itertools.groupby(l, operator.itemgetter(0))
    for key, subiter in it:
       yield key, sum(item[1] for item in subiter)

## expected functions for message version
def unpack(db, message, block_index):
    try:
        unpacked = _decode_mpmaSendDecode(message, block_index)
    except (struct.error) as e:
        raise exceptions.UnpackError('could not unpack')
    except (exceptions.AssetNameError, exceptions.AssetIDError) as e:
        raise exceptions.UnpackError('invalid asset in mpma send')
    except (ReadError) as e:
        raise exceptions.UnpackError('truncated data')

    return unpacked

def validate (db, source, asset_dest_quant_list, block_index):
    problems = []

    if len(asset_dest_quant_list) == 0:
        problems.append('send list cannot be empty')

    if len(asset_dest_quant_list) == 1:
        problems.append('send list cannot have only one element')

    if len(asset_dest_quant_list) > 0:
        # Need to manually unpack the tuple to avoid errors on scenarios where no memo is specified
        grpd = itertools.groupby([(t[0], t[1]) for t in asset_dest_quant_list])
        lengrps = [len(list(grpr)) for (group, grpr) in grpd]
        cardinality = max(lengrps)
        if cardinality > 1:
            problems.append('cannot specify more than once a destination per asset')

    cursor = db.cursor()
    for t in asset_dest_quant_list:
        # Need to manually unpack the tuple to avoid errors on scenarios where no memo is specified
        asset = t[0]
        destination = t[1]
        quantity = t[2]

        sendMemo = None
        if len(t) > 3:
            sendMemo = t[3]

        if asset == config.BTC: problems.append('cannot send {} to {}'.format(config.BTC, destination))

        if not isinstance(quantity, int):
            problems.append('quantities must be an int (in satoshis) for {} to {}'.format(asset, destination))

        if quantity < 0:
            problems.append('negative quantity for {} to {}'.format(asset, destination))

        if quantity == 0:
            problems.append('zero quantity for {} to {}'.format(asset, destination))

        # For SQLite3
        if quantity > config.MAX_INT:
            problems.append('integer overflow for {} to {}'.format(asset, destination))

        # destination is always required
        if not destination:
            problems.append('destination is required for {}'.format(asset))

        if util.enabled('options_require_memo'):
            results = cursor.execute('SELECT options FROM addresses WHERE address=?', (destination,))
            if results:
                result = results.fetchone()
                if result and result['options'] & config.ADDRESS_OPTION_REQUIRE_MEMO and (sendMemo is None):
                    problems.append('destination {} requires memo'.format(destination))

    cursor.close()

    return problems

def compose (db, source, asset_dest_quant_list, memo, memo_is_hex):
    cursor = db.cursor()

    out_balances = _accumulate([(t[0], t[2]) for t in asset_dest_quant_list])
    for (asset, quantity) in out_balances:
        if not isinstance(quantity, int):
            raise exceptions.ComposeError('quantities must be an int (in satoshis) for {}'.format(asset))

        balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, asset)))
        if not balances or balances[0]['quantity'] < quantity:
            raise exceptions.ComposeError('insufficient funds for {}'.format(asset))

    block_index = util.CURRENT_BLOCK_INDEX

    cursor.close()

    problems = validate(db, source, asset_dest_quant_list, block_index)
    if problems: raise exceptions.ComposeError(problems)

    data = message_type.pack(ID)
    data += _encode_mpmaSend(db, asset_dest_quant_list, block_index, memo=memo, memo_is_hex=memo_is_hex)

    return (source, [], data)

def parse (db, tx, message):
    try:
        unpacked = unpack(db, message, tx['block_index'])
        status = 'valid'
    except (struct.error) as e:
        status = 'invalid: truncated message'
    except (exceptions.AssetNameError, exceptions.AssetIDError) as e:
        status = 'invalid: invalid asset name/id'
    except (Exception) as e:
        status = 'invalid: couldn\'t unpack; %s' % e

    cursor = db.cursor()

    plain_sends = []
    all_debits = []
    all_credits = []
    if status == 'valid':
        for asset_id in unpacked:
            try:
                asset = util.get_asset_name(db, asset_id, tx['block_index'])
            except (exceptions.AssetNameError) as e:
                status = 'invalid: asset %s invalid at block index %i' % (asset_id, tx['block_index'])
                break

            cursor.execute('''SELECT * FROM balances \
                              WHERE (address = ? AND asset = ?)''', (tx['source'], asset_id))

            balances = cursor.fetchall()
            if not balances:
                status = 'invalid: insufficient funds for asset %s, address %s has no balance' % (asset_id, tx['source'])
                break

            credits = unpacked[asset_id]

            total_sent = reduce(lambda p, t: p + t[1], credits, 0)

            if balances[0]['quantity'] < total_sent:
                status = 'invalid: insufficient funds for asset %s, needs %i' % (asset_id, total_sent)
                break

            if status == 'valid':
                plain_sends += map(lambda t: (asset_id, *t), credits)
                all_credits += map(lambda t: {"asset": asset_id, "destination": t[0], "quantity": t[1]}, credits)
                all_debits.append({"asset": asset_id, "quantity": total_sent})

    if status == 'valid':
        problems = validate(db, tx['source'], plain_sends, tx['block_index'])

        if problems: status = 'invalid:' + '; '.join(problems)

    if status == 'valid':
        for op in all_credits:
            util.credit(db, op['destination'], op['asset'], op['quantity'], action='send', event=tx['tx_hash'])

        for op in all_debits:
            util.debit(db, tx['source'], op['asset'], op['quantity'], action='send', event=tx['tx_hash'])

        # Enumeration of the plain sends needs to be deterministic, so we sort them by asset and then by address
        plain_sends = sorted(plain_sends, key=lambda x: ''.join([x[0], x[1]]))
        for i, op in enumerate(plain_sends):
            if len(op) > 3:
                memo_bytes = op[3]
            else:
                memo_bytes = None

            bindings = {
                'tx_index': tx['tx_index'],
                'tx_hash': tx['tx_hash'],
                'block_index': tx['block_index'],
                'source': tx['source'],
                'asset': op[0],
                'destination': op[1],
                'quantity': op[2],
                'status': status,
                'memo': memo_bytes,
                'msg_index': i
            }

            sql = 'insert into sends (tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo, msg_index) values(:tx_index, :tx_hash, :block_index, :source, :destination, :asset, :quantity, :status, :memo, :msg_index)'
            cursor.execute(sql, bindings)

    if status != 'valid':
        logger.warn("Not storing [mpma] tx [%s]: %s" % (tx['tx_hash'], status))

    cursor.close()

# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
<commit_msg>Fixed star expression unsupported by python 3.4 runtime on circle<commit_after>#! /usr/bin/python3

import struct
import json
import logging
import binascii
import math
import itertools
import operator
from bitcoin import base58
from bitcoin.core import key
from functools import reduce

from bitstring import BitArray, BitStream, ConstBitStream, ReadError

logger = logging.getLogger(__name__)

from counterpartylib.lib import (config, util, exceptions, util, message_type, address)

ID = 3 # 0x03 is this specific message type

## address manipulation functions

def _addrToBytes(addr):
    return base58.decode(addr)[:-4]

def _addressFrom21Bytes(b):
    sha256_1 = key.hashlib.new('sha256')
    sha256_1.update(b)

    sha256_2 = key.hashlib.new('sha256')
    sha256_2.update(sha256_1.digest())

    chk = sha256_2.digest()[:4]

    return base58.encode(b''.join([b, chk]))

## encoding functions

def _encode_constructBaseLUT(snds):
    # t is a tuple of the form (asset, addr, amnt, memo, is_hex)
    return sorted(list(set([t[1] for t in snds]))) # Sorted to make list determinist

def _encode_constructBaseAssets(snds):
    # t is a tuple of the form (asset, addr, amnt [, memo, is_hex])
    return sorted(list(set([t[0] for t in snds]))) # Sorted to make list determinist

def _encode_constructLUT(snds):
    baseLUT = _encode_constructBaseLUT(snds)
    lutNbits = math.ceil(math.log2(len(baseLUT)))
    return {
        "nbits": lutNbits,
        "addrs": baseLUT
    }

def _encode_compressLUT(lut):
    return b''.join([struct.pack('>H', len(lut['addrs']))] +
        [
            _addrToBytes(addr)
            for addr in lut['addrs']
        ])

def _encode_memo(memo=None, is_hex=False):
    if memo is not None:
        barr = BitArray('0b1')

        if is_hex:
            barr.append('0b1')
            if type(memo) is str: # append the string as hex-string
                barr.append('uint:6=%i' % (len(memo) >> 1))
                memo = '0x%s' % memo
            else:
                barr.append('uint:6=%i' % len(memo))

            barr.append(memo)
        else:
            barr.append('0b0')
            barr.append('uint:6=%i' % len(memo))
            barr.append(BitArray(memo.encode('utf-8')))

        return barr
    else:
        return BitArray('0b0')

def _safe_tuple_index(t, i):
    if len(t) <= i:
        return None
    else:
        return t[i]

def _encode_constructSendList(send_asset, lut, sends):
    # t is a tuple of the form (asset, addr, amnt, memo, is_hex)
    return [
        (lut['addrs'].index(t[1]), t[2], _safe_tuple_index(t, 3), _safe_tuple_index(t, 4))
        for t in sends
        if t[0] == send_asset
    ]

def _solve_asset(db, assetName, block_index):
    asset = util.resolve_subasset_longname(db, assetName)
    return util.get_asset_id(db, asset, block_index)

def _encode_compressSendList(db, nbits, send, block_index):
    r = BitArray()
    r.append('uintbe:64=%i' % _solve_asset(db, send['assetName'], block_index))
    r.append('uint:%i=%i' % (nbits, len(send['sendList'])-1))
    for sendItem in send['sendList']:
        idx = sendItem[0]
        amnt = sendItem[1]
        r.append('uint:%i=%i' % (nbits, idx))
        r.append('uintbe:64=%i' % amnt)

        try:
            memoStr = _encode_memo(memo=sendItem[2], is_hex=sendItem[3])
        except:
            memoStr = BitArray('0b0')

        r.append(memoStr)
    return r

def _encode_constructSends(sends):
    lut = _encode_constructLUT(sends)
    assets = _encode_constructBaseAssets(sends)

    sendLists = [
        {
            "assetName": asset,
            "sendList": _encode_constructSendList(asset, lut, sends)
        }
        for asset in assets
    ]

    return {
        "lut": lut,
        "sendLists": sendLists
    }

def _encode_compressSends(db, mpmaSend, block_index, memo=None, memo_is_hex=False):
    compressedLUT = _encode_compressLUT(mpmaSend['lut'])
    memo_arr = _encode_memo(memo, memo_is_hex).bin

    isends = '0b' + memo_arr + ''.join([
        ''.join(['1', _encode_compressSendList(db, mpmaSend['lut']['nbits'], sendList, block_index).bin])
        for sendList in mpmaSend['sendLists']
    ])
    bstr = ''.join([isends, '0'])
    pad = '0' * ((8 - (len(bstr) - 2)) % 8) # That -2 is because the prefix 0b is there
    barr = BitArray(bstr + pad)
    return b''.join([
        compressedLUT,
        barr.bytes
    ])

def _encode_mpmaSend(db, sends, block_index, memo=None, memo_is_hex=False):
    mpma = _encode_constructSends(sends)
    send = _encode_compressSends(db, mpma, block_index, memo=memo, memo_is_hex=memo_is_hex)

    return send

## decoding functions

def _decode_decodeLUT(data):
    (numAddresses,) = struct.unpack('>H', data[0:2])
    if numAddresses == 0:
        raise exceptions.DecodeError('address list can\'t be empty')
    p = 2
    addressList = []
    bytesPerAddress = 21

    for i in range(0, numAddresses):
        addr_raw = data[p:p+bytesPerAddress]

        addressList.append(_addressFrom21Bytes(addr_raw))
        p += bytesPerAddress

    lutNbits = math.ceil(math.log2(numAddresses))

    return addressList, lutNbits, data[p:]

def _decode_decodeSendList(stream, nbits, lut, block_index):
    asset_id = stream.read('uintbe:64')
    numRecipients = stream.read('uint:%i' % nbits)
    sendList = []
    asset = util.generate_asset_name(asset_id, block_index)
    for i in range(0, numRecipients + 1):
        idx = stream.read('uint:%i' % nbits)
        addr = lut[idx]
        amount = stream.read('uintbe:64')

        memo, is_hex = _decode_memo(stream)
        if memo is not None:
            sendList.append((addr, amount, memo, is_hex))
        else:
            sendList.append((addr, amount))

    return asset, sendList

def _decode_decodeSends(stream, nbits, lut, block_index):
    #stream = ConstBitStream(data)
    sends = {}

    while stream.read('bool'):
        asset, sendList = _decode_decodeSendList(stream, nbits, lut, block_index)
        sends[asset] = sendList

    return sends

def _decode_memo(stream):
    if stream.read('bool'):
        is_hex = stream.read('bool')
        mlen = stream.read('uint:6')
        data = stream.read('bytes:%i' % mlen)

        if not(is_hex):
            # is an utf8 string
            data = data.decode('utf-8')

        return data, is_hex
    else:
        return None, None

def _decode_mpmaSendDecode(data, block_index):
    lut, nbits, remain = _decode_decodeLUT(data)
    stream = ConstBitStream(remain)
    memo, is_hex = _decode_memo(stream)
    sends = _decode_decodeSends(stream, nbits, lut, block_index)
    if memo is not None:
        for asset in sends:
            sendList = sends[asset]
            for idx, send in enumerate(sendList):
                if len(send) == 2:
                    sendList[idx] = (send[0], send[1], memo, is_hex)

    return sends

## misc funcs

def _accumulate(l):
    it = itertools.groupby(l, operator.itemgetter(0))
    for key, subiter in it:
       yield key, sum(item[1] for item in subiter)

## expected functions for message version
def unpack(db, message, block_index):
    try:
        unpacked = _decode_mpmaSendDecode(message, block_index)
    except (struct.error) as e:
        raise exceptions.UnpackError('could not unpack')
    except (exceptions.AssetNameError, exceptions.AssetIDError) as e:
        raise exceptions.UnpackError('invalid asset in mpma send')
    except (ReadError) as e:
        raise exceptions.UnpackError('truncated data')

    return unpacked

def validate (db, source, asset_dest_quant_list, block_index):
    problems = []

    if len(asset_dest_quant_list) == 0:
        problems.append('send list cannot be empty')

    if len(asset_dest_quant_list) == 1:
        problems.append('send list cannot have only one element')

    if len(asset_dest_quant_list) > 0:
        # Need to manually unpack the tuple to avoid errors on scenarios where no memo is specified
        grpd = itertools.groupby([(t[0], t[1]) for t in asset_dest_quant_list])
        lengrps = [len(list(grpr)) for (group, grpr) in grpd]
        cardinality = max(lengrps)
        if cardinality > 1:
            problems.append('cannot specify more than once a destination per asset')

    cursor = db.cursor()
    for t in asset_dest_quant_list:
        # Need to manually unpack the tuple to avoid errors on scenarios where no memo is specified
        asset = t[0]
        destination = t[1]
        quantity = t[2]

        sendMemo = None
        if len(t) > 3:
            sendMemo = t[3]

        if asset == config.BTC: problems.append('cannot send {} to {}'.format(config.BTC, destination))

        if not isinstance(quantity, int):
            problems.append('quantities must be an int (in satoshis) for {} to {}'.format(asset, destination))

        if quantity < 0:
            problems.append('negative quantity for {} to {}'.format(asset, destination))

        if quantity == 0:
            problems.append('zero quantity for {} to {}'.format(asset, destination))

        # For SQLite3
        if quantity > config.MAX_INT:
            problems.append('integer overflow for {} to {}'.format(asset, destination))

        # destination is always required
        if not destination:
            problems.append('destination is required for {}'.format(asset))

        if util.enabled('options_require_memo'):
            results = cursor.execute('SELECT options FROM addresses WHERE address=?', (destination,))
            if results:
                result = results.fetchone()
                if result and result['options'] & config.ADDRESS_OPTION_REQUIRE_MEMO and (sendMemo is None):
                    problems.append('destination {} requires memo'.format(destination))

    cursor.close()

    return problems

def compose (db, source, asset_dest_quant_list, memo, memo_is_hex):
    cursor = db.cursor()

    out_balances = _accumulate([(t[0], t[2]) for t in asset_dest_quant_list])
    for (asset, quantity) in out_balances:
        if not isinstance(quantity, int):
            raise exceptions.ComposeError('quantities must be an int (in satoshis) for {}'.format(asset))

        balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, asset)))
        if not balances or balances[0]['quantity'] < quantity:
            raise exceptions.ComposeError('insufficient funds for {}'.format(asset))

    block_index = util.CURRENT_BLOCK_INDEX

    cursor.close()

    problems = validate(db, source, asset_dest_quant_list, block_index)
    if problems: raise exceptions.ComposeError(problems)

    data = message_type.pack(ID)
    data += _encode_mpmaSend(db, asset_dest_quant_list, block_index, memo=memo, memo_is_hex=memo_is_hex)

    return (source, [], data)

def py34TupleAppend(first_elem, t):
    # Had to do it this way to support python 3.4, if we start
    # using the 3.5 runtime this can be replaced by:
    #  (first_elem, *t)

    l = list(t)
    l.insert(0, first_elem)
    return tuple(l)

def parse (db, tx, message):
    try:
        unpacked = unpack(db, message, tx['block_index'])
        status = 'valid'
    except (struct.error) as e:
        status = 'invalid: truncated message'
    except (exceptions.AssetNameError, exceptions.AssetIDError) as e:
        status = 'invalid: invalid asset name/id'
    except (Exception) as e:
        status = 'invalid: couldn\'t unpack; %s' % e

    cursor = db.cursor()

    plain_sends = []
    all_debits = []
    all_credits = []
    if status == 'valid':
        for asset_id in unpacked:
            try:
                asset = util.get_asset_name(db, asset_id, tx['block_index'])
            except (exceptions.AssetNameError) as e:
                status = 'invalid: asset %s invalid at block index %i' % (asset_id, tx['block_index'])
                break

            cursor.execute('''SELECT * FROM balances \
                              WHERE (address = ? AND asset = ?)''', (tx['source'], asset_id))

            balances = cursor.fetchall()
            if not balances:
                status = 'invalid: insufficient funds for asset %s, address %s has no balance' % (asset_id, tx['source'])
                break

            credits = unpacked[asset_id]

            total_sent = reduce(lambda p, t: p + t[1], credits, 0)

            if balances[0]['quantity'] < total_sent:
                status = 'invalid: insufficient funds for asset %s, needs %i' % (asset_id, total_sent)
                break

            if status == 'valid':
                plain_sends += map(lambda t: py34TupleAppend(asset_id, t), credits)
                all_credits += map(lambda t: {"asset": asset_id, "destination": t[0], "quantity": t[1]}, credits)
                all_debits.append({"asset": asset_id, "quantity": total_sent})

    if status == 'valid':
        problems = validate(db, tx['source'], plain_sends, tx['block_index'])

        if problems: status = 'invalid:' + '; '.join(problems)

    if status == 'valid':
        for op in all_credits:
            util.credit(db, op['destination'], op['asset'], op['quantity'], action='send', event=tx['tx_hash'])

        for op in all_debits:
            util.debit(db, tx['source'], op['asset'], op['quantity'], action='send', event=tx['tx_hash'])

        # Enumeration of the plain sends needs to be deterministic, so we sort them by asset and then by address
        plain_sends = sorted(plain_sends, key=lambda x: ''.join([x[0], x[1]]))
        for i, op in enumerate(plain_sends):
            if len(op) > 3:
                memo_bytes = op[3]
            else:
                memo_bytes = None

            bindings = {
                'tx_index': tx['tx_index'],
                'tx_hash': tx['tx_hash'],
                'block_index': tx['block_index'],
                'source': tx['source'],
                'asset': op[0],
                'destination': op[1],
                'quantity': op[2],
                'status': status,
                'memo': memo_bytes,
                'msg_index': i
            }

            sql = 'insert into sends (tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo, msg_index) values(:tx_index, :tx_hash, :block_index, :source, :destination, :asset, :quantity, :status, :memo, :msg_index)'
            cursor.execute(sql, bindings)

    if status != 'valid':
        logger.warn("Not storing [mpma] tx [%s]: %s" % (tx['tx_hash'], status))

    cursor.close()

# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
# -----------------------------------------------------------------------------

from ..geometry import create_sphere
from .mesh import MeshVisual
from .visual import CompoundVisual

class SphereVisual(CompoundVisual):
    """Visual that displays a sphere

    Parameters
    ----------
    radius : float
        The size of the sphere.
    cols : int
        Number of cols that make up the sphere mesh
        (for method='latitude' and 'cube').
    rows : int
        Number of rows that make up the sphere mesh
        (for method='latitude' and 'cube').
    depth : int
        Number of depth segments that make up the sphere mesh
        (for method='cube').
    subdivisions : int
        Number of subdivisions to perform (for method='ico').
    method : str
        Method for generating sphere. Accepts 'latitude' for
        latitude-longitude, 'ico' for icosahedron, and 'cube'
        for cube based tessellation.
    vertex_colors : ndarray
        Same as for `MeshVisual` class.
        See `create_sphere` for vertex ordering.
    face_colors : ndarray
        Same as for `MeshVisual` class.
        See `create_sphere` for vertex ordering.
    color : Color
        The `Color` to use when drawing the sphere faces.
    edge_color : tuple or Color
        The `Color` to use when drawing the sphere edges. If `None`, then no
        sphere edges are drawn.
    """
    def __init__(self, radius=1.0, cols=30, rows=30, depth=30, subdivisions=3,
                 method='latitude', vertex_colors=None, face_colors=None,
                 color=(0.5, 0.5, 1, 1), edge_color=None, **kwargs):

        mesh = create_sphere(rows, cols, depth, radius=radius,
                             subdivisions=subdivisions, method=method)

        self._mesh = MeshVisual(vertices=mesh.get_vertices(),
                                faces=mesh.get_faces(),
                                vertex_colors=vertex_colors,
                                face_colors=face_colors, color=color)
        if edge_color:
            self._border = MeshVisual(vertices=mesh.get_vertices(),
                                      faces=mesh.get_edges(),
                                      color=edge_color, mode='lines')
        else:
            self._border = MeshVisual()

        CompoundVisual.__init__(self, [self._mesh, self._border], **kwargs)
        self.mesh.set_gl_state(polygon_offset_fill=True,
                               polygon_offset=(1, 1), depth_test=True)

    @property
    def mesh(self):
        """The vispy.visuals.MeshVisual that used to fil in.
        """
        return self._mesh

    @property
    def border(self):
        """The vispy.visuals.MeshVisual that used to draw the border.
        """
        return self._border
<commit_msg>Add shading to SphereVisual<commit_after># -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
# -----------------------------------------------------------------------------

from ..geometry import create_sphere
from .mesh import MeshVisual
from .visual import CompoundVisual

class SphereVisual(CompoundVisual):
    """Visual that displays a sphere

    Parameters
    ----------
    radius : float
        The size of the sphere.
    cols : int
        Number of cols that make up the sphere mesh
        (for method='latitude' and 'cube').
    rows : int
        Number of rows that make up the sphere mesh
        (for method='latitude' and 'cube').
    depth : int
        Number of depth segments that make up the sphere mesh
        (for method='cube').
    subdivisions : int
        Number of subdivisions to perform (for method='ico').
    method : str
        Method for generating sphere. Accepts 'latitude' for
        latitude-longitude, 'ico' for icosahedron, and 'cube'
        for cube based tessellation.
    vertex_colors : ndarray
        Same as for `MeshVisual` class.
        See `create_sphere` for vertex ordering.
    face_colors : ndarray
        Same as for `MeshVisual` class.
        See `create_sphere` for vertex ordering.
    color : Color
        The `Color` to use when drawing the sphere faces.
    edge_color : tuple or Color
        The `Color` to use when drawing the sphere edges. If `None`, then no
        sphere edges are drawn.
    shading : str | None
        Shading to use.
    """
    def __init__(self, radius=1.0, cols=30, rows=30, depth=30, subdivisions=3,
                 method='latitude', vertex_colors=None, face_colors=None,
                 color=(0.5, 0.5, 1, 1), edge_color=None, shading=None, **kwargs):

        mesh = create_sphere(rows, cols, depth, radius=radius,
                             subdivisions=subdivisions, method=method)

        self._mesh = MeshVisual(vertices=mesh.get_vertices(),
                                faces=mesh.get_faces(),
                                vertex_colors=vertex_colors,
                                face_colors=face_colors, color=color,
                                shading=shading)
        if edge_color:
            self._border = MeshVisual(vertices=mesh.get_vertices(),
                                      faces=mesh.get_edges(),
                                      color=edge_color, mode='lines')
        else:
            self._border = MeshVisual()

        CompoundVisual.__init__(self, [self._mesh, self._border], **kwargs)
        self.mesh.set_gl_state(polygon_offset_fill=True,
                               polygon_offset=(1, 1), depth_test=True)

    @property
    def mesh(self):
        """The vispy.visuals.MeshVisual that used to fil in.
        """
        return self._mesh

    @property
    def border(self):
        """The vispy.visuals.MeshVisual that used to draw the border.
        """
        return self._border
<|endoftext|>


<commit_before>import argparse
import logging
import os

from conda_build.conda_interface import cc_conda_build

from conda_concourse_ci import __version__, execute

def parse_args(parse_this=None):
    parser = argparse.ArgumentParser()
    parser.add_argument('--debug', action='store_true')
    parser.add_argument('--version', action='version',
        help='Show the conda-build version number and exit.',
        version='conda-concourse-ci %s' % __version__)
    sp = parser.add_subparsers(title='subcommands', dest='subparser_name')
    examine_parser = sp.add_parser('examine', help='examine path for changed recipes')
    examine_parser.add_argument('base_name',
                                help="name of your project, to distinguish it from other projects")
    examine_parser.add_argument("path", default='.', nargs='?',
                        help="path in which to examine/build/test recipes")
    examine_parser.add_argument('--folders', default=[], nargs="+",
                        help="Rather than determine tree from git, specify folders to build")
    examine_parser.add_argument('--steps', type=int,
                        help=("Number of downstream steps to follow in the DAG when "
                              "computing what to test.  Used for making sure that an "
                              "update does not break downstream packages.  Set to -1 "
                              "to follow the complete dependency tree."),
                        default=0),
    examine_parser.add_argument('--max-downstream', default=5, type=int,
                        help=("Limit the total number of downstream packages built.  Only applies "
                              "if steps != 0.  Set to -1 for unlimited."))
    examine_parser.add_argument('--git-rev',
                        default='HEAD',
                        help=('start revision to examine.  If stop not '
                              'provided, changes are THIS_VAL~1..THIS_VAL'))
    examine_parser.add_argument('--stop-rev', default=None,
                        help=('stop revision to examine.  When provided,'
                              'changes are git_rev..stop_rev'))
    examine_parser.add_argument('--test', action='store_true',
                        help='test packages (instead of building AND testing them)')
    examine_parser.add_argument('--matrix-base-dir',
                                help='path to matrix configuration, if different from recipe path',
                                default=cc_conda_build.get('matrix_base_dir'))
    examine_parser.add_argument('--output-dir', help="folder where output plan and recipes live",
                                default='../output')
    examine_parser.add_argument('--channel', '-c', action='append',
                                help="Additional channel to use when building packages")
    examine_parser.add_argument('--platform-filter', '-p', action='append',
                                help="glob pattern(s) to filter build platforms.  For example, "
                                "linux* will build all platform files whose filenames start with "
                                "linux",
                                dest='platform_filters')
    examine_parser.add_argument('--worker-tag', '-t', action='append',
                                help="set worker tag(s) to limit where jobs will run.  Applies "
                                "to all jobs.  For finer control, use extra/worker_tags in "
                                "meta.yaml with selectors.",
                                dest='worker_tags')
    examine_parser.add_argument(
        '-m', '--variant-config-files',
        action="append",
        help="""Additional variant config files to add.  These yaml files can contain
        keys such as `c_compiler` and `target_platform` to form a build matrix."""
    )
    examine_parser.add_argument(
        '--no-skip-existing', help="Do not skip existing builds",
        dest="skip_existing", action="store_false"
    )

    submit_parser = sp.add_parser('submit', help="submit plan director to configured server")
    submit_parser.add_argument('base_name',
                               help="name of your project, to distinguish it from other projects")
    submit_parser.add_argument('--pipeline-name', help="name for the submitted pipeline",
                               default='{base_name} plan director')
    submit_parser.add_argument('--pipeline-file', default='plan_director.yml',
                               help="path to pipeline .yml file containing plan")
    submit_parser.add_argument('--config-root-dir',
                               help="path containing config.yml and matrix definitions")
    submit_parser.add_argument('--src-dir', help="folder where git repo of source code lives",
                               default=os.getcwd())
    submit_parser.add_argument('--private', action='store_false',
                        help='hide build logs (overall graph still shown in Concourse web view)',
                        dest='public')

    bootstrap_parser = sp.add_parser('bootstrap',
                                     help="create default configuration files to help you start")
    bootstrap_parser.add_argument('base_name',
                            help="name of your project, to distinguish it from other projects")

    one_off_parser = sp.add_parser('one-off',
                                   help="submit local recipes and plan to configured server")
    one_off_parser.add_argument('pipeline_label',
                                help="name of your project, to distinguish it from other projects")
    one_off_parser.add_argument('folders', nargs="+",
                                help=("Specify folders, relative to --recipe-root-dir, to upload "
                                      "and build"))
    one_off_parser.add_argument('--automated-pipeline',
                                action='store_true',
                                default=False,
                                help="Flag to run this one_off command as an automated pipeline. Default is False",
                                )
    one_off_parser.add_argument(
        '--branches',
        nargs='+',
        default=None,
        help=(
            "Only used when --automated_pipeline is specified. "
            "List of repository branches that recipes will be pulled from. "
            "Either pass in one branch or n number of branches where "
            "n is equal to the number of recipes you are building. "
            "The default is to use the 'automated-build' branch. "
            "Specific this option after the list of folders to avoid "
            "confusing which arguments are folders and which are branches, "
            "for example: "
            "c3i one-off folder1 folder2 --branches branch1 branch2"
        )
    )
    one_off_parser.add_argument('--recipe-root-dir', default=os.getcwd(),
                                help="path containing recipe folders to upload")
    one_off_parser.add_argument('--config-root-dir',
                                help="path containing config.yml and matrix definitions",
                                default=cc_conda_build.get('matrix_base_dir'))
    one_off_parser.add_argument('--private', action='store_false',
                        help='hide build logs (overall graph still shown in Concourse web view)',
                        dest='public')
    one_off_parser.add_argument('--channel', '-c', action='append',
                                help="Additional channel to use when building packages")
    one_off_parser.add_argument('--platform-filter', '-p', action='append',
                                help="glob pattern(s) to filter build platforms.  For example, "
                                "linux* will build all platform files whose filenames start with "
                                "linux", dest='platform_filters')
    one_off_parser.add_argument('--worker-tag', '-t', action='append',
                                help="set worker tag(s) to limit where jobs will run.  Applies "
                                "to all jobs.  For finer control, use extra/worker_tags in "
                                "meta.yaml with selectors.",
                                dest='worker_tags')
    one_off_parser.add_argument(
        '-m', '--variant-config-files',
        action="append",
        help="""Additional variant config files to add.  These yaml files can contain
        keys such as `c_compiler` and `target_platform` to form a build matrix."""
    )
    one_off_parser.add_argument('--output-dir', help=("folder where output plan and recipes live."
                                "Defaults to temp folder.  Set to something to save output."))
    one_off_parser.add_argument(
        '--append-file',
        help="""Append data in meta.yaml with fields from this file.  Jinja2 is not done
        on appended fields""",
        dest='append_sections_file',
    )
    one_off_parser.add_argument(
        '--clobber-file',
        help="""Clobber data in meta.yaml with fields from this file.  Jinja2 is not done
        on clobbered fields.""",
        dest='clobber_sections_file',
    )
    one_off_parser.add_argument(
        '--no-skip-existing', help="Do not skip existing builds",
        dest="skip_existing", action="store_false"
    )
    one_off_parser.add_argument(
        '--use_lock_pool',
        help="Use the lock pool to limit jobs. DO NOT USE, WORKS POORLY",
        dest="use_lock_pool", action="store_true"
    )
    one_off_parser.add_argument(
        '--use-repo-access',
        help="Pass the repo access credentials to the workers",
        action="store_true",
    )

    batch_parser = sp.add_parser('batch', help="submit a batch of one-off jobs.")
    batch_parser.add_argument(
        'batch_file',
        help="""File describing batch job.  Each lines defines a seperate
        one-off job.  List one or more folders on each line.  Job specific
        arguments can be specified after a ';' using param=value, multiple
        arguments are seperated by a ','.  For example:

            recipe-feedstock; channel=conda-forge,clobber_sections_file=clobber.yaml
        """)

    # batch specific arguments
    batch_parser.add_argument(
        '--max-builds', default=6, type=int,
        help=("maximum number of activate builds allowed before starting a new"
              "job, default is 6"))
    batch_parser.add_argument(
        '--poll-time', default=120, type=int,
        help=("time in seconds between checking concourse server for active "
              "builds, default is 120 seconds."))
    batch_parser.add_argument(
        '--build-lookback', default=500, type=int,
        help="number of builds to examine for active builds, default is 500")
    batch_parser.add_argument(
        '--label-prefix', default='autobot_',
        help="prefix for pipeline labels, default is autobot_")

    # one-off arguments
    batch_parser.add_argument('--recipe-root-dir', default=os.getcwd(),
                                help="path containing recipe folders to upload")
    batch_parser.add_argument('--config-root-dir',
                                help="path containing config.yml and matrix definitions",
                                default=cc_conda_build.get('matrix_base_dir'))
    batch_parser.add_argument('--private', action='store_false',
                        help='hide build logs (overall graph still shown in Concourse web view)',
                        dest='public')
    batch_parser.add_argument('--channel', '-c', action='append',
                                help="Additional channel to use when building packages")
    batch_parser.add_argument('--platform-filter', '-p', action='append',
                                help="glob pattern(s) to filter build platforms.  For example, "
                                "linux* will build all platform files whose filenames start with "
                                "linux", dest='platform_filters')
    batch_parser.add_argument('--worker-tag', '-t', action='append',
                                help="set worker tag(s) to limit where jobs will run.  Applies "
                                "to all jobs.  For finer control, use extra/worker_tags in "
                                "meta.yaml with selectors.",
                                dest='worker_tags')
    batch_parser.add_argument(
        '-m', '--variant-config-files',
        action="append",
        help="""Additional variant config files to add.  These yaml files can contain
        keys such as `c_compiler` and `target_platform` to form a build matrix."""
    )
    batch_parser.add_argument('--output-dir', help=("folder where output plan and recipes live."
                              "Defaults to temp folder.  Set to something to save output."))
    batch_parser.add_argument(
        '--append-file',
        help="""Append data in meta.yaml with fields from this file.  Jinja2 is not done
        on appended fields""",
        dest='append_sections_file',
    )
    batch_parser.add_argument(
        '--clobber-file',
        help="""Clobber data in meta.yaml with fields from this file.  Jinja2 is not done
        on clobbered fields.""",
        dest='clobber_sections_file',
    )
    batch_parser.add_argument(
        '--no-skip-existing', help="Do not skip existing builds",
        dest="skip_existing", action="store_false"
    )
    batch_parser.add_argument(
        '--use_lock_pool', help="Use the lock pool to limit jobs",
        dest="use_lock_pool", action="store_true"
    )
    batch_parser.add_argument(
        '--use-repo-access',
        help="Pass the repo access credentials to the workers",
        action="store_true",
    )

    rm_parser = sp.add_parser('rm', help='remove pipelines from server')
    rm_parser.add_argument('pipeline_names', nargs="+",
                           help=("Specify pipeline names on server to remove"))
    rm_parser.add_argument('--config-root-dir',
                           help="path containing config.yml and matrix definitions",
                           default=cc_conda_build.get('matrix_base_dir'))
    rm_parser.add_argument('--do-it-dammit', '-y', help="YOLO", action="store_true")

    trigger_parser = sp.add_parser('trigger', help='trigger (failed) jobs of a pipeline')
    trigger_parser.add_argument('pipeline_names', nargs='+',
                           help=("Specify pipeline names to trigger"))
    trigger_parser.add_argument('--config-root-dir',
                           help="path containing config.yml and matrix definitions",
                           default=cc_conda_build.get('matrix_base_dir'))
    trigger_parser.add_argument('--all', dest="trigger_all",
                           action="store_true", help="trigger all jobs")

    abort_parser = sp.add_parser('abort', help='abort jobs of a pipeline')
    abort_parser.add_argument('pipeline_names', nargs='+',
                           help=("Specify pipeline names to abort"))
    abort_parser.add_argument('--config-root-dir',
                           help="path containing config.yml and matrix definitions",
                           default=cc_conda_build.get('matrix_base_dir'))

    return parser.parse_known_args(parse_this)

def main(args=None):
    if not args:
        args, pass_throughs = parse_args()
    else:
        args, pass_throughs = parse_args(args)

    if args.debug:
        logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.INFO)

    if args.subparser_name == 'submit':
        args_dict = args.__dict__
        if not args_dict.get('config_root_dir'):
            args_dict['config_root_dir'] = args_dict['base_name']
        execute.submit(pass_throughs=pass_throughs, **args_dict)
    elif args.subparser_name == 'bootstrap':
        execute.bootstrap(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'examine':
        execute.compute_builds(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'one-off':
        execute.submit_one_off(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'batch':
        execute.submit_batch(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'rm':
        execute.rm_pipeline(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'trigger':
        execute.trigger_pipeline(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'abort':
        execute.abort_pipeline(pass_throughs=pass_throughs, **args.__dict__)
    else:
        # this is here so that if future subcommands are added, you don't forget to add a bit
        #     here to enable them.
        raise NotImplementedError("Command {} is not implemented".format(args.subparser_name))
<commit_msg>add pipeline_label to --branches help text<commit_after>import argparse
import logging
import os

from conda_build.conda_interface import cc_conda_build

from conda_concourse_ci import __version__, execute

def parse_args(parse_this=None):
    parser = argparse.ArgumentParser()
    parser.add_argument('--debug', action='store_true')
    parser.add_argument('--version', action='version',
        help='Show the conda-build version number and exit.',
        version='conda-concourse-ci %s' % __version__)
    sp = parser.add_subparsers(title='subcommands', dest='subparser_name')
    examine_parser = sp.add_parser('examine', help='examine path for changed recipes')
    examine_parser.add_argument('base_name',
                                help="name of your project, to distinguish it from other projects")
    examine_parser.add_argument("path", default='.', nargs='?',
                        help="path in which to examine/build/test recipes")
    examine_parser.add_argument('--folders', default=[], nargs="+",
                        help="Rather than determine tree from git, specify folders to build")
    examine_parser.add_argument('--steps', type=int,
                        help=("Number of downstream steps to follow in the DAG when "
                              "computing what to test.  Used for making sure that an "
                              "update does not break downstream packages.  Set to -1 "
                              "to follow the complete dependency tree."),
                        default=0),
    examine_parser.add_argument('--max-downstream', default=5, type=int,
                        help=("Limit the total number of downstream packages built.  Only applies "
                              "if steps != 0.  Set to -1 for unlimited."))
    examine_parser.add_argument('--git-rev',
                        default='HEAD',
                        help=('start revision to examine.  If stop not '
                              'provided, changes are THIS_VAL~1..THIS_VAL'))
    examine_parser.add_argument('--stop-rev', default=None,
                        help=('stop revision to examine.  When provided,'
                              'changes are git_rev..stop_rev'))
    examine_parser.add_argument('--test', action='store_true',
                        help='test packages (instead of building AND testing them)')
    examine_parser.add_argument('--matrix-base-dir',
                                help='path to matrix configuration, if different from recipe path',
                                default=cc_conda_build.get('matrix_base_dir'))
    examine_parser.add_argument('--output-dir', help="folder where output plan and recipes live",
                                default='../output')
    examine_parser.add_argument('--channel', '-c', action='append',
                                help="Additional channel to use when building packages")
    examine_parser.add_argument('--platform-filter', '-p', action='append',
                                help="glob pattern(s) to filter build platforms.  For example, "
                                "linux* will build all platform files whose filenames start with "
                                "linux",
                                dest='platform_filters')
    examine_parser.add_argument('--worker-tag', '-t', action='append',
                                help="set worker tag(s) to limit where jobs will run.  Applies "
                                "to all jobs.  For finer control, use extra/worker_tags in "
                                "meta.yaml with selectors.",
                                dest='worker_tags')
    examine_parser.add_argument(
        '-m', '--variant-config-files',
        action="append",
        help="""Additional variant config files to add.  These yaml files can contain
        keys such as `c_compiler` and `target_platform` to form a build matrix."""
    )
    examine_parser.add_argument(
        '--no-skip-existing', help="Do not skip existing builds",
        dest="skip_existing", action="store_false"
    )

    submit_parser = sp.add_parser('submit', help="submit plan director to configured server")
    submit_parser.add_argument('base_name',
                               help="name of your project, to distinguish it from other projects")
    submit_parser.add_argument('--pipeline-name', help="name for the submitted pipeline",
                               default='{base_name} plan director')
    submit_parser.add_argument('--pipeline-file', default='plan_director.yml',
                               help="path to pipeline .yml file containing plan")
    submit_parser.add_argument('--config-root-dir',
                               help="path containing config.yml and matrix definitions")
    submit_parser.add_argument('--src-dir', help="folder where git repo of source code lives",
                               default=os.getcwd())
    submit_parser.add_argument('--private', action='store_false',
                        help='hide build logs (overall graph still shown in Concourse web view)',
                        dest='public')

    bootstrap_parser = sp.add_parser('bootstrap',
                                     help="create default configuration files to help you start")
    bootstrap_parser.add_argument('base_name',
                            help="name of your project, to distinguish it from other projects")

    one_off_parser = sp.add_parser('one-off',
                                   help="submit local recipes and plan to configured server")
    one_off_parser.add_argument('pipeline_label',
                                help="name of your project, to distinguish it from other projects")
    one_off_parser.add_argument('folders', nargs="+",
                                help=("Specify folders, relative to --recipe-root-dir, to upload "
                                      "and build"))
    one_off_parser.add_argument('--automated-pipeline',
                                action='store_true',
                                default=False,
                                help="Flag to run this one_off command as an automated pipeline. Default is False",
                                )
    one_off_parser.add_argument(
        '--branches',
        nargs='+',
        default=None,
        help=(
            "Only used when --automated_pipeline is specified. "
            "List of repository branches that recipes will be pulled from. "
            "Either pass in one branch or n number of branches where "
            "n is equal to the number of recipes you are building. "
            "The default is to use the 'automated-build' branch. "
            "Specific this option after the list of folders to avoid "
            "confusing which arguments are folders and which are branches, "
            "for example: "
            "c3i one-off pipeline_label folder1 folder2 --branches branch1 branch2"
        )
    )
    one_off_parser.add_argument('--recipe-root-dir', default=os.getcwd(),
                                help="path containing recipe folders to upload")
    one_off_parser.add_argument('--config-root-dir',
                                help="path containing config.yml and matrix definitions",
                                default=cc_conda_build.get('matrix_base_dir'))
    one_off_parser.add_argument('--private', action='store_false',
                        help='hide build logs (overall graph still shown in Concourse web view)',
                        dest='public')
    one_off_parser.add_argument('--channel', '-c', action='append',
                                help="Additional channel to use when building packages")
    one_off_parser.add_argument('--platform-filter', '-p', action='append',
                                help="glob pattern(s) to filter build platforms.  For example, "
                                "linux* will build all platform files whose filenames start with "
                                "linux", dest='platform_filters')
    one_off_parser.add_argument('--worker-tag', '-t', action='append',
                                help="set worker tag(s) to limit where jobs will run.  Applies "
                                "to all jobs.  For finer control, use extra/worker_tags in "
                                "meta.yaml with selectors.",
                                dest='worker_tags')
    one_off_parser.add_argument(
        '-m', '--variant-config-files',
        action="append",
        help="""Additional variant config files to add.  These yaml files can contain
        keys such as `c_compiler` and `target_platform` to form a build matrix."""
    )
    one_off_parser.add_argument('--output-dir', help=("folder where output plan and recipes live."
                                "Defaults to temp folder.  Set to something to save output."))
    one_off_parser.add_argument(
        '--append-file',
        help="""Append data in meta.yaml with fields from this file.  Jinja2 is not done
        on appended fields""",
        dest='append_sections_file',
    )
    one_off_parser.add_argument(
        '--clobber-file',
        help="""Clobber data in meta.yaml with fields from this file.  Jinja2 is not done
        on clobbered fields.""",
        dest='clobber_sections_file',
    )
    one_off_parser.add_argument(
        '--no-skip-existing', help="Do not skip existing builds",
        dest="skip_existing", action="store_false"
    )
    one_off_parser.add_argument(
        '--use_lock_pool',
        help="Use the lock pool to limit jobs. DO NOT USE, WORKS POORLY",
        dest="use_lock_pool", action="store_true"
    )
    one_off_parser.add_argument(
        '--use-repo-access',
        help="Pass the repo access credentials to the workers",
        action="store_true",
    )

    batch_parser = sp.add_parser('batch', help="submit a batch of one-off jobs.")
    batch_parser.add_argument(
        'batch_file',
        help="""File describing batch job.  Each lines defines a seperate
        one-off job.  List one or more folders on each line.  Job specific
        arguments can be specified after a ';' using param=value, multiple
        arguments are seperated by a ','.  For example:

            recipe-feedstock; channel=conda-forge,clobber_sections_file=clobber.yaml
        """)

    # batch specific arguments
    batch_parser.add_argument(
        '--max-builds', default=6, type=int,
        help=("maximum number of activate builds allowed before starting a new"
              "job, default is 6"))
    batch_parser.add_argument(
        '--poll-time', default=120, type=int,
        help=("time in seconds between checking concourse server for active "
              "builds, default is 120 seconds."))
    batch_parser.add_argument(
        '--build-lookback', default=500, type=int,
        help="number of builds to examine for active builds, default is 500")
    batch_parser.add_argument(
        '--label-prefix', default='autobot_',
        help="prefix for pipeline labels, default is autobot_")

    # one-off arguments
    batch_parser.add_argument('--recipe-root-dir', default=os.getcwd(),
                                help="path containing recipe folders to upload")
    batch_parser.add_argument('--config-root-dir',
                                help="path containing config.yml and matrix definitions",
                                default=cc_conda_build.get('matrix_base_dir'))
    batch_parser.add_argument('--private', action='store_false',
                        help='hide build logs (overall graph still shown in Concourse web view)',
                        dest='public')
    batch_parser.add_argument('--channel', '-c', action='append',
                                help="Additional channel to use when building packages")
    batch_parser.add_argument('--platform-filter', '-p', action='append',
                                help="glob pattern(s) to filter build platforms.  For example, "
                                "linux* will build all platform files whose filenames start with "
                                "linux", dest='platform_filters')
    batch_parser.add_argument('--worker-tag', '-t', action='append',
                                help="set worker tag(s) to limit where jobs will run.  Applies "
                                "to all jobs.  For finer control, use extra/worker_tags in "
                                "meta.yaml with selectors.",
                                dest='worker_tags')
    batch_parser.add_argument(
        '-m', '--variant-config-files',
        action="append",
        help="""Additional variant config files to add.  These yaml files can contain
        keys such as `c_compiler` and `target_platform` to form a build matrix."""
    )
    batch_parser.add_argument('--output-dir', help=("folder where output plan and recipes live."
                              "Defaults to temp folder.  Set to something to save output."))
    batch_parser.add_argument(
        '--append-file',
        help="""Append data in meta.yaml with fields from this file.  Jinja2 is not done
        on appended fields""",
        dest='append_sections_file',
    )
    batch_parser.add_argument(
        '--clobber-file',
        help="""Clobber data in meta.yaml with fields from this file.  Jinja2 is not done
        on clobbered fields.""",
        dest='clobber_sections_file',
    )
    batch_parser.add_argument(
        '--no-skip-existing', help="Do not skip existing builds",
        dest="skip_existing", action="store_false"
    )
    batch_parser.add_argument(
        '--use_lock_pool', help="Use the lock pool to limit jobs",
        dest="use_lock_pool", action="store_true"
    )
    batch_parser.add_argument(
        '--use-repo-access',
        help="Pass the repo access credentials to the workers",
        action="store_true",
    )

    rm_parser = sp.add_parser('rm', help='remove pipelines from server')
    rm_parser.add_argument('pipeline_names', nargs="+",
                           help=("Specify pipeline names on server to remove"))
    rm_parser.add_argument('--config-root-dir',
                           help="path containing config.yml and matrix definitions",
                           default=cc_conda_build.get('matrix_base_dir'))
    rm_parser.add_argument('--do-it-dammit', '-y', help="YOLO", action="store_true")

    trigger_parser = sp.add_parser('trigger', help='trigger (failed) jobs of a pipeline')
    trigger_parser.add_argument('pipeline_names', nargs='+',
                           help=("Specify pipeline names to trigger"))
    trigger_parser.add_argument('--config-root-dir',
                           help="path containing config.yml and matrix definitions",
                           default=cc_conda_build.get('matrix_base_dir'))
    trigger_parser.add_argument('--all', dest="trigger_all",
                           action="store_true", help="trigger all jobs")

    abort_parser = sp.add_parser('abort', help='abort jobs of a pipeline')
    abort_parser.add_argument('pipeline_names', nargs='+',
                           help=("Specify pipeline names to abort"))
    abort_parser.add_argument('--config-root-dir',
                           help="path containing config.yml and matrix definitions",
                           default=cc_conda_build.get('matrix_base_dir'))

    return parser.parse_known_args(parse_this)

def main(args=None):
    if not args:
        args, pass_throughs = parse_args()
    else:
        args, pass_throughs = parse_args(args)

    if args.debug:
        logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.INFO)

    if args.subparser_name == 'submit':
        args_dict = args.__dict__
        if not args_dict.get('config_root_dir'):
            args_dict['config_root_dir'] = args_dict['base_name']
        execute.submit(pass_throughs=pass_throughs, **args_dict)
    elif args.subparser_name == 'bootstrap':
        execute.bootstrap(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'examine':
        execute.compute_builds(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'one-off':
        execute.submit_one_off(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'batch':
        execute.submit_batch(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'rm':
        execute.rm_pipeline(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'trigger':
        execute.trigger_pipeline(pass_throughs=pass_throughs, **args.__dict__)
    elif args.subparser_name == 'abort':
        execute.abort_pipeline(pass_throughs=pass_throughs, **args.__dict__)
    else:
        # this is here so that if future subcommands are added, you don't forget to add a bit
        #     here to enable them.
        raise NotImplementedError("Command {} is not implemented".format(args.subparser_name))
<|endoftext|>


<commit_before># coding=utf-8

"""This module, c_process.py, is an API for managing and interacting with a single C process."""

# Needed for running a C process and sending data to it.
from subprocess import Popen, PIPE

class CProcess(object):
	"""Represents a single running C process."""

	def __init__(self, c_executable_path, flags):
		self._c_executable_path = c_executable_path
		self._shell_string = self._c_executable_path
		if type(flags) == list:
			for f in flags:
				self._shell_string += ' ' + f + ' '
		self._c_process = None

	def _start_process(self, send_input):
		"""Starts the C process."""
		if send_input:
			self._c_process = Popen(self._shell_string, shell=True, stdin=PIPE)
		else:
			self._c_process = Popen(self._shell_string, shell=True, stdin=PIPE)

	def _wait_for_process_finish(self):
		"""Lets the process finish."""
		self.c_process.wait()
		output_stdout, output_stderr = self.c_process.communicate()
		return output_stdout, output_stderr

	def run_process_and_only_get_output(self):
		"""Runs a process without providing any input."""
		self._start_process(False)
		return self._wait_for_process_finish()

	def run_process_with_input(self, data_that_is_list_of_lists):
		"""Runs a process with provided input."""
		#self.start_process(True)
		# TODO : THIS FUNCTION!!!

'''
class CProcess(object):
	"""Represents a single running C process."""

	def __init__(self, flags):
		self._path_source_file = path_source_file
		self._path_output_file = path_output_file
		self._output_file_name = output_file_name
		self._flags            = flags

		self._run_string = output_file_name
		if len(flags) > 0:
			for f in flags:
				self._run_string += ' ' + f

		self.c_process     = None
		self.output_stdout = None
		self.output_stderr = None

	def run_process(self):
		"""Runs the c_process."""
		self.c_process = Popen(self._run_string, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
		self.c_process.wait()
		self.output_stdout, self.output_stderr = self.c_process.communicate()

	def compile_process(self):
		"""Compiles this c program."""
'''<commit_msg>finance updating 82<commit_after># coding=utf-8

"""This module, c_process.py, is an API for managing and interacting with a single C process."""

# Needed for running a C process and sending data to it.
from subprocess import Popen, PIPE

class CProcess(object):
	"""Represents a single running C process."""

	def __init__(self, c_executable_path, flags):
		self._c_executable_path = c_executable_path
		self._shell_string = self._c_executable_path
		if type(flags) == list:
			for f in flags:
				self._shell_string += ' ' + f + ' '
		self._c_process = None

	def _start_process(self, send_input):
		"""Starts the C process."""
		if send_input:
			self._c_process = Popen(self._shell_string, shell=True, stdin=PIPE)
		else:
			self._c_process = Popen(self._shell_string, shell=True, stdin=PIPE)

	def _wait_for_process_finish(self):
		"""Lets the process finish."""
		self._c_process.wait()
		output_stdout, output_stderr = self._c_process.communicate()
		return output_stdout, output_stderr

	def run_process_and_only_get_output(self):
		"""Runs a process without providing any input."""
		self._start_process(False)
		return self._wait_for_process_finish()

	def run_process_with_input(self, data_that_is_list_of_lists):
		"""Runs a process with provided input."""
		#self.start_process(True)
		# TODO : THIS FUNCTION!!!

'''
class CProcess(object):
	"""Represents a single running C process."""

	def __init__(self, flags):
		self._path_source_file = path_source_file
		self._path_output_file = path_output_file
		self._output_file_name = output_file_name
		self._flags            = flags

		self._run_string = output_file_name
		if len(flags) > 0:
			for f in flags:
				self._run_string += ' ' + f

		self.c_process     = None
		self.output_stdout = None
		self.output_stderr = None

	def run_process(self):
		"""Runs the c_process."""
		self.c_process = Popen(self._run_string, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
		self.c_process.wait()
		self.output_stdout, self.output_stderr = self.c_process.communicate()

	def compile_process(self):
		"""Compiles this c program."""
'''<|endoftext|>