<commit_before>#copyright ReportLab Inc. 2000-2001
#see license.txt for license details
#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/platypus/tableofcontents.py?cvsroot=reportlab
#$Header: /tmp/reportlab/reportlab/platypus/tableofcontents.py,v 1.4 2001/11/27 16:49:58 andy_robinson Exp $
"""
This module defines a single TableOfContents() class that can be used to
create automatically a table of tontents for Platypus documents like
this:

    story = []
    toc = TableOfContents()
    story.append(toc)
    # some heading paragraphs here...
    doc = MyTemplate(path)
    doc.multiBuild(story)

The data needed to create the table is a list of (level, text, pageNum)
triplets, plus some paragraph styles for each level of the table itself.
The triplets will usually be created in a document template's method
like afterFlowable(), making notification calls using the notify()
method with appropriate data like this:

    (level, text, pageNum) = ...
    self.notify('TOCEntry', (level, text, pageNum))

As the table of contents need at least two passes over the Platypus
story which is why the moultiBuild0() method must be called.

The level<NUMBER>ParaStyle variables are the paragraph styles used
to format the entries in the table of contents. Their indentation
is calculated like this: each entry starts at a multiple of some
constant named delta. If one entry spans more than one line, all
lines after the first are indented by the same constant named
epsilon.
"""
import string

from reportlab.lib import enums
from reportlab.lib.units import cm
from reportlab.lib.styles import ParagraphStyle
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.doctemplate import IndexingFlowable
from reportlab.platypus.tables import TableStyle, Table

# Default paragraph styles for tables of contents.
# (This could also be generated automatically or even
# on-demand if it is not known how many levels the
# TOC will finally need to display...)

delta = 1*cm
epsilon = 0.5*cm

levelZeroParaStyle = \
    ParagraphStyle(name='LevelZero',
                   fontName='Times-Roman',
                   fontSize=10,
                   leading=11,
                   firstLineIndent = -epsilon,
                   leftIndent = 0*delta + epsilon)

levelOneParaStyle = \
    ParagraphStyle(name='LevelOne',
                   parent = levelZeroParaStyle,
                   leading=11,
                   firstLineIndent = -epsilon,
                   leftIndent = 1*delta + epsilon)

levelTwoParaStyle = \
    ParagraphStyle(name='LevelTwo',
                   parent = levelOneParaStyle,
                   leading=11,
                   firstLineIndent = -epsilon,
                   leftIndent = 2*delta + epsilon)

levelThreeParaStyle = \
    ParagraphStyle(name='LevelThree',
                   parent = levelTwoParaStyle,
                   leading=11,
                   firstLineIndent = -epsilon,
                   leftIndent = 3*delta + epsilon)

levelFourParaStyle = \
    ParagraphStyle(name='LevelFour',
                   parent = levelTwoParaStyle,
                   leading=11,
                   firstLineIndent = -epsilon,
                   leftIndent = 4*delta + epsilon)

defaultTableStyle = \
    TableStyle([('VALIGN', (0,0), (-1,-1), 'TOP')])

class TableOfContents(IndexingFlowable):
    """This creates a formatted table of contents.

    It presumes a correct block of data is passed in.
    The data block contains a list of (level, text, pageNumber)
    triplets.  You can supply a paragraph style for each level
    (starting at zero).
    """

    def __init__(self):
        self.entries = []
        self.rightColumnWidth = 72
        self.levelStyles = [levelZeroParaStyle,
                            levelOneParaStyle,
                            levelTwoParaStyle,
                            levelThreeParaStyle,
                            levelFourParaStyle]
        self.tableStyle = defaultTableStyle
        self._table = None
        self._entries = []
        self._lastEntries = []

    def beforeBuild(self):
        # keep track of the last run
        self._lastEntries = self._entries[:]
        self.clearEntries()

    def isIndexing(self):
        return 1

    def isSatisfied(self):
        return (self._entries == self._lastEntries)

    def notify(self, kind, stuff):
        """The notification hook called to register all kinds of events.

        Here we are interested in 'TOCEntry' events only.
        """
        if kind == 'TOCEntry':
            (level, text, pageNum) = stuff
            self.addEntry(level, text, pageNum)

    def clearEntries(self):
        self._entries = []

    def addEntry(self, level, text, pageNum):
        """Adds one entry to the table of contents.

        This allows incremental buildup by a doctemplate.
        Requires that enough styles are defined."""

        assert type(level) == type(1), "Level must be an integer"
        assert level < len(self.levelStyles), \
               "Table of contents must have a style defined " \
               "for paragraph level %d before you add an entry" % level

        self._entries.append((level, text, pageNum))

    def addEntries(self, listOfEntries):
        """Bulk creation of entries in the table of contents.

        If you knew the titles but not the page numbers, you could
        supply them to get sensible output on the first run."""

        for (level, text, pageNum) in listOfEntries:
            self.addEntry(level, text, pageNum)

    def wrap(self, availWidth, availHeight):
        "All table properties should be known by now."

        widths = (availWidth - self.rightColumnWidth,
                  self.rightColumnWidth)

        # makes an internal table which does all the work.
        # we draw the LAST RUN's entries!  If there are
        # none, we make some dummy data to keep the table
        # from complaining
        if len(self._lastEntries) == 0:
            _tempEntries = [(0,'Placeholder for table of contents',0)]
        else:
            _tempEntries = self._lastEntries

        tableData = []
        for (level, text, pageNum) in _tempEntries:
            leftColStyle = self.levelStyles[level]
            #right col style is right aligned
            rightColStyle = ParagraphStyle(name='leftColLevel%d' % level,
                                           parent=leftColStyle,
                                           leftIndent=0,
                                           alignment=enums.TA_RIGHT)
            leftPara = Paragraph(text, leftColStyle)
            rightPara = Paragraph(str(pageNum), rightColStyle)
            tableData.append([leftPara, rightPara])

        self._table = Table(tableData, colWidths=widths,
                            style=self.tableStyle)

        self.width, self.height = self._table.wrap(availWidth, availHeight)
        return (self.width, self.height)

    def split(self, availWidth, availHeight):
        """At this stage we do not care about splitting the entries,
        we will just return a list of platypus tables.  Presumably the
        calling app has a pointer to the original TableOfContents object;
        Platypus just sees tables.
        """
        return self._table.split(availWidth, availHeight)

    def drawOn(self, canvas, x, y, _sW=0):
        """Don't do this at home!  The standard calls for implementing
        draw(); we are hooking this in order to delegate ALL the drawing
        work to the embedded table object.
        """
        self._table.drawOn(canvas, x, y, _sW)

class SimpleIndex(IndexingFlowable):
    """This creates a very simple index.

    Entries have a string key, and appear with a page number on
    the right.  Prototype for more sophisticated multi-level index."""
    def __init__(self):
        #keep stuff in a dictionary while building
        self._entries = {}
        self._lastEntries = {}
        self._table = None
        self.textStyle = ParagraphStyle(name='index',
                                        fontName='Times-Roman',
                                        fontSize=12)
    def isIndexing(self):
        return 1

    def isSatisfied(self):
        return (self._entries == self._lastEntries)

    def beforeBuild(self):
        # keep track of the last run
        self._lastEntries = self._entries.copy()
        self.clearEntries()

    def clearEntries(self):
        self._entries = {}

    def notify(self, kind, stuff):
        """The notification hook called to register all kinds of events.

        Here we are interested in 'IndexEntry' events only.
        """
        if kind == 'IndexEntry':
            (text, pageNum) = stuff
            self.addEntry(text, pageNum)

    def addEntry(self, text, pageNum):
        """Allows incremental buildup"""
        if self._entries.has_key(text):
            self._entries[text].append(str(pageNum))
        else:
            self._entries[text] = [pageNum]

    def split(self, availWidth, availHeight):
        """At this stage we do not care about splitting the entries,
        we will just return a list of platypus tables.  Presumably the
        calling app has a pointer to the original TableOfContents object;
        Platypus just sees tables.
        """
        return self._table.split(availWidth, availHeight)

    def wrap(self, availWidth, availHeight):
        "All table properties should be known by now."
        # makes an internal table which does all the work.
        # we draw the LAST RUN's entries!  If there are
        # none, we make some dummy data to keep the table
        # from complaining
        if len(self._lastEntries) == 0:
            _tempEntries = [('Placeholder for index',[0,1,2])]
        else:
            _tempEntries = self._lastEntries.items()
            _tempEntries.sort()

        tableData = []
        for (text, pageNumbers) in _tempEntries:
            #right col style is right aligned
            allText = text + ': ' + string.join(map(str, pageNumbers), ', ')
            para = Paragraph(allText, self.textStyle)
            tableData.append([para])

        self._table = Table(tableData, colWidths=[availWidth])

        self.width, self.height = self._table.wrap(availWidth, availHeight)
        return (self.width, self.height)

    def drawOn(self, canvas, x, y, _sW=0):
        """Don't do this at home!  The standard calls for implementing
        draw(); we are hooking this in order to delegate ALL the drawing
        work to the embedded table object.
        """
        self._table.drawOn(canvas, x, y, _sW)

class ReferenceText(IndexingFlowable):
    """Fakery to illustrate how a reference would work if we could
    put it in a paragraph."""
    def __init__(self, textPattern, targetKey):
        self.textPattern = textPattern
        self.target = targetKey
        self.paraStyle = ParagraphStyle('tmp')
        self._lastPageNum = None
        self._pageNum = -999
        self._para = None

    def beforeBuild(self):
        self._lastPageNum = self._pageNum

    def notify(self, kind, stuff):
        if kind == 'Target':
            (key, pageNum) = stuff
            if key == self.target:
                self._pageNum = pageNum

    def wrap(self, availWidth, availHeight):
        text = self.textPattern % self._lastPageNum
        self._para = Paragraph(text, self.paraStyle)
        return self._para.wrap(availWidth, availHeight)

    def drawOn(self, canvas, x, y, _sW=0):
        self._para.drawOn(canvas, x, y, _sW)

        <commit_msg>Propogate the canvas<commit_after>#copyright ReportLab Inc. 2000-2001
