xarray-ms 0.4.0a5__tar.gz → 0.4.0a6__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 (71) hide show
  1. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/.github/workflows/ci.yml +1 -1
  2. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/.readthedocs.yaml +4 -4
  3. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/PKG-INFO +3 -20
  4. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/source/changelog.rst +18 -1
  5. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/source/conf.py +1 -1
  6. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/source/install.rst +7 -11
  7. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/source/links.rst +1 -0
  8. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/source/partitioning.rst +2 -2
  9. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/source/roadmap.rst +1 -1
  10. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/source/tutorial.rst +1 -1
  11. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/pyproject.toml +7 -7
  12. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/conftest.py +2 -5
  13. xarray_ms-0.4.0a6/tests/test_accessors.py +24 -0
  14. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_structure.py +27 -19
  15. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/array.py +7 -4
  16. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/entrypoint.py +14 -13
  17. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/entrypoint_utils.py +10 -6
  18. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/factories/core.py +7 -4
  19. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/factories/correlated.py +13 -9
  20. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/structure.py +20 -92
  21. xarray_ms-0.4.0a5/tests/test_multiton.py +0 -70
  22. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/.github/ISSUE_TEMPLATE.md +0 -0
  23. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  24. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/.github/dependabot.yml +0 -0
  25. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/.github/workflows/pre-commit.yml +0 -0
  26. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/.github/workflows/readthedocs.yml +0 -0
  27. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/.gitignore +0 -0
  28. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/.pre-commit-config.yaml +0 -0
  29. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/LICENSE +0 -0
  30. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/README.rst +0 -0
  31. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/Makefile +0 -0
  32. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/make.bat +0 -0
  33. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/source/api.rst +0 -0
  34. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/source/index.rst +0 -0
  35. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/doc/source/introduction.rst +0 -0
  36. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/hello.txt +0 -0
  37. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/__init__.py +0 -0
  38. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/msv4_test_corpus/__init__.py +0 -0
  39. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/msv4_test_corpus/conftest.py +0 -0
  40. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/msv4_test_corpus/test_msv_corpus.py +0 -0
  41. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_antenna.py +0 -0
  42. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_backend.py +0 -0
  43. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_basic.py +0 -0
  44. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_field_and_source.py +0 -0
  45. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_github.py +0 -0
  46. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_imputation.py +0 -0
  47. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_measures.py +0 -0
  48. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_read.py +0 -0
  49. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_table_utils.py +0 -0
  50. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_utils.py +0 -0
  51. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_write.py +0 -0
  52. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/tests/test_zarr_roundtrip.py +0 -0
  53. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/__init__.py +0 -0
  54. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/factories/__init__.py +0 -0
  55. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/factories/antenna.py +0 -0
  56. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/factories/field_and_source.py +0 -0
  57. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/imputation.py +0 -0
  58. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/measures_adapters.py +0 -0
  59. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/measures_encoders.py +0 -0
  60. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/partition.py +0 -0
  61. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/table_utils.py +0 -0
  62. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/backend/msv2/writes.py +0 -0
  63. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/casa_types.py +0 -0
  64. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/errors.py +0 -0
  65. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/msv4_types.py +0 -0
  66. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/multiton.py +0 -0
  67. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/query.py +0 -0
  68. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/testing/__init__.py +0 -0
  69. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/testing/simulator.py +0 -0
  70. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/testing/utils.py +0 -0
  71. {xarray_ms-0.4.0a5 → xarray_ms-0.4.0a6}/xarray_ms/utils.py +0 -0
@@ -62,7 +62,7 @@ jobs:
62
62
  - name: Install xarray-ms
63
63
  run: |
64
64
  source .venv/bin/activate
65
- pip install .[testing]
65
+ pip install --group test .
66
66
 
67
67
  - name: Test xarray-ms
68
68
  run: |
@@ -12,11 +12,11 @@ build:
12
12
 
13
13
  python:
14
14
  install:
15
- - method: pip
16
- path: .
17
- extra_requirements:
15
+ - method: uv
16
+ command: sync
17
+ groups:
18
18
  - doc
19
- - testing
19
+ - test
20
20
 
21
21
  # Build documentation in the "docs/" directory with Sphinx
22
22
  sphinx:
@@ -1,31 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xarray-ms
3
- Version: 0.4.0a5
3
+ Version: 0.4.0a6
4
4
  Summary: xarray MSv4 views over MSv2 Measurement Sets
5
5
  Author-email: Simon Perkins <simon.perkins@gmail.com>
6
6
  License-File: LICENSE
7
7
  Requires-Python: >=3.11
8
- Requires-Dist: arcae<0.5.0,>=0.4.0a4
9
- Requires-Dist: cacheout>=0.16.0
8
+ Requires-Dist: arcae<0.5.0,>=0.4.0a5
9
+ Requires-Dist: rarg-python-patterns>=0.0.3
10
10
  Requires-Dist: typing-extensions>=4.12.2
11
11
  Requires-Dist: xarray>=2025.0
