aemo-mdff-reader 2.0.4__tar.gz → 2.1.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.1.0}/CHANGELOG.md +12 -0
  2. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/CONTRIBUTING.md +26 -0
  3. {aemo_mdff_reader-2.0.4/aemo_mdff_reader.egg-info → aemo_mdff_reader-2.1.0}/PKG-INFO +4 -7
  4. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/README.md +2 -4
  5. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader/__init__.py +1 -1
  6. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader/cli.py +1 -0
  7. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader/sql/__init__.py +6 -0
  8. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader/sql/store.py +11 -3
  9. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0/aemo_mdff_reader.egg-info}/PKG-INFO +4 -7
  10. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/pyproject.toml +6 -10
  11. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/test_parser.py +6 -3
  12. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/test_spec.py +7 -2
  13. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/test_sql.py +1 -1
  14. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/LICENSE +0 -0
  15. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/MANIFEST.in +0 -0
  16. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader/aggregate.py +0 -0
  17. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader/parser.py +0 -0
  18. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader/py.typed +0 -0
  19. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader/reader.py +0 -0
  20. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader/spec.py +0 -0
  21. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader/sql/query.py +0 -0
  22. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader/types.py +0 -0
  23. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader.egg-info/SOURCES.txt +0 -0
  24. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader.egg-info/dependency_links.txt +0 -0
  25. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader.egg-info/entry_points.txt +0 -0
  26. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader.egg-info/requires.txt +0 -0
  27. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/aemo_mdff_reader.egg-info/top_level.txt +0 -0
  28. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/benchmarks/bench_parser.py +0 -0
  29. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/setup.cfg +0 -0
  30. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/setup.py +0 -0
  31. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/__init__.py +0 -0
  32. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/fixtures/generate.py +0 -0
  33. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/fixtures/sample_nem12.csv +0 -0
  34. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/fixtures/sample_nem13.csv +0 -0
  35. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/test_cli.py +0 -0
  36. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/test_datetime_formats.py +0 -0
  37. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/test_edge_cases.py +0 -0
  38. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/test_ergonomics.py +0 -0
  39. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/test_events_b2b.py +0 -0
  40. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/test_large_files.py +0 -0
  41. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/test_nem13.py +0 -0
  42. {aemo_mdff_reader-2.0.4 → aemo_mdff_reader-2.1.0}/tests/test_validation.py +0 -0