#see license.txt for license details
#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/platypus/tableofcontents.py?cvsroot=reportlab
#$Header: /tmp/reportlab/reportlab/platypus/tableofcontents.py,v 1.5 2002/01/18 12:46:49 rgbecker Exp $
"""
This module defines a single TableOfContents() class that can be used to
create automatically a table of tontents for Platypus documents like
this:

    story = []
    toc = TableOfContents()
    story.append(toc)
    # some heading paragraphs here...
    doc = MyTemplate(path)
    doc.multiBuild(story)

The data needed to create the table is a list of (level, text, pageNum)
triplets, plus some paragraph styles for each level of the table itself.
The triplets will usually be created in a document template's method
like afterFlowable(), making notification calls using the notify()
method with appropriate data like this:

    (level, text, pageNum) = ...
    self.notify('TOCEntry', (level, text, pageNum))

As the table of contents need at least two passes over the Platypus
story which is why the moultiBuild0() method must be called.

The level<NUMBER>ParaStyle variables are the paragraph styles used
to format the entries in the table of contents. Their indentation
is calculated like this: each entry starts at a multiple of some
constant named delta. If one entry spans more than one line, all
lines after the first are indented by the same constant named
epsilon.
"""
import string

from reportlab.lib import enums
from reportlab.lib.units import cm
from reportlab.lib.styles import ParagraphStyle
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.doctemplate import IndexingFlowable
from reportlab.platypus.tables import TableStyle, Table

# Default paragraph styles for tables of contents.
# (This could also be generated automatically or even
# on-demand if it is not known how many levels the
# TOC will finally need to display...)

delta = 1*cm
epsilon = 0.5*cm

levelZeroParaStyle = \
    ParagraphStyle(name='LevelZero',
                   fontName='Times-Roman',
                   fontSize=10,
                   leading=11,
                   firstLineIndent = -epsilon,
                   leftIndent = 0*delta + epsilon)

levelOneParaStyle = \
    ParagraphStyle(name='LevelOne',
                   parent = levelZeroParaStyle,
                   leading=11,
                   firstLineIndent = -epsilon,
                   leftIndent = 1*delta + epsilon)

levelTwoParaStyle = \
    ParagraphStyle(name='LevelTwo',
                   parent = levelOneParaStyle,
                   leading=11,
                   firstLineIndent = -epsilon,
                   leftIndent = 2*delta + epsilon)

levelThreeParaStyle = \
    ParagraphStyle(name='LevelThree',
                   parent = levelTwoParaStyle,
                   leading=11,
                   firstLineIndent = -epsilon,
                   leftIndent = 3*delta + epsilon)

levelFourParaStyle = \
    ParagraphStyle(name='LevelFour',
                   parent = levelTwoParaStyle,
                   leading=11,
                   firstLineIndent = -epsilon,
                   leftIndent = 4*delta + epsilon)

defaultTableStyle = \
    TableStyle([('VALIGN', (0,0), (-1,-1), 'TOP')])

class TableOfContents(IndexingFlowable):
    """This creates a formatted table of contents.

    It presumes a correct block of data is passed in.
    The data block contains a list of (level, text, pageNumber)
    triplets.  You can supply a paragraph style for each level
    (starting at zero).
    """

    def __init__(self):
        self.entries = []
        self.rightColumnWidth = 72
        self.levelStyles = [levelZeroParaStyle,
                            levelOneParaStyle,
                            levelTwoParaStyle,
                            levelThreeParaStyle,
                            levelFourParaStyle]
        self.tableStyle = defaultTableStyle
        self._table = None
        self._entries = []
        self._lastEntries = []

    def beforeBuild(self):
        # keep track of the last run
        self._lastEntries = self._entries[:]
        self.clearEntries()

    def isIndexing(self):
        return 1

    def isSatisfied(self):
        return (self._entries == self._lastEntries)

    def notify(self, kind, stuff):
        """The notification hook called to register all kinds of events.

        Here we are interested in 'TOCEntry' events only.
        """
        if kind == 'TOCEntry':
            (level, text, pageNum) = stuff
            self.addEntry(level, text, pageNum)

    def clearEntries(self):
        self._entries = []

    def addEntry(self, level, text, pageNum):
        """Adds one entry to the table of contents.

        This allows incremental buildup by a doctemplate.
        Requires that enough styles are defined."""

        assert type(level) == type(1), "Level must be an integer"
        assert level < len(self.levelStyles), \
               "Table of contents must have a style defined " \
               "for paragraph level %d before you add an entry" % level

        self._entries.append((level, text, pageNum))

    def addEntries(self, listOfEntries):
        """Bulk creation of entries in the table of contents.

        If you knew the titles but not the page numbers, you could
        supply them to get sensible output on the first run."""

        for (level, text, pageNum) in listOfEntries:
            self.addEntry(level, text, pageNum)

    def wrap(self, availWidth, availHeight):
        "All table properties should be known by now."

        widths = (availWidth - self.rightColumnWidth,
                  self.rightColumnWidth)

        # makes an internal table which does all the work.
        # we draw the LAST RUN's entries!  If there are
        # none, we make some dummy data to keep the table
        # from complaining
        if len(self._lastEntries) == 0:
            _tempEntries = [(0,'Placeholder for table of contents',0)]
        else:
            _tempEntries = self._lastEntries

        tableData = []
        for (level, text, pageNum) in _tempEntries:
            leftColStyle = self.levelStyles[level]
            #right col style is right aligned
            rightColStyle = ParagraphStyle(name='leftColLevel%d' % level,
                                           parent=leftColStyle,
                                           leftIndent=0,
                                           alignment=enums.TA_RIGHT)
            leftPara = Paragraph(text, leftColStyle)
            rightPara = Paragraph(str(pageNum), rightColStyle)
            tableData.append([leftPara, rightPara])

        self._table = Table(tableData, colWidths=widths,
                            style=self.tableStyle)

        self.width, self.height = self._table.wrapOn(self.canv,availWidth, availHeight)
        return (self.width, self.height)

    def split(self, availWidth, availHeight):
        """At this stage we do not care about splitting the entries,
        we will just return a list of platypus tables.  Presumably the
        calling app has a pointer to the original TableOfContents object;
        Platypus just sees tables.
        """
        return self._table.splitOn(self.canv,availWidth, availHeight)

    def drawOn(self, canvas, x, y, _sW=0):
        """Don't do this at home!  The standard calls for implementing
        draw(); we are hooking this in order to delegate ALL the drawing
        work to the embedded table object.
        """
        self._table.drawOn(canvas, x, y, _sW)

class SimpleIndex(IndexingFlowable):
    """This creates a very simple index.

    Entries have a string key, and appear with a page number on
    the right.  Prototype for more sophisticated multi-level index."""
    def __init__(self):
        #keep stuff in a dictionary while building
        self._entries = {}
        self._lastEntries = {}
        self._table = None
        self.textStyle = ParagraphStyle(name='index',
                                        fontName='Times-Roman',
                                        fontSize=12)
    def isIndexing(self):
        return 1

    def isSatisfied(self):
        return (self._entries == self._lastEntries)

    def beforeBuild(self):
        # keep track of the last run
        self._lastEntries = self._entries.copy()
        self.clearEntries()

    def clearEntries(self):
        self._entries = {}

    def notify(self, kind, stuff):
        """The notification hook called to register all kinds of events.

        Here we are interested in 'IndexEntry' events only.
        """
        if kind == 'IndexEntry':
            (text, pageNum) = stuff
            self.addEntry(text, pageNum)

    def addEntry(self, text, pageNum):
        """Allows incremental buildup"""
        if self._entries.has_key(text):
            self._entries[text].append(str(pageNum))
        else:
            self._entries[text] = [pageNum]

    def split(self, availWidth, availHeight):
        """At this stage we do not care about splitting the entries,
        we will just return a list of platypus tables.  Presumably the
        calling app has a pointer to the original TableOfContents object;
        Platypus just sees tables.
        """
        return self._table.splitOn(self.canv,availWidth, availHeight)

    def wrap(self, availWidth, availHeight):
        "All table properties should be known by now."
        # makes an internal table which does all the work.
        # we draw the LAST RUN's entries!  If there are
        # none, we make some dummy data to keep the table
        # from complaining
        if len(self._lastEntries) == 0:
            _tempEntries = [('Placeholder for index',[0,1,2])]
        else:
            _tempEntries = self._lastEntries.items()
            _tempEntries.sort()

        tableData = []
        for (text, pageNumbers) in _tempEntries:
            #right col style is right aligned
            allText = text + ': ' + string.join(map(str, pageNumbers), ', ')
            para = Paragraph(allText, self.textStyle)
            tableData.append([para])

        self._table = Table(tableData, colWidths=[availWidth])

        self.width, self.height = self._table.wrapOn(self.canv,availWidth, availHeight)
        return (self.width, self.height)

    def drawOn(self, canvas, x, y, _sW=0):
        """Don't do this at home!  The standard calls for implementing
        draw(); we are hooking this in order to delegate ALL the drawing
        work to the embedded table object.
        """
        self._table.drawOn(canvas, x, y, _sW)

class ReferenceText(IndexingFlowable):
    """Fakery to illustrate how a reference would work if we could
    put it in a paragraph."""
    def __init__(self, textPattern, targetKey):
        self.textPattern = textPattern
        self.target = targetKey
        self.paraStyle = ParagraphStyle('tmp')
        self._lastPageNum = None
        self._pageNum = -999
        self._para = None

    def beforeBuild(self):
        self._lastPageNum = self._pageNum

    def notify(self, kind, stuff):
        if kind == 'Target':
            (key, pageNum) = stuff
            if key == self.target:
                self._pageNum = pageNum

    def wrap(self, availWidth, availHeight):
        text = self.textPattern % self._lastPageNum
        self._para = Paragraph(text, self.paraStyle)
        return self._para.wrap(availWidth, availHeight)

    def drawOn(self, canvas, x, y, _sW=0):
        self._para.drawOn(canvas, x, y, _sW)

