8000 ENH: optional suppression of ParserError for undecoded non-remark ele… by WeatherGod · Pull Request #36 · python-metar/python-metar · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 34 additions & 5 deletions metar/Metar.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@

import re
import datetime
import warnings

from metar.Datatypes import *

## Exceptions
Expand Down Expand Up @@ -305,8 +307,23 @@ def _unparsedGroup( self, d ):
class Metar(object):
"""METAR (aviation meteorology report)"""

def __init__( self, metarcode, month=None, year=None, utcdelta=None):
"""Parse raw METAR code."""
def __init__(self, metarcode, month=None, year=None, utcdelta=None,
strict=True):
"""
Parse raw METAR code.

Can also provide a *month* and/or *year* for unambigous date
determination. If not provided, then the month and year are
guessed from the current date. The *utcdelta* (in hours) is
reserved for future use for handling time-zones.

By default, the constructor will raise a `ParserError` if there are
non-remark elements that were left undecoded. However, one can pass
*strict=False* keyword argument to suppress raising this
exception. One can then detect if decoding is complete by checking
the :attr:`decode_completed` attribute.

"""
self.code = metarcode # original METAR code
self.type = 'METAR' # METAR (routine) or SPECI (special)
self.mod = "AUTO" # AUTO (automatic) or COR (corrected)
Expand Down Expand Up @@ -405,11 +422,23 @@ def __init__( self, metarcode, month=None, year=None, utcdelta=None):
break

except Exception as err:
raise ParserError(handler.__name__+" failed while processing '"+code+"'\n"+" ".join(err.args))
raise err
raise ParserError(handler.__name__+" failed while processing '"+
code+"'\n"+" ".join(err.args))

if self._unparsed_groups:
code = ' '.join(self._unparsed_groups)
raise ParserError("Unparsed groups in body '"+code+"' while processing '"+metarcode+"'")
message = "Unparsed groups in body '%s' while processing '%s'" % (code, metarcode)
if strict:
raise ParserError(message)
else:
warnings.warn(message, RuntimeWarning)

@property
def decode_completed(self):
"""
Indicate whether the decoding was complete for non-remark elements.
"""
return not self._unparsed_groups

def _do_trend_handlers(self, code):
for pattern, handler, repeatable in self.trend_handlers:
Expand Down
40 changes: 37 additions & 3 deletions test/test_metar.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import unittest
import warnings

from metar import Metar

# METAR fragments used in tests, below
Expand All @@ -12,9 +14,6 @@

class MetarTest(unittest.TestCase):

def raisesParserError(self, code):
self.assertRaises(Metar.ParserError, Metar.Metar, code )

def raisesParserError(self, code):
self.assertRaises(Metar.ParserError, Metar.Metar, code )

Expand Down Expand Up @@ -46,6 +45,7 @@ def test_021_parseStation_illegal(self):
def test_030_parseTime_legal(self):
"""Check parsing of the time stamp."""
report = Metar.Metar("KEWR 101651Z")
assert report.decode_completed
self.assertEqual( report.time.day, 10 )
self.assertEqual( report.time.hour, 16 )
self.assertEqual( report.time.minute, 51 )
Expand All @@ -59,6 +59,7 @@ def test_031_parseTime_specify_year(self):
other_year = 2003

report = Metar.Metar("KEWR 101651Z", year=other_year)
assert report.decode_completed
self.assertEqual( report.time.year, other_year )

def test_032_parseTime_specify_month(self):
Expand All @@ -67,6 +68,7 @@ def test_032_parseTime_specify_month(self):
last_year = today.year - 1

report = Metar.Metar("KEWR 101651Z", month=last_month)
assert report.decode_completed
self.assertEqual( report.time.month, last_month )

def test_033_parseTime_auto_month(self):
Expand All @@ -78,6 +80,7 @@ def test_033_parseTime_auto_month(self):

timestr = "%02d1651Z" % (next_day)
report = Metar.Metar("KEWR " + timestr)
assert report.decode_completed
self.assertEqual( report.time.day, next_day )
self.assertEqual( report.time.month, last_month )
if today.month > 1:
Expand All @@ -91,6 +94,7 @@ def test_034_parseTime_auto_year(self):
last_year = today.year - 1

