xarray-ms 0.5.4__tar.gz → 0.5.5__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 (69) hide show
  1. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/.github/workflows/ci.yml +1 -1
  2. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/.readthedocs.yaml +4 -4
  3. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/PKG-INFO +3 -20
  4. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/source/changelog.rst +10 -1
  5. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/source/conf.py +1 -1
  6. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/source/install.rst +7 -11
  7. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/source/links.rst +1 -0
  8. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/source/partitioning.rst +2 -2
  9. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/source/roadmap.rst +1 -1
  10. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/source/tutorial.rst +1 -1
  11. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/pyproject.toml +6 -6
  12. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/conftest.py +2 -3
  13. xarray_ms-0.5.5/tests/test_accessors.py +24 -0
  14. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_backend.py +0 -12
  15. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_structure.py +27 -19
  16. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/array.py +7 -4
  17. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/entrypoint.py +11 -21
  18. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/entrypoint_utils.py +10 -6
  19. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/factories/core.py +7 -4
  20. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/factories/correlated.py +13 -9
  21. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/structure.py +20 -92
  22. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/errors.py +0 -4
  23. xarray_ms-0.5.4/tests/test_multiton.py +0 -70
  24. xarray_ms-0.5.4/xarray_ms/multiton.py +0 -73
  25. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/.github/ISSUE_TEMPLATE.md +0 -0
  26. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  27. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/.github/dependabot.yml +0 -0
  28. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/.github/workflows/pre-commit.yml +0 -0
  29. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/.github/workflows/readthedocs.yml +0 -0
  30. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/.gitignore +0 -0
  31. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/.pre-commit-config.yaml +0 -0
  32. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/LICENSE +0 -0
  33. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/README.rst +0 -0
  34. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/Makefile +0 -0
  35. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/make.bat +0 -0
  36. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/source/api.rst +0 -0
  37. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/source/index.rst +0 -0
  38. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/doc/source/introduction.rst +0 -0
  39. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/hello.txt +0 -0
  40. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/__init__.py +0 -0
  41. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/msv4_test_corpus/__init__.py +0 -0
  42. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/msv4_test_corpus/conftest.py +0 -0
  43. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/msv4_test_corpus/test_msv_corpus.py +0 -0
  44. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_antenna.py +0 -0
  45. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_basic.py +0 -0
  46. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_field_and_source.py +0 -0
  47. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_github.py +0 -0
  48. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_imputation.py +0 -0
  49. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_measures.py +0 -0
  50. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_read.py +0 -0
  51. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_table_utils.py +0 -0
  52. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_utils.py +0 -0
  53. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/tests/test_zarr_roundtrip.py +0 -0
  54. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/__init__.py +0 -0
  55. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/factories/__init__.py +0 -0
  56. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/factories/antenna.py +0 -0
  57. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/factories/field_and_source.py +0 -0
  58. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/imputation.py +0 -0
  59. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/measures_adapters.py +0 -0
  60. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/measures_encoders.py +0 -0
  61. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/partition.py +0 -0
  62. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/backend/msv2/table_utils.py +0 -0
  63. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/casa_types.py +0 -0
  64. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/msv4_types.py +0 -0
  65. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/query.py +0 -0
  66. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/testing/__init__.py +0 -0
  67. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/testing/simulator.py +0 -0
  68. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/xarray_ms/testing/utils.py +0 -0
  69. {xarray_ms-0.5.4 → xarray_ms-0.5.5}/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.5.4
3
+ Version: 0.5.5
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.6.0,>=0.5.1
9
- Requires-Dist: cacheout>=0.16.0
8
+ Requires-Dist: arcae<0.6.0,>=0.5.2
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,15 @@
3
3
  Changelog
4
4
  =========
5
5
 