<|endoftext|>


<commit_before>"""Reads vehicle status from BMW connected drive portal."""
from __future__ import annotations

import logging

from bimmer_connected.state import ChargingState, LockState

from homeassistant.components.binary_sensor import (
    DEVICE_CLASS_OPENING,
    DEVICE_CLASS_PLUG,
    DEVICE_CLASS_PROBLEM,
    BinarySensorEntity,
    BinarySensorEntityDescription,
)
from homeassistant.const import LENGTH_KILOMETERS

from . import DOMAIN as BMW_DOMAIN, BMWConnectedDriveBaseEntity
from .const import CONF_ACCOUNT, DATA_ENTRIES

_LOGGER = logging.getLogger(__name__)

SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = (
    BinarySensorEntityDescription(
        key="lids",
        name="Doors",
        device_class=DEVICE_CLASS_OPENING,
        icon="mdi:car-door-lock",
    ),
    BinarySensorEntityDescription(
        key="windows",
        name="Windows",
        device_class=DEVICE_CLASS_OPENING,
        icon="mdi:car-door",
    ),
    BinarySensorEntityDescription(
        key="door_lock_state",
        name="Door lock state",
        device_class="lock",
        icon="mdi:car-key",
    ),
    BinarySensorEntityDescription(
        key="lights_parking",
        name="Parking lights",
        device_class="light",
        icon="mdi:car-parking-lights",
    ),
    BinarySensorEntityDescription(
        key="condition_based_services",
        name="Condition based services",
        device_class=DEVICE_CLASS_PROBLEM,
        icon="mdi:wrench",
    ),
    BinarySensorEntityDescription(
        key="check_control_messages",
        name="Control messages",
        device_class=DEVICE_CLASS_PROBLEM,
        icon="mdi:car-tire-alert",
    ),
    # electric
    BinarySensorEntityDescription(
        key="charging_status",
        name="Charging status",
        device_class="power",
        icon="mdi:ev-station",
    ),
    BinarySensorEntityDescription(
        key="connection_status",
        name="Connection status",
        device_class=DEVICE_CLASS_PLUG,
        icon="mdi:car-electric",
    ),
)

async def async_setup_entry(hass, config_entry, async_add_entities):
    """Set up the BMW ConnectedDrive binary sensors from config entry."""
    account = hass.data[BMW_DOMAIN][DATA_ENTRIES][config_entry.entry_id][CONF_ACCOUNT]

    entities = [
        BMWConnectedDriveSensor(account, vehicle, description)
        for vehicle in account.account.vehicles
        for description in SENSOR_TYPES
        if description.key in vehicle.available_attributes
    ]
    async_add_entities(entities, True)

class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, BinarySensorEntity):
    """Representation of a BMW vehicle binary sensor."""

    def __init__(self, account, vehicle, description: BinarySensorEntityDescription):
        """Initialize sensor."""
        super().__init__(account, vehicle)
        self.entity_description = description

        self._attr_name = f"{vehicle.name} {description.key}"
        self._attr_unique_id = f"{vehicle.vin}-{description.key}"

    def update(self):
        """Read new state data from the library."""
        sensor_type = self.entity_description.key
        vehicle_state = self._vehicle.state
        result = self._attrs.copy()

        # device class opening: On means open, Off means closed
        if sensor_type == "lids":
            _LOGGER.debug("Status of lid: %s", vehicle_state.all_lids_closed)
            self._attr_is_on = not vehicle_state.all_lids_closed
            for lid in vehicle_state.lids:
                result[lid.name] = lid.state.value
        elif sensor_type == "windows":
            self._attr_is_on = not vehicle_state.all_windows_closed
            for window in vehicle_state.windows:
                result[window.name] = window.state.value
        # device class lock: On means unlocked, Off means locked
        elif sensor_type == "door_lock_state":
            # Possible values: LOCKED, SECURED, SELECTIVE_LOCKED, UNLOCKED
            self._attr_is_on = vehicle_state.door_lock_state not in [
                LockState.LOCKED,
                LockState.SECURED,
            ]
            result["door_lock_state"] = vehicle_state.door_lock_state.value
            result["last_update_reason"] = vehicle_state.last_update_reason
        # device class light: On means light detected, Off means no light
        elif sensor_type == "lights_parking":
            self._attr_is_on = vehicle_state.are_parking_lights_on
            result["lights_parking"] = vehicle_state.parking_lights.value
        # device class problem: On means problem detected, Off means no problem
        elif sensor_type == "condition_based_services":
            self._attr_is_on = not vehicle_state.are_all_cbs_ok
            for report in vehicle_state.condition_based_services:
                result.update(self._format_cbs_report(report))
        elif sensor_type == "check_control_messages":
            self._attr_is_on = vehicle_state.has_check_control_messages
            check_control_messages = vehicle_state.check_control_messages
            has_check_control_messages = vehicle_state.has_check_control_messages
            if has_check_control_messages:
                cbs_list = []
                for message in check_control_messages:
                    cbs_list.append(message.description_short)
                result["check_control_messages"] = cbs_list
            else:
                result["check_control_messages"] = "OK"
        # device class power: On means power detected, Off means no power
        elif sensor_type == "charging_status":
            self._attr_is_on = vehicle_state.charging_status in [ChargingState.CHARGING]
            result["charging_status"] = vehicle_state.charging_status.value
            result["last_charging_end_result"] = vehicle_state.last_charging_end_result
        # device class plug: On means device is plugged in,
        #                    Off means device is unplugged
        elif sensor_type == "connection_status":
            self._attr_is_on = vehicle_state.connection_status == "CONNECTED"
            result["connection_status"] = vehicle_state.connection_status

        self._attr_extra_state_attributes = result

    def _format_cbs_report(self, report):
        result = {}
        service_type = report.service_type.lower().replace("_", " ")
        result[f"{service_type} status"] = report.state.value
        if report.due_date is not None:
            result[f"{service_type} date"] = report.due_date.strftime("%Y-%m-%d")
        if report.due_distance is not None:
            distance = round(
                self.hass.config.units.length(report.due_distance, LENGTH_KILOMETERS)
            )
            result[
                f"{service_type} distance"
            ] = f"{distance} {self.hass.config.units.length_unit}"
        return result
<commit_msg>Revert "Fix bmw_conntected_drive check_control_message short description" (#57928)<commit_after>"""Reads vehicle status from BMW connected drive portal."""
from __future__ import annotations

import logging

from bimmer_connected.state import ChargingState, LockState

from homeassistant.components.binary_sensor import (
    DEVICE_CLASS_OPENING,
    DEVICE_CLASS_PLUG,
    DEVICE_CLASS_PROBLEM,
    BinarySensorEntity,
    BinarySensorEntityDescription,
)
from homeassistant.const import LENGTH_KILOMETERS

from . import DOMAIN as BMW_DOMAIN, BMWConnectedDriveBaseEntity
from .const import CONF_ACCOUNT, DATA_ENTRIES

_LOGGER = logging.getLogger(__name__)

SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = (
    BinarySensorEntityDescription(
        key="lids",
        name="Doors",
        device_class=DEVICE_CLASS_OPENING,
        icon="mdi:car-door-lock",
    ),
    BinarySensorEntityDescription(
        key="windows",
        name="Windows",
        device_class=DEVICE_CLASS_OPENING,
        icon="mdi:car-door",
    ),
    BinarySensorEntityDescription(
        key="door_lock_state",
        name="Door lock state",
        device_class="lock",
        icon="mdi:car-key",
    ),
    BinarySensorEntityDescription(
        key="lights_parking",
        name="Parking lights",
        device_class="light",
        icon="mdi:car-parking-lights",
    ),
    BinarySensorEntityDescription(
        key="condition_based_services",
        name="Condition based services",
        device_class=DEVICE_CLASS_PROBLEM,
        icon="mdi:wrench",
    ),
    BinarySensorEntityDescription(
        key="check_control_messages",
        name="Control messages",
        device_class=DEVICE_CLASS_PROBLEM,
        icon="mdi:car-tire-alert",
    ),
    # electric
    BinarySensorEntityDescription(
        key="charging_status",
        name="Charging status",
        device_class="power",
        icon="mdi:ev-station",
    ),
    BinarySensorEntityDescription(
        key="connection_status",
        name="Connection status",
        device_class=DEVICE_CLASS_PLUG,
        icon="mdi:car-electric",
    ),
)

async def async_setup_entry(hass, config_entry, async_add_entities):
    """Set up the BMW ConnectedDrive binary sensors from config entry."""
    account = hass.data[BMW_DOMAIN][DATA_ENTRIES][config_entry.entry_id][CONF_ACCOUNT]

    entities = [
        BMWConnectedDriveSensor(account, vehicle, description)
        for vehicle in account.account.vehicles
        for description in SENSOR_TYPES
        if description.key in vehicle.available_attributes
    ]
    async_add_entities(entities, True)

class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, BinarySensorEntity):
    """Representation of a BMW vehicle binary sensor."""

    def __init__(self, account, vehicle, description: BinarySensorEntityDescription):
        """Initialize sensor."""
        super().__init__(account, vehicle)
        self.entity_description = description

        self._attr_name = f"{vehicle.name} {description.key}"
        self._attr_unique_id = f"{vehicle.vin}-{description.key}"

    def update(self):
        """Read new state data from the library."""
        sensor_type = self.entity_description.key
        vehicle_state = self._vehicle.state
        result = self._attrs.copy()

        # device class opening: On means open, Off means closed
        if sensor_type == "lids":
            _LOGGER.debug("Status of lid: %s", vehicle_state.all_lids_closed)
            self._attr_is_on = not vehicle_state.all_lids_closed
            for lid in vehicle_state.lids:
                result[lid.name] = lid.state.value
        elif sensor_type == "windows":
            self._attr_is_on = not vehicle_state.all_windows_closed
            for window in vehicle_state.windows:
                result[window.name] = window.state.value
        # device class lock: On means unlocked, Off means locked
        elif sensor_type == "door_lock_state":
            # Possible values: LOCKED, SECURED, SELECTIVE_LOCKED, UNLOCKED
            self._attr_is_on = vehicle_state.door_lock_state not in [
                LockState.LOCKED,
                LockState.SECURED,
            ]
            result["door_lock_state"] = vehicle_state.door_lock_state.value
            result["last_update_reason"] = vehicle_state.last_update_reason
        # device class light: On means light detected, Off means no light
        elif sensor_type == "lights_parking":
            self._attr_is_on = vehicle_state.are_parking_lights_on
            result["lights_parking"] = vehicle_state.parking_lights.value
        # device class problem: On means problem detected, Off means no problem
        elif sensor_type == "condition_based_services":
            self._attr_is_on = not vehicle_state.are_all_cbs_ok
            for report in vehicle_state.condition_based_services:
                result.update(self._format_cbs_report(report))
        elif sensor_type == "check_control_messages":
            self._attr_is_on = vehicle_state.has_check_control_messages
            check_control_messages = vehicle_state.check_control_messages
            has_check_control_messages = vehicle_state.has_check_control_messages
            if has_check_control_messages:
                cbs_list = []
                for message in check_control_messages:
                    cbs_list.append(message["ccmDescriptionShort"])
                result["check_control_messages"] = cbs_list
            else:
                result["check_control_messages"] = "OK"
        # device class power: On means power detected, Off means no power
        elif sensor_type == "charging_status":
            self._attr_is_on = vehicle_state.charging_status in [ChargingState.CHARGING]
            result["charging_status"] = vehicle_state.charging_status.value
            result["last_charging_end_result"] = vehicle_state.last_charging_end_result
        # device class plug: On means device is plugged in,
        #                    Off means device is unplugged
        elif sensor_type == "connection_status":
            self._attr_is_on = vehicle_state.connection_status == "CONNECTED"
            result["connection_status"] = vehicle_state.connection_status

        self._attr_extra_state_attributes = result

    def _format_cbs_report(self, report):
        result = {}
        service_type = report.service_type.lower().replace("_", " ")
        result[f"{service_type} status"] = report.state.value
        if report.due_date is not None:
            result[f"{service_type} date"] = report.due_date.strftime("%Y-%m-%d")
        if report.due_distance is not None:
            distance = round(
                self.hass.config.units.length(report.due_distance, LENGTH_KILOMETERS)
            )
            result[
                f"{service_type} distance"
            ] = f"{distance} {self.hass.config.units.length_unit}"
        return result
