From 649cf8922956ed0e02ae6655c2dfcc9cb9833217 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 15 Jun 2017 16:26:13 -0700 Subject: [PATCH 01/15] add deprecation warnings for API changes - closes #1383 - included deprecations: - #1373 Timeseries (targeted for 0.17) Note that the deprecation for core.Timeseries will always show up; this is deliberate so that users WILL see it as it will be gone in the next release! - #1377 Quick selectors (target 1.0) - #782 flags (target 1.0) - updated CHANGELOG --- package/CHANGELOG | 5 +- package/MDAnalysis/coordinates/memory.py | 2 + package/MDAnalysis/core/Timeseries.py | 163 ++++++++++++++---- package/MDAnalysis/core/__init__.py | 30 +++- package/MDAnalysis/core/groups.py | 76 +++++--- package/MDAnalysis/core/universe.py | 11 +- .../source/documentation_pages/selections.rst | 11 ++ 7 files changed, 231 insertions(+), 67 deletions(-) diff --git a/package/CHANGELOG b/package/CHANGELOG index 66827ab51e0..54b3e8b1ca6 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -15,7 +15,7 @@ The rules for this file: ------------------------------------------------------------------------------ -mm/dd/17 richardjgowers, rathann, jbarnoud +mm/dd/17 richardjgowers, rathann, jbarnoud, orbeckst * 0.16.2 @@ -27,6 +27,9 @@ Fixes * Groups are hashable on python 3 (Issue #1397) Changes + * added deprecation warnings for API changes (removal of Timeseries and + DCD correl functionality, removal of instant selectors, and removal of + the core.flag registry) (Issue #1383) 06/03/17 utkbansal, kain88-de, xiki-tempula, kaplajon, wouterboomsma, diff --git a/package/MDAnalysis/coordinates/memory.py b/package/MDAnalysis/coordinates/memory.py index 7a3bccab5ee..15a29b7a700 100644 --- a/package/MDAnalysis/coordinates/memory.py +++ b/package/MDAnalysis/coordinates/memory.py @@ -106,6 +106,8 @@ universe2 = mda.Universe(PSF, coordinates, format=MemoryReader, order='afc') +.. _create-in-memory-trajectory-with-AnalysisFromFunction: + .. rubric:: Creating an in-memory trajectory with :func:`~MDAnalysis.analysis.base.AnalysisFromFunction` diff --git a/package/MDAnalysis/core/Timeseries.py b/package/MDAnalysis/core/Timeseries.py index f6c191667d6..dfb0d503e66 100644 --- a/package/MDAnalysis/core/Timeseries.py +++ b/package/MDAnalysis/core/Timeseries.py @@ -21,10 +21,19 @@ # -""" -Compute observable timeseries from trajectories --- :mod:`MDAnalysis.core.Timeseries` +"""Compute observable timeseries from trajectories --- :mod:`MDAnalysis.core.Timeseries` ======================================================================================= +.. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in the + 0.17 release. See issue `#1372 + `_ for more details. + To extract coordinates efficiently one can use + :class:`MDAnalysis.analysis.base.AnalysisFromFunction` as shown in + :ref:`Creating an in-memory trajectory with AnalysisFromFunction + `. + + The collection of timeseries (such as :class:`Atom`, :class:`Bond`, :class:`Dihedral`...) can be computed from a trajectory in one go, foregoing the need to iterate through the trajectory frame by frame in python. Inspired @@ -64,6 +73,14 @@ from . import groups +with warnings.catch_warnings(): + warnings.simplefilter('always', DeprecationWarning) + warnings.warn(('The Timeseries module and TimeseriesCollection will be ' + 'removed in the 0.17 release. See issue #1372 ' + 'https://github.com/MDAnalysis/mdanalysis/issues/1373'), + DeprecationWarning) + + class TimeseriesCollection(object): '''A collection of timeseries objects. @@ -81,6 +98,14 @@ class TimeseriesCollection(object): collection.clear() - clear the collection collection[i] - access the i'th timeseries len(collection) - return the number of Timeseries added to the collection + + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self): @@ -186,7 +211,14 @@ def _getAuxData(self): class Timeseries(object): - '''Base timeseries class - define subclasses for specific timeseries computations + '''Base timeseries class - define subclasses for specific timeseries computations. + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, code, atoms, dsize): @@ -253,6 +285,13 @@ class Atom(Timeseries): can be a single :class:`~MDAnalysis.core.groups.Atom` object, a list of :class:`~MDAnalysis.core.groups.Atom` objects, or an :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, code, atoms): @@ -281,8 +320,15 @@ class Bond(Timeseries): t = Bond(atoms) - *atoms* must contain 2 :class:`~MDAnalysis.core.groups.Atom` instances, either as a list or an - :class:`~MDAnalysis.core.groups.AtomGroup` + *atoms* must contain 2 :class:`~MDAnalysis.core.groups.Atom` instances, either as a list or an + :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, atoms): @@ -296,8 +342,15 @@ class Angle(Timeseries): t = Angle(atoms) - atoms must contain 3 :class:`~MDAnalysis.core.groups.Atom` instances, either as a list or an - :class:`~MDAnalysis.core.groups.AtomGroup` + atoms must contain 3 :class:`~MDAnalysis.core.groups.Atom` instances, + either as a list or an :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, atoms): @@ -311,8 +364,15 @@ class Dihedral(Timeseries): t = Dihedral(atoms) - atoms must contain 4 :class:`~MDAnalysis.core.groups.Atom` objects, either as a list or an - :class:`~MDAnalysis.core.groups.AtomGroup` + atoms must contain 4 :class:`~MDAnalysis.core.groups.Atom` objects, either + as a list or an :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, atoms): @@ -326,9 +386,16 @@ class Distance(Timeseries): t = Distance(code, atoms) - code is one of 'd' (distance vector), or 'r' (scalar distance) - atoms must contain 2 :class:`~MDAnalysis.core.groups.Atom` objects, either as a list or an - :class:`~MDAnalysis.core.groups.AtomGroup` + code is one of 'd' (distance vector), or 'r' (scalar distance) atoms must + contain 2 :class:`~MDAnalysis.core.groups.Atom` objects, either as a list + or an :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, code, atoms): @@ -348,8 +415,16 @@ class CenterOfGeometry(Timeseries): t = CenterOfGeometry(atoms) - *atoms* can be a list of :class:`~MDAnalysis.core.groups.Atom` - objects, or a :class:`~MDAnalysis.core.groups.AtomGroup` + *atoms* can be a list of :class:`~MDAnalysis.core.groups.Atom` objects, or + a :class:`~MDAnalysis.core.groups.AtomGroup` + + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, atoms): @@ -364,8 +439,15 @@ class CenterOfMass(Timeseries): t = CenterOfMass(atoms) - *atoms* can be a list of :class:`~MDAnalysis.core.groups.Atom` - objects or a :class:`~MDAnalysis.core.groups.AtomGroup` + *atoms* can be a list of :class:`~MDAnalysis.core.groups.Atom` objects or a + :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, atoms): @@ -380,38 +462,43 @@ class WaterDipole(Timeseries): d = WaterDipole(atoms) - *atoms* must contain 3 :class:`~MDAnalysis.core.groups.Atom` - objects, either as a list or an - :class:`~MDAnalysis.core.groups.AtomGroup`; the first one *must* be - the oxygen, the other two are the hydrogens. + *atoms* must contain 3 :class:`~MDAnalysis.core.groups.Atom` objects, + either as a list or an :class:`~MDAnalysis.core.groups.AtomGroup`; the + first one *must* be the oxygen, the other two are the hydrogens. + + The vector ``d``, multiplied by the partial charge on the oxygen atom + (e.g. *q* = -0.0.834 for TIP3P water), gives the actual dipole moment. - The vector ``d``, multiplied by the partial charge on the oxygen atom - (e.g. *q* = -0.0.834 for TIP3P water), gives the actual dipole moment. + The vector is calculated from the positions of the oxygen atom + (:math:`\mathbf{x}_{\text{O}}`) and the two hydrogen atoms + (:math:`\mathbf{x}_{\text{H}_1}`, :math:`\mathbf{x}_{\text{H}_2}`) as - The vector is calculated from the positions of the oxygen atom - (:math:`\mathbf{x}_{\text{O}}`) and the two hydrogen atoms - (:math:`\mathbf{x}_{\text{H}_1}`, :math:`\mathbf{x}_{\text{H}_2}`) as + .. math:: - .. math:: + \mathbf{d} = \mathbf{x}_{\text{O}} - \frac{1}{2}(\mathbf{x}_{\text{H}_1} + \mathbf{x}_{\text{H}_2}) - \mathbf{d} = \mathbf{x}_{\text{O}} - \frac{1}{2}(\mathbf{x}_{\text{H}_1} + \mathbf{x}_{\text{H}_2}) + and the dipole moment vector is - and the dipole moment vector is + .. math:: - .. math:: + \boldsymbol{\mu} = q_{\text{O}} \mathbf{d} - \boldsymbol{\mu} = q_{\text{O}} \mathbf{d} + .. Note:: - .. Note:: + This will only work for water models that have half of the oxygen charge + on each hydrogen. The vector :math:`\mathbf{d}` has the opposite + direction of the dipole moment; multiplying with the oxygen charge + (:math:`q_{\text{O}}<0`) will flip the direction and produce the correct + orientation. - This will only work for water models that have half of the oxygen - charge on each hydrogen. The vector :math:`\mathbf{d}` has the - opposite direction of the dipole moment; multiplying with the oxygen - charge (:math:`q_{\text{O}}<0`) will flip the direction and produce - the correct orientation. + There are no sanity checks; *if the first atom in a water + molecule is not oxygen then results will be wrong.* - There are no sanity checks; *if the first atom in a water - molecule is not oxygen then results will be wrong.* + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. ''' diff --git a/package/MDAnalysis/core/__init__.py b/package/MDAnalysis/core/__init__.py index 4d2c0bc71ad..2ba6f8d778f 100644 --- a/package/MDAnalysis/core/__init__.py +++ b/package/MDAnalysis/core/__init__.py @@ -30,7 +30,8 @@ :class:`~MDAnalysis.core.groups.AtomGroup` and return another :class:`~MDAnalysis.core.groups.AtomGroup`. -:mod:`~MDAnalysis.Timeseries` are a convenient way to analyse trajectories. +:mod:`~MDAnalysis.core.Timeseries` are a convenient way to analyse +trajectories. To get started, load the Universe:: @@ -56,6 +57,12 @@ Flags ----- +.. deprecated:: 0.16.2 + The flags registry will be removed in release 1.0. + Use keyword arguments for functions to obtain the desired behavior. + See issue `#782 `_ + for more details. + (This is an advanced topic and can probably be skipped by most people.) There are a number flags that influence how MDAnalysis behaves. They are accessible @@ -92,8 +99,9 @@ __all__ = ['AtomGroup', 'Selection', 'Timeseries'] -# set up flags for core routines (more convoluted than strictly necessary but should -# be clean to add more flags if needed) +# set up flags for core routines (more convoluted than strictly necessary but +# should be clean to add more flags if needed) + class Flags(dict): """Global registry of flags. Acts like a dict for item access. @@ -109,6 +117,13 @@ class Flags(dict): New flags are added with the :meth:`Flags.register` method which takes a new :class:`Flag` instance as an argument. + + .. deprecated:: 0.16.2 + The flags registry will be removed in release 1.0. + Use keyword arguments for functions to obtain the desired behavior. + See issue `#782 `_ + for more details. + """ def __init__(self, *args): @@ -180,7 +195,14 @@ def __getitem__(self, key): class Flag(object): - """A Flag, essentially a variable that knows its default and legal values.""" + """A Flag, essentially a variable that knows its default and legal values. + + .. deprecated:: 0.16.2 + The flags registry will be removed in release 1.0. + Use keyword arguments for functions to obtain the desired behavior. + See issue `#782 `_ + for more details. + """ def __init__(self, name, default, mapping=None, doc=None): """Create a new flag which will be registered with Flags. diff --git a/package/MDAnalysis/core/groups.py b/package/MDAnalysis/core/groups.py index f9c2da1ebee..4d50f827eb0 100644 --- a/package/MDAnalysis/core/groups.py +++ b/package/MDAnalysis/core/groups.py @@ -342,7 +342,7 @@ def wrapped(self, other): class GroupBase(_MutableBase): """Base class from which a Universe's Group class is built. - Instances of :class:`GroupBase` provide the following operations that + Instances of :class:`GroupBase` provide the following operations that conserve element repetitions and order: +-------------------------------+------------+----------------------------+ @@ -989,7 +989,7 @@ def wrap(self, compound="atoms", center="com", box=None): "Please use one of 'group' 'residues' 'segments'" "or 'fragments'".format(compound)) -# TODO: ADD TRY-EXCEPT FOR MASSES PRESENCE + # TODO: ADD TRY-EXCEPT FOR MASSES PRESENCE if center.lower() in ('com', 'centerofmass'): centers = np.vstack([o.atoms.center_of_mass() for o in objects]) elif center.lower() in ('cog', 'centroid', 'centerofgeometry'): @@ -1352,9 +1352,10 @@ def is_strict_superset(self, other): class AtomGroup(GroupBase): """A group of atoms. - An AtomGroup is an ordered collection of atoms. Typically, an AtomGroup is - generated from a selection, or by indexing/slcing the AtomGroup of all - atoms in the Universe at :attr:`MDAnalysis.core.universe.Universe.atoms`. + An :class:`AtomGroup` is an ordered collection of atoms. Typically, an + :class:`AtomGroup` is generated from a selection, or by indexing/slicing + the :class:`AtomGroup` of all atoms in the :class:`Universe` at + :attr:`MDAnalysis.core.universe.Universe.atoms`. An AtomGroup can be indexed and sliced like a list:: @@ -1385,23 +1386,6 @@ class AtomGroup(GroupBase): atoms is crucial (for instance, in order to define angles or dihedrals). - Atoms can also be accessed in a Pythonic fashion by using the atom name as - an attribute. For instance, :: - - ag.CA - - will provide a :class:`AtomGroup` of all CA atoms in the - group. These *instant selector* attributes are auto-generated for - each atom name encountered in the group. - - .. note:: - - The name-attribute instant selector access to atoms is mainly - meant for quick interactive work. Thus it either returns a - single :class:`Atom` if there is only one matching atom, *or* a - new :class:`AtomGroup` for multiple matches. This makes it - difficult to use the feature consistently in scripts. - AtomGroups can be compared and combined using group operators. For instance, AtomGroups can be concatenated using `+` or :meth:`concatenate`:: @@ -1487,12 +1471,46 @@ class AtomGroup(GroupBase): AtomGroup instances are always bound to a :class:`MDAnalysis.core.universe.Universe`. They cannot exist in isolation. + + .. rubric:: Deprecated functionality + + *Instant selectors* will be removed in the 1.0 release. See issue `#1377 + `_ for more details. + + Atoms can also be accessed in a Pythonic fashion by using the atom name as + an attribute. For instance, :: + + ag.CA + + will provide a :class:`AtomGroup` of all CA atoms in the + group. These *instant selector* attributes are auto-generated for + each atom name encountered in the group. + + .. note:: + + The name-attribute instant selector access to atoms is mainly + meant for quick interactive work. Thus it either returns a + single :class:`Atom` if there is only one matching atom, *or* a + new :class:`AtomGroup` for multiple matches. This makes it + difficult to use the feature consistently in scripts. + + See Also -------- :class:`MDAnalysis.core.universe.Universe` + + .. deprecated:: 0.16.2 + *Instant selectors* of AtomGroup will be removed in the 1.0 release. + See issue `#1377 + `_ for + more details. + """ def __getitem__(self, item): + # DEPRECATED in 0.16.2 + # REMOVE in 1.0 + # # u.atoms['HT1'] access, otherwise default if isinstance(item, string_types): try: @@ -1502,6 +1520,9 @@ def __getitem__(self, item): return super(AtomGroup, self).__getitem__(item) def __getattr__(self, attr): + # DEPRECATED in 0.16.2 + # REMOVE in 1.0 + # # is this a known attribute failure? if attr in ('fragments',): # TODO: Generalise this to cover many attributes # eg: @@ -2460,7 +2481,7 @@ def ix(self): @property def ix_array(self): """Unique index of this component as an array. - + This method gives a consistent API between components and groups. See Also @@ -2637,6 +2658,12 @@ class Segment(ComponentBase): ComponentBase, so this class only includes ad-hoc methods specific to Segments. + .. deprecated:: 0.16.2 + *Instant selectors* of Segments will be removed in the 1.0 release. + See issue `#1377 + `_ for + more details. + """ def __repr__(self): me = '`_ for more details. + + If the loaded topology provided segids, then these are made accessible as attributes of the Universe. If the segid starts with a number such as '4AKE', the letter 's' will be prepended to the segid. @@ -313,6 +318,10 @@ def _generate_from_topology(self): # Update Universe namespace with segids # Many segments can have same segid, so group together first + # + # DEPRECATED in 0.16.2 + # REMOVE in 1.0 + # See https://github.com/MDAnalysis/mdanalysis/issues/1377 try: # returns dict of segid:segment segids = self.segments.groupby('segids') @@ -492,7 +501,7 @@ def transfer_to_memory(self, start=None, stop=None, step=None, # object, to provide fast access and allow coordinates # to be manipulated if step is None: - step = 1 + step = 1 self.trajectory = MemoryReader( coordinates, dimensions=self.trajectory.ts.dimensions, diff --git a/package/doc/sphinx/source/documentation_pages/selections.rst b/package/doc/sphinx/source/documentation_pages/selections.rst index 77851225050..c4773df5eb1 100644 --- a/package/doc/sphinx/source/documentation_pages/selections.rst +++ b/package/doc/sphinx/source/documentation_pages/selections.rst @@ -285,6 +285,17 @@ across frames:: Instant selectors ================= +.. deprecated:: 0.16.2 + + *Instant selectors* will be removed in the 1.0 release in order to + streamline the MDAnalysis user interface. They do not seem to be + widely used anymore, can produce cryptic error messages, and are + not considered "Pythonic" (and therefore not very intuitive for new + users). See issue `#1377 + `_ for more + details. + + For interactive work it becomes rather tedious to type common selection strings repeatedly. MDAnalysis automatically generates a number of *instant selectors* as attributes of the :class:`~MDAnalysis.core.universe.Universe` and number of From cecba37c2e23bdb1e986d1840b0c164af00123de Mon Sep 17 00:00:00 2001 From: Richard Gowers Date: Tue, 13 Jun 2017 09:16:34 +0100 Subject: [PATCH 02/15] deprecated core.Timeseries module --- package/CHANGELOG | 10 ++++------ package/MDAnalysis/core/Timeseries.py | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/package/CHANGELOG b/package/CHANGELOG index 54b3e8b1ca6..f1aba435b37 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -19,18 +19,16 @@ mm/dd/17 richardjgowers, rathann, jbarnoud, orbeckst * 0.16.2 -Enhancements +Deprecations + * deprecated core.Timeseries module for 0.17.0 (Issue #1383) + * deprecated instant selectors for 1.0 (Issue #1377) + * deprecated the core.flag registry for 1.0 (Issue #782) Fixes * fixed GROWriter truncating long resids from the wrong end (Issue #1395) * Fixed dtype of numpy arrays to accomodate 32 bit architectures (Issue #1362) * Groups are hashable on python 3 (Issue #1397) -Changes - * added deprecation warnings for API changes (removal of Timeseries and - DCD correl functionality, removal of instant selectors, and removal of - the core.flag registry) (Issue #1383) - 06/03/17 utkbansal, kain88-de, xiki-tempula, kaplajon, wouterboomsma, richardjgowers, Shtkddud123, QuantumEntangledAndy, orbeckst, diff --git a/package/MDAnalysis/core/Timeseries.py b/package/MDAnalysis/core/Timeseries.py index dfb0d503e66..92dd0a11655 100644 --- a/package/MDAnalysis/core/Timeseries.py +++ b/package/MDAnalysis/core/Timeseries.py @@ -70,8 +70,9 @@ from __future__ import division, absolute_import import warnings -from . import groups +from numpy.lib.utils import deprecate +from . import groups with warnings.catch_warnings(): warnings.simplefilter('always', DeprecationWarning) @@ -80,7 +81,7 @@ 'https://github.com/MDAnalysis/mdanalysis/issues/1373'), DeprecationWarning) - +@deprecate(message="This class will be removed in 0.17") class TimeseriesCollection(object): '''A collection of timeseries objects. @@ -210,6 +211,7 @@ def _getAuxData(self): return auxData +@deprecate(message="This class will be removed in 0.17") class Timeseries(object): '''Base timeseries class - define subclasses for specific timeseries computations. @@ -272,6 +274,7 @@ def getAuxData(self): return [0.] * self.n_atoms +@deprecate(message="This class will be removed in 0.17") class Atom(Timeseries): '''Create a timeseries that returns coordinate data for an atom or group of atoms :: @@ -315,6 +318,7 @@ def getAtomCounts(self): return [1, ] * self.n_atoms +@deprecate(message="This class will be removed in 0.17") class Bond(Timeseries): '''Create a timeseries that returns a timeseries for a bond @@ -337,6 +341,7 @@ def __init__(self, atoms): Timeseries.__init__(self, 'r', atoms, 1) +@deprecate(message="This class will be removed in 0.17") class Angle(Timeseries): '''Create a timeseries that returns a timeseries for an angle @@ -359,6 +364,7 @@ def __init__(self, atoms): Timeseries.__init__(self, 'a', atoms, 1) +@deprecate(message="This class will be removed in 0.17") class Dihedral(Timeseries): '''Create a timeseries that returns a timeseries for a dihedral angle @@ -381,6 +387,7 @@ def __init__(self, atoms): Timeseries.__init__(self, 'h', atoms, 1) +@deprecate(message="This class will be removed in 0.17") class Distance(Timeseries): '''Create a timeseries that returns distances between 2 atoms @@ -410,6 +417,7 @@ def __init__(self, code, atoms): Timeseries.__init__(self, code, atoms, size) +@deprecate(message="This class will be removed in 0.17") class CenterOfGeometry(Timeseries): '''Create a timeseries that returns the center of geometry of a group of atoms @@ -434,6 +442,7 @@ def getAuxData(self): return [1.] * self.n_atoms +@deprecate(message="This class will be removed in 0.17") class CenterOfMass(Timeseries): '''Create a timeseries that returns the center of mass of a group of atoms @@ -457,6 +466,7 @@ def getAuxData(self): return [a.mass for a in self.atoms] +@deprecate(message="This class will be removed in 0.17") class WaterDipole(Timeseries): r'''Create a Timeseries that returns a timeseries for the bisector vector of a 3-site water From 34fc6fc2e49f6c4296d081184c2ddf9c0c17c3b1 Mon Sep 17 00:00:00 2001 From: Richard Gowers Date: Tue, 13 Jun 2017 09:17:54 +0100 Subject: [PATCH 03/15] Deprecated DCDReader.correl --- package/MDAnalysis/coordinates/DCD.py | 2 ++ package/MDAnalysis/coordinates/__init__.py | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/package/MDAnalysis/coordinates/DCD.py b/package/MDAnalysis/coordinates/DCD.py index 0f5a0eb9693..0cdd4cf2709 100644 --- a/package/MDAnalysis/coordinates/DCD.py +++ b/package/MDAnalysis/coordinates/DCD.py @@ -81,6 +81,7 @@ import os import errno import numpy as np +from numpy.lib.utils import deprecate import struct import types import warnings @@ -578,6 +579,7 @@ def timeseries(self, asel=None, start=None, stop=None, step=None, skip=None, # XXX needs to be implemented return self._read_timeseries(atom_numbers, start, stop, step, format) + @deprecate(message="This method will be removed in 0.17") def correl(self, timeseries, start=None, stop=None, step=None, skip=None): """Populate a :class:`~MDAnalysis.core.Timeseries.TimeseriesCollection` object with time series computed from the trajectory. diff --git a/package/MDAnalysis/coordinates/__init__.py b/package/MDAnalysis/coordinates/__init__.py index 45c13eada4d..fc9cfc82c81 100644 --- a/package/MDAnalysis/coordinates/__init__.py +++ b/package/MDAnalysis/coordinates/__init__.py @@ -538,10 +538,6 @@ class can choose an appropriate reader automatically. ``timeseries(atomGroup, [start[,stop[,skip[,format]]]])`` returns a subset of coordinate data - ``correl(timeseriesCollection[,start[,stop[,skip]]])`` - populate a :class:`MDAnalysis.core.Timeseries.TimeseriesCollection` object - with observable timeseries computed from the trajectory - Attributes .......... From 219870ad4b985de3f62607bd73e3f24a576188b1 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 15 Jun 2017 23:05:13 -0700 Subject: [PATCH 04/15] mention optional/full dependencies in docs - improved installation instructions (minimal/full install for pip, always full install for conda; also added entry on installation of tests) - analysis landing page: better explained optional packages, removed scipy as example and replaced with sklearn and HOLE. --- .../documentation_pages/analysis_modules.rst | 46 +++++++++++-------- package/doc/sphinx/source/index.rst | 42 +++++++++++++++-- 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/package/doc/sphinx/source/documentation_pages/analysis_modules.rst b/package/doc/sphinx/source/documentation_pages/analysis_modules.rst index 7da649d8683..edd239805de 100644 --- a/package/doc/sphinx/source/documentation_pages/analysis_modules.rst +++ b/package/doc/sphinx/source/documentation_pages/analysis_modules.rst @@ -4,29 +4,39 @@ Analysis modules **************** -The :mod:`MDAnalysis.analysis` module contains code to carry out -specific analysis functionality. It is based on the core functionality -(i.e. trajectory I/O, selections etc). The analysis modules can be -used as examples for how to use MDAnalysis but also as working code -for research projects; typically all contributed code has been used by -the authors in their own work. +The :mod:`MDAnalysis.analysis` module contains code to carry out specific +analysis functionality. It is based on the core functionality (i.e. trajectory +I/O, selections etc). The analysis modules can be used as examples for how to +use MDAnalysis but also as working code for research projects; typically all +contributed code has been used by the authors in their own work. -Please see the individual module documentation for additional -references and citation information. +Please see the individual module documentation for additional references and +citation information. -These modules are not imported by default; in order to use them one -has to import them from :mod:`MDAnalysis.analysis`, for instance :: +These modules are not imported by default; in order to use them one has to +import them from :mod:`MDAnalysis.analysis`, for instance :: import MDAnalysis.analysis.align -.. Note:: - - Some of the modules require additional Python packages such as :mod:`scipy` - from the SciPy_ package. These package are *not automatically installed* - (although one can add the ``[analysis]`` requirement to the :program:`pip` - command line to force their installation. - -.. _scipy: http://www.scipy.org/ +.. rubric:: Additional dependencies + +Some of the modules in :mod:`MDAnalysis.analysis` require additional Python +packages to enable full functionality. For example, +:mod:`MDAnalysis.analysis.encore` provides more options if `scikit-learn`_ is +installed. These package are *not automatically installed* with +:program:`pip`(although one can add the ``[analysis]`` requirement to the +:program:`pip` command line to force their installation). If you install +MDAnalysis with :program:`conda` (see :ref:`installation-instructions`) then a +*full set of dependencies* is automatically installed. + +Other modules require external programs. For instance, the +:mod:`MDAnalysis.analysis.hole` module requires an installation of the HOLE_ +suite of programs. You will need to install these external dependencies by +following their installation instructions before you can use the corresponding +MDAnalysis module. + +.. _scikit-learn: http://scikit-learn.org/ +.. _HOLE: http://www.smartsci.uk/hole/ Building blocks for Analysis diff --git a/package/doc/sphinx/source/index.rst b/package/doc/sphinx/source/index.rst index 20df327041f..666117666c4 100644 --- a/package/doc/sphinx/source/index.rst +++ b/package/doc/sphinx/source/index.rst @@ -57,36 +57,70 @@ members agree and adhere to --- please read it. http://groups.google.com/group/mdnalysis-discussion .. _`Code of Conduct`: http://www.mdanalysis.org/pages/conduct/ +.. _installation-instructions: Installing MDAnalysis ===================== -To `install the latest release`_ using `pip`_: +The easiest approach to `install the latest release`_ is to use a package that +can be installed either with pip_ or conda_. + +pip +--- + +Installation with `pip`_ and a *minimal set of dependencies*: .. code-block:: bash pip install --upgrade MDAnalysis -Alternatively, to install with conda_ do +To install with a *full set of dependencies* (which includes everything needed +for :mod:`MDAnalysis.analysis`), add the ``[analysis]`` tag: + +.. code-block:: bash + + pip install --upgrade MDAnalysis[analysis] + + +conda +----- + +First installation with conda_: .. code-block:: bash conda config --add channels conda-forge conda install mdanalysis -and to upgrade +which will automatically install a *full set of dependencies*. + +To upgrade later: .. code-block:: bash conda update mdanalysis +Tests +----- + +If you want to `run the tests`_ or use example files to follow some of the +examples in the documentation or the tutorials_, also install the +``MDAnalysisTests`` package: + +.. code-block:: bash + + pip install --upgrade MDAnalysisTests # with pip + conda install mdanalysistests # with conda + .. _install the latest release: http://www.mdanalysis.org/pages/installation_quick_start/ .. _pip: http://www.pip-installer.org/en/latest/index.html .. _conda: http://conda.pydata.org/docs/ - +.. _run the tests: http://wiki.mdanalysis.org/UnitTests +.. _tutorials: http://www.mdanalysis.org/pages/learning_MDAnalysis/ + Source Code =========== From 86682592cfce34134c9f8e2eeeb43f956bf5dbaa Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Fri, 16 Jun 2017 00:56:12 -0700 Subject: [PATCH 05/15] removed top-level imports of Timeseries - removed imports in core and MDAnalysis - replaced MDAnalysis.collection with a mock object that issues warnings and raises NotImplementedError - added a test for MDAnalysis.collection --- package/MDAnalysis/__init__.py | 38 +++++++++++++++++-- package/MDAnalysis/core/Timeseries.py | 2 +- package/MDAnalysis/core/__init__.py | 1 - .../MDAnalysisTests/utils/test_deprecated.py | 13 +++++++ 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/package/MDAnalysis/__init__.py b/package/MDAnalysis/__init__.py index d324854bf16..17ba62e4aaf 100644 --- a/package/MDAnalysis/__init__.py +++ b/package/MDAnalysis/__init__.py @@ -143,7 +143,7 @@ """ from __future__ import absolute_import -__all__ = ['Timeseries', 'Universe', 'as_Universe', 'Writer', 'collection', +__all__ = ['Universe', 'as_Universe', 'Writer', 'collection', 'fetch_mmtf'] import logging @@ -192,14 +192,46 @@ from . import units # Bring some often used objects into the current namespace -from .core import Timeseries from .core.universe import Universe, as_Universe, Merge from .coordinates.core import writer as Writer # After Universe import from .coordinates.MMTF import fetch_mmtf -collection = Timeseries.TimeseriesCollection() +# REMOVE in 0.17.0 +class _MockTimeseriesCollection(object): + """When accessed the first time, emit warning and raise NotImplementedError. + + This breaks existing code that relies on MDAnalysis.collection by + replacing :: + + collection = Timeseries.TimeseriesCollection() + + with:: + + collection = _MockTimeseriesCollection() + + """ + + def __getattr__(self, name): + self._warn_and_die() + + def __getitem__(self, index): + self._warn_and_die() + + def _warn_and_die(self): + import logging + msg = "collection = Timeseries.TimeseriesCollection() will be removed in 0.17.0\n" \ + "and MDAnalysis.collection has been disabled. If you want to use it, \n" \ + "instantiate a collection yourself:\n\n" \ + " from MDAnalysis.core.Timeseries import TimeseriesCollection\n" \ + " collection = TimeseriesCollection()\n\n" \ + "Note that release 0.16.2 is the LAST RELEASE with TimeseriesCollection." + logging.getLogger("MDAnalysis").warn(msg) + warnings.warn(msg, DeprecationWarning) + raise NotImplementedError("TimeseriesCollection will be REMOVED IN THE NEXT RELEASE 0.17.0") +collection = _MockTimeseriesCollection() +del _MockTimeseriesCollection from .migration.ten2eleven import ten2eleven diff --git a/package/MDAnalysis/core/Timeseries.py b/package/MDAnalysis/core/Timeseries.py index 92dd0a11655..f7e99248673 100644 --- a/package/MDAnalysis/core/Timeseries.py +++ b/package/MDAnalysis/core/Timeseries.py @@ -81,6 +81,7 @@ 'https://github.com/MDAnalysis/mdanalysis/issues/1373'), DeprecationWarning) + @deprecate(message="This class will be removed in 0.17") class TimeseriesCollection(object): '''A collection of timeseries objects. @@ -211,7 +212,6 @@ def _getAuxData(self): return auxData -@deprecate(message="This class will be removed in 0.17") class Timeseries(object): '''Base timeseries class - define subclasses for specific timeseries computations. diff --git a/package/MDAnalysis/core/__init__.py b/package/MDAnalysis/core/__init__.py index 2ba6f8d778f..ae5c3d73879 100644 --- a/package/MDAnalysis/core/__init__.py +++ b/package/MDAnalysis/core/__init__.py @@ -451,5 +451,4 @@ class flagsDocs(object): from . import groups from . import selection -from . import Timeseries from . import AtomGroup diff --git a/testsuite/MDAnalysisTests/utils/test_deprecated.py b/testsuite/MDAnalysisTests/utils/test_deprecated.py index 3a852a0f88f..3ce63e6abe7 100644 --- a/testsuite/MDAnalysisTests/utils/test_deprecated.py +++ b/testsuite/MDAnalysisTests/utils/test_deprecated.py @@ -25,6 +25,10 @@ # will be removed in 1.0) from __future__ import absolute_import +import warnings + +from numpy.testing import assert_equal, assert_raises + class TestImports(object): def test_core_units(self): try: @@ -75,3 +79,12 @@ def test_analysis_x3dna(self): except ImportError: raise AssertionError("MDAnalysis.analysis.x3dna not available") +def test_collections_NotImplementedError(): + import MDAnalysis + with assert_raises(NotImplementedError): + MDAnalysis.collection.clear() + + + + + From bba1d85a37cd2def8a789778bdc3a69f1582a3e4 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Fri, 16 Jun 2017 13:55:02 -0700 Subject: [PATCH 06/15] new MDAnalysisTests.core.util.assert_nowarns() function --- testsuite/MDAnalysisTests/__init__.py | 3 +- testsuite/MDAnalysisTests/util.py | 50 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/testsuite/MDAnalysisTests/__init__.py b/testsuite/MDAnalysisTests/__init__.py index 81f330c43f2..2397ed8598d 100644 --- a/testsuite/MDAnalysisTests/__init__.py +++ b/testsuite/MDAnalysisTests/__init__.py @@ -98,7 +98,7 @@ A number of plugins external to nose are automatically loaded. The `knownfailure` plugin provides the `@knownfailure()` decorator, which can be used to mark tests -that are expected to fail. If used with default arguments the parentheses can be +that are expected to fail. If used with default arguments the parentheses can be excluded. .. _NumPy: http://www.numpy.org/ @@ -148,6 +148,7 @@ module_not_found, parser_not_found, in_dir, + assert_nowarns, ) from MDAnalysisTests.core.util import make_Universe diff --git a/testsuite/MDAnalysisTests/util.py b/testsuite/MDAnalysisTests/util.py index 9454255eb92..1c7aea712be 100644 --- a/testsuite/MDAnalysisTests/util.py +++ b/testsuite/MDAnalysisTests/util.py @@ -40,6 +40,8 @@ import mock import os +from numpy.testing import assert_warns + def block_import(package): """Block import of a given package @@ -148,3 +150,51 @@ def in_dir(dirname): os.chdir(dirname) yield dirname os.chdir(old_path) + + +def assert_nowarns(warning_class, *args, **kwargs): + """Fail if the given callable throws the specified warning. + + A warning of class warning_class should NOT be thrown by the callable when + invoked with arguments args and keyword arguments kwargs. + If a different type of warning is thrown, it will not be caught. + + Parameters + ---------- + warning_class : class + The class defining the warning that `func` is expected to throw. + func : callable + The callable to test. + \*args : Arguments + Arguments passed to `func`. + \*\*kwargs : Kwargs + Keyword arguments passed to `func`. + + Returns + ------- + True + if no `AssertionError` is raised + + Note + ---- + numpy.testing.assert_warn returns the value returned by `func`; we would + need a second func evaluation so in order to avoid it, only True is + returned if no assertion is raised. + + SeeAlso + ------- + numpy.testing.assert_warn + + """ + func = args[0] + args = args[1:] + try: + value = assert_warns(DeprecationWarning, func, *args, **kwargs) + except AssertionError: + # a warning was NOT emitted: all good + return True + else: + # There was a warning even though we do not want to see one. + raise AssertionError + + From 916c7ab09c25a1f644dab828eb6b4f371d704a0b Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Fri, 16 Jun 2017 14:10:29 -0700 Subject: [PATCH 07/15] make instant selectors emit DeprecationWarning - instant selectors AtomGroup[''], AtomGroup., ResidueGroup., Segment., Segment.r, SegmentGroup. emit warnings - added tests - updated docs with detailed deprecation and alternatives Thanks to @richardjgowers for insights https://github.com/MDAnalysis/mdanalysis/pull/1403#issuecomment-309136416 --- package/MDAnalysis/core/__init__.py | 3 -- package/MDAnalysis/core/groups.py | 26 ++++++++---- package/MDAnalysis/core/topologyattrs.py | 40 +++++++++++++++++++ .../source/documentation_pages/selections.rst | 20 +++++++++- testsuite/MDAnalysisTests/core/test_groups.py | 38 +++++++++++++++++- 5 files changed, 115 insertions(+), 12 deletions(-) diff --git a/package/MDAnalysis/core/__init__.py b/package/MDAnalysis/core/__init__.py index ae5c3d73879..6c3f4a4fb10 100644 --- a/package/MDAnalysis/core/__init__.py +++ b/package/MDAnalysis/core/__init__.py @@ -30,9 +30,6 @@ :class:`~MDAnalysis.core.groups.AtomGroup` and return another :class:`~MDAnalysis.core.groups.AtomGroup`. -:mod:`~MDAnalysis.core.Timeseries` are a convenient way to analyse -trajectories. - To get started, load the Universe:: u = Universe(topology_file, trajectory_file) diff --git a/package/MDAnalysis/core/groups.py b/package/MDAnalysis/core/groups.py index 4d50f827eb0..46d46c10644 100644 --- a/package/MDAnalysis/core/groups.py +++ b/package/MDAnalysis/core/groups.py @@ -101,6 +101,8 @@ import os import warnings +from numpy.lib.utils import deprecate + import MDAnalysis from .. import _ANCHOR_UNIVERSES from ..lib import util @@ -1502,9 +1504,7 @@ class AtomGroup(GroupBase): .. deprecated:: 0.16.2 *Instant selectors* of AtomGroup will be removed in the 1.0 release. - See issue `#1377 - `_ for - more details. + See :ref:`Instant selectors`_ for details and alternatives. """ def __getitem__(self, item): @@ -2199,6 +2199,11 @@ class ResidueGroup(GroupBase): ResidueGroups can be compared and combined using group operators. See the list of these operators on :class:`GroupBase`. + + .. deprecated:: 0.16.2 + *Instant selectors* of Segments will be removed in the 1.0 release. + See :ref:`Instant selectors`_ for details and alternatives. + """ @property @@ -2313,6 +2318,11 @@ class SegmentGroup(GroupBase): SegmentGroups can be compared and combined using group operators. See the list of these operators on :class:`GroupBase`. + + .. deprecated:: 0.16.2 + *Instant selectors* of Segments will be removed in the 1.0 release. + See :ref:`Instant selectors`_ for details and alternatives. + """ @property @@ -2660,9 +2670,7 @@ class Segment(ComponentBase): .. deprecated:: 0.16.2 *Instant selectors* of Segments will be removed in the 1.0 release. - See issue `#1377 - `_ for - more details. + See :ref:`Instant selectors`_ for details and alternatives. """ def __repr__(self): @@ -2686,7 +2694,11 @@ def __getattr__(self, attr): # Segment.r1 access if attr.startswith('r') and attr[1:].isdigit(): resnum = int(attr[1:]) - return self.residues[resnum - 1] # convert to 0 based + rg = self.residues[resnum - 1] # convert to 0 based + warnings.warn("Instant selectors Segment.r will be removed in 1.0. " + "Use Segment.residues[N-1] instead.", + DeprecationWarning) + return rg # Resname accesss if hasattr(self.residues, 'resnames'): try: diff --git a/package/MDAnalysis/core/topologyattrs.py b/package/MDAnalysis/core/topologyattrs.py index 9976b9b5b88..fa6997c3adc 100644 --- a/package/MDAnalysis/core/topologyattrs.py +++ b/package/MDAnalysis/core/topologyattrs.py @@ -42,6 +42,8 @@ import numbers import numpy as np +from numpy.lib.utils import deprecate + from . import flags from ..lib.util import cached, convert_aa_code, iterable from ..lib import transformations, mdamath @@ -409,6 +411,9 @@ def getattr__(atomgroup, name): raise AttributeError("'{0}' object has no attribute '{1}'".format( atomgroup.__class__.__name__, name)) + @deprecate(message="Instant selector AtomGroup[''] or AtomGroup. " + "is deprecated and will be removed in 1.0. " + "Use AtomGroup.select_atoms('name ') instead.") def _get_named_atom(group, name): """Get all atoms with name *name* in the current AtomGroup. @@ -417,6 +422,14 @@ def _get_named_atom(group, name): no atoms are found, a :exc:`SelectionError` is raised. .. versionadded:: 0.9.2 + + .. deprecated:: 0.16.2 + *Instant selectors* will be removed in the 1.0 release. + Use ``AtomGroup.select_atoms('name ')`` instead. + See issue `#1377 + `_ for + more details. + """ # There can be more than one atom with the same name atomlist = group.atoms.unique[group.atoms.unique.names == name] @@ -1057,6 +1070,13 @@ def getattr__(residuegroup, resname): # This transplant is hardcoded for now to allow for multiple getattr things #transplants[Segment].append(('__getattr__', getattr__)) + + @deprecate(message="Instant selector ResidueGroup. " + "or Segment. " + "is deprecated and will be removed in 1.0. " + "Use ResidueGroup[ResidueGroup.resnames == ''] " + "or Segment.residues[Segment.residues == ''] " + "instead.") def _get_named_residue(group, resname): """Get all residues with name *resname* in the current ResidueGroup or Segment. @@ -1068,6 +1088,15 @@ def _get_named_residue(group, resname): .. versionadded:: 0.9.2 + .. deprecated:: 0.16.2 + *Instant selectors* will be removed in the 1.0 release. + Use ``ResidueGroup[ResidueGroup.resnames == '']`` + or ``Segment.residues[Segment.residues == '']`` + instead. + See issue `#1377 + `_ for + more details. + """ # There can be more than one residue with the same name residues = group.residues.unique[ @@ -1244,6 +1273,10 @@ def getattr__(segmentgroup, segid): transplants[SegmentGroup].append( ('__getattr__', getattr__)) + @deprecate(message="Instant selector SegmentGroup. " + "is deprecated and will be removed in 1.0. " + "Use SegmentGroup[SegmentGroup.segids == ''] " + "instead.") def _get_named_segment(group, segid): """Get all segments with name *segid* in the current SegmentGroup. @@ -1254,6 +1287,13 @@ def _get_named_segment(group, segid): .. versionadded:: 0.9.2 + .. deprecated:: 0.16.2 + *Instant selectors* will be removed in the 1.0 release. + Use ``SegmentGroup[SegmentGroup.segids == '']`` instead. + See issue `#1377 + `_ for + more details. + """ # Undo adding 's' if segid started with digit if segid.startswith('s') and len(segid) >= 2 and segid[1].isdigit(): diff --git a/package/doc/sphinx/source/documentation_pages/selections.rst b/package/doc/sphinx/source/documentation_pages/selections.rst index c4773df5eb1..40caae95b7e 100644 --- a/package/doc/sphinx/source/documentation_pages/selections.rst +++ b/package/doc/sphinx/source/documentation_pages/selections.rst @@ -286,7 +286,6 @@ Instant selectors ================= .. deprecated:: 0.16.2 - *Instant selectors* will be removed in the 1.0 release in order to streamline the MDAnalysis user interface. They do not seem to be widely used anymore, can produce cryptic error messages, and are @@ -309,6 +308,11 @@ other levels of the structural hierarchy, namely for Segment selector ---------------- +.. deprecated:: 0.16.2 + Use ``SegmentGroup[SegmentGroup.segids == '']`` instead. Note that this + *always* returns a :class:`SegmentGroup` and *never* a :class:`Segment` + (unlike the instant selector). + - ``universe.`` or ``universe.s`` (if ** starts with a number) - returns a :class:`~MDAnalysis.core.groups.Segment` @@ -320,6 +324,9 @@ Segment selector Resid selector -------------- +.. deprecated:: 0.16.2 + Use ``Segment.residues[N-1]`` instead. + - ``seg.r`` selects residue with number ```` - returns a :class:`~MDAnalysis.core.groups.Residue` - works for :class:`~MDAnalysis.core.groups.Segment` and :class:`~MDAnalysis.core.groups.SegmentGroup` @@ -330,6 +337,12 @@ Resid selector Residue name selector --------------------- +.. deprecated:: 0.16.2 + Use ``ResidueGroup[ResidueGroup.resnames == '']`` or + ``Segment.residues[Segment.residues == '']`` instead. Note that this + *always* returns a :class:`ResidueGroup` and *never* a :class:`Residue` + (unlike the instant selector). + - ``seg.`` selects residues with residue name ```` - returns a :class:`~MDAnalysis.core.groups.ResidueGroup` - works for :class:`~MDAnalysis.core.groups.Segment` and :class:`~MDAnalysis.core.groups.SegmentGroup` @@ -346,6 +359,11 @@ Residue name selector Atom name selector ------------------ +.. deprecated:: 0.16.2 + Use ``AtomGroup.select_atoms('name ')`` instead. Note that this + *always* returns an :class:`AtomGroup` and *never* an :class:`Atom` (unlike + the instant selector). + - ``g.`` selects a single atom or a group of atoms with name ```` - returns diff --git a/testsuite/MDAnalysisTests/core/test_groups.py b/testsuite/MDAnalysisTests/core/test_groups.py index 004b6804729..178e9140e82 100644 --- a/testsuite/MDAnalysisTests/core/test_groups.py +++ b/testsuite/MDAnalysisTests/core/test_groups.py @@ -30,12 +30,13 @@ assert_array_equal, assert_equal, assert_raises, + assert_warns, ) import operator import six import MDAnalysis as mda -from MDAnalysisTests import make_Universe, parser_not_found +from MDAnalysisTests import make_Universe, parser_not_found, assert_nowarns from MDAnalysisTests.datafiles import PSF, DCD from MDAnalysis.core import groups from MDAnalysis.core.topology import Topology @@ -986,3 +987,38 @@ class TestAtomGroup(object): def test_PDB_atom_repr(): u = make_Universe(extras=('altLocs', 'names', 'types', 'resnames', 'resids', 'segids')) assert_equal("", u.atoms[0].__repr__()) + + +class TestInstantSelectorDeprecationWarnings(object): + def setUp(self): + self.u = make_Universe(("resids", "resnames", "segids", "names")) + + def test_AtomGroup_warn_getitem(self): + name = self.u.atoms[0].name + assert_warns(DeprecationWarning, lambda x: self.u.atoms[x], name) + + def test_AtomGroup_nowarn_getitem_index(self): + assert_nowarns(DeprecationWarning, lambda x: self.u.atoms[x], 0) + + def test_AtomGroup_warn_getattr(self): + name = self.u.atoms[0].name + assert_warns(DeprecationWarning, lambda x: getattr(self.u.atoms, x), name) + + def test_ResidueGroup_warn_getattr_resname(self): + name = self.u.residues[0].resname + assert_warns(DeprecationWarning, lambda x: getattr(self.u.residues, x), name) + + def test_Segment_warn_getattr_resname(self): + name = self.u.residues[0].resname + assert_warns(DeprecationWarning, lambda x: getattr(self.u.segments[0], x), name) + + def test_Segment_warn_getattr_rRESNUM(self): + assert_warns(DeprecationWarning, lambda x: getattr(self.u.segments[0], x), 'r1') + + def test_SegmentGroup_warn_getattr(self): + name = self.u.segments[0].segid + assert_warns(DeprecationWarning, lambda x: getattr(self.u.segments, x), name) + + def test_SegmentGroup_nowarn_getitem(self): + assert_nowarns(DeprecationWarning, lambda x: self.u.segments[x], 0) + From 7c4a98f043760d48366b5107e836e55126050a95 Mon Sep 17 00:00:00 2001 From: Max Linke Date: Sat, 17 Jun 2017 08:24:52 +0200 Subject: [PATCH 08/15] fix sphinx warnings --- package/MDAnalysis/coordinates/DCD.py | 55 ++++++++++--------- package/MDAnalysis/core/Timeseries.py | 19 ++++--- package/MDAnalysis/core/groups.py | 22 ++++---- .../source/documentation_pages/selections.rst | 1 + 4 files changed, 50 insertions(+), 47 deletions(-) diff --git a/package/MDAnalysis/coordinates/DCD.py b/package/MDAnalysis/coordinates/DCD.py index 0cdd4cf2709..70b81d52ae0 100644 --- a/package/MDAnalysis/coordinates/DCD.py +++ b/package/MDAnalysis/coordinates/DCD.py @@ -581,36 +581,37 @@ def timeseries(self, asel=None, start=None, stop=None, step=None, skip=None, @deprecate(message="This method will be removed in 0.17") def correl(self, timeseries, start=None, stop=None, step=None, skip=None): - """Populate a :class:`~MDAnalysis.core.Timeseries.TimeseriesCollection` object - with time series computed from the trajectory. - - Calling this method will iterate through the whole trajectory and - perform the calculations prescribed in `timeseries`. - - Parameters - ---------- - timeseries : :class:`MDAnalysis.core.Timeseries.TimeseriesCollection` - The :class:`MDAnalysis.core.Timeseries.TimeseriesCollection` that defines what kind - of computations should be performed on the data in this trajectory. - start : int (optional) - Begin reading the trajectory at frame index `start` (where 0 is the index - of the first frame in the trajectory); the default ``None`` starts - at the beginning. - stop : int (optional) - End reading the trajectory at frame index `stop`-1, i.e, `stop` is excluded. - The trajectory is read to the end with the default ``None``. - step : int (optional) - Step size for reading; the default ``None`` is equivalent to 1 and means to - read every frame. + """ +Populate a :class:`~MDAnalysis.core.Timeseries.TimeseriesCollection` object +with time series computed from the trajectory. + +Calling this method will iterate through the whole trajectory and +perform the calculations prescribed in `timeseries`. + +Parameters +---------- +timeseries : :class:`MDAnalysis.core.Timeseries.TimeseriesCollection` + The :class:`MDAnalysis.core.Timeseries.TimeseriesCollection` that defines what kind + of computations should be performed on the data in this trajectory. +start : int (optional) + Begin reading the trajectory at frame index `start` (where 0 is the index + of the first frame in the trajectory); the default ``None`` starts + at the beginning. +stop : int (optional) + End reading the trajectory at frame index `stop`-1, i.e, `stop` is excluded. + The trajectory is read to the end with the default ``None``. +step : int (optional) + Step size for reading; the default ``None`` is equivalent to 1 and means to + read every frame. - Note - ---- - The `correl` functionality is only implemented for DCD trajectories and - the :class:`DCDReader`. +Note +---- +The `correl` functionality is only implemented for DCD trajectories and +the :class:`DCDReader`. - .. deprecated:: 0.16.0 - `skip` has been deprecated in favor of the standard keyword `step`. +.. deprecated:: 0.16.0 + `skip` has been deprecated in favor of the standard keyword `step`. """ if skip is not None: diff --git a/package/MDAnalysis/core/Timeseries.py b/package/MDAnalysis/core/Timeseries.py index f7e99248673..024b0cb18ca 100644 --- a/package/MDAnalysis/core/Timeseries.py +++ b/package/MDAnalysis/core/Timeseries.py @@ -82,7 +82,6 @@ DeprecationWarning) -@deprecate(message="This class will be removed in 0.17") class TimeseriesCollection(object): '''A collection of timeseries objects. @@ -110,6 +109,7 @@ class TimeseriesCollection(object): ''' + @deprecate(message="This class will be removed in 0.17") def __init__(self): self.timeseries = [] @@ -223,6 +223,7 @@ class Timeseries(object): ''' + @deprecate(message="This class will be removed in 0.17") def __init__(self, code, atoms, dsize): if isinstance(atoms, groups.AtomGroup): self.atoms = atoms.atoms @@ -274,7 +275,6 @@ def getAuxData(self): return [0.] * self.n_atoms -@deprecate(message="This class will be removed in 0.17") class Atom(Timeseries): '''Create a timeseries that returns coordinate data for an atom or group of atoms :: @@ -297,6 +297,7 @@ class Atom(Timeseries): ''' + @deprecate(message="This class will be removed in 0.17") def __init__(self, code, atoms): if code not in ('x', 'y', 'z', 'v', 'w'): raise ValueError("Bad code") @@ -318,7 +319,6 @@ def getAtomCounts(self): return [1, ] * self.n_atoms -@deprecate(message="This class will be removed in 0.17") class Bond(Timeseries): '''Create a timeseries that returns a timeseries for a bond @@ -335,13 +335,13 @@ class Bond(Timeseries): ''' + @deprecate(message="This class will be removed in 0.17") def __init__(self, atoms): if not len(atoms) == 2: raise ValueError("Bond timeseries requires a 2 atom selection") Timeseries.__init__(self, 'r', atoms, 1) -@deprecate(message="This class will be removed in 0.17") class Angle(Timeseries): '''Create a timeseries that returns a timeseries for an angle @@ -358,13 +358,13 @@ class Angle(Timeseries): ''' + @deprecate(message="This class will be removed in 0.17") def __init__(self, atoms): if not len(atoms) == 3: raise ValueError("Angle timeseries requires a 3 atom selection") Timeseries.__init__(self, 'a', atoms, 1) -@deprecate(message="This class will be removed in 0.17") class Dihedral(Timeseries): '''Create a timeseries that returns a timeseries for a dihedral angle @@ -381,13 +381,13 @@ class Dihedral(Timeseries): ''' + @deprecate(message="This class will be removed in 0.17") def __init__(self, atoms): if not len(atoms) == 4: raise ValueError("Dihedral timeseries requires a 4 atom selection") Timeseries.__init__(self, 'h', atoms, 1) -@deprecate(message="This class will be removed in 0.17") class Distance(Timeseries): '''Create a timeseries that returns distances between 2 atoms @@ -405,6 +405,7 @@ class Distance(Timeseries): ''' + @deprecate(message="This class will be removed in 0.17") def __init__(self, code, atoms): if code not in ('d', 'r'): raise ValueError("Bad code") @@ -417,7 +418,6 @@ def __init__(self, code, atoms): Timeseries.__init__(self, code, atoms, size) -@deprecate(message="This class will be removed in 0.17") class CenterOfGeometry(Timeseries): '''Create a timeseries that returns the center of geometry of a group of atoms @@ -435,6 +435,7 @@ class CenterOfGeometry(Timeseries): ''' + @deprecate(message="This class will be removed in 0.17") def __init__(self, atoms): Timeseries.__init__(self, 'm', atoms, 3) @@ -442,7 +443,6 @@ def getAuxData(self): return [1.] * self.n_atoms -@deprecate(message="This class will be removed in 0.17") class CenterOfMass(Timeseries): '''Create a timeseries that returns the center of mass of a group of atoms @@ -459,6 +459,7 @@ class CenterOfMass(Timeseries): ''' + @deprecate(message="This class will be removed in 0.17") def __init__(self, atoms): Timeseries.__init__(self, 'm', atoms, 3) @@ -466,7 +467,6 @@ def getAuxData(self): return [a.mass for a in self.atoms] -@deprecate(message="This class will be removed in 0.17") class WaterDipole(Timeseries): r'''Create a Timeseries that returns a timeseries for the bisector vector of a 3-site water @@ -512,6 +512,7 @@ class WaterDipole(Timeseries): ''' + @deprecate(message="This class will be removed in 0.17") def __init__(self, atoms): if not len(atoms) == 3: raise ValueError("WaterDipole timeseries requires a 3 atom selection") diff --git a/package/MDAnalysis/core/groups.py b/package/MDAnalysis/core/groups.py index 46d46c10644..f2717469aac 100644 --- a/package/MDAnalysis/core/groups.py +++ b/package/MDAnalysis/core/groups.py @@ -1488,13 +1488,13 @@ class AtomGroup(GroupBase): group. These *instant selector* attributes are auto-generated for each atom name encountered in the group. - .. note:: - - The name-attribute instant selector access to atoms is mainly - meant for quick interactive work. Thus it either returns a - single :class:`Atom` if there is only one matching atom, *or* a - new :class:`AtomGroup` for multiple matches. This makes it - difficult to use the feature consistently in scripts. + Notes + ----- + The name-attribute instant selector access to atoms is mainly + meant for quick interactive work. Thus it either returns a + single :class:`Atom` if there is only one matching atom, *or* a + new :class:`AtomGroup` for multiple matches. This makes it + difficult to use the feature consistently in scripts. See Also @@ -1504,7 +1504,7 @@ class AtomGroup(GroupBase): .. deprecated:: 0.16.2 *Instant selectors* of AtomGroup will be removed in the 1.0 release. - See :ref:`Instant selectors`_ for details and alternatives. + See :ref:`Instant selectors ` for details and alternatives. """ def __getitem__(self, item): @@ -2202,7 +2202,7 @@ class ResidueGroup(GroupBase): .. deprecated:: 0.16.2 *Instant selectors* of Segments will be removed in the 1.0 release. - See :ref:`Instant selectors`_ for details and alternatives. + See :ref:`Instant selectors ` for details and alternatives. """ @@ -2321,7 +2321,7 @@ class SegmentGroup(GroupBase): .. deprecated:: 0.16.2 *Instant selectors* of Segments will be removed in the 1.0 release. - See :ref:`Instant selectors`_ for details and alternatives. + See :ref:`Instant selectors ` for details and alternatives. """ @@ -2670,7 +2670,7 @@ class Segment(ComponentBase): .. deprecated:: 0.16.2 *Instant selectors* of Segments will be removed in the 1.0 release. - See :ref:`Instant selectors`_ for details and alternatives. + See :ref:`Instant selectors ` for details and alternatives. """ def __repr__(self): diff --git a/package/doc/sphinx/source/documentation_pages/selections.rst b/package/doc/sphinx/source/documentation_pages/selections.rst index 40caae95b7e..48b221ec9fd 100644 --- a/package/doc/sphinx/source/documentation_pages/selections.rst +++ b/package/doc/sphinx/source/documentation_pages/selections.rst @@ -281,6 +281,7 @@ across frames:: >>> static_ag +.. _instance-selectors: Instant selectors ================= From a9b1fc31d61e0e1ef712c7ea53fee3613baa384a Mon Sep 17 00:00:00 2001 From: Max Linke Date: Sat, 17 Jun 2017 08:39:52 +0200 Subject: [PATCH 09/15] add getitem warning message --- package/MDAnalysis/core/groups.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package/MDAnalysis/core/groups.py b/package/MDAnalysis/core/groups.py index f2717469aac..9840b62276c 100644 --- a/package/MDAnalysis/core/groups.py +++ b/package/MDAnalysis/core/groups.py @@ -1513,6 +1513,9 @@ def __getitem__(self, item): # # u.atoms['HT1'] access, otherwise default if isinstance(item, string_types): + warnings.warn("Using the [] operator with strings is deprecated." + "Please use `select_atoms('name {}')` " + "instead.".format(item)) try: return self._get_named_atom(item) except (AttributeError, selection.SelectionError): From 2add693f8cbd9c8aa49fa6140e72c254744a8ed9 Mon Sep 17 00:00:00 2001 From: Max Linke Date: Sat, 17 Jun 2017 11:15:33 +0200 Subject: [PATCH 10/15] use explicit deprecation warning --- package/MDAnalysis/core/groups.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package/MDAnalysis/core/groups.py b/package/MDAnalysis/core/groups.py index 9840b62276c..445502f60dc 100644 --- a/package/MDAnalysis/core/groups.py +++ b/package/MDAnalysis/core/groups.py @@ -1515,7 +1515,8 @@ def __getitem__(self, item): if isinstance(item, string_types): warnings.warn("Using the [] operator with strings is deprecated." "Please use `select_atoms('name {}')` " - "instead.".format(item)) + "instead.".format(item), + category=DeprecationWarning) try: return self._get_named_atom(item) except (AttributeError, selection.SelectionError): From 69a4750d61f57112de52c69bdff8c989cb36de42 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Sat, 17 Jun 2017 16:49:37 -0700 Subject: [PATCH 11/15] test for no warnings raised by AtomGroup.segid See comment https://github.com/MDAnalysis/mdanalysis/pull/1403#discussion_r122565617 --- testsuite/MDAnalysisTests/core/test_groups.py | 3 +++ testsuite/MDAnalysisTests/util.py | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/testsuite/MDAnalysisTests/core/test_groups.py b/testsuite/MDAnalysisTests/core/test_groups.py index 178e9140e82..e962b045fb7 100644 --- a/testsuite/MDAnalysisTests/core/test_groups.py +++ b/testsuite/MDAnalysisTests/core/test_groups.py @@ -1000,6 +1000,9 @@ def test_AtomGroup_warn_getitem(self): def test_AtomGroup_nowarn_getitem_index(self): assert_nowarns(DeprecationWarning, lambda x: self.u.atoms[x], 0) + def test_AtomGroup_nowarn_segids_attribute(self): + assert_nowarns(DeprecationWarning, lambda x: getattr(self.u.atoms, x), "segids") + def test_AtomGroup_warn_getattr(self): name = self.u.atoms[0].name assert_warns(DeprecationWarning, lambda x: getattr(self.u.atoms, x), name) diff --git a/testsuite/MDAnalysisTests/util.py b/testsuite/MDAnalysisTests/util.py index 1c7aea712be..fc78dd31671 100644 --- a/testsuite/MDAnalysisTests/util.py +++ b/testsuite/MDAnalysisTests/util.py @@ -42,7 +42,6 @@ from numpy.testing import assert_warns - def block_import(package): """Block import of a given package @@ -195,6 +194,5 @@ def assert_nowarns(warning_class, *args, **kwargs): return True else: # There was a warning even though we do not want to see one. - raise AssertionError - - + raise AssertionError("function {0} raises warning of class {1}".format( + func.__name__, warning_class.__name__)) From bf6bc702df2077d0aebe1576b02d7b60a0016c44 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Sat, 17 Jun 2017 16:49:55 -0700 Subject: [PATCH 12/15] minor cleanups in tests --- testsuite/MDAnalysisTests/utils/test_deprecated.py | 4 +--- testsuite/MDAnalysisTests/utils/test_failure.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/testsuite/MDAnalysisTests/utils/test_deprecated.py b/testsuite/MDAnalysisTests/utils/test_deprecated.py index 3ce63e6abe7..c7acee6ffa9 100644 --- a/testsuite/MDAnalysisTests/utils/test_deprecated.py +++ b/testsuite/MDAnalysisTests/utils/test_deprecated.py @@ -25,9 +25,7 @@ # will be removed in 1.0) from __future__ import absolute_import -import warnings - -from numpy.testing import assert_equal, assert_raises +from numpy.testing import assert_raises class TestImports(object): def test_core_units(self): diff --git a/testsuite/MDAnalysisTests/utils/test_failure.py b/testsuite/MDAnalysisTests/utils/test_failure.py index c05c5c6e534..6cba555d0b5 100644 --- a/testsuite/MDAnalysisTests/utils/test_failure.py +++ b/testsuite/MDAnalysisTests/utils/test_failure.py @@ -30,4 +30,4 @@ def test_failure(): # Have a file open to trigger an output from the open_files plugin. f = open('./failure.txt', 'w') if u'MDA_FAILURE_TEST' in os.environ: - assert False + raise AssertionError("the MDA_FAILURE_TEST environment variable is set") From 6d8a5209d27c8f6972ef74e65ff96c5eba8ed5a9 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Sat, 17 Jun 2017 16:52:52 -0700 Subject: [PATCH 13/15] AtomGroup.__getitem__ depreaction warning: added removal release --- package/MDAnalysis/core/groups.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package/MDAnalysis/core/groups.py b/package/MDAnalysis/core/groups.py index 445502f60dc..c07eec5a20f 100644 --- a/package/MDAnalysis/core/groups.py +++ b/package/MDAnalysis/core/groups.py @@ -1513,7 +1513,8 @@ def __getitem__(self, item): # # u.atoms['HT1'] access, otherwise default if isinstance(item, string_types): - warnings.warn("Using the [] operator with strings is deprecated." + warnings.warn("Using the [] operator with strings is deprecated " + "and will be removed in 1.0. " "Please use `select_atoms('name {}')` " "instead.".format(item), category=DeprecationWarning) From 8c250f9020dba27696ca3cdc7e0c6f144863746c Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Sat, 17 Jun 2017 17:03:32 -0700 Subject: [PATCH 14/15] removed explicit DeprecationWarning for AtomGroup["name"] It turns out that with this warning in place we raise two deprecation warnings because we also have a warning on _get_named_atom(item). Example output: >>> w.atoms['AAA'] .../mdanalysis/package/MDAnalysis/core/groups.py:1520: DeprecationWarning: Using the [] operator with strings is deprecated and will be removed in 1.0. Please use `select_atoms('name AAA')` instead. category=DeprecationWarning) .../mdanalysis/package/MDAnalysis/core/groups.py:1522: DeprecationWarning: `_get_named_atom` is deprecated! Instant selector AtomGroup[''] or AtomGroup. is deprecated and will be removed in 1.0. Use AtomGroup.select_atoms('name ') instead. return self._get_named_atom(item) --- package/MDAnalysis/core/groups.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/package/MDAnalysis/core/groups.py b/package/MDAnalysis/core/groups.py index c07eec5a20f..f2717469aac 100644 --- a/package/MDAnalysis/core/groups.py +++ b/package/MDAnalysis/core/groups.py @@ -1513,11 +1513,6 @@ def __getitem__(self, item): # # u.atoms['HT1'] access, otherwise default if isinstance(item, string_types): - warnings.warn("Using the [] operator with strings is deprecated " - "and will be removed in 1.0. " - "Please use `select_atoms('name {}')` " - "instead.".format(item), - category=DeprecationWarning) try: return self._get_named_atom(item) except (AttributeError, selection.SelectionError): From a9f9655a3b45b8486a1aadb5ad903f8bfe68ef3f Mon Sep 17 00:00:00 2001 From: Max Linke Date: Tue, 20 Jun 2017 16:18:02 +0200 Subject: [PATCH 15/15] only issue _get_named_atom warning on successful call This allows code to use the `getattr` function and only throw an error if the requested attribute is an atom name. --- package/MDAnalysis/core/topologyattrs.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package/MDAnalysis/core/topologyattrs.py b/package/MDAnalysis/core/topologyattrs.py index fa6997c3adc..82aae50632a 100644 --- a/package/MDAnalysis/core/topologyattrs.py +++ b/package/MDAnalysis/core/topologyattrs.py @@ -41,6 +41,7 @@ import itertools import numbers import numpy as np +import warnings from numpy.lib.utils import deprecate @@ -411,9 +412,6 @@ def getattr__(atomgroup, name): raise AttributeError("'{0}' object has no attribute '{1}'".format( atomgroup.__class__.__name__, name)) - @deprecate(message="Instant selector AtomGroup[''] or AtomGroup. " - "is deprecated and will be removed in 1.0. " - "Use AtomGroup.select_atoms('name ') instead.") def _get_named_atom(group, name): """Get all atoms with name *name* in the current AtomGroup. @@ -438,10 +436,12 @@ def _get_named_atom(group, name): "No atoms with name '{0}'".format(name)) elif len(atomlist) == 1: # XXX: keep this, makes more sense for names - return atomlist[0] - else: - # XXX: but inconsistent (see residues and Issue 47) - return atomlist + atomlist = atomlist[0] + warnings.warn("Instant selector AtomGroup[''] or AtomGroup. " + "is deprecated and will be removed in 1.0. " + "Use AtomGroup.select_atoms('name ') instead.", + DeprecationWarning) + return atomlist # AtomGroup already has a getattr # transplants[AtomGroup].append(