From 3c8dd1985b6c969f1d42ab4256973754a424fc21 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Apr 2026 19:28:15 +0000 Subject: [PATCH] Fix migration not registering species when old config was replaced After `pip install -U`, pip replaces the package HCRconfig.yaml with a fresh copy (species: {}), so the migration found and copied index files but had no species entries to migrate. Now, when index directories are migrated, they are auto-registered as species entries using the directory name as both the species key and index prefix. Bump version: 0.3.1 -> 0.3.2 https://claude.ai/code/session_01L9BrYriTeA72fsqqTvgdxy --- VERSIONINFO.md | 3 +++ setup.cfg | 2 +- src/HCRProbeDesign/_datadir.py | 38 ++++++++++++++++++++++++------- tests/test_datadir.py | 41 ++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/VERSIONINFO.md b/VERSIONINFO.md index 865f6ea..d73e6e9 100644 --- a/VERSIONINFO.md +++ b/VERSIONINFO.md @@ -1,3 +1,6 @@ +## v0.3.2 - 04.08.2026 + + Fixed migration bug where species config entries were not restored after `pip install -U` + + Index directories found in the old package location are now auto-registered as species ## v0.3.1 - 04.08.2026 + Moved config and indices to persistent user data directory (`~/.hcrprobedesign/`) + Reference genomes and configuration now survive `pip install -U` upgrades diff --git a/setup.cfg b/setup.cfg index f340d73..df70e68 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [metadata] # replace with your username: name = hcrprobedesign -version = 0.3.1 +version = 0.3.2 author = Loyal A. Goff author_email = loyalgoff@jhmi.edu description = Probe Design tool for Hybridization Chain Reaction diff --git a/src/HCRProbeDesign/_datadir.py b/src/HCRProbeDesign/_datadir.py index da0d817..d4530d0 100644 --- a/src/HCRProbeDesign/_datadir.py +++ b/src/HCRProbeDesign/_datadir.py @@ -110,6 +110,7 @@ def _migrate_old_data(): new_species = new_config.setdefault("species", {}) migrated_any = False + migrated_index_dirs = [] # Migrate index directories if os.path.isdir(old_indices_dir): @@ -127,16 +128,17 @@ def _migrate_old_data(): f" Skipping index '{entry}' (already exists in {new_indices_dir})", file=sys.stderr, ) - continue + else: + print( + f" Moving index '{entry}' -> {new_entry_path}", + file=sys.stderr, + ) + shutil.copytree(old_entry_path, new_entry_path) + migrated_any = True - print( - f" Moving index '{entry}' -> {new_entry_path}", - file=sys.stderr, - ) - shutil.copytree(old_entry_path, new_entry_path) - migrated_any = True + migrated_index_dirs.append(entry) - # Migrate species config entries + # Migrate species config entries from old config for name, entry in old_species.items(): if name in new_species: continue @@ -157,6 +159,26 @@ def _migrate_old_data(): file=sys.stderr, ) + # Auto-register species for migrated index dirs that have no config entry. + # This handles the case where pip replaced the old HCRconfig.yaml with a + # fresh copy (species: {}) during upgrade, so the config entries were lost + # even though the index files survived. + for dirname in migrated_index_dirs: + # Check if any existing species entry already points to this index dir + index_prefix = os.path.join(new_indices_dir, dirname, dirname) + already_registered = any( + sp.get("bowtie2_index", "") == index_prefix + or os.path.basename(sp.get("bowtie2_index", "")).startswith(dirname) + for sp in new_species.values() + ) + if dirname not in new_species and not already_registered: + new_species[dirname] = {"bowtie2_index": index_prefix} + migrated_any = True + print( + f" Auto-registered species '{dirname}' -> {index_prefix}", + file=sys.stderr, + ) + # Preserve default_params from old config if not present in new if "default_params" not in new_config and "default_params" in old_config: new_config["default_params"] = old_config["default_params"] diff --git a/tests/test_datadir.py b/tests/test_datadir.py index 26f17fe..abaeabf 100644 --- a/tests/test_datadir.py +++ b/tests/test_datadir.py @@ -111,6 +111,47 @@ def test_migrate_old_data(tmp_path, monkeypatch): assert len(bt2_files) == 6 +def test_migrate_auto_registers_species_when_config_empty(tmp_path, monkeypatch): + """Index files survive pip upgrade but old config was replaced with species: {}. + Migration should auto-register species based on index directory names.""" + data_dir = tmp_path / "data" + monkeypatch.setenv("HCRPROBEDESIGN_DATA_DIR", str(data_dir)) + + # Set up old package dir with index files but EMPTY species config + # (simulates pip replacing HCRconfig.yaml with the clean default) + old_pkg = tmp_path / "old_pkg" + old_indices = old_pkg / "indices" / "mm10" + old_indices.mkdir(parents=True) + for suffix in ["1.bt2", "2.bt2", "3.bt2", "4.bt2", "rev.1.bt2", "rev.2.bt2"]: + (old_indices / f"mm10.{suffix}").write_text("fake") + + old_config_path = old_pkg / "HCRconfig.yaml" + old_config = { + "species": {}, + "default_params": {"tileSize": 52}, + } + with open(old_config_path, "w") as fh: + yaml.safe_dump(old_config, fh) + + monkeypatch.setattr(_datadir, "_PACKAGE_DIRECTORY", str(old_pkg)) + monkeypatch.setattr(_datadir, "_SEED_CONFIG", str(old_config_path)) + + _datadir.ensure_data_dir() + + # Index files should be migrated + new_index_dir = data_dir / "indices" / "mm10" + assert new_index_dir.exists() + bt2_files = glob.glob(str(new_index_dir / "*.bt2*")) + assert len(bt2_files) == 6 + + # Species should be auto-registered from the directory name + new_config = yaml.safe_load((data_dir / "HCRconfig.yaml").read_text()) + assert "mm10" in new_config["species"] + assert new_config["species"]["mm10"]["bowtie2_index"] == os.path.join( + str(data_dir), "indices", "mm10", "mm10" + ) + + def test_has_old_data_empty(tmp_path, monkeypatch): monkeypatch.setattr(_datadir, "_PACKAGE_DIRECTORY", str(tmp_path)) # Write an empty config