I/O Registry (astropy.io.registry)

Note

The I/O registry is only meant to be used directly by users who want to define their own custom readers/writers. Users who want to find out more about what built-in formats are supported by Table by default should see Unified File Read/Write Interface. Likewise Read, Write, and Convert Cosmology Objects for built-in formats supported by Cosmology. No built-in formats are currently defined for NDData, but this will be added in future.

Introduction

The I/O registry is a submodule used to define the readers/writers available for the Table, NDData, and Cosmology classes.

Custom Read/Write Functions

This section demonstrates how to create a custom reader/writer. A reader is written as a function that can take any arguments except format (which is needed when manually specifying the format — see below) and returns an instance of the Table or NDData classes (or subclasses).

Examples

Here we assume that we are trying to write a reader/writer for the Table class:

>>> from astropy.table import Table

>>> def my_table_reader(filename, some_option=1):
...     # Read in the table by any means necessary
...     return table  # should be an instance of Table

Such a function can then be registered with the I/O registry:

from astropy.io import registry
registry.register_reader('my-table-format', Table, my_table_reader)

where the first argument is the name of the format, the second argument is the class that the function returns an instance for, and the third argument is the reader itself.

We can then read in a table with:

d = Table.read('my_table_file.mtf', format='my-table-format')

In practice, it would be nice to have the read method automatically identify that this file is in the my-table-format format, so we can construct a function that can recognize these files, which we refer to here as an identifier function.

An identifier function should take a first argument that is a string which indicates whether the identifier is being called from read or write, and should then accept an arbitrary number of positional and keyword arguments via *args and **kwargs, which are the arguments passed to the read method.

In the above case, we can write a function that only looks at filenames (but in practice, this function could even look at the first few bytes of the file, for example). The only requirement for the identifier function is that it return a boolean indicating whether the input matches that expected for the format. In our example, we want to automatically recognize files with filenames ending in .mtf as being in the my-table-format format:

import os

def identify_mtf(origin, *args, **kwargs):
    return (isinstance(args[0], str) and
            os.path.splitext(args[0].lower())[1] == '.mtf')

Note

Identifier functions should be prepared for arbitrary input — in particular, the first argument may not be a filename or file object, so it should not assume that this is the case.

We then register this identifier function, similarly to the reader function:

registry.register_identifier('my-table-format', Table, identify_mtf)

Having registered this function, we can then do:

t = Table.read('catalog.mtf')

If multiple formats match the current input, then an exception is raised, and similarly if no format matches the current input. In that case, the format should be explicitly given with the format= keyword argument.

It is also possible to create custom writers. To go with our custom reader above, we can write a custom writer:

def my_table_writer(table, filename, overwrite=False):
    ...  # Write the table out to a file
    return ...  # generally None, but other values are not forbidden.

Writer functions should take a dataset object (either an instance of the Table or NDData classes or subclasses), and any number of subsequent positional and keyword arguments — although as for the reader, the format keyword argument cannot be used.

We then register the writer:

registry.register_writer('my-custom-format', Table, my_table_writer)

We can write the table out to a file:

t.write('catalog_new.mtf', format='my-table-format')

Since we have already registered the identifier function, we can also do:

t.write('catalog_new.mtf')

Registries, local and default

Changed in version 5.0.

As of Astropy 5.0 the I/O registry submodule has switched to a class-based architecture, allowing for the creation of custom registries. The three supported registry types are read-only – UnifiedInputRegistry – write-only – UnifiedOutputRegistry – and read/write – UnifiedIORegistry.

>>> from astropy.io.registry import UnifiedIORegistry
>>> example_reg = UnifiedIORegistry()
>>> print([m for m in dir(example_reg) if not m.startswith("_")])
['available_registries', 'delay_doc_updates', 'get_formats', 'get_reader',
 'get_writer', 'identify_format', 'read', 'register_identifier',
 'register_reader', 'register_writer', 'unregister_identifier',
 'unregister_reader', 'unregister_writer', 'write']

For backward compatibility all the methods on this registry have corresponding module-level functions, which work with the default global read/write registry. These functions were used in the previous examples. This new registry is empty.

>>> example_reg.get_formats()
<Table length=0>
Data class  Format   Read   Write  Auto-identify
 float64   float64 float64 float64    float64
---------- ------- ------- ------- -------------

We can register read / write / identify methods with this registry object:

>>> example_reg.register_reader('my-table-format', Table, my_table_reader)
>>> example_reg.get_formats()
<Table length=1>
Data class      Format     Read Write Auto-identify
   str5         str15      str3  str2      str2
---------- --------------- ---- ----- -------------
     Table my-table-format  Yes    No            No

What is the use of a custom registries?

  1. To make read-only or write-only registries.

  2. To allow for different readers for the same format.

  3. To allow for an object to have different kinds of readers and writers. E.g. Cosmology which supports both file I/O and object conversion.

Reference/API

astropy.io.registry Package

Unified I/O Registry.

Functions

register_reader(data_format, data_class, …)

Register a reader function.

register_writer(data_format, data_class, …)

Register a table writer function.

register_identifier(data_format, data_class, …)

Associate an identifier function with a specific data type.

unregister_reader(data_format, data_class)

Unregister a reader function

unregister_writer(data_format, data_class)

Unregister a writer function

unregister_identifier(data_format, data_class)

Unregister an identifier function

get_reader(data_format, data_class)

Get reader for data_format.

get_writer(data_format, data_class)

Get writer for data_format.

get_formats([data_class, readwrite])

Get the list of registered I/O formats as a Table.

read(cls, *args[, format, cache])

Read in data.

write(data, *args[, format])

Write out data.

identify_format(origin, data_class_required, …)

Loop through identifiers to see which formats match.

delay_doc_updates(self, cls)

Contextmanager to disable documentation updates when registering reader and writer.

Classes

UnifiedIORegistry()

Unified I/O Registry.

UnifiedInputRegistry()

Read-only Unified Registry.

UnifiedOutputRegistry()

Write-only Registry.

UnifiedReadWriteMethod([fget, fset, fdel, doc])

Descriptor class for creating read() and write() methods in unified I/O.

UnifiedReadWrite(instance, cls, method_name)

Base class for the worker object used in unified read() or write() methods.

IORegistryError

Custom error for registry clashes.

Class Inheritance Diagram

Inheritance diagram of astropy.io.registry.core.UnifiedIORegistry, astropy.io.registry.core.UnifiedInputRegistry, astropy.io.registry.core.UnifiedOutputRegistry, astropy.io.registry.interface.UnifiedReadWriteMethod, astropy.io.registry.interface.UnifiedReadWrite, astropy.io.registry.base.IORegistryError