Installing the offline databases¶
PROVESID answers most questions from five local databases. They are large, so
none of them ships with the package and none is downloaded behind your back:
Search runs on whatever is installed and says what is missing, and
provesid.datasets installs them by name.
| name | client | role | download | on disk |
|---|---|---|---|---|
pubchem |
PubChemID |
CAS, name, InChIKey and formula lookups; the broadest source | 14.3 GiB | 2.3 GiB |
comptox |
CompToxID |
DTXSID lookups, and curated CAS–name pairs | 817 MiB | 1.1 GiB |
chebi |
ChebiSDF |
curated structures, synonyms and ChEBI IDs | 250 MiB | 954 MiB |
chembl |
CheMBL |
enrichment: adds ChEMBL IDs to structures already found | 5.7 GiB | 2.4 GiB |
zeropm |
ZeroPM |
regulatory inventories; off unless Search(sources=...) names it |
439 MiB | 439 MiB |
The first four are what Search queries by default. All five together are
about 21.5 GiB to download and 7.2 GiB to keep; see below for why the free
space needed on the way in is larger than either.
Status, plan, fetch, remove¶
from provesid import datasets
datasets.status() # what is installed, where, and which release
datasets.plan(["pubchem", "chebi"]) # what a fetch would transfer and leave behind
datasets.fetch(["pubchem", "chebi"]) # install them
datasets.remove("chembl") # reclaim the space
On an empty data directory, plan() reads:
dataset action download installed
pubchem download 14.3 GiB 2.3 GiB
comptox download 816.6 MiB 1.1 GiB
chebi download 250.0 MiB 954.2 MiB
chembl download 5.7 GiB 2.4 GiB
zeropm download 438.7 MiB 438.7 MiB
and its attrs carry the totals: total_download_bytes,
total_resident_bytes and peak_bytes, the free space needed at the worst
moment of the install. datasets.human_bytes formats any of them.
fetchhas no "all" default. Everything is ~21.5 GiB of transfer and, while ChEMBL unpacks, up to ~38 GiB of free disk. That has to be asked for by name.- Every transfer resumes. An interrupted download leaves a
.partfile that the nextfetchcontinues with an HTTPRangerequest. A file is checked against the MD5 its server publishes, where it publishes one, and against the size the server declared, before it is moved into place, so a failed download never replaces a good database. fetchskips what is present. Calling it again on the same list is cheap.force=Truedownloads again; for ChEMBL that means the current release, which may be newer than the one installed.remove(..., dry_run=True)lists the files it would delete, including the partial downloads and build leftovers each dataset can leave behind.
Where they go¶
The data directory is the per-user one platformdirs picks —
~/.local/share/provesid on Linux, ~/Library/Application Support/provesid
on macOS, %LOCALAPPDATA%\USEtox\provesid on Windows — and is shared by every
virtual environment on the machine. Set PROVESID_DATA_DIR to put it
elsewhere, such as a larger disk:
export PROVESID_DATA_DIR=/data/provesid
Every client and every datasets function also takes data_dir=. The data
directory holds data you would have to download again; the
response cache is a separate directory of things that can be
re-fetched at any time.
What Search does when one is missing¶
Search(datasets=...) decides:
datasets= |
when a database is missing |
|---|---|
"present" (default) |
run on the ones installed, and log once which are missing and what installing them would cost; df.attrs["sources_unavailable"] names them |
"auto" |
download what is missing, then run |
"required" |
raise MissingDatasetError in the constructor, naming the fetch call |
MissingDatasetError: 1 dataset(s) missing from /home/me/.local/share/provesid:
PubChem identifiers (pubchem): 14.3 GiB to download, 2.3 GiB on disk --- CAS, name, InChIKey and formula lookups; the broadest source
Install them with:
provesid.datasets.fetch('pubchem')
datasets.missing() answers the same question without constructing anything,
and datasets.require(names) raises that error for your own code.
The clients themselves do download
Constructing a client directly — PubChemID(), CheMBL() and the others —
downloads its database if it is absent, because auto_download defaults to
True. Pass auto_download=False to get a FileNotFoundError instead.
Only Search defaults to not downloading.
PubChem: built from the FTP site, or a prebuilt copy¶
PubChemID reads pubchem_id.db: the ~1.43 million PubChem compounds that
carry a CAS number, with their CAS numbers, title, IUPAC name, formula, masses,
SMILES, InChI, InChIKey and synonyms. By default it is built from a dated
monthly snapshot of PubChem's FTP site:
from provesid import PubChemID
from provesid.pubchem_ftp import build_pubchem_id_db, list_releases
PubChemID() # source="ftp": build from the newest snapshot
PubChemID(source="zenodo") # download a 2.2 GiB prebuilt copy instead
list_releases() # ['2026-09-01', '2026-08-01', ..., 'current']
build_pubchem_id_db(release="2026-09-01")
- The compounds are those PubChem's own
CID-Identifiers.tsv.gzmaps to a CAS number, each checked against the CAS check digit. - Eight files are downloaded, read and deleted one at a time, so the free space
needed is the finished database plus the largest file (7.4 GB), not the
whole 15.4 GB. Processing takes about twelve minutes on top of the download.
keep_downloads=Truekeeps them for a later rebuild. include_inchi=Falseskips the 7.4 GB InChI file and computes InChI and InChIKey with RDKit instead: less to download, a longer build, and RDKit's InChI rather than PubChem's.- The molecular weight is in no FTP file, so it is computed from the formula.
- The built database records its release and every source file's URL and MD5
(
PubChemID().provenance()), and carries PubChem's cross-references to ChEBI, ChEMBL, CompTox, EC and UNII (PubChemID().xrefs(cid)).
The Zenodo copy is older, finds its CAS numbers by pattern-matching synonyms
(1.25% of them are not valid CAS numbers), and has no provenance or
cross-reference tables. source= only decides how a missing database is
obtained; one already on disk is opened whichever way it was made.
From a shell: python scripts/build_pubchem_id_db.py --help.
ChEMBL: three routes to the same extract¶
A ChEMBL release is a 27.7 GiB SQLite database with 74 tables, of which
PROVESID reads eight. CheMBL installs an extract of those eight tables,
2.4 GiB, and the route decides only what is downloaded to build it:
CheMBL(source=...) |
download | free disk at peak | installed |
|---|---|---|---|
"sqlite" (default) |
5.8 GB | 33.4 GiB | 2.4 GiB |
"mysql" |
2.1 GB | ~4.5 GiB | 2.4 GiB |
"full" |
5.8 GB | 33.4 GiB | 27.7 GiB |
"sqlite"downloads the SQLite archive, unpacks it, compacts it and deletes the full release. If the compaction fails, the full database is kept and opened instead, so the download is not wasted."mysql"reads the eight tables straight out of ChEMBL's plain-text MySQL dump as it is decompressed. No MySQL server is involved. Its result is the same as the SQLite route's, table for table;CheMBL.extract_digest()fingerprints an extract so that two can be compared."full"keeps the whole release, for queries outside PROVESID.
A full release already on disk can be compacted in place:
from provesid import CheMBL
CheMBL.compact(remove_source=True) # 27.7 GiB -> 2.4 GiB
CheMBL does not pin a release. On first use it reads the latest/ listing on
EBI's FTP site and downloads what it advertises; after that, any installed
ChEMBL database is reused, so a new release does not trigger a download.
datasets.fetch("chembl", force=True) moves to the newest one.
CompTox, ChEBI and ZeroPM¶
- CompTox is downloaded as one SQLite file from Zenodo. A name index is
added to it straight after the download (about 20 s, 290 MiB), so an exact
name lookup finds synonyms as well as the preferred name. A database
downloaded before the index existed builds it on its first exact name
lookup;
CompToxID().build_name_index()does it at a time of your choosing. - ChEBI is downloaded as a gzipped SDF, expanded, and indexed on first use, which takes a few minutes once.
- ZeroPM is one SQLite file from the ZeroPM project's repository.
Searchleaves it out unless asked: its records are harvested from regulatory inventories rather than curated compound by compound, so it is noisier than the other four. It is the one source that retrieves misspelled names, which is whySearch(preset="recall")turns it on.