<|endoftext|>


<commit_before>#!/usr/bin/env python
# Copyright (c) 2019-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.

from __future__ import absolute_import, division, print_function, unicode_literals

import argparse
import os
import shutil
import subprocess
import sys

from getdeps.buildopts import setup_build_options
from getdeps.dyndeps import create_dyn_dep_munger
from getdeps.errors import TransientFailure
from getdeps.load import ManifestLoader
from getdeps.manifest import ManifestParser
from getdeps.platform import HostType
from getdeps.subcmd import SubCmd, add_subcommands, cmd

try:
    import getdeps.facebook  # noqa: F401
except ImportError:
    # we don't ship the facebook specific subdir,
    # so allow that to fail silently
    pass

sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "getdeps"))

@cmd("validate-manifest", "parse a manifest and validate that it is correct")
class ValidateManifest(SubCmd):
    def run(self, args):
        try:
            ManifestParser(file_name=args.file_name)
            print("OK", file=sys.stderr)
            return 0
        except Exception as exc:
            print("ERROR: %s" % str(exc), file=sys.stderr)
            return 1

    def setup_parser(self, parser):
        parser.add_argument("file_name", help="path to the manifest file")

@cmd("show-host-type", "outputs the host type tuple for the host machine")
class ShowHostType(SubCmd):
    def run(self, args):
        host = HostType()
        print("%s" % host.as_tuple_string())
        return 0

@cmd("fetch", "fetch the code for a given project")
class FetchCmd(SubCmd):
    def setup_parser(self, parser):
        parser.add_argument(
            "project",
            help=(
                "name of the project or path to a manifest "
                "file describing the project"
            ),
        )
        parser.add_argument(
            "--recursive",
            help="fetch the transitive deps also",
            action="store_true",
            default=False,
        )
        parser.add_argument(
            "--host-type",
            help=(
                "When recursively fetching, fetch deps for "
                "this host type rather than the current system"
            ),
        )

    def run(self, args):
        opts = setup_build_options(args)
        loader = ManifestLoader(opts)
        manifest = loader.load_manifest(args.project)
        if args.recursive:
            projects = loader.manifests_in_dependency_order()
        else:
            projects = [manifest]
        for m in projects:
            fetcher = loader.create_fetcher(m)
            fetcher.update()

@cmd("list-deps", "lists the transitive deps for a given project")
class ListDepsCmd(SubCmd):
    def run(self, args):
        opts = setup_build_options(args)
        loader = ManifestLoader(opts)
        loader.ctx_gen.set_value_for_project(args.project, "test", "on")
        loader.load_manifest(args.project)
        for m in loader.manifests_in_dependency_order():
            print(m.name)
        return 0

    def setup_parser(self, parser):
        parser.add_argument(
            "--host-type",
            help=(
                "Produce the list for the specified host type, "
                "rather than that of the current system"
            ),
        )
        parser.add_argument(
            "project",
            help=(
                "name of the project or path to a manifest "
                "file describing the project"
            ),
        )

def clean_dirs(opts):
    for d in ["build", "installed", "extracted", "shipit"]:
        d = os.path.join(opts.scratch_dir, d)
        print("Cleaning %s..." % d)
        if os.path.exists(d):
            shutil.rmtree(d)

@cmd("clean", "clean up the scratch dir")
class CleanCmd(SubCmd):
    def run(self, args):
        opts = setup_build_options(args)
        clean_dirs(opts)

@cmd("show-inst-dir", "print the installation dir for a given project")
class ShowInstDirCmd(SubCmd):
    def run(self, args):
        opts = setup_build_options(args)
        loader = ManifestLoader(opts)
        loader.ctx_gen.set_value_for_project(args.project, "test", "on")
        manifest = loader.load_manifest(args.project)
        projects = loader.manifests_in_dependency_order()

        if args.recursive:
            manifests = projects
        else:
            manifests = [manifest]

        for m in manifests:
            inst_dir = loader.get_project_install_dir(m)
            print(inst_dir)

    def setup_parser(self, parser):
        parser.add_argument(
            "project",
            help=(
                "name of the project or path to a manifest "
                "file describing the project"
            ),
        )
        parser.add_argument(
            "--recursive",
            help="print the transitive deps also",
            action="store_true",
            default=False,
        )

@cmd("show-source-dir", "print the source dir for a given project")
class ShowSourceDirCmd(SubCmd):
    def run(self, args):
        opts = setup_build_options(args)
        loader = ManifestLoader(opts)
        loader.ctx_gen.set_value_for_project(args.project, "test", "on")
        manifest = loader.load_manifest(args.project)

        if args.recursive:
            manifests = loader.manifests_in_dependency_order()
        else:
            manifests = [manifest]

        for m in manifests:
            fetcher = loader.create_fetcher(m)
            print(fetcher.get_src_dir())

    def setup_parser(self, parser):
        parser.add_argument(
            "project",
            help=(
                "name of the project or path to a manifest "
                "file describing the project"
            ),
        )
        parser.add_argument(
            "--recursive",
            help="print the transitive deps also",
            action="store_true",
            default=False,
        )

@cmd("build", "build a given project")
class BuildCmd(SubCmd):
    def run(self, args):
        opts = setup_build_options(args)
        ctx_gen = opts.get_context_generator(facebook_internal=args.facebook_internal)
        if args.enable_tests:
            ctx_gen.set_value_for_project(args.project, "test", "on")
        loader = ManifestLoader(opts, ctx_gen)

        if args.clean:
            clean_dirs(opts)

        manifest = loader.load_manifest(args.project)

        print("Building on %s" % ctx_gen.get_context(args.project))
        projects = loader.manifests_in_dependency_order()

        # Accumulate the install directories so that the build steps
        # can find their dep installation
        install_dirs = []

        for m in projects:
            fetcher = loader.create_fetcher(m)

            if args.clean:
                fetcher.clean()

            build_dir = loader.get_project_build_dir(m)
            inst_dir = loader.get_project_install_dir(m)

            if m == manifest or not args.no_deps:
                print("Assessing %s..." % m.name)
                change_status = fetcher.update()
                reconfigure = change_status.build_changed()
                sources_changed = change_status.sources_changed()

                project_hash = loader.get_project_hash(m)
                built_marker = os.path.join(inst_dir, ".built-by-getdeps")
                if os.path.exists(built_marker):
                    with open(built_marker, "r") as f:
                        built_hash = f.read().strip()
                    if built_hash != project_hash:
                        # Some kind of inconsistency with a prior build,
                        # let's run it again to be sure
                        os.unlink(built_marker)
                        reconfigure = True

                if sources_changed or reconfigure or not os.path.exists(built_marker):
                    if os.path.exists(built_marker):
                        os.unlink(built_marker)
                    src_dir = fetcher.get_src_dir()
                    ctx = ctx_gen.get_context(m.name)
                    builder = m.create_builder(opts, src_dir, build_dir, inst_dir, ctx)
                    builder.build(install_dirs, reconfigure=reconfigure)

                    with open(built_marker, "w") as f:
                        f.write(project_hash)

            install_dirs.append(inst_dir)

    def setup_parser(self, parser):
        parser.add_argument(
            "project",
            help=(
                "name of the project or path to a manifest "
                "file describing the project"
            ),
        )
        parser.add_argument(
            "--clean",
            action="store_true",
            default=False,
            help=(
                "Clean up the build and installation area prior to building, "
                "causing the projects to be built from scratch"
            ),
        )
        parser.add_argument(
            "--no-deps",
            action="store_true",
            default=False,
            help=(
                "Only build the named project, not its deps. "
                "This is most useful after you've built all of the deps, "
                "and helps to avoid waiting for relatively "
                "slow up-to-date-ness checks"
            ),
        )
        parser.add_argument(
            "--enable-tests",
            action="store_true",
            default=False,
            help=(
                "For the named project, build tests so that the test command "
                "is able to execute tests"
            ),
        )
        parser.add_argument(
            "--schedule-type", help="Indicates how the build was activated"
        )

