Skip to content

Commit a8cb89c

Browse files
committed
Make base class private b/c RTD complaints
Signed-off-by: Nathaniel Starkman (@nstarman) <nstarkman@protonmail.com>
1 parent c96879c commit a8cb89c

3 files changed

Lines changed: 161 additions & 47 deletions

File tree

astropy/io/registry/base.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class IORegistryError(Exception):
1919

2020
# -----------------------------------------------------------------------------
2121

22-
class UnifiedIORegistryBase(metaclass=abc.ABCMeta):
22+
class _UnifiedIORegistryBase(metaclass=abc.ABCMeta):
2323
"""Base class for registries in Astropy's Unified IO.
2424
2525
This base class provides identification functions and miscellaneous
@@ -32,6 +32,8 @@ class UnifiedIORegistryBase(metaclass=abc.ABCMeta):
3232
:class:`~astropy.io.registry.UnifiedOutputRegistry` to enable both
3333
reading from and writing to files.
3434
35+
.. versionadded:: 5.0
36+
3537
"""
3638

3739
def __init__(self):
@@ -41,17 +43,44 @@ def __init__(self):
4143
# what this class can do: e.g. 'read' &/or 'write'
4244
self._registries = dict()
4345
self._registries["identify"] = dict(attr="_identifiers", column="Auto-identify")
44-
self._registries_order = ("identify", )
46+
self._registries_order = ("identify", ) # match keys in `_registries`
4547

4648
# If multiple formats are added to one class the update of the docs is quite
4749
# expensive. Classes for which the doc update is temporarly delayed are added
4850
# to this set.
4951
self._delayed_docs_classes = set()
5052

53+
@property
54+
def available_registries(self):
55+
"""Available registries.
56+
57+
Returns
58+
-------
59+
``dict_keys``
60+
"""
61+
return self._registries.keys()
62+
5163
def get_formats(self, data_class=None, filter_on=None):
5264
"""
53-
Get the list of registered formats as a Table.
54-
Method is abstract and must be overwritten by subclasses.
65+
Get the list of registered formats as a `~astropy.table.Table`.
66+
67+
Parameters
68+
----------
69+
data_class : class or None, optional
70+
Filter readers/writer to match data class (default = all classes).
71+
filter_on : str or None, optional
72+
Which registry to show. E.g. "identify"
73+
If None search for both. Default is None.
74+
75+
Returns
76+
-------
77+
format_table : :class:`~astropy.table.Table`
78+
Table of available I/O formats.
79+
80+
Raises
81+
------
82+
ValueError
83+
If ``filter_on`` is not None nor a registry name.
5584
"""
5685
from astropy.table import Table
5786