12
- Provides-Extra: dev
13
- Requires-Dist: pre-commit>=3.8.0; extra == 'dev'
14
- Requires-Dist: tbump>=6.11.0; extra == 'dev'
15
- Provides-Extra: doc
16
- Requires-Dist: ipython>=8.27.0; extra == 'doc'
17
- Requires-Dist: pydata-sphinx-theme>=0.15.4; extra == 'doc'
18
- Requires-Dist: pygments>=2.18.0; extra == 'doc'
19
- Requires-Dist: sphinx-copybutton>=0.5.2; extra == 'doc'
20
- Requires-Dist: sphinx>=8.0.2; extra == 'doc'
21
- Requires-Dist: sphinxcontrib-spelling; extra == 'doc'
22
- Provides-Extra: testing
23
- Requires-Dist: dask>=2024.5.0; extra == 'testing'
24
- Requires-Dist: distributed>=2024.5.0; extra == 'testing'
25
- Requires-Dist: platformdirs>=4.3.8; extra == 'testing'
26
- Requires-Dist: pytest>=8.0.0; extra == 'testing'
27
- Requires-Dist: xradio>=1.1.2; (python_version >= '3.11' and python_version < '3.14') and extra == 'testing'
28
- Requires-Dist: zarr<3.0.0,>=2.18.3; extra == 'testing'
29
12
  Description-Content-Type: text/x-rst
30
13
 
31
14
  xarray-ms
@@ -3,6 +3,23 @@
3
3
  Changelog
4
4
  =========
5
5
 
