From c1b43d5237f4e4bea6291e892e5edbfab196f787 Mon Sep 17 00:00:00 2001 From: IAlibay Date: Tue, 6 Jun 2023 02:17:09 +0100 Subject: [PATCH 01/11] fix lammps case --- package/MDAnalysis/coordinates/LAMMPS.py | 4 ++-- testsuite/MDAnalysisTests/coordinates/test_lammps.py | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/package/MDAnalysis/coordinates/LAMMPS.py b/package/MDAnalysis/coordinates/LAMMPS.py index 746d24497ee..bbb1969e3aa 100644 --- a/package/MDAnalysis/coordinates/LAMMPS.py +++ b/package/MDAnalysis/coordinates/LAMMPS.py @@ -263,7 +263,7 @@ def __init__(self, filename, convert_units=True, **kwargs): convert_units : bool, optional units are converted to the MDAnalysis base format; [``True``] """ - self.filename = util.filename(filename, ext='data') + self.filename = util.filename(filename, ext='data', keep=True) self.convert_units = convert_units @@ -419,7 +419,7 @@ def write(self, selection, frame=None): has_velocities = True features = {} - with util.openany(self.filename, 'w') as self.f: + with util.openany(self.filename, 'wt') as self.f: self.f.write('LAMMPS data file via MDAnalysis\n') self.f.write('\n') self.f.write('{:>12d} atoms\n'.format(len(atoms))) diff --git a/testsuite/MDAnalysisTests/coordinates/test_lammps.py b/testsuite/MDAnalysisTests/coordinates/test_lammps.py index f3a9e53ab93..92bfe723bc1 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_lammps.py +++ b/testsuite/MDAnalysisTests/coordinates/test_lammps.py @@ -220,8 +220,13 @@ def test_molecule_tag(self, LAMMPSDATAWriter_molecule_tag): err_msg="resids different after writing",) -def test_datawriter_universe(tmpdir): - fn = str(tmpdir.join('out.data')) +@pytest.mark.parametrize('filename', ['out.data', 'out.data.bz2']) +def test_datawriter_universe(filename, tmpdir): + """ + Test roundtrip on datawriter, and also checks compressed files + can be written (see #4159). + """ + fn = str(tmpdir.join(filename)) u = mda.Universe(LAMMPSdata_mini) From 4c72ca81f9b72dfc90ce09c40803d830d82ec648 Mon Sep 17 00:00:00 2001 From: IAlibay Date: Tue, 6 Jun 2023 02:27:54 +0100 Subject: [PATCH 02/11] correct incorrect statement about modes --- package/MDAnalysis/lib/util.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package/MDAnalysis/lib/util.py b/package/MDAnalysis/lib/util.py index 4bf21361a45..434c7b472e6 100644 --- a/package/MDAnalysis/lib/util.py +++ b/package/MDAnalysis/lib/util.py @@ -338,9 +338,10 @@ def anyopen(datasource, mode='rt', reset=True): a file (from :class:`file` or :func:`open`) or a stream (e.g. from :func:`urllib2.urlopen` or :class:`io.StringIO`) mode: {'r', 'w', 'a'} (optional) - Open in r(ead), w(rite) or a(ppen) mode. More complicated - modes ('r+', 'w+', ...) are not supported; only the first letter of - `mode` is used and thus any additional modifiers are silently ignored. + Open in r(ead), w(rite) or a(ppen) mode. This string is directly + passed to the file opening handler (either Python's openfe, bz2, or + gzip). More complex mode are supported if the file opening handler + supportst it. reset: bool (optional) try to read (`mode` 'r') the stream from the start From 336912f5716f6a4fe765ab8172a5d129e91c10aa Mon Sep 17 00:00:00 2001 From: IAlibay Date: Sun, 18 Jun 2023 15:02:46 +0100 Subject: [PATCH 03/11] O look another t typo Unpopular opinion; the Foo Fighters have become a more iconic band than Nirvana. --- package/MDAnalysis/lib/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/MDAnalysis/lib/util.py b/package/MDAnalysis/lib/util.py index 434c7b472e6..927b9d62c69 100644 --- a/package/MDAnalysis/lib/util.py +++ b/package/MDAnalysis/lib/util.py @@ -341,7 +341,7 @@ def anyopen(datasource, mode='rt', reset=True): Open in r(ead), w(rite) or a(ppen) mode. This string is directly passed to the file opening handler (either Python's openfe, bz2, or gzip). More complex mode are supported if the file opening handler - supportst it. + supports it. reset: bool (optional) try to read (`mode` 'r') the stream from the start From a8dd44ba3f9ab51660084196cac7f0c233ee3b6b Mon Sep 17 00:00:00 2001 From: IAlibay Date: Sun, 18 Jun 2023 15:16:38 +0100 Subject: [PATCH 04/11] Fix CRD case You know, I don't think I've ever watched Breakfast at Tiffany's. --- package/MDAnalysis/coordinates/CRD.py | 4 ++-- testsuite/MDAnalysisTests/coordinates/test_crd.py | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/package/MDAnalysis/coordinates/CRD.py b/package/MDAnalysis/coordinates/CRD.py index 79f88634971..826e93cf183 100644 --- a/package/MDAnalysis/coordinates/CRD.py +++ b/package/MDAnalysis/coordinates/CRD.py @@ -165,7 +165,7 @@ def __init__(self, filename, **kwargs): .. versionadded:: 2.2.0 """ - self.filename = util.filename(filename, ext='crd') + self.filename = util.filename(filename, ext='crd', keep=True) self.crd = None # account for explicit crd format, if requested @@ -247,7 +247,7 @@ def write(self, selection, frame=None): "{miss}. These will be written with default values. " "".format(miss=', '.join(missing_topology))) - with util.openany(self.filename, 'w') as crd: + with util.openany(self.filename, 'wt') as crd: # Write Title crd.write(self.fmt['TITLE'].format( frame=frame, where=u.trajectory.filename)) diff --git a/testsuite/MDAnalysisTests/coordinates/test_crd.py b/testsuite/MDAnalysisTests/coordinates/test_crd.py index 623524a8a98..8b54ab0115c 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_crd.py +++ b/testsuite/MDAnalysisTests/coordinates/test_crd.py @@ -43,14 +43,16 @@ def u(self): def outfile(self, tmpdir): return os.path.join(str(tmpdir), 'test.crd') - def test_write_atoms(self, u, outfile): + @pytest.mark.parametrize('testfile', ['test.crd', 'test.crd.bz2']) + def test_write_atoms(self, u, testfile, tmpdir): # Test that written file when read gives same coordinates - u.atoms.write(outfile) + with tmpdir.as_cwd(): + u.atoms.write(testfile) - u2 = mda.Universe(outfile) + u2 = mda.Universe(testfile) - assert_equal(u.atoms.positions, - u2.atoms.positions) + assert_equal(u.atoms.positions, + u2.atoms.positions) def test_roundtrip(self, u, outfile): # Write out a copy of the Universe, and compare this against the original From 4769296462577a1a0b4eaa8ac3d3bc896429aedd Mon Sep 17 00:00:00 2001 From: IAlibay Date: Sun, 18 Jun 2023 15:46:28 +0100 Subject: [PATCH 05/11] Fix PQR case At 20 miles a day, it would take 50 days for the Proclaimers to get to fall down at your door. That's some shockingly poor delivery service... --- package/MDAnalysis/coordinates/PQR.py | 4 ++-- testsuite/MDAnalysisTests/coordinates/test_pqr.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/package/MDAnalysis/coordinates/PQR.py b/package/MDAnalysis/coordinates/PQR.py index 1fd971b4d37..871d4b120ef 100644 --- a/package/MDAnalysis/coordinates/PQR.py +++ b/package/MDAnalysis/coordinates/PQR.py @@ -210,7 +210,7 @@ def __init__(self, filename, convert_units=True, **kwargs): remark lines (list of strings) or single string to be added to the top of the PQR file """ - self.filename = util.filename(filename, ext='pqr') + self.filename = util.filename(filename, ext='pqr', keep=True) self.convert_units = convert_units # convert length and time to base units self.remarks = kwargs.pop('remarks', "PQR file written by MDAnalysis") @@ -299,7 +299,7 @@ def write(self, selection, frame=None): "{miss}. These will be written with default values. " "".format(miss=', '.join(missing_topology))) - with util.openany(self.filename, 'w') as pqrfile: + with util.openany(self.filename, 'wt') as pqrfile: # Header / Remarks # The *remarknumber* is typically 1 but :program:`pdb2pgr` # also uses 6 for the total charge and 5 for warnings. diff --git a/testsuite/MDAnalysisTests/coordinates/test_pqr.py b/testsuite/MDAnalysisTests/coordinates/test_pqr.py index 96eeea34099..dd17e8ce206 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_pqr.py +++ b/testsuite/MDAnalysisTests/coordinates/test_pqr.py @@ -80,6 +80,14 @@ def universe(): prec = 3 + @pytest.mark.parametrize('filename', ['test.pqr', 'test.pqr.bz2']) + def test_simple_writer_roundtrip(self, universe, filename, tmpdir): + with tmpdir.as_cwd(): + universe.atoms.write(filename) + u2 = mda.Universe(filename) + assert_equal(universe.atoms.positions, + u2.atoms.positions) + def test_writer_noChainID(self, universe, tmpdir): outfile = str(tmpdir.join('pqr-test.pqr')) From 1fe6b1e4f1daea02d714c3bd08d5d8139bec22a0 Mon Sep 17 00:00:00 2001 From: IAlibay Date: Sun, 18 Jun 2023 16:04:16 +0100 Subject: [PATCH 06/11] Fix PDBQT case You know Billy, you might not have started the fire, but you _could_ have helped try to put it out... A little bit of water goes a long way. --- package/MDAnalysis/coordinates/PDBQT.py | 4 ++-- .../MDAnalysisTests/coordinates/test_pdbqt.py | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/package/MDAnalysis/coordinates/PDBQT.py b/package/MDAnalysis/coordinates/PDBQT.py index 0e07008d984..a0b9805df16 100644 --- a/package/MDAnalysis/coordinates/PDBQT.py +++ b/package/MDAnalysis/coordinates/PDBQT.py @@ -213,8 +213,8 @@ class PDBQTWriter(base.WriterBase): pdb_coor_limits = {"min": -999.9995, "max": 9999.9995} def __init__(self, filename, **kwargs): - self.filename = util.filename(filename, ext='pdbqt') - self.pdb = util.anyopen(self.filename, 'w') + self.filename = util.filename(filename, ext='pdbqt', keep=True) + self.pdb = util.anyopen(self.filename, 'wt') def close(self): self.pdb.close() diff --git a/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py b/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py index 667ed61aa36..a2781fcada2 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py +++ b/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py @@ -89,13 +89,16 @@ class TestPDBQTWriter(object): def outfile(self, tmpdir): return str(tmpdir) + 'out.pdbqt' - def test_roundtrip_writing_coords(self, outfile): - u = mda.Universe(PDBQT_input) - u.atoms.write(outfile) - u2 = mda.Universe(outfile) + @pytest.mark.parametrize('filename', ['test.pdbqt', 'test.pdbqt.bz2']) + def test_roundtrip_writing_coords(self, filename, tmpdir): + + with tmpdir.as_cwd(): + u = mda.Universe(PDBQT_input) + u.atoms.write(filename) + u2 = mda.Universe(filename) - assert_equal(u2.atoms.positions, u.atoms.positions, - "Round trip does not preserve coordinates") + assert_equal(u2.atoms.positions, u.atoms.positions, + "Round trip does not preserve coordinates") def test_roundtrip_formatting(self, outfile): # Compare formatting of first line From 65031160fa5c3429c05f27ece3de9b8d49ec8799 Mon Sep 17 00:00:00 2001 From: IAlibay Date: Sun, 18 Jun 2023 16:15:17 +0100 Subject: [PATCH 07/11] Compressed NAMDBIN isn't solvable at this stage. We built this city on Rock N Roll.. poorly documented ideas of what file types you can open. anyopen? more like maybeopen --- package/MDAnalysis/coordinates/NAMDBIN.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/package/MDAnalysis/coordinates/NAMDBIN.py b/package/MDAnalysis/coordinates/NAMDBIN.py index 2400a34fd77..01587967963 100644 --- a/package/MDAnalysis/coordinates/NAMDBIN.py +++ b/package/MDAnalysis/coordinates/NAMDBIN.py @@ -94,7 +94,12 @@ def Writer(self, filename, **kwargs): class NAMDBINWriter(base.WriterBase): """Writer for NAMD binary coordinate files. - + + Note + ---- + * Does not handle writing to bz2 or gz compressed file types. + + .. versionadded:: 1.0.0 """ format = ['COOR', 'NAMDBIN'] From b652c8aea8ed72c5db34cfa4d40bb9e73813b492 Mon Sep 17 00:00:00 2001 From: IAlibay Date: Sun, 18 Jun 2023 16:28:03 +0100 Subject: [PATCH 08/11] Add gz tests Flying like a GZIP, like a GUNZIP? --- package/MDAnalysis/coordinates/CRD.py | 3 +++ package/MDAnalysis/coordinates/PDBQT.py | 5 +++++ package/MDAnalysis/coordinates/PQR.py | 4 ++++ testsuite/MDAnalysisTests/coordinates/test_crd.py | 3 ++- testsuite/MDAnalysisTests/coordinates/test_pdbqt.py | 3 ++- testsuite/MDAnalysisTests/coordinates/test_pqr.py | 3 ++- 6 files changed, 18 insertions(+), 3 deletions(-) diff --git a/package/MDAnalysis/coordinates/CRD.py b/package/MDAnalysis/coordinates/CRD.py index 826e93cf183..dd2c92f066d 100644 --- a/package/MDAnalysis/coordinates/CRD.py +++ b/package/MDAnalysis/coordinates/CRD.py @@ -128,6 +128,9 @@ class CRDWriter(base.WriterBase): .. versionchanged:: 2.2.0 CRD extended format can now be explicitly requested with the `extended` keyword + .. versionchanged:: 2.6.0 + Files are now writen in `wt` mode, and keep extensions, allowing + for files to be written under compressed formats """ format = 'CRD' units = {'time': None, 'length': 'Angstrom'} diff --git a/package/MDAnalysis/coordinates/PDBQT.py b/package/MDAnalysis/coordinates/PDBQT.py index a0b9805df16..5543aebc5af 100644 --- a/package/MDAnalysis/coordinates/PDBQT.py +++ b/package/MDAnalysis/coordinates/PDBQT.py @@ -195,6 +195,11 @@ class PDBQTWriter(base.WriterBase): .. _PDB: http://www.wwpdb.org/documentation/file-format-content/format32/v3.2.html .. _PDBQT: http://autodock.scripps.edu/faqs-help/faq/what-is-the-format-of-a-pdbqt-file + + + .. versionchanged:: 2.6.0 + Files are now writen in `wt` mode, and keep extensions, allowing + for files to be written under compressed formats """ fmt = { diff --git a/package/MDAnalysis/coordinates/PQR.py b/package/MDAnalysis/coordinates/PQR.py index 871d4b120ef..3c8b15f8c59 100644 --- a/package/MDAnalysis/coordinates/PQR.py +++ b/package/MDAnalysis/coordinates/PQR.py @@ -186,7 +186,11 @@ class PQRWriter(base.WriterBase): The output format is similar to ``pdb2pqr --whitespace``. + .. versionadded:: 0.9.0 + .. versionchanged:: 2.6.0 + Files are now writen in `wt` mode, and keep extensions, allowing + for files to be written under compressed formats """ format = 'PQR' units = {'time': None, 'length': 'Angstrom'} diff --git a/testsuite/MDAnalysisTests/coordinates/test_crd.py b/testsuite/MDAnalysisTests/coordinates/test_crd.py index 8b54ab0115c..2b964d9c67e 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_crd.py +++ b/testsuite/MDAnalysisTests/coordinates/test_crd.py @@ -43,7 +43,8 @@ def u(self): def outfile(self, tmpdir): return os.path.join(str(tmpdir), 'test.crd') - @pytest.mark.parametrize('testfile', ['test.crd', 'test.crd.bz2']) + @pytest.mark.parametrize('testfile', + ['test.crd', 'test.crd.bz2', 'test.crd.gz']) def test_write_atoms(self, u, testfile, tmpdir): # Test that written file when read gives same coordinates with tmpdir.as_cwd(): diff --git a/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py b/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py index a2781fcada2..2c169da9889 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py +++ b/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py @@ -89,7 +89,8 @@ class TestPDBQTWriter(object): def outfile(self, tmpdir): return str(tmpdir) + 'out.pdbqt' - @pytest.mark.parametrize('filename', ['test.pdbqt', 'test.pdbqt.bz2']) + @pytest.mark.parametrize('filename', + ['test.pdbqt', 'test.pdbqt.bz2', 'test.pdbqt.gz']) def test_roundtrip_writing_coords(self, filename, tmpdir): with tmpdir.as_cwd(): diff --git a/testsuite/MDAnalysisTests/coordinates/test_pqr.py b/testsuite/MDAnalysisTests/coordinates/test_pqr.py index dd17e8ce206..9f1fe2f89b1 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_pqr.py +++ b/testsuite/MDAnalysisTests/coordinates/test_pqr.py @@ -80,7 +80,8 @@ def universe(): prec = 3 - @pytest.mark.parametrize('filename', ['test.pqr', 'test.pqr.bz2']) + @pytest.mark.parametrize('filename', + ['test.pqr', 'test.pqr.bz2', 'test.pqr.gz']) def test_simple_writer_roundtrip(self, universe, filename, tmpdir): with tmpdir.as_cwd(): universe.atoms.write(filename) From b9602703e43667769e625351a63581c19b4935fd Mon Sep 17 00:00:00 2001 From: IAlibay Date: Sun, 18 Jun 2023 16:34:36 +0100 Subject: [PATCH 09/11] Update changelog The power of a changelog is a curious thing. Make one developer weep, make another sing. Change existing scripts into new ones. --- package/CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/CHANGELOG b/package/CHANGELOG index b947200f4a6..8e64cb77492 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -19,6 +19,8 @@ The rules for this file: Fixes * Fix deletion of bond/angle/dihedral types (PR #4003, Issue #4001) + * CRD, PQR, and PDBQT writers now write BZ2 and GZ files if requested + (PR #4163, Issue #4159) Enhancements From edf54e4ec7f272766d690067a1286c5668ec5268 Mon Sep 17 00:00:00 2001 From: Irfan Alibay Date: Sun, 18 Jun 2023 17:28:08 +0100 Subject: [PATCH 10/11] Update testsuite/MDAnalysisTests/coordinates/test_lammps.py Co-authored-by: Rocco Meli --- testsuite/MDAnalysisTests/coordinates/test_lammps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/MDAnalysisTests/coordinates/test_lammps.py b/testsuite/MDAnalysisTests/coordinates/test_lammps.py index 92bfe723bc1..c8afb286fb7 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_lammps.py +++ b/testsuite/MDAnalysisTests/coordinates/test_lammps.py @@ -220,7 +220,7 @@ def test_molecule_tag(self, LAMMPSDATAWriter_molecule_tag): err_msg="resids different after writing",) -@pytest.mark.parametrize('filename', ['out.data', 'out.data.bz2']) +@pytest.mark.parametrize('filename', ['out.data', 'out.data.bz2', 'out.data.gz']) def test_datawriter_universe(filename, tmpdir): """ Test roundtrip on datawriter, and also checks compressed files From e72e05f97979024ccf5bd1922f319374969f23b4 Mon Sep 17 00:00:00 2001 From: Irfan Alibay Date: Sun, 18 Jun 2023 17:56:13 +0100 Subject: [PATCH 11/11] Apply suggestions from code review Co-authored-by: Rocco Meli --- package/MDAnalysis/coordinates/CRD.py | 2 +- package/MDAnalysis/coordinates/PDBQT.py | 2 +- package/MDAnalysis/coordinates/PQR.py | 2 +- package/MDAnalysis/lib/util.py | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package/MDAnalysis/coordinates/CRD.py b/package/MDAnalysis/coordinates/CRD.py index dd2c92f066d..89322ed771c 100644 --- a/package/MDAnalysis/coordinates/CRD.py +++ b/package/MDAnalysis/coordinates/CRD.py @@ -129,7 +129,7 @@ class CRDWriter(base.WriterBase): CRD extended format can now be explicitly requested with the `extended` keyword .. versionchanged:: 2.6.0 - Files are now writen in `wt` mode, and keep extensions, allowing + Files are now written in `wt` mode, and keep extensions, allowing for files to be written under compressed formats """ format = 'CRD' diff --git a/package/MDAnalysis/coordinates/PDBQT.py b/package/MDAnalysis/coordinates/PDBQT.py index 5543aebc5af..a941e65fa22 100644 --- a/package/MDAnalysis/coordinates/PDBQT.py +++ b/package/MDAnalysis/coordinates/PDBQT.py @@ -198,7 +198,7 @@ class PDBQTWriter(base.WriterBase): .. versionchanged:: 2.6.0 - Files are now writen in `wt` mode, and keep extensions, allowing + Files are now written in `wt` mode, and keep extensions, allowing for files to be written under compressed formats """ diff --git a/package/MDAnalysis/coordinates/PQR.py b/package/MDAnalysis/coordinates/PQR.py index 3c8b15f8c59..8ae92622e46 100644 --- a/package/MDAnalysis/coordinates/PQR.py +++ b/package/MDAnalysis/coordinates/PQR.py @@ -189,7 +189,7 @@ class PQRWriter(base.WriterBase): .. versionadded:: 0.9.0 .. versionchanged:: 2.6.0 - Files are now writen in `wt` mode, and keep extensions, allowing + Files are now written in `wt` mode, and keep extensions, allowing for files to be written under compressed formats """ format = 'PQR' diff --git a/package/MDAnalysis/lib/util.py b/package/MDAnalysis/lib/util.py index 927b9d62c69..fbbb9679340 100644 --- a/package/MDAnalysis/lib/util.py +++ b/package/MDAnalysis/lib/util.py @@ -338,9 +338,9 @@ def anyopen(datasource, mode='rt', reset=True): a file (from :class:`file` or :func:`open`) or a stream (e.g. from :func:`urllib2.urlopen` or :class:`io.StringIO`) mode: {'r', 'w', 'a'} (optional) - Open in r(ead), w(rite) or a(ppen) mode. This string is directly + Open in r(ead), w(rite) or a(ppend) mode. This string is directly passed to the file opening handler (either Python's openfe, bz2, or - gzip). More complex mode are supported if the file opening handler + gzip). More complex modes are supported if the file opening handler supports it. reset: bool (optional) try to read (`mode` 'r') the stream from the start