@@ -136,10 +165,6 @@ def delay_doc_updates(self, cls):
136165
because the documentation of the corresponding ``read`` and ``write``
137166
methods are build every time.
138167
139-
.. warning::
140-
This contextmanager is experimental and may be replaced by a more
141-
general approach.
142-
143168
Examples
144169
--------
145170
see for example the source code of ``astropy.table.__init__``.
@@ -204,7 +229,6 @@ def my_identifier(*args, **kwargs):
204229
register_identifier('ipac', Table, my_identifier)
205230
unregister_identifier('ipac', Table)
206231
"""
207-
208232
if not (data_format, data_class) in self._identifiers or force:
209233
self._identifiers[(data_format, data_class)] = identifier
210234
else:
@@ -223,7 +247,6 @@ def unregister_identifier(self, data_format, data_class):
223247
data_class : class
224248
The class of the object that can be read/written.
225249
"""
226-
227250
if (data_format, data_class) in self._identifiers:
228251
self._identifiers.pop((data_format, data_class))
229252
else:
@@ -270,8 +293,9 @@ def identify_format(self, origin, data_class_required, path, fileobj, args, kwar
270293
# =========================================================================
271294
# Utils
272295

273-
def _get_format_table_str(self, data_class, readwrite):
274-
format_table = self.get_formats(data_class, readwrite)
296+
def _get_format_table_str(self, data_class, filter_on):
297+
"""``get_formats()``, without column "Data class", as a str."""
298+
format_table = self.get_formats(data_class, filter_on)
275299
format_table.remove_column('Data class')
276300
format_table_str = '\n'.join(format_table.pformat(max_lines=-1))
277301
return format_table_str

astropy/io/registry/core.py

Lines changed: 101 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import numpy as np
88

9-
from .base import IORegistryError, UnifiedIORegistryBase
9+
from .base import IORegistryError, _UnifiedIORegistryBase
1010

1111
__all__ = ['UnifiedIORegistry', 'UnifiedInputRegistry', 'UnifiedOutputRegistry']
1212

@@ -16,17 +16,69 @@
1616

1717
# -----------------------------------------------------------------------------
1818

19-
class UnifiedInputRegistry(UnifiedIORegistryBase):
20-
"""Read-only Registry."""
19+
class UnifiedInputRegistry(_UnifiedIORegistryBase):
20+
"""Read-only Unified Registry.
21+
22+
.. versionadded:: 5.0
23+
24+
Examples
25+
--------
26+
First let's start by creating a read-only registry.
27+
28+
.. code-block:: python
29+
30+
>>> from astropy.io.registry import UnifiedInputRegistry
31+
>>> read_reg = UnifiedInputRegistry()
32+
33+
There is nothing in this registry. Let's make a reader for the
34+
:class:`~astropy.table.Table` class::
35+
36+
from astropy.table import Table
37+
38+
def my_table_reader(filename, some_option=1):
39+
# Read in the table by any means necessary
40+
return table # should be an instance of Table
41+
42+
Such a function can then be registered with the I/O registry::
43+
44+
read_reg.register_reader('my-table-format', Table, my_table_reader)
45+
46+
Note that we CANNOT then read in a table with::
47+
48+
d = Table.read('my_table_file.mtf', format='my-table-format')
49+
50+
Why? because ``Table.read`` uses Astropy's default global registry and this
51+
is a separate registry.
52+
Instead we can read by the read method on the registry::
53+
54+
d = read_reg.read(Table, 'my_table_file.mtf', format='my-table-format')
55+
56+
"""
2157

2258
def __init__(self):
2359
super().__init__() # set _identifiers
2460
self._readers = OrderedDict()
2561
self._registries["read"] = dict(attr="_readers", column="Read")
2662
self._registries_order = ("read", "identify")
2763

28-
def get_formats(self, data_class=None, *args):
29-
return super().get_formats(data_class, filter_on="Read")
64+
def get_formats(self, data_class=None, filter_on="Read"):
65+
"""
66+
Get the list of registered formats as a Table.
67+
68+
Parameters
69+
----------
70+
data_class : class or None, optional
71+
Filter readers/writer to match data class (default = all classes).
72+
filter_on : str or None, optional
73+
Which registry to show. E.g. "identify"
74+
If None search for both. Default is "Read".
75+
76+
Returns
77+
-------
78+
format_table : :class:`~astropy.table.Table`
79+
Table of available I/O formats.
80+
"""
81+
return super().get_formats(data_class, filter_on)
3082

3183
# =========================================================================
3284
# Read methods
@@ -116,9 +168,22 @@ def read(self, cls, *args, format=None, cache=False, **kwargs):
116168
"""
117169
Read in data.
118170
119-
The arguments passed to this method depend on the format.
120-
"""
171+
Parameters
172+
----------
173+
cls : class
174+
*args
175+
The arguments passed to this method depend on the format.
176+
format : str or None
177+
cache : bool
178+
Whether to cache the results of reading in the data.
179+
**kwargs
180+
The arguments passed to this method depend on the format.
121181
182+
Returns
183+
-------
184+
object or None
185+
The output of the registered reader.
186+
"""
122187
ctx = None
123188
try:
124189
if format is None:
@@ -170,17 +235,20 @@ def read(self, cls, *args, format=None, cache=False, **kwargs):
170235

171236
# -----------------------------------------------------------------------------
172237

173-
class UnifiedOutputRegistry(UnifiedIORegistryBase):
174-
"""Write-only Registry."""
238+
class UnifiedOutputRegistry(_UnifiedIORegistryBase):
239+
"""Write-only Registry.
240+
241+
.. versionadded:: 5.0
242+
"""
175243

176244
def __init__(self):
177245
super().__init__()
178246
self._writers = OrderedDict()
179247
self._registries["write"] = dict(attr="_writers", column="Write")
180248
self._registries_order = ("write", "identify", )
181249

182-
def get_formats(self, data_class=None, *args):
183-
return super().get_formats(data_class, filter_on="Write")
250+
def get_formats(self, data_class=None, filter_on="Write"):
251+
return super().get_formats(data_class, filter_on)
184252

185253
# =========================================================================
186254
# Write Methods
@@ -269,7 +337,22 @@ def write(self, data, *args, format=None, **kwargs):
269337
"""
270338
Write out data.
271339
272-
The arguments passed to this method depend on the format.
340+
Parameters
341+
----------
342+
data : object
343+
The data to write.
344+
*args
345+
The arguments passed to this method depend on the format.
346+
format : str or None
347+
**kwargs
348+
The arguments passed to this method depend on the format.
349+
350+
Returns
351+
-------
352+
object or None
353+
The output of the registered writer. Most often `None`.
354+
355+
.. versionadded:: 4.3
273356
"""
274357

275358
if format is None:
@@ -296,15 +379,18 @@ def write(self, data, *args, format=None, **kwargs):
296379
# -----------------------------------------------------------------------------
297380

298381
class UnifiedIORegistry(UnifiedInputRegistry, UnifiedOutputRegistry):
299-
"""Unified I/O Registry"""
382+
"""Unified I/O Registry.
383+
384+
.. versionadded:: 5.0
385+
"""
300386

301387
def __init__(self):
302388
super().__init__()
303389
self._registries_order = ("read", "write", "identify")
304390

305391
def get_formats(self, data_class=None, readwrite=None):
306392
"""
307-
Get the list of registered I/O formats as a Table.
393+
Get the list of registered I/O formats as a `~astropy.table.Table`.
308394
309395
Parameters
310396
----------
@@ -322,4 +408,4 @@ def get_formats(self, data_class=None, readwrite=None):
322408
format_table : :class:`~astropy.table.Table`
323409
Table of available I/O formats.
324410
"""
325-
return UnifiedIORegistryBase.get_formats(self, data_class, filter_on=readwrite)
411+
return super().get_formats(data_class, readwrite)

astropy/io/registry/tests/test_registries.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@
2424
from astropy.io import registry as io_registry
2525
from astropy.io.registry import (IORegistryError, UnifiedInputRegistry,
2626
UnifiedIORegistry, UnifiedOutputRegistry, compat)
27+
from astropy.io.registry.base import _UnifiedIORegistryBase
2728
from astropy.io.registry.compat import default_registry
28-
from astropy.io.registry.base import UnifiedIORegistryBase
2929
from astropy.table import Table
3030

3131
###############################################################################
3232
# pytest setup and fixtures
3333

3434

35-
class UnifiedIORegistryBaseSubClass(UnifiedIORegistryBase):
35+
class UnifiedIORegistryBaseSubClass(_UnifiedIORegistryBase):
3636
"""Non-abstract subclass of UnifiedIORegistryBase for testing."""
3737

3838
def get_formats(self, data_class=None):
@@ -348,18 +348,20 @@ def test_delay_doc_updates(self, registry, fmtcls1, recwarn):
348348
# test that this method is not present.
349349
if "Format" in EmptyData.read.__doc__:
350350
docs = EmptyData.read.__doc__.split("\n")
351-
ifmt = docs[8].index("Format") + 1
352-
iread = docs[8].index("Read") + 1
351+
ihd = [i for i, s in enumerate(docs)
352+
if ("Format" in s)][0]
353+
ifmt = docs[ihd].index("Format") + 1
354+
iread = docs[ihd].index("Read") + 1
353355
# there might not actually be anything here, which is also good
354-
if docs[9] != docs[10]:
355-
assert docs[10][ifmt : ifmt + 5] == "test"
356-
assert docs[10][iread : iread + 3] != "Yes"
356+
if docs[-2] != docs[-1]:
357+
assert docs[-1][ifmt : ifmt + 5] == "test"
358+
assert docs[-1][iread : iread + 3] != "Yes"
357359
# now test it's updated
358360
docs = EmptyData.read.__doc__.split("\n")
359-
ifmt = docs[8].index("Format") + 2
360-
iread = docs[8].index("Read") + 1
361-
assert docs[10][ifmt : ifmt + 4] == "test"
362-
assert docs[10][iread : iread + 3] == "Yes"
361+
ifmt = docs[ihd].index("Format") + 2
362+
iread = docs[ihd].index("Read") + 1
363+
assert docs[-2][ifmt : ifmt + 4] == "test"
364+
assert docs[-2][iread : iread + 3] == "Yes"
363365

364366
def test_identify_read_format(self, registry):
365367
"""Test ``registry.identify_format()``."""
@@ -729,18 +731,20 @@ def test_delay_doc_updates(self, registry, fmtcls1, recwarn):
729731
# test that this method is not present.
730732
if "Format" in EmptyData.read.__doc__:
731733
docs = EmptyData.write.__doc__.split("\n")
732-
ifmt = docs[8].index("Format")
733-
iwrite = docs[8].index("Write") + 1
734+
ihd = [i for i, s in enumerate(docs)
735+
if ("Format" in s)][0]
736+
ifmt = docs[ihd].index("Format")
737+
iwrite = docs[ihd].index("Write") + 1
734738
# there might not actually be anything here, which is also good
735-
if docs[9] != docs[10]:
736-
assert fmt in docs[10][ifmt : ifmt + len(fmt) + 1]
737-
assert docs[10][iwrite : iwrite + 3] != "Yes"
739+
if docs[-2] != docs[-1]:
740+
assert fmt in docs[-1][ifmt : ifmt + len(fmt) + 1]
741+
assert docs[-1][iwrite : iwrite + 3] != "Yes"
738742
# now test it's updated
739743
docs = EmptyData.write.__doc__.split("\n")
740-
ifmt = docs[8].index("Format") + 1
741-
iwrite = docs[8].index("Write") + 2
742-
assert fmt in docs[10][ifmt : ifmt + len(fmt) + 1]
743-
assert docs[10][iwrite : iwrite + 3] == "Yes"
744+
ifmt = docs[ihd].index("Format") + 1
745+
iwrite = docs[ihd].index("Write") + 2
746+
assert fmt in docs[-2][ifmt : ifmt + len(fmt) + 1]
747+
assert docs[-2][iwrite : iwrite + 3] == "Yes"
744748

745749
@pytest.mark.skip("TODO!")
746750
def test_get_formats(self, registry):

0 commit comments

Comments
 (0)