6
+ X.Y.Z (DD-MM-YYYY)
7
+ ------------------
8
+ * Introduce ``pytest != 9.1.0`` version restriction (:pr:`162`)
9
+
10
+ 0.5.5 (12-06-2026)
11
+ ------------------
12
+ * Upgrade to arcae 0.5.2 (:pr:`161`)
13
+ * Documentation fixes (:pr:`160`)
14
+ * Replace ``xarray_ms.multiton.Multiton`` with ``rarg-python-patterns.multiton.Multiton`` (:pr:`159`)
15
+ * Move development dependencies in dependency groups (:pr:`158`)
16
+ * Remove ``*kwargs`` from ``MSv2EntryPoint`` methods (:pr:`156`)
17
+ * Revert: Allow unsupported arguments in entrypoint methods (#154) (:pr:`155`)
18
+
19
+ 0.5.4 (20-05-2026)
20
+ ------------------
21
+ * Allow unsupported arguments in entrypoint methods (:pr:`154`)
22
+
6
23
  0.5.3 (11-05-2026)
7
24
  ------------------
8
25
  * Expand standard msv2 columns supported during reads (:pr:`153`)
@@ -37,7 +54,7 @@ Changelog
37
54
  * Synchronise with v4.0.0 schema version (:pr:`139`)
38
55
  * Remove ``correlated_xds.scan_number`` coordinate (:pr:`139`)
39
56
  * Remove ``correlated_xds.sub_scan_number`` coordinate (:pr:`139`)
40
- * Add ``corelated_xds.scan_name`` coordinate (:pr:`139`)
57
+ * Add ``correlated_xds.scan_name`` coordinate (:pr:`139`)
41
58
  * Add ``correlated_xds.frequency.spectral_window_intents`` coordinate attribute (:pr:`139`)
42
59
  * Remove ``correlated_xds.observation_info["intents"]`` (:pr:`139`)
43
60
  * Rename ``correlated_xds.observation_info["project"]`` to ``"project_UID"`` (:pr:`139`)
@@ -11,7 +11,7 @@
11
11
  project = "xarray-ms"
12
12
  copyright = "2024 - 2025 NRF (SARAO) and Rhodes University (RATT) Centre"
13
13
  author = "Simon Perkins"
14
- release = "0.4.0-alpha.5"
14
+ release = "0.4.0-alpha.6"
15
15
 
16
16
  # -- General configuration ---------------------------------------------------
17
17
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -16,31 +16,27 @@ they must be installed separately.
16
16
  Development
17
17
  ===========
18
18
 
19
- Create a virtual environment and install with the dev, doc and testing
20
- dependencies:
19
+ Install with the dev, doc and testing dependencies using uv_:
21
20
 
22
21
  .. code-block:: bash
23
22
 
24
-
25
- $ virtualenv -p python3.12 /tmp/xms
26
- $ source xms/bin/activate
27
- (xms) $ pip install -e .[dev,doc,testing]
23
+ $ uv sync --group dev --group test --group doc
28
24
 
29
25
  The pre-commit hooks can be manually executed as follows:
30
26
 
31
27
  .. code-block:: bash
32
28
 
33
- (xms) $ pre-commit run -a
29
+ $ uv run --dev pre-commit run -a
34
30
 
35
31
  Test Suite
36
32
  ----------
37
33
 
38
- After creating the virtual environment above, run the following command
34
+ After installing the dependencies above, run the following command
39
35
  within the xarray-ms source code directory to execute the test suite:
40
36
 
41
37
  .. code-block:: bash
42
38
 
43
- (xms) $ py.test -s -vvv tests/
39
+ $ uv run --group test py.test tests/
44
40
 
45
41
 
46
42
  Documentation
@@ -51,8 +47,8 @@ build the Sphinx documentation
51
47
 
52
48
  .. code-block:: bash
53
49
 
54
- (xms) $ cd doc
55
- (xms) $ make html
50
+ $ cd doc
51
+ $ make html
56
52
 
57
53
  Release Process
58
54
  ---------------
@@ -17,3 +17,4 @@
17
17
  .. _xarray_indexing_and_selecting: https://docs.xarray.dev/en/latest/user-guide/indexing.html
18
18
  .. _xarray_chunked_arrays: https://docs.xarray.dev/en/latest/internals/chunked-arrays.html
19
19
  .. _zarr: https://zarr.dev/
20
+ .. _uv: https://docs.astral.sh/uv/
@@ -43,7 +43,7 @@ is that it can represent overlapped or disjoint measurements in time and frequen
43
43
  for one or more baselines.
44
44
  However, most observational data is well-behaved:
45
45
  Measurements are commonly ordered by ``TIME, ANTENNA1, ANTENNA2``
46
- and ``CHAN_FREQ`` commonly increases monotically with
46
+ and ``CHAN_FREQ`` commonly increases monotonically with
47
47
  equidistant values (i.e. ``CHAN_WIDTH`` values are uniform) but this cannot
48
48
  always be assumed.
49
49
  Any regularity in an MSv2 MS is achieved through convention rather
@@ -86,7 +86,7 @@ Partitioning in time
86
86
  Compared to frequency, achieving regularity in time requires more thought
87
87
  as it depends on identifying partitions of MSv2 where data:
88
88
 
89
- 1. contains monotically increasing ``TIME`` (after ordering).
89
+ 1. contains monotonically increasing ``TIME`` (after ordering).
90
90
  2. is dumped with a uniform ``INTERVAL``.
91
91
  3. ideally contains no gaps: i.e. ``(TIME - INTERVAL)[1:] == (TIME + INTERVAL)[:-1]``.
92
92
 
@@ -18,7 +18,7 @@ In particular, it loads the MSv2 dataset present in the
18
18
  `Measurement Set v4 test suite <msv4-test-suite_>`_ except for:
19
19
 
20
20
  - ALMA Measurement Sets which sometimes do not correctly link
21
- the ANTENNNA and MAIN table via the FEED table.
21
+ the ANTENNA and MAIN table via the FEED table.
22
22
  This will need to be addressed heuristically.
23
23
  - Single-dish Measurement Sets.
24
24
  This is not difficult as it involves loading in
@@ -55,7 +55,7 @@ with a lazy view over the data.
55
55
  xarray has extensive functionality for
56
56
  `indexing and selecting data <xarray_indexing_and_selecting_>`_.
57
57
 
58
- For example, one could select select some specific dimensions out:
58
+ For example, one could select some specific dimensions out:
59
59
 
60
60
  .. ipython:: python
61
61
 
@@ -1,20 +1,20 @@
1
1
  [project]
2
2
  name = "xarray-ms"
3
- version = "0.4.0-alpha.5"
3
+ version = "0.4.0-alpha.6"
4
4
  description = "xarray MSv4 views over MSv2 Measurement Sets"
5
5
  authors = [{name = "Simon Perkins", email = "simon.perkins@gmail.com"}]
6
6
  readme = "README.rst"
7
7
  requires-python = ">=3.11"
8
8
  dependencies = [
9
9
  "xarray>=2025.0",
10
- "cacheout>=0.16.0",
11
- "arcae>=0.4.0a4, < 0.5.0",
10
+ "arcae>=0.4.0a5, < 0.5.0",
12
11
  "typing-extensions>=4.12.2",
12
+ "rarg-python-patterns>=0.0.3",
13
13
  ]
14
14
 
15
- [project.optional-dependencies]
16
- testing = [
17
- "pytest>=8.0.0",
15
+ [dependency-groups]
16
+ test = [
17
+ "pytest>=8.0.0,!=9.1.0",
18
18
  "dask>=2024.5.0",
19
19
  "distributed>=2024.5.0",
20
20
  "zarr>=2.18.3, <3.0.0",
@@ -55,7 +55,7 @@ extend-select = ["I"]
55
55
  # github_url = "https://github.com/<user or organization>/<project>/"
56
56
 
57
57
  [tool.tbump.version]
58
- current = "0.4.0-alpha.5"
58
+ current = "0.4.0-alpha.6"
59
59
 
60
60
  # https://semver.org/#spec-item-9
61
61
  regex = '''
@@ -3,9 +3,8 @@ import gc
3
3
  import numpy as np
4
4
  import pytest
5
5
  from arcae.lib.arrow_tables import Table, ms_descriptor
6
+ from rarg_python_patterns.multiton import Multiton
6
7
 
7
- from xarray_ms.backend.msv2.structure import MSv2StructureFactory
8
- from xarray_ms.multiton import Multiton
9
8
  from xarray_ms.testing.simulator import DEFAULT_SIM_PARAMS, MSStructureSimulator
10
9
 
11
10
  MSV4_TEST_CORPUS = "msv4_test_corpus"
@@ -40,10 +39,8 @@ def pytest_collection_modifyitems(config, items):
40
39
  @pytest.fixture(autouse=True)
41
40
  def clear_caches():
42
41
  yield
43
-
44
- # Structure Factories have references to Multitons
45
- MSv2StructureFactory._STRUCTURE_CACHE.clear()
46
42
  Multiton._INSTANCE_CACHE.clear()
43
+ Multiton._EXPIRY_HEAP.clear()
47
44
  gc.collect()
48
45
 
49
46
 
@@ -0,0 +1,24 @@
1
+ import pytest
2
+ import xarray
3
+
4
+
5
+ @pytest.mark.skip
6
+ @pytest.mark.filterwarnings("ignore:.*?matched multiple partitions")
7
+ @pytest.mark.parametrize(
8
+ "simmed_ms",
9
+ [
10
+ {
11
+ "name": "backend.ms",
12
+ "data_description": [(8, ["XX", "XY", "YX", "YY"]), (4, ["RR", "LL"])],
13
+ }
14
+ ],
15
+ indirect=True,
16
+ )
17
+ def test_accessors(simmed_ms):
18
+ dt = xarray.open_datatree(simmed_ms)
19
+ dt["/backend/partition_000"].attrs["links"] = {
20
+ "antenna": "/backend/partition_000/ANTENNA",
21
+ "weather": "/backend/partition_000/weather_xds",
22
+ }
23
+ # print(dt)
24
+ print(dt["/backend/partition_000"].links.antenna)
@@ -7,13 +7,15 @@ import pytest
7
7
  import xarray
8
8
  from arcae.lib.arrow_tables import Table
9
9
  from numpy.testing import assert_array_equal, assert_equal
10
+ from rarg_python_patterns.multiton import Multiton
10
11
 
11
12
  from xarray_ms.backend.msv2.structure import (
12
- MSv2StructureFactory,
13
+ MainTableFactory,
14
+ MSv2Structure,
15
+ SubtableFactory,
13
16
  TablePartitioner,
14
17
  baseline_id,
15
18
  )
16
- from xarray_ms.multiton import Multiton
17
19
 
18
20
 
19
21
  @pytest.mark.parametrize("na", [1, 2, 3, 4, 7])
@@ -34,20 +36,20 @@ def test_structure_factory(simmed_ms, epoch):
34
36
  "OBSERVATION_ID",
35
37
  "OBS_MODE",
36
38
  ]
37
- table_factory = Multiton(Table.from_filename, simmed_ms)
39
+ table_factory: MainTableFactory = Multiton(Table.from_filename, simmed_ms)
38
40
  from xarray_ms.backend.msv2.entrypoint_utils import subtable_factory
39
41
 
40
- subtables = {
42
+ subtables: dict[str, SubtableFactory] = {
41
43
  st: Multiton(subtable_factory, f"{simmed_ms}::{st}")
42
44
  for st in ("DATA_DESCRIPTION", "FEED", "FIELD", "STATE")
43
45
  }
44
- structure_factory = MSv2StructureFactory(
45
- table_factory, subtables, partition_schema, epoch, True
46
+ structure_factory = Multiton(
47
+ MSv2Structure, table_factory, subtables, partition_schema, epoch, True
46
48
  )
47
49
  assert pickle.loads(pickle.dumps(structure_factory)) == structure_factory
48
50
 
49
- structure_factory2 = MSv2StructureFactory(
50
- table_factory, subtables, partition_schema, epoch, True
51
+ structure_factory2 = Multiton(
52
+ MSv2Structure, table_factory, subtables, partition_schema, epoch, True
51
53
  )
52
54
  assert structure_factory.instance is structure_factory2.instance
53
55
 
@@ -114,34 +116,40 @@ def test_table_partitioner():
114
116
 
115
117
  def test_epoch(simmed_ms):
116
118
  partition_schema = ["FIELD_ID", "DATA_DESC_ID", "OBSERVATION_ID"]
117
- table_factory = Multiton(Table.from_filename, simmed_ms)
119
+ table_factory: MainTableFactory = Multiton(Table.from_filename, simmed_ms)
118
120
  from xarray_ms.backend.msv2.entrypoint_utils import subtable_factory
119
121
 
120
- subtables = {
122
+ subtables: dict[str, SubtableFactory] = {
121
123
  st: Multiton(subtable_factory, f"{simmed_ms}::{st}")
122
124
  for st in ("DATA_DESCRIPTION", "FEED", "FIELD", "STATE")
123
125
  }
124
126
 
125
- structure_factory = MSv2StructureFactory(
126
- table_factory, subtables, partition_schema, "abc", True
127
+ structure_factory = Multiton(
128
+ MSv2Structure, table_factory, subtables, partition_schema, "abc", True
127
129
  )
128
- structure_factory2 = MSv2StructureFactory(
129
- table_factory, subtables, partition_schema, "abc", True
130
+ structure_factory2 = Multiton(
131
+ MSv2Structure, table_factory, subtables, partition_schema, "abc", True
130
132
  )
131
133
 
132
134
  assert structure_factory.instance is structure_factory2.instance
133
135
 
134
- structure_factory3 = MSv2StructureFactory(
135
- table_factory, subtables, partition_schema, "def", True
136
+ structure_factory3 = Multiton(
137
+ MSv2Structure, table_factory, subtables, partition_schema, "def", True
136
138
  )
137
139
 
138
140
  assert structure_factory.instance is not structure_factory3.instance
139
141
 
140
142
 
141
143
  def test_msv2_structure_release(simmed_ms):
142
- assert len(MSv2StructureFactory._STRUCTURE_CACHE) == 0
144
+ def ncached_structures():
145
+ return sum(
146
+ isinstance(obj, MSv2Structure)
147
+ for obj, _, _, _ in Multiton._INSTANCE_CACHE.values()
148
+ )
149
+
150
+ assert ncached_structures() == 0
143
151
 
144
152
  with xarray.open_datatree(simmed_ms):
145
- assert len(MSv2StructureFactory._STRUCTURE_CACHE) > 0
153
+ assert ncached_structures() > 0
146
154
 
147
- assert len(MSv2StructureFactory._STRUCTURE_CACHE) == 0
155
+ assert ncached_structures() == 0
@@ -21,8 +21,11 @@ from xarray_ms.msv4_types import MAIN_PREFIX_DIMS
21
21
  if TYPE_CHECKING:
22
22
  import numpy.typing as npt
23
23
 
24
- from xarray_ms.backend.msv2.structure import MSv2StructureFactory, PartitionKeyT
25
- from xarray_ms.multiton import Multiton
24
+ from xarray_ms.backend.msv2.structure import (
25
+ MainTableFactory,
26
+ MSv2StructureFactory,
27
+ PartitionKeyT,
28
+ )
26
29
 
27
30
  TransformerT = Callable[[npt.NDArray], npt.NDArray]
28
31
 
@@ -113,7 +116,7 @@ class MainMSv2Array(MSv2Array):
113
116
  "_transform",
114
117
  )
115
118
 
116
- _table_factory: Multiton
119
+ _table_factory: MainTableFactory
117
120
  _structure_factory: MSv2StructureFactory
118
121
  _partition: PartitionKeyT
119
122
  _column: str
@@ -122,7 +125,7 @@ class MainMSv2Array(MSv2Array):
122
125
 
123
126
  def __init__(
124
127
  self,
125
- table_factory: Multiton,
128
+ table_factory: MainTableFactory,
126
129
  structure_factory: MSv2StructureFactory,
127
130
  partition: PartitionKeyT,
128
131
  column: str,
@@ -28,15 +28,19 @@ from xarray_ms.backend.msv2.structure import (
28
28
  )
29
29
  from xarray_ms.errors import FrameConversionWarning, InvalidPartitionKey
30
30
  from xarray_ms.msv4_types import CORRELATED_DATASET_TYPES
31
- from xarray_ms.multiton import Multiton
32
- from xarray_ms.utils import format_docstring
31
+ from xarray_ms.utils import format_docstring, function_arguments
33
32
 
34
33
  if TYPE_CHECKING:
35
34
  from io import BufferedIOBase
36
35
 
37
36
  from xarray.backends.common import AbstractDataStore
38
37
 
39
- from xarray_ms.backend.msv2.structure import DEFAULT_PARTITION_COLUMNS, PartitionKeyT
38
+ from xarray_ms.backend.msv2.structure import (
39
+ DEFAULT_PARTITION_COLUMNS,
40
+ MainTableFactory,
41
+ PartitionKeyT,
42
+ SubtableFactory,
43
+ )
40
44
 
41
45
 
42
46
  WriteRegionT = Mapping[str, slice | Literal["auto"]] | Literal["auto"]
@@ -91,8 +95,8 @@ class MSv2Store(AbstractWritableDataStore):
91
95
  "_write_region",
92
96
  )
93
97
 
94
- _table_factory: Multiton
95
- _subtable_factories: Dict[str, Multiton]
98
+ _table_factory: MainTableFactory
99
+ _subtable_factories: Dict[str, SubtableFactory]
96
100
  _structure_factory: MSv2StructureFactory
97
101
  _partition_schema: List[str]
98
102
  _partition_key: PartitionKeyT
@@ -104,8 +108,8 @@ class MSv2Store(AbstractWritableDataStore):
104
108
 
105
109
  def __init__(
106
110
  self,
107
- table_factory: Multiton,
108
- subtable_factories: Dict[str, Multiton],
111
+ table_factory: MainTableFactory,
112
+ subtable_factories: Dict[str, SubtableFactory],
109
113
  structure_factory: MSv2StructureFactory,
110
114
  partition_schema: List[str],
111
115
  partition_key: PartitionKeyT,
@@ -232,7 +236,8 @@ class MSv2Store(AbstractWritableDataStore):
232
236
  of the Measurement Set.
233
237
 
234
238
  Overrides AbstractDataStore.get_encoding"""
235
- table_args = self._table_factory.arguments()
239
+ multiton = self._table_factory
240
+ table_args = function_arguments(multiton._factory, multiton._args, multiton._kw)
236
241
 
237
242
  return {
238
243
  "common_store_args": {
@@ -315,7 +320,7 @@ class MSv2Store(AbstractWritableDataStore):
315
320
  self._write_region = expanded_region
316
321
 
317
322
  @property
318
- def table_factory(self) -> Multiton:
323
+ def table_factory(self) -> MainTableFactory:
319
324
  """Return the table factory associated with this store"""
320
325
  return self._table_factory
321
326
 
@@ -418,7 +423,6 @@ class MSv2EntryPoint(BackendEntrypoint):
418
423
  auto_corrs: bool = False,
419
424
  ninstances: int = 8,
420
425
  epoch: str | None = None,
421
- **kwargs,
422
426
  ) -> DataTree:
423
427
  """Create a :class:`~xarray.core.datatree.DataTree` presenting an MSv4 view
424
428
  over multiple partitions of a MSv2 CASA Measurement Set.
@@ -474,7 +478,6 @@ class MSv2EntryPoint(BackendEntrypoint):
474
478
  auto_corrs=auto_corrs,
475
479
  ninstances=ninstances,
476
480
  epoch=epoch,
477
- **kwargs,
478
481
  )
479
482
 
480
483
  dt = DataTree.from_dict(groups_dict)
@@ -555,7 +558,6 @@ class MSv2EntryPoint(BackendEntrypoint):
555
558
  ninstances: int = 8,
556
559
  epoch: str | None = None,
557
560
  structure_factory: MSv2StructureFactory | None = None,
558
- **kwargs,
559
561
  ) -> Dict[str, Dataset]:
560
562
  """Create a dictionary of :class:`~xarray.Dataset` presenting an MSv4 view
561
563
  over a partition of a MSv2 CASA Measurement Set"""
@@ -600,7 +602,6 @@ class MSv2EntryPoint(BackendEntrypoint):
600
602
  ninstances=store_args.ninstances,
601
603
  epoch=store_args.epoch,
602
604
  structure_factory=store_args.structure_factory,
603
- **kwargs,
604
605
  )
605
606
 
606
607
  antenna_factory = AntennaFactory(
@@ -4,12 +4,15 @@ from uuid import uuid4
4
4
 
5
5
  import pyarrow as pa
6
6
  from arcae.lib.arrow_tables import Table
7
+ from rarg_python_patterns.multiton import Multiton
7
8
 
8
9
  from xarray_ms.backend.msv2.structure import (
9
10
  DEFAULT_PARTITION_COLUMNS,
11
+ MainTableFactory,
12
+ MSv2Structure,
10
13
  MSv2StructureFactory,
14
+ SubtableFactory,
11
15
  )
12
- from xarray_ms.multiton import Multiton
13
16
 
14
17
  # These tables should always be present on an MS
15
18
  DEFAULT_SUBTABLES = [
@@ -57,8 +60,8 @@ class CommonStoreArgs:
57
60
  epoch: str
58
61
  partition_schema: List[str]
59
62
  preferred_chunks: Dict[str, int]
60
- ms_factory: Multiton
61
- subtable_factories: Dict[str, Multiton]
63
+ ms_factory: MainTableFactory
64
+ subtable_factories: Dict[str, SubtableFactory]
62
65
  structure_factory: MSv2StructureFactory
63
66
 
64
67
  __slots__ = (
@@ -81,8 +84,8 @@ class CommonStoreArgs:
81
84
  epoch: str | None = None,
82
85
  partition_schema: List[str] | None = None,
83
86
  preferred_chunks: Dict[str, int] | None = None,
84
- ms_factory: Multiton | None = None,
85
- subtable_factories: Dict[str, Multiton] | None = None,
87
+ ms_factory: MainTableFactory | None = None,
88
+ subtable_factories: Dict[str, SubtableFactory] | None = None,
86
89
  structure_factory: MSv2StructureFactory | None = None,
87
90
  ):
88
91
  if not os.path.exists(ms):
@@ -101,7 +104,8 @@ class CommonStoreArgs:
101
104
  subtable: Multiton(subtable_factory, f"{ms}::{subtable}")
102
105
  for subtable in (DEFAULT_SUBTABLES + EXTRA_SUBTABLES)
103
106
  }
104
- self.structure_factory = structure_factory or MSv2StructureFactory(
107
+ self.structure_factory = structure_factory or Multiton(
108
+ MSv2Structure,
105
109
  self.ms_factory,
106
110
  self.subtable_factories,
107
111
  self.partition_schema,
@@ -1,7 +1,10 @@
1
1
  from typing import Dict
2
2
 
3
- from xarray_ms.backend.msv2.structure import MSv2StructureFactory, PartitionKeyT
4
- from xarray_ms.multiton import Multiton
3
+ from xarray_ms.backend.msv2.structure import (
4
+ MSv2StructureFactory,
5
+ PartitionKeyT,
6
+ SubtableFactory,
7
+ )
5
8
 
6
9
 
7
10
  class DatasetFactory:
@@ -10,13 +13,13 @@ class DatasetFactory:
10
13
 
11
14
  _partition_key: PartitionKeyT
12
15
  _structure_factory: MSv2StructureFactory
13
- _subtable_factories: Dict[str, Multiton]
16
+ _subtable_factories: Dict[str, SubtableFactory]
14
17
 
15
18
  def __init__(
16
19
  self,
17
20
  partition_key: PartitionKeyT,
18
21
  structure_factory: MSv2StructureFactory,
19
- subtable_factories: Dict[str, Multiton],
22
+ subtable_factories: Dict[str, SubtableFactory],
20
23
  ):
21
24
  self._partition_key = partition_key
22
25
  self._structure_factory = structure_factory
@@ -25,7 +25,12 @@ from xarray_ms.backend.msv2.measures_encoders import (
25
25
  MSv2CoderFactory,
26
26
  VisibilityCoder,
27
27
  )
28
- from xarray_ms.backend.msv2.structure import MSv2StructureFactory, PartitionKeyT
28
+ from xarray_ms.backend.msv2.structure import (
29
+ MainTableFactory,
30
+ MSv2StructureFactory,
31
+ PartitionKeyT,
32
+ SubtableFactory,
33
+ )
29
34
  from xarray_ms.backend.msv2.table_utils import unique_antenna_names
30
35
  from xarray_ms.casa_types import ColumnDesc, Polarisations
31
36
  from xarray_ms.errors import (
@@ -35,7 +40,6 @@ from xarray_ms.errors import (
35
40
  IrregularChannelGridWarning,
36
41
  IrregularTimeGridWarning,
37
42
  )
38
- from xarray_ms.multiton import Multiton
39
43
 
40
44
 
41
45
  @dataclasses.dataclass
@@ -82,11 +86,11 @@ class CorrelatedFactory(DatasetFactory):
82
86
  for a partition of the Measurement Set"""
83
87
 
84
88
  _preferred_chunks: Dict[str, int]
85
- _ms_factory: Multiton
86
- _antenna_factory: Multiton
87
- _spw_factory: Multiton
88
- _pol_factory: Multiton
89
- _obs_factory: Multiton
89
+ _ms_factory: MainTableFactory
90
+ _antenna_factory: SubtableFactory
91
+ _spw_factory: SubtableFactory
92
+ _pol_factory: SubtableFactory
93
+ _obs_factory: SubtableFactory
90
94
  _main_table_desc: Dict[str, Collection[str]]
91
95
  _coder_factory: MSv2CoderFactory
92
96
 
@@ -94,8 +98,8 @@ class CorrelatedFactory(DatasetFactory):
94
98
  self,
95
99
  partition_key: PartitionKeyT,
96
100
  preferred_chunks: Dict[str, int],
97
- ms_factory: Multiton,
98
- subtable_factories: Dict[str, Multiton],
101
+ ms_factory: MainTableFactory,
102
+ subtable_factories: Dict[str, SubtableFactory],
99
103
  structure_factory: MSv2StructureFactory,
100
104
  ):
101
105
  super().__init__(partition_key, structure_factory, subtable_factories)
@@ -10,7 +10,6 @@ from functools import partial
10
10
  from numbers import Integral
11
11
  from typing import (
12
12
  Any,
13
- ClassVar,
14
13
  Dict,
15
14
  Iterator,
16
15
  List,
@@ -19,13 +18,14 @@ from typing import (
19
18
  Sequence,
20
19
  Set,
21
20
  Tuple,
21
+ TypeAlias,
22
22
  )
23
23
 
24
24
  import numpy as np
25
25
  import numpy.typing as npt
26
26
  import pyarrow as pa
27
27
  from arcae.lib.arrow_tables import Table
28
- from cacheout import Cache
28
+ from rarg_python_patterns.multiton import Multiton
29
29
 
30
30
  from xarray_ms.backend.msv2.imputation import (
31
31
  maybe_impute_field_table,
@@ -37,10 +37,18 @@ from xarray_ms.errors import (
37
37
  InvalidPartitionKey,
38
38
  PartitioningError,
39
39
  )
40
- from xarray_ms.multiton import Multiton
41
40
 
42
41
  logger = logging.getLogger(__name__)
43
42
 
43
+ MainTableFactory: TypeAlias = Multiton[Table]
44
+ """Multiton producing an arcae main-table :class:`~arcae.lib.arrow_tables.Table`."""
45
+
46
+ SubtableFactory: TypeAlias = Multiton[pa.Table]
47
+ """Multiton producing a pyarrow :class:`~pyarrow.Table` subtable."""
48
+
49
+ MSv2StructureFactory: TypeAlias = Multiton["MSv2Structure"]
50
+ """Multiton producing an :class:`MSv2Structure`."""
51
+
44
52
 
45
53
  def nr_of_baselines(na: int, auto_corrs: bool = True) -> int:
46
54
  """Returns the number of baselines, given the number of antenna
@@ -227,96 +235,14 @@ class PartitionData:
227
235
  return aids[a1], aids[a2]
228
236
 
229
237
 
230
- def on_get_keep_alive(key, value, exists):
231
- """Reinsert to refresh the TTL for the entry on get operations"""
232
- if exists:
233
- MSv2StructureFactory._STRUCTURE_CACHE._set(key, value)
234
-
235
-
236
- class MSv2StructureFactory:
237
- """Hashable, callable and picklable factory class
238
- for creating and caching an MSv2Structure"""
239
-
240
- _ms_factory: Multiton
241
- _subtable_factories: Dict[str, Multiton]
242
- _partition_schema: List[str]
243
- _epoch: str
244
- _auto_corrs: bool
245
- _STRUCTURE_CACHE: ClassVar[Cache] = Cache(
246
- maxsize=100, ttl=5 * 60, on_get=on_get_keep_alive
247
- )
248
-
249
- def __init__(
250
- self,
251
- ms: Multiton,
252
- subtables: Dict[str, Multiton],
253
- partition_schema: List[str],
254
- epoch: str,
255
- auto_corrs: bool,
256
- ):
257
- self._ms_factory = ms
258
- self._subtable_factories = subtables
259
- self._partition_schema = partition_schema
260
- self._epoch = epoch
261
- self._auto_corrs = auto_corrs
262
-
263
- def __eq__(self, other: Any) -> bool:
264
- if not isinstance(other, MSv2StructureFactory):
265
- return NotImplemented
266
-
267
- return (
268
- self._ms_factory == other._ms_factory
269
- and self._subtable_factories == other._subtable_factories
270
- and self._partition_schema == other._partition_schema
271
- and self._epoch == other._epoch
272
- and self._auto_corrs == other._auto_corrs
273
- )
274
-
275
- def __hash__(self):
276
- return hash(
277
- (
278
- self._ms_factory,
279
- frozenset(self._subtable_factories.items()),
280
- tuple(self._partition_schema),
281
- self._epoch,
282
- self._auto_corrs,
283
- )
284
- )
285
-
286
- def __reduce__(self):
287
- return (
288
- MSv2StructureFactory,
289
- (
290
- self._ms_factory,
291
- self._subtable_factories,
292
- self._partition_schema,
293
- self._epoch,
294
- self._auto_corrs,
295
- ),
296
- )
297
-
298
- @staticmethod
299
- def _create_instance(self):
300
- return MSv2Structure(
301
- self._ms_factory,
302
- self._subtable_factories,
303
- self._partition_schema,
304
- self._auto_corrs,
305
- )
306
-
307
- @property
308
- def instance(self) -> MSv2Structure:
309
- return self._STRUCTURE_CACHE.get(self, self._create_instance)
310
-
311
- def release(self) -> bool:
312
- return self._STRUCTURE_CACHE.delete(self) > 0
313
-
314
-
315
238
  class MSv2Structure(Mapping):
316
239
  """Holds structural information about an MSv2 dataset"""
317
240
 
318
- _ms_factory: Multiton
319
- _subtable_factories: Dict[str, Multiton]
241
+ _ms_factory: MainTableFactory
242
+ _subtable_factories: Dict[str, SubtableFactory]
243
+ # Not used internally, but distinguishes Multiton keys so that
244
+ # different epochs force regeneration of the same structure
245
+ _epoch: str
320
246
  _partition_columns: List[str]
321
247
  _subtable_partition_columns: List[str]
322
248
  _partitions: Mapping[PartitionKeyT, PartitionData]
@@ -606,9 +532,10 @@ class MSv2Structure(Mapping):
606
532
 
607
533
  def __init__(
608
534
  self,
609
- ms: Multiton,
610
- subtable_factories: Dict[str, Multiton],
535
+ ms: MainTableFactory,
536
+ subtable_factories: Dict[str, SubtableFactory],
611
537
  partition_schema: List[str],
538
+ epoch: str,
612
539
  auto_corrs: bool,
613
540
  ):
614
541
  import time as modtime
@@ -621,6 +548,7 @@ class MSv2Structure(Mapping):
621
548
 
622
549
  self._ms_factory = ms
623
550
  self._subtable_factories = subtable_factories
551
+ self._epoch = epoch
624
552
  self._partition_columns = partition_columns
625
553
  self._subtable_partition_columns = subtable_columns
626
554
 
@@ -1,70 +0,0 @@
1
- import pickle
2
-
3
- import xarray
4
- from arcae.lib.arrow_tables import Table
5
-
6
- from xarray_ms.multiton import Multiton
7
-
8
-
9
- def test_multiton_duplicate_args(simmed_ms):
10
- """Tests that when opened with duplicate arguments,
11
- the same Multiton is returned"""
12
-
13
- multiton = Multiton(Table.from_filename, simmed_ms, readonly=False)
14
- data = multiton.instance.getcol("DATA")
15
- assert data.shape == (30, 8, 4)
16
- assert set(multiton._INSTANCE_CACHE.keys()) == {multiton}
17
-
18
- multiton2 = Multiton(Table.from_filename, simmed_ms, readonly=False)
19
- assert multiton2.instance is multiton.instance
20
- assert set(multiton._INSTANCE_CACHE.keys()) == {multiton}
21
-
22
- multiton3 = Multiton(Table.from_filename, simmed_ms)
23
- assert multiton3.instance is not multiton.instance
24
- assert multiton3.instance is not multiton2.instance
25
- assert set(multiton._INSTANCE_CACHE.keys()) == {multiton, multiton3}
26
-
27
- # Check the positional args passed as kwargs are normalised
28
- # as positional args
29
- multiton4 = Multiton(Table.from_filename, filename=simmed_ms, readonly=False)
30
- assert multiton4.instance is multiton.instance
31
- assert multiton4.instance is multiton2.instance
32
- assert multiton4.instance is not multiton3.instance
33
- assert set(multiton._INSTANCE_CACHE.keys()) == {multiton, multiton3}
34
-
35
-
36
- def test_multiton_release():
37
- data = {"closed": False}
38
-
39
- class A:
40
- def close(self):
41
- data["closed"] = True
42
-
43
- multiton = Multiton(A)
44
- multiton.instance
45
- assert set(multiton._INSTANCE_CACHE.keys()) == {multiton}
46
- multiton.release()
47
- assert set(multiton._INSTANCE_CACHE.keys()) == set()
48
- assert data["closed"] is True
49
-
50
-
51
- def test_multiton_datatree_release(simmed_ms):
52
- assert len(Multiton._INSTANCE_CACHE) == 0
53
-
54
- with xarray.open_datatree(simmed_ms):
55
- assert len(Multiton._INSTANCE_CACHE) > 0
56
-
57
- assert len(Multiton._INSTANCE_CACHE) == 0
58
-
59
-
60
- def test_multiton_pickling(simmed_ms):
61
- """Test that a pickle roundtrip of the Multiton results in the same object"""
62
-
63
- multiton = Multiton(Table.from_filename, simmed_ms)
64
- data = multiton.instance.getcol("DATA")
65
- assert data.shape == (30, 8, 4)
66
- assert set(multiton._INSTANCE_CACHE.keys()) == {multiton}
67
-
68
- multiton2 = pickle.loads(pickle.dumps(multiton))
69
- assert multiton.instance is multiton2.instance
70
- assert set(multiton._INSTANCE_CACHE.keys()) == {multiton}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes