Skip to content

Utilities

Structures

fetch_structure

fetch_structure(identifier, source='auto', version='v6', frag=1, cache_dir=None, overwrite=False)

Return a local path to a PDB structure, downloading it if needed.

Parameters:

Name Type Description Default
identifier str

A local file path, a 4-character PDB id (RCSB), or a UniProt accession (AlphaFold DB).

required
source (auto, file, rcsb, afdb)

Where to fetch from; 'auto' infers it from the identifier.

'auto'
version str

AFDB model version (default 'v4'; its coordinates are carried forward by later AFDB releases, so this stays the stable default).

'v6'
frag int

AFDB fragment number (1 for all but very long proteins).

1
cache_dir str or None
None
Source code in makeshift/utils/structures.py
def fetch_structure(identifier, source="auto", version="v6", frag=1,
                    cache_dir=None, overwrite=False):
    """
    Return a local path to a PDB structure, downloading it if needed.

    Parameters
    ----------
    identifier : str
        A local file path, a 4-character PDB id (RCSB), or a UniProt accession
        (AlphaFold DB).
    source : {'auto', 'file', 'rcsb', 'afdb'}
        Where to fetch from; 'auto' infers it from the identifier.
    version : str
        AFDB model version (default 'v4'; its coordinates are carried forward by
        later AFDB releases, so this stays the stable default).
    frag : int
        AFDB fragment number (1 for all but very long proteins).
    cache_dir : str or None
    """
    source = source.lower()
    if source == "auto":
        source = detect_source(identifier)

    if source == "file":
        if not os.path.exists(identifier):
            raise FileNotFoundError(identifier)
        return identifier

    cache_dir = cache_dir or _default_cache()
    os.makedirs(cache_dir, exist_ok=True)

    if source == "rcsb":
        pid = identifier.strip().lower()
        url = _RCSB_URL.format(id=pid)
        dest = os.path.join(cache_dir, f"{pid}.pdb")
    elif source == "afdb":
        acc = identifier.strip().upper()
        url = _AFDB_URL.format(acc=acc, frag=frag, version=version)
        dest = os.path.join(cache_dir, f"AF-{acc}-F{frag}-{version}.pdb")
    else:
        raise ValueError(f"unknown source {source!r}. Should be auto, file, rcsb, or afdb")

    if os.path.exists(dest) and not overwrite:
        return dest
    try:
        urllib.request.urlretrieve(url, dest)
    except urllib.error.HTTPError as e:
        if os.path.exists(dest):
            os.remove(dest)
        raise ValueError(
            f"could not fetch {identifier!r} from {source} ({url}): HTTP {e.code}"
        ) from e
    return dest

detect_source

detect_source(identifier)

Infer where a structure identifier should be fetched from: 'file' if it's an existing path 'rcsb' for a PDB id, 'afdb' for a UniProt accession Raises if it matches neither pattern (pass source= explicitly).

Source code in makeshift/utils/structures.py
def detect_source(identifier):
    """
    Infer where a structure identifier should be fetched from:
        'file' if it's an existing path
        'rcsb' for a PDB id, 
        'afdb' for a UniProt accession
    Raises if it matches neither pattern (pass source= explicitly).
    """
    if os.path.exists(identifier):
        return "file"
    s = identifier.strip()
    if _PDB_RE.match(s):
        return "rcsb"
    if _UNIPROT_RE.match(s.upper()):
        return "afdb"
    raise ValueError(
        f"could not infer a source for {identifier!r}; "
        "pass source='file', 'rcsb', or 'afdb'"
    )

Datasets

fetch

fetch(name: str, url: Optional[str] = None, sha256: Optional[str] = None, cache_dir: Optional[Path] = None, overwrite: bool = False) -> Path

Download, verify, cache, and extract a dataset zip.

Three ways to call it:

  • Registered dataset — pass a name from the DATASETS registry::

    fetch("SHP2_NSH2_CPMG")

  • Arbitrary dataset — pass a url (and optionally sha256); name is used as the cache folder::

    fetch("my_set", url="https://.../my_set.zip", sha256="...")

  • A URL directly as name — the cache folder is derived from the URL::

    fetch("https://.../my_set.zip")

Parameters:

Name Type Description Default
name str

A registered dataset name, a cache-folder name (with url), or a URL.

required
url str

Direct download URL. Overrides the registry; required for datasets not in DATASETS.

None
sha256 str

Expected hex digest for integrity verification. Falls back to the registry value for registered datasets; skipped if not available.

None
cache_dir Path

Root cache directory. Defaults to ~/.makeshift/datasets/.

None
overwrite bool

If True, re-download even if already cached.

False

Returns:

Type Description
Path

Directory containing the extracted dataset files.

