From e3bc2ae56d25be543e3ff6ea7d3a59236a10ff3c Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 5 Jun 2026 10:45:30 -0700 Subject: [PATCH 001/100] Added TODO --- package/MDAnalysis/fetch/TODO | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 package/MDAnalysis/fetch/TODO diff --git a/package/MDAnalysis/fetch/TODO b/package/MDAnalysis/fetch/TODO new file mode 100644 index 0000000000..fb2e1a6c30 --- /dev/null +++ b/package/MDAnalysis/fetch/TODO @@ -0,0 +1,10 @@ +TODO LIST + +1. Implement Fetcher, and StaticFetcher (with automatic caching) Classes +2. Replicate pdb/from_pdb() existing behavior using StaticFetcher +3. Try this for other databases +3a. MDDB +3b. + +Later: +Think about how to implement the DynamicFetcher Class (have it yield a generator) \ No newline at end of file From 87a7b9404ea992f3756ca300375461bac9a7114e Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 5 Jun 2026 10:59:16 -0700 Subject: [PATCH 002/100] Added Pseudocode for Fetchers based on blog post --- package/MDAnalysis/fetch/fetchers.py | 68 ++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 package/MDAnalysis/fetch/fetchers.py diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py new file mode 100644 index 0000000000..dedd350cfa --- /dev/null +++ b/package/MDAnalysis/fetch/fetchers.py @@ -0,0 +1,68 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding: utf-8 -*- +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 +# +# MDAnalysis --- https://www.mdanalysis.org +# Copyright (c) 2006-2017 The MDAnalysis Development Team and contributors +# (see the file AUTHORS for the full list of names) +# +# Released under the Lesser GNU Public Licence, v2.1 or any higher version +# +# Please cite your use of MDAnalysis in published work: +# +# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, M. N. Melo, S. L. Seyler, +# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein. +# MDAnalysis: A Python package for the rapid analysis of molecular dynamics +# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th +# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy. +# doi: 10.25080/majora-629e541a-00e +# +# 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 +# + +import csv +import sqlite3 +import pooch +from abc import ABC + + +class BaseFetcher(ABC): + """Blueprint Class for all Fetchers""" + + def __init__(self, base_url, progressbar): + self.base_url = base_url + self.progressbar = progressbar + + def fetch(self): + # Starts file retrieval workflow + # meant to be overriden by child class + pass + + +class StaticFetcher(BaseFetcher): + """Fetcher automatically downloads file in entirety and cache it to disk""" + + def __init__(self, cache_path): + super().__init__(base_url, progressbar) + self.base_url = base_url + self.progressbar = progressbar + self.cache_path = cache_path + + def fetch(self): + # Starts file retrieval workflow + pass + + def _write_db(self): + # Create/query hash file (either a csv or database file) + pass + + def _read_db(self): + # Check and loads hash (either a csv or database file) + pass + + +class DynamicFetcher(BaseFetcher): + """Fetcher yields a Python Generator for dynamic downloading and analysis""" + + pass From ad044ef54459c92a4a643530219c8d2624111a53 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 5 Jun 2026 14:12:09 -0700 Subject: [PATCH 003/100] updated private methods --- package/MDAnalysis/fetch/fetchers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index dedd350cfa..6ef998f671 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -53,11 +53,11 @@ def fetch(self): # Starts file retrieval workflow pass - def _write_db(self): + def _write_cache(self): # Create/query hash file (either a csv or database file) pass - def _read_db(self): + def _read_cache(self): # Check and loads hash (either a csv or database file) pass From 8fbd20369d538ff35e28283f20e07888621d24e4 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 5 Jun 2026 15:00:52 -0700 Subject: [PATCH 004/100] Added Dev Things --- package/MDAnalysis/fetch/TEST.py | 10 ++++++ package/MDAnalysis/fetch/__init__.py | 1 + package/MDAnalysis/fetch/fetchers.py | 54 ++++++++++++++++++++++------ 3 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 package/MDAnalysis/fetch/TEST.py diff --git a/package/MDAnalysis/fetch/TEST.py b/package/MDAnalysis/fetch/TEST.py new file mode 100644 index 0000000000..792a28f521 --- /dev/null +++ b/package/MDAnalysis/fetch/TEST.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +## File for testing changes +## Will get deleted when PR is DONE + + +import MDAnalysis as mda + +from MDAnalysis.fetch.fetchers import StaticFetcher +from MDAnalysis.fetch.pdb import from_PDB + diff --git a/package/MDAnalysis/fetch/__init__.py b/package/MDAnalysis/fetch/__init__.py index 1ebe1c8bd1..5f73ef4fdf 100644 --- a/package/MDAnalysis/fetch/__init__.py +++ b/package/MDAnalysis/fetch/__init__.py @@ -37,3 +37,4 @@ __all__ = ["from_PDB"] from .pdb import from_PDB +from .fetchers import StaticFetcher diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 6ef998f671..778cf32cd3 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -23,21 +23,50 @@ import csv import sqlite3 -import pooch -from abc import ABC +from abc import ABC, abstractmethod + +try: + import pooch +except ImportError: + HAS_POOCH = False +else: + HAS_POOCH = True + +DEFAULT_CACHE_NAME_DOWNLOADER = "MDAnalysis_pdbs" class BaseFetcher(ABC): """Blueprint Class for all Fetchers""" - def __init__(self, base_url, progressbar): + def __init__( + self, + base_url, + progressbar, + keep_session, + ): + self.base_url = base_url - self.progressbar = progressbar + self.progressbar = progressbar # Progressbar + self.keep_session # Connection Spooling - def fetch(self): + @abstractmethod + def fetch(self, timeout, retries): # Starts file retrieval workflow - # meant to be overriden by child class - pass + # + # All fetchers should call _check_pooch + + self.timeout = timeout # timeout + self.retries = retries # number of retries + self._check_pooch() + + def _check_pooch( + self, + ): + # Note that requests is a major dependency of pooch and is guaranteed to be installed + if not HAS_POOCH: + raise ModuleNotFoundError( + "pooch is needed as a dependency for Fetchers" + ) class StaticFetcher(BaseFetcher): @@ -45,14 +74,19 @@ class StaticFetcher(BaseFetcher): def __init__(self, cache_path): super().__init__(base_url, progressbar) - self.base_url = base_url - self.progressbar = progressbar - self.cache_path = cache_path + self.cache_path = _set_cache_path(cache_path) def fetch(self): # Starts file retrieval workflow pass + def _set_cache_path(self, cache_path): + + if cache_path is None: + return pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER) + else: + return cache_path + def _write_cache(self): # Create/query hash file (either a csv or database file) pass From c4c6b56243a49d5e96bc0d4ef69847aa83f7757b Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 5 Jun 2026 15:25:17 -0700 Subject: [PATCH 005/100] more dev stuff --- package/MDAnalysis/fetch/TEST.py | 11 +++++++ package/MDAnalysis/fetch/fetchers.py | 44 +++++++++++++++++++++------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/package/MDAnalysis/fetch/TEST.py b/package/MDAnalysis/fetch/TEST.py index 792a28f521..424a7090fb 100644 --- a/package/MDAnalysis/fetch/TEST.py +++ b/package/MDAnalysis/fetch/TEST.py @@ -8,3 +8,14 @@ from MDAnalysis.fetch.fetchers import StaticFetcher from MDAnalysis.fetch.pdb import from_PDB +import hashlib + + +print(hashlib.algorithms_available) +print(" ") +print(hashlib.algorithms_guaranteed) + + +s = StaticFetcher(keep_session=False) + +print(s.cache_path) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 778cf32cd3..57e5af05d8 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -40,21 +40,20 @@ class BaseFetcher(ABC): def __init__( self, - base_url, - progressbar, - keep_session, + keep_session=True, ): - self.base_url = base_url - self.progressbar = progressbar # Progressbar - self.keep_session # Connection Spooling + self.keep_session = keep_session # Connection Spooling @abstractmethod - def fetch(self, timeout, retries): + def fetch(self, base_url, timeout, retries): # Starts file retrieval workflow # # All fetchers should call _check_pooch + self.base_url = base_url + self.progressbar = progressbar # Progressbar + self.timeout = timeout # timeout self.retries = retries # number of retries self._check_pooch() @@ -72,12 +71,32 @@ def _check_pooch( class StaticFetcher(BaseFetcher): """Fetcher automatically downloads file in entirety and cache it to disk""" - def __init__(self, cache_path): - super().__init__(base_url, progressbar) - self.cache_path = _set_cache_path(cache_path) + def __init__(self, cache_path=None, hash="sha256", **kwargs): + super().__init__(kwargs["keep_session"]) + self.cache_path = self._set_cache_path(cache_path) + + # timeout, retries + self.hash = hash - def fetch(self): + def fetch( + self, + base_url, + progressbar, + timeout, + retries, + filename, + override=False, + ignore_hash=False, + ): # Starts file retrieval workflow + + self.filename = filename # If not none, then user can change otherwise use default + self.timeout = timeout # timeout (int) + self.retries = retries # number of retries (int) + self.override = override # Boolean to override files (download despite being present) + self._ignore_hash = ( + ignore_hash # If true, ignore hash and keep downloading + ) pass def _set_cache_path(self, cache_path): @@ -89,6 +108,9 @@ def _set_cache_path(self, cache_path): def _write_cache(self): # Create/query hash file (either a csv or database file) + # + # Not using Pooch.make_registry as that implements MD5 checksum which is not secure! + pass def _read_cache(self): From b921f538cdd776825a6270e987290f13df885ed7 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 5 Jun 2026 15:46:48 -0700 Subject: [PATCH 006/100] More Dev stuff yay --- package/MDAnalysis/fetch/TEST.py | 2 +- package/MDAnalysis/fetch/fetchers.py | 54 ++++++++++++++++++---------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/package/MDAnalysis/fetch/TEST.py b/package/MDAnalysis/fetch/TEST.py index 424a7090fb..e289a2cb51 100644 --- a/package/MDAnalysis/fetch/TEST.py +++ b/package/MDAnalysis/fetch/TEST.py @@ -16,6 +16,6 @@ print(hashlib.algorithms_guaranteed) -s = StaticFetcher(keep_session=False) +s = StaticFetcher(keep_session=False, hash='booger') print(s.cache_path) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 57e5af05d8..082d16dc6e 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -23,6 +23,7 @@ import csv import sqlite3 +import hashlib from abc import ABC, abstractmethod try: @@ -43,20 +44,20 @@ def __init__( keep_session=True, ): - self.keep_session = keep_session # Connection Spooling + self.reuse= reuse # Connection Pooling + @staticmethod @abstractmethod - def fetch(self, base_url, timeout, retries): + def fetch(self, base_url, progressbar, timeout, retries): # Starts file retrieval workflow # # All fetchers should call _check_pooch + self._check_pooch() self.base_url = base_url self.progressbar = progressbar # Progressbar - self.timeout = timeout # timeout self.retries = retries # number of retries - self._check_pooch() def _check_pooch( self, @@ -72,32 +73,41 @@ class StaticFetcher(BaseFetcher): """Fetcher automatically downloads file in entirety and cache it to disk""" def __init__(self, cache_path=None, hash="sha256", **kwargs): - super().__init__(kwargs["keep_session"]) - self.cache_path = self._set_cache_path(cache_path) + super().__init__(kwargs["reuse"]) - # timeout, retries - self.hash = hash + self.cache_path = self._set_cache_path(cache_path) + self.hash = self._check_hash_input(hash) + @staticmethod def fetch( - self, - base_url, - progressbar, - timeout, - retries, - filename, + filename=None, override=False, ignore_hash=False, + **kwargs, ): # Starts file retrieval workflow - self.filename = filename # If not none, then user can change otherwise use default - self.timeout = timeout # timeout (int) - self.retries = retries # number of retries (int) + self._check_pooch() + + ## All variable to used for method + + # ABC Fetcher variables (guarenteed to exists) + self.base_url = kwargs["base_url"] + self.progressbar = kwargs["progressbar"] # Progressbar + self.timeout = kwargs["timeout"] # timeout + self.retries = kwargs["retries"] # number of retries + + # Static Fetcher Specific variables + self.filename = ( + filename # If not none, then user can change otherwise use default + ) + self.override = override # Boolean to override files (download despite being present) self._ignore_hash = ( ignore_hash # If true, ignore hash and keep downloading ) - pass + + ## def _set_cache_path(self, cache_path): @@ -106,6 +116,14 @@ def _set_cache_path(self, cache_path): else: return cache_path + def _check_hash_input(self, hash): + if hash in hashlib.algorithms_available: + return hash + else: + raise ValueError( + f"Invalid hash \"{hash}\". Valid hashes algorithms are {hashlib.algorithms_available}. See 'hashlib.algorithms_available'" + ) + def _write_cache(self): # Create/query hash file (either a csv or database file) # From f76554ec77c2e2f6f8472ab9c338ef3bbf1daa7a Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 5 Jun 2026 15:46:48 -0700 Subject: [PATCH 007/100] More Dev stuff yay --- package/MDAnalysis/fetch/TEST.py | 2 +- package/MDAnalysis/fetch/fetchers.py | 54 ++++++++++++++++++---------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/package/MDAnalysis/fetch/TEST.py b/package/MDAnalysis/fetch/TEST.py index 424a7090fb..e289a2cb51 100644 --- a/package/MDAnalysis/fetch/TEST.py +++ b/package/MDAnalysis/fetch/TEST.py @@ -16,6 +16,6 @@ print(hashlib.algorithms_guaranteed) -s = StaticFetcher(keep_session=False) +s = StaticFetcher(keep_session=False, hash='booger') print(s.cache_path) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 57e5af05d8..a0fbe1829e 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -23,6 +23,7 @@ import csv import sqlite3 +import hashlib from abc import ABC, abstractmethod try: @@ -43,20 +44,20 @@ def __init__( keep_session=True, ): - self.keep_session = keep_session # Connection Spooling + self.reuse= reuse # Connection Pooling + @staticmethod @abstractmethod - def fetch(self, base_url, timeout, retries): + def fetch(self, base_url, progressbar, timeout, retries): # Starts file retrieval workflow # # All fetchers should call _check_pooch + self._check_pooch() self.base_url = base_url self.progressbar = progressbar # Progressbar - self.timeout = timeout # timeout self.retries = retries # number of retries - self._check_pooch() def _check_pooch( self, @@ -72,32 +73,41 @@ class StaticFetcher(BaseFetcher): """Fetcher automatically downloads file in entirety and cache it to disk""" def __init__(self, cache_path=None, hash="sha256", **kwargs): - super().__init__(kwargs["keep_session"]) - self.cache_path = self._set_cache_path(cache_path) + super().__init__(kwargs["reuse"]) - # timeout, retries - self.hash = hash + self.cache_path = self._set_cache_path(cache_path) + self.hash = self._check_hash_input(hash) + @staticmethod def fetch( - self, - base_url, - progressbar, - timeout, - retries, - filename, + filename=None, override=False, ignore_hash=False, + **kwargs, ): # Starts file retrieval workflow - self.filename = filename # If not none, then user can change otherwise use default - self.timeout = timeout # timeout (int) - self.retries = retries # number of retries (int) + self._check_pooch() + + ## All variable to used for method + + # ABC Fetcher variables (guaranteed to exist) + self.base_url = kwargs["base_url"] + self.progressbar = kwargs["progressbar"] # Progressbar + self.timeout = kwargs["timeout"] # timeout + self.retries = kwargs["retries"] # number of retries + + # Static Fetcher Specific variables + self.filename = ( + filename # If not none, then user can change otherwise use default + ) + self.override = override # Boolean to override files (download despite being present) self._ignore_hash = ( ignore_hash # If true, ignore hash and keep downloading ) - pass + + ## def _set_cache_path(self, cache_path): @@ -106,6 +116,14 @@ def _set_cache_path(self, cache_path): else: return cache_path + def _check_hash_input(self, hash): + if hash in hashlib.algorithms_available: + return hash + else: + raise ValueError( + f"Invalid hash \"{hash}\". Valid hashes algorithms are {hashlib.algorithms_available}. See 'hashlib.algorithms_available'" + ) + def _write_cache(self): # Create/query hash file (either a csv or database file) # From 48e89250153a527ac526fec776d3cf35d006080d Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 9 Jun 2026 16:32:50 -0700 Subject: [PATCH 008/100] Added workflow --- package/MDAnalysis/fetch/TEST.py | 25 +++--- package/MDAnalysis/fetch/fetchers.py | 110 ++++++++++++++++++++------- 2 files changed, 97 insertions(+), 38 deletions(-) diff --git a/package/MDAnalysis/fetch/TEST.py b/package/MDAnalysis/fetch/TEST.py index e289a2cb51..35595ea9fd 100644 --- a/package/MDAnalysis/fetch/TEST.py +++ b/package/MDAnalysis/fetch/TEST.py @@ -3,19 +3,26 @@ ## Will get deleted when PR is DONE -import MDAnalysis as mda +# import MDAnalysis as mda + +# from MDAnalysis.fetch.fetchers import StaticFetcher +# from MDAnalysis.fetch.pdb import from_PDB + +# import hashlib -from MDAnalysis.fetch.fetchers import StaticFetcher -from MDAnalysis.fetch.pdb import from_PDB -import hashlib +# print(hashlib.algorithms_available) +# print(" ") +# print(hashlib.algorithms_guaranteed) -print(hashlib.algorithms_available) -print(" ") -print(hashlib.algorithms_guaranteed) +# s = StaticFetcher(reuse_connection=False, hash='sha256') +# print(s.cache_path) + + +from MDAnalysis.fetch.fetchers import StaticFetcher -s = StaticFetcher(keep_session=False, hash='booger') +s = StaticFetcher() -print(s.cache_path) +s.fetch() \ No newline at end of file diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index a0fbe1829e..a05c065f62 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -24,6 +24,8 @@ import csv import sqlite3 import hashlib + +from pathlib import Path from abc import ABC, abstractmethod try: @@ -34,28 +36,29 @@ HAS_POOCH = True DEFAULT_CACHE_NAME_DOWNLOADER = "MDAnalysis_pdbs" - +ALLOWED_EXTENSIONS_DATABASE = {".csv", ".db"} class BaseFetcher(ABC): """Blueprint Class for all Fetchers""" def __init__( self, - keep_session=True, + reuse_connection=True, ): - self.reuse= reuse # Connection Pooling + self.pooled = reuse_connection # Connection Pooling - @staticmethod + @abstractmethod def fetch(self, base_url, progressbar, timeout, retries): # Starts file retrieval workflow # - # All fetchers should call _check_pooch + # All fetchers should call _check_pooch() self._check_pooch() + # Global Variable attributes self.base_url = base_url - self.progressbar = progressbar # Progressbar + self.verbose = progressbar # Progressbar self.timeout = timeout # timeout self.retries = retries # number of retries @@ -73,41 +76,78 @@ class StaticFetcher(BaseFetcher): """Fetcher automatically downloads file in entirety and cache it to disk""" def __init__(self, cache_path=None, hash="sha256", **kwargs): - super().__init__(kwargs["reuse"]) + + ## TODO put guard parameter from ABC Fetcher + #super().__init__(kwargs["reuse_connection"]) + super().__init__() self.cache_path = self._set_cache_path(cache_path) self.hash = self._check_hash_input(hash) - @staticmethod + def fetch( + self, filename=None, - override=False, + force=False, ignore_hash=False, + db_name="hashes.db", **kwargs, ): - # Starts file retrieval workflow - self._check_pooch() - ## All variable to used for method + ### + # ## All variable to used for methods + # # ABC Fetcher variables (guaranteed to exist) + # self.base_url = kwargs["base_url"] + # self.verbose = kwargs["progressbar"] # Progressbar + # self.timeout = kwargs["timeout"] # timeout + # self.retries = kwargs["retries"] # number of retries - # ABC Fetcher variables (guaranteed to exist) - self.base_url = kwargs["base_url"] - self.progressbar = kwargs["progressbar"] # Progressbar - self.timeout = kwargs["timeout"] # timeout - self.retries = kwargs["retries"] # number of retries + # # Static Fetcher Specific variables + # self.filename = ( + # filename # If not none, then user can change otherwise use default + # ) - # Static Fetcher Specific variables - self.filename = ( - filename # If not none, then user can change otherwise use default - ) + # self.override = force # Boolean to override files (download despite being present) + # self._ignore_hash = ( + # ignore_hash # If true, ignore hash and keep downloading + # ) + # ## + # ### - self.override = override # Boolean to override files (download despite being present) - self._ignore_hash = ( - ignore_hash # If true, ignore hash and keep downloading - ) - ## + ## Pseudocode + self._check_pooch() # Check dependencies + + + if db_name is not None: + + db_name = Path(db_name) + + if db_name.suffix.lower() not in ALLOWED_EXTENSIONS_DATABASE: + raise ValueError( + f"Database name should have one of these extensions: {ALLOWED_EXTENSIONS_DATABASE}" + ) + + + + ## TODO Workflow + ## Prequel: Start Connection Pooling with Server + # + # + # 1. Check filename or get file name (content-deposition) via HTTP GET + # 2. Check against database: + # 2a. Write database if doesn't exist (cancel with db_name=None) + # 2b. Check against database -> (_read_cache): + # Check header for file_name and hash (ONLY SUPPORT ONE TYPE OF HASH for maintainability sake) + # If mismatch with hash, toss excepetion + # If empty, contuine with download and write hash to database (_write_cache() + # + + + + + def _set_cache_path(self, cache_path): @@ -124,16 +164,28 @@ def _check_hash_input(self, hash): f"Invalid hash \"{hash}\". Valid hashes algorithms are {hashlib.algorithms_available}. See 'hashlib.algorithms_available'" ) - def _write_cache(self): + def _write_cache(self, db_path): # Create/query hash file (either a csv or database file) # # Not using Pooch.make_registry as that implements MD5 checksum which is not secure! pass - def _read_cache(self): + def _read_cache(self, db_path): # Check and loads hash (either a csv or database file) - pass + + breakpoint() + db_extension = db_path.suffix.lower() + + if db_extension == ".csv": + with open(db_extension, newline='') as csvfile: + file = csv.reader(csvfile, dialect='unix') + + + + elif db_extension == ".db": + pass + class DynamicFetcher(BaseFetcher): From fc05684fb7b69138aec9c19a85752aaf20bf1950 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 9 Jun 2026 16:42:33 -0700 Subject: [PATCH 009/100] Added more notes --- package/MDAnalysis/fetch/fetchers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 9752c2b67e..93dd48ec77 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -140,9 +140,11 @@ def fetch( # 2. Check against database: # 2a. Write database if doesn't exist (cancel with db_name=None) # 2b. Check against database -> (_read_cache): - # Check header for file_name and hash (ONLY SUPPORT ONE TYPE OF HASH for maintainability sake) - # If mismatch with hash, toss excepetion - # If empty, contuine with download and write hash to database (_write_cache() + # Check header for file_name and hash (ONLY SUPPORT ONE TYPE OF HASH per DB FILE for maintainability sake) + # If mismatch with hash, toss exception (override with ignore_hash -- PUT BIG WARNING IN THIS) + # If matchs, skip download and just return pathlib.Path() (override with force) + # If empty, contuine with download and write hash to database (_write_cache()) + # From b609fd6dbc89b31f927fd12b67a4ac3ef86f6939 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 10 Jun 2026 13:43:09 -0700 Subject: [PATCH 010/100] Reworked to used pooch functions instead of writing own --- package/MDAnalysis/TEST.py | 20 +++++ package/MDAnalysis/fetch/TEST.py | 28 ------- package/MDAnalysis/fetch/fetchers.py | 113 +++++++++++++++++++-------- 3 files changed, 102 insertions(+), 59 deletions(-) create mode 100644 package/MDAnalysis/TEST.py delete mode 100644 package/MDAnalysis/fetch/TEST.py diff --git a/package/MDAnalysis/TEST.py b/package/MDAnalysis/TEST.py new file mode 100644 index 0000000000..7eb70f2df4 --- /dev/null +++ b/package/MDAnalysis/TEST.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +## File for testing changes +## Will get deleted when PR is DONE + +import shutil + +from pathlib import Path +from MDAnalysis.fetch.fetchers import StaticFetcher + + + +#shutil.rmtree('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs') +test_path = Path('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs') +test_db = test_path / 'test.txt' + +s = StaticFetcher() +t = s.fetch(base_url='https://files.wwpdb.org/download/') + +print(t) + diff --git a/package/MDAnalysis/fetch/TEST.py b/package/MDAnalysis/fetch/TEST.py deleted file mode 100644 index 35595ea9fd..0000000000 --- a/package/MDAnalysis/fetch/TEST.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 -## File for testing changes -## Will get deleted when PR is DONE - - -# import MDAnalysis as mda - -# from MDAnalysis.fetch.fetchers import StaticFetcher -# from MDAnalysis.fetch.pdb import from_PDB - -# import hashlib - - -# print(hashlib.algorithms_available) -# print(" ") -# print(hashlib.algorithms_guaranteed) - - -# s = StaticFetcher(reuse_connection=False, hash='sha256') - -# print(s.cache_path) - - -from MDAnalysis.fetch.fetchers import StaticFetcher - -s = StaticFetcher() - -s.fetch() \ No newline at end of file diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 93dd48ec77..46a909e7f1 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -30,13 +30,13 @@ try: import pooch + import requests except ImportError: HAS_POOCH = False else: HAS_POOCH = True DEFAULT_CACHE_NAME_DOWNLOADER = "MDAnalysis_pdbs" -ALLOWED_EXTENSIONS_DATABASE = {".csv", ".db"} class BaseFetcher(ABC): """Blueprint Class for all Fetchers""" @@ -81,17 +81,16 @@ def __init__(self, cache_path=None, hash="sha256", **kwargs): #super().__init__(kwargs["reuse_connection"]) super().__init__() - self.cache_path = self._set_cache_path(cache_path) + self.cache_path = self._check_cache_path_input(cache_path) self.hash = self._check_hash_input(hash) - self.hash = self._check_hash_input(hash) - def fetch( self, + base_url, filename=None, force=False, ignore_hash=False, - db_name="hashes.db", + db_name="hashes.txt", **kwargs, ): @@ -120,39 +119,89 @@ def fetch( ## Pseudocode self._check_pooch() # Check dependencies - if db_name is not None: + HAS_DATABASE = True + self.db_path = self.cache_path / Path(db_name) + else: + HAS_DATABASE = False + self.db_path = None - db_name = Path(db_name) - - if db_name.suffix.lower() not in ALLOWED_EXTENSIONS_DATABASE: - raise ValueError( - f"Database name should have one of these extensions: {ALLOWED_EXTENSIONS_DATABASE}" + if HAS_DATABASE: + #self.db_path.parent.mkdir(parents=True, exist_ok=True) + #self._file_extension = self.db_path.suffix + + if not self.db_path.exists(): + CREATE_DATABASE = True + else: + CREATE_DATABASE = False + + + CREATE_DATABASE = True + if CREATE_DATABASE: # Load a None registry dictionary + registry_dictionary = { + '1AKE.pdb': None + } + + else: # Loads from file + #registry_dictionary = pooch.Pooch.load_registry(fname = (self.cache_path / 'test.txt')) + pass + + import ipdb; ipdb.set_trace() + + + + ## Should stilll be ok + downloader = pooch.create( + path=self.cache_path, + base_url=base_url, + registry=registry_dictionary, ) + downloader.load_registry(fname = (self.cache_path / 'test.txt')) + paths = [ + Path(downloader.fetch(fname=file_name, progressbar=True)) + for file_name in registry_dictionary.keys() + ] + + print(self.cache_path) + + ## Add guard block here ro make it work + pooch.make_registry(directory=self.cache_path, output=(self.cache_path / 'test.txt')) + if len(paths) == 1: + return paths[0] + else: + return paths + + ## SAVE to registry + + + + + + + + ## TODO Workflow ## Prequel: Start Connection Pooling with Server # # # 1. Check filename or get file name (content-deposition) via HTTP GET # 2. Check against database: - # 2a. Write database if doesn't exist (cancel with db_name=None) + # 2a. Write database if doesn't exist (override with db_name=None) # 2b. Check against database -> (_read_cache): # Check header for file_name and hash (ONLY SUPPORT ONE TYPE OF HASH per DB FILE for maintainability sake) # If mismatch with hash, toss exception (override with ignore_hash -- PUT BIG WARNING IN THIS) # If matchs, skip download and just return pathlib.Path() (override with force) # If empty, contuine with download and write hash to database (_write_cache()) - # - - + # Note replace with pooch instead - + # - def _set_cache_path(self, cache_path): + def _check_cache_path_input(self, cache_path): if cache_path is None: return pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER) @@ -167,27 +216,29 @@ def _check_hash_input(self, hash): f"Invalid hash \"{hash}\". Valid hashes algorithms are {hashlib.algorithms_available}. See 'hashlib.algorithms_available'" ) - def _write_cache(self, db_path): - # Create/query hash file (either a csv or database file) - # - # Not using Pooch.make_registry as that implements MD5 checksum which is not secure! - pass + def _create_database(self): + + self.db_path.parent.mkdir(parents=True,exist_ok=True) + ## CSV + with self.db_path.open(mode='x') as f: + writer = csv.writer(f) + writer.writerow(['File', f'Hash:{self.hash}']) - def _read_cache(self, db_path): - # Check and loads hash (either a csv or database file) - breakpoint() - db_extension = db_path.suffix.lower() + def _read_database(self, db_path): + # Check and loads hash (either a csv or database file) + pass - if db_extension == ".csv": - with open(db_extension, newline='') as csvfile: - file = csv.reader(csvfile, dialect='unix') + def _write_database(self, db_path): + # Create/query hash file (either a csv or database file) + # + # Not using Pooch.make_registry as that implements MD5 checksum which is not secure! + pass - elif db_extension == ".db": - pass + From 93e777c98cc139b6c5384c02d9b07ce093c35050 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 11 Jun 2026 14:27:12 -0700 Subject: [PATCH 011/100] Works but clunky! --- package/MDAnalysis/TEST.py | 18 ++-- package/MDAnalysis/fetch/fetchers.py | 142 ++++++++------------------- 2 files changed, 54 insertions(+), 106 deletions(-) diff --git a/package/MDAnalysis/TEST.py b/package/MDAnalysis/TEST.py index 7eb70f2df4..1169cd3381 100644 --- a/package/MDAnalysis/TEST.py +++ b/package/MDAnalysis/TEST.py @@ -3,18 +3,24 @@ ## Will get deleted when PR is DONE import shutil +import os from pathlib import Path from MDAnalysis.fetch.fetchers import StaticFetcher -#shutil.rmtree('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs') -test_path = Path('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs') -test_db = test_path / 'test.txt' +shutil.rmtree('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs', ignore_errors=True) +#test_path = Path('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs') +#test_db = test_path / 'test.txt' -s = StaticFetcher() -t = s.fetch(base_url='https://files.wwpdb.org/download/') +downloader = StaticFetcher() +path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name='1AKE.pdb', db_name='test.txt') -print(t) +print(path) +print('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs') +print(os.listdir('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs')) + +print('trying cache') +path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name='1AKE.pdb', db_name='test.txt') diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 46a909e7f1..7231d271ee 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -21,8 +21,6 @@ # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # -import csv -import sqlite3 import hashlib from pathlib import Path @@ -30,7 +28,6 @@ try: import pooch - import requests except ImportError: HAS_POOCH = False else: @@ -83,11 +80,12 @@ def __init__(self, cache_path=None, hash="sha256", **kwargs): self.cache_path = self._check_cache_path_input(cache_path) self.hash = self._check_hash_input(hash) + self.db_path = None def fetch( self, base_url, - filename=None, + file_name=None, force=False, ignore_hash=False, db_name="hashes.txt", @@ -104,8 +102,8 @@ def fetch( # self.retries = kwargs["retries"] # number of retries # # Static Fetcher Specific variables - # self.filename = ( - # filename # If not none, then user can change otherwise use default + # self.file_name = ( + # file_name # If not none, then user can change otherwise use default # ) # self.override = force # Boolean to override files (download despite being present) @@ -115,94 +113,64 @@ def fetch( # ## # ### + self._check_pooch() - ## Pseudocode - self._check_pooch() # Check dependencies - - if db_name is not None: - HAS_DATABASE = True + CREATE_DATABASE = False + if db_name is not None: # HAS DATABASE self.db_path = self.cache_path / Path(db_name) - else: - HAS_DATABASE = False - self.db_path = None - - if HAS_DATABASE: - #self.db_path.parent.mkdir(parents=True, exist_ok=True) - #self._file_extension = self.db_path.suffix - if not self.db_path.exists(): CREATE_DATABASE = True - else: - CREATE_DATABASE = False - - CREATE_DATABASE = True - if CREATE_DATABASE: # Load a None registry dictionary - registry_dictionary = { - '1AKE.pdb': None - } + if CREATE_DATABASE: # Load a None registry dictionary (bc of no cache) + print('creating') + registry_dictionary = { + file_name : None + } - else: # Loads from file - #registry_dictionary = pooch.Pooch.load_registry(fname = (self.cache_path / 'test.txt')) - pass + registry=registry_dictionary - import ipdb; ipdb.set_trace() - - - - ## Should stilll be ok downloader = pooch.create( path=self.cache_path, base_url=base_url, - registry=registry_dictionary, + registry=registry_dictionary ) - downloader.load_registry(fname = (self.cache_path / 'test.txt')) - paths = [ - Path(downloader.fetch(fname=file_name, progressbar=True)) - for file_name in registry_dictionary.keys() - ] - - print(self.cache_path) - - ## Add guard block here ro make it work - pooch.make_registry(directory=self.cache_path, output=(self.cache_path / 'test.txt')) - if len(paths) == 1: - return paths[0] - else: - return paths - - ## SAVE to registry - - - - - - + else: # Loads from file + print('loading') + registry=open(self.db_path, mode='r') + registry_dictionary = {} + with open(self.db_path, mode='r') as f: + for line in f: + key, value = line.strip().split() + registry_dictionary[key] = value + downloader = pooch.create( + path=self.cache_path, + base_url=base_url, + registry=registry_dictionary + ) - ## TODO Workflow - ## Prequel: Start Connection Pooling with Server - # - # - # 1. Check filename or get file name (content-deposition) via HTTP GET - # 2. Check against database: - # 2a. Write database if doesn't exist (override with db_name=None) - # 2b. Check against database -> (_read_cache): - # Check header for file_name and hash (ONLY SUPPORT ONE TYPE OF HASH per DB FILE for maintainability sake) - # If mismatch with hash, toss exception (override with ignore_hash -- PUT BIG WARNING IN THIS) - # If matchs, skip download and just return pathlib.Path() (override with force) - # If empty, contuine with download and write hash to database (_write_cache()) + import ipdb; ipdb.set_trace() - # Note replace with pooch instead - # + + + paths = [ + Path(downloader.fetch(fname=file_name, progressbar=True)) + for file_name in registry_dictionary.keys() + ] + + if CREATE_DATABASE: + pooch.make_registry(directory=self.cache_path, output=self.db_path) + if len(paths) == 1: + return paths[0] + else: + return paths def _check_cache_path_input(self, cache_path): - if cache_path is None: return pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER) else: @@ -213,35 +181,9 @@ def _check_hash_input(self, hash): return hash else: raise ValueError( - f"Invalid hash \"{hash}\". Valid hashes algorithms are {hashlib.algorithms_available}. See 'hashlib.algorithms_available'" + f"Invalid hash \"{hash}\". Valid hashes algorithms are {hashlib.algorithms_available}." ) - - - def _create_database(self): - - self.db_path.parent.mkdir(parents=True,exist_ok=True) - ## CSV - with self.db_path.open(mode='x') as f: - writer = csv.writer(f) - writer.writerow(['File', f'Hash:{self.hash}']) - - - def _read_database(self, db_path): - # Check and loads hash (either a csv or database file) - pass - - - def _write_database(self, db_path): - # Create/query hash file (either a csv or database file) - # - # Not using Pooch.make_registry as that implements MD5 checksum which is not secure! - - pass - - - - class DynamicFetcher(BaseFetcher): """Fetcher yields a Python Generator for dynamic downloading and analysis""" From dbd62be633112f5ba08b8958dfce07fb3342d3a8 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 11 Jun 2026 14:48:23 -0700 Subject: [PATCH 012/100] Updated TEST.py --- package/MDAnalysis/TEST.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/package/MDAnalysis/TEST.py b/package/MDAnalysis/TEST.py index 1169cd3381..151881abb8 100644 --- a/package/MDAnalysis/TEST.py +++ b/package/MDAnalysis/TEST.py @@ -9,18 +9,22 @@ from MDAnalysis.fetch.fetchers import StaticFetcher +DEFAULT_CACHE_FOLDER='/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs' +downloader = StaticFetcher() + +## Inital Download Case (create db) +print("Inital Download Case (create db)\n") +shutil.rmtree(DEFAULT_CACHE_FOLDER, ignore_errors=True) +path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name='1AKE.pdb', db_name='test.txt') -shutil.rmtree('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs', ignore_errors=True) -#test_path = Path('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs') -#test_db = test_path / 'test.txt' +## Cache Case (has DB) +print('Cache Case (has DB)\n') downloader = StaticFetcher() path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name='1AKE.pdb', db_name='test.txt') -print(path) -print('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs') -print(os.listdir('/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs')) - -print('trying cache') -path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name='1AKE.pdb', db_name='test.txt') +## No Database (just download) +print('No Database (just download)\n') +shutil.rmtree(DEFAULT_CACHE_FOLDER, ignore_errors=True) +path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name='1AKE.pdb', db_name=None) From f3837aadba179adddaf43d891c4840af771bb41f Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 11 Jun 2026 15:01:36 -0700 Subject: [PATCH 013/100] Working single file fetch --- package/MDAnalysis/fetch/fetchers.py | 53 ++++++++++++---------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 7231d271ee..225e9fc3fd 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -115,48 +115,39 @@ def fetch( self._check_pooch() + + registry_dictionary = {} + LOAD_FROM_CACHE = False CREATE_DATABASE = False - if db_name is not None: # HAS DATABASE + + if db_name is not None: self.db_path = self.cache_path / Path(db_name) - if not self.db_path.exists(): - CREATE_DATABASE = True - - if CREATE_DATABASE: # Load a None registry dictionary (bc of no cache) - print('creating') - registry_dictionary = { - file_name : None - } - - registry=registry_dictionary - - downloader = pooch.create( - path=self.cache_path, - base_url=base_url, - registry=registry_dictionary - ) - else: # Loads from file - print('loading') - registry=open(self.db_path, mode='r') - - registry_dictionary = {} + if self.db_path.exists(): + LOAD_FROM_CACHE = True + else: + CREATE_DATABASE = True + if LOAD_FROM_CACHE: + # Reads pooch registry file format + # https://www.fatiando.org/pooch/latest/registry-files.html#registry-file-format with open(self.db_path, mode='r') as f: for line in f: key, value = line.strip().split() registry_dictionary[key] = value - - downloader = pooch.create( - path=self.cache_path, - base_url=base_url, - registry=registry_dictionary - ) - import ipdb; ipdb.set_trace() + else: # No Database (just download) + registry_dictionary = { + file_name : None + } - - + downloader = pooch.create( + path=self.cache_path, + base_url=base_url, + registry=registry_dictionary + ) + paths = [ Path(downloader.fetch(fname=file_name, progressbar=True)) for file_name in registry_dictionary.keys() From cefb95f3372ffc6d60e7113f308b30c2ae59fbf0 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 11 Jun 2026 15:10:30 -0700 Subject: [PATCH 014/100] Tests to fix for tomorrow --- package/MDAnalysis/TEST.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/package/MDAnalysis/TEST.py b/package/MDAnalysis/TEST.py index 151881abb8..d8b8da61ee 100644 --- a/package/MDAnalysis/TEST.py +++ b/package/MDAnalysis/TEST.py @@ -28,3 +28,9 @@ print('No Database (just download)\n') shutil.rmtree(DEFAULT_CACHE_FOLDER, ignore_errors=True) path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name='1AKE.pdb', db_name=None) + +## Multiple downloads (create database) (not working) +print('Multiple downloads (create database)\n') +shutil.rmtree(DEFAULT_CACHE_FOLDER, ignore_errors=True) +path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name=('1AKE.pdb', '4AKE.pdb'), db_name='test.txt') + From e175d6ef48ba2c25f500b64c7dbec633c583b01f Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 11 Jun 2026 15:17:41 -0700 Subject: [PATCH 015/100] working test case --- package/MDAnalysis/fetch/fetchers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 225e9fc3fd..d5e58944d0 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -138,8 +138,13 @@ def fetch( else: # No Database (just download) + if isinstance(file_name, str): + _file_name = (file_name,) + else: + _file_name = file_name + registry_dictionary = { - file_name : None + name: None for name in _file_name } downloader = pooch.create( From 41d52be06065fe428b031bbae914a9f8e4ee7498 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 11 Jun 2026 15:21:13 -0700 Subject: [PATCH 016/100] Working! Need to refactor --- package/MDAnalysis/TEST.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/package/MDAnalysis/TEST.py b/package/MDAnalysis/TEST.py index d8b8da61ee..15eee81b0f 100644 --- a/package/MDAnalysis/TEST.py +++ b/package/MDAnalysis/TEST.py @@ -27,10 +27,18 @@ ## No Database (just download) print('No Database (just download)\n') shutil.rmtree(DEFAULT_CACHE_FOLDER, ignore_errors=True) +downloader = StaticFetcher() path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name='1AKE.pdb', db_name=None) -## Multiple downloads (create database) (not working) +## Multiple downloads (create database) print('Multiple downloads (create database)\n') shutil.rmtree(DEFAULT_CACHE_FOLDER, ignore_errors=True) +downloader = StaticFetcher() path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name=('1AKE.pdb', '4AKE.pdb'), db_name='test.txt') + +## Multiple downloads (has database) (not working) +print('Multiple downloads (has database)\n') +downloader = StaticFetcher() +import ipdb; ipdb.set_trace() +path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name=('1AKE.pdb', '4AKE.pdb'), db_name='test.txt') \ No newline at end of file From 1fb4857d9a539373155c3f68af9c8fec37d5ef3c Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 11 Jun 2026 15:21:26 -0700 Subject: [PATCH 017/100] Working! Need to refactor --- package/MDAnalysis/TEST.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/MDAnalysis/TEST.py b/package/MDAnalysis/TEST.py index 15eee81b0f..4f329e779c 100644 --- a/package/MDAnalysis/TEST.py +++ b/package/MDAnalysis/TEST.py @@ -37,7 +37,7 @@ path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name=('1AKE.pdb', '4AKE.pdb'), db_name='test.txt') -## Multiple downloads (has database) (not working) +## Multiple downloads (has database) print('Multiple downloads (has database)\n') downloader = StaticFetcher() import ipdb; ipdb.set_trace() From 0b8b439b095266132815eff2b5ca91a4a4ff8628 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 16 Jun 2026 08:12:05 -0700 Subject: [PATCH 018/100] test for gh-ci.yaml --- .github/workflows/gh-ci.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/gh-ci.yaml b/.github/workflows/gh-ci.yaml index 99894ca434..dd4af990d7 100644 --- a/.github/workflows/gh-ci.yaml +++ b/.github/workflows/gh-ci.yaml @@ -21,7 +21,6 @@ defaults: jobs: main_tests: - if: "github.repository == 'MDAnalysis/mdanalysis'" runs-on: ${{ matrix.os }} timeout-minutes: 60 strategy: From 3ad1d008d62cc1437218b9dc7042497414aa297d Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 17 Jun 2026 12:09:37 -0700 Subject: [PATCH 019/100] Added pooch Downloader Support --- package/MDAnalysis/TEST.py | 1 - package/MDAnalysis/fetch/fetchers.py | 104 +++++++++++++-------------- 2 files changed, 52 insertions(+), 53 deletions(-) diff --git a/package/MDAnalysis/TEST.py b/package/MDAnalysis/TEST.py index 4f329e779c..3be59c516a 100644 --- a/package/MDAnalysis/TEST.py +++ b/package/MDAnalysis/TEST.py @@ -40,5 +40,4 @@ ## Multiple downloads (has database) print('Multiple downloads (has database)\n') downloader = StaticFetcher() -import ipdb; ipdb.set_trace() path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name=('1AKE.pdb', '4AKE.pdb'), db_name='test.txt') \ No newline at end of file diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index d5e58944d0..ee26080c6c 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -34,18 +34,18 @@ HAS_POOCH = True DEFAULT_CACHE_NAME_DOWNLOADER = "MDAnalysis_pdbs" +DEFAULT_TIMEOUT = 10 +DEFAULT_RETRIES = 2 + class BaseFetcher(ABC): """Blueprint Class for all Fetchers""" def __init__( self, - reuse_connection=True, ): + pass - self.pooled = reuse_connection # Connection Pooling - - @abstractmethod def fetch(self, base_url, progressbar, timeout, retries): # Starts file retrieval workflow @@ -53,7 +53,7 @@ def fetch(self, base_url, progressbar, timeout, retries): # All fetchers should call _check_pooch() self._check_pooch() - # Global Variable attributes + # Global Variable attributes self.base_url = base_url self.verbose = progressbar # Progressbar self.timeout = timeout # timeout @@ -68,15 +68,25 @@ def _check_pooch( "pooch is needed as a dependency for Fetchers" ) + def _validate_fetch_args(self, args): + """Checks to see if @abstractmethod fetch parameters are initalized correctly""" + + if "base_url" not in args: + raise ValueError("base_url is not defined in fetch()") + + args.setdefault("progressbar", False) + args.setdefault("timeout", DEFAULT_TIMEOUT) + args.setdefault("retries", DEFAULT_RETRIES) + + return args + class StaticFetcher(BaseFetcher): """Fetcher automatically downloads file in entirety and cache it to disk""" - def __init__(self, cache_path=None, hash="sha256", **kwargs): + def __init__(self, cache_path=None, hash="sha256"): - ## TODO put guard parameter from ABC Fetcher - #super().__init__(kwargs["reuse_connection"]) - super().__init__() + self._check_pooch() self.cache_path = self._check_cache_path_input(cache_path) self.hash = self._check_hash_input(hash) @@ -84,43 +94,20 @@ def __init__(self, cache_path=None, hash="sha256", **kwargs): def fetch( self, - base_url, file_name=None, force=False, ignore_hash=False, db_name="hashes.txt", + downloader='HTTP', **kwargs, ): - - - ### - # ## All variable to used for methods - # # ABC Fetcher variables (guaranteed to exist) - # self.base_url = kwargs["base_url"] - # self.verbose = kwargs["progressbar"] # Progressbar - # self.timeout = kwargs["timeout"] # timeout - # self.retries = kwargs["retries"] # number of retries - - # # Static Fetcher Specific variables - # self.file_name = ( - # file_name # If not none, then user can change otherwise use default - # ) - - # self.override = force # Boolean to override files (download despite being present) - # self._ignore_hash = ( - # ignore_hash # If true, ignore hash and keep downloading - # ) - # ## - # ### - - self._check_pooch() - + kwargs = self._validate_fetch_args(kwargs) registry_dictionary = {} LOAD_FROM_CACHE = False CREATE_DATABASE = False - - if db_name is not None: + + if db_name is not None: self.db_path = self.cache_path / Path(db_name) if self.db_path.exists(): @@ -131,33 +118,45 @@ def fetch( if LOAD_FROM_CACHE: # Reads pooch registry file format # https://www.fatiando.org/pooch/latest/registry-files.html#registry-file-format - with open(self.db_path, mode='r') as f: + with open(self.db_path, mode="r") as f: for line in f: key, value = line.strip().split() registry_dictionary[key] = value - - else: # No Database (just download) + else: # No Database (just download) + # This block of code allows file_name to be a tuple instead of a string if isinstance(file_name, str): _file_name = (file_name,) else: _file_name = file_name - registry_dictionary = { - name: None for name in _file_name - } + registry_dictionary = {name: None for name in _file_name} + - downloader = pooch.create( + main_downloader = pooch.create( path=self.cache_path, - base_url=base_url, - registry=registry_dictionary + base_url=kwargs["base_url"], + registry=registry_dictionary, + retry_if_failed=kwargs["retries"], ) - + + match downloader: + case 'HTTP': + fetch_downloader = pooch.HTTPDownloader(**download_kwargs) + case 'FTP': + fetch_downloader = pooch.FTPDownloader(**download_kwargs) + case 'SFTP': + fetch_downloader = pooch.SFTPDownloader(**download_kwargs) + case 'DOI': + fetch_downloader = pooch.DOIDownloader(**download_kwargs) + case _: + raise ValueError(f"Invalid downloader '{downloader}'. Valid options are 'HTTP', 'FTP', 'SFTP', 'DOI'.") + paths = [ - Path(downloader.fetch(fname=file_name, progressbar=True)) + Path(main_downloader.fetch(fname=file_name, progressbar=True, downloader=fetch_downloader)) for file_name in registry_dictionary.keys() ] - + if CREATE_DATABASE: pooch.make_registry(directory=self.cache_path, output=self.db_path) @@ -168,18 +167,19 @@ def fetch( def _check_cache_path_input(self, cache_path): if cache_path is None: - return pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER) + return Path(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER)) else: - return cache_path + return Path(cache_path) def _check_hash_input(self, hash): if hash in hashlib.algorithms_available: return hash else: raise ValueError( - f"Invalid hash \"{hash}\". Valid hashes algorithms are {hashlib.algorithms_available}." + f'Invalid hash "{hash}". Valid hashes algorithms are {hashlib.algorithms_available}.' ) - + + class DynamicFetcher(BaseFetcher): """Fetcher yields a Python Generator for dynamic downloading and analysis""" From 15e4ff9ed9ac088d6936a23fc3185c0d46e42d9c Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 17 Jun 2026 12:11:12 -0700 Subject: [PATCH 020/100] applied black --- package/MDAnalysis/fetch/fetchers.py | 29 +++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index ee26080c6c..eee48cd310 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -98,7 +98,7 @@ def fetch( force=False, ignore_hash=False, db_name="hashes.txt", - downloader='HTTP', + downloader="HTTP", **kwargs, ): kwargs = self._validate_fetch_args(kwargs) @@ -124,14 +124,13 @@ def fetch( registry_dictionary[key] = value else: # No Database (just download) - # This block of code allows file_name to be a tuple instead of a string + # This block of code allows file_name to be a tuple instead of a string if isinstance(file_name, str): _file_name = (file_name,) else: _file_name = file_name registry_dictionary = {name: None for name in _file_name} - main_downloader = pooch.create( path=self.cache_path, @@ -140,20 +139,32 @@ def fetch( retry_if_failed=kwargs["retries"], ) + download_kwargs = kwargs.copy() + download_kwargs.pop("base_url") + download_kwargs.pop("retries") + match downloader: - case 'HTTP': + case "HTTP": fetch_downloader = pooch.HTTPDownloader(**download_kwargs) - case 'FTP': + case "FTP": fetch_downloader = pooch.FTPDownloader(**download_kwargs) - case 'SFTP': + case "SFTP": fetch_downloader = pooch.SFTPDownloader(**download_kwargs) - case 'DOI': + case "DOI": fetch_downloader = pooch.DOIDownloader(**download_kwargs) case _: - raise ValueError(f"Invalid downloader '{downloader}'. Valid options are 'HTTP', 'FTP', 'SFTP', 'DOI'.") + raise ValueError( + f"Invalid downloader '{downloader}'. Valid options are 'HTTP', 'FTP', 'SFTP', 'DOI'." + ) paths = [ - Path(main_downloader.fetch(fname=file_name, progressbar=True, downloader=fetch_downloader)) + Path( + main_downloader.fetch( + fname=file_name, + progressbar=True, + downloader=fetch_downloader, + ) + ) for file_name in registry_dictionary.keys() ] From b8de1ebc2d06a0929c4027962aac98e802964884 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 17 Jun 2026 12:38:45 -0700 Subject: [PATCH 021/100] Created Test Suite --- package/MDAnalysis/TEST.py | 43 ----- .../fetch/test_static_fetcher.py | 177 ++++++++++++++++++ 2 files changed, 177 insertions(+), 43 deletions(-) delete mode 100644 package/MDAnalysis/TEST.py create mode 100644 testsuite/MDAnalysisTests/fetch/test_static_fetcher.py diff --git a/package/MDAnalysis/TEST.py b/package/MDAnalysis/TEST.py deleted file mode 100644 index 3be59c516a..0000000000 --- a/package/MDAnalysis/TEST.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -## File for testing changes -## Will get deleted when PR is DONE - -import shutil -import os - -from pathlib import Path -from MDAnalysis.fetch.fetchers import StaticFetcher - - -DEFAULT_CACHE_FOLDER='/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs' -downloader = StaticFetcher() - -## Inital Download Case (create db) -print("Inital Download Case (create db)\n") -shutil.rmtree(DEFAULT_CACHE_FOLDER, ignore_errors=True) -path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name='1AKE.pdb', db_name='test.txt') - - -## Cache Case (has DB) -print('Cache Case (has DB)\n') -downloader = StaticFetcher() -path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name='1AKE.pdb', db_name='test.txt') - - -## No Database (just download) -print('No Database (just download)\n') -shutil.rmtree(DEFAULT_CACHE_FOLDER, ignore_errors=True) -downloader = StaticFetcher() -path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name='1AKE.pdb', db_name=None) - -## Multiple downloads (create database) -print('Multiple downloads (create database)\n') -shutil.rmtree(DEFAULT_CACHE_FOLDER, ignore_errors=True) -downloader = StaticFetcher() -path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name=('1AKE.pdb', '4AKE.pdb'), db_name='test.txt') - - -## Multiple downloads (has database) -print('Multiple downloads (has database)\n') -downloader = StaticFetcher() -path = downloader.fetch(base_url='https://files.wwpdb.org/download/', file_name=('1AKE.pdb', '4AKE.pdb'), db_name='test.txt') \ No newline at end of file diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py new file mode 100644 index 0000000000..a828ea73d8 --- /dev/null +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -0,0 +1,177 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*- +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8 +# +# MDAnalysis --- https://www.mdanalysis.org +# Copyright (c) 2006-2017 The MDAnalysis Development Team and contributors +# (see the file AUTHORS for the full list of names) +# +# Released under the Lesser GNU Public Licence, v2.1 or any higher version +# +# Please cite your use of MDAnalysis in published work: +# +# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, N. M. Melo, S. L. Seyler, +# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein. +# MDAnalysis: A Python package for the rapid analysis of molecular dynamics +# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th +# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy. +# doi: 10.25080/majora-629e541a-00e +# +# 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 +# + +from pathlib import Path +import re +from shutil import rmtree +from urllib import request + +import pytest + +from MDAnalysis.fetch.fetchers import ( + DEFAULT_CACHE_NAME_DOWNLOADER, + HAS_POOCH, + StaticFetcher, +) + +if HAS_POOCH: + import pooch + +try: + request.urlopen("https://files.wwpdb.org/", timeout=2) + HAS_ACCESS_TO_WWPDB = True +except request.URLError: + HAS_ACCESS_TO_WWPDB = False + + +BASE_URL = "https://files.wwpdb.org/download/" +SINGLE_PDB = "1AKE.pdb" +MULTIPLE_PDBS = ("1AKE.pdb", "4AKE.pdb") +REGISTRY_NAME = "test_db.txt" + + +@pytest.fixture() +def clean_up_default_cache(): + rmtree(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER), ignore_errors=True) + yield + rmtree(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER), ignore_errors=True) + + +@pytest.mark.skipif(HAS_POOCH, reason="Pooch is installed.") +def test_pooch_installation(): + with pytest.raises( + ModuleNotFoundError, + match="pooch is needed as a dependency for Fetchers", + ): + StaticFetcher() + + +@pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") +class TestExpectedErrors: + + def test_missing_base_url(self, tmp_path): + downloader = StaticFetcher(cache_path=tmp_path) + + with pytest.raises( + ValueError, match=re.escape("base_url is not defined in fetch()") + ): + downloader.fetch(file_name=SINGLE_PDB) + + def test_invalid_downloader(self, tmp_path): + downloader = StaticFetcher(cache_path=tmp_path) + + with pytest.raises( + ValueError, + match=re.escape( + "Invalid downloader 'barfoo'. Valid options are " + "'HTTP', 'FTP', 'SFTP', 'DOI'." + ), + ): + downloader.fetch( + base_url=BASE_URL, + file_name=SINGLE_PDB, + downloader="barfoo", + ) + + def test_invalid_hash(self, tmp_path): + with pytest.raises(ValueError, match='Invalid hash "barfoo"'): + StaticFetcher(cache_path=tmp_path, hash="barfoo") + + +@pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") +@pytest.mark.skipif( + not HAS_ACCESS_TO_WWPDB, + reason="Can not connect to https://files.wwpdb.org/", +) +class TestExpectedBehaviors: + + def test_default_cache_path(self, clean_up_default_cache): + downloader = StaticFetcher() + path = downloader.fetch(base_url=BASE_URL, file_name=SINGLE_PDB) + + assert isinstance(path, Path) + assert path.name == SINGLE_PDB + assert path.exists() + + def test_create_database(self, tmp_path): + downloader = StaticFetcher(cache_path=tmp_path) + path = downloader.fetch( + base_url=BASE_URL, file_name=SINGLE_PDB, db_name=REGISTRY_NAME + ) + + assert isinstance(path, Path) + assert path.name == SINGLE_PDB + assert path.exists() + assert (tmp_path / REGISTRY_NAME).exists() + + def test_existing_database(self, tmp_path): + downloader = StaticFetcher(cache_path=tmp_path) + downloader.fetch( + base_url=BASE_URL, file_name=SINGLE_PDB, db_name=REGISTRY_NAME + ) + + downloader = StaticFetcher(cache_path=tmp_path) + path = downloader.fetch( + base_url=BASE_URL, file_name=SINGLE_PDB, db_name=REGISTRY_NAME + ) + + assert isinstance(path, Path) + assert path.name == SINGLE_PDB + assert path.exists() + + def test_no_database(self, tmp_path): + downloader = StaticFetcher(cache_path=tmp_path) + path = downloader.fetch( + base_url=BASE_URL, file_name=SINGLE_PDB, db_name=None + ) + + assert isinstance(path, Path) + assert path.name == SINGLE_PDB + assert path.exists() + assert not (tmp_path / REGISTRY_NAME).exists() + + def test_multiple_downloads_create_database(self, tmp_path): + downloader = StaticFetcher(cache_path=tmp_path) + paths = downloader.fetch( + base_url=BASE_URL, file_name=MULTIPLE_PDBS, db_name=REGISTRY_NAME + ) + + assert all(isinstance(path, Path) for path in paths) + assert [path.name for path in paths] == list(MULTIPLE_PDBS) + assert all(path.exists() for path in paths) + assert (tmp_path / REGISTRY_NAME).exists() + + def test_multiple_downloads_existing_database(self, tmp_path): + downloader = StaticFetcher(cache_path=tmp_path) + downloader.fetch( + base_url=BASE_URL, file_name=MULTIPLE_PDBS, db_name=REGISTRY_NAME + ) + + downloader = StaticFetcher(cache_path=tmp_path) + paths = downloader.fetch( + base_url=BASE_URL, file_name=MULTIPLE_PDBS, db_name=REGISTRY_NAME + ) + + assert all(isinstance(path, Path) for path in paths) + assert [path.name for path in paths] == list(MULTIPLE_PDBS) + assert all(path.exists() for path in paths) From 9312486cc16095f18518a6a314dc165e35f919ab Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 17 Jun 2026 13:58:35 -0700 Subject: [PATCH 022/100] Finalized code -- test still need to be written --- package/MDAnalysis/TEST.py | 10 ++ package/MDAnalysis/fetch/fetchers.py | 7 +- package/MDAnalysis/fetch/pdb.py | 39 ++---- .../MDAnalysisTests/fetch/test_from_PDB.py | 131 +++++------------- .../fetch/test_static_fetcher.py | 3 + 5 files changed, 60 insertions(+), 130 deletions(-) create mode 100644 package/MDAnalysis/TEST.py diff --git a/package/MDAnalysis/TEST.py b/package/MDAnalysis/TEST.py new file mode 100644 index 0000000000..2b97e07c5c --- /dev/null +++ b/package/MDAnalysis/TEST.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 + +from MDAnalysis.fetch.pdb import from_PDB + +import shutil + +shutil.rmtree("/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs", ignore_errors=True) + +print(from_PDB(['1AKE'])) +print(from_PDB(['1AKE', '4AKE'])) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index eee48cd310..72f29b232b 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -95,8 +95,6 @@ def __init__(self, cache_path=None, hash="sha256"): def fetch( self, file_name=None, - force=False, - ignore_hash=False, db_name="hashes.txt", downloader="HTTP", **kwargs, @@ -123,6 +121,11 @@ def fetch( key, value = line.strip().split() registry_dictionary[key] = value + # Adds files not in cache + for file in file_name: + if file not in registry_dictionary: + registry_dictionary[file] = None + else: # No Database (just download) # This block of code allows file_name to be a tuple instead of a string if isinstance(file_name, str): diff --git a/package/MDAnalysis/fetch/pdb.py b/package/MDAnalysis/fetch/pdb.py index 5820035d30..f9964402a3 100644 --- a/package/MDAnalysis/fetch/pdb.py +++ b/package/MDAnalysis/fetch/pdb.py @@ -43,6 +43,7 @@ """ from pathlib import Path +from .fetchers import StaticFetcher try: import pooch @@ -52,9 +53,11 @@ HAS_POOCH = True #: Name of the :mod:`pooch` cache directory ``pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER)``; -#: see :func:`pooch.os_cache` for further details. +#: see :func:`pooch.os_cache` for further details.' #: #: .. versionadded:: 2.11.0 +#: Moved to fetchers +# This should be removed? DEFAULT_CACHE_NAME_DOWNLOADER = "MDAnalysis_pdbs" # These file formats are here https://www.rcsb.org/docs/programmatic-access/file-download-services#pdb-entry-files" @@ -162,39 +165,19 @@ def from_PDB( .. versionadded:: 2.11.0 """ - if not HAS_POOCH: - raise ModuleNotFoundError( - "pooch is needed as a dependency for from_PDB()" - ) - elif file_format not in SUPPORTED_FILE_FORMATS_DOWNLOADER: + if file_format not in SUPPORTED_FILE_FORMATS_DOWNLOADER: raise ValueError( "Invalid file format. Supported file formats " f"are {SUPPORTED_FILE_FORMATS_DOWNLOADER}" ) + - if isinstance(pdb_ids, str): - _pdb_ids = (pdb_ids,) - else: - _pdb_ids = pdb_ids - - if cache_path is None: - cache_path = pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER) - # Have to do this dictionary approach instead of using pooch.retrieve in order - # to prevent the hardcoded known_hash warning from showing up. - registry_dictionary = { - f"{pdb_id}.{file_format}": None for pdb_id in _pdb_ids - } + pdb_ids = [pdb + "." + file_format for pdb in pdb_ids] - downloader = pooch.create( - path=cache_path, + fetcher = StaticFetcher(cache_path=cache_path) + return fetcher.fetch( + file_name=pdb_ids, base_url="https://files.wwpdb.org/download/", - registry=registry_dictionary, + progressbar=progressbar, ) - - paths = [ - Path(downloader.fetch(fname=file_name, progressbar=progressbar)) - for file_name in registry_dictionary.keys() - ] - - return paths if not isinstance(pdb_ids, str) else paths[0] diff --git a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py index 881490680b..93e7abd921 100644 --- a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py +++ b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py @@ -22,23 +22,15 @@ # import pytest - import MDAnalysis as mda -from MDAnalysis.fetch.pdb import ( - DEFAULT_CACHE_NAME_DOWNLOADER, - HAS_POOCH, - SUPPORTED_FILE_FORMATS_DOWNLOADER, -) import re + +from MDAnalysis.fetch.fetchers import HAS_POOCH +from MDAnalysis.fetch.pdb import SUPPORTED_FILE_FORMATS_DOWNLOADER from urllib import request -from shutil import rmtree from pathlib import Path -if HAS_POOCH: - from requests.exceptions import HTTPError - import pooch - try: request.urlopen("https://files.wwpdb.org/", timeout=2) HAS_ACCESS_TO_WWPDB = True @@ -46,16 +38,9 @@ HAS_ACCESS_TO_WWPDB = False -@pytest.mark.skipif( - HAS_POOCH, - reason="Pooch is installed.", -) -def test_pooch_installation(tmp_path): - with pytest.raises( - ModuleNotFoundError, - match="pooch is needed as a dependency for from_PDB()", - ): - mda.fetch.from_PDB("1AKE", cache_path=tmp_path, file_format="cif") +import pytest + +from MDAnalysis.fetch.fetchers import HAS_POOCH @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") @@ -63,75 +48,27 @@ def test_pooch_installation(tmp_path): not HAS_ACCESS_TO_WWPDB, reason="Can not connect to https://files.wwpdb.org/", ) -class TestDocstringExamples: - """This class tests all the examples found in from_PDB's docstring""" +def test_download_one_file(tmp_path): - @pytest.mark.parametrize("pdb_id", ["1AKE", "4BWZ"]) - def test_one_file_download(self, tmp_path, pdb_id): - path = mda.fetch.from_PDB( - pdb_id, cache_path=tmp_path, file_format="cif" - ) - assert isinstance(path, Path) - assert Path(path).name == f"{pdb_id}.cif" + path = mda.fetch.from_PDB(["1AKE"], cache_path=tmp_path) + assert path.exists() + assert path.name == "1AKE.cif.gz" - def test_multiple_files_download(self, tmp_path): - list_of_path_strings = mda.fetch.from_PDB( - ["1AKE", "4BWZ"], cache_path=tmp_path, progressbar=True - ) - assert all(isinstance(pdb_id, Path) for pdb_id in list_of_path_strings) - assert all( - [ - Path(path).name == f"{name}.cif.gz" - for path, name in zip( - list_of_path_strings, ["1AKE", "4BWZ"], strict=True - ) - ] - ) - @pytest.mark.parametrize( - "pdb_id, n_atoms", [("1AKE", 3816), ("4BWZ", 2824)] - ) - def test_files_to_universe(self, tmp_path, pdb_id, n_atoms): - u = mda.Universe( - mda.fetch.from_PDB( - pdb_id, - file_format="pdb.gz", - cache_path=tmp_path, - progressbar=True, - ) - ) - assert isinstance(u, mda.Universe) and (len(u.atoms) == n_atoms) +def test_download_multiple_files(tmp_path): + paths = mda.fetch.from_PDB(["1AKE", "4AKE"], cache_path=tmp_path) + assert all(isinstance(path, Path) for path in paths) + assert [path.name for path in paths] == list( + ["1AKE.cif.gz", "4AKE.cif.gz"] + ) -@pytest.fixture() -def clean_up_default_cache(): - rmtree(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER), ignore_errors=True) - yield - rmtree(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER)) - - -@pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") -@pytest.mark.skipif( - not HAS_ACCESS_TO_WWPDB, - reason="Can not connect to https://files.wwpdb.org/", -) -class TestExpectedBehaviors: - - def test_no_cache_path(self, clean_up_default_cache): - assert isinstance(mda.fetch.from_PDB("1AKE", cache_path=None), Path) - def test_str_input_gives_path_output(self, tmp_path): - assert isinstance( - mda.fetch.from_PDB( - pdb_ids="1AKE", cache_path=tmp_path, file_format="cif" - ), - Path, - ) +def test_download_file_format(tmp_path): - def test_list_input_gives_list_output(self, tmp_path): - assert isinstance( - mda.fetch.from_PDB(pdb_ids=["1AKE"], cache_path=tmp_path), list - ) + path = mda.fetch.from_PDB(["1AKE"], cache_path=tmp_path, file_format="pdb") + assert path.exists() + assert path.name == "1AKE.pdb" @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") @@ -139,20 +76,14 @@ def test_list_input_gives_list_output(self, tmp_path): not HAS_ACCESS_TO_WWPDB, reason="Can not connect to https://files.wwpdb.org/", ) -class TestExpectedErrors: - - def test_invalid_pdb(self, tmp_path): - with pytest.raises(HTTPError): - mda.fetch.from_PDB(pdb_ids="foobar", cache_path=tmp_path) - - def test_invalid_file_format(self, tmp_path): - with pytest.raises( - ValueError, - match=re.escape( - "Invalid file format. Supported file formats " - f"are {SUPPORTED_FILE_FORMATS_DOWNLOADER}" - ), - ): - mda.fetch.from_PDB( - pdb_ids="1AKE", cache_path=tmp_path, file_format="barfoo" - ) +def test_invalid_file_format(tmp_path): + with pytest.raises( + ValueError, + match=re.escape( + "Invalid file format. Supported file formats " + f"are {SUPPORTED_FILE_FORMATS_DOWNLOADER}" + ), + ): + mda.fetch.from_PDB( + pdb_ids="1AKE", cache_path=tmp_path, file_format="barfoo" + ) diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index a828ea73d8..1eccffae99 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -147,6 +147,7 @@ def test_no_database(self, tmp_path): assert isinstance(path, Path) assert path.name == SINGLE_PDB + assert downloader.db_path is None assert path.exists() assert not (tmp_path / REGISTRY_NAME).exists() @@ -167,6 +168,8 @@ def test_multiple_downloads_existing_database(self, tmp_path): base_url=BASE_URL, file_name=MULTIPLE_PDBS, db_name=REGISTRY_NAME ) + assert (tmp_path / REGISTRY_NAME).exists() + downloader = StaticFetcher(cache_path=tmp_path) paths = downloader.fetch( base_url=BASE_URL, file_name=MULTIPLE_PDBS, db_name=REGISTRY_NAME From c4490b82dd02b0074572c628af65cbad04834971 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 17 Jun 2026 14:16:10 -0700 Subject: [PATCH 023/100] Made progressbar work --- package/MDAnalysis/fetch/fetchers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 72f29b232b..49af9be499 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -47,7 +47,7 @@ def __init__( pass @abstractmethod - def fetch(self, base_url, progressbar, timeout, retries): + def fetch(self, base_url, verbose, timeout, retries): # Starts file retrieval workflow # # All fetchers should call _check_pooch() @@ -55,7 +55,6 @@ def fetch(self, base_url, progressbar, timeout, retries): # Global Variable attributes self.base_url = base_url - self.verbose = progressbar # Progressbar self.timeout = timeout # timeout self.retries = retries # number of retries @@ -95,6 +94,7 @@ def __init__(self, cache_path=None, hash="sha256"): def fetch( self, file_name=None, + verbose=False, db_name="hashes.txt", downloader="HTTP", **kwargs, @@ -164,7 +164,7 @@ def fetch( Path( main_downloader.fetch( fname=file_name, - progressbar=True, + progressbar=verbose, downloader=fetch_downloader, ) ) From d745faf9c4475314daa62d3813c02d84a5fe3383 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 24 Jun 2026 12:54:06 -0700 Subject: [PATCH 024/100] Github action working --- .github/workflows/gh-ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/gh-ci.yaml b/.github/workflows/gh-ci.yaml index dd4af990d7..bed5f56ba5 100644 --- a/.github/workflows/gh-ci.yaml +++ b/.github/workflows/gh-ci.yaml @@ -3,9 +3,11 @@ on: push: branches: - develop + - fetcher_gsoc pull_request: branches: - develop + - fetcher_gsoc workflow_dispatch: concurrency: From 7b9281d3e2687f620317e88eb02b7daff7ac39ac Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 24 Jun 2026 13:04:54 -0700 Subject: [PATCH 025/100] Repo guard removed? --- .github/workflows/gh-ci.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/gh-ci.yaml b/.github/workflows/gh-ci.yaml index bed5f56ba5..344792acf1 100644 --- a/.github/workflows/gh-ci.yaml +++ b/.github/workflows/gh-ci.yaml @@ -155,7 +155,6 @@ jobs: build_docs: - if: "github.repository == 'MDAnalysis/mdanalysis'" runs-on: ubuntu-latest timeout-minutes: 15 env: From 56ecac2d7d0ef999a26a56e46d3fbb6754b7fadc Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 24 Jun 2026 13:26:31 -0700 Subject: [PATCH 026/100] removed duplicate imports --- testsuite/MDAnalysisTests/fetch/test_from_PDB.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py index 93e7abd921..860e6bef35 100644 --- a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py +++ b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py @@ -38,11 +38,6 @@ HAS_ACCESS_TO_WWPDB = False -import pytest - -from MDAnalysis.fetch.fetchers import HAS_POOCH - - @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") @pytest.mark.skipif( not HAS_ACCESS_TO_WWPDB, From 70628d863d6d119dd1d168c94d143cf2d6a64f8d Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 24 Jun 2026 14:17:25 -0700 Subject: [PATCH 027/100] added early docs --- package/MDAnalysis/fetch/fetchers.py | 96 ++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 7 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 49af9be499..32019e7858 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -21,6 +21,29 @@ # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # +""" +Fetchers --- :mod:`MDAnalysis.fetch.fetchers` +============================================ + +This module contains the Fetchers classes that can be used to retrieve or fetch files +from remote servers. These classes used the third party library:mod:`pooch` as +a dependency. + +Classes +------- + +.. autofunction:: StaticFetcher +.. autofunction:: DynamicFetcher + +Variables +--------- + +.. autodata:: DEFAULT_CACHE_NAME_DOWNLOADER +.. autodata:: DEFAULT_TIMEOUT +.. autodata:: DEFAULT_RETRIES + +""" + import hashlib from pathlib import Path @@ -33,13 +56,33 @@ else: HAS_POOCH = True + +#: Name of the :mod:`pooch` cache directory +#: ``pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER)``; +#: +#: see :func:`pooch.os_cache` for further details.' +#: +#: .. versionadded:: 2.11.0 DEFAULT_CACHE_NAME_DOWNLOADER = "MDAnalysis_pdbs" + +#: Time in seconds to wait for a response from the server before timing out. +#: +#: .. versionadded:: 2.11.0 DEFAULT_TIMEOUT = 10 + +#: Number of times to retry a download if it fails. +#: +#: .. versionadded:: 2.11.0 DEFAULT_RETRIES = 2 -class BaseFetcher(ABC): - """Blueprint Class for all Fetchers""" +class _BaseFetcher(ABC): + """Blueprint Class for all Fetchers + + This shouldn't be initalized directly but should be inherited by other + Fetchers classes. + + """ def __init__( self, @@ -61,7 +104,8 @@ def fetch(self, base_url, verbose, timeout, retries): def _check_pooch( self, ): - # Note that requests is a major dependency of pooch and is guaranteed to be installed + # Note that requests is a major dependency of pooch and is guaranteed to be + # installed if not HAS_POOCH: raise ModuleNotFoundError( "pooch is needed as a dependency for Fetchers" @@ -80,8 +124,46 @@ def _validate_fetch_args(self, args): return args -class StaticFetcher(BaseFetcher): - """Fetcher automatically downloads file in entirety and cache it to disk""" +class StaticFetcher(_BaseFetcher): + """ + Downloads files from a static URL to disk and caches them to a local directory. + + + Parameters + ---------- + cache_path : str or pathlib.Path, optional + Path to the cache directory. If set to None, the default cache directory + will be used as specified by :data:`DEFAULT_CACHE_NAME_DOWNLOADER`. + + If the directory does not exist, it will be created. + + hash : str, optional + Hash algorithm to use for verifying the integrity of downloaded files. + The default is "sha256". Valid options are any hash algorithm available in + the :mod:`hashlib` module. + + If set to None, no hash verification will be performed. + + Methods + ------- + fetch(file_name, verbose=False, db_name="hashes.txt", downloader="HTTP", **kwargs) + Downloads the specified file(s) from the given base URL and caches them + to the local cache directory. If the file(s) already exist in the cache, + they will be used instead of downloading them again. + + + Attributes + ---------- + cache_path : pathlib.Path + Path to the cache directory. + + db_path : pathlib.Path + Path to the database file used for caching. Created after calling fetch() + + hash : str + Hash algorithm used for verifying the integrity of downloaded files. + + """ def __init__(self, cache_path=None, hash="sha256"): @@ -194,7 +276,7 @@ def _check_hash_input(self, hash): ) -class DynamicFetcher(BaseFetcher): +class DynamicFetcher(_BaseFetcher): """Fetcher yields a Python Generator for dynamic downloading and analysis""" - pass + raise NotImplementedError From bfe74b6a266530e2c84a9422244e74d01408a5c5 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 24 Jun 2026 16:03:04 -0700 Subject: [PATCH 028/100] added header to tests --- testsuite/MDAnalysisTests/fetch/test_from_PDB.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py index 860e6bef35..f71db35578 100644 --- a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py +++ b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py @@ -49,7 +49,11 @@ def test_download_one_file(tmp_path): assert path.exists() assert path.name == "1AKE.cif.gz" - +@pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") +@pytest.mark.skipif( + not HAS_ACCESS_TO_WWPDB, + reason="Can not connect to https://files.wwpdb.org/", +) def test_download_multiple_files(tmp_path): paths = mda.fetch.from_PDB(["1AKE", "4AKE"], cache_path=tmp_path) @@ -58,7 +62,11 @@ def test_download_multiple_files(tmp_path): ["1AKE.cif.gz", "4AKE.cif.gz"] ) - +@pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") +@pytest.mark.skipif( + not HAS_ACCESS_TO_WWPDB, + reason="Can not connect to https://files.wwpdb.org/", +) def test_download_file_format(tmp_path): path = mda.fetch.from_PDB(["1AKE"], cache_path=tmp_path, file_format="pdb") From 000f15c421bcab3d9433b75ead0b2accef4e4a49 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 25 Jun 2026 11:30:39 -0700 Subject: [PATCH 029/100] Docs can build now! --- package/MDAnalysis/fetch/fetchers.py | 9 ++++----- .../source/documentation_pages/fetchers/fetchers.rst | 1 + .../source/documentation_pages/fetchers_modules.rst | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 package/doc/sphinx/source/documentation_pages/fetchers/fetchers.rst diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 32019e7858..f4206568f0 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -23,7 +23,7 @@ """ Fetchers --- :mod:`MDAnalysis.fetch.fetchers` -============================================ +============================================= This module contains the Fetchers classes that can be used to retrieve or fetch files from remote servers. These classes used the third party library:mod:`pooch` as @@ -33,7 +33,6 @@ ------- .. autofunction:: StaticFetcher -.. autofunction:: DynamicFetcher Variables --------- @@ -276,7 +275,7 @@ def _check_hash_input(self, hash): ) -class DynamicFetcher(_BaseFetcher): - """Fetcher yields a Python Generator for dynamic downloading and analysis""" +# class DynamicFetcher(_BaseFetcher): +# """Fetcher yields a Python Generator for dynamic downloading and analysis""" - raise NotImplementedError +# raise NotImplementedError diff --git a/package/doc/sphinx/source/documentation_pages/fetchers/fetchers.rst b/package/doc/sphinx/source/documentation_pages/fetchers/fetchers.rst new file mode 100644 index 0000000000..58961f4806 --- /dev/null +++ b/package/doc/sphinx/source/documentation_pages/fetchers/fetchers.rst @@ -0,0 +1 @@ +.. automodule:: MDAnalysis.fetch.fetchers diff --git a/package/doc/sphinx/source/documentation_pages/fetchers_modules.rst b/package/doc/sphinx/source/documentation_pages/fetchers_modules.rst index bdcff49164..e04988b2b7 100644 --- a/package/doc/sphinx/source/documentation_pages/fetchers_modules.rst +++ b/package/doc/sphinx/source/documentation_pages/fetchers_modules.rst @@ -15,4 +15,5 @@ module. :maxdepth: 1 fetchers/init + fetchers/fetchers fetchers/PDB From 0e14f475d1c13593caa5461f629287e39b915e13 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 25 Jun 2026 11:50:43 -0700 Subject: [PATCH 030/100] docs --- package/MDAnalysis/fetch/fetchers.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index f4206568f0..48fc27c740 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -26,7 +26,7 @@ ============================================= This module contains the Fetchers classes that can be used to retrieve or fetch files -from remote servers. These classes used the third party library:mod:`pooch` as +from remote servers. These classes uses the third party library :mod:`pooch` as a dependency. Classes @@ -37,6 +37,10 @@ Variables --------- +These are global submodule level variables that affect the runtime behavior across +all Fetcher Classes. + + .. autodata:: DEFAULT_CACHE_NAME_DOWNLOADER .. autodata:: DEFAULT_TIMEOUT .. autodata:: DEFAULT_RETRIES @@ -69,7 +73,7 @@ #: .. versionadded:: 2.11.0 DEFAULT_TIMEOUT = 10 -#: Number of times to retry a download if it fails. +#: Number of attempt to retry a download if it fails. #: #: .. versionadded:: 2.11.0 DEFAULT_RETRIES = 2 @@ -91,27 +95,22 @@ def __init__( @abstractmethod def fetch(self, base_url, verbose, timeout, retries): # Starts file retrieval workflow - # # All fetchers should call _check_pooch() + # + # These arguments should be implemented by all child Fetchers. self._check_pooch() - # Global Variable attributes - self.base_url = base_url - self.timeout = timeout # timeout - self.retries = retries # number of retries - def _check_pooch( self, ): - # Note that requests is a major dependency of pooch and is guaranteed to be - # installed if not HAS_POOCH: raise ModuleNotFoundError( "pooch is needed as a dependency for Fetchers" ) def _validate_fetch_args(self, args): - """Checks to see if @abstractmethod fetch parameters are initalized correctly""" + """Set default values for @abstractmethod fetch() method if + not provided by user""" if "base_url" not in args: raise ValueError("base_url is not defined in fetch()") From 414408b23fe88f06ca6e8bb6c986820eac31157f Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 25 Jun 2026 15:24:49 -0700 Subject: [PATCH 031/100] fixed pytests --- package/MDAnalysis/fetch/fetchers.py | 7 +- testsuite/MDAnalysisTests/fetch/servers.py | 65 ++++++ .../fetch/test_static_fetcher.py | 203 ++++++++++-------- 3 files changed, 181 insertions(+), 94 deletions(-) create mode 100644 testsuite/MDAnalysisTests/fetch/servers.py diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 48fc27c740..a396f859f2 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -202,7 +202,12 @@ def fetch( registry_dictionary[key] = value # Adds files not in cache - for file in file_name: + if isinstance(file_name, str): + _file_name = (file_name,) + else: + _file_name = file_name + + for file in _file_name: if file not in registry_dictionary: registry_dictionary[file] = None diff --git a/testsuite/MDAnalysisTests/fetch/servers.py b/testsuite/MDAnalysisTests/fetch/servers.py new file mode 100644 index 0000000000..a2a84ffa28 --- /dev/null +++ b/testsuite/MDAnalysisTests/fetch/servers.py @@ -0,0 +1,65 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*- +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8 +# +# MDAnalysis --- https://www.mdanalysis.org +# Copyright (c) 2006-2017 The MDAnalysis Development Team and contributors +# (see the file AUTHORS for the full list of names) +# +# Released under the Lesser GNU Public Licence, v2.1 or any higher version +# +# Please cite your use of MDAnalysis in published work: +# +# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, N. M. Melo, S. L. Seyler, +# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein. +# MDAnalysis: A Python package for the rapid analysis of molecular dynamics +# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th +# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy. +# doi: 10.25080/majora-629e541a-00e +# +# 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 +# + +from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from functools import partial + +import tempfile +import threading + +from decorator import contextmanager + + +@contextmanager +def temporary_http_server(): + with tempfile.TemporaryDirectory() as temp_dir: + temp_folder = Path(temp_dir) + + (temp_folder / "TEST_FILE1.txt").write_text( + "The USA is going to win the 2026 World Cup!\n" + "U-S-A! U-S-A! U-S-A!" + ) + + (temp_folder / "TEST_FILE2.txt").write_text("7-1") + + http_handler = partial( + SimpleHTTPRequestHandler, + directory=str(temp_folder), + ) + + server = ThreadingHTTPServer(("127.0.0.1", 7123), http_handler) + host, port = server.server_address + + thread = threading.Thread( + target=server.serve_forever, + daemon=True, + ) + thread.start() + + try: + yield host, port, temp_folder + finally: + server.shutdown() + server.server_close() + thread.join() \ No newline at end of file diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 1eccffae99..285e056d8d 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -24,10 +24,12 @@ from pathlib import Path import re from shutil import rmtree -from urllib import request + +from servers import temporary_http_server import pytest + from MDAnalysis.fetch.fetchers import ( DEFAULT_CACHE_NAME_DOWNLOADER, HAS_POOCH, @@ -37,18 +39,11 @@ if HAS_POOCH: import pooch -try: - request.urlopen("https://files.wwpdb.org/", timeout=2) - HAS_ACCESS_TO_WWPDB = True -except request.URLError: - HAS_ACCESS_TO_WWPDB = False - - -BASE_URL = "https://files.wwpdb.org/download/" -SINGLE_PDB = "1AKE.pdb" -MULTIPLE_PDBS = ("1AKE.pdb", "4AKE.pdb") -REGISTRY_NAME = "test_db.txt" - + +# BASE_URL = "https://files.wwpdb.org/download/" +# SINGLE_PDB = "1AKE.pdb" +# MULTIPLE_PDBS = ("1AKE.pdb", "4AKE.pdb") +REGISTRY_NAME = "hashes.txt" @pytest.fixture() def clean_up_default_cache(): @@ -70,27 +65,30 @@ def test_pooch_installation(): class TestExpectedErrors: def test_missing_base_url(self, tmp_path): - downloader = StaticFetcher(cache_path=tmp_path) + with temporary_http_server() as (host, port, temp_folder): + downloader = StaticFetcher(cache_path=tmp_path) - with pytest.raises( - ValueError, match=re.escape("base_url is not defined in fetch()") - ): - downloader.fetch(file_name=SINGLE_PDB) + with pytest.raises( + ValueError, match=re.escape("base_url is not defined in fetch()") + ): + downloader.fetch(file_name="TEST_FILE1.txt") def test_invalid_downloader(self, tmp_path): - downloader = StaticFetcher(cache_path=tmp_path) - - with pytest.raises( - ValueError, - match=re.escape( - "Invalid downloader 'barfoo'. Valid options are " - "'HTTP', 'FTP', 'SFTP', 'DOI'." - ), - ): - downloader.fetch( - base_url=BASE_URL, - file_name=SINGLE_PDB, - downloader="barfoo", + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher(cache_path=tmp_path) + + with pytest.raises( + ValueError, + match=re.escape( + "Invalid downloader 'barfoo'. Valid options are " + "'HTTP', 'FTP', 'SFTP', 'DOI'." + ), + ): + downloader.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + downloader="barfoo", ) def test_invalid_hash(self, tmp_path): @@ -99,82 +97,101 @@ def test_invalid_hash(self, tmp_path): @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") -@pytest.mark.skipif( - not HAS_ACCESS_TO_WWPDB, - reason="Can not connect to https://files.wwpdb.org/", -) class TestExpectedBehaviors: def test_default_cache_path(self, clean_up_default_cache): - downloader = StaticFetcher() - path = downloader.fetch(base_url=BASE_URL, file_name=SINGLE_PDB) - assert isinstance(path, Path) - assert path.name == SINGLE_PDB - assert path.exists() + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher() + path = downloader.fetch( + base_url=base_url, file_name="TEST_FILE1.txt" + ) + + assert isinstance(path, Path) + assert path.name == "TEST_FILE1.txt" + assert path.read_text() == "The USA is going to win the 2026 World Cup!\nU-S-A! U-S-A! U-S-A!" + assert path.exists() def test_create_database(self, tmp_path): - downloader = StaticFetcher(cache_path=tmp_path) - path = downloader.fetch( - base_url=BASE_URL, file_name=SINGLE_PDB, db_name=REGISTRY_NAME - ) + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher(cache_path=tmp_path) + path = downloader.fetch( + base_url=base_url, file_name="TEST_FILE1.txt", db_name=REGISTRY_NAME + ) - assert isinstance(path, Path) - assert path.name == SINGLE_PDB - assert path.exists() - assert (tmp_path / REGISTRY_NAME).exists() + assert isinstance(path, Path) + assert path.name == "TEST_FILE1.txt" + assert path.read_text() == "The USA is going to win the 2026 World Cup!\nU-S-A! U-S-A! U-S-A!" + assert path.exists() + + # Should I check the registry file content? It not managed by us but by pooch. + assert (downloader.db_path).exists() + assert (downloader.db_path).read_text() == "TEST_FILE1.txt c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4" + "\n" def test_existing_database(self, tmp_path): - downloader = StaticFetcher(cache_path=tmp_path) - downloader.fetch( - base_url=BASE_URL, file_name=SINGLE_PDB, db_name=REGISTRY_NAME - ) + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher(cache_path=tmp_path) - downloader = StaticFetcher(cache_path=tmp_path) - path = downloader.fetch( - base_url=BASE_URL, file_name=SINGLE_PDB, db_name=REGISTRY_NAME - ) + p1 = downloader.fetch( + base_url=base_url, file_name="TEST_FILE1.txt", db_name=REGISTRY_NAME + ) + + downloader = StaticFetcher(cache_path=tmp_path) + p2 = downloader.fetch( + base_url=base_url, file_name="TEST_FILE1.txt", db_name=REGISTRY_NAME + ) - assert isinstance(path, Path) - assert path.name == SINGLE_PDB - assert path.exists() + assert p1.stat().st_mtime == p2.stat().st_mtime def test_no_database(self, tmp_path): - downloader = StaticFetcher(cache_path=tmp_path) - path = downloader.fetch( - base_url=BASE_URL, file_name=SINGLE_PDB, db_name=None - ) - - assert isinstance(path, Path) - assert path.name == SINGLE_PDB - assert downloader.db_path is None - assert path.exists() - assert not (tmp_path / REGISTRY_NAME).exists() - - def test_multiple_downloads_create_database(self, tmp_path): - downloader = StaticFetcher(cache_path=tmp_path) - paths = downloader.fetch( - base_url=BASE_URL, file_name=MULTIPLE_PDBS, db_name=REGISTRY_NAME - ) - - assert all(isinstance(path, Path) for path in paths) - assert [path.name for path in paths] == list(MULTIPLE_PDBS) - assert all(path.exists() for path in paths) - assert (tmp_path / REGISTRY_NAME).exists() + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher(cache_path=tmp_path) + path = downloader.fetch( + base_url=base_url, file_name="TEST_FILE1.txt", db_name=None + ) - def test_multiple_downloads_existing_database(self, tmp_path): - downloader = StaticFetcher(cache_path=tmp_path) - downloader.fetch( - base_url=BASE_URL, file_name=MULTIPLE_PDBS, db_name=REGISTRY_NAME - ) + assert isinstance(path, Path) + assert path.exists() + assert path.name == "TEST_FILE1.txt" + + assert downloader.db_path is None + assert not (tmp_path / REGISTRY_NAME).exists() + + + def test_multiple_downloads_no_database(self, tmp_path): + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher(cache_path=tmp_path) + paths = downloader.fetch( + base_url=base_url, file_name=("TEST_FILE1.txt", "TEST_FILE2.txt"), db_name=None + ) - assert (tmp_path / REGISTRY_NAME).exists() + assert all(isinstance(path, Path) for path in paths) + assert all(path.exists() for path in paths) + assert [path.name for path in paths] == list(("TEST_FILE1.txt", "TEST_FILE2.txt")) - downloader = StaticFetcher(cache_path=tmp_path) - paths = downloader.fetch( - base_url=BASE_URL, file_name=MULTIPLE_PDBS, db_name=REGISTRY_NAME - ) + assert downloader.db_path is None + assert not (tmp_path / REGISTRY_NAME).exists() + + + def test_multiple_downloads_existing_database(self, tmp_path): + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher(cache_path=tmp_path) + paths1 = downloader.fetch( + base_url=base_url, file_name=("TEST_FILE1.txt", "TEST_FILE2.txt"), db_name=REGISTRY_NAME + ) + mtime1 = [path.stat().st_mtime for path in paths1] + + downloader = StaticFetcher(cache_path=tmp_path) + paths2 = downloader.fetch( + base_url=base_url, file_name=("TEST_FILE1.txt", "TEST_FILE2.txt"), db_name=REGISTRY_NAME + ) + mtime2 = [path.stat().st_mtime for path in paths2] - assert all(isinstance(path, Path) for path in paths) - assert [path.name for path in paths] == list(MULTIPLE_PDBS) - assert all(path.exists() for path in paths) + assert mtime1 == mtime2 From a99c78abc43a955a214a1ab3320a4f84bd844883 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 25 Jun 2026 15:33:19 -0700 Subject: [PATCH 032/100] added minor test --- testsuite/MDAnalysisTests/fetch/servers.py | 2 +- .../fetch/test_static_fetcher.py | 76 ++++++++++++++----- 2 files changed, 58 insertions(+), 20 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/servers.py b/testsuite/MDAnalysisTests/fetch/servers.py index a2a84ffa28..c50de330d3 100644 --- a/testsuite/MDAnalysisTests/fetch/servers.py +++ b/testsuite/MDAnalysisTests/fetch/servers.py @@ -62,4 +62,4 @@ def temporary_http_server(): finally: server.shutdown() server.server_close() - thread.join() \ No newline at end of file + thread.join() diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 285e056d8d..3ce5a708d7 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -39,12 +39,13 @@ if HAS_POOCH: import pooch - + # BASE_URL = "https://files.wwpdb.org/download/" # SINGLE_PDB = "1AKE.pdb" # MULTIPLE_PDBS = ("1AKE.pdb", "4AKE.pdb") REGISTRY_NAME = "hashes.txt" + @pytest.fixture() def clean_up_default_cache(): rmtree(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER), ignore_errors=True) @@ -69,7 +70,8 @@ def test_missing_base_url(self, tmp_path): downloader = StaticFetcher(cache_path=tmp_path) with pytest.raises( - ValueError, match=re.escape("base_url is not defined in fetch()") + ValueError, + match=re.escape("base_url is not defined in fetch()"), ): downloader.fetch(file_name="TEST_FILE1.txt") @@ -89,7 +91,7 @@ def test_invalid_downloader(self, tmp_path): base_url=base_url, file_name="TEST_FILE1.txt", downloader="barfoo", - ) + ) def test_invalid_hash(self, tmp_path): with pytest.raises(ValueError, match='Invalid hash "barfoo"'): @@ -110,7 +112,10 @@ def test_default_cache_path(self, clean_up_default_cache): assert isinstance(path, Path) assert path.name == "TEST_FILE1.txt" - assert path.read_text() == "The USA is going to win the 2026 World Cup!\nU-S-A! U-S-A! U-S-A!" + assert ( + path.read_text() + == "The USA is going to win the 2026 World Cup!\nU-S-A! U-S-A! U-S-A!" + ) assert path.exists() def test_create_database(self, tmp_path): @@ -118,30 +123,40 @@ def test_create_database(self, tmp_path): base_url = f"http://{host}:{port}/" downloader = StaticFetcher(cache_path=tmp_path) path = downloader.fetch( - base_url=base_url, file_name="TEST_FILE1.txt", db_name=REGISTRY_NAME + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name=REGISTRY_NAME, ) assert isinstance(path, Path) assert path.name == "TEST_FILE1.txt" - assert path.read_text() == "The USA is going to win the 2026 World Cup!\nU-S-A! U-S-A! U-S-A!" + assert ( + path.read_text() + == "The USA is going to win the 2026 World Cup!\nU-S-A! U-S-A! U-S-A!" + ) assert path.exists() - # Should I check the registry file content? It not managed by us but by pooch. assert (downloader.db_path).exists() - assert (downloader.db_path).read_text() == "TEST_FILE1.txt c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4" + "\n" + assert ( + downloader.db_path + ).read_text() == "TEST_FILE1.txt c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4" + "\n" def test_existing_database(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): - base_url = f"http://{host}:{port}/" + base_url = f"http://{host}:{port}/" downloader = StaticFetcher(cache_path=tmp_path) p1 = downloader.fetch( - base_url=base_url, file_name="TEST_FILE1.txt", db_name=REGISTRY_NAME + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name=REGISTRY_NAME, ) - + downloader = StaticFetcher(cache_path=tmp_path) p2 = downloader.fetch( - base_url=base_url, file_name="TEST_FILE1.txt", db_name=REGISTRY_NAME + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name=REGISTRY_NAME, ) assert p1.stat().st_mtime == p2.stat().st_mtime @@ -161,37 +176,60 @@ def test_no_database(self, tmp_path): assert downloader.db_path is None assert not (tmp_path / REGISTRY_NAME).exists() - def test_multiple_downloads_no_database(self, tmp_path): - + with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" downloader = StaticFetcher(cache_path=tmp_path) paths = downloader.fetch( - base_url=base_url, file_name=("TEST_FILE1.txt", "TEST_FILE2.txt"), db_name=None + base_url=base_url, + file_name=("TEST_FILE1.txt", "TEST_FILE2.txt"), + db_name=None, ) assert all(isinstance(path, Path) for path in paths) assert all(path.exists() for path in paths) - assert [path.name for path in paths] == list(("TEST_FILE1.txt", "TEST_FILE2.txt")) + assert [path.name for path in paths] == list( + ("TEST_FILE1.txt", "TEST_FILE2.txt") + ) assert downloader.db_path is None assert not (tmp_path / REGISTRY_NAME).exists() - + def test_multiple_downloads_create_database(self, tmp_path): + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher(cache_path=tmp_path) + paths = downloader.fetch( + base_url=base_url, + file_name=("TEST_FILE1.txt", "TEST_FILE2.txt"), + db_name=REGISTRY_NAME, + ) + + assert downloader.db_path.read_text() == ( + "TEST_FILE1.txt c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + "TEST_FILE2.txt 0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" + ) + def test_multiple_downloads_existing_database(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" downloader = StaticFetcher(cache_path=tmp_path) paths1 = downloader.fetch( - base_url=base_url, file_name=("TEST_FILE1.txt", "TEST_FILE2.txt"), db_name=REGISTRY_NAME + base_url=base_url, + file_name=("TEST_FILE1.txt", "TEST_FILE2.txt"), + db_name=REGISTRY_NAME, ) mtime1 = [path.stat().st_mtime for path in paths1] downloader = StaticFetcher(cache_path=tmp_path) paths2 = downloader.fetch( - base_url=base_url, file_name=("TEST_FILE1.txt", "TEST_FILE2.txt"), db_name=REGISTRY_NAME + base_url=base_url, + file_name=("TEST_FILE1.txt", "TEST_FILE2.txt"), + db_name=REGISTRY_NAME, ) + mtime2 = [path.stat().st_mtime for path in paths2] assert mtime1 == mtime2 From a79fa788ee4c58ec1f976846b2ca9a0b6e031e7e Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 25 Jun 2026 15:50:05 -0700 Subject: [PATCH 033/100] Reforce github Actions --- testsuite/MDAnalysisTests/fetch/servers.py | 2 +- testsuite/MDAnalysisTests/fetch/test_static_fetcher.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/servers.py b/testsuite/MDAnalysisTests/fetch/servers.py index c50de330d3..8f853187cb 100644 --- a/testsuite/MDAnalysisTests/fetch/servers.py +++ b/testsuite/MDAnalysisTests/fetch/servers.py @@ -28,7 +28,7 @@ import tempfile import threading -from decorator import contextmanager +from contextlib import contextmanager @contextmanager diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 3ce5a708d7..cee7bdfe71 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -139,7 +139,7 @@ def test_create_database(self, tmp_path): assert (downloader.db_path).exists() assert ( downloader.db_path - ).read_text() == "TEST_FILE1.txt c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4" + "\n" + ).read_text() == "TEST_FILE1.txt c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" def test_existing_database(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): From 2cc27235e2b3e01f34473b00e778ff363c5cb98b Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Mon, 29 Jun 2026 14:58:04 -0700 Subject: [PATCH 034/100] added test for multiple hashes --- package/MDAnalysis/fetch/fetchers.py | 16 ++++++++--- .../fetch/test_static_fetcher.py | 28 +++++++++++++++++-- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index a396f859f2..9f4ac54497 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -98,7 +98,7 @@ def fetch(self, base_url, verbose, timeout, retries): # All fetchers should call _check_pooch() # # These arguments should be implemented by all child Fetchers. - self._check_pooch() + pass def _check_pooch( self, @@ -149,7 +149,7 @@ class StaticFetcher(_BaseFetcher): to the local cache directory. If the file(s) already exist in the cache, they will be used instead of downloading them again. - + Attributes ---------- cache_path : pathlib.Path @@ -157,7 +157,7 @@ class StaticFetcher(_BaseFetcher): db_path : pathlib.Path Path to the database file used for caching. Created after calling fetch() - + hash : str Hash algorithm used for verifying the integrity of downloaded files. @@ -257,7 +257,15 @@ def fetch( ] if CREATE_DATABASE: - pooch.make_registry(directory=self.cache_path, output=self.db_path) + hashes = [ + (fname.name, pooch.file_hash(fname, alg=self.hash)) + for fname in self.cache_path.iterdir() + if fname.is_file() + ] + + with open(self.db_path, mode="w") as f: + for fname, hash in hashes: + f.write(f"{fname} {self.hash}:{hash}\n") if len(paths) == 1: return paths[0] diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index cee7bdfe71..1307ff774f 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -46,6 +46,28 @@ REGISTRY_NAME = "hashes.txt" +def test_invalid_hash(): + pass + +def test_invalid_downloader(): + pass + +def test_different_hashes(tmp_path): + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher(cache_path=tmp_path, hash="md5") + path = downloader.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name=REGISTRY_NAME, + ) + + assert ( + downloader.db_path + ).read_text() == "TEST_FILE1.txt md5:b2f138521297db74b6b280feeb14f9f6\n" + + + @pytest.fixture() def clean_up_default_cache(): rmtree(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER), ignore_errors=True) @@ -139,7 +161,7 @@ def test_create_database(self, tmp_path): assert (downloader.db_path).exists() assert ( downloader.db_path - ).read_text() == "TEST_FILE1.txt c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + ).read_text() == "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" def test_existing_database(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): @@ -208,8 +230,8 @@ def test_multiple_downloads_create_database(self, tmp_path): ) assert downloader.db_path.read_text() == ( - "TEST_FILE1.txt c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" - "TEST_FILE2.txt 0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" + "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" ) def test_multiple_downloads_existing_database(self, tmp_path): From 80b098863c77f585ccd4bf1fd870f6cb375739b4 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Mon, 29 Jun 2026 14:59:43 -0700 Subject: [PATCH 035/100] made hashes a generator --- package/MDAnalysis/fetch/fetchers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 9f4ac54497..152868cdb3 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -257,12 +257,12 @@ def fetch( ] if CREATE_DATABASE: - hashes = [ + hashes = ( (fname.name, pooch.file_hash(fname, alg=self.hash)) for fname in self.cache_path.iterdir() if fname.is_file() - ] - + ) + with open(self.db_path, mode="w") as f: for fname, hash in hashes: f.write(f"{fname} {self.hash}:{hash}\n") From 30e8eb62bc1753c219b29f135e20cd25edaccc00 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Mon, 29 Jun 2026 15:00:42 -0700 Subject: [PATCH 036/100] made hashes an exclusive write --- package/MDAnalysis/fetch/fetchers.py | 2 +- package/doc/sphinx/build_docs.sh | 12 + package/doc/sphinx/html_output | 1063 ++++++++++++++++++ testsuite/MDAnalysisTests/fetch/run_tests.sh | 2 + testsuite/MDAnalysisTests/pytest.log | 240 ++++ 5 files changed, 1318 insertions(+), 1 deletion(-) create mode 100755 package/doc/sphinx/build_docs.sh create mode 100644 package/doc/sphinx/html_output create mode 100755 testsuite/MDAnalysisTests/fetch/run_tests.sh create mode 100644 testsuite/MDAnalysisTests/pytest.log diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 152868cdb3..c75072026f 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -263,7 +263,7 @@ def fetch( if fname.is_file() ) - with open(self.db_path, mode="w") as f: + with open(self.db_path, mode="x") as f: for fname, hash in hashes: f.write(f"{fname} {self.hash}:{hash}\n") diff --git a/package/doc/sphinx/build_docs.sh b/package/doc/sphinx/build_docs.sh new file mode 100755 index 0000000000..f586f6fa41 --- /dev/null +++ b/package/doc/sphinx/build_docs.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +#./build_docs.sh 2>&1 | tee html_output + +eval "$(mamba shell hook --shell bash)" +mamba activate mdanalysis-dev + +rm -rfv ../html/* && make html + +cd .. + +#python -m http.server 9191 diff --git a/package/doc/sphinx/html_output b/package/doc/sphinx/html_output new file mode 100644 index 0000000000..067989bcb9 --- /dev/null +++ b/package/doc/sphinx/html_output @@ -0,0 +1,1063 @@ +removed '../html/documentation_pages/analysis/align.html' +removed '../html/documentation_pages/analysis/atomicdistances.html' +removed '../html/documentation_pages/analysis/backends.html' +removed '../html/documentation_pages/analysis/base.html' +removed '../html/documentation_pages/analysis/bat.html' +removed '../html/documentation_pages/analysis/contacts.html' +removed '../html/documentation_pages/analysis/data.html' +removed '../html/documentation_pages/analysis/density.html' +removed '../html/documentation_pages/analysis/dielectric.html' +removed '../html/documentation_pages/analysis/diffusionmap.html' +removed '../html/documentation_pages/analysis/dihedrals.html' +removed '../html/documentation_pages/analysis/distances.html' +removed '../html/documentation_pages/analysis/dssp.html' +removed '../html/documentation_pages/analysis/encore.html' +removed '../html/documentation_pages/analysis/encore/bootstrap.html' +removed '../html/documentation_pages/analysis/encore/clustering.html' +removed '../html/documentation_pages/analysis/encore/confdistmatrix.html' +removed '../html/documentation_pages/analysis/encore/covariance.html' +removed '../html/documentation_pages/analysis/encore/dimensionality_reduction.html' +removed '../html/documentation_pages/analysis/encore/similarity.html' +removed '../html/documentation_pages/analysis/encore/utils.html' +removed directory '../html/documentation_pages/analysis/encore' +removed '../html/documentation_pages/analysis/gnm.html' +removed '../html/documentation_pages/analysis/hbond_autocorrel.html' +removed '../html/documentation_pages/analysis/hbond_autocorrel_deprecated.html' +removed '../html/documentation_pages/analysis/helix_analysis.html' +removed '../html/documentation_pages/analysis/hole2.html' +removed '../html/documentation_pages/analysis/hydrogenbonds.html' +removed '../html/documentation_pages/analysis/leaflet.html' +removed '../html/documentation_pages/analysis/legacy/x3dna.html' +removed directory '../html/documentation_pages/analysis/legacy' +removed '../html/documentation_pages/analysis/legacy_modules.html' +removed '../html/documentation_pages/analysis/lineardensity.html' +removed '../html/documentation_pages/analysis/msd.html' +removed '../html/documentation_pages/analysis/nucleicacids.html' +removed '../html/documentation_pages/analysis/nuclinfo.html' +removed '../html/documentation_pages/analysis/parallelization.html' +removed '../html/documentation_pages/analysis/pca.html' +removed '../html/documentation_pages/analysis/polymer.html' +removed '../html/documentation_pages/analysis/psa.html' +removed '../html/documentation_pages/analysis/rdf.html' +removed '../html/documentation_pages/analysis/results.html' +removed '../html/documentation_pages/analysis/rms.html' +removed '../html/documentation_pages/analysis/waterdynamics.html' +removed '../html/documentation_pages/analysis/wbridge_analysis.html' +removed directory '../html/documentation_pages/analysis' +removed '../html/documentation_pages/analysis_modules.html' +removed '../html/documentation_pages/auxiliary/EDR.html' +removed '../html/documentation_pages/auxiliary/XVG.html' +removed '../html/documentation_pages/auxiliary/base.html' +removed '../html/documentation_pages/auxiliary/core.html' +removed '../html/documentation_pages/auxiliary/init.html' +removed directory '../html/documentation_pages/auxiliary' +removed '../html/documentation_pages/auxiliary_modules.html' +removed '../html/documentation_pages/converters.html' +removed '../html/documentation_pages/converters/OpenMM.html' +removed '../html/documentation_pages/converters/ParmEd.html' +removed '../html/documentation_pages/converters/RDKit.html' +removed '../html/documentation_pages/converters/base.html' +removed directory '../html/documentation_pages/converters' +removed '../html/documentation_pages/coordinates/CRD.html' +removed '../html/documentation_pages/coordinates/DCD.html' +removed '../html/documentation_pages/coordinates/DLPoly.html' +removed '../html/documentation_pages/coordinates/DMS.html' +removed '../html/documentation_pages/coordinates/FHIAIMS.html' +removed '../html/documentation_pages/coordinates/GMS.html' +removed '../html/documentation_pages/coordinates/GRO.html' +removed '../html/documentation_pages/coordinates/GSD.html' +removed '../html/documentation_pages/coordinates/H5MD.html' +removed '../html/documentation_pages/coordinates/IMD.html' +removed '../html/documentation_pages/coordinates/INPCRD.html' +removed '../html/documentation_pages/coordinates/LAMMPS.html' +removed '../html/documentation_pages/coordinates/MMTF.html' +removed '../html/documentation_pages/coordinates/MOL2.html' +removed '../html/documentation_pages/coordinates/NAMDBIN.html' +removed '../html/documentation_pages/coordinates/PDB.html' +removed '../html/documentation_pages/coordinates/PDBQT.html' +removed '../html/documentation_pages/coordinates/PQR.html' +removed '../html/documentation_pages/coordinates/TNG.html' +removed '../html/documentation_pages/coordinates/TPR.html' +removed '../html/documentation_pages/coordinates/TRC.html' +removed '../html/documentation_pages/coordinates/TRJ.html' +removed '../html/documentation_pages/coordinates/TRR.html' +removed '../html/documentation_pages/coordinates/TRZ.html' +removed '../html/documentation_pages/coordinates/TXYZ.html' +removed '../html/documentation_pages/coordinates/XDR.html' +removed '../html/documentation_pages/coordinates/XTC.html' +removed '../html/documentation_pages/coordinates/XYZ.html' +removed '../html/documentation_pages/coordinates/base.html' +removed '../html/documentation_pages/coordinates/chain.html' +removed '../html/documentation_pages/coordinates/chemfiles.html' +removed '../html/documentation_pages/coordinates/core.html' +removed '../html/documentation_pages/coordinates/init.html' +removed '../html/documentation_pages/coordinates/memory.html' +removed '../html/documentation_pages/coordinates/null.html' +removed '../html/documentation_pages/coordinates/pickle_readers.html' +removed '../html/documentation_pages/coordinates/timestep.html' +removed directory '../html/documentation_pages/coordinates' +removed '../html/documentation_pages/coordinates_modules.html' +removed '../html/documentation_pages/core/accessors.html' +removed '../html/documentation_pages/core/groups.html' +removed '../html/documentation_pages/core/init.html' +removed '../html/documentation_pages/core/selection.html' +removed '../html/documentation_pages/core/topology.html' +removed '../html/documentation_pages/core/topologyattrs.html' +removed '../html/documentation_pages/core/topologyobjects.html' +removed '../html/documentation_pages/core/universe.html' +removed directory '../html/documentation_pages/core' +removed '../html/documentation_pages/core_modules.html' +removed '../html/documentation_pages/exceptions.html' +removed '../html/documentation_pages/fetchers/PDB.html' +removed '../html/documentation_pages/fetchers/fetchers.html' +removed '../html/documentation_pages/fetchers/init.html' +removed directory '../html/documentation_pages/fetchers' +removed '../html/documentation_pages/fetchers_modules.html' +removed '../html/documentation_pages/guesser_modules.html' +removed '../html/documentation_pages/guesser_modules/base.html' +removed '../html/documentation_pages/guesser_modules/default_guesser.html' +removed '../html/documentation_pages/guesser_modules/init.html' +removed '../html/documentation_pages/guesser_modules/tables.html' +removed directory '../html/documentation_pages/guesser_modules' +removed '../html/documentation_pages/lib/NeighborSearch.html' +removed '../html/documentation_pages/lib/c_distances.html' +removed '../html/documentation_pages/lib/c_distances_openmp.html' +removed '../html/documentation_pages/lib/correlations.html' +removed '../html/documentation_pages/lib/distances.html' +removed '../html/documentation_pages/lib/formats/libdcd.html' +removed '../html/documentation_pages/lib/formats/libmdaxdr.html' +removed directory '../html/documentation_pages/lib/formats' +removed '../html/documentation_pages/lib/log.html' +removed '../html/documentation_pages/lib/mdamath.html' +removed '../html/documentation_pages/lib/nsgrid.html' +removed '../html/documentation_pages/lib/picklable_file_io.html' +removed '../html/documentation_pages/lib/pkdtree.html' +removed '../html/documentation_pages/lib/qcprot.html' +removed '../html/documentation_pages/lib/transformations.html' +removed '../html/documentation_pages/lib/util.html' +removed directory '../html/documentation_pages/lib' +removed '../html/documentation_pages/lib_modules.html' +removed '../html/documentation_pages/overview.html' +removed '../html/documentation_pages/references.html' +removed '../html/documentation_pages/selections.html' +removed '../html/documentation_pages/selections/base.html' +removed '../html/documentation_pages/selections/charmm.html' +removed '../html/documentation_pages/selections/gromacs.html' +removed '../html/documentation_pages/selections/jmol.html' +removed '../html/documentation_pages/selections/pymol.html' +removed '../html/documentation_pages/selections/vmd.html' +removed directory '../html/documentation_pages/selections' +removed '../html/documentation_pages/selections_modules.html' +removed '../html/documentation_pages/topology.html' +removed '../html/documentation_pages/topology/CRDParser.html' +removed '../html/documentation_pages/topology/DLPolyParser.html' +removed '../html/documentation_pages/topology/DMSParser.html' +removed '../html/documentation_pages/topology/ExtendedPDBParser.html' +removed '../html/documentation_pages/topology/FHIAIMSParser.html' +removed '../html/documentation_pages/topology/GMSParser.html' +removed '../html/documentation_pages/topology/GROParser.html' +removed '../html/documentation_pages/topology/GSDParser.html' +removed '../html/documentation_pages/topology/HoomdXMLParser.html' +removed '../html/documentation_pages/topology/ITPParser.html' +removed '../html/documentation_pages/topology/LAMMPSParser.html' +removed '../html/documentation_pages/topology/MMTFParser.html' +removed '../html/documentation_pages/topology/MOL2Parser.html' +removed '../html/documentation_pages/topology/MinimalParser.html' +removed '../html/documentation_pages/topology/PDBParser.html' +removed '../html/documentation_pages/topology/PDBQTParser.html' +removed '../html/documentation_pages/topology/PQRParser.html' +removed '../html/documentation_pages/topology/PSFParser.html' +removed '../html/documentation_pages/topology/TOPParser.html' +removed '../html/documentation_pages/topology/TPRParser.html' +removed '../html/documentation_pages/topology/TXYZParser.html' +removed '../html/documentation_pages/topology/XYZParser.html' +removed '../html/documentation_pages/topology/base.html' +removed '../html/documentation_pages/topology/core.html' +removed '../html/documentation_pages/topology/guessers.html' +removed '../html/documentation_pages/topology/init.html' +removed '../html/documentation_pages/topology/tables.html' +removed '../html/documentation_pages/topology/tpr_util.html' +removed directory '../html/documentation_pages/topology' +removed '../html/documentation_pages/topology_modules.html' +removed '../html/documentation_pages/trajectory_transformations.html' +removed '../html/documentation_pages/transformations/base.html' +removed '../html/documentation_pages/transformations/boxdimensions.html' +removed '../html/documentation_pages/transformations/fit.html' +removed '../html/documentation_pages/transformations/nojump.html' +removed '../html/documentation_pages/transformations/positionaveraging.html' +removed '../html/documentation_pages/transformations/rotate.html' +removed '../html/documentation_pages/transformations/translate.html' +removed '../html/documentation_pages/transformations/wrap.html' +removed directory '../html/documentation_pages/transformations' +removed '../html/documentation_pages/units.html' +removed '../html/documentation_pages/version.html' +removed '../html/documentation_pages/visualization/streamlines.html' +removed '../html/documentation_pages/visualization/streamlines_3D.html' +removed directory '../html/documentation_pages/visualization' +removed '../html/documentation_pages/visualization_modules.html' +removed directory '../html/documentation_pages' +removed '../html/genindex.html' +removed '../html/_images/rama_ref_plot.png' +removed '../html/_images/janin_ref_plot.png' +removed '../html/_images/rama_demo_plot.png' +removed '../html/_images/janin_demo_plot.png' +removed '../html/_images/msd_demo_plot.png' +removed '../html/_images/AnalysisBase_parallel.png' +removed '../html/_images/parallelization_time.png' +removed '../html/_images/RSMD_plot.png' +removed '../html/_images/testing_streamline.png' +removed '../html/_images/test_streamplot_3D.png' +removed directory '../html/_images' +removed '../html/index.html' +removed '../html/_modules/MDAnalysis/analysis/align.html' +removed '../html/_modules/MDAnalysis/analysis/atomicdistances.html' +removed '../html/_modules/MDAnalysis/analysis/backends.html' +removed '../html/_modules/MDAnalysis/analysis/base.html' +removed '../html/_modules/MDAnalysis/analysis/bat.html' +removed '../html/_modules/MDAnalysis/analysis/contacts.html' +removed '../html/_modules/MDAnalysis/analysis/density.html' +removed '../html/_modules/MDAnalysis/analysis/dielectric.html' +removed '../html/_modules/MDAnalysis/analysis/diffusionmap.html' +removed '../html/_modules/MDAnalysis/analysis/dihedrals.html' +removed '../html/_modules/MDAnalysis/analysis/distances.html' +removed '../html/_modules/MDAnalysis/analysis/dssp/dssp.html' +removed '../html/_modules/MDAnalysis/analysis/dssp/pydssp_numpy.html' +removed directory '../html/_modules/MDAnalysis/analysis/dssp' +removed '../html/_modules/MDAnalysis/analysis/encore/bootstrap.html' +removed '../html/_modules/MDAnalysis/analysis/encore/clustering/ClusterCollection.html' +removed '../html/_modules/MDAnalysis/analysis/encore/clustering/ClusteringMethod.html' +removed '../html/_modules/MDAnalysis/analysis/encore/clustering/cluster.html' +removed directory '../html/_modules/MDAnalysis/analysis/encore/clustering' +removed '../html/_modules/MDAnalysis/analysis/encore/confdistmatrix.html' +removed '../html/_modules/MDAnalysis/analysis/encore/covariance.html' +removed '../html/_modules/MDAnalysis/analysis/encore/dimensionality_reduction/DimensionalityReductionMethod.html' +removed '../html/_modules/MDAnalysis/analysis/encore/dimensionality_reduction/reduce_dimensionality.html' +removed directory '../html/_modules/MDAnalysis/analysis/encore/dimensionality_reduction' +removed '../html/_modules/MDAnalysis/analysis/encore/similarity.html' +removed '../html/_modules/MDAnalysis/analysis/encore/utils.html' +removed directory '../html/_modules/MDAnalysis/analysis/encore' +removed '../html/_modules/MDAnalysis/analysis/gnm.html' +removed '../html/_modules/MDAnalysis/analysis/helix_analysis.html' +removed '../html/_modules/MDAnalysis/analysis/hydrogenbonds/hbond_analysis.html' +removed '../html/_modules/MDAnalysis/analysis/hydrogenbonds/hbond_autocorrel.html' +removed '../html/_modules/MDAnalysis/analysis/hydrogenbonds/wbridge_analysis.html' +removed directory '../html/_modules/MDAnalysis/analysis/hydrogenbonds' +removed '../html/_modules/MDAnalysis/analysis/leaflet.html' +removed '../html/_modules/MDAnalysis/analysis/legacy/x3dna.html' +removed directory '../html/_modules/MDAnalysis/analysis/legacy' +removed '../html/_modules/MDAnalysis/analysis/lineardensity.html' +removed '../html/_modules/MDAnalysis/analysis/msd.html' +removed '../html/_modules/MDAnalysis/analysis/nucleicacids.html' +removed '../html/_modules/MDAnalysis/analysis/nuclinfo.html' +removed '../html/_modules/MDAnalysis/analysis/pca.html' +removed '../html/_modules/MDAnalysis/analysis/polymer.html' +removed '../html/_modules/MDAnalysis/analysis/rdf.html' +removed '../html/_modules/MDAnalysis/analysis/results.html' +removed '../html/_modules/MDAnalysis/analysis/rms.html' +removed directory '../html/_modules/MDAnalysis/analysis' +removed '../html/_modules/MDAnalysis/auxiliary/EDR.html' +removed '../html/_modules/MDAnalysis/auxiliary/XVG.html' +removed '../html/_modules/MDAnalysis/auxiliary/base.html' +removed '../html/_modules/MDAnalysis/auxiliary/core.html' +removed directory '../html/_modules/MDAnalysis/auxiliary' +removed '../html/_modules/MDAnalysis/converters/OpenMM.html' +removed '../html/_modules/MDAnalysis/converters/OpenMMParser.html' +removed '../html/_modules/MDAnalysis/converters/ParmEd.html' +removed '../html/_modules/MDAnalysis/converters/ParmEdParser.html' +removed '../html/_modules/MDAnalysis/converters/RDKit.html' +removed '../html/_modules/MDAnalysis/converters/RDKitInferring.html' +removed '../html/_modules/MDAnalysis/converters/RDKitParser.html' +removed '../html/_modules/MDAnalysis/converters/base.html' +removed directory '../html/_modules/MDAnalysis/converters' +removed '../html/_modules/MDAnalysis/coordinates/CRD.html' +removed '../html/_modules/MDAnalysis/coordinates/DCD.html' +removed '../html/_modules/MDAnalysis/coordinates/DLPoly.html' +removed '../html/_modules/MDAnalysis/coordinates/DMS.html' +removed '../html/_modules/MDAnalysis/coordinates/FHIAIMS.html' +removed '../html/_modules/MDAnalysis/coordinates/GMS.html' +removed '../html/_modules/MDAnalysis/coordinates/GRO.html' +removed '../html/_modules/MDAnalysis/coordinates/GSD.html' +removed '../html/_modules/MDAnalysis/coordinates/H5MD.html' +removed '../html/_modules/MDAnalysis/coordinates/IMD.html' +removed '../html/_modules/MDAnalysis/coordinates/INPCRD.html' +removed '../html/_modules/MDAnalysis/coordinates/LAMMPS.html' +removed '../html/_modules/MDAnalysis/coordinates/MMTF.html' +removed '../html/_modules/MDAnalysis/coordinates/MOL2.html' +removed '../html/_modules/MDAnalysis/coordinates/NAMDBIN.html' +removed '../html/_modules/MDAnalysis/coordinates/PDB.html' +removed '../html/_modules/MDAnalysis/coordinates/PDBQT.html' +removed '../html/_modules/MDAnalysis/coordinates/PQR.html' +removed '../html/_modules/MDAnalysis/coordinates/TNG.html' +removed '../html/_modules/MDAnalysis/coordinates/TPR.html' +removed '../html/_modules/MDAnalysis/coordinates/TRC.html' +removed '../html/_modules/MDAnalysis/coordinates/TRJ.html' +removed '../html/_modules/MDAnalysis/coordinates/TRR.html' +removed '../html/_modules/MDAnalysis/coordinates/TRZ.html' +removed '../html/_modules/MDAnalysis/coordinates/TXYZ.html' +removed '../html/_modules/MDAnalysis/coordinates/XDR.html' +removed '../html/_modules/MDAnalysis/coordinates/XTC.html' +removed '../html/_modules/MDAnalysis/coordinates/XYZ.html' +removed '../html/_modules/MDAnalysis/coordinates/base.html' +removed '../html/_modules/MDAnalysis/coordinates/chain.html' +removed '../html/_modules/MDAnalysis/coordinates/chemfiles.html' +removed '../html/_modules/MDAnalysis/coordinates/core.html' +removed '../html/_modules/MDAnalysis/coordinates/memory.html' +removed '../html/_modules/MDAnalysis/coordinates/null.html' +removed directory '../html/_modules/MDAnalysis/coordinates' +removed '../html/_modules/MDAnalysis/core/_get_readers.html' +removed '../html/_modules/MDAnalysis/core/accessors.html' +removed '../html/_modules/MDAnalysis/core/groups.html' +removed '../html/_modules/MDAnalysis/core/selection.html' +removed '../html/_modules/MDAnalysis/core/topology.html' +removed '../html/_modules/MDAnalysis/core/topologyattrs.html' +removed '../html/_modules/MDAnalysis/core/topologyobjects.html' +removed '../html/_modules/MDAnalysis/core/universe.html' +removed directory '../html/_modules/MDAnalysis/core' +removed '../html/_modules/MDAnalysis/exceptions.html' +removed '../html/_modules/MDAnalysis/fetch/fetchers.html' +removed '../html/_modules/MDAnalysis/fetch/pdb.html' +removed directory '../html/_modules/MDAnalysis/fetch' +removed '../html/_modules/MDAnalysis/guesser/base.html' +removed '../html/_modules/MDAnalysis/guesser/default_guesser.html' +removed '../html/_modules/MDAnalysis/guesser/tables.html' +removed directory '../html/_modules/MDAnalysis/guesser' +removed '../html/_modules/MDAnalysis/lib/NeighborSearch.html' +removed '../html/_modules/MDAnalysis/lib/correlations.html' +removed '../html/_modules/MDAnalysis/lib/distances.html' +removed '../html/_modules/MDAnalysis/lib/log.html' +removed '../html/_modules/MDAnalysis/lib/mdamath.html' +removed '../html/_modules/MDAnalysis/lib/picklable_file_io.html' +removed '../html/_modules/MDAnalysis/lib/pkdtree.html' +removed '../html/_modules/MDAnalysis/lib/transformations.html' +removed '../html/_modules/MDAnalysis/lib/util.html' +removed directory '../html/_modules/MDAnalysis/lib' +removed '../html/_modules/MDAnalysis/selections.html' +removed '../html/_modules/MDAnalysis/selections/base.html' +removed '../html/_modules/MDAnalysis/selections/charmm.html' +removed '../html/_modules/MDAnalysis/selections/gromacs.html' +removed '../html/_modules/MDAnalysis/selections/jmol.html' +removed '../html/_modules/MDAnalysis/selections/pymol.html' +removed '../html/_modules/MDAnalysis/selections/vmd.html' +removed directory '../html/_modules/MDAnalysis/selections' +removed '../html/_modules/MDAnalysis/topology/CRDParser.html' +removed '../html/_modules/MDAnalysis/topology/DLPolyParser.html' +removed '../html/_modules/MDAnalysis/topology/DMSParser.html' +removed '../html/_modules/MDAnalysis/topology/ExtendedPDBParser.html' +removed '../html/_modules/MDAnalysis/topology/FHIAIMSParser.html' +removed '../html/_modules/MDAnalysis/topology/GMSParser.html' +removed '../html/_modules/MDAnalysis/topology/GROParser.html' +removed '../html/_modules/MDAnalysis/topology/GSDParser.html' +removed '../html/_modules/MDAnalysis/topology/HoomdXMLParser.html' +removed '../html/_modules/MDAnalysis/topology/ITPParser.html' +removed '../html/_modules/MDAnalysis/topology/LAMMPSParser.html' +removed '../html/_modules/MDAnalysis/topology/MMTFParser.html' +removed '../html/_modules/MDAnalysis/topology/MOL2Parser.html' +removed '../html/_modules/MDAnalysis/topology/MinimalParser.html' +removed '../html/_modules/MDAnalysis/topology/PDBParser.html' +removed '../html/_modules/MDAnalysis/topology/PDBQTParser.html' +removed '../html/_modules/MDAnalysis/topology/PQRParser.html' +removed '../html/_modules/MDAnalysis/topology/PSFParser.html' +removed '../html/_modules/MDAnalysis/topology/TOPParser.html' +removed '../html/_modules/MDAnalysis/topology/TPRParser.html' +removed '../html/_modules/MDAnalysis/topology/TXYZParser.html' +removed '../html/_modules/MDAnalysis/topology/XYZParser.html' +removed '../html/_modules/MDAnalysis/topology/base.html' +removed '../html/_modules/MDAnalysis/topology/tpr/obj.html' +removed '../html/_modules/MDAnalysis/topology/tpr/utils.html' +removed directory '../html/_modules/MDAnalysis/topology/tpr' +removed directory '../html/_modules/MDAnalysis/topology' +removed '../html/_modules/MDAnalysis/transformations/base.html' +removed '../html/_modules/MDAnalysis/transformations/boxdimensions.html' +removed '../html/_modules/MDAnalysis/transformations/fit.html' +removed '../html/_modules/MDAnalysis/transformations/nojump.html' +removed '../html/_modules/MDAnalysis/transformations/positionaveraging.html' +removed '../html/_modules/MDAnalysis/transformations/rotate.html' +removed '../html/_modules/MDAnalysis/transformations/translate.html' +removed '../html/_modules/MDAnalysis/transformations/wrap.html' +removed directory '../html/_modules/MDAnalysis/transformations' +removed '../html/_modules/MDAnalysis/units.html' +removed '../html/_modules/MDAnalysis/visualization/streamlines.html' +removed '../html/_modules/MDAnalysis/visualization/streamlines_3D.html' +removed directory '../html/_modules/MDAnalysis/visualization' +removed directory '../html/_modules/MDAnalysis' +removed '../html/_modules/index.html' +removed directory '../html/_modules' +removed '../html/objects.inv' +removed '../html/py-modindex.html' +removed '../html/search.html' +removed '../html/searchindex.js' +removed '../html/sitemap.xml' +removed '../html/_sources/documentation_pages/analysis/align.rst.txt' +removed '../html/_sources/documentation_pages/analysis/atomicdistances.rst.txt' +removed '../html/_sources/documentation_pages/analysis/backends.rst.txt' +removed '../html/_sources/documentation_pages/analysis/base.rst.txt' +removed '../html/_sources/documentation_pages/analysis/bat.rst.txt' +removed '../html/_sources/documentation_pages/analysis/contacts.rst.txt' +removed '../html/_sources/documentation_pages/analysis/data.rst.txt' +removed '../html/_sources/documentation_pages/analysis/density.rst.txt' +removed '../html/_sources/documentation_pages/analysis/dielectric.rst.txt' +removed '../html/_sources/documentation_pages/analysis/diffusionmap.rst.txt' +removed '../html/_sources/documentation_pages/analysis/dihedrals.rst.txt' +removed '../html/_sources/documentation_pages/analysis/distances.rst.txt' +removed '../html/_sources/documentation_pages/analysis/dssp.rst.txt' +removed '../html/_sources/documentation_pages/analysis/encore.rst.txt' +removed '../html/_sources/documentation_pages/analysis/encore/bootstrap.rst.txt' +removed '../html/_sources/documentation_pages/analysis/encore/clustering.rst.txt' +removed '../html/_sources/documentation_pages/analysis/encore/confdistmatrix.rst.txt' +removed '../html/_sources/documentation_pages/analysis/encore/covariance.rst.txt' +removed '../html/_sources/documentation_pages/analysis/encore/dimensionality_reduction.rst.txt' +removed '../html/_sources/documentation_pages/analysis/encore/similarity.rst.txt' +removed '../html/_sources/documentation_pages/analysis/encore/utils.rst.txt' +removed directory '../html/_sources/documentation_pages/analysis/encore' +removed '../html/_sources/documentation_pages/analysis/gnm.rst.txt' +removed '../html/_sources/documentation_pages/analysis/hbond_autocorrel.rst.txt' +removed '../html/_sources/documentation_pages/analysis/hbond_autocorrel_deprecated.rst.txt' +removed '../html/_sources/documentation_pages/analysis/helix_analysis.rst.txt' +removed '../html/_sources/documentation_pages/analysis/hole2.rst.txt' +removed '../html/_sources/documentation_pages/analysis/hydrogenbonds.rst.txt' +removed '../html/_sources/documentation_pages/analysis/leaflet.rst.txt' +removed '../html/_sources/documentation_pages/analysis/legacy/x3dna.rst.txt' +removed directory '../html/_sources/documentation_pages/analysis/legacy' +removed '../html/_sources/documentation_pages/analysis/legacy_modules.rst.txt' +removed '../html/_sources/documentation_pages/analysis/lineardensity.rst.txt' +removed '../html/_sources/documentation_pages/analysis/msd.rst.txt' +removed '../html/_sources/documentation_pages/analysis/nucleicacids.rst.txt' +removed '../html/_sources/documentation_pages/analysis/nuclinfo.rst.txt' +removed '../html/_sources/documentation_pages/analysis/parallelization.rst.txt' +removed '../html/_sources/documentation_pages/analysis/pca.rst.txt' +removed '../html/_sources/documentation_pages/analysis/polymer.rst.txt' +removed '../html/_sources/documentation_pages/analysis/psa.rst.txt' +removed '../html/_sources/documentation_pages/analysis/rdf.rst.txt' +removed '../html/_sources/documentation_pages/analysis/results.rst.txt' +removed '../html/_sources/documentation_pages/analysis/rms.rst.txt' +removed '../html/_sources/documentation_pages/analysis/waterdynamics.rst.txt' +removed '../html/_sources/documentation_pages/analysis/wbridge_analysis.rst.txt' +removed directory '../html/_sources/documentation_pages/analysis' +removed '../html/_sources/documentation_pages/analysis_modules.rst.txt' +removed '../html/_sources/documentation_pages/auxiliary/EDR.rst.txt' +removed '../html/_sources/documentation_pages/auxiliary/XVG.rst.txt' +removed '../html/_sources/documentation_pages/auxiliary/base.rst.txt' +removed '../html/_sources/documentation_pages/auxiliary/core.rst.txt' +removed '../html/_sources/documentation_pages/auxiliary/init.rst.txt' +removed directory '../html/_sources/documentation_pages/auxiliary' +removed '../html/_sources/documentation_pages/auxiliary_modules.rst.txt' +removed '../html/_sources/documentation_pages/converters.rst.txt' +removed '../html/_sources/documentation_pages/converters/OpenMM.rst.txt' +removed '../html/_sources/documentation_pages/converters/ParmEd.rst.txt' +removed '../html/_sources/documentation_pages/converters/RDKit.rst.txt' +removed '../html/_sources/documentation_pages/converters/base.rst.txt' +removed directory '../html/_sources/documentation_pages/converters' +removed '../html/_sources/documentation_pages/coordinates/CRD.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/DCD.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/DLPoly.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/DMS.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/FHIAIMS.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/GMS.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/GRO.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/GSD.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/H5MD.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/IMD.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/INPCRD.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/LAMMPS.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/MMTF.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/MOL2.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/NAMDBIN.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/PDB.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/PDBQT.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/PQR.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/TNG.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/TPR.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/TRC.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/TRJ.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/TRR.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/TRZ.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/TXYZ.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/XDR.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/XTC.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/XYZ.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/base.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/chain.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/chemfiles.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/core.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/init.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/memory.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/null.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/pickle_readers.rst.txt' +removed '../html/_sources/documentation_pages/coordinates/timestep.rst.txt' +removed directory '../html/_sources/documentation_pages/coordinates' +removed '../html/_sources/documentation_pages/coordinates_modules.rst.txt' +removed '../html/_sources/documentation_pages/core/accessors.rst.txt' +removed '../html/_sources/documentation_pages/core/groups.rst.txt' +removed '../html/_sources/documentation_pages/core/init.rst.txt' +removed '../html/_sources/documentation_pages/core/selection.rst.txt' +removed '../html/_sources/documentation_pages/core/topology.rst.txt' +removed '../html/_sources/documentation_pages/core/topologyattrs.rst.txt' +removed '../html/_sources/documentation_pages/core/topologyobjects.rst.txt' +removed '../html/_sources/documentation_pages/core/universe.rst.txt' +removed directory '../html/_sources/documentation_pages/core' +removed '../html/_sources/documentation_pages/core_modules.rst.txt' +removed '../html/_sources/documentation_pages/exceptions.rst.txt' +removed '../html/_sources/documentation_pages/fetchers/PDB.rst.txt' +removed '../html/_sources/documentation_pages/fetchers/fetchers.rst.txt' +removed '../html/_sources/documentation_pages/fetchers/init.rst.txt' +removed directory '../html/_sources/documentation_pages/fetchers' +removed '../html/_sources/documentation_pages/fetchers_modules.rst.txt' +removed '../html/_sources/documentation_pages/guesser_modules.rst.txt' +removed '../html/_sources/documentation_pages/guesser_modules/base.rst.txt' +removed '../html/_sources/documentation_pages/guesser_modules/default_guesser.rst.txt' +removed '../html/_sources/documentation_pages/guesser_modules/init.rst.txt' +removed '../html/_sources/documentation_pages/guesser_modules/tables.rst.txt' +removed directory '../html/_sources/documentation_pages/guesser_modules' +removed '../html/_sources/documentation_pages/lib/NeighborSearch.rst.txt' +removed '../html/_sources/documentation_pages/lib/c_distances.rst.txt' +removed '../html/_sources/documentation_pages/lib/c_distances_openmp.rst.txt' +removed '../html/_sources/documentation_pages/lib/correlations.rst.txt' +removed '../html/_sources/documentation_pages/lib/distances.rst.txt' +removed '../html/_sources/documentation_pages/lib/formats/libdcd.rst.txt' +removed '../html/_sources/documentation_pages/lib/formats/libmdaxdr.rst.txt' +removed directory '../html/_sources/documentation_pages/lib/formats' +removed '../html/_sources/documentation_pages/lib/log.rst.txt' +removed '../html/_sources/documentation_pages/lib/mdamath.rst.txt' +removed '../html/_sources/documentation_pages/lib/nsgrid.rst.txt' +removed '../html/_sources/documentation_pages/lib/picklable_file_io.rst.txt' +removed '../html/_sources/documentation_pages/lib/pkdtree.rst.txt' +removed '../html/_sources/documentation_pages/lib/qcprot.rst.txt' +removed '../html/_sources/documentation_pages/lib/transformations.rst.txt' +removed '../html/_sources/documentation_pages/lib/util.rst.txt' +removed directory '../html/_sources/documentation_pages/lib' +removed '../html/_sources/documentation_pages/lib_modules.rst.txt' +removed '../html/_sources/documentation_pages/overview.rst.txt' +removed '../html/_sources/documentation_pages/references.rst.txt' +removed '../html/_sources/documentation_pages/selections.rst.txt' +removed '../html/_sources/documentation_pages/selections/base.rst.txt' +removed '../html/_sources/documentation_pages/selections/charmm.rst.txt' +removed '../html/_sources/documentation_pages/selections/gromacs.rst.txt' +removed '../html/_sources/documentation_pages/selections/jmol.rst.txt' +removed '../html/_sources/documentation_pages/selections/pymol.rst.txt' +removed '../html/_sources/documentation_pages/selections/vmd.rst.txt' +removed directory '../html/_sources/documentation_pages/selections' +removed '../html/_sources/documentation_pages/selections_modules.rst.txt' +removed '../html/_sources/documentation_pages/topology.rst.txt' +removed '../html/_sources/documentation_pages/topology/CRDParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/DLPolyParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/DMSParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/ExtendedPDBParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/FHIAIMSParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/GMSParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/GROParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/GSDParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/HoomdXMLParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/ITPParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/LAMMPSParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/MMTFParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/MOL2Parser.rst.txt' +removed '../html/_sources/documentation_pages/topology/MinimalParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/PDBParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/PDBQTParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/PQRParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/PSFParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/TOPParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/TPRParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/TXYZParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/XYZParser.rst.txt' +removed '../html/_sources/documentation_pages/topology/base.rst.txt' +removed '../html/_sources/documentation_pages/topology/core.rst.txt' +removed '../html/_sources/documentation_pages/topology/guessers.rst.txt' +removed '../html/_sources/documentation_pages/topology/init.rst.txt' +removed '../html/_sources/documentation_pages/topology/tables.rst.txt' +removed '../html/_sources/documentation_pages/topology/tpr_util.rst.txt' +removed directory '../html/_sources/documentation_pages/topology' +removed '../html/_sources/documentation_pages/topology_modules.rst.txt' +removed '../html/_sources/documentation_pages/trajectory_transformations.rst.txt' +removed '../html/_sources/documentation_pages/transformations/base.rst.txt' +removed '../html/_sources/documentation_pages/transformations/boxdimensions.rst.txt' +removed '../html/_sources/documentation_pages/transformations/fit.rst.txt' +removed '../html/_sources/documentation_pages/transformations/nojump.rst.txt' +removed '../html/_sources/documentation_pages/transformations/positionaveraging.rst.txt' +removed '../html/_sources/documentation_pages/transformations/rotate.rst.txt' +removed '../html/_sources/documentation_pages/transformations/translate.rst.txt' +removed '../html/_sources/documentation_pages/transformations/wrap.rst.txt' +removed directory '../html/_sources/documentation_pages/transformations' +removed '../html/_sources/documentation_pages/units.rst.txt' +removed '../html/_sources/documentation_pages/version.rst.txt' +removed '../html/_sources/documentation_pages/visualization/streamlines.rst.txt' +removed '../html/_sources/documentation_pages/visualization/streamlines_3D.rst.txt' +removed directory '../html/_sources/documentation_pages/visualization' +removed '../html/_sources/documentation_pages/visualization_modules.rst.txt' +removed directory '../html/_sources/documentation_pages' +removed '../html/_sources/index.rst.txt' +removed directory '../html/_sources' +removed '../html/_static/jquery.js' +removed '../html/_static/_sphinx_javascript_frameworks_compat.js' +removed '../html/_static/pygments.css' +removed '../html/_static/basic.css' +removed '../html/_static/doctools.js' +removed '../html/_static/documentation_options.js' +removed '../html/_static/file.png' +removed '../html/_static/language_data.js' +removed '../html/_static/minus.png' +removed '../html/_static/plus.png' +removed '../html/_static/searchtools.js' +removed '../html/_static/sphinx_highlight.js' +removed '../html/_static/css/fonts/Roboto-Slab-Bold.woff' +removed '../html/_static/css/fonts/Roboto-Slab-Bold.woff2' +removed '../html/_static/css/fonts/Roboto-Slab-Regular.woff' +removed '../html/_static/css/fonts/Roboto-Slab-Regular.woff2' +removed '../html/_static/css/fonts/fontawesome-webfont.eot' +removed '../html/_static/css/fonts/fontawesome-webfont.svg' +removed '../html/_static/css/fonts/fontawesome-webfont.ttf' +removed '../html/_static/css/fonts/fontawesome-webfont.woff' +removed '../html/_static/css/fonts/fontawesome-webfont.woff2' +removed '../html/_static/css/fonts/lato-bold-italic.woff' +removed '../html/_static/css/fonts/lato-bold-italic.woff2' +removed '../html/_static/css/fonts/lato-bold.woff' +removed '../html/_static/css/fonts/lato-bold.woff2' +removed '../html/_static/css/fonts/lato-normal-italic.woff' +removed '../html/_static/css/fonts/lato-normal-italic.woff2' +removed '../html/_static/css/fonts/lato-normal.woff' +removed '../html/_static/css/fonts/lato-normal.woff2' +removed directory '../html/_static/css/fonts' +removed '../html/_static/css/badge_only.css' +removed '../html/_static/css/theme.css' +removed directory '../html/_static/css' +removed '../html/_static/fonts/Lato/lato-bold.eot' +removed '../html/_static/fonts/Lato/lato-bold.ttf' +removed '../html/_static/fonts/Lato/lato-bold.woff' +removed '../html/_static/fonts/Lato/lato-bold.woff2' +removed '../html/_static/fonts/Lato/lato-bolditalic.eot' +removed '../html/_static/fonts/Lato/lato-bolditalic.ttf' +removed '../html/_static/fonts/Lato/lato-bolditalic.woff' +removed '../html/_static/fonts/Lato/lato-bolditalic.woff2' +removed '../html/_static/fonts/Lato/lato-italic.eot' +removed '../html/_static/fonts/Lato/lato-italic.ttf' +removed '../html/_static/fonts/Lato/lato-italic.woff' +removed '../html/_static/fonts/Lato/lato-italic.woff2' +removed '../html/_static/fonts/Lato/lato-regular.eot' +removed '../html/_static/fonts/Lato/lato-regular.ttf' +removed '../html/_static/fonts/Lato/lato-regular.woff' +removed '../html/_static/fonts/Lato/lato-regular.woff2' +removed directory '../html/_static/fonts/Lato' +removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot' +removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf' +removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff' +removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2' +removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot' +removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf' +removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff' +removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2' +removed directory '../html/_static/fonts/RobotoSlab' +removed directory '../html/_static/fonts' +removed '../html/_static/js/badge_only.js' +removed '../html/_static/js/theme.js' +removed '../html/_static/js/versions.js' +removed directory '../html/_static/js' +removed '../html/_static/logo/mda_favicon.ico' +removed '../html/_static/logo/mda_logo.png' +removed '../html/_static/logo/placeholder_favicon.svg' +removed '../html/_static/logo/placeholder_logo.png' +removed directory '../html/_static/logo' +removed '../html/_static/opensearch.xml' +removed '../html/_static/site.css' +removed directory '../html/_static' +sphinx-build -v -W -b html source ../html +Running Sphinx v8.2.3 +loading translations [en]... locale_dir /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/sphinx/source/locales/en/LC_MESSAGES does not exist +locale_dir /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/sphinx/source/locales/en/LC_MESSAGES does not exist +done +Converting `source_suffix = '.rst'` to `source_suffix = {'.rst': 'restructuredtext'}`. +loading pickled environment... The configuration has changed (2 options: 'html_permalinks_icon', 'jquery_use_sri') +done +checking bibtex cache... up to date +locale_dir /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/sphinx/source/locales/en/LC_MESSAGES does not exist +building [mo]: targets for 0 po files that are out of date +writing output... +building [html]: targets for 183 source files that are out of date +updating environment: locale_dir /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/sphinx/source/locales/en/LC_MESSAGES does not exist +0 added, 1 changed, 0 removed +reading sources... [100%] documentation_pages/fetchers/fetchers + +looking for now-outdated files... none found +pickling environment... done +checking consistency... done +preparing documents... done +copying assets... +copying static files... +Writing evaluated template result to /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/_static/basic.css +Writing evaluated template result to /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/_static/documentation_options.js +Writing evaluated template result to /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/_static/language_data.js +Writing evaluated template result to /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/_static/js/versions.js +copying static files: done +copying extra files... +copying extra files: done +copying assets: done +writing output... [ 1%] documentation_pages/analysis/align +writing output... [ 1%] documentation_pages/analysis/atomicdistances +writing output... [ 2%] documentation_pages/analysis/backends +writing output... [ 2%] documentation_pages/analysis/base +writing output... [ 3%] documentation_pages/analysis/bat +writing output... [ 3%] documentation_pages/analysis/contacts +writing output... [ 4%] documentation_pages/analysis/data +writing output... [ 4%] documentation_pages/analysis/density +writing output... [ 5%] documentation_pages/analysis/dielectric +writing output... [ 5%] documentation_pages/analysis/diffusionmap +writing output... [ 6%] documentation_pages/analysis/dihedrals +writing output... [ 7%] documentation_pages/analysis/distances +writing output... [ 7%] documentation_pages/analysis/dssp +writing output... [ 8%] documentation_pages/analysis/encore +writing output... [ 8%] documentation_pages/analysis/encore/bootstrap +writing output... [ 9%] documentation_pages/analysis/encore/clustering +writing output... [ 9%] documentation_pages/analysis/encore/confdistmatrix +writing output... [ 10%] documentation_pages/analysis/encore/covariance +writing output... [ 10%] documentation_pages/analysis/encore/dimensionality_reduction +writing output... [ 11%] documentation_pages/analysis/encore/similarity +writing output... [ 11%] documentation_pages/analysis/encore/utils +writing output... [ 12%] documentation_pages/analysis/gnm +writing output... [ 13%] documentation_pages/analysis/hbond_autocorrel +writing output... [ 13%] documentation_pages/analysis/hbond_autocorrel_deprecated +writing output... [ 14%] documentation_pages/analysis/helix_analysis +writing output... [ 14%] documentation_pages/analysis/hole2 +writing output... [ 15%] documentation_pages/analysis/hydrogenbonds +writing output... [ 15%] documentation_pages/analysis/leaflet +writing output... [ 16%] documentation_pages/analysis/legacy/x3dna +writing output... [ 16%] documentation_pages/analysis/legacy_modules +writing output... [ 17%] documentation_pages/analysis/lineardensity +writing output... [ 17%] documentation_pages/analysis/msd +writing output... [ 18%] documentation_pages/analysis/nucleicacids +writing output... [ 19%] documentation_pages/analysis/nuclinfo +writing output... [ 19%] documentation_pages/analysis/parallelization +writing output... [ 20%] documentation_pages/analysis/pca +writing output... [ 20%] documentation_pages/analysis/polymer +writing output... [ 21%] documentation_pages/analysis/psa +writing output... [ 21%] documentation_pages/analysis/rdf +writing output... [ 22%] documentation_pages/analysis/results +writing output... [ 22%] documentation_pages/analysis/rms +writing output... [ 23%] documentation_pages/analysis/waterdynamics +writing output... [ 23%] documentation_pages/analysis/wbridge_analysis +writing output... [ 24%] documentation_pages/analysis_modules +writing output... [ 25%] documentation_pages/auxiliary/EDR +writing output... [ 25%] documentation_pages/auxiliary/XVG +writing output... [ 26%] documentation_pages/auxiliary/base +writing output... [ 26%] documentation_pages/auxiliary/core +writing output... [ 27%] documentation_pages/auxiliary/init +writing output... [ 27%] documentation_pages/auxiliary_modules +writing output... [ 28%] documentation_pages/converters +writing output... [ 28%] documentation_pages/converters/OpenMM +writing output... [ 29%] documentation_pages/converters/ParmEd +writing output... [ 30%] documentation_pages/converters/RDKit +writing output... [ 30%] documentation_pages/converters/base +writing output... [ 31%] documentation_pages/coordinates/CRD +writing output... [ 31%] documentation_pages/coordinates/DCD +writing output... [ 32%] documentation_pages/coordinates/DLPoly +writing output... [ 32%] documentation_pages/coordinates/DMS +writing output... [ 33%] documentation_pages/coordinates/FHIAIMS +writing output... [ 33%] documentation_pages/coordinates/GMS +writing output... [ 34%] documentation_pages/coordinates/GRO +writing output... [ 34%] documentation_pages/coordinates/GSD +writing output... [ 35%] documentation_pages/coordinates/H5MD +writing output... [ 36%] documentation_pages/coordinates/IMD +writing output... [ 36%] documentation_pages/coordinates/INPCRD +writing output... [ 37%] documentation_pages/coordinates/LAMMPS +writing output... [ 37%] documentation_pages/coordinates/MMTF +writing output... [ 38%] documentation_pages/coordinates/MOL2 +writing output... [ 38%] documentation_pages/coordinates/NAMDBIN +writing output... [ 39%] documentation_pages/coordinates/PDB +writing output... [ 39%] documentation_pages/coordinates/PDBQT +writing output... [ 40%] documentation_pages/coordinates/PQR +writing output... [ 40%] documentation_pages/coordinates/TNG +writing output... [ 41%] documentation_pages/coordinates/TPR +writing output... [ 42%] documentation_pages/coordinates/TRC +writing output... [ 42%] documentation_pages/coordinates/TRJ +writing output... [ 43%] documentation_pages/coordinates/TRR +writing output... [ 43%] documentation_pages/coordinates/TRZ +writing output... [ 44%] documentation_pages/coordinates/TXYZ +writing output... [ 44%] documentation_pages/coordinates/XDR +writing output... [ 45%] documentation_pages/coordinates/XTC +writing output... [ 45%] documentation_pages/coordinates/XYZ +writing output... [ 46%] documentation_pages/coordinates/base +writing output... [ 46%] documentation_pages/coordinates/chain +writing output... [ 47%] documentation_pages/coordinates/chemfiles +writing output... [ 48%] documentation_pages/coordinates/core +writing output... [ 48%] documentation_pages/coordinates/init +writing output... [ 49%] documentation_pages/coordinates/memory +writing output... [ 49%] documentation_pages/coordinates/null +writing output... [ 50%] documentation_pages/coordinates/pickle_readers +writing output... [ 50%] documentation_pages/coordinates/timestep +writing output... [ 51%] documentation_pages/coordinates_modules +writing output... [ 51%] documentation_pages/core/accessors +writing output... [ 52%] documentation_pages/core/groups +writing output... [ 52%] documentation_pages/core/init +writing output... [ 53%] documentation_pages/core/selection +writing output... [ 54%] documentation_pages/core/topology +writing output... [ 54%] documentation_pages/core/topologyattrs +writing output... [ 55%] documentation_pages/core/topologyobjects +writing output... [ 55%] documentation_pages/core/universe +writing output... [ 56%] documentation_pages/core_modules +writing output... [ 56%] documentation_pages/exceptions +writing output... [ 57%] documentation_pages/fetchers/PDB +writing output... [ 57%] documentation_pages/fetchers/fetchers +writing output... [ 58%] documentation_pages/fetchers/init +writing output... [ 58%] documentation_pages/fetchers_modules +writing output... [ 59%] documentation_pages/guesser_modules +writing output... [ 60%] documentation_pages/guesser_modules/base +writing output... [ 60%] documentation_pages/guesser_modules/default_guesser +writing output... [ 61%] documentation_pages/guesser_modules/init +writing output... [ 61%] documentation_pages/guesser_modules/tables +writing output... [ 62%] documentation_pages/lib/NeighborSearch +writing output... [ 62%] documentation_pages/lib/c_distances +writing output... [ 63%] documentation_pages/lib/c_distances_openmp +writing output... [ 63%] documentation_pages/lib/correlations +writing output... [ 64%] documentation_pages/lib/distances +writing output... [ 64%] documentation_pages/lib/formats/libdcd +writing output... [ 65%] documentation_pages/lib/formats/libmdaxdr +writing output... [ 66%] documentation_pages/lib/log +writing output... [ 66%] documentation_pages/lib/mdamath +writing output... [ 67%] documentation_pages/lib/nsgrid +writing output... [ 67%] documentation_pages/lib/picklable_file_io +writing output... [ 68%] documentation_pages/lib/pkdtree +writing output... [ 68%] documentation_pages/lib/qcprot +writing output... [ 69%] documentation_pages/lib/transformations +writing output... [ 69%] documentation_pages/lib/util +writing output... [ 70%] documentation_pages/lib_modules +writing output... [ 70%] documentation_pages/overview +writing output... [ 71%] documentation_pages/references +writing output... [ 72%] documentation_pages/selections +writing output... [ 72%] documentation_pages/selections/base +writing output... [ 73%] documentation_pages/selections/charmm +writing output... [ 73%] documentation_pages/selections/gromacs +writing output... [ 74%] documentation_pages/selections/jmol +writing output... [ 74%] documentation_pages/selections/pymol +writing output... [ 75%] documentation_pages/selections/vmd +writing output... [ 75%] documentation_pages/selections_modules +writing output... [ 76%] documentation_pages/topology +writing output... [ 77%] documentation_pages/topology/CRDParser +writing output... [ 77%] documentation_pages/topology/DLPolyParser +writing output... [ 78%] documentation_pages/topology/DMSParser +writing output... [ 78%] documentation_pages/topology/ExtendedPDBParser +writing output... [ 79%] documentation_pages/topology/FHIAIMSParser +writing output... [ 79%] documentation_pages/topology/GMSParser +writing output... [ 80%] documentation_pages/topology/GROParser +writing output... [ 80%] documentation_pages/topology/GSDParser +writing output... [ 81%] documentation_pages/topology/HoomdXMLParser +writing output... [ 81%] documentation_pages/topology/ITPParser +writing output... [ 82%] documentation_pages/topology/LAMMPSParser +writing output... [ 83%] documentation_pages/topology/MMTFParser +writing output... [ 83%] documentation_pages/topology/MOL2Parser +writing output... [ 84%] documentation_pages/topology/MinimalParser +writing output... [ 84%] documentation_pages/topology/PDBParser +writing output... [ 85%] documentation_pages/topology/PDBQTParser +writing output... [ 85%] documentation_pages/topology/PQRParser +writing output... [ 86%] documentation_pages/topology/PSFParser +writing output... [ 86%] documentation_pages/topology/TOPParser +writing output... [ 87%] documentation_pages/topology/TPRParser +writing output... [ 87%] documentation_pages/topology/TXYZParser +writing output... [ 88%] documentation_pages/topology/XYZParser +writing output... [ 89%] documentation_pages/topology/base +writing output... [ 89%] documentation_pages/topology/core +writing output... [ 90%] documentation_pages/topology/guessers +writing output... [ 90%] documentation_pages/topology/init +writing output... [ 91%] documentation_pages/topology/tables +writing output... [ 91%] documentation_pages/topology/tpr_util +writing output... [ 92%] documentation_pages/topology_modules +writing output... [ 92%] documentation_pages/trajectory_transformations +writing output... [ 93%] documentation_pages/transformations/base +writing output... [ 93%] documentation_pages/transformations/boxdimensions +writing output... [ 94%] documentation_pages/transformations/fit +writing output... [ 95%] documentation_pages/transformations/nojump +writing output... [ 95%] documentation_pages/transformations/positionaveraging +writing output... [ 96%] documentation_pages/transformations/rotate +writing output... [ 96%] documentation_pages/transformations/translate +writing output... [ 97%] documentation_pages/transformations/wrap +writing output... [ 97%] documentation_pages/units +writing output... [ 98%] documentation_pages/version +writing output... [ 98%] documentation_pages/visualization/streamlines +writing output... [ 99%] documentation_pages/visualization/streamlines_3D +writing output... [ 99%] documentation_pages/visualization_modules +writing output... [100%] index + +/nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/dssp/dssp.py:docstring of MDAnalysis.analysis.dssp.dssp.DSSP:54: WARNING: undefined label: 'selection-of-acceleration-backend' [ref.ref] +/nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/converters/RDKitInferring.py:docstring of MDAnalysis.converters.RDKitInferring.RDKitInferrer:1: WARNING: undefined label: 'https://github.com/jensengroup/xyz2mol' [ref.ref] +generating indices... genindex py-modindex done +highlighting module code... [ 1%] MDAnalysis.analysis.align +highlighting module code... [ 1%] MDAnalysis.analysis.atomicdistances +highlighting module code... [ 2%] MDAnalysis.analysis.backends +highlighting module code... [ 2%] MDAnalysis.analysis.base +highlighting module code... [ 3%] MDAnalysis.analysis.bat +highlighting module code... [ 4%] MDAnalysis.analysis.contacts +highlighting module code... [ 4%] MDAnalysis.analysis.density +highlighting module code... [ 5%] MDAnalysis.analysis.dielectric +highlighting module code... [ 6%] MDAnalysis.analysis.diffusionmap +highlighting module code... [ 6%] MDAnalysis.analysis.dihedrals +highlighting module code... [ 7%] MDAnalysis.analysis.distances +highlighting module code... [ 7%] MDAnalysis.analysis.dssp.dssp +highlighting module code... [ 8%] MDAnalysis.analysis.dssp.pydssp_numpy +highlighting module code... [ 9%] MDAnalysis.analysis.encore.bootstrap +/nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/encore/__init__.py:45: DeprecationWarning: Deprecation in version 2.8.0 +MDAnalysis.analysis.encore is deprecated in favour of the MDAKit mdaencore (https://www.mdanalysis.org/mdaencore/) and will be removed in MDAnalysis version 3.0.0. + warnings.warn(wmsg, category=DeprecationWarning) +highlighting module code... [ 9%] MDAnalysis.analysis.encore.clustering.ClusterCollection +highlighting module code... [ 10%] MDAnalysis.analysis.encore.clustering.ClusteringMethod +highlighting module code... [ 10%] MDAnalysis.analysis.encore.clustering.affinityprop +highlighting module code... [ 11%] MDAnalysis.analysis.encore.clustering.cluster +highlighting module code... [ 12%] MDAnalysis.analysis.encore.confdistmatrix +highlighting module code... [ 12%] MDAnalysis.analysis.encore.covariance +highlighting module code... [ 13%] MDAnalysis.analysis.encore.cutils +highlighting module code... [ 14%] MDAnalysis.analysis.encore.dimensionality_reduction.DimensionalityReductionMethod +highlighting module code... [ 14%] MDAnalysis.analysis.encore.dimensionality_reduction.reduce_dimensionality +highlighting module code... [ 15%] MDAnalysis.analysis.encore.dimensionality_reduction.stochasticproxembed +highlighting module code... [ 15%] MDAnalysis.analysis.encore.similarity +highlighting module code... [ 16%] MDAnalysis.analysis.encore.utils +highlighting module code... [ 17%] MDAnalysis.analysis.gnm +highlighting module code... [ 17%] MDAnalysis.analysis.helix_analysis +highlighting module code... [ 18%] MDAnalysis.analysis.hydrogenbonds.hbond_analysis +highlighting module code... [ 19%] MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel +highlighting module code... [ 19%] MDAnalysis.analysis.hydrogenbonds.wbridge_analysis +highlighting module code... [ 20%] MDAnalysis.analysis.leaflet +highlighting module code... [ 20%] MDAnalysis.analysis.legacy.x3dna +highlighting module code... [ 21%] MDAnalysis.analysis.lineardensity +highlighting module code... [ 22%] MDAnalysis.analysis.msd +highlighting module code... [ 22%] MDAnalysis.analysis.nucleicacids +highlighting module code... [ 23%] MDAnalysis.analysis.nuclinfo +highlighting module code... [ 23%] MDAnalysis.analysis.pca +highlighting module code... [ 24%] MDAnalysis.analysis.polymer +highlighting module code... [ 25%] MDAnalysis.analysis.rdf +highlighting module code... [ 25%] MDAnalysis.analysis.results +highlighting module code... [ 26%] MDAnalysis.analysis.rms +highlighting module code... [ 27%] MDAnalysis.auxiliary.EDR +highlighting module code... [ 27%] MDAnalysis.auxiliary.XVG +highlighting module code... [ 28%] MDAnalysis.auxiliary.base +highlighting module code... [ 28%] MDAnalysis.auxiliary.core +highlighting module code... [ 29%] MDAnalysis.converters.OpenMM +highlighting module code... [ 30%] MDAnalysis.converters.OpenMMParser +highlighting module code... [ 30%] MDAnalysis.converters.ParmEd +highlighting module code... [ 31%] MDAnalysis.converters.ParmEdParser +highlighting module code... [ 31%] MDAnalysis.converters.RDKit +highlighting module code... [ 32%] MDAnalysis.converters.RDKitInferring +highlighting module code... [ 33%] MDAnalysis.converters.RDKitParser +highlighting module code... [ 33%] MDAnalysis.converters.base +highlighting module code... [ 34%] MDAnalysis.coordinates.CRD +highlighting module code... [ 35%] MDAnalysis.coordinates.DCD +highlighting module code... [ 35%] MDAnalysis.coordinates.DLPoly +highlighting module code... [ 36%] MDAnalysis.coordinates.DMS +highlighting module code... [ 36%] MDAnalysis.coordinates.FHIAIMS +highlighting module code... [ 37%] MDAnalysis.coordinates.GMS +highlighting module code... [ 38%] MDAnalysis.coordinates.GRO +highlighting module code... [ 38%] MDAnalysis.coordinates.GSD +highlighting module code... [ 39%] MDAnalysis.coordinates.H5MD +highlighting module code... [ 40%] MDAnalysis.coordinates.IMD +highlighting module code... [ 40%] MDAnalysis.coordinates.INPCRD +highlighting module code... [ 41%] MDAnalysis.coordinates.LAMMPS +highlighting module code... [ 41%] MDAnalysis.coordinates.MMTF +highlighting module code... [ 42%] MDAnalysis.coordinates.MOL2 +highlighting module code... [ 43%] MDAnalysis.coordinates.NAMDBIN +highlighting module code... [ 43%] MDAnalysis.coordinates.PDB +highlighting module code... [ 44%] MDAnalysis.coordinates.PDBQT +highlighting module code... [ 44%] MDAnalysis.coordinates.PQR +highlighting module code... [ 45%] MDAnalysis.coordinates.TNG +highlighting module code... [ 46%] MDAnalysis.coordinates.TPR +highlighting module code... [ 46%] MDAnalysis.coordinates.TRC +highlighting module code... [ 47%] MDAnalysis.coordinates.TRJ +highlighting module code... [ 48%] MDAnalysis.coordinates.TRR +highlighting module code... [ 48%] MDAnalysis.coordinates.TRZ +highlighting module code... [ 49%] MDAnalysis.coordinates.TXYZ +highlighting module code... [ 49%] MDAnalysis.coordinates.XDR +highlighting module code... [ 50%] MDAnalysis.coordinates.XTC +highlighting module code... [ 51%] MDAnalysis.coordinates.XYZ +highlighting module code... [ 51%] MDAnalysis.coordinates.base +highlighting module code... [ 52%] MDAnalysis.coordinates.chain +highlighting module code... [ 52%] MDAnalysis.coordinates.chemfiles +highlighting module code... [ 53%] MDAnalysis.coordinates.core +highlighting module code... [ 54%] MDAnalysis.coordinates.memory +highlighting module code... [ 54%] MDAnalysis.coordinates.null +highlighting module code... [ 55%] MDAnalysis.coordinates.timestep +highlighting module code... [ 56%] MDAnalysis.core._get_readers +highlighting module code... [ 56%] MDAnalysis.core.accessors +highlighting module code... [ 57%] MDAnalysis.core.groups +highlighting module code... [ 57%] MDAnalysis.core.selection +highlighting module code... [ 58%] MDAnalysis.core.topology +highlighting module code... [ 59%] MDAnalysis.core.topologyattrs +highlighting module code... [ 59%] MDAnalysis.core.topologyobjects +highlighting module code... [ 60%] MDAnalysis.core.universe +highlighting module code... [ 60%] MDAnalysis.exceptions +highlighting module code... [ 61%] MDAnalysis.fetch.fetchers +highlighting module code... [ 62%] MDAnalysis.fetch.pdb +highlighting module code... [ 62%] MDAnalysis.guesser.base +highlighting module code... [ 63%] MDAnalysis.guesser.default_guesser +highlighting module code... [ 64%] MDAnalysis.guesser.tables +highlighting module code... [ 64%] MDAnalysis.lib.NeighborSearch +highlighting module code... [ 65%] MDAnalysis.lib._augment +highlighting module code... [ 65%] MDAnalysis.lib._cutil +highlighting module code... [ 66%] MDAnalysis.lib.correlations +highlighting module code... [ 67%] MDAnalysis.lib.distances +highlighting module code... [ 67%] MDAnalysis.lib.formats.libdcd +highlighting module code... [ 68%] MDAnalysis.lib.formats.libmdaxdr +highlighting module code... [ 69%] MDAnalysis.lib.log +highlighting module code... [ 69%] MDAnalysis.lib.mdamath +highlighting module code... [ 70%] MDAnalysis.lib.nsgrid +highlighting module code... [ 70%] MDAnalysis.lib.picklable_file_io +highlighting module code... [ 71%] MDAnalysis.lib.pkdtree +highlighting module code... [ 72%] MDAnalysis.lib.qcprot +highlighting module code... [ 72%] MDAnalysis.lib.transformations +highlighting module code... [ 73%] MDAnalysis.lib.util +highlighting module code... [ 73%] MDAnalysis.selections +highlighting module code... [ 74%] MDAnalysis.selections.base +highlighting module code... [ 75%] MDAnalysis.selections.charmm +highlighting module code... [ 75%] MDAnalysis.selections.gromacs +highlighting module code... [ 76%] MDAnalysis.selections.jmol +highlighting module code... [ 77%] MDAnalysis.selections.pymol +highlighting module code... [ 77%] MDAnalysis.selections.vmd +highlighting module code... [ 78%] MDAnalysis.topology.CRDParser +highlighting module code... [ 78%] MDAnalysis.topology.DLPolyParser +highlighting module code... [ 79%] MDAnalysis.topology.DMSParser +highlighting module code... [ 80%] MDAnalysis.topology.ExtendedPDBParser +highlighting module code... [ 80%] MDAnalysis.topology.FHIAIMSParser +highlighting module code... [ 81%] MDAnalysis.topology.GMSParser +highlighting module code... [ 81%] MDAnalysis.topology.GROParser +highlighting module code... [ 82%] MDAnalysis.topology.GSDParser +highlighting module code... [ 83%] MDAnalysis.topology.HoomdXMLParser +highlighting module code... [ 83%] MDAnalysis.topology.ITPParser +highlighting module code... [ 84%] MDAnalysis.topology.LAMMPSParser +highlighting module code... [ 85%] MDAnalysis.topology.MMTFParser +highlighting module code... [ 85%] MDAnalysis.topology.MOL2Parser +highlighting module code... [ 86%] MDAnalysis.topology.MinimalParser +highlighting module code... [ 86%] MDAnalysis.topology.PDBParser +highlighting module code... [ 87%] MDAnalysis.topology.PDBQTParser +highlighting module code... [ 88%] MDAnalysis.topology.PQRParser +highlighting module code... [ 88%] MDAnalysis.topology.PSFParser +highlighting module code... [ 89%] MDAnalysis.topology.TOPParser +highlighting module code... [ 90%] MDAnalysis.topology.TPRParser +highlighting module code... [ 90%] MDAnalysis.topology.TXYZParser +highlighting module code... [ 91%] MDAnalysis.topology.XYZParser +highlighting module code... [ 91%] MDAnalysis.topology.base +highlighting module code... [ 92%] MDAnalysis.topology.tpr.obj +highlighting module code... [ 93%] MDAnalysis.topology.tpr.utils +highlighting module code... [ 93%] MDAnalysis.transformations.base +highlighting module code... [ 94%] MDAnalysis.transformations.boxdimensions +highlighting module code... [ 94%] MDAnalysis.transformations.fit +highlighting module code... [ 95%] MDAnalysis.transformations.nojump +highlighting module code... [ 96%] MDAnalysis.transformations.positionaveraging +highlighting module code... [ 96%] MDAnalysis.transformations.rotate +highlighting module code... [ 97%] MDAnalysis.transformations.translate +highlighting module code... [ 98%] MDAnalysis.transformations.wrap +highlighting module code... [ 98%] MDAnalysis.units +highlighting module code... [ 99%] MDAnalysis.visualization.streamlines +highlighting module code... [ 99%] MDAnalysis.visualization.streamlines_3D +highlighting module code... [100%] builtins + +writing additional pages... search opensearch done +copying images... [ 10%] images/rama_ref_plot.png +copying images... [ 20%] images/janin_ref_plot.png +copying images... [ 30%] images/rama_demo_plot.png +copying images... [ 40%] images/janin_demo_plot.png +copying images... [ 50%] images/msd_demo_plot.png +copying images... [ 60%] images/AnalysisBase_parallel.png +copying images... [ 70%] images/parallelization_time.png +copying images... [ 80%] images/RSMD_plot.png +copying images... [ 90%] documentation_pages/visualization/testing_streamline.png +copying images... [100%] documentation_pages/visualization/test_streamplot_3D.png + +dumping search index in English (code: en)... done +dumping object inventory... done +sphinx-sitemap: sitemap.xml was generated for URL https://docs.mdanalysis.org/ in /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/sitemap.xml +Writing compiled SASS to /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/_static/site.css +build finished with problems, 2 warnings (with warnings treated as errors). +make: *** [Makefile:42: html] Error 1 diff --git a/testsuite/MDAnalysisTests/fetch/run_tests.sh b/testsuite/MDAnalysisTests/fetch/run_tests.sh new file mode 100755 index 0000000000..485d7d9f33 --- /dev/null +++ b/testsuite/MDAnalysisTests/fetch/run_tests.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +pytest -v --cov=/nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/fetch --cov-report term-missing diff --git a/testsuite/MDAnalysisTests/pytest.log b/testsuite/MDAnalysisTests/pytest.log new file mode 100644 index 0000000000..f63dff14ec --- /dev/null +++ b/testsuite/MDAnalysisTests/pytest.log @@ -0,0 +1,240 @@ +============================= test session starts ============================== +platform linux -- Python 3.13.14, pytest-9.1.0, pluggy-1.6.0 +rootdir: /nfs/homes3/jauy1/Projects/Dev/mdanalysis/testsuite +configfile: pyproject.toml +plugins: hypothesis-6.155.3, xdist-3.8.0, timeout-2.4.0, cov-7.1.0 +collected 23956 items / 1 error + +==================================== ERRORS ==================================== +___________ ERROR collecting MDAnalysisTests/core/test_atomgroup.py ____________ +MDAnalysisTests/core/test_atomgroup.py::TestAtomGroupToTopology::test_VE: in "parametrize" the number of names (1): + ['btype'] +must be equal to the number of values (4): + bond +=============================== warnings summary =============================== +../../package/MDAnalysis/coordinates/DCD.py:171 +../../package/MDAnalysis/coordinates/DCD.py:171 + /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/coordinates/DCD.py:171: DeprecationWarning: DCDReader currently makes independent timesteps by copying self.ts while other readers update self.ts inplace. This behavior will be changed in 3.0 to be the same as other readers. Read more at https://github.com/MDAnalysis/mdanalysis/issues/3889 to learn if this change in behavior might affect you. + warnings.warn("DCDReader currently makes independent timesteps" + +../../package/MDAnalysis/analysis/encore/__init__.py:45 + /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/encore/__init__.py:45: DeprecationWarning: Deprecation in version 2.8.0 + MDAnalysis.analysis.encore is deprecated in favour of the MDAKit mdaencore (https://www.mdanalysis.org/mdaencore/) and will be removed in MDAnalysis version 3.0.0. + warnings.warn(wmsg, category=DeprecationWarning) + +../../package/MDAnalysis/analysis/hole2/__init__.py:58 + /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/hole2/__init__.py:58: DeprecationWarning: Deprecated in version 2.8.0 + MDAnalysis.analysis.hole2 is deprecated in favour of the MDAKit madahole2 (https://www.mdanalysis.org/mdahole2/) and will be removed in MDAnalysis version 3.0.0 + warnings.warn(wmsg, category=DeprecationWarning) + +../../package/MDAnalysis/analysis/hbonds/hbond_autocorrel.py:54 + /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/hbonds/hbond_autocorrel.py:54: DeprecationWarning: This module was moved to MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel; hbonds.hbond_autocorrel will be removed in 3.0.0. + warnings.warn(wmsg, category=DeprecationWarning) + +analysis/test_nucleicacids.py:212 + /nfs/homes3/jauy1/Projects/Dev/mdanalysis/testsuite/MDAnalysisTests/analysis/test_nucleicacids.py:212: SyntaxWarning: invalid escape sequence '\.' + """WatsonCrickDist must match the full resname, not just resname[0]\. + +../../package/MDAnalysis/analysis/psa.py:80 + /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/psa.py:80: DeprecationWarning: Deprecation in version 2.8.0: + MDAnalysis.analysis.psa is deprecated in favour of the MDAKit PathSimAnalysis (https://github.com/MDAnalysis/PathSimAnalysis) and will be removed in MDAnalysis version 3.0.0 + warnings.warn(wmsg, category=DeprecationWarning) + +../../package/MDAnalysis/analysis/waterdynamics.py:65 + /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/waterdynamics.py:65: DeprecationWarning: Deprecation in version 2.8.0 + MDAnalysis.analysis.waterdynamics is deprecated in favour of the MDAKit waterdynamics (https://www.mdanalysis.org/waterdynamics/) and will be removed in MDAnalysis version 3.0.0 + warnings.warn(wmsg, category=DeprecationWarning) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/coordinates/test_timestep_api.py::TestTimestep::test_from_coordinates, argvalues type: filter + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/coordinates/test_timestep_api.py::TestTimestep::test_check_equal, argvalues type: filter + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/core/test_atomgroup.py::TestCrossUniverse::test_add_mixed_universes, argvalues type: product + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/core/test_groups.py::TestGroupSlicing::test_slice, argvalues type: product + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/core/test_groups.py::TestGroupAddition::test_addition, argvalues type: generator + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/core/test_groups.py::TestGroupAddition::test_sum, argvalues type: generator + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/core/test_groups.py::TestGroupAddition::test_bad_sum, argvalues type: generator + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/core/test_groups.py::TestComponentComparisons::test_crosslevel_cmp, argvalues type: permutations + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/core/test_groups.py::TestComponentComparisons::test_crosslevel_eq, argvalues type: permutations + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/core/test_groups.py::TestGroupBaseOperators::test_failing_pairs, argvalues type: chain + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/core/test_groups.py::TestGroupBaseOperators::test_succeeding_pairs, argvalues type: chain + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/core/test_groups.py::TestGroupHash::test_hash_difference_cross, argvalues type: permutations + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/lib/test_distances.py::test_minimize_vectors, argvalues type: product + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/lib/test_mdamath.py::TestMatrixOperations::test_triclinic_vectors, argvalues type: combinations_with_replacement + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/lib/test_mdamath.py::TestMatrixOperations::test_triclinic_box, argvalues type: combinations_with_replacement + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/lib/test_mdamath.py::TestMatrixOperations::test_box_volume, argvalues type: combinations_with_replacement + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/lib/test_util.py::TestStringFunctions::test_convert_aa_3to1, argvalues type: generator + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 + /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. + Test: MDAnalysisTests/test_api.py::test_all_import, argvalues type: generator + Please convert to a list or tuple. + See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + +../../package/MDAnalysis/topology/tables.py:52 + /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/topology/tables.py:52: DeprecationWarning: Deprecated in version 2.8.0 + MDAnalysis.topology.tables has been moved to MDAnalysis.guesser.tables. This import point will be removed in MDAnalysis version 3.0.0 + warnings.warn(wmsg, category=DeprecationWarning) + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +=========================== short test summary info ============================ +ERROR core/test_atomgroup.py::TestAtomGroupToTopology - Failed: MDAnalysisTes... +!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!! +================== 30 warnings, 1 error in 105.46s (0:01:45) =================== + +DueCredit Report: +- Molecular simulation analysis library / MDAnalysis (v 2.11.0.dev0) [1, 2] + - Iterative Calculation of Opimal Reference / MDAnalysis.analysis.align.iterative_average (v 2.11.0.dev0) [3] + - Bond-Angle-Torsions Coordinate Transformation / MDAnalysis.analysis.bat.BAT (v 2.11.0.dev0) [4] + - Dielectric analysis / MDAnalysis.analysis.dielectric (v 2.11.0.dev0) [5] + - DSSP algorithm description / MDAnalysis.analysis.dssp (v 2.11.0.dev0) [6] + - ENCORE Ensemble Comparison / MDAnalysis.analysis.encore (v 2.11.0.dev0) [7] + - Hydrogen bond analysis implementation / MDAnalysis.analysis.hydrogenbonds.hbond_analysis (v 2.11.0.dev0) [8] + - Hydrogen bonding autocorrelation time / MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel (v 2.11.0.dev0) [9] + - LeafletFinder algorithm / MDAnalysis.analysis.leaflet (v 2.11.0.dev0) [2] + - Mean Squared Displacements with tidynamics, FCA fast correlation algorithm / MDAnalysis.analysis.msd (v 2.11.0.dev0) [10, 11] + - Cumulative overlap / MDAnalysis.analysis.pca (v 2.11.0.dev0) [12] + - MDAnalysis trajectory reader/writer of the H5MDformat, Specifications of the H5MD standard / MDAnalysis.coordinates.H5MD (v 1.1, 2.11.0.dev0) [13, 14] + - MMTF Reader / MDAnalysis.coordinates.MMTF (v 2.11.0.dev0) [15] + - The TNG paper / MDAnalysis.coordinates.TNG (v 2.11.0.dev0) [16] + - QCProt implementation / MDAnalysis.lib.qcprot (v 2.11.0.dev0) [17, 18] + - MMTF Parser / MDAnalysis.topology.MMTFParser (v 2.11.0.dev0) [15] + - Works through the orthogonal case for unwrapping, and proposes the non-orthogonal approach. / MDAnalysis.transformations.nojump (v 2.11.0.dev0) [19] + - HOLE program, HOLE trajectory analysis with orderparameters / mdahole2.analysis.hole (v 0.0.0) [20, 21, 22] +- Scientific tools library / numpy (v 1.26.4) [23] + - Path Similarity Analysis algorithm and implementation / pathsimanalysis.psa (v 0.0.0) [24] + +2 packages cited +18 modules cited +0 functions cited + +References +---------- + +[1] Gowers, R. et al., 2016. MDAnalysis: A Python Package for the Rapid Analysis of Molecular Dynamics Simulations. In Proceedings of the 15th Python in Science Conference. SciPy. SciPy, pp. 98–105. +[2] Michaud‐Agrawal, N. et al., 2011. MDAnalysis: A toolkit for the analysis of molecular dynamics simulations. Journal of Computational Chemistry, 32(10), pp.2319–2327. +[3] Linke, M., Köfinger, J. & Hummer, G., 2018. Fully Anisotropic Rotational Diffusion Tensor from Molecular Dynamics Simulations. The Journal of Physical Chemistry B, 122(21), pp.5630–5639. +[4] Minh, D.D.L., 2019. Alchemical Grid Dock (AlGDock): Binding Free Energy Calculations between Flexible Ligands and Rigid Receptors. Journal of Computational Chemistry, 41(7), pp.715–730. +[5] Neumann, M., 1983. Dipole moment fluctuation formulas in computer simulations of polar systems. Molecular Physics, 50(4), pp.841–858. +[6] Kabsch, W. & Sander, C., 1983. Dictionary of protein secondary structure: Pattern recognition of hydrogen‐bonded and geometrical features. Biopolymers, 22(12), pp.2577–2637. +[7] Tiberti, M. et al., 2015. ENCORE: Software for Quantitative Ensemble Comparison B. L. de Groot, ed.. PLOS Computational Biology, 11(10), p.1004415. +[8] Smith, P. et al., 2019. On the interaction of hyaluronic acid with synovial fluid lipid membranes. Physical Chemistry Chemical Physics, 21(19), pp.9845–9857. +[9] ERRORED: 'june' +[10] de Buyl, P., 2018. tidynamics: A tiny package to compute the dynamics of stochastic and molecular simulations. Journal of Open Source Software, 3(28), p.877. +[11] Calandrini, V. et al., 2011. nMoldyn - Interfacing spectroscopic experiments, molecular dynamics simulations and models for time correlation functions. École thématique de la Société Française de la Neutronique, 12, pp.201–232. +[12] Yang, L. et al., 2008. Close Correspondence between the Motions from Principal Component Analysis of Multiple HIV-1 Protease Structures and Elastic Network Modes. Structure, 16(2), pp.321–330. +[13] Jakupovic, E. & Beckstein, O., 2021. MPI-parallel Molecular Dynamics Trajectory Analysis with the H5MD Format in the MDAnalysis Python Package. In Proceedings of the 20th Python in Science Conference. SciPy. SciPy, pp. 40–48. +[14] ERRORED: 'june' +[15] ERRORED: 'june' +[16] Lundborg, M. et al., 2013. An efficient and extensible format, library, and API for binary trajectory data from molecular simulations. Journal of Computational Chemistry, 35(3), pp.260–269. +[17] ERRORED: 'june' +[18] Liu, P., Agrafiotis, D.K. & Theobald, D.L., Fast determination of the optimal rotational matrix for macromolecular superpositions. Journal of Computational Chemistry, 31(7), pp.1561–1563. +[19] ERRORED: 'sept' +[20] Smart, O.S., Goodfellow, J.M. & Wallace, B.A., 1993. The pore dimensions of gramicidin A. Biophysical Journal, 65(6), pp.2455–2460. +[21] Smart, O.S. et al., 1996. HOLE: A program for the analysis of the pore dimensions of ion channel structural models. Journal of Molecular Graphics, 14(6), pp.354–360. +[22] Stelzl, L.S. et al., 2014. Flexible Gates Generate Occluded Intermediates in the Transport Cycle of LacY. Journal of Molecular Biology, 426(3), pp.735–751. +[23] Van Der Walt, S., Colbert, S.C. & Varoquaux, G., 2011. The NumPy array: a structure for efficient numerical computation. Computing in Science & Engineering, 13(2), pp.22–30. +[24] Seyler, S.L. et al., 2015. Path Similarity Analysis: A Method for Quantifying Macromolecular Pathways E. Tajkhorshid, ed.. PLOS Computational Biology, 11(10), p.1004568. From 497b4002112d487b225d23b4f50c94c9655a407b Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 30 Jun 2026 07:29:45 -0700 Subject: [PATCH 037/100] finalized Tests --- package/MDAnalysis/fetch/pdb.py | 15 ------ testsuite/MDAnalysisTests/fetch/servers.py | 2 + .../fetch/test_static_fetcher.py | 48 +++++++++++++------ 3 files changed, 35 insertions(+), 30 deletions(-) diff --git a/package/MDAnalysis/fetch/pdb.py b/package/MDAnalysis/fetch/pdb.py index f9964402a3..dacca97d57 100644 --- a/package/MDAnalysis/fetch/pdb.py +++ b/package/MDAnalysis/fetch/pdb.py @@ -45,21 +45,6 @@ from pathlib import Path from .fetchers import StaticFetcher -try: - import pooch -except ImportError: - HAS_POOCH = False -else: - HAS_POOCH = True - -#: Name of the :mod:`pooch` cache directory ``pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER)``; -#: see :func:`pooch.os_cache` for further details.' -#: -#: .. versionadded:: 2.11.0 -#: Moved to fetchers -# This should be removed? -DEFAULT_CACHE_NAME_DOWNLOADER = "MDAnalysis_pdbs" - # These file formats are here https://www.rcsb.org/docs/programmatic-access/file-download-services#pdb-entry-files" SUPPORTED_FILE_FORMATS_DOWNLOADER = ( "cif", diff --git a/testsuite/MDAnalysisTests/fetch/servers.py b/testsuite/MDAnalysisTests/fetch/servers.py index 8f853187cb..59944e93c2 100644 --- a/testsuite/MDAnalysisTests/fetch/servers.py +++ b/testsuite/MDAnalysisTests/fetch/servers.py @@ -43,6 +43,8 @@ def temporary_http_server(): (temp_folder / "TEST_FILE2.txt").write_text("7-1") + (temp_folder / "TEST_FILE3.txt").write_text("David Beckham in a World Cup ad") + http_handler = partial( SimpleHTTPRequestHandler, directory=str(temp_folder), diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 1307ff774f..c65360e949 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -27,6 +27,7 @@ from servers import temporary_http_server +import hashlib import pytest @@ -46,26 +47,29 @@ REGISTRY_NAME = "hashes.txt" -def test_invalid_hash(): - pass - -def test_invalid_downloader(): - pass +def test_invalid_hash(tmp_path): + hash = "foo" -def test_different_hashes(tmp_path): with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" - downloader = StaticFetcher(cache_path=tmp_path, hash="md5") - path = downloader.fetch( - base_url=base_url, - file_name="TEST_FILE1.txt", - db_name=REGISTRY_NAME, - ) + + + with pytest.raises( + ValueError, + match=re.escape( + f'Invalid hash "{hash}". Valid hashes algorithms are {hashlib.algorithms_available}.' + ), + ): + downloader = StaticFetcher(cache_path=tmp_path, hash=hash) + + path = downloader.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + ) - assert ( - downloader.db_path - ).read_text() == "TEST_FILE1.txt md5:b2f138521297db74b6b280feeb14f9f6\n" +def test_invalid_downloader(): + pass @pytest.fixture() @@ -163,6 +167,20 @@ def test_create_database(self, tmp_path): downloader.db_path ).read_text() == "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + def test_different_hashes(self, tmp_path): + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher(cache_path=tmp_path, hash="md5") + path = downloader.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name=REGISTRY_NAME, + ) + + assert ( + downloader.db_path + ).read_text() == "TEST_FILE1.txt md5:b2f138521297db74b6b280feeb14f9f6\n" + def test_existing_database(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" From da3087a7f4262b346d7f618b2d60822908c6471d Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 30 Jun 2026 07:37:28 -0700 Subject: [PATCH 038/100] Applied black --- package/MDAnalysis/fetch/pdb.py | 2 -- testsuite/MDAnalysisTests/fetch/servers.py | 4 +++- testsuite/MDAnalysisTests/fetch/test_from_PDB.py | 2 ++ testsuite/MDAnalysisTests/fetch/test_static_fetcher.py | 9 --------- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/package/MDAnalysis/fetch/pdb.py b/package/MDAnalysis/fetch/pdb.py index dacca97d57..dcd336c74d 100644 --- a/package/MDAnalysis/fetch/pdb.py +++ b/package/MDAnalysis/fetch/pdb.py @@ -155,8 +155,6 @@ def from_PDB( "Invalid file format. Supported file formats " f"are {SUPPORTED_FILE_FORMATS_DOWNLOADER}" ) - - pdb_ids = [pdb + "." + file_format for pdb in pdb_ids] diff --git a/testsuite/MDAnalysisTests/fetch/servers.py b/testsuite/MDAnalysisTests/fetch/servers.py index 59944e93c2..2ff5ec2cbc 100644 --- a/testsuite/MDAnalysisTests/fetch/servers.py +++ b/testsuite/MDAnalysisTests/fetch/servers.py @@ -43,7 +43,9 @@ def temporary_http_server(): (temp_folder / "TEST_FILE2.txt").write_text("7-1") - (temp_folder / "TEST_FILE3.txt").write_text("David Beckham in a World Cup ad") + (temp_folder / "TEST_FILE3.txt").write_text( + "David Beckham in a World Cup ad" + ) http_handler = partial( SimpleHTTPRequestHandler, diff --git a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py index f71db35578..dc644e8fc0 100644 --- a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py +++ b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py @@ -49,6 +49,7 @@ def test_download_one_file(tmp_path): assert path.exists() assert path.name == "1AKE.cif.gz" + @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") @pytest.mark.skipif( not HAS_ACCESS_TO_WWPDB, @@ -62,6 +63,7 @@ def test_download_multiple_files(tmp_path): ["1AKE.cif.gz", "4AKE.cif.gz"] ) + @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") @pytest.mark.skipif( not HAS_ACCESS_TO_WWPDB, diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index c65360e949..b12db157eb 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -40,10 +40,6 @@ if HAS_POOCH: import pooch - -# BASE_URL = "https://files.wwpdb.org/download/" -# SINGLE_PDB = "1AKE.pdb" -# MULTIPLE_PDBS = ("1AKE.pdb", "4AKE.pdb") REGISTRY_NAME = "hashes.txt" @@ -52,7 +48,6 @@ def test_invalid_hash(tmp_path): with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" - with pytest.raises( ValueError, @@ -68,10 +63,6 @@ def test_invalid_hash(tmp_path): ) -def test_invalid_downloader(): - pass - - @pytest.fixture() def clean_up_default_cache(): rmtree(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER), ignore_errors=True) From 0ee322f58ce56e1adc953225cf070e2b82e91ed4 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 30 Jun 2026 07:40:48 -0700 Subject: [PATCH 039/100] Moved to proper spot --- .../fetch/test_static_fetcher.py | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index b12db157eb..dfc4cabac1 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -43,26 +43,6 @@ REGISTRY_NAME = "hashes.txt" -def test_invalid_hash(tmp_path): - hash = "foo" - - with temporary_http_server() as (host, port, temp_folder): - base_url = f"http://{host}:{port}/" - - with pytest.raises( - ValueError, - match=re.escape( - f'Invalid hash "{hash}". Valid hashes algorithms are {hashlib.algorithms_available}.' - ), - ): - downloader = StaticFetcher(cache_path=tmp_path, hash=hash) - - path = downloader.fetch( - base_url=base_url, - file_name="TEST_FILE1.txt", - ) - - @pytest.fixture() def clean_up_default_cache(): rmtree(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER), ignore_errors=True) @@ -111,8 +91,18 @@ def test_invalid_downloader(self, tmp_path): ) def test_invalid_hash(self, tmp_path): - with pytest.raises(ValueError, match='Invalid hash "barfoo"'): - StaticFetcher(cache_path=tmp_path, hash="barfoo") + hash = "foo" + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + + with pytest.raises( + ValueError, + match=re.escape( + f'Invalid hash "{hash}". Valid hashes algorithms are {hashlib.algorithms_available}.' + ), + ): + downloader = StaticFetcher(cache_path=tmp_path, hash=hash) @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") From f140dce8710026737815c59ea3dcd41b5a009447 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 8 Jul 2026 15:05:10 -0700 Subject: [PATCH 040/100] Added enviornment variable override --- package/MDAnalysis/fetch/fetchers.py | 1 + .../fetch/test_static_fetcher.py | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index c75072026f..4bcf324701 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -225,6 +225,7 @@ def fetch( base_url=kwargs["base_url"], registry=registry_dictionary, retry_if_failed=kwargs["retries"], + env="MDANALYSIS_FETCHER_DATA", ) download_kwargs = kwargs.copy() diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index dfc4cabac1..0c53eddf72 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -21,8 +21,8 @@ # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # -from pathlib import Path import re +from pathlib import Path from shutil import rmtree from servers import temporary_http_server @@ -254,3 +254,22 @@ def test_multiple_downloads_existing_database(self, tmp_path): mtime2 = [path.stat().st_mtime for path in paths2] assert mtime1 == mtime2 + + +def test_environment_variable_override(tmp_path, monkeypatch): + monkeypatch.setenv("MDANALYSIS_FETCHER_DATA", str(tmp_path)) + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + + Path("/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs").mkdir( + parents=True, exist_ok=True + ) + + downloader = StaticFetcher() + path = downloader.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + ) + + assert Path(tmp_path / "TEST_FILE1.txt").exists() From f1273961b4ac644b8fd481f8fda34daf3c7e65c8 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 8 Jul 2026 15:07:24 -0700 Subject: [PATCH 041/100] added variable to expected behaviors --- .../fetch/test_static_fetcher.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 0c53eddf72..e5e9bceadc 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -255,21 +255,20 @@ def test_multiple_downloads_existing_database(self, tmp_path): assert mtime1 == mtime2 + def test_environment_variable_override(tmp_path, monkeypatch): + monkeypatch.setenv("MDANALYSIS_FETCHER_DATA", str(tmp_path)) -def test_environment_variable_override(tmp_path, monkeypatch): - monkeypatch.setenv("MDANALYSIS_FETCHER_DATA", str(tmp_path)) - - with temporary_http_server() as (host, port, temp_folder): - base_url = f"http://{host}:{port}/" + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" - Path("/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs").mkdir( - parents=True, exist_ok=True - ) + Path("/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs").mkdir( + parents=True, exist_ok=True + ) - downloader = StaticFetcher() - path = downloader.fetch( - base_url=base_url, - file_name="TEST_FILE1.txt", - ) + downloader = StaticFetcher() + path = downloader.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + ) - assert Path(tmp_path / "TEST_FILE1.txt").exists() + assert Path(tmp_path / "TEST_FILE1.txt").exists() From d3cda7733645fa12cc1358d5ec6c38a2bc59a752 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 8 Jul 2026 15:23:20 -0700 Subject: [PATCH 042/100] TESTs finally working! --- package/MDAnalysis/fetch/fetchers.py | 9 +++++++-- .../MDAnalysisTests/fetch/test_static_fetcher.py | 11 ++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 4bcf324701..4da3710163 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -179,6 +179,7 @@ def fetch( downloader="HTTP", **kwargs, ): + # Keywords arguments are reserved for common _BaseFetcher.fetch() arguements. kwargs = self._validate_fetch_args(kwargs) registry_dictionary = {} @@ -275,9 +276,13 @@ def fetch( def _check_cache_path_input(self, cache_path): if cache_path is None: - return Path(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER)) + path = Path(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER)) else: - return Path(cache_path) + path = Path(cache_path) + + Path(path).mkdir(parents=True, exist_ok=True) + return path + def _check_hash_input(self, hash): if hash in hashlib.algorithms_available: diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index e5e9bceadc..1b6fa3e3b1 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -21,6 +21,7 @@ # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # +import os import re from pathlib import Path from shutil import rmtree @@ -255,20 +256,16 @@ def test_multiple_downloads_existing_database(self, tmp_path): assert mtime1 == mtime2 - def test_environment_variable_override(tmp_path, monkeypatch): + def test_environment_variable_override(self, tmp_path, monkeypatch): monkeypatch.setenv("MDANALYSIS_FETCHER_DATA", str(tmp_path)) with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" - - Path("/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs").mkdir( - parents=True, exist_ok=True - ) - downloader = StaticFetcher() + path = downloader.fetch( base_url=base_url, file_name="TEST_FILE1.txt", ) - assert Path(tmp_path / "TEST_FILE1.txt").exists() + assert (tmp_path / "TEST_FILE1.txt").exists() \ No newline at end of file From 7ae0b4ce473e90f00edf1052ae2ba48bb261ed89 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 10 Jul 2026 14:31:09 -0700 Subject: [PATCH 043/100] More docs --- package/MDAnalysis/fetch/fetchers.py | 117 +++++++++++++++++++++------ 1 file changed, 92 insertions(+), 25 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 4da3710163..6d903b8d58 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -32,13 +32,15 @@ Classes ------- -.. autofunction:: StaticFetcher +.. autoclass:: StaticFetcher + :members: + :inherited-members: Variables --------- These are global submodule level variables that affect the runtime behavior across -all Fetcher Classes. +all Fetcher Classes. Changing these values will affect all Fetchers! .. autodata:: DEFAULT_CACHE_NAME_DOWNLOADER @@ -68,12 +70,12 @@ #: .. versionadded:: 2.11.0 DEFAULT_CACHE_NAME_DOWNLOADER = "MDAnalysis_pdbs" -#: Time in seconds to wait for a response from the server before timing out. +#: Default time in seconds to wait for a response from the server before timing out. #: #: .. versionadded:: 2.11.0 DEFAULT_TIMEOUT = 10 -#: Number of attempt to retry a download if it fails. +#: Default number of attempts to retry a download if it fails. #: #: .. versionadded:: 2.11.0 DEFAULT_RETRIES = 2 @@ -124,43 +126,42 @@ def _validate_fetch_args(self, args): class StaticFetcher(_BaseFetcher): """ - Downloads files from a static URL to disk and caches them to a local directory. - + Downloads files from a URL to disk and caches them to a local directory. Parameters ---------- cache_path : str or pathlib.Path, optional - Path to the cache directory. If set to None, the default cache directory - will be used as specified by :data:`DEFAULT_CACHE_NAME_DOWNLOADER`. + Path to the cache directory. If set to ``None``, the default cache + directory will be used as specified by + :data:`DEFAULT_CACHE_NAME_DOWNLOADER`. - If the directory does not exist, it will be created. + If the directory does not exist, it will attempt to be created. hash : str, optional Hash algorithm to use for verifying the integrity of downloaded files. - The default is "sha256". Valid options are any hash algorithm available in - the :mod:`hashlib` module. - - If set to None, no hash verification will be performed. - - Methods - ------- - fetch(file_name, verbose=False, db_name="hashes.txt", downloader="HTTP", **kwargs) - Downloads the specified file(s) from the given base URL and caches them - to the local cache directory. If the file(s) already exist in the cache, - they will be used instead of downloading them again. + The default is ``sha256``. Valid options are any hash algorithm + available in the :mod:`hashlib` module. + If set to ``None``, no hash verification will be performed. Attributes ---------- - cache_path : pathlib.Path + cache_path : pathlib.Path or ``None`` Path to the cache directory. - db_path : pathlib.Path - Path to the database file used for caching. Created after calling fetch() + db_path : pathlib.Path or ``None`` + Path to the database file used for caching. Created after calling + fetch(). - hash : str + hash : str or ``None`` Hash algorithm used for verifying the integrity of downloaded files. + Notes + ----- + The download directory can be overridden by setting the environment + variable ``MDANALYSIS_FETCHER_DATA`` to a valid path. This class uses + :mod:`pooch` as a backend for downloading and caching files. + """ def __init__(self, cache_path=None, hash="sha256"): @@ -173,12 +174,78 @@ def __init__(self, cache_path=None, hash="sha256"): def fetch( self, - file_name=None, + file_name, verbose=False, db_name="hashes.txt", downloader="HTTP", **kwargs, ): + """ + Download one or more files from a static base URL and cache them + locally. + + Parameters + ---------- + base_url : str + Base URL from which to download the file(s). This should be a valid + URL pointing to the directory containing the files to be downloaded. + file_name : str or sequence of str + Name of the file or files to download. + Note that the request is phrased as {base_url}/{file_name}. + verbose : bool, optional + If True, shows fetcher progress. + Default is False. + db_name : str, optional + Name of the local hash database file used to verify cached downloads. + Default is "hashes.txt". If None, no registry database is read or written. + timeout : float, optional + Time in seconds to wait for a response from the server before timing out. + Default is :data:`DEFAULT_TIMEOUT`. + retries : int, optional + Number of attempts to retry a download if it fails. Default is :data:`DEFAULT_RETRIES`. + downloader : str or callable, optional + Downloader backend to use. If a string is provided, it must identify + a supported downloader such as "HTTP". + Default is "HTTP". + + Returns + ------- + pathlib.Path or list of pathlib.Path + The downloaded file path for a single file, or a list of paths for + multiple files. + + Example + ------- + + A script to download a protein from the RCSB Protein Data Bank. + + .. code-block:: python + + from MDAnalysis.fetch import StaticFetcher + + fetcher = StaticFetcher(cache_path=cache_path) + + # Download a single file from the RCSB Protein Data Bank + path = fetcher.fetch( + file_name="1AKE.cif", + base_url="https://files.wwpdb.org/download/", + ) + + # Download multiple files from the RCSB Protein Data Bank + path = fetcher.fetch( + file_name=["1AKE.cif", "4AKE.cif"], + base_url="https://files.wwpdb.org/download/", + ) + + Notes + ----- + The download directory can be overridden by setting the environment + variable ``MDANALYSIS_FETCHER_DATA`` to a valid path. This class uses + :mod:`pooch` as a backend for downloading and caching files.The + cache database is created on demand when ``db_name`` does not + exist. + + """ # Keywords arguments are reserved for common _BaseFetcher.fetch() arguements. kwargs = self._validate_fetch_args(kwargs) From 149d8b577bbc002fb41cb16bf851d52d7b8e45c1 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 02:17:58 -0700 Subject: [PATCH 044/100] Post processing --- package/MDAnalysis/fetch/fetchers.py | 167 ++++++++++++------ .../fetch/test_static_fetcher.py | 38 ++-- 2 files changed, 144 insertions(+), 61 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 6d903b8d58..dd61621628 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -111,13 +111,8 @@ def _check_pooch( ) def _validate_fetch_args(self, args): - """Set default values for @abstractmethod fetch() method if - not provided by user""" + """This initalized the fetcher library variables""" - if "base_url" not in args: - raise ValueError("base_url is not defined in fetch()") - - args.setdefault("progressbar", False) args.setdefault("timeout", DEFAULT_TIMEOUT) args.setdefault("retries", DEFAULT_RETRIES) @@ -135,14 +130,13 @@ class StaticFetcher(_BaseFetcher): directory will be used as specified by :data:`DEFAULT_CACHE_NAME_DOWNLOADER`. - If the directory does not exist, it will attempt to be created. + If the directory does not exist, it will attempted to be created. - hash : str, optional + hash : str Hash algorithm to use for verifying the integrity of downloaded files. The default is ``sha256``. Valid options are any hash algorithm available in the :mod:`hashlib` module. - If set to ``None``, no hash verification will be performed. Attributes ---------- @@ -174,9 +168,11 @@ def __init__(self, cache_path=None, hash="sha256"): def fetch( self, + base_url, file_name, verbose=False, db_name="hashes.txt", + append_db=False, downloader="HTTP", **kwargs, ): @@ -203,7 +199,7 @@ def fetch( Default is :data:`DEFAULT_TIMEOUT`. retries : int, optional Number of attempts to retry a download if it fails. Default is :data:`DEFAULT_RETRIES`. - downloader : str or callable, optional + downloader : str, optional Downloader backend to use. If a string is provided, it must identify a supported downloader such as "HTTP". Default is "HTTP". @@ -236,12 +232,12 @@ def fetch( file_name=["1AKE.cif", "4AKE.cif"], base_url="https://files.wwpdb.org/download/", ) - + Notes ----- The download directory can be overridden by setting the environment variable ``MDANALYSIS_FETCHER_DATA`` to a valid path. This class uses - :mod:`pooch` as a backend for downloading and caching files.The + :mod:`pooch` as a backend for downloading and caching files. The cache database is created on demand when ``db_name`` does not exist. @@ -249,9 +245,12 @@ def fetch( # Keywords arguments are reserved for common _BaseFetcher.fetch() arguements. kwargs = self._validate_fetch_args(kwargs) - registry_dictionary = {} LOAD_FROM_CACHE = False CREATE_DATABASE = False + MISSING_FILES = False + APPEND_DATABASE = append_db + + registry_dictionary = {} if db_name is not None: self.db_path = self.cache_path / Path(db_name) @@ -262,42 +261,34 @@ def fetch( CREATE_DATABASE = True if LOAD_FROM_CACHE: - # Reads pooch registry file format - # https://www.fatiando.org/pooch/latest/registry-files.html#registry-file-format - with open(self.db_path, mode="r") as f: - for line in f: - key, value = line.strip().split() - registry_dictionary[key] = value - - # Adds files not in cache - if isinstance(file_name, str): - _file_name = (file_name,) - else: - _file_name = file_name + registry_dictionary = self.read_registry(self.db_path) + missing_files_list = self.check_registry(self.db_path) - for file in _file_name: - if file not in registry_dictionary: - registry_dictionary[file] = None + if len(missing_files_list) != 0: + MISSING_FILES = True - else: # No Database (just download) - # This block of code allows file_name to be a tuple instead of a string - if isinstance(file_name, str): - _file_name = (file_name,) - else: - _file_name = file_name + if MISSING_FILES and not APPEND_DATABASE: + raise ValueError( + f"There are unknown files in the registry! The missing files are {missing_files_list}. To fix this, please set append_db=True to append the database" + ) - registry_dictionary = {name: None for name in _file_name} + # Code to process non-registry files + # One-liner that forces strings into tuple + no_db_files = (file_name,) if isinstance(file_name, str) else file_name + for file in no_db_files: + if file not in registry_dictionary: + registry_dictionary[file] = None + ## Pooch setup main_downloader = pooch.create( path=self.cache_path, - base_url=kwargs["base_url"], + base_url=base_url, registry=registry_dictionary, retry_if_failed=kwargs["retries"], env="MDANALYSIS_FETCHER_DATA", ) download_kwargs = kwargs.copy() - download_kwargs.pop("base_url") download_kwargs.pop("retries") match downloader: @@ -324,23 +315,98 @@ def fetch( ) for file_name in registry_dictionary.keys() ] + ## if CREATE_DATABASE: - hashes = ( - (fname.name, pooch.file_hash(fname, alg=self.hash)) - for fname in self.cache_path.iterdir() - if fname.is_file() - ) + self.write_registry(self.db_path, paths) - with open(self.db_path, mode="x") as f: - for fname, hash in hashes: - f.write(f"{fname} {self.hash}:{hash}\n") + if APPEND_DATABASE: + self.fix_registry(self.db_path, registry_dictionary) - if len(paths) == 1: - return paths[0] - else: - return paths + return paths[0] if len(paths) == 1 else paths + + # Reads pooch registry file format + # https://www.fatiando.org/pooch/latest/registry-files.html#registry-file-format + + def fix_registry(self, db_path, file_dict, ignore_files=[]): + + none_keys = [k for k, v in file_dict.items() if v is None] + + with open(db_path, "a") as f: + for no_hash_file in none_keys: + file = self.cache_path / no_hash_file + digest = pooch.file_hash(file, alg=self.hash) + + f.write(f"{file.name} {self.hash}:{digest}\n") + + + + + def check_registry(self, db_path): + """ + Return cache files that are missing from the registry database. + + Reads the registry at ``db_path`` and compares its recorded filenames + against the files currently present in ``self.cache_path``. The registry + database file itself is ignored. + + Args: + db_path: Path to the registry database to read. + + Returns: + list[pathlib.Path]: A list of cache file paths whose filenames are not + present in the registry. + """ + + registry_dictionary = self.read_registry(db_path) + database_files = set(registry_dictionary.keys()) + + cache_files = ( + path + for path in self.cache_path.rglob("*") + if path != self.db_path and path.is_file() + ) + + return [ + path for path in cache_files if path.name not in database_files + ] + + def read_registry(self, db_path): + """ + Read a registry file into a dictionary of filenames and hashes. + + Each line in the registry file is expected to contain a filename and its + corresponding hash value, separated by whitespace. + + Args: + db_path: Path to the registry file to read. + + Returns: + dict[str, str]: A dictionary where each key is a filename and each value + is the file's stored hash. + """ + + hash_dict = {} + + with open(db_path, mode="r") as f: + for line in f: + key, value = line.strip().split() + hash_dict[key] = value + + return hash_dict + + def write_registry(self, db_path, files, mode="w"): + """Method to exclusively write pooch registry""" + + with open(db_path, mode=mode) as f: + for file in files: + if self.hash is None: + f.write(f"{file.name} None\n") + else: + digest = pooch.file_hash(file, alg=self.hash) + f.write(f"{file.name} {self.hash}:{digest}\n") + ### Arugment Validation Methods def _check_cache_path_input(self, cache_path): if cache_path is None: path = Path(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER)) @@ -349,11 +415,12 @@ def _check_cache_path_input(self, cache_path): Path(path).mkdir(parents=True, exist_ok=True) return path - def _check_hash_input(self, hash): if hash in hashlib.algorithms_available: return hash + elif hash is None: + return None else: raise ValueError( f'Invalid hash "{hash}". Valid hashes algorithms are {hashlib.algorithms_available}.' diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 1b6fa3e3b1..5f565b0df2 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -21,7 +21,6 @@ # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # -import os import re from pathlib import Path from shutil import rmtree @@ -63,16 +62,6 @@ def test_pooch_installation(): @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") class TestExpectedErrors: - def test_missing_base_url(self, tmp_path): - with temporary_http_server() as (host, port, temp_folder): - downloader = StaticFetcher(cache_path=tmp_path) - - with pytest.raises( - ValueError, - match=re.escape("base_url is not defined in fetch()"), - ): - downloader.fetch(file_name="TEST_FILE1.txt") - def test_invalid_downloader(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" @@ -149,6 +138,33 @@ def test_create_database(self, tmp_path): downloader.db_path ).read_text() == "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + def test_append_database(self, tmp_path): + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher(cache_path=tmp_path) + path = downloader.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name=REGISTRY_NAME, + ) + + path = downloader.fetch( + base_url=base_url, + file_name="TEST_FILE2.txt", + db_name=REGISTRY_NAME, + append_db=True + ) + + assert downloader.db_path.read_text() == ( + "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" + ) + + + + + + def test_different_hashes(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" From 4532bf2e8feeb9c8b3be4c42589500cf1ef7633a Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 02:35:29 -0700 Subject: [PATCH 045/100] works with test --- package/MDAnalysis/fetch/fetchers.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index dd61621628..e0de3e577c 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -320,7 +320,7 @@ def fetch( if CREATE_DATABASE: self.write_registry(self.db_path, paths) - if APPEND_DATABASE: + if APPEND_DATABASE and LOAD_FROM_CACHE: self.fix_registry(self.db_path, registry_dictionary) return paths[0] if len(paths) == 1 else paths @@ -328,16 +328,15 @@ def fetch( # Reads pooch registry file format # https://www.fatiando.org/pooch/latest/registry-files.html#registry-file-format - def fix_registry(self, db_path, file_dict, ignore_files=[]): + def fix_registry(self, db_path, file_dict): + """Append newly downloaded files to an existing registry.""" - none_keys = [k for k, v in file_dict.items() if v is None] - - with open(db_path, "a") as f: - for no_hash_file in none_keys: - file = self.cache_path / no_hash_file - digest = pooch.file_hash(file, alg=self.hash) - - f.write(f"{file.name} {self.hash}:{digest}\n") + new_files = [ + self.cache_path / file_name + for file_name, file_hash in file_dict.items() + if file_hash is None + ] + self.write_registry(db_path, new_files, mode="a") From b9edac0677d3dae3539b81dd1ee923402bd3862f Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 02:42:01 -0700 Subject: [PATCH 046/100] removed Hash=None --- package/MDAnalysis/fetch/fetchers.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index e0de3e577c..dec08eb077 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -399,11 +399,8 @@ def write_registry(self, db_path, files, mode="w"): with open(db_path, mode=mode) as f: for file in files: - if self.hash is None: - f.write(f"{file.name} None\n") - else: - digest = pooch.file_hash(file, alg=self.hash) - f.write(f"{file.name} {self.hash}:{digest}\n") + digest = pooch.file_hash(file, alg=self.hash) + f.write(f"{file.name} {self.hash}:{digest}\n") ### Arugment Validation Methods def _check_cache_path_input(self, cache_path): @@ -418,8 +415,6 @@ def _check_cache_path_input(self, cache_path): def _check_hash_input(self, hash): if hash in hashlib.algorithms_available: return hash - elif hash is None: - return None else: raise ValueError( f'Invalid hash "{hash}". Valid hashes algorithms are {hashlib.algorithms_available}.' From 0a8d4d69dda19dc052feddfb31f3bd5449191a1a Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 03:03:53 -0700 Subject: [PATCH 047/100] minor update --- package/MDAnalysis/fetch/fetchers.py | 80 +++++++++++++++++++--------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index dec08eb077..00312701cb 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -338,25 +338,28 @@ def fix_registry(self, db_path, file_dict): ] self.write_registry(db_path, new_files, mode="a") - - - def check_registry(self, db_path): """ - Return cache files that are missing from the registry database. + Return cache files that are missing from the registry. - Reads the registry at ``db_path`` and compares its recorded filenames - against the files currently present in ``self.cache_path``. The registry - database file itself is ignored. + Parameters + ---------- + db_path : str or path-like + Path to the registry file to read. - Args: - db_path: Path to the registry database to read. + Returns + ------- + missing_files : list of pathlib.Path + Cache file paths whose filenames are not present in the registry. + The registry database file itself is excluded from the result. - Returns: - list[pathlib.Path]: A list of cache file paths whose filenames are not - present in the registry. + Notes + ----- + This method compares filenames from the registry against files found + recursively under ``self.cache_path``. A cache file is considered missing + from the registry when ``path.name`` is not a key in the registry + dictionary. """ - registry_dictionary = self.read_registry(db_path) database_files = set(registry_dictionary.keys()) @@ -372,19 +375,26 @@ def check_registry(self, db_path): def read_registry(self, db_path): """ - Read a registry file into a dictionary of filenames and hashes. + Read a Pooch registry file into a dictionary. - Each line in the registry file is expected to contain a filename and its - corresponding hash value, separated by whitespace. + Parameters + ---------- + db_path : str or path-like + Path to the registry file to read. - Args: - db_path: Path to the registry file to read. + Returns + ------- + hash_dict : dict + Dictionary mapping each filename in the registry to its stored hash + value. Hash values are expected to include the hash algorithm prefix, + for example ``"sha256:"``. - Returns: - dict[str, str]: A dictionary where each key is a filename and each value - is the file's stored hash. - """ + Notes + ----- + Each line in the registry file is expected to have the format:: + : + """ hash_dict = {} with open(db_path, mode="r") as f: @@ -395,13 +405,35 @@ def read_registry(self, db_path): return hash_dict def write_registry(self, db_path, files, mode="w"): - """Method to exclusively write pooch registry""" + """ + Write a Pooch registry file with hashes for the given files. + Parameters + ---------- + db_path : str or path-like + Path to the registry file to write. + files : iterable of path-like + Files to include in the registry. Each file must provide a ``name`` + attribute and be readable by ``pooch.file_hash``. + mode : str, optional + File opening mode used when writing the registry. Default is ``"w"``. + + Returns + ------- + None + This method writes the registry to disk and does not return a value. + + Notes + ----- + Each registry line is written in the format:: + + : + """ with open(db_path, mode=mode) as f: for file in files: digest = pooch.file_hash(file, alg=self.hash) f.write(f"{file.name} {self.hash}:{digest}\n") - + ### Arugment Validation Methods def _check_cache_path_input(self, cache_path): if cache_path is None: From 191465044332eb4645de4076a7f95599f70fb426 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 03:08:47 -0700 Subject: [PATCH 048/100] minor docstring --- package/MDAnalysis/fetch/fetchers.py | 28 ++++++++++++++++---- package/doc/sphinx/build_docs.sh | 12 --------- testsuite/MDAnalysisTests/fetch/run_tests.sh | 2 -- 3 files changed, 23 insertions(+), 19 deletions(-) delete mode 100755 package/doc/sphinx/build_docs.sh delete mode 100755 testsuite/MDAnalysisTests/fetch/run_tests.sh diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 00312701cb..b9d3901f7a 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -325,19 +325,37 @@ def fetch( return paths[0] if len(paths) == 1 else paths - # Reads pooch registry file format - # https://www.fatiando.org/pooch/latest/registry-files.html#registry-file-format - def fix_registry(self, db_path, file_dict): - """Append newly downloaded files to an existing registry.""" + """ + Append newly downloaded files to an existing Pooch registry. + + Parameters + ---------- + db_path : str or path-like + Path to the registry file to update. + file_dict : dict + Dictionary mapping filenames to hash values. Files with a hash value of + ``None`` are treated as newly downloaded files and appended to the + registry. + + Returns + ------- + None + This method updates the registry file in place and does not return a + value. + Notes + ----- + For each entry in ``file_dict`` with a value of ``None``, this method builds + the corresponding file path relative to ``self.cache_path`` and appends its + hash to the registry using ``self.write_registry``. + """ new_files = [ self.cache_path / file_name for file_name, file_hash in file_dict.items() if file_hash is None ] self.write_registry(db_path, new_files, mode="a") - def check_registry(self, db_path): """ Return cache files that are missing from the registry. diff --git a/package/doc/sphinx/build_docs.sh b/package/doc/sphinx/build_docs.sh deleted file mode 100755 index f586f6fa41..0000000000 --- a/package/doc/sphinx/build_docs.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -#./build_docs.sh 2>&1 | tee html_output - -eval "$(mamba shell hook --shell bash)" -mamba activate mdanalysis-dev - -rm -rfv ../html/* && make html - -cd .. - -#python -m http.server 9191 diff --git a/testsuite/MDAnalysisTests/fetch/run_tests.sh b/testsuite/MDAnalysisTests/fetch/run_tests.sh deleted file mode 100755 index 485d7d9f33..0000000000 --- a/testsuite/MDAnalysisTests/fetch/run_tests.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -pytest -v --cov=/nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/fetch --cov-report term-missing From 57af7d80687624f277f8bad6cd040d0ac007fe83 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 03:11:47 -0700 Subject: [PATCH 049/100] add gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index ed6eebde88..41a19aa709 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# for rn +package/doc/sphinx/build_docs.sh +testsuite/MDAnalysisTests/fetch/run_tests.sh + + # Ignore python bytecoded files *.py[cod] *.[oa] From 47a1220d66027b3e0f64a29633c29e7b9debbee3 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 03:12:58 -0700 Subject: [PATCH 050/100] removed pytest.log --- testsuite/MDAnalysisTests/pytest.log | 240 --------------------------- 1 file changed, 240 deletions(-) delete mode 100644 testsuite/MDAnalysisTests/pytest.log diff --git a/testsuite/MDAnalysisTests/pytest.log b/testsuite/MDAnalysisTests/pytest.log deleted file mode 100644 index f63dff14ec..0000000000 --- a/testsuite/MDAnalysisTests/pytest.log +++ /dev/null @@ -1,240 +0,0 @@ -============================= test session starts ============================== -platform linux -- Python 3.13.14, pytest-9.1.0, pluggy-1.6.0 -rootdir: /nfs/homes3/jauy1/Projects/Dev/mdanalysis/testsuite -configfile: pyproject.toml -plugins: hypothesis-6.155.3, xdist-3.8.0, timeout-2.4.0, cov-7.1.0 -collected 23956 items / 1 error - -==================================== ERRORS ==================================== -___________ ERROR collecting MDAnalysisTests/core/test_atomgroup.py ____________ -MDAnalysisTests/core/test_atomgroup.py::TestAtomGroupToTopology::test_VE: in "parametrize" the number of names (1): - ['btype'] -must be equal to the number of values (4): - bond -=============================== warnings summary =============================== -../../package/MDAnalysis/coordinates/DCD.py:171 -../../package/MDAnalysis/coordinates/DCD.py:171 - /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/coordinates/DCD.py:171: DeprecationWarning: DCDReader currently makes independent timesteps by copying self.ts while other readers update self.ts inplace. This behavior will be changed in 3.0 to be the same as other readers. Read more at https://github.com/MDAnalysis/mdanalysis/issues/3889 to learn if this change in behavior might affect you. - warnings.warn("DCDReader currently makes independent timesteps" - -../../package/MDAnalysis/analysis/encore/__init__.py:45 - /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/encore/__init__.py:45: DeprecationWarning: Deprecation in version 2.8.0 - MDAnalysis.analysis.encore is deprecated in favour of the MDAKit mdaencore (https://www.mdanalysis.org/mdaencore/) and will be removed in MDAnalysis version 3.0.0. - warnings.warn(wmsg, category=DeprecationWarning) - -../../package/MDAnalysis/analysis/hole2/__init__.py:58 - /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/hole2/__init__.py:58: DeprecationWarning: Deprecated in version 2.8.0 - MDAnalysis.analysis.hole2 is deprecated in favour of the MDAKit madahole2 (https://www.mdanalysis.org/mdahole2/) and will be removed in MDAnalysis version 3.0.0 - warnings.warn(wmsg, category=DeprecationWarning) - -../../package/MDAnalysis/analysis/hbonds/hbond_autocorrel.py:54 - /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/hbonds/hbond_autocorrel.py:54: DeprecationWarning: This module was moved to MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel; hbonds.hbond_autocorrel will be removed in 3.0.0. - warnings.warn(wmsg, category=DeprecationWarning) - -analysis/test_nucleicacids.py:212 - /nfs/homes3/jauy1/Projects/Dev/mdanalysis/testsuite/MDAnalysisTests/analysis/test_nucleicacids.py:212: SyntaxWarning: invalid escape sequence '\.' - """WatsonCrickDist must match the full resname, not just resname[0]\. - -../../package/MDAnalysis/analysis/psa.py:80 - /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/psa.py:80: DeprecationWarning: Deprecation in version 2.8.0: - MDAnalysis.analysis.psa is deprecated in favour of the MDAKit PathSimAnalysis (https://github.com/MDAnalysis/PathSimAnalysis) and will be removed in MDAnalysis version 3.0.0 - warnings.warn(wmsg, category=DeprecationWarning) - -../../package/MDAnalysis/analysis/waterdynamics.py:65 - /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/waterdynamics.py:65: DeprecationWarning: Deprecation in version 2.8.0 - MDAnalysis.analysis.waterdynamics is deprecated in favour of the MDAKit waterdynamics (https://www.mdanalysis.org/waterdynamics/) and will be removed in MDAnalysis version 3.0.0 - warnings.warn(wmsg, category=DeprecationWarning) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/coordinates/test_timestep_api.py::TestTimestep::test_from_coordinates, argvalues type: filter - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/coordinates/test_timestep_api.py::TestTimestep::test_check_equal, argvalues type: filter - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/core/test_atomgroup.py::TestCrossUniverse::test_add_mixed_universes, argvalues type: product - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/core/test_groups.py::TestGroupSlicing::test_slice, argvalues type: product - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/core/test_groups.py::TestGroupAddition::test_addition, argvalues type: generator - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/core/test_groups.py::TestGroupAddition::test_sum, argvalues type: generator - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/core/test_groups.py::TestGroupAddition::test_bad_sum, argvalues type: generator - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/core/test_groups.py::TestComponentComparisons::test_crosslevel_cmp, argvalues type: permutations - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/core/test_groups.py::TestComponentComparisons::test_crosslevel_eq, argvalues type: permutations - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/core/test_groups.py::TestGroupBaseOperators::test_failing_pairs, argvalues type: chain - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/core/test_groups.py::TestGroupBaseOperators::test_succeeding_pairs, argvalues type: chain - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/core/test_groups.py::TestGroupHash::test_hash_difference_cross, argvalues type: permutations - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/lib/test_distances.py::test_minimize_vectors, argvalues type: product - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/lib/test_mdamath.py::TestMatrixOperations::test_triclinic_vectors, argvalues type: combinations_with_replacement - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/lib/test_mdamath.py::TestMatrixOperations::test_triclinic_box, argvalues type: combinations_with_replacement - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/lib/test_mdamath.py::TestMatrixOperations::test_box_volume, argvalues type: combinations_with_replacement - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/lib/test_util.py::TestStringFunctions::test_convert_aa_3to1, argvalues type: generator - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../../../../opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124 - /nfs/homes3/jauy1/opt/miniforge3/envs/mdanalysis-dev/lib/python3.13/site-packages/_pytest/python.py:124: PytestRemovedIn10Warning: Passing a non-Collection iterable to parametrize is deprecated. - Test: MDAnalysisTests/test_api.py::test_all_import, argvalues type: generator - Please convert to a list or tuple. - See https://docs.pytest.org/en/stable/deprecations.html#parametrize-iterators - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - -../../package/MDAnalysis/topology/tables.py:52 - /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/topology/tables.py:52: DeprecationWarning: Deprecated in version 2.8.0 - MDAnalysis.topology.tables has been moved to MDAnalysis.guesser.tables. This import point will be removed in MDAnalysis version 3.0.0 - warnings.warn(wmsg, category=DeprecationWarning) - --- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html -=========================== short test summary info ============================ -ERROR core/test_atomgroup.py::TestAtomGroupToTopology - Failed: MDAnalysisTes... -!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!! -================== 30 warnings, 1 error in 105.46s (0:01:45) =================== - -DueCredit Report: -- Molecular simulation analysis library / MDAnalysis (v 2.11.0.dev0) [1, 2] - - Iterative Calculation of Opimal Reference / MDAnalysis.analysis.align.iterative_average (v 2.11.0.dev0) [3] - - Bond-Angle-Torsions Coordinate Transformation / MDAnalysis.analysis.bat.BAT (v 2.11.0.dev0) [4] - - Dielectric analysis / MDAnalysis.analysis.dielectric (v 2.11.0.dev0) [5] - - DSSP algorithm description / MDAnalysis.analysis.dssp (v 2.11.0.dev0) [6] - - ENCORE Ensemble Comparison / MDAnalysis.analysis.encore (v 2.11.0.dev0) [7] - - Hydrogen bond analysis implementation / MDAnalysis.analysis.hydrogenbonds.hbond_analysis (v 2.11.0.dev0) [8] - - Hydrogen bonding autocorrelation time / MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel (v 2.11.0.dev0) [9] - - LeafletFinder algorithm / MDAnalysis.analysis.leaflet (v 2.11.0.dev0) [2] - - Mean Squared Displacements with tidynamics, FCA fast correlation algorithm / MDAnalysis.analysis.msd (v 2.11.0.dev0) [10, 11] - - Cumulative overlap / MDAnalysis.analysis.pca (v 2.11.0.dev0) [12] - - MDAnalysis trajectory reader/writer of the H5MDformat, Specifications of the H5MD standard / MDAnalysis.coordinates.H5MD (v 1.1, 2.11.0.dev0) [13, 14] - - MMTF Reader / MDAnalysis.coordinates.MMTF (v 2.11.0.dev0) [15] - - The TNG paper / MDAnalysis.coordinates.TNG (v 2.11.0.dev0) [16] - - QCProt implementation / MDAnalysis.lib.qcprot (v 2.11.0.dev0) [17, 18] - - MMTF Parser / MDAnalysis.topology.MMTFParser (v 2.11.0.dev0) [15] - - Works through the orthogonal case for unwrapping, and proposes the non-orthogonal approach. / MDAnalysis.transformations.nojump (v 2.11.0.dev0) [19] - - HOLE program, HOLE trajectory analysis with orderparameters / mdahole2.analysis.hole (v 0.0.0) [20, 21, 22] -- Scientific tools library / numpy (v 1.26.4) [23] - - Path Similarity Analysis algorithm and implementation / pathsimanalysis.psa (v 0.0.0) [24] - -2 packages cited -18 modules cited -0 functions cited - -References ----------- - -[1] Gowers, R. et al., 2016. MDAnalysis: A Python Package for the Rapid Analysis of Molecular Dynamics Simulations. In Proceedings of the 15th Python in Science Conference. SciPy. SciPy, pp. 98–105. -[2] Michaud‐Agrawal, N. et al., 2011. MDAnalysis: A toolkit for the analysis of molecular dynamics simulations. Journal of Computational Chemistry, 32(10), pp.2319–2327. -[3] Linke, M., Köfinger, J. & Hummer, G., 2018. Fully Anisotropic Rotational Diffusion Tensor from Molecular Dynamics Simulations. The Journal of Physical Chemistry B, 122(21), pp.5630–5639. -[4] Minh, D.D.L., 2019. Alchemical Grid Dock (AlGDock): Binding Free Energy Calculations between Flexible Ligands and Rigid Receptors. Journal of Computational Chemistry, 41(7), pp.715–730. -[5] Neumann, M., 1983. Dipole moment fluctuation formulas in computer simulations of polar systems. Molecular Physics, 50(4), pp.841–858. -[6] Kabsch, W. & Sander, C., 1983. Dictionary of protein secondary structure: Pattern recognition of hydrogen‐bonded and geometrical features. Biopolymers, 22(12), pp.2577–2637. -[7] Tiberti, M. et al., 2015. ENCORE: Software for Quantitative Ensemble Comparison B. L. de Groot, ed.. PLOS Computational Biology, 11(10), p.1004415. -[8] Smith, P. et al., 2019. On the interaction of hyaluronic acid with synovial fluid lipid membranes. Physical Chemistry Chemical Physics, 21(19), pp.9845–9857. -[9] ERRORED: 'june' -[10] de Buyl, P., 2018. tidynamics: A tiny package to compute the dynamics of stochastic and molecular simulations. Journal of Open Source Software, 3(28), p.877. -[11] Calandrini, V. et al., 2011. nMoldyn - Interfacing spectroscopic experiments, molecular dynamics simulations and models for time correlation functions. École thématique de la Société Française de la Neutronique, 12, pp.201–232. -[12] Yang, L. et al., 2008. Close Correspondence between the Motions from Principal Component Analysis of Multiple HIV-1 Protease Structures and Elastic Network Modes. Structure, 16(2), pp.321–330. -[13] Jakupovic, E. & Beckstein, O., 2021. MPI-parallel Molecular Dynamics Trajectory Analysis with the H5MD Format in the MDAnalysis Python Package. In Proceedings of the 20th Python in Science Conference. SciPy. SciPy, pp. 40–48. -[14] ERRORED: 'june' -[15] ERRORED: 'june' -[16] Lundborg, M. et al., 2013. An efficient and extensible format, library, and API for binary trajectory data from molecular simulations. Journal of Computational Chemistry, 35(3), pp.260–269. -[17] ERRORED: 'june' -[18] Liu, P., Agrafiotis, D.K. & Theobald, D.L., Fast determination of the optimal rotational matrix for macromolecular superpositions. Journal of Computational Chemistry, 31(7), pp.1561–1563. -[19] ERRORED: 'sept' -[20] Smart, O.S., Goodfellow, J.M. & Wallace, B.A., 1993. The pore dimensions of gramicidin A. Biophysical Journal, 65(6), pp.2455–2460. -[21] Smart, O.S. et al., 1996. HOLE: A program for the analysis of the pore dimensions of ion channel structural models. Journal of Molecular Graphics, 14(6), pp.354–360. -[22] Stelzl, L.S. et al., 2014. Flexible Gates Generate Occluded Intermediates in the Transport Cycle of LacY. Journal of Molecular Biology, 426(3), pp.735–751. -[23] Van Der Walt, S., Colbert, S.C. & Varoquaux, G., 2011. The NumPy array: a structure for efficient numerical computation. Computing in Science & Engineering, 13(2), pp.22–30. -[24] Seyler, S.L. et al., 2015. Path Similarity Analysis: A Method for Quantifying Macromolecular Pathways E. Tajkhorshid, ed.. PLOS Computational Biology, 11(10), p.1004568. From 3c76626eba42f91110a1e5399351b012808de98f Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 07:56:53 -0700 Subject: [PATCH 051/100] Applied black --- package/MDAnalysis/fetch/fetchers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index b9d3901f7a..9eb3a31b5b 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -356,6 +356,7 @@ def fix_registry(self, db_path, file_dict): if file_hash is None ] self.write_registry(db_path, new_files, mode="a") + def check_registry(self, db_path): """ Return cache files that are missing from the registry. @@ -451,7 +452,7 @@ def write_registry(self, db_path, files, mode="w"): for file in files: digest = pooch.file_hash(file, alg=self.hash) f.write(f"{file.name} {self.hash}:{digest}\n") - + ### Arugment Validation Methods def _check_cache_path_input(self, cache_path): if cache_path is None: From fb9676daa1a7290d0fff0f5ba5951f8a28cdd64f Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 13:24:31 -0700 Subject: [PATCH 052/100] Removed weird files --- .github/workflows/gh-ci.yaml | 4 ++-- package/MDAnalysis/TEST.py | 10 ---------- package/MDAnalysis/fetch/TODO | 10 ---------- 3 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 package/MDAnalysis/TEST.py delete mode 100644 package/MDAnalysis/fetch/TODO diff --git a/.github/workflows/gh-ci.yaml b/.github/workflows/gh-ci.yaml index 344792acf1..99894ca434 100644 --- a/.github/workflows/gh-ci.yaml +++ b/.github/workflows/gh-ci.yaml @@ -3,11 +3,9 @@ on: push: branches: - develop - - fetcher_gsoc pull_request: branches: - develop - - fetcher_gsoc workflow_dispatch: concurrency: @@ -23,6 +21,7 @@ defaults: jobs: main_tests: + if: "github.repository == 'MDAnalysis/mdanalysis'" runs-on: ${{ matrix.os }} timeout-minutes: 60 strategy: @@ -155,6 +154,7 @@ jobs: build_docs: + if: "github.repository == 'MDAnalysis/mdanalysis'" runs-on: ubuntu-latest timeout-minutes: 15 env: diff --git a/package/MDAnalysis/TEST.py b/package/MDAnalysis/TEST.py deleted file mode 100644 index 2b97e07c5c..0000000000 --- a/package/MDAnalysis/TEST.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python3 - -from MDAnalysis.fetch.pdb import from_PDB - -import shutil - -shutil.rmtree("/nfs/homes3/jauy1/.cache/MDAnalysis_pdbs", ignore_errors=True) - -print(from_PDB(['1AKE'])) -print(from_PDB(['1AKE', '4AKE'])) diff --git a/package/MDAnalysis/fetch/TODO b/package/MDAnalysis/fetch/TODO deleted file mode 100644 index fb2e1a6c30..0000000000 --- a/package/MDAnalysis/fetch/TODO +++ /dev/null @@ -1,10 +0,0 @@ -TODO LIST - -1. Implement Fetcher, and StaticFetcher (with automatic caching) Classes -2. Replicate pdb/from_pdb() existing behavior using StaticFetcher -3. Try this for other databases -3a. MDDB -3b. - -Later: -Think about how to implement the DynamicFetcher Class (have it yield a generator) \ No newline at end of file From dcc07e0934ca9488df9ce1d6fd6a4b877734e7d0 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 13:25:33 -0700 Subject: [PATCH 053/100] removed junk output --- package/doc/sphinx/html_output | 1063 -------------------------------- 1 file changed, 1063 deletions(-) delete mode 100644 package/doc/sphinx/html_output diff --git a/package/doc/sphinx/html_output b/package/doc/sphinx/html_output deleted file mode 100644 index 067989bcb9..0000000000 --- a/package/doc/sphinx/html_output +++ /dev/null @@ -1,1063 +0,0 @@ -removed '../html/documentation_pages/analysis/align.html' -removed '../html/documentation_pages/analysis/atomicdistances.html' -removed '../html/documentation_pages/analysis/backends.html' -removed '../html/documentation_pages/analysis/base.html' -removed '../html/documentation_pages/analysis/bat.html' -removed '../html/documentation_pages/analysis/contacts.html' -removed '../html/documentation_pages/analysis/data.html' -removed '../html/documentation_pages/analysis/density.html' -removed '../html/documentation_pages/analysis/dielectric.html' -removed '../html/documentation_pages/analysis/diffusionmap.html' -removed '../html/documentation_pages/analysis/dihedrals.html' -removed '../html/documentation_pages/analysis/distances.html' -removed '../html/documentation_pages/analysis/dssp.html' -removed '../html/documentation_pages/analysis/encore.html' -removed '../html/documentation_pages/analysis/encore/bootstrap.html' -removed '../html/documentation_pages/analysis/encore/clustering.html' -removed '../html/documentation_pages/analysis/encore/confdistmatrix.html' -removed '../html/documentation_pages/analysis/encore/covariance.html' -removed '../html/documentation_pages/analysis/encore/dimensionality_reduction.html' -removed '../html/documentation_pages/analysis/encore/similarity.html' -removed '../html/documentation_pages/analysis/encore/utils.html' -removed directory '../html/documentation_pages/analysis/encore' -removed '../html/documentation_pages/analysis/gnm.html' -removed '../html/documentation_pages/analysis/hbond_autocorrel.html' -removed '../html/documentation_pages/analysis/hbond_autocorrel_deprecated.html' -removed '../html/documentation_pages/analysis/helix_analysis.html' -removed '../html/documentation_pages/analysis/hole2.html' -removed '../html/documentation_pages/analysis/hydrogenbonds.html' -removed '../html/documentation_pages/analysis/leaflet.html' -removed '../html/documentation_pages/analysis/legacy/x3dna.html' -removed directory '../html/documentation_pages/analysis/legacy' -removed '../html/documentation_pages/analysis/legacy_modules.html' -removed '../html/documentation_pages/analysis/lineardensity.html' -removed '../html/documentation_pages/analysis/msd.html' -removed '../html/documentation_pages/analysis/nucleicacids.html' -removed '../html/documentation_pages/analysis/nuclinfo.html' -removed '../html/documentation_pages/analysis/parallelization.html' -removed '../html/documentation_pages/analysis/pca.html' -removed '../html/documentation_pages/analysis/polymer.html' -removed '../html/documentation_pages/analysis/psa.html' -removed '../html/documentation_pages/analysis/rdf.html' -removed '../html/documentation_pages/analysis/results.html' -removed '../html/documentation_pages/analysis/rms.html' -removed '../html/documentation_pages/analysis/waterdynamics.html' -removed '../html/documentation_pages/analysis/wbridge_analysis.html' -removed directory '../html/documentation_pages/analysis' -removed '../html/documentation_pages/analysis_modules.html' -removed '../html/documentation_pages/auxiliary/EDR.html' -removed '../html/documentation_pages/auxiliary/XVG.html' -removed '../html/documentation_pages/auxiliary/base.html' -removed '../html/documentation_pages/auxiliary/core.html' -removed '../html/documentation_pages/auxiliary/init.html' -removed directory '../html/documentation_pages/auxiliary' -removed '../html/documentation_pages/auxiliary_modules.html' -removed '../html/documentation_pages/converters.html' -removed '../html/documentation_pages/converters/OpenMM.html' -removed '../html/documentation_pages/converters/ParmEd.html' -removed '../html/documentation_pages/converters/RDKit.html' -removed '../html/documentation_pages/converters/base.html' -removed directory '../html/documentation_pages/converters' -removed '../html/documentation_pages/coordinates/CRD.html' -removed '../html/documentation_pages/coordinates/DCD.html' -removed '../html/documentation_pages/coordinates/DLPoly.html' -removed '../html/documentation_pages/coordinates/DMS.html' -removed '../html/documentation_pages/coordinates/FHIAIMS.html' -removed '../html/documentation_pages/coordinates/GMS.html' -removed '../html/documentation_pages/coordinates/GRO.html' -removed '../html/documentation_pages/coordinates/GSD.html' -removed '../html/documentation_pages/coordinates/H5MD.html' -removed '../html/documentation_pages/coordinates/IMD.html' -removed '../html/documentation_pages/coordinates/INPCRD.html' -removed '../html/documentation_pages/coordinates/LAMMPS.html' -removed '../html/documentation_pages/coordinates/MMTF.html' -removed '../html/documentation_pages/coordinates/MOL2.html' -removed '../html/documentation_pages/coordinates/NAMDBIN.html' -removed '../html/documentation_pages/coordinates/PDB.html' -removed '../html/documentation_pages/coordinates/PDBQT.html' -removed '../html/documentation_pages/coordinates/PQR.html' -removed '../html/documentation_pages/coordinates/TNG.html' -removed '../html/documentation_pages/coordinates/TPR.html' -removed '../html/documentation_pages/coordinates/TRC.html' -removed '../html/documentation_pages/coordinates/TRJ.html' -removed '../html/documentation_pages/coordinates/TRR.html' -removed '../html/documentation_pages/coordinates/TRZ.html' -removed '../html/documentation_pages/coordinates/TXYZ.html' -removed '../html/documentation_pages/coordinates/XDR.html' -removed '../html/documentation_pages/coordinates/XTC.html' -removed '../html/documentation_pages/coordinates/XYZ.html' -removed '../html/documentation_pages/coordinates/base.html' -removed '../html/documentation_pages/coordinates/chain.html' -removed '../html/documentation_pages/coordinates/chemfiles.html' -removed '../html/documentation_pages/coordinates/core.html' -removed '../html/documentation_pages/coordinates/init.html' -removed '../html/documentation_pages/coordinates/memory.html' -removed '../html/documentation_pages/coordinates/null.html' -removed '../html/documentation_pages/coordinates/pickle_readers.html' -removed '../html/documentation_pages/coordinates/timestep.html' -removed directory '../html/documentation_pages/coordinates' -removed '../html/documentation_pages/coordinates_modules.html' -removed '../html/documentation_pages/core/accessors.html' -removed '../html/documentation_pages/core/groups.html' -removed '../html/documentation_pages/core/init.html' -removed '../html/documentation_pages/core/selection.html' -removed '../html/documentation_pages/core/topology.html' -removed '../html/documentation_pages/core/topologyattrs.html' -removed '../html/documentation_pages/core/topologyobjects.html' -removed '../html/documentation_pages/core/universe.html' -removed directory '../html/documentation_pages/core' -removed '../html/documentation_pages/core_modules.html' -removed '../html/documentation_pages/exceptions.html' -removed '../html/documentation_pages/fetchers/PDB.html' -removed '../html/documentation_pages/fetchers/fetchers.html' -removed '../html/documentation_pages/fetchers/init.html' -removed directory '../html/documentation_pages/fetchers' -removed '../html/documentation_pages/fetchers_modules.html' -removed '../html/documentation_pages/guesser_modules.html' -removed '../html/documentation_pages/guesser_modules/base.html' -removed '../html/documentation_pages/guesser_modules/default_guesser.html' -removed '../html/documentation_pages/guesser_modules/init.html' -removed '../html/documentation_pages/guesser_modules/tables.html' -removed directory '../html/documentation_pages/guesser_modules' -removed '../html/documentation_pages/lib/NeighborSearch.html' -removed '../html/documentation_pages/lib/c_distances.html' -removed '../html/documentation_pages/lib/c_distances_openmp.html' -removed '../html/documentation_pages/lib/correlations.html' -removed '../html/documentation_pages/lib/distances.html' -removed '../html/documentation_pages/lib/formats/libdcd.html' -removed '../html/documentation_pages/lib/formats/libmdaxdr.html' -removed directory '../html/documentation_pages/lib/formats' -removed '../html/documentation_pages/lib/log.html' -removed '../html/documentation_pages/lib/mdamath.html' -removed '../html/documentation_pages/lib/nsgrid.html' -removed '../html/documentation_pages/lib/picklable_file_io.html' -removed '../html/documentation_pages/lib/pkdtree.html' -removed '../html/documentation_pages/lib/qcprot.html' -removed '../html/documentation_pages/lib/transformations.html' -removed '../html/documentation_pages/lib/util.html' -removed directory '../html/documentation_pages/lib' -removed '../html/documentation_pages/lib_modules.html' -removed '../html/documentation_pages/overview.html' -removed '../html/documentation_pages/references.html' -removed '../html/documentation_pages/selections.html' -removed '../html/documentation_pages/selections/base.html' -removed '../html/documentation_pages/selections/charmm.html' -removed '../html/documentation_pages/selections/gromacs.html' -removed '../html/documentation_pages/selections/jmol.html' -removed '../html/documentation_pages/selections/pymol.html' -removed '../html/documentation_pages/selections/vmd.html' -removed directory '../html/documentation_pages/selections' -removed '../html/documentation_pages/selections_modules.html' -removed '../html/documentation_pages/topology.html' -removed '../html/documentation_pages/topology/CRDParser.html' -removed '../html/documentation_pages/topology/DLPolyParser.html' -removed '../html/documentation_pages/topology/DMSParser.html' -removed '../html/documentation_pages/topology/ExtendedPDBParser.html' -removed '../html/documentation_pages/topology/FHIAIMSParser.html' -removed '../html/documentation_pages/topology/GMSParser.html' -removed '../html/documentation_pages/topology/GROParser.html' -removed '../html/documentation_pages/topology/GSDParser.html' -removed '../html/documentation_pages/topology/HoomdXMLParser.html' -removed '../html/documentation_pages/topology/ITPParser.html' -removed '../html/documentation_pages/topology/LAMMPSParser.html' -removed '../html/documentation_pages/topology/MMTFParser.html' -removed '../html/documentation_pages/topology/MOL2Parser.html' -removed '../html/documentation_pages/topology/MinimalParser.html' -removed '../html/documentation_pages/topology/PDBParser.html' -removed '../html/documentation_pages/topology/PDBQTParser.html' -removed '../html/documentation_pages/topology/PQRParser.html' -removed '../html/documentation_pages/topology/PSFParser.html' -removed '../html/documentation_pages/topology/TOPParser.html' -removed '../html/documentation_pages/topology/TPRParser.html' -removed '../html/documentation_pages/topology/TXYZParser.html' -removed '../html/documentation_pages/topology/XYZParser.html' -removed '../html/documentation_pages/topology/base.html' -removed '../html/documentation_pages/topology/core.html' -removed '../html/documentation_pages/topology/guessers.html' -removed '../html/documentation_pages/topology/init.html' -removed '../html/documentation_pages/topology/tables.html' -removed '../html/documentation_pages/topology/tpr_util.html' -removed directory '../html/documentation_pages/topology' -removed '../html/documentation_pages/topology_modules.html' -removed '../html/documentation_pages/trajectory_transformations.html' -removed '../html/documentation_pages/transformations/base.html' -removed '../html/documentation_pages/transformations/boxdimensions.html' -removed '../html/documentation_pages/transformations/fit.html' -removed '../html/documentation_pages/transformations/nojump.html' -removed '../html/documentation_pages/transformations/positionaveraging.html' -removed '../html/documentation_pages/transformations/rotate.html' -removed '../html/documentation_pages/transformations/translate.html' -removed '../html/documentation_pages/transformations/wrap.html' -removed directory '../html/documentation_pages/transformations' -removed '../html/documentation_pages/units.html' -removed '../html/documentation_pages/version.html' -removed '../html/documentation_pages/visualization/streamlines.html' -removed '../html/documentation_pages/visualization/streamlines_3D.html' -removed directory '../html/documentation_pages/visualization' -removed '../html/documentation_pages/visualization_modules.html' -removed directory '../html/documentation_pages' -removed '../html/genindex.html' -removed '../html/_images/rama_ref_plot.png' -removed '../html/_images/janin_ref_plot.png' -removed '../html/_images/rama_demo_plot.png' -removed '../html/_images/janin_demo_plot.png' -removed '../html/_images/msd_demo_plot.png' -removed '../html/_images/AnalysisBase_parallel.png' -removed '../html/_images/parallelization_time.png' -removed '../html/_images/RSMD_plot.png' -removed '../html/_images/testing_streamline.png' -removed '../html/_images/test_streamplot_3D.png' -removed directory '../html/_images' -removed '../html/index.html' -removed '../html/_modules/MDAnalysis/analysis/align.html' -removed '../html/_modules/MDAnalysis/analysis/atomicdistances.html' -removed '../html/_modules/MDAnalysis/analysis/backends.html' -removed '../html/_modules/MDAnalysis/analysis/base.html' -removed '../html/_modules/MDAnalysis/analysis/bat.html' -removed '../html/_modules/MDAnalysis/analysis/contacts.html' -removed '../html/_modules/MDAnalysis/analysis/density.html' -removed '../html/_modules/MDAnalysis/analysis/dielectric.html' -removed '../html/_modules/MDAnalysis/analysis/diffusionmap.html' -removed '../html/_modules/MDAnalysis/analysis/dihedrals.html' -removed '../html/_modules/MDAnalysis/analysis/distances.html' -removed '../html/_modules/MDAnalysis/analysis/dssp/dssp.html' -removed '../html/_modules/MDAnalysis/analysis/dssp/pydssp_numpy.html' -removed directory '../html/_modules/MDAnalysis/analysis/dssp' -removed '../html/_modules/MDAnalysis/analysis/encore/bootstrap.html' -removed '../html/_modules/MDAnalysis/analysis/encore/clustering/ClusterCollection.html' -removed '../html/_modules/MDAnalysis/analysis/encore/clustering/ClusteringMethod.html' -removed '../html/_modules/MDAnalysis/analysis/encore/clustering/cluster.html' -removed directory '../html/_modules/MDAnalysis/analysis/encore/clustering' -removed '../html/_modules/MDAnalysis/analysis/encore/confdistmatrix.html' -removed '../html/_modules/MDAnalysis/analysis/encore/covariance.html' -removed '../html/_modules/MDAnalysis/analysis/encore/dimensionality_reduction/DimensionalityReductionMethod.html' -removed '../html/_modules/MDAnalysis/analysis/encore/dimensionality_reduction/reduce_dimensionality.html' -removed directory '../html/_modules/MDAnalysis/analysis/encore/dimensionality_reduction' -removed '../html/_modules/MDAnalysis/analysis/encore/similarity.html' -removed '../html/_modules/MDAnalysis/analysis/encore/utils.html' -removed directory '../html/_modules/MDAnalysis/analysis/encore' -removed '../html/_modules/MDAnalysis/analysis/gnm.html' -removed '../html/_modules/MDAnalysis/analysis/helix_analysis.html' -removed '../html/_modules/MDAnalysis/analysis/hydrogenbonds/hbond_analysis.html' -removed '../html/_modules/MDAnalysis/analysis/hydrogenbonds/hbond_autocorrel.html' -removed '../html/_modules/MDAnalysis/analysis/hydrogenbonds/wbridge_analysis.html' -removed directory '../html/_modules/MDAnalysis/analysis/hydrogenbonds' -removed '../html/_modules/MDAnalysis/analysis/leaflet.html' -removed '../html/_modules/MDAnalysis/analysis/legacy/x3dna.html' -removed directory '../html/_modules/MDAnalysis/analysis/legacy' -removed '../html/_modules/MDAnalysis/analysis/lineardensity.html' -removed '../html/_modules/MDAnalysis/analysis/msd.html' -removed '../html/_modules/MDAnalysis/analysis/nucleicacids.html' -removed '../html/_modules/MDAnalysis/analysis/nuclinfo.html' -removed '../html/_modules/MDAnalysis/analysis/pca.html' -removed '../html/_modules/MDAnalysis/analysis/polymer.html' -removed '../html/_modules/MDAnalysis/analysis/rdf.html' -removed '../html/_modules/MDAnalysis/analysis/results.html' -removed '../html/_modules/MDAnalysis/analysis/rms.html' -removed directory '../html/_modules/MDAnalysis/analysis' -removed '../html/_modules/MDAnalysis/auxiliary/EDR.html' -removed '../html/_modules/MDAnalysis/auxiliary/XVG.html' -removed '../html/_modules/MDAnalysis/auxiliary/base.html' -removed '../html/_modules/MDAnalysis/auxiliary/core.html' -removed directory '../html/_modules/MDAnalysis/auxiliary' -removed '../html/_modules/MDAnalysis/converters/OpenMM.html' -removed '../html/_modules/MDAnalysis/converters/OpenMMParser.html' -removed '../html/_modules/MDAnalysis/converters/ParmEd.html' -removed '../html/_modules/MDAnalysis/converters/ParmEdParser.html' -removed '../html/_modules/MDAnalysis/converters/RDKit.html' -removed '../html/_modules/MDAnalysis/converters/RDKitInferring.html' -removed '../html/_modules/MDAnalysis/converters/RDKitParser.html' -removed '../html/_modules/MDAnalysis/converters/base.html' -removed directory '../html/_modules/MDAnalysis/converters' -removed '../html/_modules/MDAnalysis/coordinates/CRD.html' -removed '../html/_modules/MDAnalysis/coordinates/DCD.html' -removed '../html/_modules/MDAnalysis/coordinates/DLPoly.html' -removed '../html/_modules/MDAnalysis/coordinates/DMS.html' -removed '../html/_modules/MDAnalysis/coordinates/FHIAIMS.html' -removed '../html/_modules/MDAnalysis/coordinates/GMS.html' -removed '../html/_modules/MDAnalysis/coordinates/GRO.html' -removed '../html/_modules/MDAnalysis/coordinates/GSD.html' -removed '../html/_modules/MDAnalysis/coordinates/H5MD.html' -removed '../html/_modules/MDAnalysis/coordinates/IMD.html' -removed '../html/_modules/MDAnalysis/coordinates/INPCRD.html' -removed '../html/_modules/MDAnalysis/coordinates/LAMMPS.html' -removed '../html/_modules/MDAnalysis/coordinates/MMTF.html' -removed '../html/_modules/MDAnalysis/coordinates/MOL2.html' -removed '../html/_modules/MDAnalysis/coordinates/NAMDBIN.html' -removed '../html/_modules/MDAnalysis/coordinates/PDB.html' -removed '../html/_modules/MDAnalysis/coordinates/PDBQT.html' -removed '../html/_modules/MDAnalysis/coordinates/PQR.html' -removed '../html/_modules/MDAnalysis/coordinates/TNG.html' -removed '../html/_modules/MDAnalysis/coordinates/TPR.html' -removed '../html/_modules/MDAnalysis/coordinates/TRC.html' -removed '../html/_modules/MDAnalysis/coordinates/TRJ.html' -removed '../html/_modules/MDAnalysis/coordinates/TRR.html' -removed '../html/_modules/MDAnalysis/coordinates/TRZ.html' -removed '../html/_modules/MDAnalysis/coordinates/TXYZ.html' -removed '../html/_modules/MDAnalysis/coordinates/XDR.html' -removed '../html/_modules/MDAnalysis/coordinates/XTC.html' -removed '../html/_modules/MDAnalysis/coordinates/XYZ.html' -removed '../html/_modules/MDAnalysis/coordinates/base.html' -removed '../html/_modules/MDAnalysis/coordinates/chain.html' -removed '../html/_modules/MDAnalysis/coordinates/chemfiles.html' -removed '../html/_modules/MDAnalysis/coordinates/core.html' -removed '../html/_modules/MDAnalysis/coordinates/memory.html' -removed '../html/_modules/MDAnalysis/coordinates/null.html' -removed directory '../html/_modules/MDAnalysis/coordinates' -removed '../html/_modules/MDAnalysis/core/_get_readers.html' -removed '../html/_modules/MDAnalysis/core/accessors.html' -removed '../html/_modules/MDAnalysis/core/groups.html' -removed '../html/_modules/MDAnalysis/core/selection.html' -removed '../html/_modules/MDAnalysis/core/topology.html' -removed '../html/_modules/MDAnalysis/core/topologyattrs.html' -removed '../html/_modules/MDAnalysis/core/topologyobjects.html' -removed '../html/_modules/MDAnalysis/core/universe.html' -removed directory '../html/_modules/MDAnalysis/core' -removed '../html/_modules/MDAnalysis/exceptions.html' -removed '../html/_modules/MDAnalysis/fetch/fetchers.html' -removed '../html/_modules/MDAnalysis/fetch/pdb.html' -removed directory '../html/_modules/MDAnalysis/fetch' -removed '../html/_modules/MDAnalysis/guesser/base.html' -removed '../html/_modules/MDAnalysis/guesser/default_guesser.html' -removed '../html/_modules/MDAnalysis/guesser/tables.html' -removed directory '../html/_modules/MDAnalysis/guesser' -removed '../html/_modules/MDAnalysis/lib/NeighborSearch.html' -removed '../html/_modules/MDAnalysis/lib/correlations.html' -removed '../html/_modules/MDAnalysis/lib/distances.html' -removed '../html/_modules/MDAnalysis/lib/log.html' -removed '../html/_modules/MDAnalysis/lib/mdamath.html' -removed '../html/_modules/MDAnalysis/lib/picklable_file_io.html' -removed '../html/_modules/MDAnalysis/lib/pkdtree.html' -removed '../html/_modules/MDAnalysis/lib/transformations.html' -removed '../html/_modules/MDAnalysis/lib/util.html' -removed directory '../html/_modules/MDAnalysis/lib' -removed '../html/_modules/MDAnalysis/selections.html' -removed '../html/_modules/MDAnalysis/selections/base.html' -removed '../html/_modules/MDAnalysis/selections/charmm.html' -removed '../html/_modules/MDAnalysis/selections/gromacs.html' -removed '../html/_modules/MDAnalysis/selections/jmol.html' -removed '../html/_modules/MDAnalysis/selections/pymol.html' -removed '../html/_modules/MDAnalysis/selections/vmd.html' -removed directory '../html/_modules/MDAnalysis/selections' -removed '../html/_modules/MDAnalysis/topology/CRDParser.html' -removed '../html/_modules/MDAnalysis/topology/DLPolyParser.html' -removed '../html/_modules/MDAnalysis/topology/DMSParser.html' -removed '../html/_modules/MDAnalysis/topology/ExtendedPDBParser.html' -removed '../html/_modules/MDAnalysis/topology/FHIAIMSParser.html' -removed '../html/_modules/MDAnalysis/topology/GMSParser.html' -removed '../html/_modules/MDAnalysis/topology/GROParser.html' -removed '../html/_modules/MDAnalysis/topology/GSDParser.html' -removed '../html/_modules/MDAnalysis/topology/HoomdXMLParser.html' -removed '../html/_modules/MDAnalysis/topology/ITPParser.html' -removed '../html/_modules/MDAnalysis/topology/LAMMPSParser.html' -removed '../html/_modules/MDAnalysis/topology/MMTFParser.html' -removed '../html/_modules/MDAnalysis/topology/MOL2Parser.html' -removed '../html/_modules/MDAnalysis/topology/MinimalParser.html' -removed '../html/_modules/MDAnalysis/topology/PDBParser.html' -removed '../html/_modules/MDAnalysis/topology/PDBQTParser.html' -removed '../html/_modules/MDAnalysis/topology/PQRParser.html' -removed '../html/_modules/MDAnalysis/topology/PSFParser.html' -removed '../html/_modules/MDAnalysis/topology/TOPParser.html' -removed '../html/_modules/MDAnalysis/topology/TPRParser.html' -removed '../html/_modules/MDAnalysis/topology/TXYZParser.html' -removed '../html/_modules/MDAnalysis/topology/XYZParser.html' -removed '../html/_modules/MDAnalysis/topology/base.html' -removed '../html/_modules/MDAnalysis/topology/tpr/obj.html' -removed '../html/_modules/MDAnalysis/topology/tpr/utils.html' -removed directory '../html/_modules/MDAnalysis/topology/tpr' -removed directory '../html/_modules/MDAnalysis/topology' -removed '../html/_modules/MDAnalysis/transformations/base.html' -removed '../html/_modules/MDAnalysis/transformations/boxdimensions.html' -removed '../html/_modules/MDAnalysis/transformations/fit.html' -removed '../html/_modules/MDAnalysis/transformations/nojump.html' -removed '../html/_modules/MDAnalysis/transformations/positionaveraging.html' -removed '../html/_modules/MDAnalysis/transformations/rotate.html' -removed '../html/_modules/MDAnalysis/transformations/translate.html' -removed '../html/_modules/MDAnalysis/transformations/wrap.html' -removed directory '../html/_modules/MDAnalysis/transformations' -removed '../html/_modules/MDAnalysis/units.html' -removed '../html/_modules/MDAnalysis/visualization/streamlines.html' -removed '../html/_modules/MDAnalysis/visualization/streamlines_3D.html' -removed directory '../html/_modules/MDAnalysis/visualization' -removed directory '../html/_modules/MDAnalysis' -removed '../html/_modules/index.html' -removed directory '../html/_modules' -removed '../html/objects.inv' -removed '../html/py-modindex.html' -removed '../html/search.html' -removed '../html/searchindex.js' -removed '../html/sitemap.xml' -removed '../html/_sources/documentation_pages/analysis/align.rst.txt' -removed '../html/_sources/documentation_pages/analysis/atomicdistances.rst.txt' -removed '../html/_sources/documentation_pages/analysis/backends.rst.txt' -removed '../html/_sources/documentation_pages/analysis/base.rst.txt' -removed '../html/_sources/documentation_pages/analysis/bat.rst.txt' -removed '../html/_sources/documentation_pages/analysis/contacts.rst.txt' -removed '../html/_sources/documentation_pages/analysis/data.rst.txt' -removed '../html/_sources/documentation_pages/analysis/density.rst.txt' -removed '../html/_sources/documentation_pages/analysis/dielectric.rst.txt' -removed '../html/_sources/documentation_pages/analysis/diffusionmap.rst.txt' -removed '../html/_sources/documentation_pages/analysis/dihedrals.rst.txt' -removed '../html/_sources/documentation_pages/analysis/distances.rst.txt' -removed '../html/_sources/documentation_pages/analysis/dssp.rst.txt' -removed '../html/_sources/documentation_pages/analysis/encore.rst.txt' -removed '../html/_sources/documentation_pages/analysis/encore/bootstrap.rst.txt' -removed '../html/_sources/documentation_pages/analysis/encore/clustering.rst.txt' -removed '../html/_sources/documentation_pages/analysis/encore/confdistmatrix.rst.txt' -removed '../html/_sources/documentation_pages/analysis/encore/covariance.rst.txt' -removed '../html/_sources/documentation_pages/analysis/encore/dimensionality_reduction.rst.txt' -removed '../html/_sources/documentation_pages/analysis/encore/similarity.rst.txt' -removed '../html/_sources/documentation_pages/analysis/encore/utils.rst.txt' -removed directory '../html/_sources/documentation_pages/analysis/encore' -removed '../html/_sources/documentation_pages/analysis/gnm.rst.txt' -removed '../html/_sources/documentation_pages/analysis/hbond_autocorrel.rst.txt' -removed '../html/_sources/documentation_pages/analysis/hbond_autocorrel_deprecated.rst.txt' -removed '../html/_sources/documentation_pages/analysis/helix_analysis.rst.txt' -removed '../html/_sources/documentation_pages/analysis/hole2.rst.txt' -removed '../html/_sources/documentation_pages/analysis/hydrogenbonds.rst.txt' -removed '../html/_sources/documentation_pages/analysis/leaflet.rst.txt' -removed '../html/_sources/documentation_pages/analysis/legacy/x3dna.rst.txt' -removed directory '../html/_sources/documentation_pages/analysis/legacy' -removed '../html/_sources/documentation_pages/analysis/legacy_modules.rst.txt' -removed '../html/_sources/documentation_pages/analysis/lineardensity.rst.txt' -removed '../html/_sources/documentation_pages/analysis/msd.rst.txt' -removed '../html/_sources/documentation_pages/analysis/nucleicacids.rst.txt' -removed '../html/_sources/documentation_pages/analysis/nuclinfo.rst.txt' -removed '../html/_sources/documentation_pages/analysis/parallelization.rst.txt' -removed '../html/_sources/documentation_pages/analysis/pca.rst.txt' -removed '../html/_sources/documentation_pages/analysis/polymer.rst.txt' -removed '../html/_sources/documentation_pages/analysis/psa.rst.txt' -removed '../html/_sources/documentation_pages/analysis/rdf.rst.txt' -removed '../html/_sources/documentation_pages/analysis/results.rst.txt' -removed '../html/_sources/documentation_pages/analysis/rms.rst.txt' -removed '../html/_sources/documentation_pages/analysis/waterdynamics.rst.txt' -removed '../html/_sources/documentation_pages/analysis/wbridge_analysis.rst.txt' -removed directory '../html/_sources/documentation_pages/analysis' -removed '../html/_sources/documentation_pages/analysis_modules.rst.txt' -removed '../html/_sources/documentation_pages/auxiliary/EDR.rst.txt' -removed '../html/_sources/documentation_pages/auxiliary/XVG.rst.txt' -removed '../html/_sources/documentation_pages/auxiliary/base.rst.txt' -removed '../html/_sources/documentation_pages/auxiliary/core.rst.txt' -removed '../html/_sources/documentation_pages/auxiliary/init.rst.txt' -removed directory '../html/_sources/documentation_pages/auxiliary' -removed '../html/_sources/documentation_pages/auxiliary_modules.rst.txt' -removed '../html/_sources/documentation_pages/converters.rst.txt' -removed '../html/_sources/documentation_pages/converters/OpenMM.rst.txt' -removed '../html/_sources/documentation_pages/converters/ParmEd.rst.txt' -removed '../html/_sources/documentation_pages/converters/RDKit.rst.txt' -removed '../html/_sources/documentation_pages/converters/base.rst.txt' -removed directory '../html/_sources/documentation_pages/converters' -removed '../html/_sources/documentation_pages/coordinates/CRD.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/DCD.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/DLPoly.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/DMS.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/FHIAIMS.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/GMS.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/GRO.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/GSD.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/H5MD.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/IMD.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/INPCRD.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/LAMMPS.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/MMTF.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/MOL2.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/NAMDBIN.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/PDB.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/PDBQT.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/PQR.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/TNG.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/TPR.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/TRC.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/TRJ.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/TRR.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/TRZ.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/TXYZ.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/XDR.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/XTC.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/XYZ.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/base.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/chain.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/chemfiles.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/core.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/init.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/memory.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/null.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/pickle_readers.rst.txt' -removed '../html/_sources/documentation_pages/coordinates/timestep.rst.txt' -removed directory '../html/_sources/documentation_pages/coordinates' -removed '../html/_sources/documentation_pages/coordinates_modules.rst.txt' -removed '../html/_sources/documentation_pages/core/accessors.rst.txt' -removed '../html/_sources/documentation_pages/core/groups.rst.txt' -removed '../html/_sources/documentation_pages/core/init.rst.txt' -removed '../html/_sources/documentation_pages/core/selection.rst.txt' -removed '../html/_sources/documentation_pages/core/topology.rst.txt' -removed '../html/_sources/documentation_pages/core/topologyattrs.rst.txt' -removed '../html/_sources/documentation_pages/core/topologyobjects.rst.txt' -removed '../html/_sources/documentation_pages/core/universe.rst.txt' -removed directory '../html/_sources/documentation_pages/core' -removed '../html/_sources/documentation_pages/core_modules.rst.txt' -removed '../html/_sources/documentation_pages/exceptions.rst.txt' -removed '../html/_sources/documentation_pages/fetchers/PDB.rst.txt' -removed '../html/_sources/documentation_pages/fetchers/fetchers.rst.txt' -removed '../html/_sources/documentation_pages/fetchers/init.rst.txt' -removed directory '../html/_sources/documentation_pages/fetchers' -removed '../html/_sources/documentation_pages/fetchers_modules.rst.txt' -removed '../html/_sources/documentation_pages/guesser_modules.rst.txt' -removed '../html/_sources/documentation_pages/guesser_modules/base.rst.txt' -removed '../html/_sources/documentation_pages/guesser_modules/default_guesser.rst.txt' -removed '../html/_sources/documentation_pages/guesser_modules/init.rst.txt' -removed '../html/_sources/documentation_pages/guesser_modules/tables.rst.txt' -removed directory '../html/_sources/documentation_pages/guesser_modules' -removed '../html/_sources/documentation_pages/lib/NeighborSearch.rst.txt' -removed '../html/_sources/documentation_pages/lib/c_distances.rst.txt' -removed '../html/_sources/documentation_pages/lib/c_distances_openmp.rst.txt' -removed '../html/_sources/documentation_pages/lib/correlations.rst.txt' -removed '../html/_sources/documentation_pages/lib/distances.rst.txt' -removed '../html/_sources/documentation_pages/lib/formats/libdcd.rst.txt' -removed '../html/_sources/documentation_pages/lib/formats/libmdaxdr.rst.txt' -removed directory '../html/_sources/documentation_pages/lib/formats' -removed '../html/_sources/documentation_pages/lib/log.rst.txt' -removed '../html/_sources/documentation_pages/lib/mdamath.rst.txt' -removed '../html/_sources/documentation_pages/lib/nsgrid.rst.txt' -removed '../html/_sources/documentation_pages/lib/picklable_file_io.rst.txt' -removed '../html/_sources/documentation_pages/lib/pkdtree.rst.txt' -removed '../html/_sources/documentation_pages/lib/qcprot.rst.txt' -removed '../html/_sources/documentation_pages/lib/transformations.rst.txt' -removed '../html/_sources/documentation_pages/lib/util.rst.txt' -removed directory '../html/_sources/documentation_pages/lib' -removed '../html/_sources/documentation_pages/lib_modules.rst.txt' -removed '../html/_sources/documentation_pages/overview.rst.txt' -removed '../html/_sources/documentation_pages/references.rst.txt' -removed '../html/_sources/documentation_pages/selections.rst.txt' -removed '../html/_sources/documentation_pages/selections/base.rst.txt' -removed '../html/_sources/documentation_pages/selections/charmm.rst.txt' -removed '../html/_sources/documentation_pages/selections/gromacs.rst.txt' -removed '../html/_sources/documentation_pages/selections/jmol.rst.txt' -removed '../html/_sources/documentation_pages/selections/pymol.rst.txt' -removed '../html/_sources/documentation_pages/selections/vmd.rst.txt' -removed directory '../html/_sources/documentation_pages/selections' -removed '../html/_sources/documentation_pages/selections_modules.rst.txt' -removed '../html/_sources/documentation_pages/topology.rst.txt' -removed '../html/_sources/documentation_pages/topology/CRDParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/DLPolyParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/DMSParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/ExtendedPDBParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/FHIAIMSParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/GMSParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/GROParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/GSDParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/HoomdXMLParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/ITPParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/LAMMPSParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/MMTFParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/MOL2Parser.rst.txt' -removed '../html/_sources/documentation_pages/topology/MinimalParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/PDBParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/PDBQTParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/PQRParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/PSFParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/TOPParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/TPRParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/TXYZParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/XYZParser.rst.txt' -removed '../html/_sources/documentation_pages/topology/base.rst.txt' -removed '../html/_sources/documentation_pages/topology/core.rst.txt' -removed '../html/_sources/documentation_pages/topology/guessers.rst.txt' -removed '../html/_sources/documentation_pages/topology/init.rst.txt' -removed '../html/_sources/documentation_pages/topology/tables.rst.txt' -removed '../html/_sources/documentation_pages/topology/tpr_util.rst.txt' -removed directory '../html/_sources/documentation_pages/topology' -removed '../html/_sources/documentation_pages/topology_modules.rst.txt' -removed '../html/_sources/documentation_pages/trajectory_transformations.rst.txt' -removed '../html/_sources/documentation_pages/transformations/base.rst.txt' -removed '../html/_sources/documentation_pages/transformations/boxdimensions.rst.txt' -removed '../html/_sources/documentation_pages/transformations/fit.rst.txt' -removed '../html/_sources/documentation_pages/transformations/nojump.rst.txt' -removed '../html/_sources/documentation_pages/transformations/positionaveraging.rst.txt' -removed '../html/_sources/documentation_pages/transformations/rotate.rst.txt' -removed '../html/_sources/documentation_pages/transformations/translate.rst.txt' -removed '../html/_sources/documentation_pages/transformations/wrap.rst.txt' -removed directory '../html/_sources/documentation_pages/transformations' -removed '../html/_sources/documentation_pages/units.rst.txt' -removed '../html/_sources/documentation_pages/version.rst.txt' -removed '../html/_sources/documentation_pages/visualization/streamlines.rst.txt' -removed '../html/_sources/documentation_pages/visualization/streamlines_3D.rst.txt' -removed directory '../html/_sources/documentation_pages/visualization' -removed '../html/_sources/documentation_pages/visualization_modules.rst.txt' -removed directory '../html/_sources/documentation_pages' -removed '../html/_sources/index.rst.txt' -removed directory '../html/_sources' -removed '../html/_static/jquery.js' -removed '../html/_static/_sphinx_javascript_frameworks_compat.js' -removed '../html/_static/pygments.css' -removed '../html/_static/basic.css' -removed '../html/_static/doctools.js' -removed '../html/_static/documentation_options.js' -removed '../html/_static/file.png' -removed '../html/_static/language_data.js' -removed '../html/_static/minus.png' -removed '../html/_static/plus.png' -removed '../html/_static/searchtools.js' -removed '../html/_static/sphinx_highlight.js' -removed '../html/_static/css/fonts/Roboto-Slab-Bold.woff' -removed '../html/_static/css/fonts/Roboto-Slab-Bold.woff2' -removed '../html/_static/css/fonts/Roboto-Slab-Regular.woff' -removed '../html/_static/css/fonts/Roboto-Slab-Regular.woff2' -removed '../html/_static/css/fonts/fontawesome-webfont.eot' -removed '../html/_static/css/fonts/fontawesome-webfont.svg' -removed '../html/_static/css/fonts/fontawesome-webfont.ttf' -removed '../html/_static/css/fonts/fontawesome-webfont.woff' -removed '../html/_static/css/fonts/fontawesome-webfont.woff2' -removed '../html/_static/css/fonts/lato-bold-italic.woff' -removed '../html/_static/css/fonts/lato-bold-italic.woff2' -removed '../html/_static/css/fonts/lato-bold.woff' -removed '../html/_static/css/fonts/lato-bold.woff2' -removed '../html/_static/css/fonts/lato-normal-italic.woff' -removed '../html/_static/css/fonts/lato-normal-italic.woff2' -removed '../html/_static/css/fonts/lato-normal.woff' -removed '../html/_static/css/fonts/lato-normal.woff2' -removed directory '../html/_static/css/fonts' -removed '../html/_static/css/badge_only.css' -removed '../html/_static/css/theme.css' -removed directory '../html/_static/css' -removed '../html/_static/fonts/Lato/lato-bold.eot' -removed '../html/_static/fonts/Lato/lato-bold.ttf' -removed '../html/_static/fonts/Lato/lato-bold.woff' -removed '../html/_static/fonts/Lato/lato-bold.woff2' -removed '../html/_static/fonts/Lato/lato-bolditalic.eot' -removed '../html/_static/fonts/Lato/lato-bolditalic.ttf' -removed '../html/_static/fonts/Lato/lato-bolditalic.woff' -removed '../html/_static/fonts/Lato/lato-bolditalic.woff2' -removed '../html/_static/fonts/Lato/lato-italic.eot' -removed '../html/_static/fonts/Lato/lato-italic.ttf' -removed '../html/_static/fonts/Lato/lato-italic.woff' -removed '../html/_static/fonts/Lato/lato-italic.woff2' -removed '../html/_static/fonts/Lato/lato-regular.eot' -removed '../html/_static/fonts/Lato/lato-regular.ttf' -removed '../html/_static/fonts/Lato/lato-regular.woff' -removed '../html/_static/fonts/Lato/lato-regular.woff2' -removed directory '../html/_static/fonts/Lato' -removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot' -removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf' -removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff' -removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2' -removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot' -removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf' -removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff' -removed '../html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2' -removed directory '../html/_static/fonts/RobotoSlab' -removed directory '../html/_static/fonts' -removed '../html/_static/js/badge_only.js' -removed '../html/_static/js/theme.js' -removed '../html/_static/js/versions.js' -removed directory '../html/_static/js' -removed '../html/_static/logo/mda_favicon.ico' -removed '../html/_static/logo/mda_logo.png' -removed '../html/_static/logo/placeholder_favicon.svg' -removed '../html/_static/logo/placeholder_logo.png' -removed directory '../html/_static/logo' -removed '../html/_static/opensearch.xml' -removed '../html/_static/site.css' -removed directory '../html/_static' -sphinx-build -v -W -b html source ../html -Running Sphinx v8.2.3 -loading translations [en]... locale_dir /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/sphinx/source/locales/en/LC_MESSAGES does not exist -locale_dir /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/sphinx/source/locales/en/LC_MESSAGES does not exist -done -Converting `source_suffix = '.rst'` to `source_suffix = {'.rst': 'restructuredtext'}`. -loading pickled environment... The configuration has changed (2 options: 'html_permalinks_icon', 'jquery_use_sri') -done -checking bibtex cache... up to date -locale_dir /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/sphinx/source/locales/en/LC_MESSAGES does not exist -building [mo]: targets for 0 po files that are out of date -writing output... -building [html]: targets for 183 source files that are out of date -updating environment: locale_dir /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/sphinx/source/locales/en/LC_MESSAGES does not exist -0 added, 1 changed, 0 removed -reading sources... [100%] documentation_pages/fetchers/fetchers - -looking for now-outdated files... none found -pickling environment... done -checking consistency... done -preparing documents... done -copying assets... -copying static files... -Writing evaluated template result to /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/_static/basic.css -Writing evaluated template result to /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/_static/documentation_options.js -Writing evaluated template result to /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/_static/language_data.js -Writing evaluated template result to /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/_static/js/versions.js -copying static files: done -copying extra files... -copying extra files: done -copying assets: done -writing output... [ 1%] documentation_pages/analysis/align -writing output... [ 1%] documentation_pages/analysis/atomicdistances -writing output... [ 2%] documentation_pages/analysis/backends -writing output... [ 2%] documentation_pages/analysis/base -writing output... [ 3%] documentation_pages/analysis/bat -writing output... [ 3%] documentation_pages/analysis/contacts -writing output... [ 4%] documentation_pages/analysis/data -writing output... [ 4%] documentation_pages/analysis/density -writing output... [ 5%] documentation_pages/analysis/dielectric -writing output... [ 5%] documentation_pages/analysis/diffusionmap -writing output... [ 6%] documentation_pages/analysis/dihedrals -writing output... [ 7%] documentation_pages/analysis/distances -writing output... [ 7%] documentation_pages/analysis/dssp -writing output... [ 8%] documentation_pages/analysis/encore -writing output... [ 8%] documentation_pages/analysis/encore/bootstrap -writing output... [ 9%] documentation_pages/analysis/encore/clustering -writing output... [ 9%] documentation_pages/analysis/encore/confdistmatrix -writing output... [ 10%] documentation_pages/analysis/encore/covariance -writing output... [ 10%] documentation_pages/analysis/encore/dimensionality_reduction -writing output... [ 11%] documentation_pages/analysis/encore/similarity -writing output... [ 11%] documentation_pages/analysis/encore/utils -writing output... [ 12%] documentation_pages/analysis/gnm -writing output... [ 13%] documentation_pages/analysis/hbond_autocorrel -writing output... [ 13%] documentation_pages/analysis/hbond_autocorrel_deprecated -writing output... [ 14%] documentation_pages/analysis/helix_analysis -writing output... [ 14%] documentation_pages/analysis/hole2 -writing output... [ 15%] documentation_pages/analysis/hydrogenbonds -writing output... [ 15%] documentation_pages/analysis/leaflet -writing output... [ 16%] documentation_pages/analysis/legacy/x3dna -writing output... [ 16%] documentation_pages/analysis/legacy_modules -writing output... [ 17%] documentation_pages/analysis/lineardensity -writing output... [ 17%] documentation_pages/analysis/msd -writing output... [ 18%] documentation_pages/analysis/nucleicacids -writing output... [ 19%] documentation_pages/analysis/nuclinfo -writing output... [ 19%] documentation_pages/analysis/parallelization -writing output... [ 20%] documentation_pages/analysis/pca -writing output... [ 20%] documentation_pages/analysis/polymer -writing output... [ 21%] documentation_pages/analysis/psa -writing output... [ 21%] documentation_pages/analysis/rdf -writing output... [ 22%] documentation_pages/analysis/results -writing output... [ 22%] documentation_pages/analysis/rms -writing output... [ 23%] documentation_pages/analysis/waterdynamics -writing output... [ 23%] documentation_pages/analysis/wbridge_analysis -writing output... [ 24%] documentation_pages/analysis_modules -writing output... [ 25%] documentation_pages/auxiliary/EDR -writing output... [ 25%] documentation_pages/auxiliary/XVG -writing output... [ 26%] documentation_pages/auxiliary/base -writing output... [ 26%] documentation_pages/auxiliary/core -writing output... [ 27%] documentation_pages/auxiliary/init -writing output... [ 27%] documentation_pages/auxiliary_modules -writing output... [ 28%] documentation_pages/converters -writing output... [ 28%] documentation_pages/converters/OpenMM -writing output... [ 29%] documentation_pages/converters/ParmEd -writing output... [ 30%] documentation_pages/converters/RDKit -writing output... [ 30%] documentation_pages/converters/base -writing output... [ 31%] documentation_pages/coordinates/CRD -writing output... [ 31%] documentation_pages/coordinates/DCD -writing output... [ 32%] documentation_pages/coordinates/DLPoly -writing output... [ 32%] documentation_pages/coordinates/DMS -writing output... [ 33%] documentation_pages/coordinates/FHIAIMS -writing output... [ 33%] documentation_pages/coordinates/GMS -writing output... [ 34%] documentation_pages/coordinates/GRO -writing output... [ 34%] documentation_pages/coordinates/GSD -writing output... [ 35%] documentation_pages/coordinates/H5MD -writing output... [ 36%] documentation_pages/coordinates/IMD -writing output... [ 36%] documentation_pages/coordinates/INPCRD -writing output... [ 37%] documentation_pages/coordinates/LAMMPS -writing output... [ 37%] documentation_pages/coordinates/MMTF -writing output... [ 38%] documentation_pages/coordinates/MOL2 -writing output... [ 38%] documentation_pages/coordinates/NAMDBIN -writing output... [ 39%] documentation_pages/coordinates/PDB -writing output... [ 39%] documentation_pages/coordinates/PDBQT -writing output... [ 40%] documentation_pages/coordinates/PQR -writing output... [ 40%] documentation_pages/coordinates/TNG -writing output... [ 41%] documentation_pages/coordinates/TPR -writing output... [ 42%] documentation_pages/coordinates/TRC -writing output... [ 42%] documentation_pages/coordinates/TRJ -writing output... [ 43%] documentation_pages/coordinates/TRR -writing output... [ 43%] documentation_pages/coordinates/TRZ -writing output... [ 44%] documentation_pages/coordinates/TXYZ -writing output... [ 44%] documentation_pages/coordinates/XDR -writing output... [ 45%] documentation_pages/coordinates/XTC -writing output... [ 45%] documentation_pages/coordinates/XYZ -writing output... [ 46%] documentation_pages/coordinates/base -writing output... [ 46%] documentation_pages/coordinates/chain -writing output... [ 47%] documentation_pages/coordinates/chemfiles -writing output... [ 48%] documentation_pages/coordinates/core -writing output... [ 48%] documentation_pages/coordinates/init -writing output... [ 49%] documentation_pages/coordinates/memory -writing output... [ 49%] documentation_pages/coordinates/null -writing output... [ 50%] documentation_pages/coordinates/pickle_readers -writing output... [ 50%] documentation_pages/coordinates/timestep -writing output... [ 51%] documentation_pages/coordinates_modules -writing output... [ 51%] documentation_pages/core/accessors -writing output... [ 52%] documentation_pages/core/groups -writing output... [ 52%] documentation_pages/core/init -writing output... [ 53%] documentation_pages/core/selection -writing output... [ 54%] documentation_pages/core/topology -writing output... [ 54%] documentation_pages/core/topologyattrs -writing output... [ 55%] documentation_pages/core/topologyobjects -writing output... [ 55%] documentation_pages/core/universe -writing output... [ 56%] documentation_pages/core_modules -writing output... [ 56%] documentation_pages/exceptions -writing output... [ 57%] documentation_pages/fetchers/PDB -writing output... [ 57%] documentation_pages/fetchers/fetchers -writing output... [ 58%] documentation_pages/fetchers/init -writing output... [ 58%] documentation_pages/fetchers_modules -writing output... [ 59%] documentation_pages/guesser_modules -writing output... [ 60%] documentation_pages/guesser_modules/base -writing output... [ 60%] documentation_pages/guesser_modules/default_guesser -writing output... [ 61%] documentation_pages/guesser_modules/init -writing output... [ 61%] documentation_pages/guesser_modules/tables -writing output... [ 62%] documentation_pages/lib/NeighborSearch -writing output... [ 62%] documentation_pages/lib/c_distances -writing output... [ 63%] documentation_pages/lib/c_distances_openmp -writing output... [ 63%] documentation_pages/lib/correlations -writing output... [ 64%] documentation_pages/lib/distances -writing output... [ 64%] documentation_pages/lib/formats/libdcd -writing output... [ 65%] documentation_pages/lib/formats/libmdaxdr -writing output... [ 66%] documentation_pages/lib/log -writing output... [ 66%] documentation_pages/lib/mdamath -writing output... [ 67%] documentation_pages/lib/nsgrid -writing output... [ 67%] documentation_pages/lib/picklable_file_io -writing output... [ 68%] documentation_pages/lib/pkdtree -writing output... [ 68%] documentation_pages/lib/qcprot -writing output... [ 69%] documentation_pages/lib/transformations -writing output... [ 69%] documentation_pages/lib/util -writing output... [ 70%] documentation_pages/lib_modules -writing output... [ 70%] documentation_pages/overview -writing output... [ 71%] documentation_pages/references -writing output... [ 72%] documentation_pages/selections -writing output... [ 72%] documentation_pages/selections/base -writing output... [ 73%] documentation_pages/selections/charmm -writing output... [ 73%] documentation_pages/selections/gromacs -writing output... [ 74%] documentation_pages/selections/jmol -writing output... [ 74%] documentation_pages/selections/pymol -writing output... [ 75%] documentation_pages/selections/vmd -writing output... [ 75%] documentation_pages/selections_modules -writing output... [ 76%] documentation_pages/topology -writing output... [ 77%] documentation_pages/topology/CRDParser -writing output... [ 77%] documentation_pages/topology/DLPolyParser -writing output... [ 78%] documentation_pages/topology/DMSParser -writing output... [ 78%] documentation_pages/topology/ExtendedPDBParser -writing output... [ 79%] documentation_pages/topology/FHIAIMSParser -writing output... [ 79%] documentation_pages/topology/GMSParser -writing output... [ 80%] documentation_pages/topology/GROParser -writing output... [ 80%] documentation_pages/topology/GSDParser -writing output... [ 81%] documentation_pages/topology/HoomdXMLParser -writing output... [ 81%] documentation_pages/topology/ITPParser -writing output... [ 82%] documentation_pages/topology/LAMMPSParser -writing output... [ 83%] documentation_pages/topology/MMTFParser -writing output... [ 83%] documentation_pages/topology/MOL2Parser -writing output... [ 84%] documentation_pages/topology/MinimalParser -writing output... [ 84%] documentation_pages/topology/PDBParser -writing output... [ 85%] documentation_pages/topology/PDBQTParser -writing output... [ 85%] documentation_pages/topology/PQRParser -writing output... [ 86%] documentation_pages/topology/PSFParser -writing output... [ 86%] documentation_pages/topology/TOPParser -writing output... [ 87%] documentation_pages/topology/TPRParser -writing output... [ 87%] documentation_pages/topology/TXYZParser -writing output... [ 88%] documentation_pages/topology/XYZParser -writing output... [ 89%] documentation_pages/topology/base -writing output... [ 89%] documentation_pages/topology/core -writing output... [ 90%] documentation_pages/topology/guessers -writing output... [ 90%] documentation_pages/topology/init -writing output... [ 91%] documentation_pages/topology/tables -writing output... [ 91%] documentation_pages/topology/tpr_util -writing output... [ 92%] documentation_pages/topology_modules -writing output... [ 92%] documentation_pages/trajectory_transformations -writing output... [ 93%] documentation_pages/transformations/base -writing output... [ 93%] documentation_pages/transformations/boxdimensions -writing output... [ 94%] documentation_pages/transformations/fit -writing output... [ 95%] documentation_pages/transformations/nojump -writing output... [ 95%] documentation_pages/transformations/positionaveraging -writing output... [ 96%] documentation_pages/transformations/rotate -writing output... [ 96%] documentation_pages/transformations/translate -writing output... [ 97%] documentation_pages/transformations/wrap -writing output... [ 97%] documentation_pages/units -writing output... [ 98%] documentation_pages/version -writing output... [ 98%] documentation_pages/visualization/streamlines -writing output... [ 99%] documentation_pages/visualization/streamlines_3D -writing output... [ 99%] documentation_pages/visualization_modules -writing output... [100%] index - -/nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/dssp/dssp.py:docstring of MDAnalysis.analysis.dssp.dssp.DSSP:54: WARNING: undefined label: 'selection-of-acceleration-backend' [ref.ref] -/nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/converters/RDKitInferring.py:docstring of MDAnalysis.converters.RDKitInferring.RDKitInferrer:1: WARNING: undefined label: 'https://github.com/jensengroup/xyz2mol' [ref.ref] -generating indices... genindex py-modindex done -highlighting module code... [ 1%] MDAnalysis.analysis.align -highlighting module code... [ 1%] MDAnalysis.analysis.atomicdistances -highlighting module code... [ 2%] MDAnalysis.analysis.backends -highlighting module code... [ 2%] MDAnalysis.analysis.base -highlighting module code... [ 3%] MDAnalysis.analysis.bat -highlighting module code... [ 4%] MDAnalysis.analysis.contacts -highlighting module code... [ 4%] MDAnalysis.analysis.density -highlighting module code... [ 5%] MDAnalysis.analysis.dielectric -highlighting module code... [ 6%] MDAnalysis.analysis.diffusionmap -highlighting module code... [ 6%] MDAnalysis.analysis.dihedrals -highlighting module code... [ 7%] MDAnalysis.analysis.distances -highlighting module code... [ 7%] MDAnalysis.analysis.dssp.dssp -highlighting module code... [ 8%] MDAnalysis.analysis.dssp.pydssp_numpy -highlighting module code... [ 9%] MDAnalysis.analysis.encore.bootstrap -/nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/MDAnalysis/analysis/encore/__init__.py:45: DeprecationWarning: Deprecation in version 2.8.0 -MDAnalysis.analysis.encore is deprecated in favour of the MDAKit mdaencore (https://www.mdanalysis.org/mdaencore/) and will be removed in MDAnalysis version 3.0.0. - warnings.warn(wmsg, category=DeprecationWarning) -highlighting module code... [ 9%] MDAnalysis.analysis.encore.clustering.ClusterCollection -highlighting module code... [ 10%] MDAnalysis.analysis.encore.clustering.ClusteringMethod -highlighting module code... [ 10%] MDAnalysis.analysis.encore.clustering.affinityprop -highlighting module code... [ 11%] MDAnalysis.analysis.encore.clustering.cluster -highlighting module code... [ 12%] MDAnalysis.analysis.encore.confdistmatrix -highlighting module code... [ 12%] MDAnalysis.analysis.encore.covariance -highlighting module code... [ 13%] MDAnalysis.analysis.encore.cutils -highlighting module code... [ 14%] MDAnalysis.analysis.encore.dimensionality_reduction.DimensionalityReductionMethod -highlighting module code... [ 14%] MDAnalysis.analysis.encore.dimensionality_reduction.reduce_dimensionality -highlighting module code... [ 15%] MDAnalysis.analysis.encore.dimensionality_reduction.stochasticproxembed -highlighting module code... [ 15%] MDAnalysis.analysis.encore.similarity -highlighting module code... [ 16%] MDAnalysis.analysis.encore.utils -highlighting module code... [ 17%] MDAnalysis.analysis.gnm -highlighting module code... [ 17%] MDAnalysis.analysis.helix_analysis -highlighting module code... [ 18%] MDAnalysis.analysis.hydrogenbonds.hbond_analysis -highlighting module code... [ 19%] MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel -highlighting module code... [ 19%] MDAnalysis.analysis.hydrogenbonds.wbridge_analysis -highlighting module code... [ 20%] MDAnalysis.analysis.leaflet -highlighting module code... [ 20%] MDAnalysis.analysis.legacy.x3dna -highlighting module code... [ 21%] MDAnalysis.analysis.lineardensity -highlighting module code... [ 22%] MDAnalysis.analysis.msd -highlighting module code... [ 22%] MDAnalysis.analysis.nucleicacids -highlighting module code... [ 23%] MDAnalysis.analysis.nuclinfo -highlighting module code... [ 23%] MDAnalysis.analysis.pca -highlighting module code... [ 24%] MDAnalysis.analysis.polymer -highlighting module code... [ 25%] MDAnalysis.analysis.rdf -highlighting module code... [ 25%] MDAnalysis.analysis.results -highlighting module code... [ 26%] MDAnalysis.analysis.rms -highlighting module code... [ 27%] MDAnalysis.auxiliary.EDR -highlighting module code... [ 27%] MDAnalysis.auxiliary.XVG -highlighting module code... [ 28%] MDAnalysis.auxiliary.base -highlighting module code... [ 28%] MDAnalysis.auxiliary.core -highlighting module code... [ 29%] MDAnalysis.converters.OpenMM -highlighting module code... [ 30%] MDAnalysis.converters.OpenMMParser -highlighting module code... [ 30%] MDAnalysis.converters.ParmEd -highlighting module code... [ 31%] MDAnalysis.converters.ParmEdParser -highlighting module code... [ 31%] MDAnalysis.converters.RDKit -highlighting module code... [ 32%] MDAnalysis.converters.RDKitInferring -highlighting module code... [ 33%] MDAnalysis.converters.RDKitParser -highlighting module code... [ 33%] MDAnalysis.converters.base -highlighting module code... [ 34%] MDAnalysis.coordinates.CRD -highlighting module code... [ 35%] MDAnalysis.coordinates.DCD -highlighting module code... [ 35%] MDAnalysis.coordinates.DLPoly -highlighting module code... [ 36%] MDAnalysis.coordinates.DMS -highlighting module code... [ 36%] MDAnalysis.coordinates.FHIAIMS -highlighting module code... [ 37%] MDAnalysis.coordinates.GMS -highlighting module code... [ 38%] MDAnalysis.coordinates.GRO -highlighting module code... [ 38%] MDAnalysis.coordinates.GSD -highlighting module code... [ 39%] MDAnalysis.coordinates.H5MD -highlighting module code... [ 40%] MDAnalysis.coordinates.IMD -highlighting module code... [ 40%] MDAnalysis.coordinates.INPCRD -highlighting module code... [ 41%] MDAnalysis.coordinates.LAMMPS -highlighting module code... [ 41%] MDAnalysis.coordinates.MMTF -highlighting module code... [ 42%] MDAnalysis.coordinates.MOL2 -highlighting module code... [ 43%] MDAnalysis.coordinates.NAMDBIN -highlighting module code... [ 43%] MDAnalysis.coordinates.PDB -highlighting module code... [ 44%] MDAnalysis.coordinates.PDBQT -highlighting module code... [ 44%] MDAnalysis.coordinates.PQR -highlighting module code... [ 45%] MDAnalysis.coordinates.TNG -highlighting module code... [ 46%] MDAnalysis.coordinates.TPR -highlighting module code... [ 46%] MDAnalysis.coordinates.TRC -highlighting module code... [ 47%] MDAnalysis.coordinates.TRJ -highlighting module code... [ 48%] MDAnalysis.coordinates.TRR -highlighting module code... [ 48%] MDAnalysis.coordinates.TRZ -highlighting module code... [ 49%] MDAnalysis.coordinates.TXYZ -highlighting module code... [ 49%] MDAnalysis.coordinates.XDR -highlighting module code... [ 50%] MDAnalysis.coordinates.XTC -highlighting module code... [ 51%] MDAnalysis.coordinates.XYZ -highlighting module code... [ 51%] MDAnalysis.coordinates.base -highlighting module code... [ 52%] MDAnalysis.coordinates.chain -highlighting module code... [ 52%] MDAnalysis.coordinates.chemfiles -highlighting module code... [ 53%] MDAnalysis.coordinates.core -highlighting module code... [ 54%] MDAnalysis.coordinates.memory -highlighting module code... [ 54%] MDAnalysis.coordinates.null -highlighting module code... [ 55%] MDAnalysis.coordinates.timestep -highlighting module code... [ 56%] MDAnalysis.core._get_readers -highlighting module code... [ 56%] MDAnalysis.core.accessors -highlighting module code... [ 57%] MDAnalysis.core.groups -highlighting module code... [ 57%] MDAnalysis.core.selection -highlighting module code... [ 58%] MDAnalysis.core.topology -highlighting module code... [ 59%] MDAnalysis.core.topologyattrs -highlighting module code... [ 59%] MDAnalysis.core.topologyobjects -highlighting module code... [ 60%] MDAnalysis.core.universe -highlighting module code... [ 60%] MDAnalysis.exceptions -highlighting module code... [ 61%] MDAnalysis.fetch.fetchers -highlighting module code... [ 62%] MDAnalysis.fetch.pdb -highlighting module code... [ 62%] MDAnalysis.guesser.base -highlighting module code... [ 63%] MDAnalysis.guesser.default_guesser -highlighting module code... [ 64%] MDAnalysis.guesser.tables -highlighting module code... [ 64%] MDAnalysis.lib.NeighborSearch -highlighting module code... [ 65%] MDAnalysis.lib._augment -highlighting module code... [ 65%] MDAnalysis.lib._cutil -highlighting module code... [ 66%] MDAnalysis.lib.correlations -highlighting module code... [ 67%] MDAnalysis.lib.distances -highlighting module code... [ 67%] MDAnalysis.lib.formats.libdcd -highlighting module code... [ 68%] MDAnalysis.lib.formats.libmdaxdr -highlighting module code... [ 69%] MDAnalysis.lib.log -highlighting module code... [ 69%] MDAnalysis.lib.mdamath -highlighting module code... [ 70%] MDAnalysis.lib.nsgrid -highlighting module code... [ 70%] MDAnalysis.lib.picklable_file_io -highlighting module code... [ 71%] MDAnalysis.lib.pkdtree -highlighting module code... [ 72%] MDAnalysis.lib.qcprot -highlighting module code... [ 72%] MDAnalysis.lib.transformations -highlighting module code... [ 73%] MDAnalysis.lib.util -highlighting module code... [ 73%] MDAnalysis.selections -highlighting module code... [ 74%] MDAnalysis.selections.base -highlighting module code... [ 75%] MDAnalysis.selections.charmm -highlighting module code... [ 75%] MDAnalysis.selections.gromacs -highlighting module code... [ 76%] MDAnalysis.selections.jmol -highlighting module code... [ 77%] MDAnalysis.selections.pymol -highlighting module code... [ 77%] MDAnalysis.selections.vmd -highlighting module code... [ 78%] MDAnalysis.topology.CRDParser -highlighting module code... [ 78%] MDAnalysis.topology.DLPolyParser -highlighting module code... [ 79%] MDAnalysis.topology.DMSParser -highlighting module code... [ 80%] MDAnalysis.topology.ExtendedPDBParser -highlighting module code... [ 80%] MDAnalysis.topology.FHIAIMSParser -highlighting module code... [ 81%] MDAnalysis.topology.GMSParser -highlighting module code... [ 81%] MDAnalysis.topology.GROParser -highlighting module code... [ 82%] MDAnalysis.topology.GSDParser -highlighting module code... [ 83%] MDAnalysis.topology.HoomdXMLParser -highlighting module code... [ 83%] MDAnalysis.topology.ITPParser -highlighting module code... [ 84%] MDAnalysis.topology.LAMMPSParser -highlighting module code... [ 85%] MDAnalysis.topology.MMTFParser -highlighting module code... [ 85%] MDAnalysis.topology.MOL2Parser -highlighting module code... [ 86%] MDAnalysis.topology.MinimalParser -highlighting module code... [ 86%] MDAnalysis.topology.PDBParser -highlighting module code... [ 87%] MDAnalysis.topology.PDBQTParser -highlighting module code... [ 88%] MDAnalysis.topology.PQRParser -highlighting module code... [ 88%] MDAnalysis.topology.PSFParser -highlighting module code... [ 89%] MDAnalysis.topology.TOPParser -highlighting module code... [ 90%] MDAnalysis.topology.TPRParser -highlighting module code... [ 90%] MDAnalysis.topology.TXYZParser -highlighting module code... [ 91%] MDAnalysis.topology.XYZParser -highlighting module code... [ 91%] MDAnalysis.topology.base -highlighting module code... [ 92%] MDAnalysis.topology.tpr.obj -highlighting module code... [ 93%] MDAnalysis.topology.tpr.utils -highlighting module code... [ 93%] MDAnalysis.transformations.base -highlighting module code... [ 94%] MDAnalysis.transformations.boxdimensions -highlighting module code... [ 94%] MDAnalysis.transformations.fit -highlighting module code... [ 95%] MDAnalysis.transformations.nojump -highlighting module code... [ 96%] MDAnalysis.transformations.positionaveraging -highlighting module code... [ 96%] MDAnalysis.transformations.rotate -highlighting module code... [ 97%] MDAnalysis.transformations.translate -highlighting module code... [ 98%] MDAnalysis.transformations.wrap -highlighting module code... [ 98%] MDAnalysis.units -highlighting module code... [ 99%] MDAnalysis.visualization.streamlines -highlighting module code... [ 99%] MDAnalysis.visualization.streamlines_3D -highlighting module code... [100%] builtins - -writing additional pages... search opensearch done -copying images... [ 10%] images/rama_ref_plot.png -copying images... [ 20%] images/janin_ref_plot.png -copying images... [ 30%] images/rama_demo_plot.png -copying images... [ 40%] images/janin_demo_plot.png -copying images... [ 50%] images/msd_demo_plot.png -copying images... [ 60%] images/AnalysisBase_parallel.png -copying images... [ 70%] images/parallelization_time.png -copying images... [ 80%] images/RSMD_plot.png -copying images... [ 90%] documentation_pages/visualization/testing_streamline.png -copying images... [100%] documentation_pages/visualization/test_streamplot_3D.png - -dumping search index in English (code: en)... done -dumping object inventory... done -sphinx-sitemap: sitemap.xml was generated for URL https://docs.mdanalysis.org/ in /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/sitemap.xml -Writing compiled SASS to /nfs/homes3/jauy1/Projects/Dev/mdanalysis/package/doc/html/_static/site.css -build finished with problems, 2 warnings (with warnings treated as errors). -make: *** [Makefile:42: html] Error 1 From ae667f7b454767384cd6cd740365672887659682 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 13:33:50 -0700 Subject: [PATCH 054/100] fixed docstring --- package/MDAnalysis/fetch/fetchers.py | 64 +++++++++++++++------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 9eb3a31b5b..59d6df1efb 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -25,9 +25,8 @@ Fetchers --- :mod:`MDAnalysis.fetch.fetchers` ============================================= -This module contains the Fetchers classes that can be used to retrieve or fetch files -from remote servers. These classes uses the third party library :mod:`pooch` as -a dependency. +This module contains fetcher classes that retrieve files from remote servers. +These classes use the third-party library :mod:`pooch` as a dependency. Classes ------- @@ -39,8 +38,8 @@ Variables --------- -These are global submodule level variables that affect the runtime behavior across -all Fetcher Classes. Changing these values will affect all Fetchers! +These module-level variables affect runtime behavior across all fetcher classes. +Changing these values affects all fetchers. .. autodata:: DEFAULT_CACHE_NAME_DOWNLOADER @@ -65,7 +64,7 @@ #: Name of the :mod:`pooch` cache directory #: ``pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER)``; #: -#: see :func:`pooch.os_cache` for further details.' +#: See :func:`pooch.os_cache` for further details. #: #: .. versionadded:: 2.11.0 DEFAULT_CACHE_NAME_DOWNLOADER = "MDAnalysis_pdbs" @@ -82,10 +81,10 @@ class _BaseFetcher(ABC): - """Blueprint Class for all Fetchers + """Base class for all fetchers. - This shouldn't be initalized directly but should be inherited by other - Fetchers classes. + This class should not be initialized directly; fetcher implementations + should inherit from it. """ @@ -96,6 +95,7 @@ def __init__( @abstractmethod def fetch(self, base_url, verbose, timeout, retries): + """Retrieve files from a remote server.""" # Starts file retrieval workflow # All fetchers should call _check_pooch() # @@ -105,13 +105,14 @@ def fetch(self, base_url, verbose, timeout, retries): def _check_pooch( self, ): + """Raise an error if :mod:`pooch` is not installed.""" if not HAS_POOCH: raise ModuleNotFoundError( "pooch is needed as a dependency for Fetchers" ) def _validate_fetch_args(self, args): - """This initalized the fetcher library variables""" + """Add default timeout and retry values to fetch arguments.""" args.setdefault("timeout", DEFAULT_TIMEOUT) args.setdefault("retries", DEFAULT_RETRIES) @@ -129,10 +130,9 @@ class StaticFetcher(_BaseFetcher): Path to the cache directory. If set to ``None``, the default cache directory will be used as specified by :data:`DEFAULT_CACHE_NAME_DOWNLOADER`. + If the directory does not exist, it will be created. - If the directory does not exist, it will attempted to be created. - - hash : str + hash : str, optional Hash algorithm to use for verifying the integrity of downloaded files. The default is ``sha256``. Valid options are any hash algorithm available in the :mod:`hashlib` module. @@ -147,7 +147,7 @@ class StaticFetcher(_BaseFetcher): Path to the database file used for caching. Created after calling fetch(). - hash : str or ``None`` + hash : str Hash algorithm used for verifying the integrity of downloaded files. Notes @@ -187,22 +187,26 @@ def fetch( URL pointing to the directory containing the files to be downloaded. file_name : str or sequence of str Name of the file or files to download. - Note that the request is phrased as {base_url}/{file_name}. + The requested URL has the form ``{base_url}/{file_name}``. verbose : bool, optional - If True, shows fetcher progress. - Default is False. - db_name : str, optional + If ``True``, show download progress. The default is ``False``. + db_name : str or None, optional Name of the local hash database file used to verify cached downloads. - Default is "hashes.txt". If None, no registry database is read or written. + The default is ``"hashes.txt"``. If ``None``, no registry database + is read or written. + append_db : bool, optional + If ``True``, add downloaded files that are missing from an existing + registry to that registry. If ``False``, missing registry entries + raise a :class:`ValueError`. The default is ``False``. timeout : float, optional - Time in seconds to wait for a response from the server before timing out. - Default is :data:`DEFAULT_TIMEOUT`. + Time in seconds to wait for a response from the server before timing + out. The default is :data:`DEFAULT_TIMEOUT`. retries : int, optional - Number of attempts to retry a download if it fails. Default is :data:`DEFAULT_RETRIES`. + Number of times to retry a failed download. The default is + :data:`DEFAULT_RETRIES`. downloader : str, optional - Downloader backend to use. If a string is provided, it must identify - a supported downloader such as "HTTP". - Default is "HTTP". + Downloader backend to use. Supported values are ``"HTTP"``, + ``"FTP"``, ``"SFTP"``, and ``"DOI"``. The default is ``"HTTP"``. Returns ------- @@ -210,8 +214,8 @@ def fetch( The downloaded file path for a single file, or a list of paths for multiple files. - Example - ------- + Examples + -------- A script to download a protein from the RCSB Protein Data Bank. @@ -347,8 +351,8 @@ def fix_registry(self, db_path, file_dict): Notes ----- For each entry in ``file_dict`` with a value of ``None``, this method builds - the corresponding file path relative to ``self.cache_path`` and appends its - hash to the registry using ``self.write_registry``. + the corresponding file path relative to :attr:`cache_path` and appends its + hash to the registry using :meth:`write_registry`. """ new_files = [ self.cache_path / file_name @@ -375,7 +379,7 @@ def check_registry(self, db_path): Notes ----- This method compares filenames from the registry against files found - recursively under ``self.cache_path``. A cache file is considered missing + recursively under :attr:`cache_path`. A cache file is considered missing from the registry when ``path.name`` is not a key in the registry dictionary. """ From 84bcf1dfac2a48b8ce973595ba86a5e76caa52ce Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 14 Jul 2026 13:44:09 -0700 Subject: [PATCH 055/100] formatting fetchers.py --- package/MDAnalysis/fetch/fetchers.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 59d6df1efb..82bfd62dad 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -273,7 +273,9 @@ def fetch( if MISSING_FILES and not APPEND_DATABASE: raise ValueError( - f"There are unknown files in the registry! The missing files are {missing_files_list}. To fix this, please set append_db=True to append the database" + "There are unknown files in the registry! The missing files are" + + f" {missing_files_list}. To fix this, please set append_db=True" + + " to append the database" ) # Code to process non-registry files @@ -283,7 +285,7 @@ def fetch( if file not in registry_dictionary: registry_dictionary[file] = None - ## Pooch setup + # Pooch setup main_downloader = pooch.create( path=self.cache_path, base_url=base_url, @@ -306,7 +308,8 @@ def fetch( fetch_downloader = pooch.DOIDownloader(**download_kwargs) case _: raise ValueError( - f"Invalid downloader '{downloader}'. Valid options are 'HTTP', 'FTP', 'SFTP', 'DOI'." + f"Invalid downloader '{downloader}'. Valid options " + + "are 'HTTP', 'FTP', 'SFTP', 'DOI'." ) paths = [ @@ -457,7 +460,7 @@ def write_registry(self, db_path, files, mode="w"): digest = pooch.file_hash(file, alg=self.hash) f.write(f"{file.name} {self.hash}:{digest}\n") - ### Arugment Validation Methods + # Argument validation methods def _check_cache_path_input(self, cache_path): if cache_path is None: path = Path(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER)) @@ -472,7 +475,8 @@ def _check_hash_input(self, hash): return hash else: raise ValueError( - f'Invalid hash "{hash}". Valid hashes algorithms are {hashlib.algorithms_available}.' + f'Invalid hash "{hash}". Valid hashes algorithms' + + f" are {hashlib.algorithms_available}." ) From c77a86ac3aa60747052a354399971adcb2deeef2 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 16 Jul 2026 13:31:03 -0700 Subject: [PATCH 056/100] Fetchers now only downloaed requested_files --- package/MDAnalysis/fetch/fetchers.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 82bfd62dad..cf0b1eef64 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -246,7 +246,7 @@ def fetch( exist. """ - # Keywords arguments are reserved for common _BaseFetcher.fetch() arguements. + # Keywords arguments that are reserved for common _BaseFetcher.fetch() arguments. kwargs = self._validate_fetch_args(kwargs) LOAD_FROM_CACHE = False @@ -254,6 +254,7 @@ def fetch( MISSING_FILES = False APPEND_DATABASE = append_db + ## Reading from Registry registry_dictionary = {} if db_name is not None: @@ -278,14 +279,14 @@ def fetch( + " to append the database" ) - # Code to process non-registry files - # One-liner that forces strings into tuple - no_db_files = (file_name,) if isinstance(file_name, str) else file_name - for file in no_db_files: - if file not in registry_dictionary: - registry_dictionary[file] = None + # Ensure fetch() only get requested files + requested_files = (file_name,) if isinstance(file_name, str) else tuple(file_name) + for name in requested_files: + registry_dictionary.setdefault(name, None) + + ## - # Pooch setup + ## Download code using pooch main_downloader = pooch.create( path=self.cache_path, base_url=base_url, @@ -311,7 +312,7 @@ def fetch( f"Invalid downloader '{downloader}'. Valid options " + "are 'HTTP', 'FTP', 'SFTP', 'DOI'." ) - + paths = [ Path( main_downloader.fetch( @@ -320,15 +321,19 @@ def fetch( downloader=fetch_downloader, ) ) - for file_name in registry_dictionary.keys() + for file_name in requested_files ] + ## + ## Registry write code if CREATE_DATABASE: self.write_registry(self.db_path, paths) if APPEND_DATABASE and LOAD_FROM_CACHE: self.fix_registry(self.db_path, registry_dictionary) + + ## return paths[0] if len(paths) == 1 else paths From 1bcaaba302aa9a31907f3f0736b9760b1bc03923 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 16 Jul 2026 14:07:35 -0700 Subject: [PATCH 057/100] added auto to downloader arguement --- package/MDAnalysis/fetch/fetchers.py | 68 ++++++++++++++----- .../fetch/test_static_fetcher.py | 9 ++- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index cf0b1eef64..f907589a41 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -49,6 +49,7 @@ """ import hashlib +import re from pathlib import Path from abc import ABC, abstractmethod @@ -173,7 +174,7 @@ def fetch( verbose=False, db_name="hashes.txt", append_db=False, - downloader="HTTP", + downloader="auto", **kwargs, ): """ @@ -205,8 +206,8 @@ def fetch( Number of times to retry a failed download. The default is :data:`DEFAULT_RETRIES`. downloader : str, optional - Downloader backend to use. Supported values are ``"HTTP"``, - ``"FTP"``, ``"SFTP"``, and ``"DOI"``. The default is ``"HTTP"``. + Downloader backend to use. Supported values are ``"auto", "http"``, + ``"ftp"``, ``"sftp"``, and ``"doi"``. The default is ``"auto"``. Returns ------- @@ -298,21 +299,9 @@ def fetch( download_kwargs = kwargs.copy() download_kwargs.pop("retries") - match downloader: - case "HTTP": - fetch_downloader = pooch.HTTPDownloader(**download_kwargs) - case "FTP": - fetch_downloader = pooch.FTPDownloader(**download_kwargs) - case "SFTP": - fetch_downloader = pooch.SFTPDownloader(**download_kwargs) - case "DOI": - fetch_downloader = pooch.DOIDownloader(**download_kwargs) - case _: - raise ValueError( - f"Invalid downloader '{downloader}'. Valid options " - + "are 'HTTP', 'FTP', 'SFTP', 'DOI'." - ) - + #import ipdb; ipdb.set_trace() + fetch_downloader = self._set_downloader(base_url, downloader, **download_kwargs) + paths = [ Path( main_downloader.fetch( @@ -337,6 +326,49 @@ def fetch( return paths[0] if len(paths) == 1 else paths + def _set_downloader(self, base_url, downloader, **kwargs): + """Sets Downloader in fetch() by matching a regex against the download link""" + + + SUPPORTED_DOWNLOADERS = ('auto', 'http', 'https', 'ftp', 'sftp','doi') + + if downloader not in SUPPORTED_DOWNLOADERS: + raise ValueError( + f"Invalid downloader '{downloader}'. Valid options " + + f"are {SUPPORTED_DOWNLOADERS}" + ) + + if downloader == 'auto': + # The regex below is AI generated, but the overall idea of using regular expressions + # was thought up by me. + # + # I thought of these four examples and prompt AI to come with a regex that capture the + # susbtring before "://"" + # + # doi://10.6084/m9.figshare.14763051.v1/tiny-data.txt + # https://www.example.com/page + # ftp://ftp.example.com/files/document.txt + # sftp://username@example.com/path/to/folder + + regex = r'^([^:]+)://' + match = re.match(regex, base_url) + + if match: + _downloader = match.group(1) + else: + _downloader = downloader + + match _downloader: + case "http" | "https": + return pooch.HTTPDownloader(**kwargs) + case "ftp": + return pooch.FTPDownloader(**kwargs) + case "sftp": + return pooch.SFTPDownloader(**kwargs) + case "doi": + return pooch.DOIDownloader(**kwargs) + + def fix_registry(self, db_path, file_dict): """ Append newly downloaded files to an existing Pooch registry. diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 5f565b0df2..c904664d8e 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -70,8 +70,8 @@ def test_invalid_downloader(self, tmp_path): with pytest.raises( ValueError, match=re.escape( - "Invalid downloader 'barfoo'. Valid options are " - "'HTTP', 'FTP', 'SFTP', 'DOI'." + "Invalid downloader 'barfoo'. Valid options " + + "are ('auto', 'http', 'https', 'ftp', 'sftp', 'doi')" ), ): downloader.fetch( @@ -284,4 +284,7 @@ def test_environment_variable_override(self, tmp_path, monkeypatch): file_name="TEST_FILE1.txt", ) - assert (tmp_path / "TEST_FILE1.txt").exists() \ No newline at end of file + assert (tmp_path / "TEST_FILE1.txt").exists() + +# def test_missing_files(): +# pass From 620978d6d67386ebaa2117b3226c70863b7bcc05 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 16 Jul 2026 14:21:04 -0700 Subject: [PATCH 058/100] Removed StaticFetcher().db_path --- package/MDAnalysis/fetch/fetchers.py | 15 +++++++-------- .../fetch/test_static_fetcher.py | 18 +++++++++--------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index f907589a41..e786020d32 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -165,7 +165,6 @@ def __init__(self, cache_path=None, hash="sha256"): self.cache_path = self._check_cache_path_input(cache_path) self.hash = self._check_hash_input(hash) - self.db_path = None def fetch( self, @@ -259,16 +258,16 @@ def fetch( registry_dictionary = {} if db_name is not None: - self.db_path = self.cache_path / Path(db_name) + db_path = self.cache_path / Path(db_name) - if self.db_path.exists(): + if db_path.exists(): LOAD_FROM_CACHE = True else: CREATE_DATABASE = True if LOAD_FROM_CACHE: - registry_dictionary = self.read_registry(self.db_path) - missing_files_list = self.check_registry(self.db_path) + registry_dictionary = self.read_registry(db_path) + missing_files_list = self.check_registry(db_path) if len(missing_files_list) != 0: MISSING_FILES = True @@ -317,10 +316,10 @@ def fetch( ## Registry write code if CREATE_DATABASE: - self.write_registry(self.db_path, paths) + self.write_registry(db_path, paths) if APPEND_DATABASE and LOAD_FROM_CACHE: - self.fix_registry(self.db_path, registry_dictionary) + self.fix_registry(db_path, registry_dictionary) ## @@ -429,7 +428,7 @@ def check_registry(self, db_path): cache_files = ( path for path in self.cache_path.rglob("*") - if path != self.db_path and path.is_file() + if path != db_path and path.is_file() ) return [ diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index c904664d8e..1cac6d1980 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -133,9 +133,9 @@ def test_create_database(self, tmp_path): ) assert path.exists() - assert (downloader.db_path).exists() + assert Path(downloader.cache_path / REGISTRY_NAME ).exists() assert ( - downloader.db_path + downloader.cache_path / REGISTRY_NAME ).read_text() == "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" def test_append_database(self, tmp_path): @@ -155,7 +155,7 @@ def test_append_database(self, tmp_path): append_db=True ) - assert downloader.db_path.read_text() == ( + assert Path(downloader.cache_path / REGISTRY_NAME).read_text() == ( "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" ) @@ -175,8 +175,8 @@ def test_different_hashes(self, tmp_path): db_name=REGISTRY_NAME, ) - assert ( - downloader.db_path + assert Path( + downloader.cache_path / REGISTRY_NAME ).read_text() == "TEST_FILE1.txt md5:b2f138521297db74b6b280feeb14f9f6\n" def test_existing_database(self, tmp_path): @@ -211,7 +211,7 @@ def test_no_database(self, tmp_path): assert path.exists() assert path.name == "TEST_FILE1.txt" - assert downloader.db_path is None + assert not Path(downloader.cache_path / REGISTRY_NAME).exists() assert not (tmp_path / REGISTRY_NAME).exists() def test_multiple_downloads_no_database(self, tmp_path): @@ -231,7 +231,7 @@ def test_multiple_downloads_no_database(self, tmp_path): ("TEST_FILE1.txt", "TEST_FILE2.txt") ) - assert downloader.db_path is None + assert not Path(downloader.cache_path / REGISTRY_NAME).exists() assert not (tmp_path / REGISTRY_NAME).exists() def test_multiple_downloads_create_database(self, tmp_path): @@ -245,7 +245,7 @@ def test_multiple_downloads_create_database(self, tmp_path): db_name=REGISTRY_NAME, ) - assert downloader.db_path.read_text() == ( + assert Path(downloader.cache_path / REGISTRY_NAME).read_text() == ( "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" ) @@ -284,7 +284,7 @@ def test_environment_variable_override(self, tmp_path, monkeypatch): file_name="TEST_FILE1.txt", ) - assert (tmp_path / "TEST_FILE1.txt").exists() + assert Path(tmp_path / "TEST_FILE1.txt").exists() # def test_missing_files(): # pass From cf36767d12072d3c2a0d5fafdc4b7fbc6a34fa2a Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 16 Jul 2026 16:06:49 -0700 Subject: [PATCH 059/100] Fixed and added docs to append_registry() --- package/MDAnalysis/fetch/fetchers.py | 187 ++++++++++-------- .../fetch/test_static_fetcher.py | 33 +++- 2 files changed, 127 insertions(+), 93 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index e786020d32..3b0a1ac829 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -113,7 +113,7 @@ def _check_pooch( ) def _validate_fetch_args(self, args): - """Add default timeout and retry values to fetch arguments.""" + """Add the default global timeout and retry variables to fetch arguments.""" args.setdefault("timeout", DEFAULT_TIMEOUT) args.setdefault("retries", DEFAULT_RETRIES) @@ -144,10 +144,6 @@ class StaticFetcher(_BaseFetcher): cache_path : pathlib.Path or ``None`` Path to the cache directory. - db_path : pathlib.Path or ``None`` - Path to the database file used for caching. Created after calling - fetch(). - hash : str Hash algorithm used for verifying the integrity of downloaded files. @@ -216,26 +212,16 @@ def fetch( Examples -------- + Download a single PDB file from the RCSB Protein Data Bank. + + >>> StaticFetcher.fetch(file_name="1AKE.cif", + base_url="https://files.wwpdb.org/download/") + './MDAnalysis_pdbs/1AKE.cif' - A script to download a protein from the RCSB Protein Data Bank. - - .. code-block:: python - - from MDAnalysis.fetch import StaticFetcher - - fetcher = StaticFetcher(cache_path=cache_path) - - # Download a single file from the RCSB Protein Data Bank - path = fetcher.fetch( - file_name="1AKE.cif", - base_url="https://files.wwpdb.org/download/", - ) - - # Download multiple files from the RCSB Protein Data Bank - path = fetcher.fetch( - file_name=["1AKE.cif", "4AKE.cif"], - base_url="https://files.wwpdb.org/download/", - ) + Download multiple PDB files from the RCSB Protein Data Bank. + >>> StaticFetcher.fetch(file_name=["1AKE.cif", "4AKE.cif"], + base_url="https://files.wwpdb.org/download/") + ['./MDAnalysis_pdbs/1AKE.pdb.gz', './MDAnalysis_pdbs/4BWZ.pdb.gz'] Notes ----- @@ -276,7 +262,9 @@ def fetch( raise ValueError( "There are unknown files in the registry! The missing files are" + f" {missing_files_list}. To fix this, please set append_db=True" - + " to append the database" + + " to append the database." + "\n" + + f"These missing files are {missing_files_list}" ) # Ensure fetch() only get requested files @@ -297,8 +285,6 @@ def fetch( download_kwargs = kwargs.copy() download_kwargs.pop("retries") - - #import ipdb; ipdb.set_trace() fetch_downloader = self._set_downloader(base_url, downloader, **download_kwargs) paths = [ @@ -311,7 +297,7 @@ def fetch( ) for file_name in requested_files ] - + ## ## Registry write code @@ -319,67 +305,27 @@ def fetch( self.write_registry(db_path, paths) if APPEND_DATABASE and LOAD_FROM_CACHE: - self.fix_registry(db_path, registry_dictionary) + self.append_registry(db_path, requested_files) ## return paths[0] if len(paths) == 1 else paths - def _set_downloader(self, base_url, downloader, **kwargs): - """Sets Downloader in fetch() by matching a regex against the download link""" - - - SUPPORTED_DOWNLOADERS = ('auto', 'http', 'https', 'ftp', 'sftp','doi') - - if downloader not in SUPPORTED_DOWNLOADERS: - raise ValueError( - f"Invalid downloader '{downloader}'. Valid options " - + f"are {SUPPORTED_DOWNLOADERS}" - ) - - if downloader == 'auto': - # The regex below is AI generated, but the overall idea of using regular expressions - # was thought up by me. - # - # I thought of these four examples and prompt AI to come with a regex that capture the - # susbtring before "://"" - # - # doi://10.6084/m9.figshare.14763051.v1/tiny-data.txt - # https://www.example.com/page - # ftp://ftp.example.com/files/document.txt - # sftp://username@example.com/path/to/folder - - regex = r'^([^:]+)://' - match = re.match(regex, base_url) - - if match: - _downloader = match.group(1) - else: - _downloader = downloader - - match _downloader: - case "http" | "https": - return pooch.HTTPDownloader(**kwargs) - case "ftp": - return pooch.FTPDownloader(**kwargs) - case "sftp": - return pooch.SFTPDownloader(**kwargs) - case "doi": - return pooch.DOIDownloader(**kwargs) - - - def fix_registry(self, db_path, file_dict): + def append_registry(self, db_path, files): """ - Append newly downloaded files to an existing Pooch registry. + Append cached files to an existing Pooch registry. + + Each entry in ``files`` is resolved relative to ``self.cache_path``. The + file hash is computed using the fetcher's configured hash algorithm ``self.hash`` and a + new registry line is appended to ``db_path``. Parameters ---------- db_path : str or path-like Path to the registry file to update. - file_dict : dict - Dictionary mapping filenames to hash values. Files with a hash value of - ``None`` are treated as newly downloaded files and appended to the - registry. + files : iterable of str or path-like + File names or paths for cached files to append to the registry. Relative + paths are interpreted relative to ``self.cache_path``. Returns ------- @@ -389,16 +335,42 @@ def fix_registry(self, db_path, file_dict): Notes ----- - For each entry in ``file_dict`` with a value of ``None``, this method builds - the corresponding file path relative to :attr:`cache_path` and appends its - hash to the registry using :meth:`write_registry`. + Existing registry entries are preserved. This method does not check for or + remove duplicate file entries. + + Each appended registry line has the format:: + + : + + Example + ------- + .. code-block:: python + + from MDAnalysis.fetch.fetchers import StaticFetcher + + fetcher = StaticFetcher() + + file1 = fetcher.fetch( + file_name="1AKE.cif", + base_url="https://files.wwpdb.org/download/", + db_name="db_hash1.txt", + ) + + file2 = fetcher.fetch( + file_name="4AKE.cif", + base_url="https://files.wwpdb.org/download/", + db_name="db_hash2.txt", + ) + + registry = file1.parent / "db_hash1.txt" + fetcher.append_registry(registry, ["4AKE.cif"]) + + print(registry.read_text()) + # 1AKE.cif sha256:01f41b1b42318a1a5df7f650dbab881677aa0e8d825f7c42dd26ae16a94c0948 + # 4AKE.cif sha256:fcb2ff49a3e255797fee277ce28e0acace67f6e6ddf432841f8451f00cbde9e9 """ - new_files = [ - self.cache_path / file_name - for file_name, file_hash in file_dict.items() - if file_hash is None - ] - self.write_registry(db_path, new_files, mode="a") + new_files = [self.cache_path / file_name for file_name in files] + self.write_registry(Path(db_path), new_files, mode="a") def check_registry(self, db_path): """ @@ -514,6 +486,47 @@ def _check_hash_input(self, hash): f'Invalid hash "{hash}". Valid hashes algorithms' + f" are {hashlib.algorithms_available}." ) + def _set_downloader(self, base_url, downloader, **kwargs): + """Sets Downloader in fetch() by matching a regex against the download link""" + + + SUPPORTED_DOWNLOADERS = ('auto', 'http', 'https', 'ftp', 'sftp','doi') + + if downloader not in SUPPORTED_DOWNLOADERS: + raise ValueError( + f"Invalid downloader '{downloader}'. Valid options " + + f"are {SUPPORTED_DOWNLOADERS}" + ) + + if downloader == 'auto': + # The regex below is AI generated, but the overall idea of using regular expressions + # was thought up by me. + # + # I thought of these four examples and prompt AI to come with a regex that capture the + # susbtring before "://"" + # + # doi://10.6084/m9.figshare.14763051.v1/tiny-data.txt + # https://www.example.com/page + # ftp://ftp.example.com/files/document.txt + # sftp://username@example.com/path/to/folder + + regex = r'^([^:]+)://' + match = re.match(regex, base_url) + + if match: + _downloader = match.group(1) + else: + _downloader = downloader + + match _downloader: + case "http" | "https": + return pooch.HTTPDownloader(**kwargs) + case "ftp": + return pooch.FTPDownloader(**kwargs) + case "sftp": + return pooch.SFTPDownloader(**kwargs) + case "doi": + return pooch.DOIDownloader(**kwargs) # class DynamicFetcher(_BaseFetcher): diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 1cac6d1980..5b410bf38e 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -161,10 +161,6 @@ def test_append_database(self, tmp_path): ) - - - - def test_different_hashes(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" @@ -286,5 +282,30 @@ def test_environment_variable_override(self, tmp_path, monkeypatch): assert Path(tmp_path / "TEST_FILE1.txt").exists() -# def test_missing_files(): -# pass + +def test_append_registry(tmp_path): + + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + fetcher = StaticFetcher(cache_path=tmp_path) + + file1 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name="db_hash1.txt", + ) + + file2 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE2.txt", + db_name="db_hash2.txt", + ) + + registry = file1.parent / "db_hash1.txt" + fetcher.append_registry(registry, ["TEST_FILE2.txt"]) + + assert Path(registry).read_text() == ( + 'TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n' + 'TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n' + ) \ No newline at end of file From 75b058de170a1ee37c00d518c5d3285b94beecff Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 16 Jul 2026 17:02:22 -0700 Subject: [PATCH 060/100] minor modifications --- package/MDAnalysis/fetch/fetchers.py | 36 ++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 3b0a1ac829..8315b5eb9a 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -315,8 +315,8 @@ def append_registry(self, db_path, files): """ Append cached files to an existing Pooch registry. - Each entry in ``files`` is resolved relative to ``self.cache_path``. The - file hash is computed using the fetcher's configured hash algorithm ``self.hash`` and a + Each entry in ``files`` is resolved relative to :attr:`cache_path`. The + file hash is computed using the fetcher's configured hash algorithm :attr:`hash` and a new registry line is appended to ``db_path``. Parameters @@ -325,7 +325,7 @@ def append_registry(self, db_path, files): Path to the registry file to update. files : iterable of str or path-like File names or paths for cached files to append to the registry. Relative - paths are interpreted relative to ``self.cache_path``. + paths are interpreted relative to :attr:`cache_path`. Returns ------- @@ -372,14 +372,20 @@ def append_registry(self, db_path, files): new_files = [self.cache_path / file_name for file_name in files] self.write_registry(Path(db_path), new_files, mode="a") - def check_registry(self, db_path): + def check_registry(self, db_path, ignore=[]): """ Return cache files that are missing from the registry. + This method compares filenames within the registry against files found + recursively under :attr:`cache_path`. A cache file is considered missing + when it is recorded in the registry, but not physically present on disk. + Parameters ---------- db_path : str or path-like Path to the registry file to read. + ignore : list of str or path-like + Files to be ignored Returns ------- @@ -387,12 +393,20 @@ def check_registry(self, db_path): Cache file paths whose filenames are not present in the registry. The registry database file itself is excluded from the result. + Example + ------- + .. code-block:: python + + + + Notes ----- - This method compares filenames from the registry against files found - recursively under :attr:`cache_path`. A cache file is considered missing - from the registry when ``path.name`` is not a key in the registry - dictionary. + Each line in the registry file is expected to have the format:: + + : + + """ registry_dictionary = self.read_registry(db_path) database_files = set(registry_dictionary.keys()) @@ -446,7 +460,7 @@ def write_registry(self, db_path, files, mode="w"): ---------- db_path : str or path-like Path to the registry file to write. - files : iterable of path-like + files : iterable of str or path-like Files to include in the registry. Each file must provide a ``name`` attribute and be readable by ``pooch.file_hash``. mode : str, optional @@ -465,6 +479,7 @@ def write_registry(self, db_path, files, mode="w"): """ with open(db_path, mode=mode) as f: for file in files: + file = Path(file) digest = pooch.file_hash(file, alg=self.hash) f.write(f"{file.name} {self.hash}:{digest}\n") @@ -486,6 +501,7 @@ def _check_hash_input(self, hash): f'Invalid hash "{hash}". Valid hashes algorithms' + f" are {hashlib.algorithms_available}." ) + def _set_downloader(self, base_url, downloader, **kwargs): """Sets Downloader in fetch() by matching a regex against the download link""" @@ -500,7 +516,7 @@ def _set_downloader(self, base_url, downloader, **kwargs): if downloader == 'auto': # The regex below is AI generated, but the overall idea of using regular expressions - # was thought up by me. + # to check the first bit of the url was thought up by me. # # I thought of these four examples and prompt AI to come with a regex that capture the # susbtring before "://"" From 608a4d772738ba9f45f571216d066bb50dc82a61 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 16 Jul 2026 17:38:12 -0700 Subject: [PATCH 061/100] done for the day --- package/MDAnalysis/fetch/fetchers.py | 69 +++++++++++-------- .../MDAnalysisTests/fetch/file_1_hash.txt | 0 .../fetch/test_static_fetcher.py | 36 +++++++++- 3 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 testsuite/MDAnalysisTests/fetch/file_1_hash.txt diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 8315b5eb9a..efbe93bd8f 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -333,6 +333,26 @@ def append_registry(self, db_path, files): This method updates the registry file in place and does not return a value. + Example + ------- + >>> from MDAnalysis.fetch.fetchers import StaticFetcher + >>> fetcher = StaticFetcher() + >>> file1 = fetcher.fetch( + ... file_name="1AKE.cif", + ... base_url="https://files.wwpdb.org/download/", + ... db_name="db_hash1.txt", + ... ) + >>> file2 = fetcher.fetch( + ... file_name="4AKE.cif", + ... base_url="https://files.wwpdb.org/download/", + ... db_name="db_hash2.txt", + ... ) + >>> registry = file1.parent / "db_hash1.txt" + >>> fetcher.append_registry(registry, ["4AKE.cif"]) + >>> print(registry.read_text()) + 1AKE.cif sha256:01f41b1b42318a1a5df7f650dbab881677aa0e8d825f7c42dd26ae16a94c0948 + 4AKE.cif sha256:fcb2ff49a3e255797fee277ce28e0acace67f6e6ddf432841f8451f00cbde9e9 + Notes ----- Existing registry entries are preserved. This method does not check for or @@ -342,32 +362,6 @@ def append_registry(self, db_path, files): : - Example - ------- - .. code-block:: python - - from MDAnalysis.fetch.fetchers import StaticFetcher - - fetcher = StaticFetcher() - - file1 = fetcher.fetch( - file_name="1AKE.cif", - base_url="https://files.wwpdb.org/download/", - db_name="db_hash1.txt", - ) - - file2 = fetcher.fetch( - file_name="4AKE.cif", - base_url="https://files.wwpdb.org/download/", - db_name="db_hash2.txt", - ) - - registry = file1.parent / "db_hash1.txt" - fetcher.append_registry(registry, ["4AKE.cif"]) - - print(registry.read_text()) - # 1AKE.cif sha256:01f41b1b42318a1a5df7f650dbab881677aa0e8d825f7c42dd26ae16a94c0948 - # 4AKE.cif sha256:fcb2ff49a3e255797fee277ce28e0acace67f6e6ddf432841f8451f00cbde9e9 """ new_files = [self.cache_path / file_name for file_name in files] self.write_registry(Path(db_path), new_files, mode="a") @@ -436,7 +430,6 @@ def read_registry(self, db_path): Dictionary mapping each filename in the registry to its stored hash value. Hash values are expected to include the hash algorithm prefix, for example ``"sha256:"``. - Notes ----- Each line in the registry file is expected to have the format:: @@ -465,12 +458,32 @@ def write_registry(self, db_path, files, mode="w"): attribute and be readable by ``pooch.file_hash``. mode : str, optional File opening mode used when writing the registry. Default is ``"w"``. - + Returns ------- None This method writes the registry to disk and does not return a value. + Example + ------- + .. code-block:: python + >>> files = { + ... "file1.txt": "Molecular \\n", + ... "file2.txt": "Dynamics. \\n", + ... } + >>> for filename, content in files.items(): + ... with open(tmp_path / filename, "w") as f: + ... _ = f.write(content) + ... + >>> fetcher = StaticFetcher(cache_path=tmp_path) + >>> fetcher.write_registry( + ... "file_1_and_2_hash.txt", + ... ["file1.txt", "file2.txt"], + ... ) + >>> print("file_1_and_2_hash.txt").read_text()) + file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20 + file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a + Notes ----- Each registry line is written in the format:: diff --git a/testsuite/MDAnalysisTests/fetch/file_1_hash.txt b/testsuite/MDAnalysisTests/fetch/file_1_hash.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 5b410bf38e..038dce6e8c 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -308,4 +308,38 @@ def test_append_registry(tmp_path): assert Path(registry).read_text() == ( 'TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n' 'TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n' - ) \ No newline at end of file + ) + +def test_write_registry(tmp_path): + files = { + "file1.txt": "Molecular \n", + "file2.txt": "Dynamics. \n", + } + + + for filename, content in files.items(): + with open(tmp_path / filename, "w") as f: + f.write(content) + + + fetcher = StaticFetcher(cache_path=tmp_path) + + fetcher.write_registry(tmp_path / "file_1_hash.txt", [tmp_path / "file1.txt"]) + assert (tmp_path / "file_1_hash.txt").read_text() == 'file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20\n' + + fetcher.write_registry(tmp_path / "file_2_hash.txt", [tmp_path / "file2.txt"]) + assert (tmp_path / "file_2_hash.txt").read_text() == 'file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a\n' + + + fetcher.write_registry(tmp_path / "file_1_and_2_hash.txt", [tmp_path / "file1.txt", tmp_path / "file2.txt"]) + assert (tmp_path / "file_1_and_2_hash.txt").read_text() == ( + "file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20\n" + "file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a\n" + ) + + def test_read_registry(): + pass + + def test_check_registry(): + pass + \ No newline at end of file From 673e9820ae9b359fd14fbe6505d3fa42813ee058 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 17 Jul 2026 12:27:13 -0700 Subject: [PATCH 062/100] some docs --- package/MDAnalysis/fetch/fetchers.py | 55 +++++++++++--- .../MDAnalysisTests/fetch/file_1_hash.txt | 0 .../fetch/test_static_fetcher.py | 76 +++++++++++++++++-- 3 files changed, 114 insertions(+), 17 deletions(-) delete mode 100644 testsuite/MDAnalysisTests/fetch/file_1_hash.txt diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index efbe93bd8f..232ed58ea5 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -201,7 +201,7 @@ def fetch( Number of times to retry a failed download. The default is :data:`DEFAULT_RETRIES`. downloader : str, optional - Downloader backend to use. Supported values are ``"auto", "http"``, + Downloader backend to use. Supported values are ``"auto"``, ``"http"``, ``"ftp"``, ``"sftp"``, and ``"doi"``. The default is ``"auto"``. Returns @@ -212,16 +212,16 @@ def fetch( Examples -------- - Download a single PDB file from the RCSB Protein Data Bank. + Download a single CIF file from the RCSB Protein Data Bank. >>> StaticFetcher.fetch(file_name="1AKE.cif", base_url="https://files.wwpdb.org/download/") './MDAnalysis_pdbs/1AKE.cif' - Download multiple PDB files from the RCSB Protein Data Bank. + Download multiple CIF files from the RCSB Protein Data Bank. >>> StaticFetcher.fetch(file_name=["1AKE.cif", "4AKE.cif"], base_url="https://files.wwpdb.org/download/") - ['./MDAnalysis_pdbs/1AKE.pdb.gz', './MDAnalysis_pdbs/4BWZ.pdb.gz'] + ['./MDAnalysis_pdbs/1AKE.cif', './MDAnalysis_pdbs/4AKE.cif'] Notes ----- @@ -349,7 +349,7 @@ def append_registry(self, db_path, files): ... ) >>> registry = file1.parent / "db_hash1.txt" >>> fetcher.append_registry(registry, ["4AKE.cif"]) - >>> print(registry.read_text()) + >>> registry.read_text() 1AKE.cif sha256:01f41b1b42318a1a5df7f650dbab881677aa0e8d825f7c42dd26ae16a94c0948 4AKE.cif sha256:fcb2ff49a3e255797fee277ce28e0acace67f6e6ddf432841f8451f00cbde9e9 @@ -390,8 +390,23 @@ def check_registry(self, db_path, ignore=[]): Example ------- .. code-block:: python - - + >>> files = { + ... "file1.txt": "Molecular \\n", + ... "file2.txt": "Dynamics. \\n", + ... "file3.txt": "Analysis. \\n" + ... } + >>> for filename, content in files.items(): + ... with open(tmp_path / filename, "w") as f: + ... f.write(content) + ... + >>> fetcher = StaticFetcher(cache_path=tmp_path) + >>> fetcher.write_registry( + ... "file_1_2_and_3_hash.txt", + ... files=["file1.txt"], + ... ) + >>> fetcher.check_registry("file_1_2_and_3_hash.txt") + [Path('./MDAnalysis_pdbs/file3.txt'), Path('./MDAnalysis_pdbs/file2.txt')] + Notes @@ -430,6 +445,27 @@ def read_registry(self, db_path): Dictionary mapping each filename in the registry to its stored hash value. Hash values are expected to include the hash algorithm prefix, for example ``"sha256:"``. + + Example + ------- + .. code-block:: python + + >>> files = { + ... "file1.txt": "Molecular \\n", + ... "file2.txt": "Dynamics. \\n", + ... } + >>> for filename, content in files.items(): + ... with open(tmp_path / filename, "w") as f: + ... f.write(content) + ... + >>> fetcher = StaticFetcher(cache_path=tmp_path) + >>> fetcher.write_registry( + ... "file_1_and_2_hash.txt", + ... ["file1.txt", "file2.txt"], + ... ) + >>> fetcher.read_registry("file_1_and_2_hash.txt") + {'file1.txt': 'sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20', + 'file2.txt': 'sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a'} Notes ----- Each line in the registry file is expected to have the format:: @@ -473,14 +509,14 @@ def write_registry(self, db_path, files, mode="w"): ... } >>> for filename, content in files.items(): ... with open(tmp_path / filename, "w") as f: - ... _ = f.write(content) + ... f.write(content) ... >>> fetcher = StaticFetcher(cache_path=tmp_path) >>> fetcher.write_registry( ... "file_1_and_2_hash.txt", ... ["file1.txt", "file2.txt"], ... ) - >>> print("file_1_and_2_hash.txt").read_text()) + >>> Path("file_1_and_2_hash.txt").read_text() file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20 file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a @@ -517,7 +553,6 @@ def _check_hash_input(self, hash): def _set_downloader(self, base_url, downloader, **kwargs): """Sets Downloader in fetch() by matching a regex against the download link""" - SUPPORTED_DOWNLOADERS = ('auto', 'http', 'https', 'ftp', 'sftp','doi') diff --git a/testsuite/MDAnalysisTests/fetch/file_1_hash.txt b/testsuite/MDAnalysisTests/fetch/file_1_hash.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 038dce6e8c..f19373e1a2 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -310,13 +310,14 @@ def test_append_registry(tmp_path): 'TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n' ) + def test_write_registry(tmp_path): + # This can't call StaticFetcher directly for an effective test + # Maybe refactor the file creation into a function handle or fixture files = { "file1.txt": "Molecular \n", "file2.txt": "Dynamics. \n", } - - for filename, content in files.items(): with open(tmp_path / filename, "w") as f: f.write(content) @@ -337,9 +338,70 @@ def test_write_registry(tmp_path): "file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a\n" ) - def test_read_registry(): - pass +def test_append_db(tmp_path): + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + fetcher = StaticFetcher(cache_path=tmp_path) + + file1 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name=REGISTRY_NAME, + ) + + assert fetcher.read_registry(tmp_path / REGISTRY_NAME) == { + "TEST_FILE1.txt": "sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4", + } + + +def test_append_db(tmp_path): + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + fetcher = StaticFetcher(cache_path=tmp_path) + + file1 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name=REGISTRY_NAME, + ) + + file2 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE2.txt", + db_name=REGISTRY_NAME, + append_db=True + ) + + assert fetcher.read_registry(tmp_path / REGISTRY_NAME) == { + "TEST_FILE1.txt": "sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4", + "TEST_FILE2.txt": "sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458" + } + + + +def test_check_registry(tmp_path): + + files = { + "file1.txt": "Molecular \\n", + "file2.txt": "Dynamics. \\n", + "file3.txt": "Analysis. \\n" + } + + for filename, content in files.items(): + with open(tmp_path / filename, "w") as f: + f.write(content) + + fetcher = StaticFetcher(cache_path=tmp_path) + + # Write only file1 + fetcher.write_registry( + tmp_path / "file_1_2_and_3_hash.txt", + [tmp_path / "file1.txt"], + ) - def test_check_registry(): - pass - \ No newline at end of file + assert fetcher.check_registry(tmp_path / "file_1_2_and_3_hash.txt") == [ + tmp_path / "file3.txt", + tmp_path / "file2.txt", + ] From 53b280d0f595fea8d64109528274bf5e408d5461 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 17 Jul 2026 12:30:17 -0700 Subject: [PATCH 063/100] Added ignore flag --- package/MDAnalysis/fetch/fetchers.py | 4 ++- .../fetch/test_static_fetcher.py | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 232ed58ea5..1bcac7cc3f 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -406,6 +406,8 @@ def check_registry(self, db_path, ignore=[]): ... ) >>> fetcher.check_registry("file_1_2_and_3_hash.txt") [Path('./MDAnalysis_pdbs/file3.txt'), Path('./MDAnalysis_pdbs/file2.txt')] + >>> fetcher.check_registry("file_1_2_and_3_hash.txt", ignore=["file2.txt"]) + [Path('./MDAnalysis_pdbs/file3.txt')] @@ -427,7 +429,7 @@ def check_registry(self, db_path, ignore=[]): ) return [ - path for path in cache_files if path.name not in database_files + path for path in cache_files if (path.name not in database_files) and (path not in ignore) ] def read_registry(self, db_path): diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index f19373e1a2..e40c540869 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -405,3 +405,28 @@ def test_check_registry(tmp_path): tmp_path / "file3.txt", tmp_path / "file2.txt", ] + + +def test_check_registry_ignore(tmp_path): + + files = { + "file1.txt": "Molecular \\n", + "file2.txt": "Dynamics. \\n", + "file3.txt": "Analysis. \\n" + } + + for filename, content in files.items(): + with open(tmp_path / filename, "w") as f: + f.write(content) + + fetcher = StaticFetcher(cache_path=tmp_path) + + # Write only file1 + fetcher.write_registry( + tmp_path / "file_1_2_and_3_hash.txt", + [tmp_path / "file1.txt"], + ) + + assert fetcher.check_registry(tmp_path / "file_1_2_and_3_hash.txt", ignore=[tmp_path / "file2.txt"]) == [ + tmp_path / "file3.txt", + ] From 8e5cbcd3bf4b72dc21a166af422910b1ccbf7b0d Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 17 Jul 2026 12:31:29 -0700 Subject: [PATCH 064/100] Applied black --- package/MDAnalysis/fetch/fetchers.py | 45 ++++++------ .../fetch/test_static_fetcher.py | 72 +++++++++++-------- 2 files changed, 66 insertions(+), 51 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 1bcac7cc3f..47b108d8fa 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -213,7 +213,7 @@ def fetch( Examples -------- Download a single CIF file from the RCSB Protein Data Bank. - + >>> StaticFetcher.fetch(file_name="1AKE.cif", base_url="https://files.wwpdb.org/download/") './MDAnalysis_pdbs/1AKE.cif' @@ -262,16 +262,18 @@ def fetch( raise ValueError( "There are unknown files in the registry! The missing files are" + f" {missing_files_list}. To fix this, please set append_db=True" - + " to append the database." + "\n" - + + " to append the database." + + "\n" f"These missing files are {missing_files_list}" ) # Ensure fetch() only get requested files - requested_files = (file_name,) if isinstance(file_name, str) else tuple(file_name) + requested_files = ( + (file_name,) if isinstance(file_name, str) else tuple(file_name) + ) for name in requested_files: registry_dictionary.setdefault(name, None) - + ## ## Download code using pooch @@ -285,7 +287,9 @@ def fetch( download_kwargs = kwargs.copy() download_kwargs.pop("retries") - fetch_downloader = self._set_downloader(base_url, downloader, **download_kwargs) + fetch_downloader = self._set_downloader( + base_url, downloader, **download_kwargs + ) paths = [ Path( @@ -306,7 +310,7 @@ def fetch( if APPEND_DATABASE and LOAD_FROM_CACHE: self.append_registry(db_path, requested_files) - + ## return paths[0] if len(paths) == 1 else paths @@ -379,7 +383,7 @@ def check_registry(self, db_path, ignore=[]): db_path : str or path-like Path to the registry file to read. ignore : list of str or path-like - Files to be ignored + Files to be ignored Returns ------- @@ -405,10 +409,9 @@ def check_registry(self, db_path, ignore=[]): ... files=["file1.txt"], ... ) >>> fetcher.check_registry("file_1_2_and_3_hash.txt") - [Path('./MDAnalysis_pdbs/file3.txt'), Path('./MDAnalysis_pdbs/file2.txt')] + [Path('./MDAnalysis_pdbs/file3.txt'), Path('./MDAnalysis_pdbs/file2.txt')] >>> fetcher.check_registry("file_1_2_and_3_hash.txt", ignore=["file2.txt"]) - [Path('./MDAnalysis_pdbs/file3.txt')] - + [Path('./MDAnalysis_pdbs/file3.txt')] Notes @@ -429,7 +432,9 @@ def check_registry(self, db_path, ignore=[]): ) return [ - path for path in cache_files if (path.name not in database_files) and (path not in ignore) + path + for path in cache_files + if (path.name not in database_files) and (path not in ignore) ] def read_registry(self, db_path): @@ -496,7 +501,7 @@ def write_registry(self, db_path, files, mode="w"): attribute and be readable by ``pooch.file_hash``. mode : str, optional File opening mode used when writing the registry. Default is ``"w"``. - + Returns ------- None @@ -556,15 +561,15 @@ def _check_hash_input(self, hash): def _set_downloader(self, base_url, downloader, **kwargs): """Sets Downloader in fetch() by matching a regex against the download link""" - SUPPORTED_DOWNLOADERS = ('auto', 'http', 'https', 'ftp', 'sftp','doi') + SUPPORTED_DOWNLOADERS = ("auto", "http", "https", "ftp", "sftp", "doi") if downloader not in SUPPORTED_DOWNLOADERS: raise ValueError( - f"Invalid downloader '{downloader}'. Valid options " - + f"are {SUPPORTED_DOWNLOADERS}" - ) + f"Invalid downloader '{downloader}'. Valid options " + + f"are {SUPPORTED_DOWNLOADERS}" + ) - if downloader == 'auto': + if downloader == "auto": # The regex below is AI generated, but the overall idea of using regular expressions # to check the first bit of the url was thought up by me. # @@ -576,14 +581,14 @@ def _set_downloader(self, base_url, downloader, **kwargs): # ftp://ftp.example.com/files/document.txt # sftp://username@example.com/path/to/folder - regex = r'^([^:]+)://' + regex = r"^([^:]+)://" match = re.match(regex, base_url) if match: _downloader = match.group(1) else: _downloader = downloader - + match _downloader: case "http" | "https": return pooch.HTTPDownloader(**kwargs) diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index e40c540869..99cac0bcd5 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -70,8 +70,8 @@ def test_invalid_downloader(self, tmp_path): with pytest.raises( ValueError, match=re.escape( - "Invalid downloader 'barfoo'. Valid options " + - "are ('auto', 'http', 'https', 'ftp', 'sftp', 'doi')" + "Invalid downloader 'barfoo'. Valid options " + + "are ('auto', 'http', 'https', 'ftp', 'sftp', 'doi')" ), ): downloader.fetch( @@ -133,9 +133,9 @@ def test_create_database(self, tmp_path): ) assert path.exists() - assert Path(downloader.cache_path / REGISTRY_NAME ).exists() + assert Path(downloader.cache_path / REGISTRY_NAME).exists() assert ( - downloader.cache_path / REGISTRY_NAME + downloader.cache_path / REGISTRY_NAME ).read_text() == "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" def test_append_database(self, tmp_path): @@ -152,14 +152,13 @@ def test_append_database(self, tmp_path): base_url=base_url, file_name="TEST_FILE2.txt", db_name=REGISTRY_NAME, - append_db=True + append_db=True, ) assert Path(downloader.cache_path / REGISTRY_NAME).read_text() == ( - "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" - "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" - ) - + "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" + ) def test_different_hashes(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): @@ -171,9 +170,10 @@ def test_different_hashes(self, tmp_path): db_name=REGISTRY_NAME, ) - assert Path( - downloader.cache_path / REGISTRY_NAME - ).read_text() == "TEST_FILE1.txt md5:b2f138521297db74b6b280feeb14f9f6\n" + assert ( + Path(downloader.cache_path / REGISTRY_NAME).read_text() + == "TEST_FILE1.txt md5:b2f138521297db74b6b280feeb14f9f6\n" + ) def test_existing_database(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): @@ -274,7 +274,7 @@ def test_environment_variable_override(self, tmp_path, monkeypatch): with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" downloader = StaticFetcher() - + path = downloader.fetch( base_url=base_url, file_name="TEST_FILE1.txt", @@ -285,7 +285,6 @@ def test_environment_variable_override(self, tmp_path, monkeypatch): def test_append_registry(tmp_path): - with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" fetcher = StaticFetcher(cache_path=tmp_path) @@ -306,14 +305,14 @@ def test_append_registry(tmp_path): fetcher.append_registry(registry, ["TEST_FILE2.txt"]) assert Path(registry).read_text() == ( - 'TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n' - 'TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n' + "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" ) def test_write_registry(tmp_path): # This can't call StaticFetcher directly for an effective test - # Maybe refactor the file creation into a function handle or fixture + # Maybe refactor the file creation into a function handle or fixture files = { "file1.txt": "Molecular \n", "file2.txt": "Dynamics. \n", @@ -322,22 +321,32 @@ def test_write_registry(tmp_path): with open(tmp_path / filename, "w") as f: f.write(content) - fetcher = StaticFetcher(cache_path=tmp_path) - fetcher.write_registry(tmp_path / "file_1_hash.txt", [tmp_path / "file1.txt"]) - assert (tmp_path / "file_1_hash.txt").read_text() == 'file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20\n' + fetcher.write_registry( + tmp_path / "file_1_hash.txt", [tmp_path / "file1.txt"] + ) + assert ( + tmp_path / "file_1_hash.txt" + ).read_text() == "file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20\n" - fetcher.write_registry(tmp_path / "file_2_hash.txt", [tmp_path / "file2.txt"]) - assert (tmp_path / "file_2_hash.txt").read_text() == 'file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a\n' + fetcher.write_registry( + tmp_path / "file_2_hash.txt", [tmp_path / "file2.txt"] + ) + assert ( + tmp_path / "file_2_hash.txt" + ).read_text() == "file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a\n" - - fetcher.write_registry(tmp_path / "file_1_and_2_hash.txt", [tmp_path / "file1.txt", tmp_path / "file2.txt"]) + fetcher.write_registry( + tmp_path / "file_1_and_2_hash.txt", + [tmp_path / "file1.txt", tmp_path / "file2.txt"], + ) assert (tmp_path / "file_1_and_2_hash.txt").read_text() == ( "file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20\n" "file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a\n" ) + def test_append_db(tmp_path): with temporary_http_server() as (host, port, temp_folder): @@ -371,22 +380,21 @@ def test_append_db(tmp_path): base_url=base_url, file_name="TEST_FILE2.txt", db_name=REGISTRY_NAME, - append_db=True + append_db=True, ) assert fetcher.read_registry(tmp_path / REGISTRY_NAME) == { "TEST_FILE1.txt": "sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4", - "TEST_FILE2.txt": "sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458" + "TEST_FILE2.txt": "sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458", } - def test_check_registry(tmp_path): files = { "file1.txt": "Molecular \\n", "file2.txt": "Dynamics. \\n", - "file3.txt": "Analysis. \\n" + "file3.txt": "Analysis. \\n", } for filename, content in files.items(): @@ -403,7 +411,7 @@ def test_check_registry(tmp_path): assert fetcher.check_registry(tmp_path / "file_1_2_and_3_hash.txt") == [ tmp_path / "file3.txt", - tmp_path / "file2.txt", + tmp_path / "file2.txt", ] @@ -412,7 +420,7 @@ def test_check_registry_ignore(tmp_path): files = { "file1.txt": "Molecular \\n", "file2.txt": "Dynamics. \\n", - "file3.txt": "Analysis. \\n" + "file3.txt": "Analysis. \\n", } for filename, content in files.items(): @@ -427,6 +435,8 @@ def test_check_registry_ignore(tmp_path): [tmp_path / "file1.txt"], ) - assert fetcher.check_registry(tmp_path / "file_1_2_and_3_hash.txt", ignore=[tmp_path / "file2.txt"]) == [ + assert fetcher.check_registry( + tmp_path / "file_1_2_and_3_hash.txt", ignore=[tmp_path / "file2.txt"] + ) == [ tmp_path / "file3.txt", ] From 288e66a060de48333bc4fd881342114cd54b1c7c Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 17 Jul 2026 14:38:14 -0700 Subject: [PATCH 065/100] fixed value error --- package/MDAnalysis/fetch/fetchers.py | 34 ++++++++++--------- .../fetch/test_static_fetcher.py | 21 ++++++++++++ 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 47b108d8fa..c0b0806072 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -240,9 +240,19 @@ def fetch( MISSING_FILES = False APPEND_DATABASE = append_db - ## Reading from Registry + registry_dictionary = {} + # Process file names + requested_files = ( + (file_name,) if isinstance(file_name, str) else tuple(file_name) + ) + requested_files_abs = [self.cache_path / name for name in requested_files] + for name in requested_files: + registry_dictionary.setdefault(name, None) + + + ## Reading from Registry if db_name is not None: db_path = self.cache_path / Path(db_name) @@ -253,26 +263,18 @@ def fetch( if LOAD_FROM_CACHE: registry_dictionary = self.read_registry(db_path) - missing_files_list = self.check_registry(db_path) + missing_files_list = self.check_registry(db_path, files=list(requested_files_abs)) if len(missing_files_list) != 0: MISSING_FILES = True if MISSING_FILES and not APPEND_DATABASE: raise ValueError( - "There are unknown files in the registry! The missing files are" - + f" {missing_files_list}. To fix this, please set append_db=True" - + " to append the database." - + "\n" - f"These missing files are {missing_files_list}" + "fetch() is requesting files not found in the registry. " + + f"The missing files are {missing_files_list}. " + + "To fix this, please set append_db=True to append the registry." ) - # Ensure fetch() only get requested files - requested_files = ( - (file_name,) if isinstance(file_name, str) else tuple(file_name) - ) - for name in requested_files: - registry_dictionary.setdefault(name, None) ## @@ -370,7 +372,7 @@ def append_registry(self, db_path, files): new_files = [self.cache_path / file_name for file_name in files] self.write_registry(Path(db_path), new_files, mode="a") - def check_registry(self, db_path, ignore=[]): + def check_registry(self, db_path, files=[], ignore=[]): """ Return cache files that are missing from the registry. @@ -425,11 +427,11 @@ def check_registry(self, db_path, ignore=[]): registry_dictionary = self.read_registry(db_path) database_files = set(registry_dictionary.keys()) - cache_files = ( + cache_files = [ path for path in self.cache_path.rglob("*") if path != db_path and path.is_file() - ) + ] + files return [ path diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 99cac0bcd5..374a4c6620 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -440,3 +440,24 @@ def test_check_registry_ignore(tmp_path): ) == [ tmp_path / "file3.txt", ] + + +def test_error(tmp_path): + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + fetcher = StaticFetcher(cache_path=tmp_path) + + file1 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name=REGISTRY_NAME, + ) + # match later + + with pytest.raises(ValueError): + file2 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE2.txt", + db_name=REGISTRY_NAME, + ) From 654bebf1b0e4845c390588681fc0ba13030c3542 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 17 Jul 2026 14:55:45 -0700 Subject: [PATCH 066/100] working tests --- package/MDAnalysis/fetch/fetchers.py | 20 +- .../fetch/test_static_fetcher.py | 259 ++++++++---------- 2 files changed, 120 insertions(+), 159 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index c0b0806072..d2c4a2f981 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -240,18 +240,16 @@ def fetch( MISSING_FILES = False APPEND_DATABASE = append_db - registry_dictionary = {} # Process file names requested_files = ( (file_name,) if isinstance(file_name, str) else tuple(file_name) ) - requested_files_abs = [self.cache_path / name for name in requested_files] - for name in requested_files: - registry_dictionary.setdefault(name, None) + requested_files_abs = [ + self.cache_path / name for name in requested_files + ] - ## Reading from Registry if db_name is not None: db_path = self.cache_path / Path(db_name) @@ -263,18 +261,22 @@ def fetch( if LOAD_FROM_CACHE: registry_dictionary = self.read_registry(db_path) - missing_files_list = self.check_registry(db_path, files=list(requested_files_abs)) + missing_files_list = self.check_registry( + db_path, files=list(requested_files_abs) + ) if len(missing_files_list) != 0: MISSING_FILES = True if MISSING_FILES and not APPEND_DATABASE: raise ValueError( - "fetch() is requesting files not found in the registry. " + - f"The missing files are {missing_files_list}. " + - "To fix this, please set append_db=True to append the registry." + "fetch() is requesting files not found in the registry. " + + f"The missing files are {missing_files_list}. " + + "To fix this, please set append_db=True to append the registry." ) + for name in requested_files: + registry_dictionary.setdefault(name, None) ## diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 374a4c6620..1efdbfdbc1 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -94,6 +94,26 @@ def test_invalid_hash(self, tmp_path): ): downloader = StaticFetcher(cache_path=tmp_path, hash=hash) + def test_append_error(self, tmp_path): + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + fetcher = StaticFetcher(cache_path=tmp_path) + + file1 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name=REGISTRY_NAME, + ) + + # match later + with pytest.raises(ValueError): + file2 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE2.txt", + db_name=REGISTRY_NAME, + ) + @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") class TestExpectedBehaviors: @@ -283,181 +303,120 @@ def test_environment_variable_override(self, tmp_path, monkeypatch): assert Path(tmp_path / "TEST_FILE1.txt").exists() -def test_append_registry(tmp_path): - - with temporary_http_server() as (host, port, temp_folder): - base_url = f"http://{host}:{port}/" - fetcher = StaticFetcher(cache_path=tmp_path) - - file1 = fetcher.fetch( - base_url=base_url, - file_name="TEST_FILE1.txt", - db_name="db_hash1.txt", - ) - - file2 = fetcher.fetch( - base_url=base_url, - file_name="TEST_FILE2.txt", - db_name="db_hash2.txt", - ) +@pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") +class TestRegistry: + def test_append_registry(self, tmp_path): - registry = file1.parent / "db_hash1.txt" - fetcher.append_registry(registry, ["TEST_FILE2.txt"]) + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + fetcher = StaticFetcher(cache_path=tmp_path) - assert Path(registry).read_text() == ( - "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" - "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" - ) + file1 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name="db_hash1.txt", + ) + file2 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE2.txt", + db_name="db_hash2.txt", + ) -def test_write_registry(tmp_path): - # This can't call StaticFetcher directly for an effective test - # Maybe refactor the file creation into a function handle or fixture - files = { - "file1.txt": "Molecular \n", - "file2.txt": "Dynamics. \n", - } - for filename, content in files.items(): - with open(tmp_path / filename, "w") as f: - f.write(content) - - fetcher = StaticFetcher(cache_path=tmp_path) - - fetcher.write_registry( - tmp_path / "file_1_hash.txt", [tmp_path / "file1.txt"] - ) - assert ( - tmp_path / "file_1_hash.txt" - ).read_text() == "file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20\n" - - fetcher.write_registry( - tmp_path / "file_2_hash.txt", [tmp_path / "file2.txt"] - ) - assert ( - tmp_path / "file_2_hash.txt" - ).read_text() == "file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a\n" - - fetcher.write_registry( - tmp_path / "file_1_and_2_hash.txt", - [tmp_path / "file1.txt", tmp_path / "file2.txt"], - ) - assert (tmp_path / "file_1_and_2_hash.txt").read_text() == ( - "file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20\n" - "file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a\n" - ) - - -def test_append_db(tmp_path): - - with temporary_http_server() as (host, port, temp_folder): - base_url = f"http://{host}:{port}/" - fetcher = StaticFetcher(cache_path=tmp_path) + registry = file1.parent / "db_hash1.txt" + fetcher.append_registry(registry, ["TEST_FILE2.txt"]) - file1 = fetcher.fetch( - base_url=base_url, - file_name="TEST_FILE1.txt", - db_name=REGISTRY_NAME, - ) + assert Path(registry).read_text() == ( + "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" + ) - assert fetcher.read_registry(tmp_path / REGISTRY_NAME) == { - "TEST_FILE1.txt": "sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4", + def test_write_registry(self, tmp_path): + # This can't call StaticFetcher directly for an effective test + # Maybe refactor the file creation into a function handle or fixture + files = { + "file1.txt": "Molecular \n", + "file2.txt": "Dynamics. \n", } + for filename, content in files.items(): + with open(tmp_path / filename, "w") as f: + f.write(content) - -def test_append_db(tmp_path): - - with temporary_http_server() as (host, port, temp_folder): - base_url = f"http://{host}:{port}/" fetcher = StaticFetcher(cache_path=tmp_path) - file1 = fetcher.fetch( - base_url=base_url, - file_name="TEST_FILE1.txt", - db_name=REGISTRY_NAME, + fetcher.write_registry( + tmp_path / "file_1_hash.txt", [tmp_path / "file1.txt"] ) + assert ( + tmp_path / "file_1_hash.txt" + ).read_text() == "file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20\n" - file2 = fetcher.fetch( - base_url=base_url, - file_name="TEST_FILE2.txt", - db_name=REGISTRY_NAME, - append_db=True, + fetcher.write_registry( + tmp_path / "file_2_hash.txt", [tmp_path / "file2.txt"] ) + assert ( + tmp_path / "file_2_hash.txt" + ).read_text() == "file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a\n" - assert fetcher.read_registry(tmp_path / REGISTRY_NAME) == { - "TEST_FILE1.txt": "sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4", - "TEST_FILE2.txt": "sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458", - } - - -def test_check_registry(tmp_path): - - files = { - "file1.txt": "Molecular \\n", - "file2.txt": "Dynamics. \\n", - "file3.txt": "Analysis. \\n", - } - - for filename, content in files.items(): - with open(tmp_path / filename, "w") as f: - f.write(content) - - fetcher = StaticFetcher(cache_path=tmp_path) - - # Write only file1 - fetcher.write_registry( - tmp_path / "file_1_2_and_3_hash.txt", - [tmp_path / "file1.txt"], - ) - - assert fetcher.check_registry(tmp_path / "file_1_2_and_3_hash.txt") == [ - tmp_path / "file3.txt", - tmp_path / "file2.txt", - ] + fetcher.write_registry( + tmp_path / "file_1_and_2_hash.txt", + [tmp_path / "file1.txt", tmp_path / "file2.txt"], + ) + assert (tmp_path / "file_1_and_2_hash.txt").read_text() == ( + "file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20\n" + "file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a\n" + ) + def test_check_registry(self, tmp_path): -def test_check_registry_ignore(tmp_path): + files = { + "file1.txt": "Molecular \\n", + "file2.txt": "Dynamics. \\n", + "file3.txt": "Analysis. \\n", + } - files = { - "file1.txt": "Molecular \\n", - "file2.txt": "Dynamics. \\n", - "file3.txt": "Analysis. \\n", - } + for filename, content in files.items(): + with open(tmp_path / filename, "w") as f: + f.write(content) - for filename, content in files.items(): - with open(tmp_path / filename, "w") as f: - f.write(content) + fetcher = StaticFetcher(cache_path=tmp_path) - fetcher = StaticFetcher(cache_path=tmp_path) + # Write only file1 + fetcher.write_registry( + tmp_path / "file_1_2_and_3_hash.txt", + [tmp_path / "file1.txt"], + ) - # Write only file1 - fetcher.write_registry( - tmp_path / "file_1_2_and_3_hash.txt", - [tmp_path / "file1.txt"], - ) + assert fetcher.check_registry( + tmp_path / "file_1_2_and_3_hash.txt" + ) == [ + tmp_path / "file3.txt", + tmp_path / "file2.txt", + ] - assert fetcher.check_registry( - tmp_path / "file_1_2_and_3_hash.txt", ignore=[tmp_path / "file2.txt"] - ) == [ - tmp_path / "file3.txt", - ] + def test_check_registry_ignore(self, tmp_path): + files = { + "file1.txt": "Molecular \\n", + "file2.txt": "Dynamics. \\n", + "file3.txt": "Analysis. \\n", + } -def test_error(tmp_path): + for filename, content in files.items(): + with open(tmp_path / filename, "w") as f: + f.write(content) - with temporary_http_server() as (host, port, temp_folder): - base_url = f"http://{host}:{port}/" fetcher = StaticFetcher(cache_path=tmp_path) - file1 = fetcher.fetch( - base_url=base_url, - file_name="TEST_FILE1.txt", - db_name=REGISTRY_NAME, + # Write only file1 + fetcher.write_registry( + tmp_path / "file_1_2_and_3_hash.txt", + [tmp_path / "file1.txt"], ) - # match later - with pytest.raises(ValueError): - file2 = fetcher.fetch( - base_url=base_url, - file_name="TEST_FILE2.txt", - db_name=REGISTRY_NAME, - ) + assert fetcher.check_registry( + tmp_path / "file_1_2_and_3_hash.txt", + ignore=[tmp_path / "file2.txt"], + ) == [ + tmp_path / "file3.txt", + ] From 2dff5e971530bfb5cb4862f118799642f316765b Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 17 Jul 2026 15:07:08 -0700 Subject: [PATCH 067/100] Pre-AI review docstring --- package/MDAnalysis/fetch/fetchers.py | 44 ++++++++++++++++------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index d2c4a2f981..bb31c92e97 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -262,7 +262,7 @@ def fetch( if LOAD_FROM_CACHE: registry_dictionary = self.read_registry(db_path) missing_files_list = self.check_registry( - db_path, files=list(requested_files_abs) + db_path, files=list(requested_files) ) if len(missing_files_list) != 0: @@ -376,7 +376,7 @@ def append_registry(self, db_path, files): def check_registry(self, db_path, files=[], ignore=[]): """ - Return cache files that are missing from the registry. + Return absolute paths to cache files that are missing from the registry. This method compares filenames within the registry against files found recursively under :attr:`cache_path`. A cache file is considered missing @@ -386,8 +386,12 @@ def check_registry(self, db_path, files=[], ignore=[]): ---------- db_path : str or path-like Path to the registry file to read. + files : list of pathlib.Path + Paths to additional files to check. Each path must be relative to + :attr:`cache_path`. ignore : list of str or path-like - Files to be ignored + Files to be ignored. Each path must be relative to + :attr:`cache_path`. Returns ------- @@ -404,10 +408,10 @@ def check_registry(self, db_path, files=[], ignore=[]): ... "file3.txt": "Analysis. \\n" ... } >>> for filename, content in files.items(): - ... with open(tmp_path / filename, "w") as f: + ... with open(filename, "w") as f: ... f.write(content) ... - >>> fetcher = StaticFetcher(cache_path=tmp_path) + >>> fetcher = StaticFetcher() >>> fetcher.write_registry( ... "file_1_2_and_3_hash.txt", ... files=["file1.txt"], @@ -433,7 +437,7 @@ def check_registry(self, db_path, files=[], ignore=[]): path for path in self.cache_path.rglob("*") if path != db_path and path.is_file() - ] + files + ] + [self.cache_path / file_name for file_name in files] return [ path @@ -445,6 +449,10 @@ def read_registry(self, db_path): """ Read a Pooch registry file into a dictionary. + This method returns filenames within the registry against files found + recursively under :attr:`cache_path`. Each key in the returned dictionary + corresponds to a filename in the registry relative to :attr:`cache_path`. + Parameters ---------- db_path : str or path-like @@ -466,10 +474,10 @@ def read_registry(self, db_path): ... "file2.txt": "Dynamics. \\n", ... } >>> for filename, content in files.items(): - ... with open(tmp_path / filename, "w") as f: + ... with open(filename, "w") as f: ... f.write(content) ... - >>> fetcher = StaticFetcher(cache_path=tmp_path) + >>> fetcher = StaticFetcher() >>> fetcher.write_registry( ... "file_1_and_2_hash.txt", ... ["file1.txt", "file2.txt"], @@ -477,6 +485,7 @@ def read_registry(self, db_path): >>> fetcher.read_registry("file_1_and_2_hash.txt") {'file1.txt': 'sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20', 'file2.txt': 'sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a'} + Notes ----- Each line in the registry file is expected to have the format:: @@ -496,13 +505,17 @@ def write_registry(self, db_path, files, mode="w"): """ Write a Pooch registry file with hashes for the given files. + This method computes the hash for each file and writes it to the registry file. + The registry file maps each filename to its corresponding hash value. The hash + algorithm used is determined by the :attr:`hash` of the fetcher. + Parameters ---------- db_path : str or path-like Path to the registry file to write. files : iterable of str or path-like - Files to include in the registry. Each file must provide a ``name`` - attribute and be readable by ``pooch.file_hash``. + Files to be include in the registry. Each file must be relative + to :attr:`cache_path`. mode : str, optional File opening mode used when writing the registry. Default is ``"w"``. @@ -519,10 +532,10 @@ def write_registry(self, db_path, files, mode="w"): ... "file2.txt": "Dynamics. \\n", ... } >>> for filename, content in files.items(): - ... with open(tmp_path / filename, "w") as f: + ... with open(filename, "w") as f: ... f.write(content) ... - >>> fetcher = StaticFetcher(cache_path=tmp_path) + >>> fetcher = StaticFetcher() >>> fetcher.write_registry( ... "file_1_and_2_hash.txt", ... ["file1.txt", "file2.txt"], @@ -602,9 +615,4 @@ def _set_downloader(self, base_url, downloader, **kwargs): return pooch.SFTPDownloader(**kwargs) case "doi": return pooch.DOIDownloader(**kwargs) - - -# class DynamicFetcher(_BaseFetcher): -# """Fetcher yields a Python Generator for dynamic downloading and analysis""" - -# raise NotImplementedError + \ No newline at end of file From c0548cb414cae6ed6732fb3639719115db3f739f Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 17 Jul 2026 15:24:38 -0700 Subject: [PATCH 068/100] refactor environment override --- package/MDAnalysis/fetch/fetchers.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index bb31c92e97..58c4f65719 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -50,6 +50,7 @@ import hashlib import re +import os from pathlib import Path from abc import ABC, abstractmethod @@ -286,7 +287,6 @@ def fetch( base_url=base_url, registry=registry_dictionary, retry_if_failed=kwargs["retries"], - env="MDANALYSIS_FETCHER_DATA", ) download_kwargs = kwargs.copy() @@ -376,11 +376,11 @@ def append_registry(self, db_path, files): def check_registry(self, db_path, files=[], ignore=[]): """ - Return absolute paths to cache files that are missing from the registry. + Return paths relative to :attr:`cache_path` for cache files that are missing from the registry. This method compares filenames within the registry against files found recursively under :attr:`cache_path`. A cache file is considered missing - when it is recorded in the registry, but not physically present on disk. + when it is on disk, but it is not recorded in the registry. Parameters ---------- @@ -389,7 +389,7 @@ def check_registry(self, db_path, files=[], ignore=[]): files : list of pathlib.Path Paths to additional files to check. Each path must be relative to :attr:`cache_path`. - ignore : list of str or path-like + ignore : list of pathlib.Path Files to be ignored. Each path must be relative to :attr:`cache_path`. @@ -485,7 +485,7 @@ def read_registry(self, db_path): >>> fetcher.read_registry("file_1_and_2_hash.txt") {'file1.txt': 'sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20', 'file2.txt': 'sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a'} - + Notes ----- Each line in the registry file is expected to have the format:: @@ -558,11 +558,16 @@ def write_registry(self, db_path, files, mode="w"): # Argument validation methods def _check_cache_path_input(self, cache_path): + if cache_path is None: path = Path(pooch.os_cache(DEFAULT_CACHE_NAME_DOWNLOADER)) else: path = Path(cache_path) + # Environment variable override + if not os.environ.get("MDANALYSIS_FETCHER_DATA") is None: + path = Path(os.environ.get("MDANALYSIS_FETCHER_DATA")) + Path(path).mkdir(parents=True, exist_ok=True) return path @@ -615,4 +620,3 @@ def _set_downloader(self, base_url, downloader, **kwargs): return pooch.SFTPDownloader(**kwargs) case "doi": return pooch.DOIDownloader(**kwargs) - \ No newline at end of file From e131b2864f3c79a42eda7189060453ee89a56338 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Mon, 20 Jul 2026 12:02:10 -0700 Subject: [PATCH 069/100] added version --- package/MDAnalysis/fetch/fetchers.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 58c4f65719..86ddfd684d 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -88,6 +88,8 @@ class _BaseFetcher(ABC): This class should not be initialized directly; fetcher implementations should inherit from it. + .. versionadded:: 2.11.0 + """ def __init__( @@ -154,6 +156,8 @@ class StaticFetcher(_BaseFetcher): variable ``MDANALYSIS_FETCHER_DATA`` to a valid path. This class uses :mod:`pooch` as a backend for downloading and caching files. + .. versionadded:: 2.11.0 + """ def __init__(self, cache_path=None, hash="sha256"): @@ -232,6 +236,8 @@ def fetch( cache database is created on demand when ``db_name`` does not exist. + .. versionadded:: 2.11.0 + """ # Keywords arguments that are reserved for common _BaseFetcher.fetch() arguments. kwargs = self._validate_fetch_args(kwargs) @@ -370,6 +376,8 @@ def append_registry(self, db_path, files): : + .. versionadded:: 2.11.0 + """ new_files = [self.cache_path / file_name for file_name in files] self.write_registry(Path(db_path), new_files, mode="a") @@ -491,6 +499,9 @@ def read_registry(self, db_path): Each line in the registry file is expected to have the format:: : + + .. versionadded:: 2.11.0 + """ hash_dict = {} @@ -549,6 +560,9 @@ def write_registry(self, db_path, files, mode="w"): Each registry line is written in the format:: : + + .. versionadded:: 2.11.0 + """ with open(db_path, mode=mode) as f: for file in files: From 89367ad728350d10d883b361ebbb1d5f67caeffe Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Mon, 20 Jul 2026 12:51:21 -0700 Subject: [PATCH 070/100] Pass by hand --- package/MDAnalysis/fetch/fetchers.py | 29 +++++++++++-------- package/MDAnalysis/fetch/pdb.py | 12 ++------ .../MDAnalysisTests/fetch/test_from_PDB.py | 4 +-- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 86ddfd684d..2763a3ee67 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -25,7 +25,7 @@ Fetchers --- :mod:`MDAnalysis.fetch.fetchers` ============================================= -This module contains fetcher classes that retrieve files from remote servers. +This module contains Fetcher classes which are able to retrieve files from remote servers. These classes use the third-party library :mod:`pooch` as a dependency. Classes @@ -38,8 +38,8 @@ Variables --------- -These module-level variables affect runtime behavior across all fetcher classes. -Changing these values affects all fetchers. +These module-level variables affect the runtime behavior across all Fetcher classes. +Changing these values affects all initalized Fetchers. .. autodata:: DEFAULT_CACHE_NAME_DOWNLOADER @@ -130,13 +130,13 @@ class StaticFetcher(_BaseFetcher): Parameters ---------- - cache_path : str or pathlib.Path, optional + cache_path : str or pathlib.Path Path to the cache directory. If set to ``None``, the default cache directory will be used as specified by :data:`DEFAULT_CACHE_NAME_DOWNLOADER`. - If the directory does not exist, it will be created. + If the directory does not exist, it will attempt to be created. - hash : str, optional + hash : str Hash algorithm to use for verifying the integrity of downloaded files. The default is ``sha256``. Valid options are any hash algorithm available in the :mod:`hashlib` module. @@ -181,6 +181,14 @@ def fetch( Download one or more files from a static base URL and cache them locally. + Primarily designed to be working with `FAIR`_ + databases, this method works by sending a request to a web server and caching them to a registry. + The registry is in the format of a `pooch registry file`_, and it will be created or read relative to + :attr:`cache_path`. + + .. _FAIR: https://www.nature.com/articles/s41592-025-02635-0 + .. _`pooch registry file`: https://www.fatiando.org/pooch/latest/registry-files.html#registry-file-format + Parameters ---------- base_url : str @@ -234,7 +242,7 @@ def fetch( variable ``MDANALYSIS_FETCHER_DATA`` to a valid path. This class uses :mod:`pooch` as a backend for downloading and caching files. The cache database is created on demand when ``db_name`` does not - exist. + exist relative to :attr:`cache_path`. .. versionadded:: 2.11.0 @@ -253,9 +261,6 @@ def fetch( requested_files = ( (file_name,) if isinstance(file_name, str) else tuple(file_name) ) - requested_files_abs = [ - self.cache_path / name for name in requested_files - ] ## Reading from Registry if db_name is not None: @@ -562,7 +567,7 @@ def write_registry(self, db_path, files, mode="w"): : .. versionadded:: 2.11.0 - + """ with open(db_path, mode=mode) as f: for file in files: @@ -578,7 +583,7 @@ def _check_cache_path_input(self, cache_path): else: path = Path(cache_path) - # Environment variable override + # Environment variable master override if not os.environ.get("MDANALYSIS_FETCHER_DATA") is None: path = Path(os.environ.get("MDANALYSIS_FETCHER_DATA")) diff --git a/package/MDAnalysis/fetch/pdb.py b/package/MDAnalysis/fetch/pdb.py index dcd336c74d..1fdecc6cb8 100644 --- a/package/MDAnalysis/fetch/pdb.py +++ b/package/MDAnalysis/fetch/pdb.py @@ -30,12 +30,6 @@ .. _Protein Data Batabank: https://www.rcsb.org/ -Variables ---------- - -.. autodata:: DEFAULT_CACHE_NAME_DOWNLOADER - - Functions --------- @@ -46,7 +40,7 @@ from .fetchers import StaticFetcher # These file formats are here https://www.rcsb.org/docs/programmatic-access/file-download-services#pdb-entry-files" -SUPPORTED_FILE_FORMATS_DOWNLOADER = ( +_SUPPORTED_FILE_FORMATS_PDB = ( "cif", "cif.gz", "bcif", @@ -150,10 +144,10 @@ def from_PDB( .. versionadded:: 2.11.0 """ - if file_format not in SUPPORTED_FILE_FORMATS_DOWNLOADER: + if file_format not in _SUPPORTED_FILE_FORMATS_PDB: raise ValueError( "Invalid file format. Supported file formats " - f"are {SUPPORTED_FILE_FORMATS_DOWNLOADER}" + f"are {_SUPPORTED_FILE_FORMATS_PDB}" ) pdb_ids = [pdb + "." + file_format for pdb in pdb_ids] diff --git a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py index dc644e8fc0..afead3548d 100644 --- a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py +++ b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py @@ -27,7 +27,7 @@ import re from MDAnalysis.fetch.fetchers import HAS_POOCH -from MDAnalysis.fetch.pdb import SUPPORTED_FILE_FORMATS_DOWNLOADER +from MDAnalysis.fetch.pdb import _SUPPORTED_FILE_FORMATS_PDB from urllib import request from pathlib import Path @@ -86,7 +86,7 @@ def test_invalid_file_format(tmp_path): ValueError, match=re.escape( "Invalid file format. Supported file formats " - f"are {SUPPORTED_FILE_FORMATS_DOWNLOADER}" + f"are {_SUPPORTED_FILE_FORMATS_PDB}" ), ): mda.fetch.from_PDB( From db3324271a6881c35a0c7e79a2f2ca403ffdff3a Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 21 Jul 2026 13:28:02 -0700 Subject: [PATCH 071/100] updated regex --- package/MDAnalysis/fetch/fetchers.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 2763a3ee67..cc888dbf62 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -183,7 +183,7 @@ def fetch( Primarily designed to be working with `FAIR`_ databases, this method works by sending a request to a web server and caching them to a registry. - The registry is in the format of a `pooch registry file`_, and it will be created or read relative to + The registry is in the format of a `pooch registry file`_, and it will be created or read relative to :attr:`cache_path`. .. _FAIR: https://www.nature.com/articles/s41592-025-02635-0 @@ -610,23 +610,14 @@ def _set_downloader(self, base_url, downloader, **kwargs): + f"are {SUPPORTED_DOWNLOADERS}" ) + # Regex matching if downloader is set to auto if downloader == "auto": - # The regex below is AI generated, but the overall idea of using regular expressions - # to check the first bit of the url was thought up by me. - # - # I thought of these four examples and prompt AI to come with a regex that capture the - # susbtring before "://"" - # - # doi://10.6084/m9.figshare.14763051.v1/tiny-data.txt - # https://www.example.com/page - # ftp://ftp.example.com/files/document.txt - # sftp://username@example.com/path/to/folder - - regex = r"^([^:]+)://" + regex = r"^([^:]+):" match = re.match(regex, base_url) if match: _downloader = match.group(1) + else: _downloader = downloader From 25fe27d6570a4d9b122ae62890b4745d4065a849 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Fri, 24 Jul 2026 15:31:07 -0700 Subject: [PATCH 072/100] allowed from_pdb() to accept strings --- package/MDAnalysis/fetch/pdb.py | 7 +++++-- testsuite/MDAnalysisTests/fetch/test_from_PDB.py | 11 +++++++++++ .../MDAnalysisTests/fetch/test_static_fetcher.py | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/package/MDAnalysis/fetch/pdb.py b/package/MDAnalysis/fetch/pdb.py index 1fdecc6cb8..9c4e05f77f 100644 --- a/package/MDAnalysis/fetch/pdb.py +++ b/package/MDAnalysis/fetch/pdb.py @@ -150,11 +150,14 @@ def from_PDB( f"are {_SUPPORTED_FILE_FORMATS_PDB}" ) - pdb_ids = [pdb + "." + file_format for pdb in pdb_ids] + if isinstance(pdb_ids, str): + _pdb_ids = (pdb_ids + "." + file_format,) + else: + _pdb_ids = [pdb + "." + file_format for pdb in pdb_ids] fetcher = StaticFetcher(cache_path=cache_path) return fetcher.fetch( - file_name=pdb_ids, + file_name=_pdb_ids, base_url="https://files.wwpdb.org/download/", progressbar=progressbar, ) diff --git a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py index afead3548d..9ec52b54b0 100644 --- a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py +++ b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py @@ -37,6 +37,17 @@ except request.URLError: HAS_ACCESS_TO_WWPDB = False +@pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") +@pytest.mark.skipif( + not HAS_ACCESS_TO_WWPDB, + reason="Can not connect to https://files.wwpdb.org/", +) +def test_download_one_file_str(tmp_path): + + path = mda.fetch.from_PDB("1AKE", cache_path=tmp_path) + assert path.exists() + assert path.name == "1AKE.cif.gz" + @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") @pytest.mark.skipif( diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 1efdbfdbc1..9fdefd4062 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -387,6 +387,7 @@ def test_check_registry(self, tmp_path): [tmp_path / "file1.txt"], ) + # Show that file2 and file3 are missing assert fetcher.check_registry( tmp_path / "file_1_2_and_3_hash.txt" ) == [ From 8d386f33f6eeda46e44b68ec3689f2b8fd6b5fab Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 28 Jul 2026 13:22:17 -0700 Subject: [PATCH 073/100] getting test to work --- .gitignore | 2 +- .../fetch/test_static_fetcher.py | 55 ++++++++++--------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 41a19aa709..b0fac3d19e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # for rn package/doc/sphinx/build_docs.sh testsuite/MDAnalysisTests/fetch/run_tests.sh - +package/MDAnalysis/TEST.py # Ignore python bytecoded files *.py[cod] diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 9fdefd4062..57d3c5e73a 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -367,33 +367,34 @@ def test_write_registry(self, tmp_path): "file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a\n" ) - def test_check_registry(self, tmp_path): - - files = { - "file1.txt": "Molecular \\n", - "file2.txt": "Dynamics. \\n", - "file3.txt": "Analysis. \\n", - } - - for filename, content in files.items(): - with open(tmp_path / filename, "w") as f: - f.write(content) - - fetcher = StaticFetcher(cache_path=tmp_path) - - # Write only file1 - fetcher.write_registry( - tmp_path / "file_1_2_and_3_hash.txt", - [tmp_path / "file1.txt"], - ) - - # Show that file2 and file3 are missing - assert fetcher.check_registry( - tmp_path / "file_1_2_and_3_hash.txt" - ) == [ - tmp_path / "file3.txt", - tmp_path / "file2.txt", - ] + ## Test works, but doesn't work on github action. Idk why and need to find out at some point. + # def test_check_registry(self, tmp_path): + + # files = { + # "file1.txt": "Molecular \\n", + # "file2.txt": "Dynamics. \\n", + # "file3.txt": "Analysis. \\n", + # } + + # for filename, content in files.items(): + # with open(tmp_path / filename, "w") as f: + # f.write(content) + + # fetcher = StaticFetcher(cache_path=tmp_path) + + # # Write only file1 + # fetcher.write_registry( + # tmp_path / "file_1_2_and_3_hash.txt", + # [tmp_path / "file1.txt"], + # ) + + # # Show that file2 and file3 are missing + # assert fetcher.check_registry( + # tmp_path / "file_1_2_and_3_hash.txt" + # ) == [ + # tmp_path / "file3.txt", + # tmp_path / "file2.txt", + # ] def test_check_registry_ignore(self, tmp_path): From 71b9826d7e2ee6f08db0897be81ea2d7c3eacd1d Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 28 Jul 2026 13:50:55 -0700 Subject: [PATCH 074/100] added downloader stuff' --- package/MDAnalysis/fetch/fetchers.py | 10 ++++++++ .../fetch/test_static_fetcher.py | 25 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index cc888dbf62..74df631709 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -617,6 +617,10 @@ def _set_downloader(self, base_url, downloader, **kwargs): if match: _downloader = match.group(1) + else: + raise ValueError( + f"Unable to determine downloader for URL '{base_url}'." + ) else: _downloader = downloader @@ -630,3 +634,9 @@ def _set_downloader(self, base_url, downloader, **kwargs): return pooch.SFTPDownloader(**kwargs) case "doi": return pooch.DOIDownloader(**kwargs) + case _: + raise ValueError( + f"Invalid downloader '{_downloader}'. Valid options " + + f"are {SUPPORTED_DOWNLOADERS}" + ) + diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 57d3c5e73a..9eb8c543f4 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -422,3 +422,28 @@ def test_check_registry_ignore(self, tmp_path): ) == [ tmp_path / "file3.txt", ] + +def test_invalid_auto_downloader(tmp_path): + + base_url = "foo" + with pytest.raises(ValueError): + + fetcher = StaticFetcher(cache_path=tmp_path,) + fetcher.fetch( + base_url=base_url, + file_name="bar", + downloader="auto" + ) + +def test_invalid_manual_downloader(tmp_path): + + base_url = "foo" + with pytest.raises(ValueError): + + fetcher = StaticFetcher(cache_path=tmp_path,) + fetcher.fetch( + base_url=base_url, + file_name="bar", + downloader='FOO' + + ) \ No newline at end of file From d356da161583947f4a03bc2236aad47315ff282f Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 12:43:33 -0700 Subject: [PATCH 075/100] added exception --- .../fetch/test_static_fetcher.py | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 9eb8c543f4..881b407856 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -114,6 +114,28 @@ def test_append_error(self, tmp_path): db_name=REGISTRY_NAME, ) + def test_invalid_auto_downloader(self, tmp_path): + + base_url = "foo" + with pytest.raises(ValueError): + + fetcher = StaticFetcher( + cache_path=tmp_path, + ) + fetcher.fetch( + base_url=base_url, file_name="bar", downloader="auto" + ) + + def test_invalid_manual_downloader(self, tmp_path): + + base_url = "foo" + with pytest.raises(ValueError): + + fetcher = StaticFetcher( + cache_path=tmp_path, + ) + fetcher.fetch(base_url=base_url, file_name="bar", downloader="FOO") + @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") class TestExpectedBehaviors: @@ -422,28 +444,3 @@ def test_check_registry_ignore(self, tmp_path): ) == [ tmp_path / "file3.txt", ] - -def test_invalid_auto_downloader(tmp_path): - - base_url = "foo" - with pytest.raises(ValueError): - - fetcher = StaticFetcher(cache_path=tmp_path,) - fetcher.fetch( - base_url=base_url, - file_name="bar", - downloader="auto" - ) - -def test_invalid_manual_downloader(tmp_path): - - base_url = "foo" - with pytest.raises(ValueError): - - fetcher = StaticFetcher(cache_path=tmp_path,) - fetcher.fetch( - base_url=base_url, - file_name="bar", - downloader='FOO' - - ) \ No newline at end of file From 2779a96c3d727ab24fb4e5979727e0fef389046d Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 12:52:43 -0700 Subject: [PATCH 076/100] changed FILE1.txt content --- testsuite/MDAnalysisTests/fetch/servers.py | 3 +-- .../MDAnalysisTests/fetch/test_static_fetcher.py | 14 +++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/servers.py b/testsuite/MDAnalysisTests/fetch/servers.py index 2ff5ec2cbc..9f5a4c12c4 100644 --- a/testsuite/MDAnalysisTests/fetch/servers.py +++ b/testsuite/MDAnalysisTests/fetch/servers.py @@ -37,8 +37,7 @@ def temporary_http_server(): temp_folder = Path(temp_dir) (temp_folder / "TEST_FILE1.txt").write_text( - "The USA is going to win the 2026 World Cup!\n" - "U-S-A! U-S-A! U-S-A!" + "Sally sells seashells by the seashore" ) (temp_folder / "TEST_FILE2.txt").write_text("7-1") diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 881b407856..c175aaa9b2 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -153,7 +153,7 @@ def test_default_cache_path(self, clean_up_default_cache): assert path.name == "TEST_FILE1.txt" assert ( path.read_text() - == "The USA is going to win the 2026 World Cup!\nU-S-A! U-S-A! U-S-A!" + == "Sally sells seashells by the seashore" ) assert path.exists() @@ -171,14 +171,14 @@ def test_create_database(self, tmp_path): assert path.name == "TEST_FILE1.txt" assert ( path.read_text() - == "The USA is going to win the 2026 World Cup!\nU-S-A! U-S-A! U-S-A!" + == "Sally sells seashells by the seashore" ) assert path.exists() assert Path(downloader.cache_path / REGISTRY_NAME).exists() assert ( downloader.cache_path / REGISTRY_NAME - ).read_text() == "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + ).read_text() == "TEST_FILE1.txt sha256:a625aaf4ca5e2d358b216165cee3247a93a40e699bb864193499d230ab7aad7e\n" def test_append_database(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): @@ -198,7 +198,7 @@ def test_append_database(self, tmp_path): ) assert Path(downloader.cache_path / REGISTRY_NAME).read_text() == ( - "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + "TEST_FILE1.txt sha256:a625aaf4ca5e2d358b216165cee3247a93a40e699bb864193499d230ab7aad7e\n" "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" ) @@ -214,7 +214,7 @@ def test_different_hashes(self, tmp_path): assert ( Path(downloader.cache_path / REGISTRY_NAME).read_text() - == "TEST_FILE1.txt md5:b2f138521297db74b6b280feeb14f9f6\n" + == "TEST_FILE1.txt md5:adf1020ce2ffe073600990e1c9c72ce8\n" ) def test_existing_database(self, tmp_path): @@ -284,7 +284,7 @@ def test_multiple_downloads_create_database(self, tmp_path): ) assert Path(downloader.cache_path / REGISTRY_NAME).read_text() == ( - "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + "TEST_FILE1.txt sha256:a625aaf4ca5e2d358b216165cee3247a93a40e699bb864193499d230ab7aad7e\n" "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" ) @@ -349,7 +349,7 @@ def test_append_registry(self, tmp_path): fetcher.append_registry(registry, ["TEST_FILE2.txt"]) assert Path(registry).read_text() == ( - "TEST_FILE1.txt sha256:c4bdb6ba200a917b8384ffeffa4999bf05bd4e479f6580d795aca509c9122dc4\n" + "TEST_FILE1.txt sha256:a625aaf4ca5e2d358b216165cee3247a93a40e699bb864193499d230ab7aad7e\n" "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" ) From 2bec26cdee2e60257e4ec665d25fb3ed07f7fb84 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 12:56:18 -0700 Subject: [PATCH 077/100] Changed content of TEST_FILE2 --- testsuite/MDAnalysisTests/fetch/servers.py | 4 +++- testsuite/MDAnalysisTests/fetch/test_static_fetcher.py | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/servers.py b/testsuite/MDAnalysisTests/fetch/servers.py index 9f5a4c12c4..bafb2cf105 100644 --- a/testsuite/MDAnalysisTests/fetch/servers.py +++ b/testsuite/MDAnalysisTests/fetch/servers.py @@ -40,7 +40,9 @@ def temporary_http_server(): "Sally sells seashells by the seashore" ) - (temp_folder / "TEST_FILE2.txt").write_text("7-1") + (temp_folder / "TEST_FILE2.txt").write_text( + "Life, Liberty and the pursuit of Happiness" + ) (temp_folder / "TEST_FILE3.txt").write_text( "David Beckham in a World Cup ad" diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index c175aaa9b2..cac447a22b 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -199,7 +199,7 @@ def test_append_database(self, tmp_path): assert Path(downloader.cache_path / REGISTRY_NAME).read_text() == ( "TEST_FILE1.txt sha256:a625aaf4ca5e2d358b216165cee3247a93a40e699bb864193499d230ab7aad7e\n" - "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" + "TEST_FILE2.txt sha256:eff7c015c379263afdf464bd1baf266909d0e4d4af7cccb722dd4994ff4e998c\n" ) def test_different_hashes(self, tmp_path): @@ -285,7 +285,7 @@ def test_multiple_downloads_create_database(self, tmp_path): assert Path(downloader.cache_path / REGISTRY_NAME).read_text() == ( "TEST_FILE1.txt sha256:a625aaf4ca5e2d358b216165cee3247a93a40e699bb864193499d230ab7aad7e\n" - "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" + "TEST_FILE2.txt sha256:eff7c015c379263afdf464bd1baf266909d0e4d4af7cccb722dd4994ff4e998c\n" ) def test_multiple_downloads_existing_database(self, tmp_path): @@ -350,7 +350,7 @@ def test_append_registry(self, tmp_path): assert Path(registry).read_text() == ( "TEST_FILE1.txt sha256:a625aaf4ca5e2d358b216165cee3247a93a40e699bb864193499d230ab7aad7e\n" - "TEST_FILE2.txt sha256:0ec192c0f90d1332f2abca4398596d3978434ecbae6abea8ffd989412b592458\n" + "TEST_FILE2.txt sha256:eff7c015c379263afdf464bd1baf266909d0e4d4af7cccb722dd4994ff4e998c\n" ) def test_write_registry(self, tmp_path): From 4ed255386fba89fbba2b52482c5d2bdcbf0f4cbb Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 13:03:02 -0700 Subject: [PATCH 078/100] changed content of TEST_FILE_3.txt --- testsuite/MDAnalysisTests/fetch/servers.py | 4 +++- testsuite/MDAnalysisTests/fetch/test_static_fetcher.py | 10 ++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/servers.py b/testsuite/MDAnalysisTests/fetch/servers.py index bafb2cf105..85de61b47a 100644 --- a/testsuite/MDAnalysisTests/fetch/servers.py +++ b/testsuite/MDAnalysisTests/fetch/servers.py @@ -44,8 +44,10 @@ def temporary_http_server(): "Life, Liberty and the pursuit of Happiness" ) + # This is a beautiful quote from the GROMACS source code (temp_folder / "TEST_FILE3.txt").write_text( - "David Beckham in a World Cup ad" + "Unlike teachers or doctors, our efforts improve the lives of people we'll never meet. \n" + + "- Katie Busch-Sorensen" ) http_handler = partial( diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index cac447a22b..c276720392 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -151,10 +151,7 @@ def test_default_cache_path(self, clean_up_default_cache): assert isinstance(path, Path) assert path.name == "TEST_FILE1.txt" - assert ( - path.read_text() - == "Sally sells seashells by the seashore" - ) + assert path.read_text() == "Sally sells seashells by the seashore" assert path.exists() def test_create_database(self, tmp_path): @@ -169,10 +166,7 @@ def test_create_database(self, tmp_path): assert isinstance(path, Path) assert path.name == "TEST_FILE1.txt" - assert ( - path.read_text() - == "Sally sells seashells by the seashore" - ) + assert path.read_text() == "Sally sells seashells by the seashore" assert path.exists() assert Path(downloader.cache_path / REGISTRY_NAME).exists() From 4062c7411a3e4afaee08c5a9ae1ddb29a249162f Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 13:07:59 -0700 Subject: [PATCH 079/100] fixed test_static_fetcher.py --- .../fetch/test_static_fetcher.py | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index c276720392..3799f28a46 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -384,33 +384,33 @@ def test_write_registry(self, tmp_path): ) ## Test works, but doesn't work on github action. Idk why and need to find out at some point. - # def test_check_registry(self, tmp_path): - - # files = { - # "file1.txt": "Molecular \\n", - # "file2.txt": "Dynamics. \\n", - # "file3.txt": "Analysis. \\n", - # } - - # for filename, content in files.items(): - # with open(tmp_path / filename, "w") as f: - # f.write(content) - - # fetcher = StaticFetcher(cache_path=tmp_path) - - # # Write only file1 - # fetcher.write_registry( - # tmp_path / "file_1_2_and_3_hash.txt", - # [tmp_path / "file1.txt"], - # ) - - # # Show that file2 and file3 are missing - # assert fetcher.check_registry( - # tmp_path / "file_1_2_and_3_hash.txt" - # ) == [ - # tmp_path / "file3.txt", - # tmp_path / "file2.txt", - # ] + def test_check_registry(self, tmp_path): + + files = { + "file1.txt": "Molecular \\n", + "file2.txt": "Dynamics. \\n", + "file3.txt": "Analysis. \\n", + } + + for filename, content in files.items(): + with open(tmp_path / filename, "w") as f: + f.write(content) + + fetcher = StaticFetcher(cache_path=tmp_path) + + # Write only file1 + fetcher.write_registry( + tmp_path / "file_1_2_and_3_hash.txt", + [tmp_path / "file1.txt"], + ) + + # Show that file2 and file3 are missing + assert sorted(fetcher.check_registry( + tmp_path / "file_1_2_and_3_hash.txt" + )) == sorted([ + tmp_path / "file3.txt", + tmp_path / "file2.txt", + ]) def test_check_registry_ignore(self, tmp_path): From f3c45dc807576e531ee3789e5a45a47ab2308bfd Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 13:10:09 -0700 Subject: [PATCH 080/100] applied black --- .../MDAnalysisTests/fetch/test_static_fetcher.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 3799f28a46..35b032707f 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -405,12 +405,14 @@ def test_check_registry(self, tmp_path): ) # Show that file2 and file3 are missing - assert sorted(fetcher.check_registry( - tmp_path / "file_1_2_and_3_hash.txt" - )) == sorted([ - tmp_path / "file3.txt", - tmp_path / "file2.txt", - ]) + assert sorted( + fetcher.check_registry(tmp_path / "file_1_2_and_3_hash.txt") + ) == sorted( + [ + tmp_path / "file3.txt", + tmp_path / "file2.txt", + ] + ) def test_check_registry_ignore(self, tmp_path): From e270abcfa729526c60c958f8195cccfc1f69133e Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 13:26:06 -0700 Subject: [PATCH 081/100] fixed typos --- package/MDAnalysis/fetch/fetchers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 74df631709..09c07f606f 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -39,7 +39,7 @@ --------- These module-level variables affect the runtime behavior across all Fetcher classes. -Changing these values affects all initalized Fetchers. +Changing these values affects all initialized Fetchers. .. autodata:: DEFAULT_CACHE_NAME_DOWNLOADER From f3d4bc8ea4519771cf9411246a6e17f79df33e41 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 13:28:53 -0700 Subject: [PATCH 082/100] renamed test --- testsuite/MDAnalysisTests/fetch/test_from_PDB.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py index 9ec52b54b0..7071bcc4c9 100644 --- a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py +++ b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py @@ -54,7 +54,7 @@ def test_download_one_file_str(tmp_path): not HAS_ACCESS_TO_WWPDB, reason="Can not connect to https://files.wwpdb.org/", ) -def test_download_one_file(tmp_path): +def test_download_one_file_list(tmp_path): path = mda.fetch.from_PDB(["1AKE"], cache_path=tmp_path) assert path.exists() From eff83546246acb02844f07ffee55309daa1ba8f8 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 13:34:54 -0700 Subject: [PATCH 083/100] made servers.py a relative import --- testsuite/MDAnalysisTests/fetch/__init__.py | 0 testsuite/MDAnalysisTests/fetch/test_static_fetcher.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 testsuite/MDAnalysisTests/fetch/__init__.py diff --git a/testsuite/MDAnalysisTests/fetch/__init__.py b/testsuite/MDAnalysisTests/fetch/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 35b032707f..44a7dbbf9f 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -25,7 +25,7 @@ from pathlib import Path from shutil import rmtree -from servers import temporary_http_server +from .servers import temporary_http_server import hashlib import pytest From 6e89ff628392e1433eecfe75d1af71d093d4300c Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 13:36:34 -0700 Subject: [PATCH 084/100] made temp server do a random port --- testsuite/MDAnalysisTests/fetch/servers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/MDAnalysisTests/fetch/servers.py b/testsuite/MDAnalysisTests/fetch/servers.py index 85de61b47a..d0b98ec3f6 100644 --- a/testsuite/MDAnalysisTests/fetch/servers.py +++ b/testsuite/MDAnalysisTests/fetch/servers.py @@ -55,7 +55,7 @@ def temporary_http_server(): directory=str(temp_folder), ) - server = ThreadingHTTPServer(("127.0.0.1", 7123), http_handler) + server = ThreadingHTTPServer(("127.0.0.1", 0), http_handler) host, port = server.server_address thread = threading.Thread( From c626c2aefe3cb3423f5f2b1199c55730412944af Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 13:42:29 -0700 Subject: [PATCH 085/100] fixed mutable default arguement --- package/MDAnalysis/fetch/fetchers.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 09c07f606f..cab6e4f15b 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -387,7 +387,7 @@ def append_registry(self, db_path, files): new_files = [self.cache_path / file_name for file_name in files] self.write_registry(Path(db_path), new_files, mode="a") - def check_registry(self, db_path, files=[], ignore=[]): + def check_registry(self, db_path, files=None, ignore=None): """ Return paths relative to :attr:`cache_path` for cache files that are missing from the registry. @@ -443,6 +443,9 @@ def check_registry(self, db_path, files=[], ignore=[]): """ + files = [] if files is None else files + ignore = [] if ignore is None else ignore + registry_dictionary = self.read_registry(db_path) database_files = set(registry_dictionary.keys()) @@ -639,4 +642,3 @@ def _set_downloader(self, base_url, downloader, **kwargs): f"Invalid downloader '{_downloader}'. Valid options " + f"are {SUPPORTED_DOWNLOADERS}" ) - From dfb0568599711767bb34b1af66f1667e1320ef80 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 22:16:38 -0700 Subject: [PATCH 086/100] fixed typo --- package/MDAnalysis/fetch/fetchers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index cab6e4f15b..215d444773 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -227,12 +227,12 @@ def fetch( -------- Download a single CIF file from the RCSB Protein Data Bank. - >>> StaticFetcher.fetch(file_name="1AKE.cif", + >>> StaticFetcher().fetch(file_name="1AKE.cif", base_url="https://files.wwpdb.org/download/") './MDAnalysis_pdbs/1AKE.cif' Download multiple CIF files from the RCSB Protein Data Bank. - >>> StaticFetcher.fetch(file_name=["1AKE.cif", "4AKE.cif"], + >>> StaticFetcher().fetch(file_name=["1AKE.cif", "4AKE.cif"], base_url="https://files.wwpdb.org/download/") ['./MDAnalysis_pdbs/1AKE.cif', './MDAnalysis_pdbs/4AKE.cif'] From 4f8c8bed1b3013c96a7919f5859590ecf15237a9 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 22:17:48 -0700 Subject: [PATCH 087/100] revert local impot --- testsuite/MDAnalysisTests/fetch/test_static_fetcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 44a7dbbf9f..35b032707f 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -25,7 +25,7 @@ from pathlib import Path from shutil import rmtree -from .servers import temporary_http_server +from servers import temporary_http_server import hashlib import pytest From 891f8c92f44a8009fc58c506926e8f51b1bb5c19 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 22:23:19 -0700 Subject: [PATCH 088/100] readded variable for backwards compatiblity --- package/MDAnalysis/fetch/pdb.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/package/MDAnalysis/fetch/pdb.py b/package/MDAnalysis/fetch/pdb.py index 9c4e05f77f..fbf755b2e3 100644 --- a/package/MDAnalysis/fetch/pdb.py +++ b/package/MDAnalysis/fetch/pdb.py @@ -30,15 +30,28 @@ .. _Protein Data Batabank: https://www.rcsb.org/ +Variables +--------- + +.. autodata:: DEFAULT_CACHE_NAME_DOWNLOADER + Functions --------- .. autofunction:: from_PDB +.. """ from pathlib import Path from .fetchers import StaticFetcher + +#: Alias to fetchers/DEFAULT_CACHE_NAME_DOWNLOADER +#: +#: Maintained for backwards compatiblity +#: +from .fetchers import DEFAULT_CACHE_NAME_DOWNLOADER + # These file formats are here https://www.rcsb.org/docs/programmatic-access/file-download-services#pdb-entry-files" _SUPPORTED_FILE_FORMATS_PDB = ( "cif", From 48e4bd3fb26e82181733b3b0809abbcf8a2441c3 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 29 Jul 2026 22:25:58 -0700 Subject: [PATCH 089/100] applied black --- package/MDAnalysis/fetch/pdb.py | 5 ++--- testsuite/MDAnalysisTests/fetch/test_from_PDB.py | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package/MDAnalysis/fetch/pdb.py b/package/MDAnalysis/fetch/pdb.py index fbf755b2e3..7c3f1c07c4 100644 --- a/package/MDAnalysis/fetch/pdb.py +++ b/package/MDAnalysis/fetch/pdb.py @@ -39,7 +39,6 @@ --------- .. autofunction:: from_PDB -.. """ from pathlib import Path @@ -47,7 +46,7 @@ #: Alias to fetchers/DEFAULT_CACHE_NAME_DOWNLOADER -#: +#: #: Maintained for backwards compatiblity #: from .fetchers import DEFAULT_CACHE_NAME_DOWNLOADER @@ -165,7 +164,7 @@ def from_PDB( if isinstance(pdb_ids, str): _pdb_ids = (pdb_ids + "." + file_format,) - else: + else: _pdb_ids = [pdb + "." + file_format for pdb in pdb_ids] fetcher = StaticFetcher(cache_path=cache_path) diff --git a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py index 7071bcc4c9..af6c26aefc 100644 --- a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py +++ b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py @@ -37,6 +37,7 @@ except request.URLError: HAS_ACCESS_TO_WWPDB = False + @pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") @pytest.mark.skipif( not HAS_ACCESS_TO_WWPDB, From 47152729fc2e07acb195c2fb8615a13f54f60911 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 30 Jul 2026 14:09:04 -0700 Subject: [PATCH 090/100] reenabled tests? --- testsuite/MDAnalysisTests/fetch/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 testsuite/MDAnalysisTests/fetch/__init__.py diff --git a/testsuite/MDAnalysisTests/fetch/__init__.py b/testsuite/MDAnalysisTests/fetch/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 From 0c427e7dba14aadd87e956e9741c5b8bdb72b079 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 30 Jul 2026 15:31:21 -0700 Subject: [PATCH 091/100] applied monkeypatch --- .../fetch/test_static_fetcher.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 35b032707f..b566126555 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -24,6 +24,7 @@ import re from pathlib import Path from shutil import rmtree +from unittest.mock import Mock from servers import temporary_http_server @@ -35,6 +36,7 @@ DEFAULT_CACHE_NAME_DOWNLOADER, HAS_POOCH, StaticFetcher, + pooch, ) if HAS_POOCH: @@ -440,3 +442,52 @@ def test_check_registry_ignore(self, tmp_path): ) == [ tmp_path / "file3.txt", ] + + +@pytest.mark.parametrize( + ("downloader", "constructor_name"), + [ + ("ftp", "FTPDownloader"), + ("sftp", "SFTPDownloader"), + ("doi", "DOIDownloader"), + ], +) +def test_fetch_with_other_downloaders( + monkeypatch, + tmp_path, + downloader, + constructor_name, +): + downloaded_path = tmp_path / "example.dat" + + # Monkeypatches the pooch.create to return a fake path + pooch_fetch = Mock(return_value=str(downloaded_path)) + pooch_instance = Mock(fetch=pooch_fetch) + + monkeypatch.setattr( + pooch, + "create", + Mock(return_value=pooch_instance), + ) + + # Monkekpatch a generic pooch Downloader + # This will be used to mock the behavior of a generic pooch Downloader + downloader_instance = object() + downloader_constructor = Mock(return_value=downloader_instance) + + monkeypatch.setattr( + pooch, + constructor_name, + downloader_constructor, + ) + + # Since pooch.create() is monkeypatched to return a fake path. This won't raise an exception + # And the fake downloader will be passed into the fetch method to mock its behavior. + result = StaticFetcher(cache_path=tmp_path).fetch( + base_url="https://example.com/", + file_name="example.dat", + db_name=None, + downloader=downloader, + ) + + assert result == downloaded_path From 519fe2e553e21e381938754061d7fce29563b100 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 30 Jul 2026 15:46:14 -0700 Subject: [PATCH 092/100] Formalized Downloaders Test --- .../fetch/test_static_fetcher.py | 107 +++++++++++------- 1 file changed, 65 insertions(+), 42 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index b566126555..f757112946 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -198,6 +198,7 @@ def test_append_database(self, tmp_path): "TEST_FILE2.txt sha256:eff7c015c379263afdf464bd1baf266909d0e4d4af7cccb722dd4994ff4e998c\n" ) + # Add paramertization def test_different_hashes(self, tmp_path): with temporary_http_server() as (host, port, temp_folder): base_url = f"http://{host}:{port}/" @@ -444,50 +445,72 @@ def test_check_registry_ignore(self, tmp_path): ] -@pytest.mark.parametrize( - ("downloader", "constructor_name"), - [ - ("ftp", "FTPDownloader"), - ("sftp", "SFTPDownloader"), - ("doi", "DOIDownloader"), - ], -) -def test_fetch_with_other_downloaders( - monkeypatch, - tmp_path, - downloader, - constructor_name, -): - downloaded_path = tmp_path / "example.dat" - - # Monkeypatches the pooch.create to return a fake path - pooch_fetch = Mock(return_value=str(downloaded_path)) - pooch_instance = Mock(fetch=pooch_fetch) - - monkeypatch.setattr( - pooch, - "create", - Mock(return_value=pooch_instance), +@pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") +class TestDownloaders: + + @pytest.mark.parametrize( + ("downloader", "constructor_name"), + [ + ("ftp", "FTPDownloader"), + ("sftp", "SFTPDownloader"), + ("doi", "DOIDownloader"), + ], ) + def test_monkeypatch_other_downloaders( + self, + monkeypatch, + tmp_path, + downloader, + constructor_name, + ): + downloaded_path = tmp_path / "example.dat" - # Monkekpatch a generic pooch Downloader - # This will be used to mock the behavior of a generic pooch Downloader - downloader_instance = object() - downloader_constructor = Mock(return_value=downloader_instance) + # Monkeypatches the pooch.create to return a fake path + pooch_fetch = Mock(return_value=str(downloaded_path)) + pooch_instance = Mock(fetch=pooch_fetch) - monkeypatch.setattr( - pooch, - constructor_name, - downloader_constructor, - ) + monkeypatch.setattr( + pooch, + "create", + Mock(return_value=pooch_instance), + ) - # Since pooch.create() is monkeypatched to return a fake path. This won't raise an exception - # And the fake downloader will be passed into the fetch method to mock its behavior. - result = StaticFetcher(cache_path=tmp_path).fetch( - base_url="https://example.com/", - file_name="example.dat", - db_name=None, - downloader=downloader, - ) + # Monkekpatch a generic pooch Downloader + # This will be used to mock the behavior of a generic pooch Downloader + downloader_instance = object() + downloader_constructor = Mock(return_value=downloader_instance) + + monkeypatch.setattr( + pooch, + constructor_name, + downloader_constructor, + ) - assert result == downloaded_path + # Since pooch.create() is monkeypatched to return a fake path. This won't raise an exception + # And the fake downloader will be passed into the fetch method to mock its behavior. + result = StaticFetcher(cache_path=tmp_path).fetch( + base_url="https://example.com/", + file_name="example.dat", + db_name=None, + downloader=downloader, + ) + + assert result == downloaded_path + + def test_downloader_error(self, tmp_path): + with pytest.raises( + ValueError, + ): + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + downloader = StaticFetcher() + + downloader = StaticFetcher( + cache_path=tmp_path, + ) + path = downloader.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + downloader="FOO", + ) From 99f5d5387a72e3b8fd32f6b1b26b96e0e06f271d Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Thu, 30 Jul 2026 16:00:11 -0700 Subject: [PATCH 093/100] fixed pooch import --- testsuite/MDAnalysisTests/fetch/test_static_fetcher.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index f757112946..88a8fcbbe2 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -31,12 +31,10 @@ import hashlib import pytest - from MDAnalysis.fetch.fetchers import ( DEFAULT_CACHE_NAME_DOWNLOADER, HAS_POOCH, StaticFetcher, - pooch, ) if HAS_POOCH: From 494042899dc61987b6222de5bb3bb431f86bb2e2 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 4 Aug 2026 11:36:25 -0700 Subject: [PATCH 094/100] added fetch(append_db=True) --- package/MDAnalysis/fetch/pdb.py | 4 ++-- testsuite/MDAnalysisTests/fetch/test_from_PDB.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/package/MDAnalysis/fetch/pdb.py b/package/MDAnalysis/fetch/pdb.py index 7c3f1c07c4..c25931790a 100644 --- a/package/MDAnalysis/fetch/pdb.py +++ b/package/MDAnalysis/fetch/pdb.py @@ -41,9 +41,8 @@ .. autofunction:: from_PDB """ -from pathlib import Path -from .fetchers import StaticFetcher +from .fetchers import StaticFetcher #: Alias to fetchers/DEFAULT_CACHE_NAME_DOWNLOADER #: @@ -172,4 +171,5 @@ def from_PDB( file_name=_pdb_ids, base_url="https://files.wwpdb.org/download/", progressbar=progressbar, + append_db=True, ) diff --git a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py index af6c26aefc..8bccdc4eca 100644 --- a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py +++ b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py @@ -104,3 +104,17 @@ def test_invalid_file_format(tmp_path): mda.fetch.from_PDB( pdb_ids="1AKE", cache_path=tmp_path, file_format="barfoo" ) + + +@pytest.mark.skipif(not HAS_POOCH, reason="Pooch is not installed.") +@pytest.mark.skipif( + not HAS_ACCESS_TO_WWPDB, + reason="Can not connect to https://files.wwpdb.org/", +) +def test_download_multiple_calls(tmp_path): + + p1 = mda.fetch.from_PDB(["9BUY"], cache_path=tmp_path) + p2 = mda.fetch.from_PDB(["3SN6"], cache_path=tmp_path) + + assert (tmp_path / "9BUY.cif.gz").exists() + assert (tmp_path / "3SN6.cif.gz").exists() From 878e6a1a9537ee2d8eae110eea54adc69cdeb907 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Tue, 4 Aug 2026 13:59:20 -0700 Subject: [PATCH 095/100] Added to replicate --- package/MDAnalysis/fetch/fetchers.py | 15 ++++++- .../fetch/test_static_fetcher.py | 43 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 215d444773..8a28295fe2 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -330,7 +330,7 @@ def fetch( return paths[0] if len(paths) == 1 else paths - def append_registry(self, db_path, files): + def append_registry(self, db_path, files, write_duplicate=False): """ Append cached files to an existing Pooch registry. @@ -385,7 +385,18 @@ def append_registry(self, db_path, files): """ new_files = [self.cache_path / file_name for file_name in files] - self.write_registry(Path(db_path), new_files, mode="a") + + if not write_duplicate: + files_dict = self.read_registry(db_path) + + _new_files = [ + file for file in new_files if file.name not in files_dict + ] + + else: + _new_files = new_files + + self.write_registry(Path(db_path), _new_files, mode="a") def check_registry(self, db_path, files=None, ignore=None): """ diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 88a8fcbbe2..ba9c8793c1 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -348,6 +348,49 @@ def test_append_registry(self, tmp_path): "TEST_FILE2.txt sha256:eff7c015c379263afdf464bd1baf266909d0e4d4af7cccb722dd4994ff4e998c\n" ) + def test_append_registry_duplicate(self, tmp_path): + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + fetcher = StaticFetcher(cache_path=tmp_path) + + file1 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name="db_hash1.txt", + ) + + registry = file1.parent / "db_hash1.txt" + fetcher.append_registry( + registry, ["TEST_FILE1.txt"], write_duplicate=True + ) + + assert Path(registry).read_text() == ( + "TEST_FILE1.txt sha256:a625aaf4ca5e2d358b216165cee3247a93a40e699bb864193499d230ab7aad7e\n" + "TEST_FILE1.txt sha256:a625aaf4ca5e2d358b216165cee3247a93a40e699bb864193499d230ab7aad7e\n" + ) + + def test_append_registry_no_duplicate(self, tmp_path): + + with temporary_http_server() as (host, port, temp_folder): + base_url = f"http://{host}:{port}/" + fetcher = StaticFetcher(cache_path=tmp_path) + + file1 = fetcher.fetch( + base_url=base_url, + file_name="TEST_FILE1.txt", + db_name="db_hash1.txt", + ) + + registry = file1.parent / "db_hash1.txt" + fetcher.append_registry( + registry, ["TEST_FILE1.txt"], write_duplicate=False + ) + + assert Path(registry).read_text() == ( + "TEST_FILE1.txt sha256:a625aaf4ca5e2d358b216165cee3247a93a40e699bb864193499d230ab7aad7e\n" + ) + def test_write_registry(self, tmp_path): # This can't call StaticFetcher directly for an effective test # Maybe refactor the file creation into a function handle or fixture From 423f7b553b3de612319e23949ff907a0e3d86447 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 5 Aug 2026 11:49:55 -0700 Subject: [PATCH 096/100] Updated docs --- package/MDAnalysis/fetch/fetchers.py | 128 +++++++++++++++------------ 1 file changed, 69 insertions(+), 59 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 8a28295fe2..41c915882c 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -144,7 +144,7 @@ class StaticFetcher(_BaseFetcher): Attributes ---------- - cache_path : pathlib.Path or ``None`` + cache_path : pathlib.Path Path to the cache directory. hash : str @@ -225,16 +225,20 @@ def fetch( Examples -------- - Download a single CIF file from the RCSB Protein Data Bank. - >>> StaticFetcher().fetch(file_name="1AKE.cif", - base_url="https://files.wwpdb.org/download/") - './MDAnalysis_pdbs/1AKE.cif' + .. code-block:: pycon - Download multiple CIF files from the RCSB Protein Data Bank. - >>> StaticFetcher().fetch(file_name=["1AKE.cif", "4AKE.cif"], - base_url="https://files.wwpdb.org/download/") - ['./MDAnalysis_pdbs/1AKE.cif', './MDAnalysis_pdbs/4AKE.cif'] + Download a single CIF file from the RCSB Protein Data Bank. + + >>> StaticFetcher().fetch(file_name="1AKE.cif", + base_url="https://files.wwpdb.org/download/") + './MDAnalysis_pdbs/1AKE.cif' + + Download multiple CIF files from the RCSB Protein Data Bank. + + >>> StaticFetcher().fetch(file_name=["1AKE.cif", "4AKE.cif"], + base_url="https://files.wwpdb.org/download/") + ['./MDAnalysis_pdbs/1AKE.cif', './MDAnalysis_pdbs/4AKE.cif'] Notes ----- @@ -345,6 +349,10 @@ def append_registry(self, db_path, files, write_duplicate=False): files : iterable of str or path-like File names or paths for cached files to append to the registry. Relative paths are interpreted relative to :attr:`cache_path`. + write_duplicate : bool + If set to True, append_registry will write the file and its hash to the registry + regardless of the existing presence of a entry in the registry. Default behavior is + False. Returns ------- @@ -426,24 +434,25 @@ def check_registry(self, db_path, files=None, ignore=None): Example ------- .. code-block:: python - >>> files = { - ... "file1.txt": "Molecular \\n", - ... "file2.txt": "Dynamics. \\n", - ... "file3.txt": "Analysis. \\n" - ... } - >>> for filename, content in files.items(): - ... with open(filename, "w") as f: - ... f.write(content) - ... - >>> fetcher = StaticFetcher() - >>> fetcher.write_registry( - ... "file_1_2_and_3_hash.txt", - ... files=["file1.txt"], - ... ) - >>> fetcher.check_registry("file_1_2_and_3_hash.txt") - [Path('./MDAnalysis_pdbs/file3.txt'), Path('./MDAnalysis_pdbs/file2.txt')] - >>> fetcher.check_registry("file_1_2_and_3_hash.txt", ignore=["file2.txt"]) - [Path('./MDAnalysis_pdbs/file3.txt')] + + >>> files = { + ... "file1.txt": "Molecular \\n", + ... "file2.txt": "Dynamics. \\n", + ... "file3.txt": "Analysis. \\n" + ... } + >>> for filename, content in files.items(): + ... with open(filename, "w") as f: + ... f.write(content) + ... + >>> fetcher = StaticFetcher() + >>> fetcher.write_registry( + ... "file_1_2_and_3_hash.txt", + ... files=["file1.txt"], + ... ) + >>> fetcher.check_registry("file_1_2_and_3_hash.txt") + [Path('./MDAnalysis_pdbs/file3.txt'), Path('./MDAnalysis_pdbs/file2.txt')] + >>> fetcher.check_registry("file_1_2_and_3_hash.txt", ignore=["file2.txt"]) + [Path('./MDAnalysis_pdbs/file3.txt')] Notes @@ -496,22 +505,22 @@ def read_registry(self, db_path): ------- .. code-block:: python - >>> files = { - ... "file1.txt": "Molecular \\n", - ... "file2.txt": "Dynamics. \\n", - ... } - >>> for filename, content in files.items(): - ... with open(filename, "w") as f: - ... f.write(content) - ... - >>> fetcher = StaticFetcher() - >>> fetcher.write_registry( - ... "file_1_and_2_hash.txt", - ... ["file1.txt", "file2.txt"], - ... ) - >>> fetcher.read_registry("file_1_and_2_hash.txt") - {'file1.txt': 'sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20', - 'file2.txt': 'sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a'} + >>> files = { + ... "file1.txt": "Molecular \\n", + ... "file2.txt": "Dynamics. \\n", + ... } + >>> for filename, content in files.items(): + ... with open(filename, "w") as f: + ... f.write(content) + ... + >>> fetcher = StaticFetcher() + >>> fetcher.write_registry( + ... "file_1_and_2_hash.txt", + ... ["file1.txt", "file2.txt"], + ... ) + >>> fetcher.read_registry("file_1_and_2_hash.txt") + {'file1.txt': 'sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20', + 'file2.txt': 'sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a'} Notes ----- @@ -557,22 +566,23 @@ def write_registry(self, db_path, files, mode="w"): Example ------- .. code-block:: python - >>> files = { - ... "file1.txt": "Molecular \\n", - ... "file2.txt": "Dynamics. \\n", - ... } - >>> for filename, content in files.items(): - ... with open(filename, "w") as f: - ... f.write(content) - ... - >>> fetcher = StaticFetcher() - >>> fetcher.write_registry( - ... "file_1_and_2_hash.txt", - ... ["file1.txt", "file2.txt"], - ... ) - >>> Path("file_1_and_2_hash.txt").read_text() - file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20 - file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a + + >>> files = { + ... "file1.txt": "Molecular \\n", + ... "file2.txt": "Dynamics. \\n", + ... } + >>> for filename, content in files.items(): + ... with open(filename, "w") as f: + ... f.write(content) + ... + >>> fetcher = StaticFetcher() + >>> fetcher.write_registry( + ... "file_1_and_2_hash.txt", + ... ["file1.txt", "file2.txt"], + ... ) + >>> Path("file_1_and_2_hash.txt").read_text() + file1.txt sha256:2da169c5aae36a823c202da49fb11935b76277efcb5cd42a4cf238ddda2a9b20 + file2.txt sha256:3a0dbd9e2abc4a7bbae6adfe92e2858218135926dacd4a7d3fb4ca2dbdbe457a Notes ----- From b550fe429294a8a5a1c7f195fb811e8fa4dc2bed Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 5 Aug 2026 12:06:04 -0700 Subject: [PATCH 097/100] applied flake8 --- package/MDAnalysis/fetch/fetchers.py | 104 +++++++++++++++------------ package/MDAnalysis/fetch/pdb.py | 18 ++--- 2 files changed, 69 insertions(+), 53 deletions(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 41c915882c..05bfbf0b40 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -25,8 +25,9 @@ Fetchers --- :mod:`MDAnalysis.fetch.fetchers` ============================================= -This module contains Fetcher classes which are able to retrieve files from remote servers. -These classes use the third-party library :mod:`pooch` as a dependency. +This module contains Fetcher classes which are able to retrieve files from +remote servers.These classes use the third-party library :mod:`pooch` as +a dependency. Classes ------- @@ -38,8 +39,8 @@ Variables --------- -These module-level variables affect the runtime behavior across all Fetcher classes. -Changing these values affects all initialized Fetchers. +These module-level variables affect the runtime behavior across all Fetcher +classes. Changing these values affects all initialized Fetchers. .. autodata:: DEFAULT_CACHE_NAME_DOWNLOADER @@ -71,7 +72,8 @@ #: .. versionadded:: 2.11.0 DEFAULT_CACHE_NAME_DOWNLOADER = "MDAnalysis_pdbs" -#: Default time in seconds to wait for a response from the server before timing out. +#: Default time in seconds to wait for a response from the server before +#: timing out. #: #: .. versionadded:: 2.11.0 DEFAULT_TIMEOUT = 10 @@ -116,7 +118,8 @@ def _check_pooch( ) def _validate_fetch_args(self, args): - """Add the default global timeout and retry variables to fetch arguments.""" + """Add the default global timeout and retry variables to fetch + arguments.""" args.setdefault("timeout", DEFAULT_TIMEOUT) args.setdefault("retries", DEFAULT_RETRIES) @@ -182,8 +185,9 @@ def fetch( locally. Primarily designed to be working with `FAIR`_ - databases, this method works by sending a request to a web server and caching them to a registry. - The registry is in the format of a `pooch registry file`_, and it will be created or read relative to + databases, this method works by sending a request to a web server and + caching them to a registry.The registry is in the format of a + `pooch registry file`_, and it will be created or read relative to :attr:`cache_path`. .. _FAIR: https://www.nature.com/articles/s41592-025-02635-0 @@ -193,29 +197,31 @@ def fetch( ---------- base_url : str Base URL from which to download the file(s). This should be a valid - URL pointing to the directory containing the files to be downloaded. + URL pointing to the directory containing the files to be + downloaded. file_name : str or sequence of str Name of the file or files to download. The requested URL has the form ``{base_url}/{file_name}``. verbose : bool, optional If ``True``, show download progress. The default is ``False``. db_name : str or None, optional - Name of the local hash database file used to verify cached downloads. - The default is ``"hashes.txt"``. If ``None``, no registry database - is read or written. + Name of the local hash database file used to verify cached + downloads. The default is ``"hashes.txt"``. If ``None``, + no registry database is read or written. append_db : bool, optional If ``True``, add downloaded files that are missing from an existing registry to that registry. If ``False``, missing registry entries raise a :class:`ValueError`. The default is ``False``. timeout : float, optional - Time in seconds to wait for a response from the server before timing - out. The default is :data:`DEFAULT_TIMEOUT`. + Time in seconds to wait for a response from the server before + timing out. The default is :data:`DEFAULT_TIMEOUT`. retries : int, optional Number of times to retry a failed download. The default is :data:`DEFAULT_RETRIES`. downloader : str, optional - Downloader backend to use. Supported values are ``"auto"``, ``"http"``, - ``"ftp"``, ``"sftp"``, and ``"doi"``. The default is ``"auto"``. + Downloader backend to use. Supported values are ``"auto"``, + ``"http"``, ``"ftp"``, ``"sftp"``, and ``"doi"``. + The default is ``"auto"``. Returns ------- @@ -251,7 +257,8 @@ def fetch( .. versionadded:: 2.11.0 """ - # Keywords arguments that are reserved for common _BaseFetcher.fetch() arguments. + # Keywords arguments that are reserved for common + # _BaseFetcher.fetch() arguments. kwargs = self._validate_fetch_args(kwargs) LOAD_FROM_CACHE = False @@ -266,7 +273,7 @@ def fetch( (file_name,) if isinstance(file_name, str) else tuple(file_name) ) - ## Reading from Registry + # Reading from Registry if db_name is not None: db_path = self.cache_path / Path(db_name) @@ -288,7 +295,8 @@ def fetch( raise ValueError( "fetch() is requesting files not found in the registry. " + f"The missing files are {missing_files_list}. " - + "To fix this, please set append_db=True to append the registry." + + "To fix this, please set append_db=True to append the " + + "registry." ) for name in requested_files: @@ -296,7 +304,7 @@ def fetch( ## - ## Download code using pooch + # Download code using pooch main_downloader = pooch.create( path=self.cache_path, base_url=base_url, @@ -323,7 +331,7 @@ def fetch( ## - ## Registry write code + # Registry write code if CREATE_DATABASE: self.write_registry(db_path, paths) @@ -339,26 +347,26 @@ def append_registry(self, db_path, files, write_duplicate=False): Append cached files to an existing Pooch registry. Each entry in ``files`` is resolved relative to :attr:`cache_path`. The - file hash is computed using the fetcher's configured hash algorithm :attr:`hash` and a - new registry line is appended to ``db_path``. + file hash is computed using the fetcher's configured hash algorithm + :attr:`hash` and a new registry line is appended to ``db_path``. Parameters ---------- db_path : str or path-like Path to the registry file to update. files : iterable of str or path-like - File names or paths for cached files to append to the registry. Relative - paths are interpreted relative to :attr:`cache_path`. + File names or paths for cached files to append to the registry. + Relative paths are interpreted relative to :attr:`cache_path`. write_duplicate : bool - If set to True, append_registry will write the file and its hash to the registry - regardless of the existing presence of a entry in the registry. Default behavior is - False. + If set to True, append_registry will write the file and its hash + to the registry regardless of the existing presence of a entry + in the registry. Default behavior is False. Returns ------- None - This method updates the registry file in place and does not return a - value. + This method updates the registry file in place and does not + return a value. Example ------- @@ -382,8 +390,8 @@ def append_registry(self, db_path, files, write_duplicate=False): Notes ----- - Existing registry entries are preserved. This method does not check for or - remove duplicate file entries. + Existing registry entries are preserved. This method does not check + for or remove duplicate file entries. Each appended registry line has the format:: @@ -408,11 +416,12 @@ def append_registry(self, db_path, files, write_duplicate=False): def check_registry(self, db_path, files=None, ignore=None): """ - Return paths relative to :attr:`cache_path` for cache files that are missing from the registry. + Return paths relative to :attr:`cache_path` for cache files that are + missing from the registry. This method compares filenames within the registry against files found - recursively under :attr:`cache_path`. A cache file is considered missing - when it is on disk, but it is not recorded in the registry. + recursively under :attr:`cache_path`. A cache file is considered + missing when it is on disk, but it is not recorded in the registry. Parameters ---------- @@ -486,8 +495,9 @@ def read_registry(self, db_path): Read a Pooch registry file into a dictionary. This method returns filenames within the registry against files found - recursively under :attr:`cache_path`. Each key in the returned dictionary - corresponds to a filename in the registry relative to :attr:`cache_path`. + recursively under :attr:`cache_path`. Each key in the returned + dictionary corresponds to a filename in the registry relative to + :attr:`cache_path`. Parameters ---------- @@ -498,8 +508,8 @@ def read_registry(self, db_path): ------- hash_dict : dict Dictionary mapping each filename in the registry to its stored hash - value. Hash values are expected to include the hash algorithm prefix, - for example ``"sha256:"``. + value. Hash values are expected to include the hash + algorithm prefix. Example ------- @@ -544,9 +554,10 @@ def write_registry(self, db_path, files, mode="w"): """ Write a Pooch registry file with hashes for the given files. - This method computes the hash for each file and writes it to the registry file. - The registry file maps each filename to its corresponding hash value. The hash - algorithm used is determined by the :attr:`hash` of the fetcher. + This method computes the hash for each file and writes it to the + registry file.The registry file maps each filename to its + corresponding hash value. The hash algorithm used is determined + by the :attr:`hash` of the fetcher. Parameters ---------- @@ -556,12 +567,14 @@ def write_registry(self, db_path, files, mode="w"): Files to be include in the registry. Each file must be relative to :attr:`cache_path`. mode : str, optional - File opening mode used when writing the registry. Default is ``"w"``. + File opening mode used when writing the registry. + Default is ``"w"``. Returns ------- None - This method writes the registry to disk and does not return a value. + This method writes the registry to disk and does not return + a value. Example ------- @@ -624,7 +637,8 @@ def _check_hash_input(self, hash): ) def _set_downloader(self, base_url, downloader, **kwargs): - """Sets Downloader in fetch() by matching a regex against the download link""" + """Sets Downloader in fetch() by matching a regex against the + download link""" SUPPORTED_DOWNLOADERS = ("auto", "http", "https", "ftp", "sftp", "doi") diff --git a/package/MDAnalysis/fetch/pdb.py b/package/MDAnalysis/fetch/pdb.py index c25931790a..b9ba3166f4 100644 --- a/package/MDAnalysis/fetch/pdb.py +++ b/package/MDAnalysis/fetch/pdb.py @@ -25,8 +25,9 @@ PDB Fetchers --- :mod:`MDAnalysis.fetch.pdb` ============================================ -This suite of functions download structure files from the Research Collaboratory for -Structural Bioinformatics (RCSB) `Protein Data Batabank`_ (PDB). +This suite of functions download structure files from the Research +Collaboratory for Structural Bioinformatics (RCSB) +`Protein Data Batabank`_ (PDB). .. _Protein Data Batabank: https://www.rcsb.org/ @@ -93,7 +94,8 @@ def from_PDB( The file extension/format to download (e.g., "cif", "pdb"). See the Notes section below for a list of all supported file formats. progressbar : bool - If True, display a progress bar during file downloads. Default is False. + If True, display a progress bar during file downloads. Default + is False. Returns ------- @@ -112,8 +114,8 @@ def from_PDB( Notes ----- - This function uses the `RCSB File Download Services`_ for directly downloading - structure files via https. + This function uses the `RCSB File Download Services`_ for directly + downloading structure files via https. .. _`RCSB File Download Services`: https://www.rcsb.org/docs/programmatic-access/file-download-services @@ -125,9 +127,9 @@ def from_PDB( Caching, controlled by the `cache_path` parameter, is handled internally by :mod:`pooch`. The default cache name is taken from - :data:`DEFAULT_CACHE_NAME_DOWNLOADER`. To clear cache (and subsequently force - re-fetching), it is required to delete the cache folder as specified by - `cache_path`. + :data:`DEFAULT_CACHE_NAME_DOWNLOADER`. To clear cache (and subsequently + force re-fetching), it is required to delete the cache folder + as specified by `cache_path`. Examples -------- From d0e494e142b79b946d486757b8a36dece9259d2c Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 5 Aug 2026 12:11:00 -0700 Subject: [PATCH 098/100] applied flake8 where it made sense --- testsuite/MDAnalysisTests/fetch/servers.py | 4 ++-- testsuite/MDAnalysisTests/fetch/test_from_PDB.py | 4 ++-- testsuite/MDAnalysisTests/fetch/test_static_fetcher.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/servers.py b/testsuite/MDAnalysisTests/fetch/servers.py index d0b98ec3f6..2656a8af75 100644 --- a/testsuite/MDAnalysisTests/fetch/servers.py +++ b/testsuite/MDAnalysisTests/fetch/servers.py @@ -46,8 +46,8 @@ def temporary_http_server(): # This is a beautiful quote from the GROMACS source code (temp_folder / "TEST_FILE3.txt").write_text( - "Unlike teachers or doctors, our efforts improve the lives of people we'll never meet. \n" - + "- Katie Busch-Sorensen" + "Unlike teachers or doctors, our efforts improve the lives of " + + "people we'll never meet. \n" + "- Katie Busch-Sorensen" ) http_handler = partial( diff --git a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py index 8bccdc4eca..4ec983076e 100644 --- a/testsuite/MDAnalysisTests/fetch/test_from_PDB.py +++ b/testsuite/MDAnalysisTests/fetch/test_from_PDB.py @@ -116,5 +116,5 @@ def test_download_multiple_calls(tmp_path): p1 = mda.fetch.from_PDB(["9BUY"], cache_path=tmp_path) p2 = mda.fetch.from_PDB(["3SN6"], cache_path=tmp_path) - assert (tmp_path / "9BUY.cif.gz").exists() - assert (tmp_path / "3SN6.cif.gz").exists() + assert p1.exists() + assert p2.exists() diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index ba9c8793c1..085f962297 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -84,15 +84,15 @@ def test_invalid_hash(self, tmp_path): hash = "foo" with temporary_http_server() as (host, port, temp_folder): - base_url = f"http://{host}:{port}/" with pytest.raises( ValueError, match=re.escape( - f'Invalid hash "{hash}". Valid hashes algorithms are {hashlib.algorithms_available}.' + f'Invalid hash "{hash}". Valid hashes algorithms ' + + f'are {hashlib.algorithms_available}.' ), ): - downloader = StaticFetcher(cache_path=tmp_path, hash=hash) + StaticFetcher(cache_path=tmp_path, hash=hash) def test_append_error(self, tmp_path): From 2d7c289adece186e9cb5d4cff6b0f6f888f3b4f0 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 5 Aug 2026 13:12:06 -0700 Subject: [PATCH 099/100] applied black --- testsuite/MDAnalysisTests/fetch/servers.py | 5 +++-- testsuite/MDAnalysisTests/fetch/test_static_fetcher.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/testsuite/MDAnalysisTests/fetch/servers.py b/testsuite/MDAnalysisTests/fetch/servers.py index 2656a8af75..4bc983b85c 100644 --- a/testsuite/MDAnalysisTests/fetch/servers.py +++ b/testsuite/MDAnalysisTests/fetch/servers.py @@ -46,8 +46,9 @@ def temporary_http_server(): # This is a beautiful quote from the GROMACS source code (temp_folder / "TEST_FILE3.txt").write_text( - "Unlike teachers or doctors, our efforts improve the lives of " + - "people we'll never meet. \n" + "- Katie Busch-Sorensen" + "Unlike teachers or doctors, our efforts improve the lives of " + + "people we'll never meet. \n" + + "- Katie Busch-Sorensen" ) http_handler = partial( diff --git a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py index 085f962297..5b8dea84b0 100644 --- a/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py +++ b/testsuite/MDAnalysisTests/fetch/test_static_fetcher.py @@ -88,8 +88,8 @@ def test_invalid_hash(self, tmp_path): with pytest.raises( ValueError, match=re.escape( - f'Invalid hash "{hash}". Valid hashes algorithms ' + - f'are {hashlib.algorithms_available}.' + f'Invalid hash "{hash}". Valid hashes algorithms ' + + f"are {hashlib.algorithms_available}." ), ): StaticFetcher(cache_path=tmp_path, hash=hash) From 1d1c8c4f34f3a595e98c1d2a1e9db97f7d48e995 Mon Sep 17 00:00:00 2001 From: Joshua Raphael Uy Date: Wed, 5 Aug 2026 13:14:42 -0700 Subject: [PATCH 100/100] removed not needed pass statement --- package/MDAnalysis/fetch/fetchers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/package/MDAnalysis/fetch/fetchers.py b/package/MDAnalysis/fetch/fetchers.py index 05bfbf0b40..dc027e718c 100644 --- a/package/MDAnalysis/fetch/fetchers.py +++ b/package/MDAnalysis/fetch/fetchers.py @@ -106,7 +106,6 @@ def fetch(self, base_url, verbose, timeout, retries): # All fetchers should call _check_pooch() # # These arguments should be implemented by all child Fetchers. - pass def _check_pooch( self,