@cmd("fixup-dyn-deps", "Adjusts dynamic dependencies for packaging purposes")
class FixupDeps(SubCmd):
    def run(self, args):
        opts = setup_build_options(args)
        ctx_gen = opts.get_context_generator(facebook_internal=args.facebook_internal)
        if args.enable_tests:
            ctx_gen.set_value_for_project(args.project, "test", "on")

        loader = ManifestLoader(opts, ctx_gen)
        manifest = loader.load_manifest(args.project)

        projects = loader.manifests_in_dependency_order()

        # Accumulate the install directories so that the build steps
        # can find their dep installation
        install_dirs = []

        for m in projects:
            inst_dir = loader.get_project_install_dir(m)
            install_dirs.append(inst_dir)

            if m == manifest:
                dep_munger = create_dyn_dep_munger(opts, install_dirs)
                dep_munger.process_deps(args.destdir, args.final_install_prefix)

    def setup_parser(self, parser):
        parser.add_argument(
            "project",
            help=(
                "name of the project or path to a manifest "
                "file describing the project"
            ),
        )
        parser.add_argument("destdir", help=("Where to copy the fixed up executables"))
        parser.add_argument(
            "--final-install-prefix", help=("specify the final installation prefix")
        )
        parser.add_argument(
            "--enable-tests",
            action="store_true",
            default=False,
            help=(
                "For the named project, build tests so that the test command "
                "is able to execute tests"
            ),
        )
        parser.add_argument(
            "--schedule-type", help="Indicates how the build was activated"
        )

@cmd("test", "test a given project")
class TestCmd(SubCmd):
    def run(self, args):
        opts = setup_build_options(args)
        ctx_gen = opts.get_context_generator(facebook_internal=args.facebook_internal)
        if args.test_all:
            ctx_gen.set_value_for_all_projects("test", "on")
        else:
            ctx_gen.set_value_for_project(args.project, "test", "on")

        loader = ManifestLoader(opts, ctx_gen)
        manifest = loader.load_manifest(args.project)
        projects = loader.manifests_in_dependency_order()

        # Accumulate the install directories so that the test steps
        # can find their dep installation
        install_dirs = []

        for m in projects:
            inst_dir = loader.get_project_install_dir(m)

            if m == manifest or args.test_all:
                built_marker = os.path.join(inst_dir, ".built-by-getdeps")
                if not os.path.exists(built_marker):
                    print("project %s has not been built" % m.name)
                    # TODO: we could just go ahead and build it here, but I
                    # want to tackle that as part of adding build-for-test
                    # support.
                    return 1
                fetcher = loader.create_fetcher(m)
                src_dir = fetcher.get_src_dir()
                ctx = ctx_gen.get_context(m.name)
                build_dir = loader.get_project_build_dir(m)
                builder = m.create_builder(opts, src_dir, build_dir, inst_dir, ctx)
                builder.run_tests(install_dirs, schedule_type=args.schedule_type)

            install_dirs.append(inst_dir)

    def setup_parser(self, parser):
        parser.add_argument(
            "project",
            help=(
                "name of the project or path to a manifest "
                "file describing the project"
            ),
        )
        parser.add_argument(
            "--test-all",
            action="store_true",
            default=False,
            help="Enable running tests for the named project and all of its deps",
        )
        parser.add_argument(
            "--schedule-type", help="Indicates how the build was activated"
        )

def get_arg_var_name(args):
    for arg in args:
        if arg.startswith("--"):
            return arg[2:].replace("-", "_")

    raise Exception("unable to determine argument variable name from %r" % (args,))

def parse_args():
    # We want to allow common arguments to be specified either before or after
    # the subcommand name.  In order to do this we add them to the main parser
    # and to subcommand parsers.  In order for this to work, we need to tell
    # argparse that the default value is SUPPRESS, so that the default values
    # from the subparser arguments won't override values set by the user from
    # the main parser.  We maintain our own list of desired defaults in the
    # common_defaults dictionary, and manually set those if the argument wasn't
    # present at all.
    common_args = argparse.ArgumentParser(add_help=False)
    common_defaults = {}

    def add_common_arg(*args, **kwargs):
        var_name = get_arg_var_name(args)
        default_value = kwargs.pop("default", None)
        common_defaults[var_name] = default_value
        kwargs["default"] = argparse.SUPPRESS
        common_args.add_argument(*args, **kwargs)

    add_common_arg("--scratch-path", help="Where to maintain checkouts and build dirs")
    add_common_arg(
        "--vcvars-path", default=None, help="Path to the vcvarsall.bat on Windows."
    )
    add_common_arg(
        "--install-prefix",
        help=(
            "Where the final build products will be installed "
            "(default is [scratch-path]/installed)"
        ),
    )
    add_common_arg(
        "--num-jobs",
        type=int,
        help=(
            "Number of concurrent jobs to use while building. "
            "(default=number of cpu cores)"
        ),
    )
    add_common_arg(
        "--use-shipit",
        help="use the real ShipIt instead of the simple shipit transformer",
        action="store_true",
        default=False,
    )
    add_common_arg(
        "--facebook-internal",
        help="Setup the build context as an FB internal build",
        action="store_true",
        default=False,
    )

    ap = argparse.ArgumentParser(
        description="Get and build dependencies and projects", parents=[common_args]
    )
    sub = ap.add_subparsers(
        # metavar suppresses the long and ugly default list of subcommands on a
        # single line.  We still render the nicer list below where we would
        # have shown the nasty one.
        metavar="",
        title="Available commands",
        help="",
    )

    add_subcommands(sub, common_args)

    args = ap.parse_args()
    for var_name, default_value in common_defaults.items():
        if not hasattr(args, var_name):
            setattr(args, var_name, default_value)

    return ap, args

def main():
    ap, args = parse_args()
    if getattr(args, "func", None) is None:
        ap.print_help()
        return 0
    try:
        return args.func(args)
    except TransientFailure as exc:
        print("TransientFailure: %s" % str(exc))
        # This return code is treated as a retryable transient infrastructure
        # error by Facebook's internal CI, rather than eg: a build or code
        # related error that needs to be fixed before progress can be made.
        return 128
    except subprocess.CalledProcessError as exc:
        print("%s" % str(exc), file=sys.stderr)
        print("!! Failed", file=sys.stderr)
        return 1

if __name__ == "__main__":
    sys.exit(main())
<commit_msg>getdeps: consolidate code for project subcommands<commit_after>#!/usr/bin/env python
# Copyright (c) 2019-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.

from __future__ import absolute_import, division, print_function, unicode_literals

import argparse
import os
import shutil
import subprocess
import sys

from getdeps.buildopts import setup_build_options
from getdeps.dyndeps import create_dyn_dep_munger
from getdeps.errors import TransientFailure
from getdeps.load import ManifestLoader
from getdeps.manifest import ManifestParser
from getdeps.platform import HostType
from getdeps.subcmd import SubCmd, add_subcommands, cmd

try:
    import getdeps.facebook  # noqa: F401
except ImportError:
    # we don't ship the facebook specific subdir,
    # so allow that to fail silently
    pass

sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "getdeps"))

@cmd("validate-manifest", "parse a manifest and validate that it is correct")
class ValidateManifest(SubCmd):
    def run(self, args):
        try:
            ManifestParser(file_name=args.file_name)
            print("OK", file=sys.stderr)
            return 0
        except Exception as exc:
            print("ERROR: %s" % str(exc), file=sys.stderr)
            return 1

    def setup_parser(self, parser):
        parser.add_argument("file_name", help="path to the manifest file")

@cmd("show-host-type", "outputs the host type tuple for the host machine")
class ShowHostType(SubCmd):
    def run(self, args):
        host = HostType()
        print("%s" % host.as_tuple_string())
        return 0

class ProjectCmdBase(SubCmd):
    def run(self, args):
        opts = setup_build_options(args)
        ctx_gen = opts.get_context_generator(facebook_internal=args.facebook_internal)
        if args.test_dependencies:
            ctx_gen.set_value_for_all_projects("test", "on")
        if args.enable_tests:
            ctx_gen.set_value_for_project(args.project, "test", "on")
        else:
            ctx_gen.set_value_for_project(args.project, "test", "off")

        loader = ManifestLoader(opts, ctx_gen)
        manifest = loader.load_manifest(args.project)

        self.run_project_cmd(args, loader, manifest)

    def setup_parser(self, parser):
        parser.add_argument(
            "project",
            help=(
                "name of the project or path to a manifest "
                "file describing the project"
            ),
        )
        parser.add_argument(
            "--no-tests",
            action="store_false",
            dest="enable_tests",
            default=True,
            help="Disable building tests for this project.",
        )
        parser.add_argument(
            "--test-dependencies",
            action="store_true",
            help="Enable building tests for dependencies as well.",
        )

        self.setup_project_cmd_parser(parser)

    def setup_project_cmd_parser(self, parser):
        pass