@@ -4,6 +4,18 @@ 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.1.0](https://github.com/Utilified/aemo-mdff-reader/compare/v2.0.4...v2.1.0) (2026-05-10)
8
+
9
+
10
+ ### Features
11
+
12
+ * drop Python 3.9, hash-pin CI installs, scope release-please permissions ([447ec98](https://github.com/Utilified/aemo-mdff-reader/commit/447ec98408edabfab45d28841a2e714d54f8dd11))
13
+
14
+
15
+ ### Documentation
16
+
17
+ * 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))
18
+
7
19
  ## [2.0.4](https://github.com/Utilified/aemo-mdff-reader/compare/v2.0.3...v2.0.4) (2026-05-10)
8
20
 
9
21
  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.10 --output-file requirements/dev.txt requirements/dev.in
40
+ uv pip compile --generate-hashes --strip-extras --python-version 3.10 --output-file requirements/lint.txt requirements/lint.in
41
+ uv pip compile --generate-hashes --strip-extras --python-version 3.10 --output-file requirements/build.txt requirements/build.in
42
+ uv pip compile --generate-hashes --strip-extras --python-version 3.10 --output-file requirements/audit.txt requirements/audit.in
43
+ ```
44
+
45
+ `uv` is used over `pip-tools` because its resolver handles the
46
+ 3.10–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.10` flag pins
49
+ resolution to the lowest supported interpreter so packages whose
50
+ latest releases dropped 3.10 are correctly stepped back to a
51
+ 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.1.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,7 +15,6 @@ 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
18
  Classifier: Programming Language :: Python :: 3.10
20
19
  Classifier: Programming Language :: Python :: 3.11
21
20
  Classifier: Programming Language :: Python :: 3.12
@@ -23,7 +22,7 @@ Classifier: Programming Language :: Python :: 3.13
23
22
  Classifier: Topic :: Office/Business :: Financial
24
23
  Classifier: Topic :: Scientific/Engineering
25
24
  Classifier: Topic :: Utilities
26
- Requires-Python: >=3.9
25
+ Requires-Python: >=3.10
27
26
  Description-Content-Type: text/markdown
28
27
  License-File: LICENSE
29
28
  Provides-Extra: pandas
@@ -49,12 +48,10 @@ Dynamic: license-file
49
48
 
50
49
  [![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
50
  [![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/)
51
+ [![Python versions](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/project/aemo-mdff-reader/)
53
52
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
54
53
  [![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
54
  [![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
55
 
59
56
  Fast, zero-dependency streaming reader for AEMO **NEM12** and **NEM13**
60
57
  metering files. Implements AEMO MDFF (Meter Data File Format) v2.6.
@@ -241,7 +238,7 @@ pip install -e .[dev]
241
238
  pytest
242
239
  ```
243
240
 
244
- CI runs ruff, mypy --strict, the test matrix on Python 3.9 → 3.12 /
241
+ CI runs ruff, mypy --strict, the test matrix on Python 3.10 → 3.12 /
245
242
  Linux / macOS / Windows, `pip-audit`, `bandit`, CodeQL, OpenSSF
246
243
  Scorecard, and a wheel-install smoke test.
247
244
 
@@ -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.10%20%7C%203.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.10 → 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.1.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.1.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,7 +15,6 @@ 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
18
  Classifier: Programming Language :: Python :: 3.10
20
19
  Classifier: Programming Language :: Python :: 3.11
21
20
  Classifier: Programming Language :: Python :: 3.12
@@ -23,7 +22,7 @@ Classifier: Programming Language :: Python :: 3.13
23
22
  Classifier: Topic :: Office/Business :: Financial
24
23
  Classifier: Topic :: Scientific/Engineering
25
24
  Classifier: Topic :: Utilities
26
- Requires-Python: >=3.9
25
+ Requires-Python: >=3.10
27
26
  Description-Content-Type: text/markdown
28
27
  License-File: LICENSE
29
28
  Provides-Extra: pandas
@@ -49,12 +48,10 @@ Dynamic: license-file
49
48
 
50
49
  [![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
50
  [![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/)
51
+ [![Python versions](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/project/aemo-mdff-reader/)
53
52
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
54
53
  [![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
54
  [![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
55
 
59
56
  Fast, zero-dependency streaming reader for AEMO **NEM12** and **NEM13**
60
57
  metering files. Implements AEMO MDFF (Meter Data File Format) v2.6.
@@ -241,7 +238,7 @@ pip install -e .[dev]
241
238
  pytest
242
239
  ```
243
240
 
244
- CI runs ruff, mypy --strict, the test matrix on Python 3.9 → 3.12 /
241
+ CI runs ruff, mypy --strict, the test matrix on Python 3.10 → 3.12 /
245
242
  Linux / macOS / Windows, `pip-audit`, `bandit`, CodeQL, OpenSSF
246
243
  Scorecard, and a wheel-install smoke test.
247
244
 
@@ -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.1.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 end-of-life in October 2025; CI hash-pinning of
20
+ # pandas/pyarrow/numpy no longer resolves cleanly against 3.9 wheels.
21
+ # 3.10 is the lowest supported runtime as of v3.0.
22
+ requires-python = ">=3.10"
22
23
  authors = [
23
24
  { name = "Utilified Holdings Pty Ltd" },
24
25
  ]
@@ -30,7 +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
34
  "Programming Language :: Python :: 3.10",
35
35
  "Programming Language :: Python :: 3.11",
36
36
  "Programming Language :: Python :: 3.12",
@@ -95,7 +95,7 @@ exclude_lines = [
95
95
 
96
96
  [tool.ruff]
97
97
  line-length = 100
98
- target-version = "py39"
98
+ target-version = "py310"
99
99
 
100
100
  [tool.ruff.lint]
101
101
  select = [
@@ -128,10 +128,6 @@ ignore = [
128
128
  "aemo_mdff_reader/sql/*" = ["F401", "F403", "F405", "UP031"]
129
129
 
130
130
  [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
131
  python_version = "3.10"
136
132
  strict = true
137
133
  files = ["aemo_mdff_reader"]
@@ -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