docs: def fetch_weather(self, id, metric=False):
          """Fetch a location's weather.
  
          *id* can be either a WOEID or LID. The weather data returned for each
          is identical except that the WOEID returns a 2-day forecast and the LID
          returns a 5-day forecast. The LID uses an undocumented API, so use it
          at your own risk.
  
          Args:
              id: (string) the location's WOEID or LID.
              metric: (bool) return metric data; defaults to False.
  
          Returns:
              a dict containing the location's weather data or None if
                  the weather data couldn't be fetched.
  
          Raises:
              urllib.error.URLError: urllib.request could not open the URL
                  (Python 3).
              urllib2.URLError: urllib2 could not open the URL (Python 2).
              xml.etree.ElementTree.ParseError: xml.etree.ElementTree failed to
                  parse the XML document.
  
          """
  
          units = "c" if metric else "f"
  
          # WOEID is a 32-bit integer, while LID is XXXXNNNN, where X is a letter
          # and N is a number. So, we pick the URL to use based on whether or not
          # the *id* begins with a letter.
  
          if re.match("^[A-Za-z]", id):
              url = LID_WEATHER_URL.format(id, units)
          else:
              url = WEATHER_URL.format(id, units)
  
          rss = self._fetch_xml(url)
  
          if rss.find("channel/item/title").text == "City not found":
              return None
  
          # xml_items details which tags should be read and what their
          # destination dict key should be. These tags don't appear
          # multiple times.
          # {XML tag: [ElementTree access method, dict key]}
  
          xml_items = {
              "channel/title": ["text", "title"],
              "channel/link": ["text", "link"],
              "channel/language": ["text", "language"],
              "channel/description": ["text", "description"],
              "channel/lastBuildDate": ["text", "lastBuildDate"],
              "channel/ttl": ["text", "ttl"],
              "channel/image/url": ["text", "logo"],
              "channel/item/guid": ["text", "guid"],
              "channel/{%s}location" % WEATHER_NS:
                  ["attrib", "location"],
              # "channel/{%s}units" % WEATHER_NS:
              #     ["attrib", "units"],
              "channel/{%s}wind" % WEATHER_NS:
                  ["attrib", "wind"],
              "channel/{%s}atmosphere" % WEATHER_NS:
                  ["attrib", "atmosphere"],
              "channel/{%s}astronomy" % WEATHER_NS:
                  ["attrib", "astronomy"],
              "channel/item/{%s}condition" % WEATHER_NS:
                  ["attrib", "condition"],
          }
          weather = {}
          weather["units"] = UNITS[units]
  
          for (tag, meta) in xml_items.items():
              if meta[0] == "text":
                  try:
                      weather[meta[1]] = rss.find(tag).text
                  except AttributeError:
                      weather[meta[1]] = None
              elif meta[0] == "attrib":
                  try:
                      weather[meta[1]] = rss.find(tag).attrib
                  except AttributeError:
                      weather[meta[1]] = None
              else:
                  weather[meta[1]] = None
  
          try:
              image_url = CONDITION_IMAGE_URL.format(weather["condition"]["code"])
              weather["condition"]["image"] = image_url
          except (AttributeError, TypeError):
              pass
  
          try:
              state = weather["atmosphere"]["rising"]
              if state == "0":
                  weather["atmosphere"]["state"] = "steady"
              elif state == "1":
                  weather["atmosphere"]["state"] = "rising"
              elif state == "2":
                  weather["atmosphere"]["state"] = "falling"
              else:
                  weather["atmosphere"]["state"] = None
          except (AttributeError, TypeError):
              pass
  
          weather["forecast"] = []
          try:
              for item in rss.findall(
                      "channel/item/{%s}forecast" % WEATHER_NS):
                  weather["forecast"].append(item.attrib)
          except AttributeError:
              weather["forecast"] = None
  
          weather["geo"] = {}
          try:
              weather["geo"]["lat"] = rss.find(
                  "channel/item/{%s}lat" % GEO_NS).text
              weather["geo"]["long"] = rss.find(
                  "channel/item/{%s}long" % GEO_NS).text
          except AttributeError:
              weather["geo"] = None
  
          try:
              weather["wind"]["compass"] = self._degrees_to_direction(
                  weather["wind"]["direction"])
          except TypeError:
              pass
  
          return weather