@cmd("fetch", "fetch the code for a given project")
class FetchCmd(ProjectCmdBase):
    def setup_project_cmd_parser(self, parser):
        parser.add_argument(
            "--recursive",
            help="fetch the transitive deps also",
            action="store_true",
            default=False,
        )
        parser.add_argument(
            "--host-type",
            help=(
                "When recursively fetching, fetch deps for "
                "this host type rather than the current system"
            ),
        )

    def run_project_cmd(self, args, loader, manifest):
        if args.recursive:
            projects = loader.manifests_in_dependency_order()
        else:
            projects = [manifest]
        for m in projects:
            fetcher = loader.create_fetcher(m)
            fetcher.update()

@cmd("list-deps", "lists the transitive deps for a given project")
class ListDepsCmd(ProjectCmdBase):
    def run_project_cmd(self, args, loader, manifest):
        for m in loader.manifests_in_dependency_order():
            print(m.name)
        return 0

    def setup_project_cmd_parser(self, parser):
        parser.add_argument(
            "--host-type",
            help=(
                "Produce the list for the specified host type, "
                "rather than that of the current system"
            ),
        )

def clean_dirs(opts):
    for d in ["build", "installed", "extracted", "shipit"]:
        d = os.path.join(opts.scratch_dir, d)
        print("Cleaning %s..." % d)
        if os.path.exists(d):
            shutil.rmtree(d)

@cmd("clean", "clean up the scratch dir")
class CleanCmd(SubCmd):
    def run(self, args):
        opts = setup_build_options(args)
        clean_dirs(opts)

@cmd("show-inst-dir", "print the installation dir for a given project")
class ShowInstDirCmd(ProjectCmdBase):
    def run_project_cmd(self, args, loader, manifest):
        if args.recursive:
            manifests = loader.manifests_in_dependency_order()
        else:
            manifests = [manifest]

        for m in manifests:
            inst_dir = loader.get_project_install_dir(m)
            print(inst_dir)

    def setup_project_cmd_parser(self, parser):
        parser.add_argument(
            "--recursive",
            help="print the transitive deps also",
            action="store_true",
            default=False,
        )

@cmd("show-source-dir", "print the source dir for a given project")
class ShowSourceDirCmd(ProjectCmdBase):
    def run_project_cmd(self, args, loader, manifest):
        if args.recursive:
            manifests = loader.manifests_in_dependency_order()
        else:
            manifests = [manifest]

        for m in manifests:
            fetcher = loader.create_fetcher(m)
            print(fetcher.get_src_dir())

    def setup_project_cmd_parser(self, parser):
        parser.add_argument(
            "--recursive",
            help="print the transitive deps also",
            action="store_true",
            default=False,
        )

@cmd("build", "build a given project")
class BuildCmd(ProjectCmdBase):
    def run_project_cmd(self, args, loader, manifest):
        if args.clean:
            clean_dirs(loader.build_opts)

        print("Building on %s" % loader.ctx_gen.get_context(args.project))
        projects = loader.manifests_in_dependency_order()

        # Accumulate the install directories so that the build steps
        # can find their dep installation
        install_dirs = []

        for m in projects:
            fetcher = loader.create_fetcher(m)

            if args.clean:
                fetcher.clean()

            build_dir = loader.get_project_build_dir(m)
            inst_dir = loader.get_project_install_dir(m)

            if m == manifest or not args.no_deps:
                print("Assessing %s..." % m.name)
                change_status = fetcher.update()
                reconfigure = change_status.build_changed()
                sources_changed = change_status.sources_changed()

                project_hash = loader.get_project_hash(m)
                built_marker = os.path.join(inst_dir, ".built-by-getdeps")
                if os.path.exists(built_marker):
                    with open(built_marker, "r") as f:
                        built_hash = f.read().strip()
                    if built_hash != project_hash:
                        # Some kind of inconsistency with a prior build,
                        # let's run it again to be sure
                        os.unlink(built_marker)
                        reconfigure = True

                if sources_changed or reconfigure or not os.path.exists(built_marker):
                    if os.path.exists(built_marker):
                        os.unlink(built_marker)
                    src_dir = fetcher.get_src_dir()
                    ctx = loader.ctx_gen.get_context(m.name)
                    builder = m.create_builder(
                        loader.build_opts, src_dir, build_dir, inst_dir, ctx
                    )
                    builder.build(install_dirs, reconfigure=reconfigure)

                    with open(built_marker, "w") as f:
                        f.write(project_hash)

            install_dirs.append(inst_dir)

    def setup_project_cmd_parser(self, parser):
        parser.add_argument(
            "--clean",
            action="store_true",
            default=False,
            help=(
                "Clean up the build and installation area prior to building, "
                "causing the projects to be built from scratch"
            ),
        )
        parser.add_argument(
            "--no-deps",
            action="store_true",
            default=False,
            help=(
                "Only build the named project, not its deps. "
                "This is most useful after you've built all of the deps, "
                "and helps to avoid waiting for relatively "
                "slow up-to-date-ness checks"
            ),
        )

@cmd("fixup-dyn-deps", "Adjusts dynamic dependencies for packaging purposes")
class FixupDeps(ProjectCmdBase):
    def run_project_cmd(self, args, loader, manifest):
        projects = loader.manifests_in_dependency_order()

        # Accumulate the install directories so that the build steps
        # can find their dep installation
        install_dirs = []

        for m in projects:
            inst_dir = loader.get_project_install_dir(m)
            install_dirs.append(inst_dir)

            if m == manifest:
                dep_munger = create_dyn_dep_munger(loader.build_opts, install_dirs)
                dep_munger.process_deps(args.destdir, args.final_install_prefix)

    def setup_project_cmd_parser(self, parser):
        parser.add_argument("destdir", help=("Where to copy the fixed up executables"))
        parser.add_argument(
            "--final-install-prefix", help=("specify the final installation prefix")
        )

@cmd("test", "test a given project")
class TestCmd(ProjectCmdBase):
    def run_project_cmd(self, args, loader, manifest):
        projects = loader.manifests_in_dependency_order()

        # Accumulate the install directories so that the test steps
        # can find their dep installation
        install_dirs = []

        for m in projects:
            inst_dir = loader.get_project_install_dir(m)

            if m == manifest or args.test_dependencies:
                built_marker = os.path.join(inst_dir, ".built-by-getdeps")
                if not os.path.exists(built_marker):
                    print("project %s has not been built" % m.name)
                    # TODO: we could just go ahead and build it here, but I
                    # want to tackle that as part of adding build-for-test
                    # support.
                    return 1
                fetcher = loader.create_fetcher(m)
                src_dir = fetcher.get_src_dir()
                ctx = loader.ctx_gen.get_context(m.name)
                build_dir = loader.get_project_build_dir(m)
                builder = m.create_builder(
                    loader.build_opts, src_dir, build_dir, inst_dir, ctx
                )
                builder.run_tests(install_dirs, schedule_type=args.schedule_type)

            install_dirs.append(inst_dir)

    def setup_project_cmd_parser(self, parser):
        parser.add_argument(
            "--schedule-type", help="Indicates how the build was activated"
        )

def get_arg_var_name(args):
    for arg in args:
        if arg.startswith("--"):
            return arg[2:].replace("-", "_")

    raise Exception("unable to determine argument variable name from %r" % (args,))

def parse_args():
    # We want to allow common arguments to be specified either before or after
    # the subcommand name.  In order to do this we add them to the main parser
    # and to subcommand parsers.  In order for this to work, we need to tell
    # argparse that the default value is SUPPRESS, so that the default values
    # from the subparser arguments won't override values set by the user from
    # the main parser.  We maintain our own list of desired defaults in the
    # common_defaults dictionary, and manually set those if the argument wasn't
    # present at all.
    common_args = argparse.ArgumentParser(add_help=False)
    common_defaults = {}

    def add_common_arg(*args, **kwargs):
        var_name = get_arg_var_name(args)
        default_value = kwargs.pop("default", None)
        common_defaults[var_name] = default_value
        kwargs["default"] = argparse.SUPPRESS
        common_args.add_argument(*args, **kwargs)

    add_common_arg("--scratch-path", help="Where to maintain checkouts and build dirs")
    add_common_arg(
        "--vcvars-path", default=None, help="Path to the vcvarsall.bat on Windows."
    )
    add_common_arg(
        "--install-prefix",
        help=(
            "Where the final build products will be installed "
            "(default is [scratch-path]/installed)"
        ),
    )
    add_common_arg(
        "--num-jobs",
        type=int,
        help=(
            "Number of concurrent jobs to use while building. "
            "(default=number of cpu cores)"
        ),
    )
    add_common_arg(
        "--use-shipit",
        help="use the real ShipIt instead of the simple shipit transformer",
        action="store_true",
        default=False,
    )
    add_common_arg(
        "--facebook-internal",
        help="Setup the build context as an FB internal build",
        action="store_true",
        default=False,
    )

    ap = argparse.ArgumentParser(
        description="Get and build dependencies and projects", parents=[common_args]
    )
    sub = ap.add_subparsers(
        # metavar suppresses the long and ugly default list of subcommands on a
        # single line.  We still render the nicer list below where we would
        # have shown the nasty one.
        metavar="",
        title="Available commands",
        help="",
    )

    add_subcommands(sub, common_args)

    args = ap.parse_args()
    for var_name, default_value in common_defaults.items():
        if not hasattr(args, var_name):
            setattr(args, var_name, default_value)

    return ap, args

