Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Python 3.12 #3223

Merged
merged 14 commits into from
Nov 6, 2023
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
11 changes: 8 additions & 3 deletions .github/actions/install-pypi/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ inputs:
python-version:
description: 'What version of Python to use'
required: true
old-build:
description: 'Whether to enable old builds for shapely and cartopy'
required: false
default: 'false'

runs:
using: composite
Expand All @@ -30,13 +34,13 @@ runs:
ci/extra_requirements.txt

# This installs the stuff needed to build and install Shapely and CartoPy from source.
- name: Install CartoPy build dependencies
if: ${{ inputs.need-extras == 'true' }}
- name: Install CartoPy/Shapely build dependencies
if: ${{ inputs.old-build == 'true' }}
shell: bash
run: sudo apt-get install libgeos-dev

- name: Disable Shapely Wheels
if: ${{ inputs.need-extras == 'true' }}
if: ${{ inputs.old-build == 'true' }}
shell: bash
run: echo "PIP_NO_BINARY=shapely" >> $GITHUB_ENV

Expand All @@ -57,6 +61,7 @@ runs:
-c ci/${{ inputs.version-file }} -c ci/${{ inputs.type }}_requirements.txt -c ci/extra_requirements.txt

- name: Install additional test tools
if: ${{ inputs.type == 'test' }}
shell: bash
run: >
python -m pip install coverage
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/docs-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:
os: Windows
- python-version: 3.11
os: macOS
- python-version: 3.12
os: macOS

steps:
- name: Checkout source
Expand Down
14 changes: 5 additions & 9 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,16 @@ jobs:
# Build our docs on Linux against multiple Pythons
#
Docs:
name: ${{ matrix.python-version }} ${{ matrix.dep-versions }}
name: "Linux ${{ matrix.python-version }}"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [3.9, '3.10', 3.11]
check-links: [false]
include:
- python-version: 3.9
check-links: false
dep-versions: requirements.txt
- python-version: '3.10'
check-links: false
dep-versions: requirements.txt
- python-version: 3.11
- python-version: 3.12
check-links: true
dep-versions: requirements.txt
outputs:
doc-version: ${{ steps.build-docs.outputs.doc-version }}

Expand All @@ -52,6 +47,7 @@ jobs:
with:
type: 'doc'
python-version: ${{ matrix.python-version }}
need-extras: true

- name: Build docs
id: build-docs
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/nightly-builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ on:
- main
paths:
- .github/workflows/nightly-builds.yml
- .github/workflows/unstable-builds.yml
pull_request:
paths:
- .github/workflows/nightly-builds.yml
- .github/workflows/unstable-builds.yml

jobs:
Builds:
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/tests-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.9, '3.10', 3.11]
python-version: [3.9, 3.12]
os: [macOS, Windows]
include:
- python-version: '3.10'
os: macOS
- python-version: 3.11
os: Windows

steps:
- name: Checkout source
Expand Down Expand Up @@ -66,3 +71,4 @@ jobs:
uses: codecov/codecov-action@v3
with:
name: ${{ github.workflow }}
fail_ci_if_error: true
7 changes: 5 additions & 2 deletions .github/workflows/tests-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.9, '3.10', 3.11]
python-version: [3.9, '3.10', 3.11, 3.12]
dep-versions: [Latest]
no-extras: ['']
include:
Expand All @@ -34,7 +34,8 @@ jobs:
- python-version: 3.9
dep-versions: Minimum
no-extras: 'No Extras'
- python-version: 3.11
- python-version: 3.12
dep-versions: Latest
no-extras: 'No Extras'

steps:
Expand Down Expand Up @@ -71,6 +72,7 @@ jobs:
need-extras: ${{ matrix.no-extras != 'No Extras' }}
type: 'test'
python-version: ${{ matrix.python-version }}
old-build: ${{ matrix.no-extras != 'No Extras' && matrix.dep-versions == 'Minimum' }}

- name: Run tests
uses: ./.github/actions/run-tests
Expand All @@ -94,3 +96,4 @@ jobs:
uses: codecov/codecov-action@v3
with:
name: ${{ github.workflow }}
fail_ci_if_error: true
4 changes: 2 additions & 2 deletions .github/workflows/unstable-builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
need-extras: true
type: test
version-file: Prerelease
python-version: 3.11
python-version: 3.12

- name: Run tests
id: tests
Expand Down Expand Up @@ -72,7 +72,7 @@ jobs:
with:
type: doc
version-file: Prerelease
python-version: 3.11
python-version: 3.12

- name: Build docs
id: build
Expand Down
9 changes: 4 additions & 5 deletions docs/api/references.rst
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,10 @@ References

.. [WMO8] WMO, 2020: Guide to Meteorological Instruments and Methods of Observation,
Volume 1: Measurement of Meteorological Variables.
`WMO No.8 <https://library.wmo.int/index.php?lvl=notice_display&id=12407>`_.
`WMO No.8 <https://library.wmo.int/idurl/4/41650>`_.

.. [WMO306] WMO, 2011: Manual on Codes - International Codes, Volume I.1, Annex II to the WMO
Technical Regulations: Part A - Alphanumeric Codes. `WMO No.306
<https://library.wmo.int/index.php?lvl=notice_display&id=13617>`_.
Technical Regulations: Part A - Alphanumeric Codes. `WMO No.306 <https://library.wmo.int/idurl/4/35713>`_.

