The title is self-explanatory. Is there a way to downgrade the conda packages to the ones that were the latest on a certain date?
2 Answers
This is not possible programmatically. Packages in Conda are specified through MatchSpec, which does not currently have any way to constrain on a build timestamp.
Manual Searching
When searching for packages via conda search
, the --info
flag will print the build timestamps if they are available. So, for example, if one wanted to find the latest version of PyMC3 that someone with Python 3.6 was running a year ago (9 Dec 2018), one could check
conda search --info 'conda-forge::pymc3'
and see that version 3.5, build py36_1000 would satisfy this. If one wanted to create an env with this build in it, they could use
conda create -n py36_pymc35 -c conda-forge pymc3=3.5=py36_1000

- 67,214
- 13
- 180
- 245
2023 Update
In addition to Merv's post, I may add that the --json
flag makes it actually quite easy to programmatically gather the history. Once you have the history, you can search for the latest package versions as of some date, and make an environment with them (we do that routinely to establish "low watermark" environments for our CIs).
The conda
command line invocation is:
f'conda search -q {package} --info --json`
Here is some code that uses that to gather the history of a few packages. It is also multi-threaded to speed up things a little.
import io
import json
import subprocess
import yaml
from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime, timedelta
from tqdm import tqdm
def shell(cmd):
proc = subprocess.run(cmd, shell=True, capture_output=True)
return proc.stdout.decode('utf-8')
def version_as_tuple(v):
return tuple(map(int, v.split('.')))
def get_history(p):
txt = shell(f"conda search -q {p} --info --json")
d = json.loads(txt)
h = defaultdict(set)
for vv in d.values():
for x in vv:
h[version_as_tuple(x['version'])].add(
datetime.fromtimestamp(x.get('timestamp', 0) / 1e3)
)
h = {vers: min(dates) for vers, dates in h.items()}
return p, h
Example usage:
metayaml = """
- boto3
- pandas >=0.25
- python >=3.8
"""
reqs = yaml.safe_load(metayaml) # in real life, read from conda.recipe/meta.yaml
all_pkgs = sorted(set([p.split()[0] for p in reqs]))
with ThreadPoolExecutor() as pool:
history = dict(tqdm(pool.map(get_history, all_pkgs), total=len(all_pkgs)))
After that, we have a neat version history for all dependent packages. For example:
>>> {v: f'{t:%Y-%m-%d}' for v, t in history['pandas'].items()}
{(0, 20, 3): '2017-09-18',
(0, 21, 0): '2017-11-06',
(0, 21, 1): '2017-12-12',
...
(1, 4, 4): '2022-09-21',
(1, 5, 1): '2022-11-16',
(1, 5, 2): '2022-12-07'}
And:
asof = datetime.now() - timedelta(weeks=2*52)
new = {
name: max([(vers, t) for vers, t in v.items() if t < asof])
for name, v in history.items()
}
print(f'# as of {asof:%Y-%m-%d}')
for name, (vers, t) in new.items():
print(f' - {name} =={".".join(map(str, vers))} # released on {t:%Y-%m-%d}')
Which produces:
# as of 2021-01-20
- boto3 ==1.16.55 # released on 2021-01-15
- pandas ==1.2.0 # released on 2020-12-26
- python ==3.9.1 # released on 2020-12-11

- 24,012
- 7
- 60
- 96