planet.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import re
import subprocess

import rose.macro


class PlanetChecker(rose.macro.MacroBase):

    """Checks option values that refer to planets."""

    error_text = "planet is too far away."
    opts_to_check = [("env", "WORLD")]

    def validate(self, config, meta_config=None):
        """Return a list of errors, if any."""
        allowed_planets = self._get_allowed_planets()
        for section, option in self.opts_to_check:
            node = config.get([section, option])
            if node is None or node.is_ignored():
                continue
            if node.value not in allowed_planets:
                self.add_report(section, option, node.value, self.error_text)
        return self.reports

    def _get_allowed_planets(self):
        # Retrieve planets less than a certain distance away.
        cmd_strings = ["curl", "-s",
                       "http://www.heavens-above.com/planetsummary.aspx"]
        p = subprocess.Popen(cmd_strings, stdout=subprocess.PIPE)
        text = p.communicate()[0]
        planets = re.findall("(\w+)</td>",
                             re.sub(r'(?s)^.*(<thead.*?ascension).*$',
                                    r"\1", text))
        distances = re.findall("([\d.]+)</td>",
                               re.sub('(?s)^.*(Range.*?Brightness).*$',
                                      r"\1", text))
        for planet, distance in zip(planets, distances):
            if float(distance) > 5.0:
                # The planet is more than 5 AU away.
                planets.remove(planet)
        planets += ["Earth"]  # Distance ~ 0
        return planets