.. [WMO1966] WMO, 1966: International Meteorological Tables, `WMO-No. 188.TP.94
<https://library.wmo.int/doc_num.php?explnum_id=7997>`_.
.. [WMO1966] WMO, 1966: International Meteorological Tables,
`WMO-No. 188.TP.94 <https://library.wmo.int/idurl/4/59923>`_.
12 changes: 8 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@
'reset_modules': [lambda conf, fname: sys.modules.pop('pint', None)]
}

# By default, only generate all the areas when running in CI
metpy_generate_all_areas = 'GITHUB_ACTIONS' in os.environ
# By default, only generate all the areas when running on a release CI job
metpy_generate_all_areas = (not os.environ.get('GITHUB_REF', '').startswith('refs/pull')
and sys.version_info < (3, 12))

# Turn off code and image links for embedded mpl plots
plot_html_show_source_link = False
Expand Down Expand Up @@ -431,7 +432,9 @@
r'https://doi\.org/10\.1289/ehp\.1206273',
# Couldn't fix these 403's with user agents
r'https://doi\.org/10\.1029/2010GL045777',
r'https://doi\.org/10\.1098/rspa\.2004\.1430'
r'https://doi\.org/10\.1098/rspa\.2004\.1430',
# Currently giving certificate errors on GitHub
r'https://library.wmo.int/.*'
]

# Dictionary of URL redirects allowed
Expand All @@ -440,7 +443,8 @@
r'https://conda.io/docs/': r'https://conda.io/en/latest/',
r'https://github.com/Unidata/MetPy/issues/new/choose': r'https://github.com/login.*choose',
r'https://doi.org/.*': r'https://.*',
r'https://gitter.im/Unidata/MetPy': r'https://app.gitter.im/.*MetPy.*'
r'https://gitter.im/Unidata/MetPy': r'https://app.gitter.im/.*MetPy.*',
r'https://library.wmo.int/idurl/.*': r'https://library.wmo.int/.*'
}

# Domain-specific HTTP headers for requests
Expand Down
2 changes: 2 additions & 0 deletions docs/make_areas.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def generate_area_file(app):
if area in states_provinces:
code = textwrap.dedent(f"""
.. plot::
:context: reset

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
Expand All @@ -75,6 +76,7 @@ def generate_area_file(app):
else:
code = textwrap.dedent(f"""
.. plot::
:context: reset

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ classifiers = [
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Atmospheric Science",
"Intended Audience :: Science/Research",
Expand Down
28 changes: 19 additions & 9 deletions src/metpy/io/_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def open_as_needed(filename, mode='rb'):
return open(filename, mode, **kwargs) # noqa: SIM115


class NamedStruct(Struct):
class NamedStruct:
"""Parse bytes using :class:`Struct` but provide named fields."""

def __init__(self, info, prefmt='', tuple_name=None):
Expand All @@ -73,7 +73,12 @@ def __init__(self, info, prefmt='', tuple_name=None):
elif not i[0]: # Skip items with no name
conv_off += 1
self._tuple = namedtuple(tuple_name, ' '.join(n for n in names if n))
super().__init__(prefmt + ''.join(f for f in fmts if f))
self._struct = Struct(prefmt + ''.join(f for f in fmts if f))

@property
def size(self):
"""Return the size of the struct in bytes."""
return self._struct.size

def _create(self, items):
if self.converters:
Expand All @@ -90,11 +95,11 @@ def make_tuple(self, *args, **kwargs):

def unpack(self, s):
"""Parse bytes and return a namedtuple."""
return self._create(super().unpack(s))
return self._create(self._struct.unpack(s))

def unpack_from(self, buff, offset=0):
"""Read bytes from a buffer and return as a namedtuple."""
return self._create(super().unpack_from(buff, offset))
return self._create(self._struct.unpack_from(buff, offset))

def unpack_file(self, fobj):
"""Unpack the next bytes from a file object."""
Expand All @@ -103,12 +108,12 @@ def unpack_file(self, fobj):
def pack(self, **kwargs):
"""Pack the arguments into bytes using the structure."""
t = self.make_tuple(**kwargs)
return super().pack(*t)
return self._struct.pack(*t)


# This works around times when we have more than 255 items and can't use
# NamedStruct. This is a CPython limit for arguments.
class DictStruct(Struct):
class DictStruct:
"""Parse bytes using :class:`Struct` but provide named fields using dictionary access."""

def __init__(self, info, prefmt=''):
Expand All @@ -118,18 +123,23 @@ def __init__(self, info, prefmt=''):
# Remove empty names
self._names = [n for n in names if n]

super().__init__(prefmt + ''.join(f for f in formats if f))
self._struct = Struct(prefmt + ''.join(f for f in formats if f))

@property
def size(self):
"""Return the size of the struct in bytes."""
return self._struct.size

def _create(self, items):
return dict(zip(self._names, items))

def unpack(self, s):
"""Parse bytes and return a dict."""
return self._create(super().unpack(s))
return self._create(self._struct.unpack(s))

def unpack_from(self, buff, offset=0):
"""Unpack the next bytes from a file object."""
return self._create(super().unpack_from(buff, offset))
return self._create(self._struct.unpack_from(buff, offset))


class Enum:
Expand Down