6
+ 0.5.5 (12-06-2026)
7
+ ------------------
8
+ * Upgrade to arcae 0.5.2 (:pr:`161`)
9
+ * Documentation fixes (:pr:`160`)
10
+ * Replace ``xarray_ms.multiton.Multiton`` with ``rarg-python-patterns.multiton.Multiton`` (:pr:`159`)
11
+ * Move development dependencies in dependency groups (:pr:`158`)
12
+ * Remove ``*kwargs`` from ``MSv2EntryPoint`` methods (:pr:`156`)
13
+ * Revert: Allow unsupported arguments in entrypoint methods (#154) (:pr:`155`)
14
+
6
15
  0.5.4 (20-05-2026)
7
16
  ------------------
8
17
  * Allow unsupported arguments in entrypoint methods (:pr:`154`)
@@ -41,7 +50,7 @@ Changelog
41
50
  * Synchronise with v4.0.0 schema version (:pr:`139`)
42
51
  * Remove ``correlated_xds.scan_number`` coordinate (:pr:`139`)
43
52
  * Remove ``correlated_xds.sub_scan_number`` coordinate (:pr:`139`)
44
- * Add ``corelated_xds.scan_name`` coordinate (:pr:`139`)
53
+ * Add ``correlated_xds.scan_name`` coordinate (:pr:`139`)
45
54
  * Add ``correlated_xds.frequency.spectral_window_intents`` coordinate attribute (:pr:`139`)
46
55
  * Remove ``correlated_xds.observation_info["intents"]`` (:pr:`139`)
47
56
  * 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.5.4"
14
+ release = "0.5.5"
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,19 +1,19 @@
1
1
  [project]
2
2
  name = "xarray-ms"
3
- version = "0.5.4"
3
+ version = "0.5.5"
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.5.1, < 0.6.0",
10
+ "arcae>=0.5.2, < 0.6.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 = [
15
+ [dependency-groups]
16
+ test = [
17
17
  "pytest>=8.0.0",
18
18
  "dask>=2024.5.0",
19
19
  "distributed>=2024.5.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.5.4"
58
+ current = "0.5.5"
59
59
 
60
60
  # Example of a semver regexp.
61
61
  # Make sure this matches current_version before
@@ -1,9 +1,8 @@
1
1
  import numpy as np
2
2
  import pytest
3
3
  from arcae.lib.arrow_tables import Table, ms_descriptor
4
+ from rarg_python_patterns.multiton import Multiton
4
5
 
5
- from xarray_ms.backend.msv2.structure import MSv2StructureFactory
6
- from xarray_ms.multiton import Multiton
7
6
  from xarray_ms.testing.simulator import DEFAULT_SIM_PARAMS, MSStructureSimulator
8
7
 
9
8
  MSV4_TEST_CORPUS = "msv4_test_corpus"
@@ -39,7 +38,7 @@ def pytest_collection_modifyitems(config, items):
39
38
  def clear_caches():
40
39
  yield
41
40
  Multiton._INSTANCE_CACHE.clear()
42
- MSv2StructureFactory._STRUCTURE_CACHE.clear()
41
+ Multiton._EXPIRY_HEAP.clear()
43
42
 
44
43
 
45
44
  @pytest.fixture(scope="session", params=[DEFAULT_SIM_PARAMS])
@@ -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,7 +7,6 @@ import xarray.testing as xt
7
7
  from numpy.testing import assert_array_equal
8
8
 
9
9
  from xarray_ms.backend.msv2.entrypoint import MSv2EntryPoint
10
- from xarray_ms.errors import IgnoredArgument
11
10
  from xarray_ms.testing.utils import id_string, prune_datetime_structures
12
11
 
13
12
 
@@ -238,14 +237,3 @@ def test_open_datatree_chunking(simmed_ms):
238
237
  }
239
238
 
240
239
  assert ncdt.identical(prune_datetime_structures(dt))
241
-
242
-
243
- @pytest.mark.parametrize(
244
- "simmed_ms",
245
- [{"name": "ignored_kwargs.ms"}],
246
- indirect=True,
247
- )
248
- def test_ignored_kwargs_warning(simmed_ms):
249
- with pytest.warns(IgnoredArgument, match="unsupported_kwarg"):
250
- dt = xarray.open_datatree(simmed_ms, unsupported_kwarg=True)
251
- assert isinstance(dt, xarray.DataTree)
@@ -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
@@ -15,8 +15,11 @@ from xarray.core.indexing import (
15
15
  if TYPE_CHECKING:
16
16
  import numpy.typing as npt
17
17
 
18
- from xarray_ms.backend.msv2.structure import MSv2StructureFactory, PartitionKeyT
19
- from xarray_ms.multiton import Multiton
18
+ from xarray_ms.backend.msv2.structure import (
19
+ MainTableFactory,
20
+ MSv2StructureFactory,
21
+ PartitionKeyT,
22
+ )
20
23
 
21
24
  TransformerT = Callable[[npt.NDArray], npt.NDArray]
22
25
 
@@ -73,7 +76,7 @@ class MainMSv2Array(MSv2Array):
73
76
  "_transform",
74
77
  )
75
78
 
76
- _table_factory: Multiton
79
+ _table_factory: MainTableFactory
77
80
  _structure_factory: MSv2StructureFactory
78
81
  _partition: PartitionKeyT
79
82
  _column: str
@@ -82,7 +85,7 @@ class MainMSv2Array(MSv2Array):
82
85
 
83
86
  def __init__(
84
87
  self,
85
- table_factory: Multiton,
88
+ table_factory: MainTableFactory,
86
89
  structure_factory: MSv2StructureFactory,
87
90
  partition: PartitionKeyT,
88
91
  column: str,
@@ -25,13 +25,8 @@ from xarray_ms.backend.msv2.structure import (
25
25
  MSv2Structure,
26
26
  MSv2StructureFactory,
27
27
  )
28
- from xarray_ms.errors import (
29
- FrameConversionWarning,
30
- IgnoredArgument,
31
- InvalidPartitionKey,
32
- )
28
+ from xarray_ms.errors import FrameConversionWarning, InvalidPartitionKey
33
29
  from xarray_ms.msv4_types import CORRELATED_DATASET_TYPES
34
- from xarray_ms.multiton import Multiton
35
30
  from xarray_ms.utils import format_docstring
36
31
 
37
32
  if TYPE_CHECKING:
@@ -39,7 +34,12 @@ if TYPE_CHECKING:
39
34
 
40
35
  from xarray.backends.common import AbstractDataStore
41
36
 
42
- from xarray_ms.backend.msv2.structure import DEFAULT_PARTITION_COLUMNS, PartitionKeyT
37
+ from xarray_ms.backend.msv2.structure import (
38
+ DEFAULT_PARTITION_COLUMNS,
39
+ MainTableFactory,
40
+ PartitionKeyT,
41
+ SubtableFactory,
42
+ )
43
43
 
44
44
 
45
45
  def promote_chunks(
@@ -90,8 +90,8 @@ class MSv2Store(AbstractWritableDataStore):
90
90
  "_epoch",
91
91
  )
92
92
 
93
- _table_factory: Multiton
94
- _subtable_factories: Dict[str, Multiton]
93
+ _table_factory: MainTableFactory
94
+ _subtable_factories: Dict[str, SubtableFactory]
95
95
  _structure_factory: MSv2StructureFactory
96
96
  _partition_schema: List[str]
97
97
  _partition_key: PartitionKeyT
@@ -102,8 +102,8 @@ class MSv2Store(AbstractWritableDataStore):
102
102
 
103
103
  def __init__(
104
104
  self,
105
- table_factory: Multiton,
106
- subtable_factories: Dict[str, Multiton],
105
+ table_factory: MainTableFactory,
106
+ subtable_factories: Dict[str, SubtableFactory],
107
107
  structure_factory: MSv2StructureFactory,
108
108
  partition_schema: List[str],
109
109
  partition_key: PartitionKeyT,
@@ -314,7 +314,6 @@ class MSv2EntryPoint(BackendEntrypoint):
314
314
  auto_corrs: bool = False,
315
315
  ninstances: int = 8,
316
316
  epoch: str | None = None,
317
- **kwargs,
318
317
  ) -> DataTree:
319
318
  """Create a :class:`~xarray.core.datatree.DataTree` presenting an MSv4 view
320
319
  over multiple partitions of a MSv2 CASA Measurement Set.
@@ -370,7 +369,6 @@ class MSv2EntryPoint(BackendEntrypoint):
370
369
  auto_corrs=auto_corrs,
371
370
  ninstances=ninstances,
372
371
  epoch=epoch,
373
- **kwargs,
374
372
  )
375
373
 
376
374
  dt = DataTree.from_dict(groups_dict)
@@ -451,18 +449,10 @@ class MSv2EntryPoint(BackendEntrypoint):
451
449
  ninstances: int = 8,
452
450
  epoch: str | None = None,
453
451
  structure_factory: MSv2StructureFactory | None = None,
454
- **kwargs,
455
452
  ) -> Dict[str, Dataset]:
456
453
  """Create a dictionary of :class:`~xarray.Dataset` presenting an MSv4 view
457
454
  over a partition of a MSv2 CASA Measurement Set"""
458
455
 
459
- if kwargs:
460
- warnings.warn(
461
- f"xarray-ms does not support the following arguments and will ignore them: "
462
- f"{sorted(kwargs)}",
463
- IgnoredArgument,
464
- )
465
-
466
456
  if isinstance(filename_or_obj, os.PathLike):
467
457
  ms = str(filename_or_obj)
468
458
  elif isinstance(filename_or_obj, str):
@@ -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 = [
@@ -59,8 +62,8 @@ class CommonStoreArgs:
59
62
  epoch: str
60
63
  partition_schema: List[str]
61
64
  preferred_chunks: Dict[str, int]
62
- ms_factory: Multiton
63
- subtable_factories: Dict[str, Multiton]
65
+ ms_factory: MainTableFactory
66
+ subtable_factories: Dict[str, SubtableFactory]
64
67
  structure_factory: MSv2StructureFactory
65
68
 
66
69
  __slots__ = (
@@ -83,8 +86,8 @@ class CommonStoreArgs:
83
86
  epoch: str | None = None,
84
87
  partition_schema: List[str] | None = None,
85
88
  preferred_chunks: Dict[str, int] | None = None,
86
- ms_factory: Multiton | None = None,
87
- subtable_factories: Dict[str, Multiton] | None = None,
89
+ ms_factory: MainTableFactory | None = None,
90
+ subtable_factories: Dict[str, SubtableFactory] | None = None,
88
91
  structure_factory: MSv2StructureFactory | None = None,
89
92
  ):
90
93
  if not os.path.exists(ms):
@@ -107,7 +110,8 @@ class CommonStoreArgs:
107
110
  subtable: Multiton(subtable_factory, f"{ms}::{subtable}")
108
111
  for subtable in (DEFAULT_SUBTABLES + EXTRA_SUBTABLES)
109
112
  }
110
- self.structure_factory = structure_factory or MSv2StructureFactory(
113
+ self.structure_factory = structure_factory or Multiton(
114
+ MSv2Structure,
111
115
  self.ms_factory,
112
116
  self.subtable_factories,
113
117
  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,7 +1,3 @@
1
- class IgnoredArgument(UserWarning):
2
- """Issued when keyword arguments are passed that this backend does not support."""
3
-
4
-
5
1
  class IrregularGridWarning(UserWarning):
6
2
  """Base Warning for irregular grids"""
7
3
 
@@ -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}
@@ -1,73 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any, Callable, ClassVar, Mapping, Tuple
4
-
5
- from cacheout import Cache
6
-
7
- from xarray_ms.utils import FrozenKey, normalise_args
8
-
9
- FactoryFunctionT = Callable[..., Any]
10
-
11
-
12
- def on_get_keep_alive(key, value, exists):
13
- """Re-insert on get to update the TTL"""
14
- if exists:
15
- # Re-insert to update the TTL
16
- Multiton._INSTANCE_CACHE.set(key, value)
17
-
18
-
19
- def on_instance_delete(key, value, cause):
20
- """Invoke any instance close methods on deletion"""
21
- if hasattr(value, "close") and callable(value.close):
22
- value.close()
23
-
24
-
25
- class Multiton:
26
- """Hashable and pickleable factory class
27
- for creating and caching an object instance"""
28
-
29
- _INSTANCE_CACHE: ClassVar[Cache] = Cache(
30
- maxsize=100, ttl=5 * 60, on_get=on_get_keep_alive, on_delete=on_instance_delete
31
- )
32
- _factory: FactoryFunctionT
33
- _args: Tuple[Any, ...]
34
- _kw: Mapping[str, Any]
35
- _key: FrozenKey
36
-
37
- def __init__(self, factory: FactoryFunctionT, *args: Any, **kw: Any):
38
- self._factory = factory
39
- self._args, self._kw = normalise_args(factory, args, kw)
40
- self._key = FrozenKey(factory, *self._args, **self._kw)
41
-
42
- @staticmethod
43
- def from_reduce_args(
44
- factory: FactoryFunctionT, args: Tuple[Any, ...], kw: Mapping[str, Any]
45
- ) -> Multiton:
46
- return Multiton(factory, *args, **kw)
47
-
48
- def __reduce__(
49
- self,
50
- ) -> Tuple[Callable, Tuple[Callable, Tuple[Any, ...], Mapping[str, Any]]]:
51
- return (self.from_reduce_args, (self._factory, self._args, self._kw))
52
-
53
- def __hash__(self) -> int:
54
- return hash(self._key)
55
-
56
- def __eq__(self, other: Any) -> bool:
57
- if not isinstance(other, Multiton):
58
- return NotImplemented
59
- return self._key == other._key
60
-
61
- @staticmethod
62
- def _create_instance(self) -> Any:
63
- return self._factory(*self._args, **self._kw)
64
-
65
- @property
66
- def instance(self) -> Any:
67
- """Create the object instance represented by the Multiton,
68
- or retrieved the cache instance"""
69
- return self._INSTANCE_CACHE.get(self, self._create_instance)
70
-
71
- def release(self) -> bool:
72
- """Evict any cached instance associated with this Multiton"""
73
- return self._INSTANCE_CACHE.delete(self) > 0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes