aemo-mdff-reader 2.0.4__tar.gz → 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/CHANGELOG.md +23 -0
  2. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/CONTRIBUTING.md +26 -0
  3. {aemo_mdff_reader-2.0.4/aemo_mdff_reader.egg-info → aemo_mdff_reader-2.2.0}/PKG-INFO +7 -11
  4. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/README.md +2 -4
  5. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader/__init__.py +1 -1
  6. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader/cli.py +1 -0
  7. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader/sql/__init__.py +6 -0
  8. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader/sql/store.py +11 -3
  9. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0/aemo_mdff_reader.egg-info}/PKG-INFO +7 -11
  10. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader.egg-info/requires.txt +3 -3
  11. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/pyproject.toml +12 -19
  12. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/test_parser.py +6 -3
  13. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/test_spec.py +7 -2
  14. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/test_sql.py +1 -1
  15. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/LICENSE +0 -0
  16. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/MANIFEST.in +0 -0
  17. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader/aggregate.py +0 -0
  18. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader/parser.py +0 -0
  19. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader/py.typed +0 -0
  20. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader/reader.py +0 -0
  21. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader/spec.py +0 -0
  22. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader/sql/query.py +0 -0
  23. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader/types.py +0 -0
  24. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader.egg-info/SOURCES.txt +0 -0
  25. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader.egg-info/dependency_links.txt +0 -0
  26. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader.egg-info/entry_points.txt +0 -0
  27. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/aemo_mdff_reader.egg-info/top_level.txt +0 -0
  28. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/benchmarks/bench_parser.py +0 -0
  29. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/setup.cfg +0 -0
  30. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/setup.py +0 -0
  31. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/__init__.py +0 -0
  32. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/fixtures/generate.py +0 -0
  33. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/fixtures/sample_nem12.csv +0 -0
  34. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/fixtures/sample_nem13.csv +0 -0
  35. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/test_cli.py +0 -0
  36. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/test_datetime_formats.py +0 -0
  37. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/test_edge_cases.py +0 -0
  38. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/test_ergonomics.py +0 -0
  39. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/test_events_b2b.py +0 -0
  40. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/test_large_files.py +0 -0
  41. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/test_nem13.py +0 -0
  42. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.2.0}/tests/test_validation.py +0 -0
@@ -4,6 +4,29 @@ All notable changes are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
5
5
  project follows [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [2.2.0](https://github.com/Utilified/aemo-mdff-reader/compare/v2.1.0...v2.2.0) (2026-05-10)
8
+
9
+
10
+ ### ⚠ BREAKING CHANGES
11
+
12
+ * minimum Python version is now 3.11. Users on 3.10 should pin to `aemo-mdff-reader<2.2`.
13
+
14
+ ### Features
15
+
16
+ * drop Python 3.10, bump dev floor to pandas 3 / numpy 2.x ([876c863](https://github.com/Utilified/aemo-mdff-reader/commit/876c863186a7de14b05fa35b0a864b38e4292701))
17
+
18
+ ## [2.1.0](https://github.com/Utilified/aemo-mdff-reader/compare/v2.0.4...v2.1.0) (2026-05-10)
19
+
20
+
21
+ ### Features
22
+
23
+ * drop Python 3.9, hash-pin CI installs, scope release-please permissions ([447ec98](https://github.com/Utilified/aemo-mdff-reader/commit/447ec98408edabfab45d28841a2e714d54f8dd11))
24
+
25
+
26
+ ### Documentation
27
+
28
+ * remove broken OpenSSF Best Practices badge ([#21](https://github.com/Utilified/aemo-mdff-reader/issues/21)) ([91a68d5](https://github.com/Utilified/aemo-mdff-reader/commit/91a68d5a4b4fd4c027488b4fe770c4cd142a7454))
29
+
7
30
  ## [2.0.4](https://github.com/Utilified/aemo-mdff-reader/compare/v2.0.3...v2.0.4) (2026-05-10)
8
31
 
9
32
  First release published to PyPI after the supply-chain hardening landed
@@ -24,6 +24,32 @@ pip install -e .[dev]
24
24
  pytest
25
25
  ```
26
26
 
27
+ ## CI requirements files
28
+
29
+ `requirements/{dev,lint,build,audit}.txt` are hash-pinned lock files
30
+ consumed by CI (`pip install --require-hashes`). They satisfy
31
+ Scorecard's `PinnedDependenciesID` and protect CI against a compromised
32
+ PyPI mirror. Dependabot refreshes them weekly.
33
+
34
+ Source `.in` files are committed alongside the locks in `requirements/`.
35
+ To regenerate manually after editing them or `pyproject.toml`:
36
+
37
+ ```bash
38
+ pip install uv
39
+ uv pip compile --generate-hashes --strip-extras --python-version 3.11 --output-file requirements/dev.txt requirements/dev.in
40
+ uv pip compile --generate-hashes --strip-extras --python-version 3.11 --output-file requirements/lint.txt requirements/lint.in
41
+ uv pip compile --generate-hashes --strip-extras --python-version 3.11 --output-file requirements/build.txt requirements/build.in
42
+ uv pip compile --generate-hashes --strip-extras --python-version 3.11 --output-file requirements/audit.txt requirements/audit.in
43
+ ```
44
+
45
+ `uv` is used over `pip-tools` because its resolver handles the
46
+ 3.11–3.12 matrix without back-tracking failures, and it emits hashes
47
+ for every wheel of each pinned version (so a single lock file works
48
+ across the whole CI matrix). The `--python-version 3.11` flag pins
49
+ resolution to the lowest supported interpreter so packages whose
50
+ latest releases dropped support for it are correctly stepped back
51
+ to a compatible version.
52
+
27
53
  ## Running the test suite
28
54
 
29
55
  ```bash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aemo-mdff-reader
3
- Version: 2.0.4
3
+ Version: 2.2.0
4
4
  Summary: Fast streaming reader for AEMO MDFF NEM12 / NEM13 metering files. Zero required dependencies.
5
5
  Author: Utilified Holdings Pty Ltd
6
6
  License-Expression: MIT
@@ -15,28 +15,26 @@ Classifier: Intended Audience :: Information Technology
15
15
  Classifier: Operating System :: OS Independent
16
16
  Classifier: Programming Language :: Python :: 3
17
17
  Classifier: Programming Language :: Python :: 3 :: Only
18
- Classifier: Programming Language :: Python :: 3.9
19
- Classifier: Programming Language :: Python :: 3.10
20
18
  Classifier: Programming Language :: Python :: 3.11
21
19
  Classifier: Programming Language :: Python :: 3.12
22
20
  Classifier: Programming Language :: Python :: 3.13
23
21
  Classifier: Topic :: Office/Business :: Financial
24
22
  Classifier: Topic :: Scientific/Engineering
25
23
  Classifier: Topic :: Utilities
26
- Requires-Python: >=3.9
24
+ Requires-Python: >=3.11
27
25
  Description-Content-Type: text/markdown
28
26
  License-File: LICENSE
29
27
  Provides-Extra: pandas
30
- Requires-Dist: pandas>=1.3; extra == "pandas"
28
+ Requires-Dist: pandas>=2; extra == "pandas"
31
29
  Provides-Extra: parquet
32
- Requires-Dist: pandas>=1.3; extra == "parquet"
30
+ Requires-Dist: pandas>=2; extra == "parquet"
33
31
  Requires-Dist: pyarrow>=10; extra == "parquet"
34
32
  Provides-Extra: mysql
35
33
  Requires-Dist: PyMySQL>=1.0; extra == "mysql"
36
34
  Provides-Extra: dev
37
35
  Requires-Dist: pytest>=7; extra == "dev"
38
36
  Requires-Dist: pytest-cov>=4; extra == "dev"
39
- Requires-Dist: pandas>=1.3; extra == "dev"
37
+ Requires-Dist: pandas>=2; extra == "dev"
40
38
  Requires-Dist: pyarrow>=10; extra == "dev"
41
39
  Requires-Dist: mypy>=1.8; extra == "dev"
42
40
  Requires-Dist: ruff>=0.5; extra == "dev"
@@ -49,12 +47,10 @@ Dynamic: license-file
49
47
 
50
48
  [![CI](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/ci.yml/badge.svg)](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/ci.yml)
51
49
  [![PyPI](https://img.shields.io/pypi/v/aemo-mdff-reader.svg)](https://pypi.org/project/aemo-mdff-reader/)
52
- [![Python versions](https://img.shields.io/pypi/pyversions/aemo-mdff-reader.svg)](https://pypi.org/project/aemo-mdff-reader/)
50
+ [![Python versions](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/project/aemo-mdff-reader/)
53
51
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
54
52
  [![CodeQL](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/codeql.yml/badge.svg)](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/codeql.yml)
55
53
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Utilified/aemo-mdff-reader/badge)](https://scorecard.dev/viewer/?uri=github.com/Utilified/aemo-mdff-reader)
56
- <!-- TODO: replace PROJECTID after registering at https://www.bestpractices.dev/ -->
57
- [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/PROJECTID/badge)](https://www.bestpractices.dev/projects/PROJECTID)
58
54
 
59
55
  Fast, zero-dependency streaming reader for AEMO **NEM12** and **NEM13**
60
56
  metering files. Implements AEMO MDFF (Meter Data File Format) v2.6.
@@ -241,7 +237,7 @@ pip install -e .[dev]
241
237
  pytest
242
238
  ```
243
239
 
244
- CI runs ruff, mypy --strict, the test matrix on Python 3.9 → 3.12 /
240
+ CI runs ruff, mypy --strict, the test matrix on Python 3.11 → 3.12 /
245
241
  Linux / macOS / Windows, `pip-audit`, `bandit`, CodeQL, OpenSSF
246
242
  Scorecard, and a wheel-install smoke test.
247
243
 
@@ -2,12 +2,10 @@
2
2
 
3
3
  [![CI](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/ci.yml/badge.svg)](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/ci.yml)
4
4
  [![PyPI](https://img.shields.io/pypi/v/aemo-mdff-reader.svg)](https://pypi.org/project/aemo-mdff-reader/)
5
- [![Python versions](https://img.shields.io/pypi/pyversions/aemo-mdff-reader.svg)](https://pypi.org/project/aemo-mdff-reader/)
5
+ [![Python versions](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/project/aemo-mdff-reader/)
6
6
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
7
  [![CodeQL](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/codeql.yml/badge.svg)](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/codeql.yml)
8
8
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Utilified/aemo-mdff-reader/badge)](https://scorecard.dev/viewer/?uri=github.com/Utilified/aemo-mdff-reader)
9
- <!-- TODO: replace PROJECTID after registering at https://www.bestpractices.dev/ -->
10
- [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/PROJECTID/badge)](https://www.bestpractices.dev/projects/PROJECTID)
11
9
 
12
10
  Fast, zero-dependency streaming reader for AEMO **NEM12** and **NEM13**
13
11
  metering files. Implements AEMO MDFF (Meter Data File Format) v2.6.
@@ -194,7 +192,7 @@ pip install -e .[dev]
194
192
  pytest
195
193
  ```
196
194
 
197
- CI runs ruff, mypy --strict, the test matrix on Python 3.9 → 3.12 /
195
+ CI runs ruff, mypy --strict, the test matrix on Python 3.11 → 3.12 /
198
196
  Linux / macOS / Windows, `pip-audit`, `bandit`, CodeQL, OpenSSF
199
197
  Scorecard, and a wheel-install smoke test.
200
198
 
@@ -48,7 +48,7 @@ from .types import (
48
48
  NMIDetails,
49
49
  )
50
50
 
51
- __version__ = "2.0.4" # x-release-please-version
51
+ __version__ = "2.2.0" # x-release-please-version
52
52
 
53
53
  __all__ = [
54
54
  "INTERVAL_DATA_OUTPUT_HEADERS",
@@ -223,6 +223,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
223
223
  try:
224
224
  sys.stdout.flush()
225
225
  except BrokenPipeError:
226
+ # Pipe is already gone; nothing to flush.
226
227
  pass
227
228
  return 0
228
229
 
@@ -7,8 +7,14 @@ The dependency is only required when you actually instantiate
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ from typing import TYPE_CHECKING
11
+
10
12
  from .query import COLUMNS, QueryBuilder
11
13
 
14
+ if TYPE_CHECKING:
15
+ # Visible to static analysers / IDEs without forcing pymysql to load.
16
+ from .store import DBCredentials, Storer
17
+
12
18
  __all__ = ["COLUMNS", "DBCredentials", "QueryBuilder", "Storer"]
13
19
 
14
20
 
@@ -4,7 +4,15 @@
4
4
 
5
5
  import pymysql.cursors
6
6
 
7
- from .query import *
7
+ from .query import (
8
+ CHANNEL_TABLE,
9
+ IMPORT_TABLE,
10
+ INTERVAL_DATA_TABLE,
11
+ INTERVAL_TABLE,
12
+ INTERVAL_TYPE_TABLE,
13
+ NMI_TABLE,
14
+ QueryBuilder,
15
+ )
8
16
 
9
17
  # Load queries once session is imported
10
18
  QUERY_FOLDER = "queries"
@@ -128,11 +136,11 @@ class Storer:
128
136
 
129
137
  data_intervals.append(data_interval)
130
138
  query = QueryBuilder.insert_query(NMI_TABLE, data_nmi)
131
- resultset = cursor.execute(query)
139
+ cursor.execute(query)
132
140
 
133
141
  for row in range(len(data_channel)):
134
142
  query = QueryBuilder.insert_query(CHANNEL_TABLE, [data_channel[row]])
135
- resultset = cursor.execute(query)
143
+ cursor.execute(query)
136
144
  query = "SELECT channelID FROM `nem`.`channel` WHERE `NMI` = %s AND `NMISuffix` = %s AND `MeterSerialNumber` = %s"
137
145
  cursor.execute(
138
146
  query, (data_channel[row][0], data_channel[row][2], data_channel[row][4])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aemo-mdff-reader
3
- Version: 2.0.4
3
+ Version: 2.2.0
4
4
  Summary: Fast streaming reader for AEMO MDFF NEM12 / NEM13 metering files. Zero required dependencies.
5
5
  Author: Utilified Holdings Pty Ltd
6
6
  License-Expression: MIT
@@ -15,28 +15,26 @@ Classifier: Intended Audience :: Information Technology
15
15
  Classifier: Operating System :: OS Independent
16
16
  Classifier: Programming Language :: Python :: 3
17
17
  Classifier: Programming Language :: Python :: 3 :: Only
18
- Classifier: Programming Language :: Python :: 3.9
19
- Classifier: Programming Language :: Python :: 3.10
20
18
  Classifier: Programming Language :: Python :: 3.11
21
19
  Classifier: Programming Language :: Python :: 3.12
22
20
  Classifier: Programming Language :: Python :: 3.13
23
21
  Classifier: Topic :: Office/Business :: Financial
24
22
  Classifier: Topic :: Scientific/Engineering
25
23
  Classifier: Topic :: Utilities
26
- Requires-Python: >=3.9
24
+ Requires-Python: >=3.11
27
25
  Description-Content-Type: text/markdown
28
26
  License-File: LICENSE
29
27
  Provides-Extra: pandas
30
- Requires-Dist: pandas>=1.3; extra == "pandas"
28
+ Requires-Dist: pandas>=2; extra == "pandas"
31
29
  Provides-Extra: parquet
32
- Requires-Dist: pandas>=1.3; extra == "parquet"
30
+ Requires-Dist: pandas>=2; extra == "parquet"
33
31
  Requires-Dist: pyarrow>=10; extra == "parquet"
34
32
  Provides-Extra: mysql
35
33
  Requires-Dist: PyMySQL>=1.0; extra == "mysql"
36
34
  Provides-Extra: dev
37
35
  Requires-Dist: pytest>=7; extra == "dev"
38
36
  Requires-Dist: pytest-cov>=4; extra == "dev"
39
- Requires-Dist: pandas>=1.3; extra == "dev"
37
+ Requires-Dist: pandas>=2; extra == "dev"
40
38
  Requires-Dist: pyarrow>=10; extra == "dev"
41
39
  Requires-Dist: mypy>=1.8; extra == "dev"
42
40
  Requires-Dist: ruff>=0.5; extra == "dev"
@@ -49,12 +47,10 @@ Dynamic: license-file
49
47
 
50
48
  [![CI](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/ci.yml/badge.svg)](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/ci.yml)
51
49
  [![PyPI](https://img.shields.io/pypi/v/aemo-mdff-reader.svg)](https://pypi.org/project/aemo-mdff-reader/)
52
- [![Python versions](https://img.shields.io/pypi/pyversions/aemo-mdff-reader.svg)](https://pypi.org/project/aemo-mdff-reader/)
50
+ [![Python versions](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/project/aemo-mdff-reader/)
53
51
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
54
52
  [![CodeQL](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/codeql.yml/badge.svg)](https://github.com/Utilified/aemo-mdff-reader/actions/workflows/codeql.yml)
55
53
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Utilified/aemo-mdff-reader/badge)](https://scorecard.dev/viewer/?uri=github.com/Utilified/aemo-mdff-reader)
56
- <!-- TODO: replace PROJECTID after registering at https://www.bestpractices.dev/ -->
57
- [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/PROJECTID/badge)](https://www.bestpractices.dev/projects/PROJECTID)
58
54
 
59
55
  Fast, zero-dependency streaming reader for AEMO **NEM12** and **NEM13**
60
56
  metering files. Implements AEMO MDFF (Meter Data File Format) v2.6.
@@ -241,7 +237,7 @@ pip install -e .[dev]
241
237
  pytest
242
238
  ```
243
239
 
244
- CI runs ruff, mypy --strict, the test matrix on Python 3.9 → 3.12 /
240
+ CI runs ruff, mypy --strict, the test matrix on Python 3.11 → 3.12 /
245
241
  Linux / macOS / Windows, `pip-audit`, `bandit`, CodeQL, OpenSSF
246
242
  Scorecard, and a wheel-install smoke test.
247
243
 
@@ -2,7 +2,7 @@
2
2
  [dev]
3
3
  pytest>=7
4
4
  pytest-cov>=4
5
- pandas>=1.3
5
+ pandas>=2
6
6
  pyarrow>=10
7
7
  mypy>=1.8
8
8
  ruff>=0.5
@@ -14,8 +14,8 @@ packaging>=24.2
14
14
  PyMySQL>=1.0
15
15
 
16
16
  [pandas]
17
- pandas>=1.3
17
+ pandas>=2
18
18
 
19
19
  [parquet]
20
- pandas>=1.3
20
+ pandas>=2
21
21
  pyarrow>=10
@@ -11,14 +11,15 @@ build-backend = "setuptools.build_meta"
11
11
  # Pillow / PIL precedent (``pip install Pillow`` → ``import PIL``)
12
12
  # applies here.
13
13
  name = "aemo-mdff-reader"
14
- version = "2.0.4"
14
+ version = "2.2.0"
15
15
  description = "Fast streaming reader for AEMO MDFF NEM12 / NEM13 metering files. Zero required dependencies."
16
16
  readme = "README.md"
17
17
  license = "MIT"
18
18
  license-files = ["LICENSE"]
19
- # Python 3.8 is EOL (Oct 2024) and setuptools 77+ — required for the
20
- # PEP 639 license metadata above — does not publish a 3.8 wheel.
21
- requires-python = ">=3.9"
19
+ # Python 3.9 reached EOL in October 2025; pandas 3.0 dropped Python
20
+ # 3.10 support. Bumping the floor to 3.11 keeps us aligned with the
21
+ # upstream pandas/numpy/pyarrow release trains used by the dev extras.
22
+ requires-python = ">=3.11"
22
23
  authors = [
23
24
  { name = "Utilified Holdings Pty Ltd" },
24
25
  ]
@@ -30,8 +31,6 @@ classifiers = [
30
31
  "Operating System :: OS Independent",
31
32
  "Programming Language :: Python :: 3",
32
33
  "Programming Language :: Python :: 3 :: Only",
33
- "Programming Language :: Python :: 3.9",
34
- "Programming Language :: Python :: 3.10",
35
34
  "Programming Language :: Python :: 3.11",
36
35
  "Programming Language :: Python :: 3.12",
37
36
  "Programming Language :: Python :: 3.13",
@@ -43,13 +42,13 @@ classifiers = [
43
42
  dependencies = []
44
43
 
45
44
  [project.optional-dependencies]
46
- pandas = ["pandas>=1.3"]
47
- parquet = ["pandas>=1.3", "pyarrow>=10"]
45
+ pandas = ["pandas>=2"]
46
+ parquet = ["pandas>=2", "pyarrow>=10"]
48
47
  mysql = ["PyMySQL>=1.0"]
49
48
  dev = [
50
49
  "pytest>=7",
51
50
  "pytest-cov>=4",
52
- "pandas>=1.3",
51
+ "pandas>=2",
53
52
  "pyarrow>=10",
54
53
  "mypy>=1.8",
55
54
  "ruff>=0.5",
@@ -95,7 +94,7 @@ exclude_lines = [
95
94
 
96
95
  [tool.ruff]
97
96
  line-length = 100
98
- target-version = "py39"
97
+ target-version = "py311"
99
98
 
100
99
  [tool.ruff.lint]
101
100
  select = [
@@ -109,10 +108,8 @@ select = [
109
108
  ]
110
109
  ignore = [
111
110
  "E501", # line too long — handled by formatter
112
- # We use ``Union[...]`` / ``Optional[...]`` / ``Dict[...]`` so the
113
- # type aliases work at runtime on 3.9 without ``from __future__
114
- # import annotations`` everywhere. Adopting PEP 604 / PEP 585 syntax
115
- # is a follow-up; left as-is to keep the public-API diff small.
111
+ # PEP 604 / PEP 585 typing migration is a follow-up; left for now
112
+ # to keep the public-API surface diff small for 2.x.
116
113
  "UP007", # Use X | Y instead of Union[X, Y]
117
114
  "UP045", # Use X | None instead of Optional[X]
118
115
  "UP006", # Use builtin generics (dict[X, Y]) instead of typing.Dict
@@ -128,11 +125,7 @@ ignore = [
128
125
  "aemo_mdff_reader/sql/*" = ["F401", "F403", "F405", "UP031"]
129
126
 
130
127
  [tool.mypy]
131
- # Lowest supported runtime is 3.9 (see ``requires-python``), but mypy
132
- # 1.16+ no longer accepts ``<3.10`` here. Type-checking against 3.10
133
- # still produces useful results — we don't use any 3.10-only typing
134
- # features (no ``match`` statements, no PEP 604 ``X | Y`` at runtime).
135
- python_version = "3.10"
128
+ python_version = "3.11"
136
129
  strict = true
137
130
  files = ["aemo_mdff_reader"]
138
131
  # Optional dependencies that don't ship type stubs.
@@ -127,7 +127,8 @@ def test_parse_to_columns_matches_to_columns():
127
127
 
128
128
 
129
129
  def test_parse_to_columns_from_iterable():
130
- rows = list(csv.reader(open(FIXTURE)))
130
+ with open(FIXTURE) as f:
131
+ rows = list(csv.reader(f))
131
132
  a = parse_to_columns(FIXTURE)
132
133
  b = parse_to_columns(rows)
133
134
  assert a == b
@@ -178,7 +179,8 @@ def test_header_repr():
178
179
 
179
180
 
180
181
  def test_nem_reader_read_from_array():
181
- rows = list(csv.reader(open(FIXTURE)))
182
+ with open(FIXTURE) as f:
183
+ rows = list(csv.reader(f))
182
184
  rdr = NEMReader()
183
185
  rdr.read_from_array(rows)
184
186
  assert len(rdr.readings) == 3 * 48
@@ -188,7 +190,8 @@ def test_nem_reader_read_from_array():
188
190
  def test_nem_reader_to_columns_without_filename():
189
191
  # NEMReader.to_columns falls through the Reading-iterable branch
190
192
  # when no source filename is buffered.
191
- rows = list(csv.reader(open(FIXTURE)))
193
+ with open(FIXTURE) as f:
194
+ rows = list(csv.reader(f))
192
195
  rdr = NEMReader()
193
196
  rdr.read_from_array(rows)
194
197
  cols = rdr.to_columns()
@@ -2,8 +2,11 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from urllib.parse import urlparse
6
+
5
7
  import aemo_mdff_reader
6
- from aemo_mdff_reader import spec
8
+
9
+ spec = aemo_mdff_reader.spec
7
10
 
8
11
 
9
12
  def test_spec_module_is_re_exported():
@@ -16,7 +19,9 @@ def test_spec_version_metadata():
16
19
  # ISO-8601 effective date keeps callers parseable.
17
20
  assert spec.SPEC_EFFECTIVE_DATE == "2024-09-29"
18
21
  assert "Meter Data File Format" in spec.SPEC_DOCUMENT_TITLE
19
- assert spec.SPEC_URL.startswith("https://www.aemo.com.au")
22
+ parsed = urlparse(spec.SPEC_URL)
23
+ assert parsed.scheme == "https"
24
+ assert parsed.hostname == "www.aemo.com.au"
20
25
 
21
26
 
22
27
  def test_allowed_interval_lengths_match_spec():
@@ -43,7 +43,7 @@ def test_storer_attribute_lazy_loads():
43
43
  # If pymysql is not installed, accessing Storer should raise ImportError
44
44
  # only at access time, not at module import time.
45
45
  pytest.importorskip("aemo_mdff_reader.sql")
46
- import aemo_mdff_reader.sql as sql_mod
46
+ from aemo_mdff_reader import sql as sql_mod
47
47
 
48
48
  if "pymysql" in sys.modules:
49
49
  # If pymysql is available in the test env, we can at least confirm