def main():
    ap, args = parse_args()
    if getattr(args, "func", None) is None:
        ap.print_help()
        return 0
    try:
        return args.func(args)
    except TransientFailure as exc:
        print("TransientFailure: %s" % str(exc))
        # This return code is treated as a retryable transient infrastructure
        # error by Facebook's internal CI, rather than eg: a build or code
        # related error that needs to be fixed before progress can be made.
        return 128
    except subprocess.CalledProcessError as exc:
        print("%s" % str(exc), file=sys.stderr)
        print("!! Failed", file=sys.stderr)
        return 1

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


<commit_before># -*- coding: utf-8 -*-
#
# defivelo-intranet -- Outil métier pour la gestion du Défi Vélo
# Copyright (C) 2015 Didier Raboud <me+defivelo@odyx.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals

from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import FieldError
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from django.views.generic.dates import WeekArchiveView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from rolepermissions.mixins import HasPermissionsMixin

from defivelo.views import MenuView

from ..forms import SessionForm
from ..models import Session
from .mixins import CantonSeasonFormMixin

class SessionMixin(CantonSeasonFormMixin, HasPermissionsMixin, MenuView):
    required_permission = 'challenge_session_crud'
    model = Session
    context_object_name = 'session'
    form_class = SessionForm

    def get_queryset(self):
        qs = super(SessionMixin, self).get_queryset()
        try:
            return qs.filter(
                    organization__address_canton__in=self.season.cantons
                )
        except FieldError:
            # For the cases qs is Qualification, not Session
            return qs

    def get_success_url(self):
        return reverse_lazy('session-detail',
                            kwargs={
                                'seasonpk': self.season.pk,
                                'pk': self.object.pk
                                })

    def get_context_data(self, **kwargs):
        context = super(SessionMixin, self).get_context_data(**kwargs)
        # Add our menu_category context
        context['menu_category'] = 'season'
        context['season'] = self.season
        mysession = self.get_object()
        session_pages = {
            'session_current': None,
            'session_next': None,
        }

        # Iterate through all of them, index is 'next'
        for session in self.season.sessions.all():
            session_pages['session_current'] = session_pages['session_next']
            session_pages['session_next'] = session
            # On arrive
            if session_pages['session_next'].pk == mysession.pk:
                context['session_previous'] = session_pages['session_current']
            # On y est
            if (
                session_pages['session_current'] and
                session_pages['session_current'].pk == mysession.pk
            ):
                context['session_next'] = session_pages['session_next']
                break
        return context

class SessionsListView(SessionMixin, WeekArchiveView):
    date_field = "day"
    context_object_name = 'sessions'
    allow_empty = True
    allow_future = True
    week_format = '%W'
    ordering = ['day', 'begin', 'duration']

class SessionDetailView(SessionMixin, DetailView):
    def get_queryset(self):
        return (
            super(SessionDetailView, self).get_queryset()
            .prefetch_related(
                'organization',
                'qualifications',
                'qualifications__leader',
                'qualifications__leader__profile',
                'qualifications__helpers',
                'qualifications__helpers__profile',
                'qualifications__actor',
                'qualifications__actor__profile',
                'qualifications__activity_A',
                'qualifications__activity_B',
                'qualifications__activity_C')
        )

class SessionUpdateView(SessionMixin, SuccessMessageMixin, UpdateView):
    success_message = _("Session mise à jour")

class SessionCreateView(SessionMixin, SuccessMessageMixin, CreateView):
    success_message = _("Session créée")

class SessionDeleteView(SessionMixin, SuccessMessageMixin, DeleteView):
    success_message = _("Session supprimée")

    def get_success_url(self):
        return reverse_lazy('season-detail', kwargs={
            'pk': self.kwargs['seasonpk']
            })

class SessionStaffChoiceView(SessionDetailView):
    template_name = 'challenge/session_availability.html'
<commit_msg>Corrige la navigation dans les vues par semaines<commit_after># -*- coding: utf-8 -*-
#
# defivelo-intranet -- Outil métier pour la gestion du Défi Vélo
# Copyright (C) 2015 Didier Raboud <me+defivelo@odyx.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals

from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import FieldError
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from django.views.generic.dates import WeekArchiveView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from rolepermissions.mixins import HasPermissionsMixin

from defivelo.views import MenuView

from ..forms import SessionForm
from ..models import Session
from .mixins import CantonSeasonFormMixin

class SessionMixin(CantonSeasonFormMixin, HasPermissionsMixin, MenuView):
    required_permission = 'challenge_session_crud'
    model = Session
    context_object_name = 'session'
    form_class = SessionForm

    def get_queryset(self):
        qs = super(SessionMixin, self).get_queryset()
        try:
            return qs.filter(
                    organization__address_canton__in=self.season.cantons
                )
        except FieldError:
            # For the cases qs is Qualification, not Session
            return qs

    def get_success_url(self):
        return reverse_lazy('session-detail',
                            kwargs={
                                'seasonpk': self.season.pk,
                                'pk': self.object.pk
                                })

    def get_context_data(self, **kwargs):
        context = super(SessionMixin, self).get_context_data(**kwargs)
        # Add our menu_category context
        context['menu_category'] = 'season'
        context['season'] = self.season
        try:
            mysession = self.get_object()
        except AttributeError:
            return context

        session_pages = {
            'session_current': None,
            'session_next': None,
        }

        # Iterate through all of them, index is 'next'
        for session in self.season.sessions.all():
            session_pages['session_current'] = session_pages['session_next']
            session_pages['session_next'] = session
            # On arrive
            if session_pages['session_next'].pk == mysession.pk:
                context['session_previous'] = session_pages['session_current']
            # On y est
            if (
                session_pages['session_current'] and
                session_pages['session_current'].pk == mysession.pk
            ):
                context['session_next'] = session_pages['session_next']
                break
        return context

class SessionsListView(SessionMixin, WeekArchiveView):
    date_field = "day"
    context_object_name = 'sessions'
    allow_empty = True
    allow_future = True
    week_format = '%W'
    ordering = ['day', 'begin', 'duration']

class SessionDetailView(SessionMixin, DetailView):
    def get_queryset(self):
        return (
            super(SessionDetailView, self).get_queryset()
            .prefetch_related(
                'organization',
                'qualifications',
                'qualifications__leader',
                'qualifications__leader__profile',
                'qualifications__helpers',
                'qualifications__helpers__profile',
                'qualifications__actor',
                'qualifications__actor__profile',
                'qualifications__activity_A',
                'qualifications__activity_B',
                'qualifications__activity_C')
        )

class SessionUpdateView(SessionMixin, SuccessMessageMixin, UpdateView):
    success_message = _("Session mise à jour")

class SessionCreateView(SessionMixin, SuccessMessageMixin, CreateView):
    success_message = _("Session créée")

class SessionDeleteView(SessionMixin, SuccessMessageMixin, DeleteView):
    success_message = _("Session supprimée")

    def get_success_url(self):
        return reverse_lazy('season-detail', kwargs={
            'pk': self.kwargs['seasonpk']
            })

class SessionStaffChoiceView(SessionDetailView):
    template_name = 'challenge/session_availability.html'
<|endoftext|>


<commit_before>"""
Illustrate a comparison window for a substring
"""

mystring = 'cacaca'

"""
Initial window size is 1/2 the total length of the string
BECAUSE you can't match more than half of the string
to less than half of the string, the lengths aren't even right.
"""
init_window_size = 3 # half the string length

substr1 = mystring[0:init_window_size]
substr2 = mystring[init_window_size:]

# print substr1 == substr2

# if it is odd, round up.
#init_window_size = int(len(mystring)/2)

"""
we will calculate the next windowsize by subtracting 1 from
the current window size.

"""
window_size_2 = 2 # hard coded the next windowsize

# we will check substr1 against all the other substrings.
substr1 = mystring[0:window_size_2]

substr2 = mystring[window_size_2:(window_size_2+window_size_2)]

# increment by 1 for the next substring to check
# (we can keep incrementing the window position by 1
# until we hit the top)
substr3 = mystring[window_size_2+1:(window_size_2+window_size_2+1)]

substr4 = mystring[window_size_2+1+1:(window_size_2+window_size_2+1+1)]

print substr1
print substr2

print substr1 == substr2
print substr1 == substr3
print substr1 == substr4

# while loop (while the second value is less than the total)

<commit_msg>Finished while loop to check window lengths.<commit_after>"""
Illustrate a comparison window for a substring
"""

mystring = 'cacacacacaca'

"""
Initial window size is 1/2 the total length of the string
BECAUSE you can't match more than half of the string
to less than half of the string, the lengths aren't even right.
"""
init_window_size = 3 # half the string length

substr1 = mystring[0:init_window_size]
substr2 = mystring[init_window_size:]

# print substr1 == substr2

# if it is odd, round up.
#init_window_size = int(len(mystring)/2)

"""
we will calculate the next windowsize by subtracting 1 from
the current window size.

"""

# find the new window size from the initial window size
window_size = init_window_size - 1

# we will check substr1 against all the other substrings.
substr1 = mystring[0:window_size]

substr2 = mystring[window_size:(window_size+window_size)]

# increment by 1 for the next substring to check
# (we can keep incrementing the window position by 1
# until we hit the top)
substr3 = mystring[window_size+1:(window_size+window_size+1)]

substr4 = mystring[window_size+1+1:(window_size+window_size+1+1)]

# print substr1
# print substr2
#
# print substr1 == substr2
# print substr1 == substr3
# print substr1 == substr4
#

# window lower bound starts right after the window we are checking against
window_lower_bound = window_size
target_substring = mystring[0:window_size]

# while loop (while the second value is less than the total)
while (window_lower_bound + window_size) <= len(mystring):

    current_substring_to_check = mystring[window_lower_bound:(window_size+window_lower_bound)]
    window_lower_bound += 1

    print target_substring == current_substring_to_check

<|endoftext|>