Source code in makeshift/utils/datasets.py
def fetch(name: str, url: Optional[str] = None, sha256: Optional[str] = None,
          cache_dir: Optional[Path] = None, overwrite: bool = False) -> Path:
    """
    Download, verify, cache, and extract a dataset zip.

    Three ways to call it:

    * Registered dataset — pass a ``name`` from the DATASETS registry::

          fetch("SHP2_NSH2_CPMG")

    * Arbitrary dataset — pass a ``url`` (and optionally ``sha256``); ``name``
      is used as the cache folder::

          fetch("my_set", url="https://.../my_set.zip", sha256="...")

    * A URL directly as ``name`` — the cache folder is derived from the URL::

          fetch("https://.../my_set.zip")

    Parameters
    ----------
    name : str
        A registered dataset name, a cache-folder name (with ``url``), or a URL.
    url : str, optional
        Direct download URL. Overrides the registry; required for datasets not
        in DATASETS.
    sha256 : str, optional
        Expected hex digest for integrity verification. Falls back to the
        registry value for registered datasets; skipped if not available.
    cache_dir : Path, optional
        Root cache directory. Defaults to ~/.makeshift/datasets/.
    overwrite : bool
        If True, re-download even if already cached.

    Returns
    -------
    Path
        Directory containing the extracted dataset files.
    """
    # allow a URL to be passed directly as `name`
    if url is None and _looks_like_url(name):
        url = name
        name = Path(urlparse(url).path).stem

    if url is None:
        # registry lookup
        if name not in DATASETS:
            available = list(DATASETS)
            raise ValueError(
                f"Unknown dataset {name!r} and no url given. "
                f"Registered: {available if available else '(none registered)'}. "
                f"Pass url=... to fetch an arbitrary dataset."
            )
        entry = DATASETS[name]
        url = entry["url"]
        if sha256 is None:
            sha256 = entry.get("sha256")

    root = Path(cache_dir) if cache_dir is not None else _CACHE_DIR
    extract_dir = root / name
    marker = extract_dir / ".complete"
    if marker.exists() and not overwrite:
        return _locate_data(extract_dir)

    extract_dir.mkdir(parents=True, exist_ok=True)
    zip_path = extract_dir / f"{name}.zip"
    print(f"Downloading {name} from {url} ...")
    urllib.request.urlretrieve(url, zip_path, reporthook=_progress_hook())
    print()

    if sha256:
        actual = _sha256(zip_path)
        if actual != sha256:
            zip_path.unlink()
            raise RuntimeError(
                f"Checksum mismatch for {name}.\n"
                f"  expected: {sha256}\n"
                f"  got:      {actual}"
            )

    print(f"Extracting to {extract_dir} ...")
    with zipfile.ZipFile(zip_path, "r") as zf:
        zf.extractall(extract_dir)
    zip_path.unlink()
    marker.touch()

    data_dir = _locate_data(extract_dir)
    print(f"Dataset {name!r} ready at {data_dir}")
    return data_dir

list_datasets

list_datasets() -> list

Return names of all registered datasets.

Source code in makeshift/utils/datasets.py
def list_datasets() -> list:
    """Return names of all registered datasets."""
    return list(DATASETS)

Reference tables

tables

get_random_coil

get_random_coil()

Random coil chemical shifts (Wishart & Sykes 1994). Returns {residue: {atom: float}}.

Source code in makeshift/data/tables.py
def get_random_coil():
    """Random coil chemical shifts (Wishart & Sykes 1994). Returns {residue: {atom: float}}."""
    df = pd.read_csv(_DATA / 'random_coil.csv')
    out = {}
    for _, row in df.iterrows():
        res, atom = row['residue'], row['atom']
        val = row['value']
        out.setdefault(res, {})[atom] = np.nan if (isinstance(val, float) and np.isnan(val)) or val == '' else float(val)
    return out

get_panav_distns

get_panav_distns()

PANAV reference distributions. Returns {residue: {ss: {atom: (mean, std)}}}.

Source code in makeshift/data/tables.py
def get_panav_distns():
    """PANAV reference distributions. Returns {residue: {ss: {atom: (mean, std)}}}."""
    df = pd.read_csv(_DATA / 'panav_distns.csv')
    out = {}
    for _, row in df.iterrows():
        res  = row['AA'].upper()
        ss   = row['SS']
        atom = row['Atom_name'].upper()
        out.setdefault(res, {}).setdefault(ss, {})[atom] = (float(row['mean']), float(row['stdev']))
    return out

get_bmrb_stats

get_bmrb_stats()

BMRB full-database statistics. Returns {residue: {atom: (mean, std)}}.

Source code in makeshift/data/tables.py
def get_bmrb_stats():
    """BMRB full-database statistics. Returns {residue: {atom: (mean, std)}}."""
    df = pd.read_csv(_DATA / 'bmrb_stats.csv')
    out = {}
    for _, row in df.iterrows():
        out.setdefault(row['residue'], {})[row['atom']] = (float(row['mean']), float(row['std']))
    return out

get_c_prime_rc

get_c_prime_rc()

C' random coil values (Wishart et al. 1995). Returns {residue: float}.

Source code in makeshift/data/tables.py
def get_c_prime_rc():
    """C' random coil values (Wishart et al. 1995). Returns {residue: float}."""
    df = pd.read_csv(_DATA / 'c_prime_rc.csv')
    return dict(zip(df['residue'], df['value'].astype(float)))