1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
|
# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
#
# MDAnalysis --- http://www.MDAnalysis.org
# Copyright (c) 2006-2015 Naveen Michaud-Agrawal, Elizabeth J. Denning, Oliver Beckstein
# and contributors (see AUTHORS for the full list)
#
# Released under the Lesser GNU Public Licence, v2.1 or any higher version
#
# Please cite your use of MDAnalysis in published work:
# N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and O. Beckstein.
# MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations.
# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787
#
"""Functions for fetching Readers
These functions officially live in in topology/core (parsers) and
coordinates/core (all others). They are declared here to avoid
circular imports.
"""
import copy
import inspect
from .. import (
_READERS,
_READER_HINTS,
_PARSERS,
_PARSER_HINTS,
_MULTIFRAME_WRITERS,
_SINGLEFRAME_WRITERS,
_CONVERTERS,
)
from ..lib import util
def get_reader_for(filename, format=None):
"""Return the appropriate trajectory reader class for `filename`.
Parameters
----------
filename
filename of the input trajectory or coordinate file. Also can
handle a few special cases, see notes below.
format : str or :class:`Reader` (optional)
Define the desired format. Can be a string to request a given
Reader.
If a class is passed, it will be assumed that this is
a Reader and will be returned.
Returns
-------
:class:`Reader`
A Reader object
Raises
------
ValueError
If no appropriate Reader is found
Notes
-----
There are a number of special cases that can be handled:
- If `filename` is a numpy array,
:class:`~MDAnalysis.coordinates.memory.MemoryReader` is returned.
- If `filename` is an MMTF object,
:class:`~MDAnalysis.coordinates.MMTF.MMTFReader` is returned.
- If `filename` is a ParmEd Structure,
:class:`~MDAnalysis.coordinates.ParmEd.ParmEdReader` is returned.
- If `filename` is an iterable of filenames,
:class:`~MDAnalysis.coordinates.chain.ChainReader` is returned.
Automatic detection is disabled when an explicit `format` is provided,
unless a list of filenames is given, in which case
:class:`~MDAnalysis.coordinates.chain.ChainReader` is returned and `format`
passed to the :class:`~MDAnalysis.coordinates.chain.ChainReader`.
.. versionchanged:: 1.0.0
Added format_hint functionalityx
"""
# check if format is actually a Reader
if inspect.isclass(format):
return format
# ChainReader gets returned even if format is specified
if _READER_HINTS["CHAIN"](filename):
format = "CHAIN"
# Only guess if format is not specified
if format is None:
for fmt_name, test in _READER_HINTS.items():
if test(filename):
format = fmt_name
break
else: # hits else if for loop completes
# else let the guessing begin!
format = util.guess_format(filename)
format = format.upper()
try:
return _READERS[format]
except KeyError:
errmsg = (
"Unknown coordinate trajectory format '{0}' for '{1}'. The FORMATs \n"
" {2}\n"
" are implemented in MDAnalysis.\n"
" See https://docs.mdanalysis.org/documentation_pages/coordinates/init.html#id1\n"
" Use the format keyword to explicitly set the format: 'Universe(...,format=FORMAT)'\n"
" For missing formats, raise an issue at "
"https://github.com/MDAnalysis/mdanalysis/issues".format(
format, filename, _READERS.keys()
)
)
raise ValueError(errmsg) from None
def get_writer_for(filename, format=None, multiframe=None):
"""Return an appropriate trajectory or frame writer class for `filename`.
The format is determined by the `format` argument or the extension of
`filename`. If `format` is provided, it takes precedence over the
extension of `filename`.
Parameters
----------
filename : str or ``None``
If no *format* is supplied, then the filename for the trajectory is
examined for its extension and the Writer is chosen accordingly.
If ``None`` is provided, then
:class:`~MDAnalysis.coordinates.null.NullWriter` is selected (and
all output is discarded silently).
format : str (optional)
Explicitly set a format.
multiframe : bool (optional)
``True``: write multiple frames to the trajectory; ``False``: only
write a single coordinate frame; ``None``: first try trajectory (multi
frame writers), then the single frame ones. Default is ``None``.
Returns
-------
:class:`Writer`
A Writer object
Raises
------
ValueError:
The format could not be deduced from `filename` or an unexpected value
was provided for the `multiframe` argument.
TypeError:
No writer was found for the required format or the required `filename`
argument was omitted.
.. versionchanged:: 0.7.6
Added `multiframe` keyword; the default ``None`` reflects the previous
behaviour.
.. versionchanged:: 0.14.0
Removed the default value for the `format` argument. Now, the value
provided with the `format` parameter takes precedence over the extension
of `filename`. A :exc:`ValueError` is raised if the format cannot be
deduced from `filename`.
.. versionchanged:: 0.16.0
The `filename` argument has been made mandatory.
"""
if filename is None:
format = "NULL"
elif format is None:
try:
root, ext = util.get_ext(filename)
except (TypeError, AttributeError):
# An AttributeError is raised if filename cannot
# be manipulated as a string.
# A TypeError is raised in py3.6
# "TypeError: expected str, bytes or os.PathLike object"
errmsg = f'File format could not be guessed from "{filename}"'
raise ValueError(errmsg) from None
else:
format = util.check_compressed_format(root, ext)
if format == "":
raise ValueError(
(
"File format could not be guessed from {}, "
"resulting in empty string - "
"only None or valid formats are supported."
).format(filename)
)
format = format.upper()
if multiframe is None:
# Multiframe takes priority, else use singleframe
options = copy.copy(
_SINGLEFRAME_WRITERS
) # do copy to avoid changing in place
options.update(
_MULTIFRAME_WRITERS
) # update overwrites existing entries
errmsg = "No trajectory or frame writer for format '{0}'"
elif multiframe is True:
options = _MULTIFRAME_WRITERS
errmsg = "No trajectory writer for format '{0}'"
elif multiframe is False:
options = _SINGLEFRAME_WRITERS
errmsg = "No single frame writer for format '{0}'"
else:
raise ValueError(
"Unknown value '{0}' for multiframe,"
" only True, False, None allowed"
"".format(multiframe)
)
try:
return options[format]
except KeyError:
raise TypeError(errmsg.format(format)) from None
def get_parser_for(filename, format=None):
"""Return the appropriate topology parser for `filename`.
Automatic detection is disabled when an explicit `format` is
provided.
Parameters
----------
filename : str or mmtf.MMTFDecoder
name of the topology file; if this is an instance of
:class:`mmtf.MMTFDecoder` then directly use the MMTF format.
format : str
description of the file format
Raises
------
ValueError
If no appropriate parser could be found.
.. versionchanged:: 1.0.0
Added format_hint functionality
"""
if inspect.isclass(format):
return format
# Only guess if format is not provided
if format is None:
for fmt_name, test in _PARSER_HINTS.items():
if test(filename):
format = fmt_name
break
else:
format = util.guess_format(filename)
format = format.upper()
try:
return _PARSERS[format]
except KeyError:
try:
rdr = get_reader_for(filename)
except ValueError:
errmsg = (
"'{0}' isn't a valid topology format, nor a coordinate format\n"
" from which a topology can be minimally inferred.\n"
" You can use 'Universe(topology, ..., topology_format=FORMAT)'\n"
" to explicitly specify the format and\n"
" override automatic detection. Known FORMATs are:\n"
" {1}\n"
" See https://docs.mdanalysis.org/documentation_pages/topology/init.html#supported-topology-formats\n"
" For missing formats, raise an issue at \n"
" https://github.com/MDAnalysis/mdanalysis/issues".format(
format, _PARSERS.keys()
)
)
raise ValueError(errmsg) from None
else:
return _PARSERS["MINIMAL"]
def get_converter_for(format):
"""Return the appropriate topology converter for ``format``.
Parameters
----------
format : str
description of the file format
Raises
------
TypeError
If no appropriate parser could be found.
.. versionadded:: 1.0.0
"""
try:
writer = _CONVERTERS[format]
except KeyError:
errmsg = f"No converter found for {format} format"
raise TypeError(errmsg) from None
return writer
|