akrherz: Adding a test here that actually generates an exception is rather funny, since if we have parsing code that raises exceptions, we should fix that bug! :slightly_smiling_face: 
akrherz: I have a test in and realize now that known Exceptions can come from other places than just Metar, this all will take some consideration for the API.
akrherz: Thanks @phobson , I'd like to see if @WeatherGod chimes in on this prior to merging.  Will wait a bit while I work on a PR for a formal release.  Or are we still not close enough for what you need for cloudside to use?
phobson: To clear: strictly speaking, this change is not necessary for cloudside to function as intended, but may help preventing ASOS's weirder codes from being completely ignored.

Punting on this in favor of more robust error handling would be fine by me.
akrherz: Thanks @phobson , this change does not impact previous code behavior, so will take this PR now so to proceed with a release.
diff --git a/metar/Metar.py b/metar/Metar.py
index b682d2c..766c485 100755
--- a/metar/Metar.py
+++ b/metar/Metar.py
@@ -324,13 +324,11 @@ class Metar(object):
         provided, then the month and year are guessed from the current date.
       utcdelta : int or datetime.timedelta, optional
         An int of hours or a timedelta object used to specify the timezone.
-      strict : bool (default is True) or str {'raise', 'warn', 'log'}
-        When unparsed or unparseable groups exist in a METAR code, a
-        ``ParserError`` will be raised. Setting this to False will suppress that
-        behavior and will use the standard library's logging module to record
-        all supressed errors. One can then detect if decoding is complete by
-        checking the :attr:`decode_completed` attribute.
-
+      strict : bool (default is True)
+        This option determines if a ``ParserError`` is raised when
+        unparsable groups are found or an unexpected exception is encountered.
+        Setting this to `False` will prevent exceptions from being raised and
+        only generate warning messages.
       """
 
       self.code = metarcode              # original METAR code
@@ -439,9 +437,13 @@ class Metar(object):
 
       except Exception as err:
           message = (
-              "%s failed while processing '%s'\n\t%s" % (handler.__name__, code, "\n\t".join(err.args))
+              ("%s failed while processing '%s'\n\t%s"
+               ) % (handler.__name__, code, "\n\t".join(err.args))
           )
-          raise ParserError(message)
+          if strict:
+              raise ParserError(message)
+          else:
+              warnings.warn(message, RuntimeWarning)
 
       if self._unparsed_groups:
           code = ' '.join(self._unparsed_groups)

Allow `ParserErrors` to not raise
If you're like me and get most of your METAR codes from FAA's ASOS program, you get a *lot* of parser errors.

The `MetarParser` subclass in [cloudside](https://github.com/Geosyntec/cloudside/blob/master/cloudside/station.py#L149) gets around this by simply logging the error and moving on. 

It'd be nice to have this as an *option* upstream.
diff --git a/test/test_metar.py b/test/test_metar.py
index d358061..269cf02 100644
--- a/test/test_metar.py
+++ b/test/test_metar.py
@@ -267,6 +267,13 @@ class MetarTest(unittest.TestCase):
     self.assertEqual( report("MMMMMGMMKT").wind(), "missing" )
     self.assertEqual( report("MMMMMG01KT").wind(), "missing" )
 
+  def test_issue51_strict(self):
+    """Check that setting strict=False prevents a ParserError"""
+    with warnings.catch_warnings(record=True) as w:
+        report = Metar.Metar(sta_time+"90010KT", strict=False)
+    assert len(w) == 1
+    assert report.wind_speed is None
+
   def test_142_parseWind_illegal(self):
     """Check rejection of illegal wind groups."""
     self.raisesParserError( sta_time+"90010KT" )

pip install -e .
parse_log_pytest
pytest
pytest
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
certifi==2021.5.30
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
-e git+https://github.com/python-metar/python-metar.git@94a48ea3b965ed1b38c5ab52553dd4cbcc23867c#egg=metar
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: python-metar
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
prefix: /opt/conda/envs/python-metar

test/test_metar.py::MetarTest::test_issue51_strict
test/test_metar.py::MetarTest::test_010_parseType_default test/test_metar.py::MetarTest::test_011_parseType_legal test/test_metar.py::MetarTest::test_020_parseStation_legal test/test_metar.py::MetarTest::test_021_parseStation_illegal test/test_metar.py::MetarTest::test_030_parseTime_legal test/test_metar.py::MetarTest::test_031_parseTime_specify_year test/test_metar.py::MetarTest::test_032_parseTime_specify_month test/test_metar.py::MetarTest::test_033_parseTime_auto_month test/test_metar.py::MetarTest::test_034_parseTime_auto_year test/test_metar.py::MetarTest::test_035_parseTime_suppress_auto_month test/test_metar.py::MetarTest::test_040_parseModifier_default test/test_metar.py::MetarTest::test_041_parseModifier test/test_metar.py::MetarTest::test_042_parseModifier_nonstd test/test_metar.py::MetarTest::test_043_parseModifier_illegal test/test_metar.py::MetarTest::test_140_parseWind test/test_metar.py::MetarTest::test_141_parseWind_nonstd test/test_metar.py::MetarTest::test_142_parseWind_illegal test/test_metar.py::MetarTest::test_150_parseVisibility test/test_metar.py::MetarTest::test_151_parseVisibility_direction test/test_metar.py::MetarTest::test_152_parseVisibility_with_following_temperature test/test_metar.py::MetarTest::test_290_ranway_state test/test_metar.py::MetarTest::test_300_parseTrend test/test_metar.py::MetarTest::test_310_parse_sky_conditions test/test_metar.py::MetarTest::test_issue40_runwayunits test/test_metar.py::MetarTest::test_not_strict_mode test/test_metar.py::MetarTest::test_snowdepth
metar/Metar.py
metar/Metar.py