Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions VERSIONINFO.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -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
Expand Down
38 changes: 30 additions & 8 deletions src/HCRProbeDesign/_datadir.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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
Expand All @@ -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"]
Expand Down
41 changes: 41 additions & 0 deletions tests/test_datadir.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down