report = Metar.Metar("KEWR 101651Z", month=next_month)
assert report.decode_completed
self.assertEqual( report.time.month, next_month )
if next_month > 1:
self.assertEqual( report.time.year, last_year )
Expand All @@ -106,6 +110,7 @@ def test_035_parseTime_suppress_auto_month(self):

timestr = "%02d1651Z" % (next_day)
report = Metar.Metar("KEWR " + timestr, month=1)
assert report.decode_completed
self.assertEqual( report.time.day, next_day )
self.assertEqual( report.time.month, 1 )
if today.month > 1:
Expand Down Expand Up @@ -152,6 +157,7 @@ def test_043_parseModifier_illegal(self):
def test_140_parseWind(self):
"""Check parsing of wind groups."""
report = Metar.Metar(sta_time+"09010KT" )
assert report.decode_completed
self.assertEqual( report.wind_dir.value(), 90 )
self.assertEqual( report.wind_speed.value(), 10 )
self.assertEqual( report.wind_gust, None )
Expand All @@ -160,35 +166,42 @@ def test_140_parseWind(self):
self.assertEqual( report.wind(), "E at 10 knots" )

report = Metar.Metar(sta_time+"09010MPS" )
assert report.decode_completed
self.assertEqual( report.wind_speed.value(), 10 )
self.assertEqual( report.wind_speed.value("KMH"), 36 )
self.assertEqual( report.wind(), "E at 19 knots" )
self.assertEqual( report.wind("MPS"), "E at 10 mps" )
self.assertEqual( report.wind("KMH"), "E at 36 km/h" )

report = Metar.Metar(sta_time+"09010KMH" )
assert report.decode_completed
self.assertEqual( report.wind_speed.value(), 10 )
self.assertEqual( report.wind(), "E at 5 knots" )
self.assertEqual( report.wind('KMH'), "E at 10 km/h" )

report = Metar.Metar(sta_time+"090010KT" )
assert report.decode_completed
self.assertEqual( report.wind_dir.value(), 90 )
self.assertEqual( report.wind_speed.value(), 10 )

report = Metar.Metar(sta_time+"000000KT" )
assert report.decode_completed
self.assertEqual( report.wind_dir.value(), 0 )
self.assertEqual( report.wind_speed.value(), 0 )
self.assertEqual( report.wind(), "calm" )

report = Metar.Metar(sta_time+"VRB03KT" )
assert report.decode_completed
self.assertEqual( report.wind_dir, None )
self.assertEqual( report.wind_speed.value(), 3 )
self.assertEqual( report.wind(), "variable at 3 knots" )

report = Metar.Metar(sta_time+"VRB00KT" )
assert report.decode_completed
self.assertEqual( report.wind(), "calm" )

report = Metar.Metar(sta_time+"VRB03G40KT" )
assert report.decode_completed
self.assertEqual( report.wind_dir, None )
self.assertEqual( report.wind_speed.value(), 3 )
self.assertEqual( report.wind_gust.value(), 40 )
Expand All @@ -197,6 +210,7 @@ def test_140_parseWind(self):
self.assertEqual( report.wind(), "variable at 3 knots, gusting to 40 knots" )

report = Metar.Metar(sta_time+"21010G30KT" )
assert report.decode_completed
self.assertEqual( report.wind(), "SSW at 10 knots, gusting to 30 knots" )

report = Metar.Metar(sta_time+"21010KT 180V240" )
Expand Down Expand Up @@ -440,6 +454,26 @@ def report(sky_conditions):
self.assertEqual( report('CLR').sky_conditions(), 'clear' )
self.assertEqual( report('NSC').sky_conditions(), 'clear' )

def test_not_strict_mode(self):
# This example metar has an extraneous 'M' in it, but the rest is fine
# Let's make sure that we can activate a non-strict mode, and flag that there
# are unparsed portions
code = 'K9L2 100958Z AUTO 33006KT 10SM CLR M A3007 RMK AO2 SLPNO FZRANO $'
self.raisesParserError(code)

with warnings.catch_warnings(record=True) as w:
report = Metar.Metar(code, strict=False)
assert len(w) == 1

assert not report.decode_completed
self.assertEqual( report.cycle, 10 )
self.assertEqual( report.mod, 'AUTO' )
self.assertEqual( report.recent, [] )
self.assertEqual( report.station_id, 'K9L2' )
self.assertEqual( report.vis.value(), 10 )
self.assertEqual( report.sky_conditions(), 'clear' )


if __name__=='__main__':
unittest.main( )

0