<commit_before>#
# This file is part of khmer, http://github.com/ged-lab/khmer/, and is
# Copyright (C) Michigan State University, 2014. It is licensed under
# the three-clause BSD license; see doc/LICENSE.txt.
# Contact: khmer-project@idyll.org
#
"""
Tests for various argument-handling code.
"""

import sys
import cStringIO
import khmer_tst_utils as utils

import khmer.file

def test_check_space():
    fakelump_fa = utils.get_test_data('fakelump.fa')

    save_stderr, sys.stderr = sys.stderr, cStringIO.StringIO()
    try:
        khmer.file.check_space([fakelump_fa], _testhook_free_space=0)
        assert 0, "this should fail"
    except SystemExit, e:
        print str(e)
    finally:
        sys.stderr = save_stderr

def test_check_tablespace():
    save_stderr, sys.stderr = sys.stderr, cStringIO.StringIO()
    try:
        khmer.file.check_space_for_hashtable(1e9, _testhook_free_space=0)
        assert 0, "this should fail"
    except SystemExit, e:
        print str(e)
    finally:
        sys.stderr = save_stderr

def test_invalid_file_warn():
    save_stderr, sys.stderr = sys.stderr, cStringIO.StringIO()
    try:
        khmer.file.check_valid_file_exists(["nonexistent", "nonexistent2"])
        assert sys.stderr.getvalue().count("\n") == 2,  \
            "Should produce two warning lines"
    except SystemExit, e:
        print str(e)
    finally:
        sys.stderr = save_stderr
<commit_msg>module name changed, changed code to match<commit_after>#
# This file is part of khmer, http://github.com/ged-lab/khmer/, and is
# Copyright (C) Michigan State University, 2014. It is licensed under
# the three-clause BSD license; see doc/LICENSE.txt.
# Contact: khmer-project@idyll.org
#
"""
Tests for various argument-handling code.
"""

import sys
import cStringIO
import khmer_tst_utils as utils

import khmer.file

def test_check_space():
    fakelump_fa = utils.get_test_data('fakelump.fa')

    save_stderr, sys.stderr = sys.stderr, cStringIO.StringIO()
    try:
        khmer.file.check_space([fakelump_fa], _testhook_free_space=0)
        assert 0, "this should fail"
    except SystemExit, e:
        print str(e)
    finally:
        sys.stderr = save_stderr

def test_check_tablespace():
    save_stderr, sys.stderr = sys.stderr, cStringIO.StringIO()
    try:
        khmer.file.check_space_for_hashtable(1e9, _testhook_free_space=0)
        assert 0, "this should fail"
    except SystemExit, e:
        print str(e)
    finally:
        sys.stderr = save_stderr

def test_invalid_file_warn():
    save_stderr, sys.stderr = sys.stderr, cStringIO.StringIO()
    try:
        khmer.kfile.check_valid_file_exists(["nonexistent", "nonexistent2"])
        assert sys.stderr.getvalue().count("\n") == 2,  \
            "Should produce two warning lines"
    except SystemExit, e:
        print str(e)
    finally:
        sys.stderr = save_stderr
<|endoftext|>


<commit_before>import os
import angr
import shutil
import tempfile
import subprocess
import shellphish_afl
from .fuzzer import Fuzzer

import logging
l = logging.getLogger("fuzzer.Showmap")

class Showmap(object):
    """Show map"""

    def __init__(self, binary_path, testcase, timeout=None):
        """
        :param binary_path: path to the binary which the testcase applies to
        :param testcase: string representing the contents of the testcase
        :param timeout: millisecond timeout
        """

        self.binary_path = binary_path
        self.testcase = testcase
        self.timeout = None

        if timeout is not None:
            if isinstance(timeout, (int, long)):
                self.timeout = str(timeout)
            elif isinstance(timeout, (str)):
                self.timeout = timeout
            else:
                raise ValueError("timeout param must be of type int or str")

        # will be set by showmap's return code
        self.causes_crash = False

        Fuzzer._perform_env_checks()

        self.base = Fuzzer._get_base()
        l.debug("got base dir %s", self.base)

        # unfortunately here is some code reuse between Fuzzer and Minimizer
        p = angr.Project(self.binary_path)
        tracer_id = 'cgc' if p.loader.main_bin.os == 'cgc' else p.arch.qemu_name
        self.showmap_path = os.path.join(shellphish_afl.afl_dir(tracer_id), "afl-showmap")
        self.afl_path_var = shellphish_afl.afl_path_var(tracer_id)

        l.debug("showmap_path: %s", self.showmap_path)
        l.debug("afl_path_var: %s", self.afl_path_var)

        os.environ['AFL_PATH'] = self.afl_path_var

        # create temp
        self.work_dir = tempfile.mkdtemp(prefix='showmap-', dir='/tmp/')

        # flag for work directory removal
        self._removed = False

        self.input_testcase = os.path.join(self.work_dir, 'testcase')
        self.output = os.path.join(self.work_dir, 'out')

        l.debug("input_testcase: %s", self.input_testcase)
        l.debug("output: %s", self.output)

        # populate contents of input testcase
        with open(self.input_testcase, 'w') as f:
            f.write(testcase)

    def __del__(self):
        if not self._removed:
            shutil.rmtree(self.work_dir)

    def showmap(self):
        """Create the map"""

        if self._start_showmap().wait() == 2:
            self.causes_crash = True

        with open(self.output) as f: result = f.read()

        shutil.rmtree(self.work_dir)
        self._removed = True

        shownmap = dict()
        for line in result.split("\n")[:-1]:
            key, h_count = map(int, line.split(":"))
            shownmap[key] = h_count

        return shownmap

    def _start_showmap(self, memory="8G"):

        args = [self.showmap_path]

        args += ["-o", self.output]
        args += ["-m", memory]
        args += ["-Q"]

        if self.timeout:
            args += ["-t", self.timeout]

        args += ["--"]
        args += [self.binary_path]

        outfile = "minimizer.log"

        l.debug("execing: %s > %s", " ".join(args), outfile)

        outfile = os.path.join(self.work_dir, outfile)
        with open(outfile, "w") as fp, open(self.input_testcase, 'rb') as it, open("/dev/null", 'wb') as devnull:
            return subprocess.Popen(args, stdin=it, stdout=devnull, stderr=fp)
<commit_msg>Add theoretical support for MultiCBs<commit_after>import os
import angr
import shutil
import tempfile
import subprocess
import shellphish_afl
from .fuzzer import Fuzzer

import logging
l = logging.getLogger("fuzzer.Showmap")

class Showmap(object):
    """Show map"""

    def __init__(self, binary_path, testcase, timeout=None):
        """
        :param binary_path: path to the binary which the testcase applies to
        :param testcase: string representing the contents of the testcase
        :param timeout: millisecond timeout
        """

        self.binary_path = binary_path
        self.testcase = testcase
        self.timeout = None

        if isinstance(binary_path, basestring):
            self.is_multicb = False
            self.binaries = [binary_path]
        elif isinstance(binary_path, (list,tuple)):
            self.is_multicb = True
            self.binaries = binary_path
        else:
            raise ValueError("Was expecting either a string or a list/tuple for binary_path! "
                "It's {} instead.".format(type(binary_path)))

        if timeout is not None:
            if isinstance(timeout, (int, long)):
                self.timeout = str(timeout)
            elif isinstance(timeout, (str)):
                self.timeout = timeout
            else:
                raise ValueError("timeout param must be of type int or str")

        # will be set by showmap's return code
        self.causes_crash = False

        Fuzzer._perform_env_checks()

        self.base = Fuzzer._get_base()
        l.debug("got base dir %s", self.base)

        # unfortunately here is some code reuse between Fuzzer and Minimizer (and Showmap!)
        p = angr.Project(self.binaries[0])
        tracer_id = 'cgc' if p.loader.main_bin.os == 'cgc' else p.arch.qemu_name
        if self.is_multicb:
            tracer_id = 'multi-{}'.format(tracer_id)

        self.showmap_path = os.path.join(shellphish_afl.afl_dir(tracer_id), "afl-showmap")
        self.afl_path_var = shellphish_afl.afl_path_var(tracer_id)

        l.debug("showmap_path: %s", self.showmap_path)
        l.debug("afl_path_var: %s", self.afl_path_var)

        os.environ['AFL_PATH'] = self.afl_path_var

        # create temp
        self.work_dir = tempfile.mkdtemp(prefix='showmap-', dir='/tmp/')

        # flag for work directory removal
        self._removed = False

        self.input_testcase = os.path.join(self.work_dir, 'testcase')
        self.output = os.path.join(self.work_dir, 'out')

        l.debug("input_testcase: %s", self.input_testcase)
        l.debug("output: %s", self.output)

        # populate contents of input testcase
        with open(self.input_testcase, 'w') as f:
            f.write(testcase)

    def __del__(self):
        if not self._removed:
            shutil.rmtree(self.work_dir)

    def showmap(self):
        """Create the map"""

        if self._start_showmap().wait() == 2:
            self.causes_crash = True

        with open(self.output) as f: result = f.read()

        shutil.rmtree(self.work_dir)
        self._removed = True

        shownmap = dict()
        for line in result.split("\n")[:-1]:
            key, h_count = map(int, line.split(":"))
            shownmap[key] = h_count

        return shownmap

    def _start_showmap(self, memory="8G"):

        args = [self.showmap_path]

        args += ["-o", self.output]
        args += ["-m", memory]
        args += ["-Q"]

        if self.timeout:
            args += ["-t", self.timeout]

        args += ["--"]
        args += self.binaries

        outfile = "minimizer.log"

        l.debug("execing: %s > %s", " ".join(args), outfile)

        outfile = os.path.join(self.work_dir, outfile)
        with open(outfile, "w") as fp, open(self.input_testcase, 'rb') as it, open("/dev/null", 'wb') as devnull:
            return subprocess.Popen(args, stdin=it, stdout=devnull, stderr=fp)
<|endoftext|>