Repository: python-metar/python-metar (language: python)

Issue:
 DeprecationWarning('datetime.utcnow() is deprecated
In preparation for Fedora 39 we have been building and testing python modules for the upcoming python3.12 version (tested with Python 3.12.0b3).
For python-metar this resulted in 2 test failures:

```
# pytest-3
================================== test session starts ===================================
platform linux -- Python 3.12.0b3, pytest-7.3.2, pluggy-1.0.0
rootdir: /builddir/build/BUILD/metar-1.10.0
configfile: setup.cfg
collected 70 items                                                                       

test/test_direction.py ...                                                         [  4%]
test/test_distance.py ....                                                         [ 10%]
test/test_metar.py ..................................F........F.....               [ 80%]
test/test_precipitation.py .                                                       [ 81%]
test/test_pressure.py ....                                                         [ 87%]
test/test_speed.py ....                                                            [ 92%]
test/test_station.py .                                                             [ 94%]
test/test_temperature.py ....                                                      [100%]

======================================== FAILURES ========================================
__________________________________ test_issue51_strict ___________________________________

    def test_issue51_strict():
        """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
E       assert 2 == 1
E        +  where 2 = len([<warnings.WarningMessage object at 0x7f74e47e87a0>, <warnings.WarningMessage object at 0x7f74e47e8890>])

test/test_metar.py:401: AssertionError
__________________________________ test_not_strict_mode __________________________________

    def test_not_strict_mode():
        """Test the strict attribute on parsing."""
        # 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 $"
        raisesParserError(code)
    
        with warnings.catch_warnings(record=True) as w:
            report = Metar.Metar(code, strict=False)
>       assert len(w) == 1
E       assert 2 == 1
E        +  where 2 = len([<warnings.WarningMessage object at 0x7f74e3e21580>, <warnings.WarningMessage object at 0x7f74e3e20b30>])

test/test_metar.py:638: AssertionError
==================================== warnings summary ====================================
../../../../usr/lib/python3.12/site-packages/_pytest/config/__init__.py:1314
  /usr/lib/python3.12/site-packages/_pytest/config/__init__.py:1314: PytestConfigWarning: Unknown config option: pep8ignore
  
    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")

../../../../usr/lib/python3.12/site-packages/_pytest/config/__init__.py:1314
  /usr/lib/python3.12/site-packages/_pytest/config/__init__.py:1314: PytestConfigWarning: Unknown config option: pep8maxlinelength
  
    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")

test/test_metar.py:14
  /builddir/build/BUILD/metar-1.10.0/test/test_metar.py:14: DeprecationWarning: datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.now(datetime.UTC).
    today = datetime.utcnow()

test/test_metar.py: 209 warnings
  /builddir/build/BUILDROOT/python-metar-1.10.0-1.fc39.x86_64/usr/lib/python3.12/site-packages/metar/Metar.py:420: DeprecationWarning: datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.now(datetime.UTC).
    self._now = datetime.datetime.utcnow()

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================ short test summary info =================================
FAILED test/test_metar.py::test_issue51_strict - assert 2 == 1
FAILED test/test_metar.py::test_not_strict_mode - assert 2 == 1
======================= 2 failed, 68 passed, 212 warnings in 0.41s =======================
```

After inspecting the failing tests and adding some test prints in them it became clear that a new DeprecationWarning is issued which confuses the warning count tested by these 2 tests.
For example the call to `Metar.Metar(code, strict=False) ` in test `test_not_strict_mode()` now triggers 2 warnings in stead of just one. After inserting the lines
```
for warn in w:
        print('DEBUG: warn=', warn)
```
just after the with block I see this output:

'''
DEBUG: warn= {message : DeprecationWarning('datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.now(datetime.UTC).'), category : 'DeprecationWarning', filename : '/builddir/build/BUILDROOT/python-metar-1.10.0-1.fc39.x86_64/usr/lib/python3.12/site-packages/metar/Metar.py', lineno : 420, line : None}
DEBUG: warn= {message : RuntimeWarning("Unparsed groups in body 'M' while processing 'K9L2 100958Z AUTO 33006KT 10SM CLR M A3007 RMK AO2 SLPNO FZRANO $'"), category : 'RuntimeWarning', filename : '/builddir/build/BUILDROOT/python-metar-1.10.0-1.fc39.x86_64/usr/lib/python3.12/site-packages/metar/Metar.py', lineno : 502, line : None}
'''



Discussion / hints:
The following edits solve the problem for this python3.12 version, but I guess they will not be compatible with earlier python versions:

file metar/Metar.py
replace line 3
`from datetime import datetime, timedelta`
with
`from datetime import datetime, timedelta, UTC`
and replace line 14
`today = datetime.utcnow()`
with
`today = datetime.now(UTC)`

file test/test_metar.py
replace line 420
`self._now = datetime.datetime.utcnow()`
with
`self._now = datetime.datetime.now()`
and replace lines 424-425
```
self._utcdelta = datetime.datetime.now() - self._now
self._utcdelta = now.hour - now_utc.hour
```
with
```
now = datetime.datetime.now()
now_utc = datetime.datetime.now(datetime.UTC)
self._utcdelta = now.hour - now_utc.hour
```

Thank you for the excellent report.  I'm boggling a path forward, including changing CI infrastructure so that a python 3.12 version can be readily added to test matrix.
Thanks a lot, looking forward to that.

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 55b740b..da46529 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,24 +6,30 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: ["3.8", "3.9", "3.10", "3.11"]
+        python-version:
+          - '3.8'
+          - '3.9'
+          - '3.10'
+          - '3.11'
+          - '3.12-dev'
     steps:
     - uses: actions/checkout@v3
-
-    - name: Monolith Workflow
+    - uses: actions/setup-python@v4
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install Package
       run: |
-        wget -q https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-x86_64.sh -O miniforge.sh
-        bash miniforge.sh -f -b -p $HOME/miniconda
-        . $HOME/miniconda/etc/profile.d/conda.sh
-        conda config --set quiet True --set always_yes yes --set changeps1 no
-        conda create --name testenv --yes python=${{ matrix.python-version }} pip six pytest pytest-runner pytest-cov pytest-runner
-        conda activate testenv
+        python -m pip install --upgrade setuptools setuptools_scm wheel
         python -m pip install .
+    - name: Run Tests
+      run: |
+        python -m pip install pytest pytest-runner pytest-cov pytest-runner
         python setup.py test --addopts " --cov=metar"
         if [[ ${{ matrix.python-version }} == "3.8" ]]; then
           pip install codecov
           codecov
         fi
+
   typing:
     name: Typing Check
     runs-on: ubuntu-latest
diff --git a/metar/Metar.py b/metar/Metar.py
index e5d113d..01c436f 100644
--- a/metar/Metar.py
+++ b/metar/Metar.py
@@ -417,7 +417,7 @@ def __init__(self, metarcode, month=None, year=None, utcdelta=None, strict=True)
         self._unparsed_groups = []
         self._unparsed_remarks = []
 
-        self._now = datetime.datetime.utcnow()
+        self._now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
         if utcdelta:
             self._utcdelta = utcdelta
         else:
diff --git a/misc/getstation.py b/misc/getstation.py
index 1f5ccc8..d52e9a0 100755
--- a/misc/getstation.py
+++ b/misc/getstation.py
@@ -31,7 +31,7 @@ def usage():
     sys.exit(1)
 
 
-today = datetime.datetime.utcnow()
+today = datetime.datetime.now(datetime.timezone.utc)
 
 stations = []
 pipe = False


Test patch:
diff --git a/test/test_metar.py b/test/test_metar.py
index ae1edea..53c1148 100644
--- a/test/test_metar.py
+++ b/test/test_metar.py
@@ -1,6 +1,6 @@
 """Test the main Metar Library."""
 import warnings
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
 
 import pytest
 import metar
@@ -11,7 +11,7 @@
 sta_time_mod = "KEWR 101651Z AUTO "
 sta_time_wind = "KEWR 101651Z 00000KT "
 
-today = datetime.utcnow()
+today = datetime.now(timezone.utc)
 tomorrow = today + timedelta(days=1)