1.. _io_registry: 2 3************************************ 4I/O Registry (`astropy.io.registry`) 5************************************ 6 7.. note:: 8 9 The I/O registry is only meant to be used directly by users who want to 10 define their own custom readers/writers. Users who want to find out more 11 about what built-in formats are supported by :class:`~astropy.table.Table` 12 by default should see :ref:`table_io`. 13 Likewise :ref:`cosmology_io` for built-in formats supported by 14 :class:`~astropy.cosmology.Cosmology`. 15 No built-in formats are currently defined for 16 :class:`~astropy.nddata.NDData`, but this will be added in future. 17 18Introduction 19============ 20 21The I/O registry is a submodule used to define the readers/writers available 22for the :class:`~astropy.table.Table`, :class:`~astropy.nddata.NDData`, 23and :class:`~astropy.cosmology.Cosmology` classes. 24 25 26Custom Read/Write Functions 27=========================== 28 29This section demonstrates how to create a custom reader/writer. A reader is 30written as a function that can take any arguments except ``format`` (which is 31needed when manually specifying the format — see below) and returns an 32instance of the :class:`~astropy.table.Table` or 33:class:`~astropy.nddata.NDData` classes (or subclasses). 34 35 36Examples 37-------- 38 39.. 40 EXAMPLE START 41 Using astropy.io.registry to Create a Custom Reader/Writer 42 43Here we assume that we are trying to write a reader/writer for the 44:class:`~astropy.table.Table` class:: 45 46 >>> from astropy.table import Table 47 48 >>> def my_table_reader(filename, some_option=1): 49 ... # Read in the table by any means necessary 50 ... return table # should be an instance of Table 51 52Such a function can then be registered with the I/O registry:: 53 54 from astropy.io import registry 55 registry.register_reader('my-table-format', Table, my_table_reader) 56 57where the first argument is the name of the format, the second argument is the 58class that the function returns an instance for, and the third argument is the 59reader itself. 60 61We can then read in a table with:: 62 63 d = Table.read('my_table_file.mtf', format='my-table-format') 64 65In practice, it would be nice to have the ``read`` method automatically 66identify that this file is in the ``my-table-format`` format, so we can 67construct a function that can recognize these files, which we refer to here as 68an *identifier* function. 69 70An identifier function should take a first argument that is a string 71which indicates whether the identifier is being called from ``read`` or 72``write``, and should then accept an arbitrary number of positional and keyword 73arguments via ``*args`` and ``**kwargs``, which are the arguments passed to 74the ``read`` method. 75 76In the above case, we can write a function that only looks at 77filenames (but in practice, this function could even look at the first few 78bytes of the file, for example). The only requirement for the identifier 79function is that it return a boolean indicating whether the input matches that 80expected for the format. In our example, we want to automatically recognize 81files with filenames ending in ``.mtf`` as being in the ``my-table-format`` 82format:: 83 84 import os 85 86 def identify_mtf(origin, *args, **kwargs): 87 return (isinstance(args[0], str) and 88 os.path.splitext(args[0].lower())[1] == '.mtf') 89 90.. note:: 91 92 Identifier functions should be prepared for arbitrary input — in 93 particular, the first argument may not be a filename or file object, so it 94 should not assume that this is the case. 95 96We then register this identifier function, similarly to the reader function:: 97 98 registry.register_identifier('my-table-format', Table, identify_mtf) 99 100Having registered this function, we can then do:: 101 102 t = Table.read('catalog.mtf') 103 104If multiple formats match the current input, then an exception is 105raised, and similarly if no format matches the current input. In that 106case, the format should be explicitly given with the ``format=`` 107keyword argument. 108 109It is also possible to create custom writers. To go with our custom reader 110above, we can write a custom writer:: 111 112 def my_table_writer(table, filename, overwrite=False): 113 ... # Write the table out to a file 114 return ... # generally None, but other values are not forbidden. 115 116Writer functions should take a dataset object (either an instance of the 117:class:`~astropy.table.Table` or :class:`~astropy.nddata.NDData` 118classes or subclasses), and any number of subsequent positional and keyword 119arguments — although as for the reader, the ``format`` keyword argument cannot 120be used. 121 122We then register the writer:: 123 124 registry.register_writer('my-custom-format', Table, my_table_writer) 125 126We can write the table out to a file:: 127 128 t.write('catalog_new.mtf', format='my-table-format') 129 130Since we have already registered the identifier function, we can also do:: 131 132 t.write('catalog_new.mtf') 133 134.. 135 EXAMPLE END 136 137 138Registries, local and default 139============================= 140 141.. versionchanged:: 5.0 142 143As of Astropy 5.0 the I/O registry submodule has switched to a class-based 144architecture, allowing for the creation of custom registries. 145The three supported registry types are read-only -- 146:class:`~astropy.io.registry.UnifiedInputRegistry` -- 147write-only -- :class:`~astropy.io.registry.UnifiedOutputRegistry` -- 148and read/write -- :class:`~astropy.io.registry.UnifiedIORegistry`. 149 150 >>> from astropy.io.registry import UnifiedIORegistry 151 >>> example_reg = UnifiedIORegistry() 152 >>> print([m for m in dir(example_reg) if not m.startswith("_")]) 153 ['available_registries', 'delay_doc_updates', 'get_formats', 'get_reader', 154 'get_writer', 'identify_format', 'read', 'register_identifier', 155 'register_reader', 'register_writer', 'unregister_identifier', 156 'unregister_reader', 'unregister_writer', 'write'] 157 158For backward compatibility all the methods on this registry have corresponding 159module-level functions, which work with the default global read/write registry. 160These functions were used in the previous examples. This new registry is empty. 161 162 >>> example_reg.get_formats() 163 <Table length=0> 164 Data class Format Read Write Auto-identify 165 float64 float64 float64 float64 float64 166 ---------- ------- ------- ------- ------------- 167 168We can register read / write / identify methods with this registry object: 169 170 >>> example_reg.register_reader('my-table-format', Table, my_table_reader) 171 >>> example_reg.get_formats() 172 <Table length=1> 173 Data class Format Read Write Auto-identify 174 str5 str15 str3 str2 str2 175 ---------- --------------- ---- ----- ------------- 176 Table my-table-format Yes No No 177 178 179What is the use of a custom registries? 180 181 1. To make read-only or write-only registries. 182 2. To allow for different readers for the same format. 183 3. To allow for an object to have different *kinds* of readers and writers. 184 E.g. |Cosmology| which supports both file I/O and object conversion. 185 186 187Reference/API 188============= 189 190.. automodapi:: astropy.io.registry 191