From e7abbe37773471fa820836aa73f4fbebcad29735 Mon Sep 17 00:00:00 2001 From: Patrick Kunzmann Date: Wed, 1 Jul 2026 10:28:19 +0200 Subject: [PATCH] Refactor test suite --- benchmarks/sequence/align/benchmark_kmer.py | 3 +- .../sequence/align/benchmark_multiple.py | 3 +- .../sequence/align/benchmark_pairwise.py | 3 +- .../sequence/align/benchmark_selector.py | 3 +- benchmarks/sequence/benchmark_fasta.py | 11 +- benchmarks/sequence/benchmark_phylo.py | 3 +- benchmarks/structure/benchmark_alphabet.py | 5 +- benchmarks/structure/benchmark_bonds.py | 5 +- benchmarks/structure/benchmark_celllist.py | 3 +- benchmarks/structure/benchmark_compare.py | 10 +- benchmarks/structure/benchmark_pdb.py | 3 +- benchmarks/structure/benchmark_pdbx.py | 13 +- benchmarks/structure/benchmark_sasa.py | 3 +- benchmarks/structure/benchmark_superimpose.py | 3 +- doc/tutorial/structure/trajectories.rst | 2 +- src/biotite/structure/basepairs.py | 8 +- src/biotite/structure/io/mol/mol.py | 2 +- src/biotite/structure/io/mol/sdf.py | 2 +- src/biotite/structure/io/pdbx/convert.py | 2 +- src/biotite/structure/rdf.py | 2 +- src/biotite/structure/rings.py | 2 +- tests/application/test_autodock.py | 3 +- tests/application/test_blast.py | 7 +- tests/application/test_dssp.py | 9 +- tests/application/test_msa.py | 4 +- tests/application/test_sra.py | 23 +-- tests/database/test_afdb.py | 9 +- tests/database/test_entrez.py | 10 +- tests/database/test_pubchem.py | 25 +-- tests/database/test_rcsb.py | 28 ++- tests/database/test_uniprot.py | 12 +- tests/interface/pymol/test_cgo.py | 4 +- tests/interface/pymol/test_command.py | 7 +- tests/interface/pymol/test_conversion.py | 3 +- tests/interface/pymol/test_display.py | 5 +- tests/interface/pymol/test_selection.py | 7 +- tests/interface/test_openmm.py | 7 +- tests/interface/test_rdkit.py | 3 +- tests/sequence/align/conftest.py | 3 +- tests/sequence/align/test_align.py | 2 +- tests/sequence/align/test_banded.py | 23 +-- tests/sequence/align/test_cigar.py | 58 +++--- tests/sequence/align/test_kmeralphabet.py | 19 +- tests/sequence/align/test_kmersimilarity.py | 6 +- tests/sequence/align/test_kmertable.py | 57 +++-- tests/sequence/align/test_localgapped.py | 33 +-- tests/sequence/align/test_localungapped.py | 138 ++++++------- tests/sequence/align/test_matrix.py | 8 +- tests/sequence/align/test_pairwise.py | 36 ++-- tests/sequence/align/test_permutation.py | 16 +- tests/sequence/align/test_selector.py | 40 ++-- tests/sequence/align/test_statistics.py | 2 +- tests/sequence/align/util.py | 7 +- tests/sequence/test_alphabet.py | 19 +- tests/sequence/test_annotation.py | 3 +- tests/sequence/test_clustal.py | 16 +- tests/sequence/test_fasta.py | 37 ++-- tests/sequence/test_fastq.py | 29 ++- tests/sequence/test_genbank.py | 27 +-- tests/sequence/test_generalio.py | 41 ++-- tests/sequence/test_gff.py | 13 +- tests/sequence/test_graphics.py | 8 +- tests/sequence/test_phylo.py | 9 +- tests/sequence/test_seqtypes.py | 12 +- tests/structure/alphabet/test_i3d.py | 11 +- tests/structure/alphabet/test_pb.py | 5 +- tests/structure/data/{ => pdb}/1aki.bcif | Bin tests/structure/data/{ => pdb}/1aki.cif | 0 tests/structure/data/{ => pdb}/1aki.gro | 0 tests/structure/data/{ => pdb}/1aki.pdb | 0 tests/structure/data/{ => pdb}/1crr.bcif | Bin tests/structure/data/{ => pdb}/1crr.cif | 0 tests/structure/data/{ => pdb}/1crr.gro | 0 tests/structure/data/{ => pdb}/1crr.pdb | 0 tests/structure/data/{ => pdb}/1dix.bcif | Bin tests/structure/data/{ => pdb}/1dix.cif | 0 tests/structure/data/{ => pdb}/1dix.gro | 0 tests/structure/data/{ => pdb}/1dix.pdb | 0 tests/structure/data/{ => pdb}/1f2n.bcif | Bin tests/structure/data/{ => pdb}/1f2n.cif | 0 tests/structure/data/{ => pdb}/1f2n.gro | 0 tests/structure/data/{ => pdb}/1f2n.pdb | 0 tests/structure/data/{ => pdb}/1gya.bcif | Bin tests/structure/data/{ => pdb}/1gya.cif | 0 tests/structure/data/{ => pdb}/1gya.gro | 0 tests/structure/data/{ => pdb}/1gya.pdb | 0 tests/structure/data/{ => pdb}/1igy.bcif | Bin tests/structure/data/{ => pdb}/1igy.cif | 0 tests/structure/data/{ => pdb}/1igy.gro | 0 tests/structure/data/{ => pdb}/1igy.pdb | 0 tests/structure/data/{ => pdb}/1k6p.bcif | Bin tests/structure/data/{ => pdb}/1k6p.cif | 0 tests/structure/data/{ => pdb}/1k6p.pdb | 0 tests/structure/data/{ => pdb}/1l2y.bcif | Bin tests/structure/data/{ => pdb}/1l2y.cif | 0 tests/structure/data/{ => pdb}/1l2y.dcd | Bin tests/structure/data/{ => pdb}/1l2y.gro | 0 tests/structure/data/{ => pdb}/1l2y.netcdf | Bin tests/structure/data/{ => pdb}/1l2y.pdb | 0 tests/structure/data/{ => pdb}/1l2y.trr | Bin tests/structure/data/{ => pdb}/1l2y.xtc | Bin tests/structure/data/{ => pdb}/1ncb.bcif | Bin tests/structure/data/{ => pdb}/1ncb.cif | 0 tests/structure/data/{ => pdb}/1o1z.bcif | Bin tests/structure/data/{ => pdb}/1o1z.cif | 0 tests/structure/data/{ => pdb}/1o1z.gro | 0 tests/structure/data/{ => pdb}/1o1z.pdb | 0 tests/structure/data/{ => pdb}/2axd.bcif | Bin tests/structure/data/{ => pdb}/2axd.cif | 0 tests/structure/data/{ => pdb}/2axd.pdb | 0 tests/structure/data/{ => pdb}/2d0f.bcif | Bin tests/structure/data/{ => pdb}/2d0f.cif | 0 tests/structure/data/{ => pdb}/2d0f.pdb | 0 tests/structure/data/{ => pdb}/3o5r.bcif | Bin tests/structure/data/{ => pdb}/3o5r.cif | 0 tests/structure/data/{ => pdb}/3o5r.gro | 0 tests/structure/data/{ => pdb}/3o5r.pdb | 0 tests/structure/data/{ => pdb}/3wip.bcif | Bin tests/structure/data/{ => pdb}/3wip.cif | 0 tests/structure/data/{ => pdb}/3wip.pdb | 0 tests/structure/data/{ => pdb}/4gxy.bcif | Bin tests/structure/data/{ => pdb}/4gxy.cif | 0 tests/structure/data/{ => pdb}/4gxy.gro | 0 tests/structure/data/{ => pdb}/4gxy.pdb | 0 tests/structure/data/{ => pdb}/4i39.bcif | Bin tests/structure/data/{ => pdb}/4i39.cif | 0 tests/structure/data/{ => pdb}/4p5j.bcif | Bin tests/structure/data/{ => pdb}/4p5j.cif | 0 tests/structure/data/{ => pdb}/4p5j.gro | 0 tests/structure/data/{ => pdb}/4p5j.pdb | 0 tests/structure/data/{ => pdb}/4zxb.bcif | Bin tests/structure/data/{ => pdb}/4zxb.cif | 0 tests/structure/data/{ => pdb}/5eil.bcif | Bin tests/structure/data/{ => pdb}/5eil.cif | 0 tests/structure/data/{ => pdb}/5eil.pdb | 0 tests/structure/data/{ => pdb}/5h73.bcif | Bin tests/structure/data/{ => pdb}/5h73.cif | 0 tests/structure/data/{ => pdb}/5h73.gro | 0 tests/structure/data/{ => pdb}/5h73.pdb | 0 tests/structure/data/{ => pdb}/5ugo.bcif | Bin tests/structure/data/{ => pdb}/5ugo.cif | 0 tests/structure/data/{ => pdb}/5ugo.gro | 0 tests/structure/data/{ => pdb}/5ugo.pdb | 0 tests/structure/data/{ => pdb}/5zng.bcif | Bin tests/structure/data/{ => pdb}/5zng.cif | 0 tests/structure/data/{ => pdb}/5zng.pdb | 0 tests/structure/data/{ => pdb}/7gsa.bcif | Bin tests/structure/data/{ => pdb}/7gsa.cif | 0 tests/structure/data/{ => pdb}/README.rst | 0 .../data/{ => pdb}/create_test_structures.py | 18 +- tests/structure/data/{ => pdb}/ids.txt | 0 tests/structure/io/test_gro.py | 34 +-- tests/structure/io/test_mol.py | 68 +++--- tests/structure/io/test_pdb.py | 98 +++++---- tests/structure/io/test_pdbqt.py | 7 +- tests/structure/io/test_pdbx.py | 195 +++++++++--------- tests/structure/io/test_trajectory.py | 83 +++----- tests/structure/test_basepairs.py | 22 +- tests/structure/test_bonds.py | 19 +- tests/structure/test_box.py | 43 ++-- tests/structure/test_celllist.py | 7 +- tests/structure/test_chains.py | 15 +- tests/structure/test_charges.py | 2 +- tests/structure/test_compare.py | 19 +- tests/structure/test_connect.py | 13 +- tests/structure/test_dotbracket.py | 7 +- tests/structure/test_filter.py | 28 ++- tests/structure/test_generalio.py | 57 +++-- tests/structure/test_geometry.py | 59 +++--- tests/structure/test_hbond.py | 23 ++- tests/structure/test_info.py | 30 +-- tests/structure/test_integrity.py | 3 +- tests/structure/test_mechanics.py | 7 +- tests/structure/test_molecules.py | 10 +- tests/structure/test_pseudoknots.py | 17 +- tests/structure/test_rdf.py | 18 +- tests/structure/test_repair.py | 7 +- tests/structure/test_residues.py | 15 +- tests/structure/test_rings.py | 13 +- tests/structure/test_sasa.py | 7 +- tests/structure/test_sequence.py | 10 +- tests/structure/test_sse.py | 21 +- tests/structure/test_superimpose.py | 98 +++++---- tests/structure/test_tm.py | 19 +- tests/structure/test_transform.py | 69 +++---- tests/test_doctest.py | 16 +- tests/test_modname.py | 8 +- tests/util.py | 4 +- 188 files changed, 1000 insertions(+), 1124 deletions(-) rename tests/structure/data/{ => pdb}/1aki.bcif (100%) rename tests/structure/data/{ => pdb}/1aki.cif (100%) rename tests/structure/data/{ => pdb}/1aki.gro (100%) rename tests/structure/data/{ => pdb}/1aki.pdb (100%) rename tests/structure/data/{ => pdb}/1crr.bcif (100%) rename tests/structure/data/{ => pdb}/1crr.cif (100%) rename tests/structure/data/{ => pdb}/1crr.gro (100%) rename tests/structure/data/{ => pdb}/1crr.pdb (100%) rename tests/structure/data/{ => pdb}/1dix.bcif (100%) rename tests/structure/data/{ => pdb}/1dix.cif (100%) rename tests/structure/data/{ => pdb}/1dix.gro (100%) rename tests/structure/data/{ => pdb}/1dix.pdb (100%) rename tests/structure/data/{ => pdb}/1f2n.bcif (100%) rename tests/structure/data/{ => pdb}/1f2n.cif (100%) rename tests/structure/data/{ => pdb}/1f2n.gro (100%) rename tests/structure/data/{ => pdb}/1f2n.pdb (100%) rename tests/structure/data/{ => pdb}/1gya.bcif (100%) rename tests/structure/data/{ => pdb}/1gya.cif (100%) rename tests/structure/data/{ => pdb}/1gya.gro (100%) rename tests/structure/data/{ => pdb}/1gya.pdb (100%) rename tests/structure/data/{ => pdb}/1igy.bcif (100%) rename tests/structure/data/{ => pdb}/1igy.cif (100%) rename tests/structure/data/{ => pdb}/1igy.gro (100%) rename tests/structure/data/{ => pdb}/1igy.pdb (100%) rename tests/structure/data/{ => pdb}/1k6p.bcif (100%) rename tests/structure/data/{ => pdb}/1k6p.cif (100%) rename tests/structure/data/{ => pdb}/1k6p.pdb (100%) rename tests/structure/data/{ => pdb}/1l2y.bcif (100%) rename tests/structure/data/{ => pdb}/1l2y.cif (100%) rename tests/structure/data/{ => pdb}/1l2y.dcd (100%) rename tests/structure/data/{ => pdb}/1l2y.gro (100%) rename tests/structure/data/{ => pdb}/1l2y.netcdf (100%) rename tests/structure/data/{ => pdb}/1l2y.pdb (100%) rename tests/structure/data/{ => pdb}/1l2y.trr (100%) rename tests/structure/data/{ => pdb}/1l2y.xtc (100%) rename tests/structure/data/{ => pdb}/1ncb.bcif (100%) rename tests/structure/data/{ => pdb}/1ncb.cif (100%) rename tests/structure/data/{ => pdb}/1o1z.bcif (100%) rename tests/structure/data/{ => pdb}/1o1z.cif (100%) rename tests/structure/data/{ => pdb}/1o1z.gro (100%) rename tests/structure/data/{ => pdb}/1o1z.pdb (100%) rename tests/structure/data/{ => pdb}/2axd.bcif (100%) rename tests/structure/data/{ => pdb}/2axd.cif (100%) rename tests/structure/data/{ => pdb}/2axd.pdb (100%) rename tests/structure/data/{ => pdb}/2d0f.bcif (100%) rename tests/structure/data/{ => pdb}/2d0f.cif (100%) rename tests/structure/data/{ => pdb}/2d0f.pdb (100%) rename tests/structure/data/{ => pdb}/3o5r.bcif (100%) rename tests/structure/data/{ => pdb}/3o5r.cif (100%) rename tests/structure/data/{ => pdb}/3o5r.gro (100%) rename tests/structure/data/{ => pdb}/3o5r.pdb (100%) rename tests/structure/data/{ => pdb}/3wip.bcif (100%) rename tests/structure/data/{ => pdb}/3wip.cif (100%) rename tests/structure/data/{ => pdb}/3wip.pdb (100%) rename tests/structure/data/{ => pdb}/4gxy.bcif (100%) rename tests/structure/data/{ => pdb}/4gxy.cif (100%) rename tests/structure/data/{ => pdb}/4gxy.gro (100%) rename tests/structure/data/{ => pdb}/4gxy.pdb (100%) rename tests/structure/data/{ => pdb}/4i39.bcif (100%) rename tests/structure/data/{ => pdb}/4i39.cif (100%) rename tests/structure/data/{ => pdb}/4p5j.bcif (100%) rename tests/structure/data/{ => pdb}/4p5j.cif (100%) rename tests/structure/data/{ => pdb}/4p5j.gro (100%) rename tests/structure/data/{ => pdb}/4p5j.pdb (100%) rename tests/structure/data/{ => pdb}/4zxb.bcif (100%) rename tests/structure/data/{ => pdb}/4zxb.cif (100%) rename tests/structure/data/{ => pdb}/5eil.bcif (100%) rename tests/structure/data/{ => pdb}/5eil.cif (100%) rename tests/structure/data/{ => pdb}/5eil.pdb (100%) rename tests/structure/data/{ => pdb}/5h73.bcif (100%) rename tests/structure/data/{ => pdb}/5h73.cif (100%) rename tests/structure/data/{ => pdb}/5h73.gro (100%) rename tests/structure/data/{ => pdb}/5h73.pdb (100%) rename tests/structure/data/{ => pdb}/5ugo.bcif (100%) rename tests/structure/data/{ => pdb}/5ugo.cif (100%) rename tests/structure/data/{ => pdb}/5ugo.gro (100%) rename tests/structure/data/{ => pdb}/5ugo.pdb (100%) rename tests/structure/data/{ => pdb}/5zng.bcif (100%) rename tests/structure/data/{ => pdb}/5zng.cif (100%) rename tests/structure/data/{ => pdb}/5zng.pdb (100%) rename tests/structure/data/{ => pdb}/7gsa.bcif (100%) rename tests/structure/data/{ => pdb}/7gsa.cif (100%) rename tests/structure/data/{ => pdb}/README.rst (100%) rename tests/structure/data/{ => pdb}/create_test_structures.py (80%) rename tests/structure/data/{ => pdb}/ids.txt (100%) diff --git a/benchmarks/sequence/align/benchmark_kmer.py b/benchmarks/sequence/align/benchmark_kmer.py index f2efaa721..adf517baa 100644 --- a/benchmarks/sequence/align/benchmark_kmer.py +++ b/benchmarks/sequence/align/benchmark_kmer.py @@ -9,9 +9,8 @@ @pytest.fixture(scope="module") def sequence(): - np.random.seed(0) sequence = seq.ProteinSequence() - sequence.code = np.random.randint( + sequence.code = np.random.default_rng(0).integers( len(seq.ProteinSequence.alphabet), size=SEQ_LENGTH ) return sequence diff --git a/benchmarks/sequence/align/benchmark_multiple.py b/benchmarks/sequence/align/benchmark_multiple.py index b905a57c0..6f287596d 100644 --- a/benchmarks/sequence/align/benchmark_multiple.py +++ b/benchmarks/sequence/align/benchmark_multiple.py @@ -1,4 +1,3 @@ -from pathlib import Path import pytest import biotite.sequence.align as align import biotite.sequence.io.fasta as fasta @@ -7,7 +6,7 @@ @pytest.fixture(scope="module") def sequences(): - fasta_file = fasta.FastaFile.read(Path(data_dir("sequence")) / "cas9.fasta") + fasta_file = fasta.FastaFile.read(data_dir("sequence") / "cas9.fasta") return list(fasta.get_sequences(fasta_file).values()) diff --git a/benchmarks/sequence/align/benchmark_pairwise.py b/benchmarks/sequence/align/benchmark_pairwise.py index a9b43feac..77e0a9498 100644 --- a/benchmarks/sequence/align/benchmark_pairwise.py +++ b/benchmarks/sequence/align/benchmark_pairwise.py @@ -1,5 +1,4 @@ from functools import partial -from pathlib import Path import pytest import biotite.sequence as seq import biotite.sequence.align as align @@ -11,7 +10,7 @@ @pytest.fixture(scope="module") def sequences(): - fasta_file = fasta.FastaFile.read(Path(data_dir("sequence")) / "cas9.fasta") + fasta_file = fasta.FastaFile.read(data_dir("sequence") / "cas9.fasta") return [seq.ProteinSequence(s) for s in fasta_file.values()] diff --git a/benchmarks/sequence/align/benchmark_selector.py b/benchmarks/sequence/align/benchmark_selector.py index 19aa5c9d9..8028de510 100644 --- a/benchmarks/sequence/align/benchmark_selector.py +++ b/benchmarks/sequence/align/benchmark_selector.py @@ -13,9 +13,8 @@ @pytest.fixture(scope="module") def sequence(): - np.random.seed(0) s = seq.NucleotideSequence() - s.code = np.random.randint(len(ALPHABET), size=SEQ_LENGTH) + s.code = np.random.default_rng(0).integers(len(ALPHABET), size=SEQ_LENGTH) return s diff --git a/benchmarks/sequence/benchmark_fasta.py b/benchmarks/sequence/benchmark_fasta.py index 24a313c36..7c653c39e 100644 --- a/benchmarks/sequence/benchmark_fasta.py +++ b/benchmarks/sequence/benchmark_fasta.py @@ -1,5 +1,3 @@ -import os -from pathlib import Path import pytest import biotite.sequence as seq import biotite.sequence.io.fasta as fasta @@ -10,10 +8,11 @@ ["fasta_path", "seq_type"], [ # Single nucleotide sequence entry - (Path(data_dir("sequence")) / "ec_bl21.fasta", seq.NucleotideSequence), + (data_dir("sequence") / "ec_bl21.fasta", seq.NucleotideSequence), # Multiple protein sequence entries - (Path(data_dir("sequence")) / "cas9.fasta", seq.ProteinSequence), + (data_dir("sequence") / "cas9.fasta", seq.ProteinSequence), ], + ids=["ec_bl21.fasta", "cas9.fasta"], ) @pytest.mark.benchmark def benchmark_get_sequences(fasta_path, seq_type): @@ -23,7 +22,5 @@ def benchmark_get_sequences(fasta_path, seq_type): @pytest.mark.benchmark def benchmark_get_a3m_alignments(): - a3m_file = fasta.FastaFile.read( - os.path.join(data_dir("sequence"), "1a00_A_uniref90.a3m") - ) + a3m_file = fasta.FastaFile.read(data_dir("sequence") / "1a00_A_uniref90.a3m") fasta.get_a3m_alignments(a3m_file, seq_type=seq.ProteinSequence) diff --git a/benchmarks/sequence/benchmark_phylo.py b/benchmarks/sequence/benchmark_phylo.py index 704ee98ef..789fd68ba 100644 --- a/benchmarks/sequence/benchmark_phylo.py +++ b/benchmarks/sequence/benchmark_phylo.py @@ -7,8 +7,7 @@ @pytest.fixture(scope="module") def distances(): - np.random.seed(0) - rand = np.random.rand(N, N).astype(np.float32) + rand = np.random.default_rng(0).random((N, N)).astype(np.float32) distances = (rand + rand.T) / 2 np.fill_diagonal(distances, 0) return distances diff --git a/benchmarks/structure/benchmark_alphabet.py b/benchmarks/structure/benchmark_alphabet.py index 4d351853e..766157842 100644 --- a/benchmarks/structure/benchmark_alphabet.py +++ b/benchmarks/structure/benchmark_alphabet.py @@ -1,4 +1,3 @@ -from pathlib import Path import pytest import biotite.structure.alphabet as strucalph import biotite.structure.io.pdbx as pdbx @@ -9,7 +8,9 @@ @pytest.fixture(scope="module") def atoms(): - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / f"{PDB_ID}.bcif") + pdbx_file = pdbx.BinaryCIFFile.read( + data_dir("structure") / "pdb" / f"{PDB_ID}.bcif" + ) return pdbx.get_structure(pdbx_file, model=1, include_bonds=True) diff --git a/benchmarks/structure/benchmark_bonds.py b/benchmarks/structure/benchmark_bonds.py index 0bf01fa46..e3e68da12 100644 --- a/benchmarks/structure/benchmark_bonds.py +++ b/benchmarks/structure/benchmark_bonds.py @@ -1,4 +1,3 @@ -from pathlib import Path import pytest import biotite.structure as struc import biotite.structure.info as info @@ -18,7 +17,9 @@ def load_ccd(): @pytest.fixture(scope="module") def atoms(): - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / f"{PDB_ID}.bcif") + pdbx_file = pdbx.BinaryCIFFile.read( + data_dir("structure") / "pdb" / f"{PDB_ID}.bcif" + ) return pdbx.get_structure(pdbx_file, model=1, include_bonds=True) diff --git a/benchmarks/structure/benchmark_celllist.py b/benchmarks/structure/benchmark_celllist.py index 3c57896f2..035693508 100644 --- a/benchmarks/structure/benchmark_celllist.py +++ b/benchmarks/structure/benchmark_celllist.py @@ -1,4 +1,3 @@ -from pathlib import Path import pytest import biotite.structure as struc import biotite.structure.io.pdbx as pdbx @@ -7,7 +6,7 @@ @pytest.fixture(scope="module") def atoms(): - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1gya.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1gya.bcif") return pdbx.get_structure(pdbx_file, model=1) diff --git a/benchmarks/structure/benchmark_compare.py b/benchmarks/structure/benchmark_compare.py index 67f9a5cbe..fd6965083 100644 --- a/benchmarks/structure/benchmark_compare.py +++ b/benchmarks/structure/benchmark_compare.py @@ -1,5 +1,3 @@ -import itertools -from pathlib import Path import pytest import biotite.structure as struc import biotite.structure.io.pdbx as pdbx @@ -9,17 +7,15 @@ @pytest.fixture(scope="module") def atoms(): - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1gya.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1gya.bcif") atoms = pdbx.get_structure(pdbx_file) # Reduce the number of atoms to speed up the benchmark return atoms[..., filter_heavy(atoms)] @pytest.mark.benchmark -@pytest.mark.parametrize( - "multi_model, aggregation", - itertools.product([False, True], ["all", "chain", "residue", "atom"]), -) +@pytest.mark.parametrize("multi_model", [False, True]) +@pytest.mark.parametrize("aggregation", ["all", "chain", "residue", "atom"]) def benchmark_lddt(atoms, multi_model, aggregation): """ Compute lDDT on different aggregation levels. diff --git a/benchmarks/structure/benchmark_pdb.py b/benchmarks/structure/benchmark_pdb.py index fb8006b48..b2baf4e40 100644 --- a/benchmarks/structure/benchmark_pdb.py +++ b/benchmarks/structure/benchmark_pdb.py @@ -1,4 +1,3 @@ -from pathlib import Path import pytest import biotite.structure.info as info import biotite.structure.io.pdb as pdb @@ -15,7 +14,7 @@ def load_ccd(): @pytest.fixture(scope="module") def pdb_file_path(): - return Path(data_dir("structure")) / "1aki.pdb" + return data_dir("structure") / "pdb" / "1aki.pdb" @pytest.fixture(scope="module") diff --git a/benchmarks/structure/benchmark_pdbx.py b/benchmarks/structure/benchmark_pdbx.py index 29132de24..594069da8 100644 --- a/benchmarks/structure/benchmark_pdbx.py +++ b/benchmarks/structure/benchmark_pdbx.py @@ -1,4 +1,3 @@ -from pathlib import Path import pytest import biotite.structure.io.pdbx as pdbx from tests.util import data_dir @@ -8,7 +7,7 @@ @pytest.fixture def pdbx_file(): - return pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / f"{PDB_ID}.bcif") + return pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / f"{PDB_ID}.bcif") @pytest.fixture @@ -24,7 +23,9 @@ def deserialized_data(pdbx_file): @pytest.fixture def atoms(): - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / f"{PDB_ID}.bcif") + pdbx_file = pdbx.BinaryCIFFile.read( + data_dir("structure") / "pdb" / f"{PDB_ID}.bcif" + ) return pdbx.get_structure(pdbx_file, model=1, include_bonds=True) @@ -34,7 +35,7 @@ def benchmark_deserialize_pdbx(format): """ Deserialize all categories of a CIF or BinaryCIF file. """ - path = Path(data_dir("structure")) / f"{PDB_ID}.{format}" + path = data_dir("structure") / "pdb" / f"{PDB_ID}.{format}" if format == "cif": pdbx_file = pdbx.CIFFile.read(path) else: @@ -76,7 +77,7 @@ def benchmark_get_structure(format, include_bonds): """ Parse a structure from a CIF or BinaryCIF file. """ - path = Path(data_dir("structure")) / f"{PDB_ID}.{format}" + path = data_dir("structure") / "pdb" / f"{PDB_ID}.{format}" if format == "cif": pdbx_file = pdbx.CIFFile.read(path) else: @@ -112,7 +113,7 @@ def benchmark_get_assembly(): Use BinaryCIF to focus on the performance of the assembly operations, rather than file parsing. """ - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1f2n.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1f2n.bcif") pdbx.get_assembly(pdbx_file, model=1) diff --git a/benchmarks/structure/benchmark_sasa.py b/benchmarks/structure/benchmark_sasa.py index 1bd2e987f..52f9061ab 100644 --- a/benchmarks/structure/benchmark_sasa.py +++ b/benchmarks/structure/benchmark_sasa.py @@ -1,4 +1,3 @@ -from pathlib import Path import pytest import biotite.structure as struc import biotite.structure.io.pdbx as pdbx @@ -10,7 +9,7 @@ def atoms(): """ A structure that includes hydrogen atoms. """ - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1gya.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1gya.bcif") return pdbx.get_structure(pdbx_file, model=1, include_bonds=True) diff --git a/benchmarks/structure/benchmark_superimpose.py b/benchmarks/structure/benchmark_superimpose.py index f030c1430..61ff39c92 100644 --- a/benchmarks/structure/benchmark_superimpose.py +++ b/benchmarks/structure/benchmark_superimpose.py @@ -1,4 +1,3 @@ -from pathlib import Path import pytest import biotite.structure as struc import biotite.structure.io.pdbx as pdbx @@ -7,7 +6,7 @@ @pytest.fixture(scope="module") def atoms(): - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1gya.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1gya.bcif") return pdbx.get_structure(pdbx_file) diff --git a/doc/tutorial/structure/trajectories.rst b/doc/tutorial/structure/trajectories.rst index f199b345b..69fbdfa0d 100644 --- a/doc/tutorial/structure/trajectories.rst +++ b/doc/tutorial/structure/trajectories.rst @@ -31,7 +31,7 @@ These can be extracted as :class:`ndarray` with the temp_xtc_file = NamedTemporaryFile("wb", suffix=".xtc", delete=False) response = requests.get( "https://raw.githubusercontent.com/biotite-dev/biotite/master/" - "tests/structure/data/1l2y.xtc" + "tests/structure/data/pdb/1l2y.xtc" ) temp_xtc_file.write(response.content) diff --git a/src/biotite/structure/basepairs.py b/src/biotite/structure/basepairs.py index 6d4d26803..166edadfb 100644 --- a/src/biotite/structure/basepairs.py +++ b/src/biotite/structure/basepairs.py @@ -372,7 +372,7 @@ def base_pairs_edge( >>> from os.path import join >>> dna_helix = load_structure( - ... join(path_to_structures, "base_pairs", "1qxb.cif") + ... join(path_to_structure_data, "base_pairs", "1qxb.cif") ... ) >>> basepairs = base_pairs(dna_helix) >>> interacting_edges = base_pairs_edge(dna_helix, basepairs) @@ -541,7 +541,7 @@ def base_pairs_glycosidic_bond( >>> from os.path import join >>> dna_helix = load_structure( - ... join(path_to_structures, "base_pairs", "1qxb.cif") + ... join(path_to_structure_data, "base_pairs", "1qxb.cif") ... ) >>> basepairs = base_pairs(dna_helix) >>> orientations = base_pairs_glycosidic_bond(dna_helix, basepairs) @@ -684,7 +684,7 @@ def base_stacking( >>> from os.path import join >>> dna_helix = load_structure( - ... join(path_to_structures, "base_pairs", "1bna.pdb") + ... join(path_to_structure_data, "base_pairs", "1bna.pdb") ... ) >>> stacking_interactions = base_stacking(dna_helix) >>> print(dna_helix[stacking_interactions].res_id) @@ -866,7 +866,7 @@ def base_pairs( >>> from os.path import join >>> dna_helix = load_structure( - ... join(path_to_structures, "base_pairs", "1qxb.cif") + ... join(path_to_structure_data, "base_pairs", "1qxb.cif") ... ) >>> basepairs = base_pairs(dna_helix) >>> print(dna_helix[basepairs].res_name) diff --git a/src/biotite/structure/io/mol/mol.py b/src/biotite/structure/io/mol/mol.py index bbbdff2db..1731e5ccc 100644 --- a/src/biotite/structure/io/mol/mol.py +++ b/src/biotite/structure/io/mol/mol.py @@ -51,7 +51,7 @@ class MOLFile(TextFile): -------- >>> from os.path import join - >>> mol_file = MOLFile.read(join(path_to_structures, "molecules", "TYR.sdf")) + >>> mol_file = MOLFile.read(join(path_to_structure_data, "molecules", "TYR.sdf")) >>> atom_array = mol_file.get_structure() >>> print(atom_array) 0 N 1.320 0.952 1.428 diff --git a/src/biotite/structure/io/mol/sdf.py b/src/biotite/structure/io/mol/sdf.py index 3b7d02bce..39fbac3d1 100644 --- a/src/biotite/structure/io/mol/sdf.py +++ b/src/biotite/structure/io/mol/sdf.py @@ -591,7 +591,7 @@ class SDFile(File, MutableMapping[str, SDRecord]): Read a SD file and parse the molecular structure: >>> import os.path - >>> file = SDFile.read(os.path.join(path_to_structures, "molecules", "TYR.sdf")) + >>> file = SDFile.read(os.path.join(path_to_structure_data, "molecules", "TYR.sdf")) >>> molecule = file.record.get_structure() >>> print(molecule) 0 N 1.320 0.952 1.428 diff --git a/src/biotite/structure/io/pdbx/convert.py b/src/biotite/structure/io/pdbx/convert.py index 208932d0b..2b3b918ca 100644 --- a/src/biotite/structure/io/pdbx/convert.py +++ b/src/biotite/structure/io/pdbx/convert.py @@ -1498,7 +1498,7 @@ def get_component( >>> import os.path >>> file = CIFFile.read( - ... os.path.join(path_to_structures, "molecules", "TYR.cif") + ... os.path.join(path_to_structure_data, "molecules", "TYR.cif") ... ) >>> comp = get_component(file) >>> print(comp) diff --git a/src/biotite/structure/rdf.py b/src/biotite/structure/rdf.py index 2c7928c7d..e61a43f96 100644 --- a/src/biotite/structure/rdf.py +++ b/src/biotite/structure/rdf.py @@ -110,7 +110,7 @@ def rdf( ignore the counts for the density for each oxygen to itself. >>> from os.path import join - >>> waterbox = load_structure(join(path_to_structures, "waterbox.gro")) + >>> waterbox = load_structure(join(path_to_structure_data, "waterbox.gro")) >>> oxygens = waterbox[:, waterbox.atom_name == 'OW'] >>> bins, g_r = rdf(oxygens, oxygens, bins=49, interval=(0.2, 10), periodic=True) diff --git a/src/biotite/structure/rings.py b/src/biotite/structure/rings.py index 9258d38a4..3996e76a0 100644 --- a/src/biotite/structure/rings.py +++ b/src/biotite/structure/rings.py @@ -181,7 +181,7 @@ def find_stacking_interactions( >>> from os.path import join >>> dna_helix = load_structure( - ... join(path_to_structures, "base_pairs", "1qxb.cif"), include_bonds=True + ... join(path_to_structure_data, "base_pairs", "1qxb.cif"), include_bonds=True ... ) >>> interactions = find_stacking_interactions(dna_helix) >>> for ring_atom_indices_1, ring_atom_indices_2, stacking_type in interactions: diff --git a/tests/application/test_autodock.py b/tests/application/test_autodock.py index 8e7e03755..fb4638b54 100644 --- a/tests/application/test_autodock.py +++ b/tests/application/test_autodock.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import numpy as np import pytest import biotite.structure as struc @@ -24,7 +23,7 @@ def test_docking(flexible): MAX_DEVIATION = 2.0 # A structure of a straptavidin-biotin complex - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("application"), "2rtg.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("application") / "2rtg.bcif") structure = pdbx.get_structure( pdbx_file, model=1, extra_fields=["charge"], include_bonds=True ) diff --git a/tests/application/test_blast.py b/tests/application/test_blast.py index d9bb69f3a..00952e4fa 100644 --- a/tests/application/test_blast.py +++ b/tests/application/test_blast.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import os.path import pytest import biotite.application.blast as blast import biotite.sequence as seq @@ -83,7 +82,7 @@ def test_tblastn(): def test_file_input(): - path = os.path.join(data_dir("sequence"), "prot.fasta") + path = data_dir("sequence") / "prot.fasta" blast.BlastWebApp("blastp", path, obey_rules=False) @@ -119,9 +118,7 @@ def test_invalid_input(): @pytest.mark.skipif(cannot_connect_to(BLAST_URL), reason="NCBI BLAST is not available") def test_hit_with_selenocysteine(): # Sequence is taken from issue #344 - query = seqio.load_sequence( - os.path.join(data_dir("sequence"), "selenocysteine.fasta") - ) + query = seqio.load_sequence(data_dir("sequence") / "selenocysteine.fasta") # Expect hit containing selenocysteine when searching Swiss-Prot blast_app = blast.BlastWebApp("blastp", query, "swissprot") diff --git a/tests/application/test_dssp.py b/tests/application/test_dssp.py index bb32cf32f..e10bee70f 100644 --- a/tests/application/test_dssp.py +++ b/tests/application/test_dssp.py @@ -2,11 +2,9 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import numpy as np import pytest import biotite.structure as struc -import biotite.structure.io as strucio import biotite.structure.io.pdbx as pdbx from biotite.application.dssp import DsspApp from tests.util import data_dir, is_not_installed @@ -26,7 +24,8 @@ def test_annotation(pdb_id): Check if the DSSP annotation has the correct length and reasonable values. """ atoms = pdbx.get_structure( - pdbx.BinaryCIFFile.read(join(data_dir("structure"), f"{pdb_id}.bcif")), model=1 + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / f"{pdb_id}.bcif"), + model=1, ) atoms = atoms[struc.filter_amino_acids(atoms)] sse = DsspApp.annotate_sse(atoms) @@ -42,7 +41,9 @@ def test_invalid_structure(): Check if an exception is raised, if the input structure contains non-amino-acid residues. """ - array = strucio.load_structure(join(data_dir("structure"), "5ugo.bcif")) + array = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "5ugo.bcif"), model=1 + ) # Get DNA chain -> Invalid for DSSP chain = array[array.chain_id == "T"] with pytest.raises(struc.BadStructureError): diff --git a/tests/application/test_msa.py b/tests/application/test_msa.py index 12f304867..eebf7788a 100644 --- a/tests/application/test_msa.py +++ b/tests/application/test_msa.py @@ -30,7 +30,7 @@ def sequences(): @pytest.mark.parametrize( - "app_cls, exp_ali, exp_order", + ["app_cls", "exp_ali", "exp_order"], [ ( MuscleApp, @@ -103,7 +103,7 @@ def test_large_sequence_number(app_cls): # Create random sequence sequence = seq.ProteinSequence() - sequence.code = np.random.randint(20, size=SEQ_LENGTH) + sequence.code = np.random.default_rng().integers(20, size=SEQ_LENGTH) # Use identical sequences sequences = [sequence] * SEQ_NUMBER diff --git a/tests/application/test_sra.py b/tests/application/test_sra.py index c758a4ffd..1c70538c9 100644 --- a/tests/application/test_sra.py +++ b/tests/application/test_sra.py @@ -2,9 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools -from os.path import join -from tempfile import gettempdir import pytest from biotite.application.sra import FastaDumpApp, FastqDumpApp from biotite.sequence.io.fasta import FastaFile @@ -15,18 +12,16 @@ @pytest.mark.skipif( is_not_installed("fasterq-dump"), reason="sra-tools is not installed" ) -@pytest.mark.parametrize( - "app_class, custom_prefix", - itertools.product([FastqDumpApp, FastaDumpApp], [False, True]), -) -def test_objects(app_class, custom_prefix): +@pytest.mark.parametrize("app_class", [FastqDumpApp, FastaDumpApp]) +@pytest.mark.parametrize("custom_prefix", [False, True]) +def test_objects(app_class, custom_prefix, tmp_path): """ Test return types of methods from the respective `Application` class. """ # Small dataset for low server impact UID = "ERR11344941" - prefix = join(gettempdir(), "test_fastq") if custom_prefix else None + prefix = str(tmp_path / "test_fastq") if custom_prefix else None app = app_class(UID, output_path_prefix=prefix) app.start() app.join() @@ -49,17 +44,15 @@ def test_objects(app_class, custom_prefix): @pytest.mark.skipif( is_not_installed("fasterq-dump"), reason="sra-tools is not installed" ) -@pytest.mark.parametrize( - "app_class, custom_prefix", - itertools.product([FastqDumpApp, FastaDumpApp], [False, True]), -) -def test_classmethod(app_class, custom_prefix): +@pytest.mark.parametrize("app_class", [FastqDumpApp, FastaDumpApp]) +@pytest.mark.parametrize("custom_prefix", [False, True]) +def test_classmethod(app_class, custom_prefix, tmp_path): """ Test return types of the `fetch()` class method. """ UID = "ERR11344941" - prefix = join(gettempdir(), "test_fastq") if custom_prefix else None + prefix = str(tmp_path / "test_fastq") if custom_prefix else None sequences = app_class.fetch(UID, output_path_prefix=prefix) diff --git a/tests/database/test_afdb.py b/tests/database/test_afdb.py index 3453ca5b2..0600d858d 100644 --- a/tests/database/test_afdb.py +++ b/tests/database/test_afdb.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import tempfile import pytest import biotite.database.afdb as afdb import biotite.database.rcsb as rcsb @@ -20,12 +19,12 @@ "entry_id", ["P12345", "AF-P12345-F1", "AF-P12345F1", "AF_AFP12345F1"] ) @pytest.mark.parametrize("format", ["pdb", "cif", "bcif"]) -def test_fetch(as_file_like, entry_id, format): +def test_fetch(as_file_like, entry_id, format, tmp_path): """ Check if files in different formats can be downloaded by being able to parse them. Also ensure that the downloaded file refers to the given input ID """ - path = None if as_file_like else tempfile.gettempdir() + path = None if as_file_like else tmp_path try: file_path_or_obj = afdb.fetch(entry_id, format, path, overwrite=True) except RequestError: @@ -72,12 +71,12 @@ def test_fetch_cross_id(): @pytest.mark.skipif(cannot_connect_to(AFDB_URL), reason="AlphaFold DB is not available") -def test_fetch_multiple(): +def test_fetch_multiple(tmp_path): """ Check if multiple files can be downloaded by being able to parse them. """ ids = ["P12345", "Q8K9I1"] - files = afdb.fetch(ids, "cif", tempfile.gettempdir(), overwrite=True) + files = afdb.fetch(ids, "cif", tmp_path, overwrite=True) for file in files: assert "citation_author" in pdbx.CIFFile.read(file).block diff --git a/tests/database/test_entrez.py b/tests/database/test_entrez.py index bc1e94f34..903439639 100644 --- a/tests/database/test_entrez.py +++ b/tests/database/test_entrez.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools import tempfile import pytest import biotite.database.entrez as entrez @@ -14,11 +13,10 @@ @pytest.mark.skipif(cannot_connect_to(NCBI_URL), reason="NCBI Entrez is not available") -@pytest.mark.parametrize( - "common_name, as_file_like", itertools.product([False, True], [False, True]) -) -def test_fetch(common_name, as_file_like): - path = None if as_file_like else tempfile.gettempdir() +@pytest.mark.parametrize("common_name", [False, True]) +@pytest.mark.parametrize("as_file_like", [False, True]) +def test_fetch(common_name, as_file_like, tmp_path): + path = None if as_file_like else tmp_path db_name = "Protein" if common_name else "protein" file = entrez.fetch("1L2Y_A", path, "fa", db_name, "fasta", overwrite=True) fasta_file = fasta.FastaFile.read(file) diff --git a/tests/database/test_pubchem.py b/tests/database/test_pubchem.py index 464bba39b..12e3698c2 100644 --- a/tests/database/test_pubchem.py +++ b/tests/database/test_pubchem.py @@ -3,9 +3,7 @@ # information. import functools -import itertools import re -import tempfile import numpy as np import pytest import biotite.database.pubchem as pubchem @@ -42,17 +40,16 @@ def wrapper(*args, **kwargs): @pytest.mark.skipif(cannot_connect_to(PUBCHEM_URL), reason="Pubchem is not available") -@pytest.mark.parametrize( - "format, as_file_like", itertools.product(["sdf", "png"], [False, True]) -) +@pytest.mark.parametrize("format", ["sdf", "png"]) +@pytest.mark.parametrize("as_file_like", [False, True]) @accept_busy_pubchem -def test_fetch(format, as_file_like): +def test_fetch(format, as_file_like, tmp_path): """ Check download of a record in binary and text form. """ CID = 2244 - path = None if as_file_like else tempfile.gettempdir() + path = None if as_file_like else tmp_path file_path_or_obj = pubchem.fetch(CID, format, path, overwrite=True) if format == "sdf": mol_file = mol.MOLFile.read(file_path_or_obj) @@ -97,7 +94,7 @@ def test_fetch_invalid(): @pytest.mark.skipif(cannot_connect_to(PUBCHEM_URL), reason="PubChem is not available") @pytest.mark.parametrize( - "query, ref_ids", + ["query", "ref_ids"], [ (pubchem.NameQuery("Alanine"), [5950]), (pubchem.SmilesQuery("CCCC"), [7843]), @@ -135,11 +132,10 @@ def test_search_formula(): @pytest.mark.skipif(cannot_connect_to(PUBCHEM_URL), reason="PubChem is not available") +@pytest.mark.parametrize("cid", [2244]) +@pytest.mark.parametrize("from_atoms", [False, True]) @pytest.mark.parametrize( - "cid, from_atoms, query_type", - itertools.product( - [2244], [False, True], [pubchem.SuperstructureQuery, pubchem.SubstructureQuery] - ), + "query_type", [pubchem.SuperstructureQuery, pubchem.SubstructureQuery] ) @accept_busy_pubchem def test_search_super_and_substructure(cid, from_atoms, query_type): @@ -180,9 +176,8 @@ def test_search_super_and_substructure(cid, from_atoms, query_type): @pytest.mark.skipif(cannot_connect_to(PUBCHEM_URL), reason="PubChem is not available") -@pytest.mark.parametrize( - "conformation_based, from_atoms", itertools.product([False, True], [False, True]) -) +@pytest.mark.parametrize("conformation_based", [False, True]) +@pytest.mark.parametrize("from_atoms", [False, True]) @accept_busy_pubchem def test_search_similarity(conformation_based, from_atoms): """ diff --git a/tests/database/test_rcsb.py b/tests/database/test_rcsb.py index 24dffc057..da47a6130 100644 --- a/tests/database/test_rcsb.py +++ b/tests/database/test_rcsb.py @@ -3,9 +3,7 @@ # information. import gzip -import tempfile from datetime import date -from os.path import join import numpy as np import pytest import biotite.database.rcsb as rcsb @@ -25,7 +23,7 @@ @pytest.mark.parametrize("as_file_like", [False, True]) @pytest.mark.parametrize( - "format, extended_id, use_gzip", + ["format", "extended_id", "use_gzip"], [ pytest.param("pdb", False, False), pytest.param("pdb", False, True), @@ -41,7 +39,7 @@ pytest.param("fasta", False, False), ], ) -def test_fetch(format, as_file_like, extended_id, use_gzip): +def test_fetch(format, as_file_like, extended_id, use_gzip, tmp_path): HANDLE_CHAR = { "pdb": "t", "cif": "t", @@ -51,7 +49,7 @@ def test_fetch(format, as_file_like, extended_id, use_gzip): PDB_ID = "1aki" pdb_id = PDB_ID if not extended_id else "pdb_0000" + PDB_ID - path = None if as_file_like else tempfile.gettempdir() + path = None if as_file_like else tmp_path file_path_or_obj = rcsb.fetch(pdb_id, format, path, overwrite=True, gzip=use_gzip) if use_gzip: @@ -74,9 +72,9 @@ def test_fetch(format, as_file_like, extended_id, use_gzip): @pytest.mark.skipif(cannot_connect_to(RCSB_URL), reason="RCSB PDB is not available") @pytest.mark.parametrize("format", ["pdb", "cif", "bcif", "fasta"]) -def test_fetch_invalid(format): +def test_fetch_invalid(format, tmp_path): with pytest.raises(RequestError): - rcsb.fetch("xxxx", format, tempfile.gettempdir(), overwrite=True) + rcsb.fetch("xxxx", format, tmp_path, overwrite=True) def test_search_basic(): @@ -86,7 +84,7 @@ def test_search_basic(): @pytest.mark.parametrize( - "field, molecular_definition, params, ref_ids", + ["field", "molecular_definition", "params", "ref_ids"], [ ( "pdbx_serial_crystallography_sample_delivery_injection.preparation", @@ -178,7 +176,7 @@ def test_search_field(field, molecular_definition, params, ref_ids): @pytest.mark.skipif(cannot_connect_to(RCSB_URL), reason="RCSB PDB is not available") def test_search_sequence(): IDENTIY_CUTOFF = 0.9 - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") ref_sequence = pdbx.get_sequence(pdbx_file)["A"] query = rcsb.SequenceQuery(ref_sequence, "protein", min_identity=IDENTIY_CUTOFF) test_ids = rcsb.search(query) @@ -196,7 +194,7 @@ def test_search_sequence(): @pytest.mark.skipif(cannot_connect_to(RCSB_URL), reason="RCSB PDB is not available") -@pytest.mark.parametrize("chain, assembly", [("A", None), (None, "1")]) +@pytest.mark.parametrize(["chain", "assembly"], [("A", None), (None, "1")]) def test_search_structure(chain, assembly): query = rcsb.StructureQuery("1L2Y", chain=chain, assembly=assembly) test_ids = rcsb.search(query) @@ -228,7 +226,7 @@ def test_search_composite(): @pytest.mark.parametrize( - "return_type, expected", + ["return_type", "expected"], [ ("entry", ["1L2Y"] ), ("assembly", ["1L2Y-1"]), @@ -254,8 +252,8 @@ def test_search_range(seed): ref_entries = rcsb.search(query) assert len(ref_entries) == count - np.random.seed(seed) - range = sorted(np.random.choice(count, 2, replace=False)) + rng = np.random.default_rng(seed) + range = sorted(rng.choice(count, 2, replace=False)) if range[1] - range[0] > 10000: # pagination only supports up to 10000 entries # (https://search.rcsb.org/#pagination) @@ -325,7 +323,7 @@ def test_search_content_types(): @pytest.mark.skipif(cannot_connect_to(RCSB_URL), reason="RCSB PDB is not available") @pytest.mark.parametrize( - "grouping, resolution_threshold, return_type, ref_groups", + ["grouping", "resolution_threshold", "return_type", "ref_groups"], [ ( rcsb.IdentityGrouping( @@ -417,7 +415,7 @@ def test_search_empty(): @pytest.mark.parametrize( - "field, params", + ["field", "params"], [("invalid.field", {"exact_match": "Some Value"}), ("exptl.method", {"less": 5})], ) @pytest.mark.skipif(cannot_connect_to(RCSB_URL), reason="RCSB PDB is not available") diff --git a/tests/database/test_uniprot.py b/tests/database/test_uniprot.py index 7af70393a..f1711ad96 100644 --- a/tests/database/test_uniprot.py +++ b/tests/database/test_uniprot.py @@ -2,8 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools -import tempfile import pytest import biotite.database.uniprot as uniprot import biotite.sequence.io.fasta as fasta @@ -14,9 +12,9 @@ @pytest.mark.skipif(cannot_connect_to(UNIPROT_URL), reason="UniProt is not available") -@pytest.mark.parametrize("as_file_like", itertools.product([False, True])) -def test_fetch(as_file_like): - path = None if as_file_like else tempfile.gettempdir() +@pytest.mark.parametrize("as_file_like", [False, True]) +def test_fetch(as_file_like, tmp_path): + path = None if as_file_like else tmp_path # UniProtKB file = uniprot.fetch("P12345", "fasta", path, overwrite=True) @@ -39,9 +37,9 @@ def test_fetch(as_file_like): @pytest.mark.skipif(cannot_connect_to(UNIPROT_URL), reason="UniProt is not available") @pytest.mark.parametrize("format", ["fasta", "gff", "txt", "xml", "rdf", "tab"]) -def test_fetch_invalid(format): +def test_fetch_invalid(format, tmp_path): with pytest.raises(RequestError): - uniprot.fetch("xxxx", format, tempfile.gettempdir(), overwrite=True) + uniprot.fetch("xxxx", format, tmp_path, overwrite=True) @pytest.mark.skipif(cannot_connect_to(UNIPROT_URL), reason="UniProt is not available") diff --git a/tests/interface/pymol/test_cgo.py b/tests/interface/pymol/test_cgo.py index 6db194466..2c6d2f546 100644 --- a/tests/interface/pymol/test_cgo.py +++ b/tests/interface/pymol/test_cgo.py @@ -11,7 +11,7 @@ @pytest.mark.parametrize( - "cgo_func, param", + ["cgo_func", "param"], [ (get_cylinder_cgo, [(0, 0, 0), (1, 1, 1), 5, (1, 1, 1), (1, 1, 1)]), (get_cone_cgo, [(0, 0, 0), (1, 1, 1), 5, 10, (1, 1, 1), (1, 1, 1), True, True]), @@ -52,7 +52,7 @@ def test_draw_multiple_cgo(): @pytest.mark.parametrize( - "cgo_func, param", + ["cgo_func", "param"], [ (get_cylinder_cgo, [(0, 0, 0), (1, 1, 1), 5, (1, 1), (1, 1, 1)]), (get_cylinder_cgo, [(0, 0, 0), (1, 1, 1), 5, (1, 1, 1), (1, 1, 1, 0)]), diff --git a/tests/interface/pymol/test_command.py b/tests/interface/pymol/test_command.py index 1fdd7c28b..8353ac722 100644 --- a/tests/interface/pymol/test_command.py +++ b/tests/interface/pymol/test_command.py @@ -1,4 +1,3 @@ -from os.path import join import pytest import biotite.interface.pymol as pymol_interface import biotite.structure.io.pdbx as pdbx @@ -6,7 +5,7 @@ def _get_mask(): - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") structure = pdbx.get_structure(pdbx_file) return structure.res_id < 10 @@ -15,7 +14,7 @@ def _get_mask(): @pytest.mark.parametrize( - "command_name, kwargs", + ["command_name", "kwargs"], [ ( "alter", @@ -167,7 +166,7 @@ def _get_mask(): ], ) def test_command(command_name, kwargs): - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") structure = pdbx.get_structure(pdbx_file, include_bonds=True) pymol_obj = pymol_interface.PyMOLObject.from_structure(structure) command = getattr(pymol_interface.PyMOLObject, command_name) diff --git a/tests/interface/pymol/test_conversion.py b/tests/interface/pymol/test_conversion.py index 283a5e4f3..cc9926300 100644 --- a/tests/interface/pymol/test_conversion.py +++ b/tests/interface/pymol/test_conversion.py @@ -1,4 +1,3 @@ -from os.path import join import numpy as np import pytest import biotite.interface.pymol as pymol_interface @@ -23,7 +22,7 @@ ) def path(request): pdb_id = request.param - return join(data_dir("structure"), f"{pdb_id}.cif") + return str(data_dir("structure") / "pdb" / f"{pdb_id}.cif") @pytest.mark.parametrize("state", [1, None]) diff --git a/tests/interface/pymol/test_display.py b/tests/interface/pymol/test_display.py index 0a69c1b9c..37b033e14 100644 --- a/tests/interface/pymol/test_display.py +++ b/tests/interface/pymol/test_display.py @@ -1,4 +1,3 @@ -from os.path import join import pytest import biotite.interface.pymol as pymol_interface import biotite.structure.io.pdbx as pdbx @@ -10,7 +9,7 @@ reason="Rendering software is not installed", ) @pytest.mark.parametrize( - "function_name, kwargs", + ["function_name", "kwargs"], [ ("show", {"use_ray": False}), ("show", {"use_ray": True}), @@ -22,7 +21,7 @@ def test_display(function_name, kwargs): """ Simply check if the :func:`show()` and :func:`play()` function creates image data. """ - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") structure = pdbx.get_structure(pdbx_file, include_bonds=True) _ = pymol_interface.PyMOLObject.from_structure(structure) pymol_interface.cmd.mset() diff --git a/tests/interface/pymol/test_selection.py b/tests/interface/pymol/test_selection.py index d0368c95c..1e751212d 100644 --- a/tests/interface/pymol/test_selection.py +++ b/tests/interface/pymol/test_selection.py @@ -1,4 +1,3 @@ -from os.path import join import numpy as np import pytest import biotite.interface.pymol as pymol_interface @@ -15,15 +14,15 @@ def test_select(random_seed): boolean mask. Use the B factor as indicator for masked atoms. """ - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") array = pdbx.get_structure(pdbx_file, model=1, include_bonds=True) # Use B factor as indicator if the selection was correctly applied array.set_annotation("b_factor", np.zeros(array.array_length())) pymol_object = pymol_interface.PyMOLObject.from_structure(array) - np.random.seed(random_seed) - ref_mask = np.random.choice([False, True], array.array_length()) + rng = np.random.default_rng(random_seed) + ref_mask = rng.choice([False, True], array.array_length()) # The method that is actually tested test_selection = pymol_object.where(ref_mask) diff --git a/tests/interface/test_openmm.py b/tests/interface/test_openmm.py index 9ca92dd15..390f8d69a 100644 --- a/tests/interface/test_openmm.py +++ b/tests/interface/test_openmm.py @@ -1,4 +1,3 @@ -from os.path import join import numpy as np import openmm import openmm.app as app @@ -13,7 +12,7 @@ @pytest.fixture def test_path(): # Use a structure with hydrogen atoms - return join(data_dir("structure"), "1l2y.cif") + return data_dir("structure") / "pdb" / "1l2y.cif" @pytest.mark.parametrize("multi_state", [False, True]) @@ -68,7 +67,7 @@ def test_system_consistency(test_path): :class:`System` directly read via OpenMM. Forces and constraints are not tested, as they are not set by :func:`to_system()`. """ - topology = app.PDBxFile(test_path).topology + topology = app.PDBxFile(str(test_path)).topology force_field = app.ForceField("amber14-all.xml") ref_system = force_field.createSystem(topology) @@ -127,7 +126,7 @@ def test_topology_consistency(test_path): ref_atoms.del_annotation("label_asym_id") ref_atoms.bonds = struc.connect_via_residue_names(ref_atoms) - topology = app.PDBxFile(test_path).topology + topology = app.PDBxFile(str(test_path)).topology test_atoms = openmm_interface.from_topology(topology) # OpenMM does not parse bond types for all bonds when parsing CIF files diff --git a/tests/interface/test_rdkit.py b/tests/interface/test_rdkit.py index a188ec06a..c9e65a27a 100644 --- a/tests/interface/test_rdkit.py +++ b/tests/interface/test_rdkit.py @@ -1,5 +1,4 @@ import itertools -from pathlib import Path import numpy as np import pytest import rdkit.Chem.AllChem as Chem @@ -18,7 +17,7 @@ def _released_ccd_residues(): def _load_smiles(): - with open(Path(data_dir("interface")) / "smiles.txt") as file: + with open(data_dir("interface") / "smiles.txt") as file: return file.read().splitlines() diff --git a/tests/sequence/align/conftest.py b/tests/sequence/align/conftest.py index 836fec2b7..574ae6585 100644 --- a/tests/sequence/align/conftest.py +++ b/tests/sequence/align/conftest.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import pytest from tests.util import data_dir @@ -17,5 +16,5 @@ def sequences(): import biotite.sequence as seq import biotite.sequence.io.fasta as fasta - fasta_file = fasta.FastaFile.read(join(data_dir("sequence"), "cas9.fasta")) + fasta_file = fasta.FastaFile.read(data_dir("sequence") / "cas9.fasta") return [seq.ProteinSequence(sequence) for sequence in fasta_file.values()] diff --git a/tests/sequence/align/test_align.py b/tests/sequence/align/test_align.py index 8a840fc6f..9ff536dfc 100644 --- a/tests/sequence/align/test_align.py +++ b/tests/sequence/align/test_align.py @@ -232,7 +232,7 @@ def test_comparison_to_optimal_alignment(sequences, param): @pytest.mark.parametrize( - "param, ref_alignment", + ["param", "ref_alignment"], _LEGACY_ALIGNMENTS, ids=_IDS, ) diff --git a/tests/sequence/align/test_banded.py b/tests/sequence/align/test_banded.py index 2b1661ceb..f50b30369 100644 --- a/tests/sequence/align/test_banded.py +++ b/tests/sequence/align/test_banded.py @@ -2,17 +2,15 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools import numpy as np import pytest import biotite.sequence as seq import biotite.sequence.align as align -@pytest.mark.parametrize( - "gap_penalty, local, band_width", - itertools.product([-10, (-10, -1)], [False, True], [2, 5, 20, 100]), -) +@pytest.mark.parametrize("gap_penalty", [-10, (-10, -1)]) +@pytest.mark.parametrize("local", [False, True]) +@pytest.mark.parametrize("band_width", [2, 5, 20, 100]) def test_simple_alignment(gap_penalty, local, band_width): """ Test `align_banded()` by comparing the output to `align_optimal()`. @@ -45,10 +43,9 @@ def test_simple_alignment(gap_penalty, local, band_width): assert alignment in ref_alignments -@pytest.mark.parametrize( - "length, excerpt_length, seed", - itertools.product([1_000, 1_000_000], [50, 500], range(10)), -) +@pytest.mark.parametrize("length", [1_000, 1_000_000]) +@pytest.mark.parametrize("excerpt_length", [50, 500]) +@pytest.mark.parametrize("seed", range(10)) def test_large_sequence_mapping(length, excerpt_length, seed): """ Test whether an excerpt of a very large sequence is aligned to that @@ -56,14 +53,14 @@ def test_large_sequence_mapping(length, excerpt_length, seed): """ BAND_WIDTH = 100 - np.random.seed(seed) + rng = np.random.default_rng(seed) sequence = seq.NucleotideSequence() - sequence.code = np.random.randint(len(sequence.alphabet), size=length) - excerpt_pos = np.random.randint(len(sequence) - excerpt_length) + sequence.code = rng.integers(len(sequence.alphabet), size=length) + excerpt_pos = rng.integers(len(sequence) - excerpt_length) excerpt = sequence[excerpt_pos : excerpt_pos + excerpt_length] - diagonal = np.random.randint(excerpt_pos - BAND_WIDTH, excerpt_pos + BAND_WIDTH) + diagonal = rng.integers(excerpt_pos - BAND_WIDTH, excerpt_pos + BAND_WIDTH) band = (diagonal - BAND_WIDTH, diagonal + BAND_WIDTH) matrix = align.SubstitutionMatrix.std_nucleotide_matrix() diff --git a/tests/sequence/align/test_cigar.py b/tests/sequence/align/test_cigar.py index e4ffe4b04..9692b7439 100644 --- a/tests/sequence/align/test_cigar.py +++ b/tests/sequence/align/test_cigar.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools import re import numpy as np import pytest @@ -11,33 +10,33 @@ def _generate_cigar(seed): - np.random.seed(seed) + rng = np.random.default_rng(seed) cigar = "" - n_op_pairs = np.random.choice(range(1, 10)) + n_op_pairs = rng.choice(range(1, 10)) for _ in range(n_op_pairs): # Alternatingly insert matches and insertions/deletions - cigar += f"{np.random.randint(1, 100)}M" + cigar += f"{rng.integers(1, 100)}M" op = align.CigarOp( - np.random.choice( + rng.choice( [ align.CigarOp.INSERTION, align.CigarOp.DELETION, ] ) ).to_cigar_symbol() - cigar += f"{np.random.randint(1, 100)}{op}" + cigar += f"{rng.integers(1, 100)}{op}" # Alignment must end with a match - cigar += f"{np.random.randint(1, 100)}M" + cigar += f"{rng.integers(1, 100)}M" # Possibly add a soft clip at the alignment start # Soft clip at alignment end would be ignored # and thus could not be mapped back - if np.random.choice([True, False]): - cigar = f"{np.random.randint(1, 100)}S" + cigar + if rng.choice([True, False]): + cigar = f"{rng.integers(1, 100)}S" + cigar return cigar def _mutate_sequence( - original, max_subsitutions=50, max_insertions=50, max_deletions=50 + original, rng, max_subsitutions=50, max_insertions=50, max_deletions=50 ): """ Introduce random deletions, insertions and substitutions into a @@ -46,24 +45,24 @@ def _mutate_sequence( mutant = original.copy() # Random Substitutions - n_subsitutions = np.random.randint(max_subsitutions) - subsitution_indices = np.random.choice( + n_subsitutions = rng.integers(max_subsitutions) + subsitution_indices = rng.choice( np.arange(len(mutant)), size=n_subsitutions, replace=False ) - subsitution_values = np.random.randint(len(original.alphabet), size=n_subsitutions) + subsitution_values = rng.integers(len(original.alphabet), size=n_subsitutions) mutant.code[subsitution_indices] = subsitution_values # Random insertions - n_insertions = np.random.randint(max_insertions) - insertion_indices = np.random.choice( + n_insertions = rng.integers(max_insertions) + insertion_indices = rng.choice( np.arange(len(mutant)), size=n_insertions, replace=False ) - insertion_values = np.random.randint(len(original.alphabet), size=n_insertions) + insertion_values = rng.integers(len(original.alphabet), size=n_insertions) mutant.code = np.insert(mutant.code, insertion_indices, insertion_values) # Random deletions - n_deletions = np.random.randint(max_deletions) - deletion_indices = np.random.choice( + n_deletions = rng.integers(max_deletions) + deletion_indices = rng.choice( np.arange(len(mutant)), size=n_deletions, replace=False ) mutant.code = np.delete(mutant.code, deletion_indices) @@ -95,29 +94,24 @@ def test_cigar_conversion(cigar): assert test_cigar == cigar -@pytest.mark.parametrize( - "seed, local, distinguish_matches, include_terminal_gaps", - itertools.product( - range(20), - [False, True], - [False, True], - [False, True], - ), -) +@pytest.mark.parametrize("seed", range(20)) +@pytest.mark.parametrize("local", [False, True]) +@pytest.mark.parametrize("distinguish_matches", [False, True]) +@pytest.mark.parametrize("include_terminal_gaps", [False, True]) def test_alignment_conversion(seed, local, distinguish_matches, include_terminal_gaps): """ Check whether an :class:`Alignment` converted into a CIGAR string and back again into an :class:`Alignment` gives the same result. """ REF_LENGTH = 1000 - np.random.seed(seed) + rng = np.random.default_rng(seed) ref = seq.NucleotideSequence(ambiguous=False) - ref.code = np.random.randint(0, len(ref.alphabet), REF_LENGTH, dtype=np.uint8) + ref.code = rng.integers(0, len(ref.alphabet), REF_LENGTH, dtype=np.uint8) - excerpt_start = np.random.randint(0, 200) - excerpt_stop = np.random.randint(REF_LENGTH - 200, REF_LENGTH) + excerpt_start = rng.integers(0, 200) + excerpt_stop = rng.integers(REF_LENGTH - 200, REF_LENGTH) seg = ref[excerpt_start:excerpt_stop] - seg = _mutate_sequence(seg) + seg = _mutate_sequence(seg, rng) matrix = align.SubstitutionMatrix.std_nucleotide_matrix() if local: diff --git a/tests/sequence/align/test_kmeralphabet.py b/tests/sequence/align/test_kmeralphabet.py index 67b3f9b03..67225767e 100644 --- a/tests/sequence/align/test_kmeralphabet.py +++ b/tests/sequence/align/test_kmeralphabet.py @@ -20,18 +20,18 @@ def spaced_kmer_alphabet(): return align.KmerAlphabet(seq.ProteinSequence.alphabet, K, spacing=[0, 1, 2]) -np.random.seed(0) N = 10 L = 30 +_rng = np.random.default_rng(0) @pytest.mark.parametrize( "ref_split_kmer_code", # Test for single instances as input - list(np.random.randint(len(seq.ProteinSequence.alphabet), size=(N, K))) + list(_rng.integers(len(seq.ProteinSequence.alphabet), size=(N, K))) + # Test for multiple instances as input - list(np.random.randint(len(seq.ProteinSequence.alphabet), size=(N, L, K))), + list(_rng.integers(len(seq.ProteinSequence.alphabet), size=(N, L, K))), ) def test_fuse_and_split(kmer_alphabet, ref_split_kmer_code): """ @@ -44,12 +44,12 @@ def test_fuse_and_split(kmer_alphabet, ref_split_kmer_code): assert test_split_kmer_code.tolist() == ref_split_kmer_code.tolist() -np.random.seed(0) N = 10 @pytest.mark.parametrize( - "split_kmer_code", np.random.randint(len(seq.ProteinSequence.alphabet), size=(N, K)) + "split_kmer_code", + np.random.default_rng(0).integers(len(seq.ProteinSequence.alphabet), size=(N, K)), ) def test_encode_and_decode(kmer_alphabet, split_kmer_code): """ @@ -71,10 +71,9 @@ def test_create_continuous_kmers(kmer_alphabet): :meth:`fuse()`, which rely on two different implementations. The input sequence code is randomly created. """ - np.random.seed(0) LENGTH = 100 - seq_code = np.random.randint( + seq_code = np.random.default_rng(0).integers( len(seq.ProteinSequence.alphabet), size=LENGTH, dtype=np.uint8 ) @@ -101,10 +100,10 @@ def test_create_spaced_kmers(kmer_alphabet, spaced_kmer_alphabet, seed): """ MIN_LENGTH = 10 MAX_LENGTH = 1000 - np.random.seed(seed) + rng = np.random.default_rng(seed) sequence = seq.ProteinSequence() - sequence.code = np.random.randint( - len(sequence.alphabet), size=np.random.randint(MIN_LENGTH, MAX_LENGTH) + sequence.code = rng.integers( + len(sequence.alphabet), size=rng.integers(MIN_LENGTH, MAX_LENGTH) ) ref_kmers = kmer_alphabet.create_kmers(sequence.code) diff --git a/tests/sequence/align/test_kmersimilarity.py b/tests/sequence/align/test_kmersimilarity.py index 5f1fbbf44..f962b46e6 100644 --- a/tests/sequence/align/test_kmersimilarity.py +++ b/tests/sequence/align/test_kmersimilarity.py @@ -13,13 +13,13 @@ def kmer_alphabet(): return align.KmerAlphabet(seq.ProteinSequence.alphabet, 3) -np.random.seed(0) N = 10 +_rng = np.random.default_rng(0) @pytest.mark.parametrize( - "ref_kmer, threshold", - zip(np.random.randint(10000, size=N), np.random.randint(-5, 15, size=N)), + ["ref_kmer", "threshold"], + zip(_rng.integers(10000, size=N), _rng.integers(-5, 15, size=N)), ) def test_score_threshold_rule(kmer_alphabet, ref_kmer, threshold): """ diff --git a/tests/sequence/align/test_kmertable.py b/tests/sequence/align/test_kmertable.py index 9c7cc4e30..ed2c434d6 100644 --- a/tests/sequence/align/test_kmertable.py +++ b/tests/sequence/align/test_kmertable.py @@ -3,7 +3,6 @@ # information. import functools -import itertools import pickle import string import numpy as np @@ -64,19 +63,17 @@ def random_sequences(request, k, alphabet): return sequences +@pytest.mark.parametrize("spacing", [None, "10111011011", "1001111010101"]) @pytest.mark.parametrize( - "spacing, table_class", - itertools.product( - [None, "10111011011", "1001111010101"], - [ - align.KmerTable, - # Choose number of buckets, so that there is one test case - # with less buckets than number of possible kmers ... - FixedBucketKmerTable(1000), - # ... and one test case with more buckets (perfect hashing) - FixedBucketKmerTable(1000000), - ], - ), + "table_class", + [ + align.KmerTable, + # Choose number of buckets, so that there is one test case + # with less buckets than number of possible kmers ... + FixedBucketKmerTable(1000), + # ... and one test case with more buckets (perfect hashing) + FixedBucketKmerTable(1000000), + ], ids=idfn, ) def test_from_sequences(k, random_sequences, spacing, table_class): @@ -197,13 +194,11 @@ def test_from_positions(k, random_sequences): @pytest.mark.parametrize( - "table_class, use_similarity_rule", - itertools.product( - [align.KmerTable, FixedBucketKmerTable(1000), FixedBucketKmerTable(10000000)], - [False, True], - ), + "table_class", + [align.KmerTable, FixedBucketKmerTable(1000), FixedBucketKmerTable(10000000)], ids=idfn, ) +@pytest.mark.parametrize("use_similarity_rule", [False, True]) def test_match_table(table_class, use_similarity_rule): """ Test the :meth:`match_table()` method based on a known example. @@ -253,13 +248,11 @@ def test_match_table(table_class, use_similarity_rule): @pytest.mark.parametrize( - "table_class, use_similarity_rule", - itertools.product( - [align.KmerTable, FixedBucketKmerTable(1000), FixedBucketKmerTable(1000000)], - [False, True], - ), + "table_class", + [align.KmerTable, FixedBucketKmerTable(1000), FixedBucketKmerTable(1000000)], ids=idfn, ) +@pytest.mark.parametrize("use_similarity_rule", [False, True]) def test_match(k, random_sequences, table_class, use_similarity_rule): """ Test the :meth:`match()` method compared to a manual, @@ -324,13 +317,11 @@ def test_match_kmer_selection(k, random_sequences, table_class): @pytest.mark.parametrize( - "table_class, use_mask", - itertools.product( - [align.KmerTable, FixedBucketKmerTable(1000), FixedBucketKmerTable(1000000)], - [False, True], - ), + "table_class", + [align.KmerTable, FixedBucketKmerTable(1000), FixedBucketKmerTable(1000000)], ids=idfn, ) +@pytest.mark.parametrize("use_mask", [False, True]) def test_match_equivalence(k, random_sequences, table_class, use_mask): """ Check if both, the :meth:`match()` and meth:`match_table()` @@ -374,7 +365,7 @@ def test_match_equivalence(k, random_sequences, table_class, use_mask): @pytest.mark.parametrize( - "k, input_mask, ref_output_mask", + ["k", "input_mask", "ref_output_mask"], [ ( 3, @@ -413,7 +404,7 @@ def test_masking(k, input_mask, ref_output_mask): @pytest.mark.parametrize( - "table_class, selected_kmers", + ["table_class", "selected_kmers"], [ (align.KmerTable, False), (align.KmerTable, True), @@ -484,10 +475,8 @@ def test_pickle(k, random_sequences, table_class): assert test_table == ref_table -@pytest.mark.parametrize( - "n_kmers, load_factor", - itertools.product([1_000, 100_000, 10_000_000, 1_000_000_000], [0.2, 1.0, 2.0]), -) +@pytest.mark.parametrize("n_kmers", [1_000, 100_000, 10_000_000, 1_000_000_000]) +@pytest.mark.parametrize("load_factor", [0.2, 1.0, 2.0]) def test_bucket_number(n_kmers, load_factor): """ Check if the number of buckets is always maximum five percent larger diff --git a/tests/sequence/align/test_localgapped.py b/tests/sequence/align/test_localgapped.py index 480a64e43..61014d03a 100644 --- a/tests/sequence/align/test_localgapped.py +++ b/tests/sequence/align/test_localgapped.py @@ -2,23 +2,17 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools import numpy as np import pytest import biotite.sequence as seq import biotite.sequence.align as align -@pytest.mark.parametrize( - "gap_penalty, seed, threshold, direction, score_only", - itertools.product( - [-10, (-10, -1)], - [(0, 0), (11, 11), (20, 19), (30, 29)], - [20, 100, 500], - ["both", "upstream", "downstream"], - [False, True], - ), -) +@pytest.mark.parametrize("gap_penalty", [-10, (-10, -1)]) +@pytest.mark.parametrize("seed", [(0, 0), (11, 11), (20, 19), (30, 29)]) +@pytest.mark.parametrize("threshold", [20, 100, 500]) +@pytest.mark.parametrize("direction", ["both", "upstream", "downstream"]) +@pytest.mark.parametrize("score_only", [False, True]) def test_simple_alignment(gap_penalty, seed, threshold, direction, score_only): """ Test `align_local_gapped()` by comparing the output to @@ -59,15 +53,10 @@ def test_simple_alignment(gap_penalty, seed, threshold, direction, score_only): assert alignment in ref_alignments -@pytest.mark.parametrize( - "gap_penalty, direction, score_only, should_raise", - itertools.product( - [-10, (-10, -1)], - ["both", "upstream", "downstream"], - [False, True], - [False, True], - ), -) +@pytest.mark.parametrize("gap_penalty", [-10, (-10, -1)]) +@pytest.mark.parametrize("direction", ["both", "upstream", "downstream"]) +@pytest.mark.parametrize("score_only", [False, True]) +@pytest.mark.parametrize("should_raise", [False, True]) def test_max_table_size(gap_penalty, direction, score_only, should_raise): """ Check if the `max_table_size` parameter in `align_local_gapped()` @@ -83,9 +72,9 @@ def test_max_table_size(gap_penalty, direction, score_only, should_raise): # Align a long random sequence to itself, # effectively resulting in a global alignment - np.random.seed(0) + rng = np.random.default_rng(0) seq1 = seq.NucleotideSequence() - seq1.code = np.random.randint(len(seq1.alphabet), size=10000) + seq1.code = rng.integers(len(seq1.alphabet), size=10000) matrix = align.SubstitutionMatrix.std_nucleotide_matrix() # Local alignment starts in the center of the sequences diff --git a/tests/sequence/align/test_localungapped.py b/tests/sequence/align/test_localungapped.py index 066a2b77b..985eff925 100644 --- a/tests/sequence/align/test_localungapped.py +++ b/tests/sequence/align/test_localungapped.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools import numpy as np import pytest import biotite.sequence as seq @@ -11,67 +10,61 @@ @pytest.mark.parametrize( - "seq_type, seq1, seq2, seed, threshold, ref_range1, ref_range2," - "direction, score_only, uint8_code", - [list(itertools.chain(*e)) for e in itertools.product( - [ - ( - seq.NucleotideSequence, # seq_type - "TTTAAAAAAATTTTT", # seq1 - "CCCCCAAAAAAACCC", # seq1 - (5, 7), # seed - 0, # threshold - (3, 10), # ref_range1 - (5, 12), # ref_range2 - ), - ( - seq.NucleotideSequence, - "TTTAAAAAAATTTTT", - "CCCCCAAAAAAACCC", - (3, 5), - 100, - (3, 10), - (5, 12), - ), - # Mismatch should lead to alignment termination - ( - seq.ProteinSequence, - "NLYIQWLKDGGPSSGRPPPS", - "YIQWLKDGGPSLGRPPPS", - (8, 6), - 0, - (2, 13), - (0, 11), - ), - # Mismatch should not terminate alignment - ( - seq.ProteinSequence, - "NLYIQWLKDGGPSSGRPPPS", - "YIQWLKDGGPSLGRPPPS", - (8, 6), - 2, - (2, 20), - (0, 18), - ), - # Mismatch should terminate alignment - ( - seq.ProteinSequence, - "NLYIQWLKDGGPSSGRPPPS", - "YIQWLKDWWPSSGRPPPS", - (6, 4), - 3, - (2, 9), - (0, 7), - ), - ], - - [["both"], ["upstream"], ["downstream"]], # direction - - [[False], [True]], # score_only - - [[False], [True]], # uint8_code - )] + ["seq_type", "seq1", "seq2", "seed", "threshold", "ref_range1", "ref_range2"], + [ + ( + seq.NucleotideSequence, # seq_type + "TTTAAAAAAATTTTT", # seq1 + "CCCCCAAAAAAACCC", # seq2 + (5, 7), # seed + 0, # threshold + (3, 10), # ref_range1 + (5, 12), # ref_range2 + ), + ( + seq.NucleotideSequence, + "TTTAAAAAAATTTTT", + "CCCCCAAAAAAACCC", + (3, 5), + 100, + (3, 10), + (5, 12), + ), + # Mismatch should lead to alignment termination + ( + seq.ProteinSequence, + "NLYIQWLKDGGPSSGRPPPS", + "YIQWLKDGGPSLGRPPPS", + (8, 6), + 0, + (2, 13), + (0, 11), + ), + # Mismatch should not terminate alignment + ( + seq.ProteinSequence, + "NLYIQWLKDGGPSSGRPPPS", + "YIQWLKDGGPSLGRPPPS", + (8, 6), + 2, + (2, 20), + (0, 18), + ), + # Mismatch should terminate alignment + ( + seq.ProteinSequence, + "NLYIQWLKDGGPSSGRPPPS", + "YIQWLKDWWPSSGRPPPS", + (6, 4), + 3, + (2, 9), + (0, 7), + ), + ], ) # fmt: skip +@pytest.mark.parametrize("direction", ["both", "upstream", "downstream"]) +@pytest.mark.parametrize("score_only", [False, True]) +@pytest.mark.parametrize("uint8_code", [False, True]) def test_simple_alignments( seq_type, seq1, @@ -124,9 +117,8 @@ def test_simple_alignments( assert test_result == ref_alignment -@pytest.mark.parametrize( - "seed, uint8_code", itertools.product(range(100), [False, True]) -) +@pytest.mark.parametrize("seed", range(100)) +@pytest.mark.parametrize("uint8_code", [False, True]) def test_random_alignment(seed, uint8_code): """ Create two randomized sequences and place a conserved region into @@ -144,12 +136,12 @@ def test_random_alignment(seed, uint8_code): MUTATION_PROB = 0.1 THRESHOLD = 100 - np.random.seed(seed) + rng = np.random.default_rng(seed) # Create conserved regions conserved1 = ProteinSequence() - conserved_len = np.random.randint(MIN_CONSERVED_SIZE, MAX_CONSERVED_SIZE + 1) - conserved1.code = np.random.randint( + conserved_len = rng.integers(MIN_CONSERVED_SIZE, MAX_CONSERVED_SIZE + 1) + conserved1.code = rng.integers( # Do not include stop symbol for aesthetic reasons -> -1 len(conserved1.alphabet) - 1, size=conserved_len, @@ -158,10 +150,10 @@ def test_random_alignment(seed, uint8_code): # The second conserved regions is equal to the first one, # except a few point mutations conserved2.code = conserved1.code.copy() - mutation_mask = np.random.choice( + mutation_mask = rng.choice( [False, True], size=conserved_len, p=[1 - MUTATION_PROB, MUTATION_PROB] ) - conserved2.code[mutation_mask] = np.random.randint( + conserved2.code[mutation_mask] = rng.integers( len(conserved2.alphabet) - 1, size=np.count_nonzero(mutation_mask) ) # Flank the conserved regions with equal termini to ensure @@ -174,15 +166,15 @@ def test_random_alignment(seed, uint8_code): seq2 = ProteinSequence() offset = [] for sequence, conserved in zip((seq1, seq2), (conserved1, conserved2)): - sequence.code = np.random.randint( - len(sequence.alphabet) - 1, size=np.random.randint(MIN_SIZE, MAX_SIZE + 1) + sequence.code = rng.integers( + len(sequence.alphabet) - 1, size=rng.integers(MIN_SIZE, MAX_SIZE + 1) ) # Place conserved region randomly within the sequence - conserved_pos = np.random.randint(0, len(sequence) - len(conserved)) + conserved_pos = rng.integers(0, len(sequence) - len(conserved)) sequence.code[conserved_pos : conserved_pos + len(conserved)] = conserved.code offset.append(conserved_pos) # The seed is placed somewhere in the conserved region - seed = np.array(offset) + np.random.randint(len(conserved)) + seed = np.array(offset) + rng.integers(len(conserved)) matrix = align.SubstitutionMatrix.std_protein_matrix() if not uint8_code: diff --git a/tests/sequence/align/test_matrix.py b/tests/sequence/align/test_matrix.py index 94234f1b7..c562e756c 100644 --- a/tests/sequence/align/test_matrix.py +++ b/tests/sequence/align/test_matrix.py @@ -27,7 +27,7 @@ def test_matrices(db_entry): @pytest.mark.parametrize( - "matrix_name, alphabet", + ["matrix_name", "alphabet"], [ ("3Di", strucalph.I3DSequence.alphabet), ], @@ -82,13 +82,13 @@ def test_as_positional(seed): """ MAX_SEQ_LENGTH = 10 - np.random.seed(seed) + rng = np.random.default_rng(seed) matrix = align.SubstitutionMatrix.std_protein_matrix() sequences = [] for _ in range(2): - seq_length = np.random.randint(1, MAX_SEQ_LENGTH) + seq_length = rng.integers(1, MAX_SEQ_LENGTH) sequence = seq.ProteinSequence() - sequence.code = np.random.randint(0, len(sequence.alphabet), size=seq_length) + sequence.code = rng.integers(0, len(sequence.alphabet), size=seq_length) sequences.append(sequence) pos_matrix, *pos_sequences = matrix.as_positional(*sequences) diff --git a/tests/sequence/align/test_pairwise.py b/tests/sequence/align/test_pairwise.py index 050d7a345..58145186e 100644 --- a/tests/sequence/align/test_pairwise.py +++ b/tests/sequence/align/test_pairwise.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools import numpy as np import pytest import biotite.application.muscle as muscle @@ -51,7 +50,7 @@ def test_align_ungapped(): @pytest.mark.parametrize( - "local, term, gap_penalty, input1, input2, expect", align_cases + ["local", "term", "gap_penalty", "input1", "input2", "expect"], align_cases ) def test_align_optimal_simple(local, term, gap_penalty, input1, input2, expect): """ @@ -76,11 +75,11 @@ def test_align_optimal_simple(local, term, gap_penalty, input1, input2, expect): @pytest.mark.skipif(is_not_installed("muscle"), reason="MUSCLE is not installed") # Ignore warning about MUSCLE writing no second guide tree @pytest.mark.filterwarnings("ignore") +@pytest.mark.parametrize("gap_penalty", [-10, (-10, -1)]) @pytest.mark.parametrize( - "gap_penalty, seq_indices", - itertools.product( - [-10, (-10, -1)], [(i, j) for i in range(10) for j in range(i + 1)] - ), + "seq_indices", + [(i, j) for i in range(10) for j in range(i + 1)], + ids=lambda indices: f"{indices[0]}-{indices[1]}", ) def test_align_optimal_complex(sequences, gap_penalty, seq_indices): """ @@ -121,10 +120,10 @@ def test_align_optimal_complex(sequences, gap_penalty, seq_indices): raise -@pytest.mark.parametrize( - "local, term, gap_penalty, seed", - itertools.product([True, False], [True, False], [-5, -8, -10, -15], range(10)), -) +@pytest.mark.parametrize("local", [True, False]) +@pytest.mark.parametrize("term", [True, False]) +@pytest.mark.parametrize("gap_penalty", [-5, -8, -10, -15]) +@pytest.mark.parametrize("seed", range(10)) def test_affine_gap_penalty(local, term, gap_penalty, seed): """ Expect the same alignment results for a linear gap penalty and an @@ -133,12 +132,12 @@ def test_affine_gap_penalty(local, term, gap_penalty, seed): LENGTH_RANGE = (10, 100) MAX_NUMBER = 1000 - np.random.seed(seed) + rng = np.random.default_rng(seed) sequences = [] for _ in range(2): sequence = seq.NucleotideSequence() - length = np.random.randint(*LENGTH_RANGE) - sequence.code = np.random.randint(len(sequence.alphabet), size=length) + length = rng.integers(*LENGTH_RANGE) + sequence.code = rng.integers(len(sequence.alphabet), size=length) sequences.append(sequence) matrix = align.SubstitutionMatrix.std_nucleotide_matrix() @@ -168,13 +167,12 @@ def test_affine_gap_penalty(local, term, gap_penalty, seed): raise +@pytest.mark.parametrize("gap_penalty", [-10, (-10, -1)]) +@pytest.mark.parametrize("term", [False, True]) @pytest.mark.parametrize( - "gap_penalty, term, seq_indices", - itertools.product( - [-10, (-10, -1)], - [False, True], - [(i, j) for i in range(10) for j in range(i + 1)], - ), + "seq_indices", + [(i, j) for i in range(10) for j in range(i + 1)], + ids=lambda indices: f"{indices[0]}-{indices[1]}", ) def test_scoring_terminal_penalty(sequences, gap_penalty, term, seq_indices): """ diff --git a/tests/sequence/align/test_permutation.py b/tests/sequence/align/test_permutation.py index 1b22b5579..27a9ec3c8 100644 --- a/tests/sequence/align/test_permutation.py +++ b/tests/sequence/align/test_permutation.py @@ -29,10 +29,8 @@ def test_random_permutation_modulo(): LCG_M = int(2**64) SEQ_LENGTH = 10_000 - np.random.seed(0) - kmers = np.random.randint( - np.iinfo(np.int64).max + 1, size=SEQ_LENGTH, dtype=np.int64 - ) + rng = np.random.default_rng(0) + kmers = rng.integers(np.iinfo(np.int64).max + 1, size=SEQ_LENGTH, dtype=np.int64) ref_order = [(LCG_A * kmer.item() + LCG_C) % LCG_M for kmer in kmers] @@ -72,12 +70,12 @@ def test_random_permutation_randomness(): def test_frequency_permutation(): K = 5 kmer_alphabet = align.KmerAlphabet(seq.NucleotideSequence.alphabet_unamb, K) - np.random.seed(0) + rng = np.random.default_rng(0) # Generate a random count order for each k-mer # Use 'np.arange()' to generate a unique order, # but also use step != 1 to make the count != order counts = np.arange(2 * len(kmer_alphabet), step=2) - np.random.shuffle(counts) + rng.shuffle(counts) kmer_table = align.KmerTable.from_positions( kmer_alphabet, # The actual k-mer positions are dummy values, @@ -94,7 +92,7 @@ def test_frequency_permutation(): @pytest.mark.parametrize( - "kmer_range, permutation", + ["kmer_range", "permutation"], [ (np.iinfo(np.int64).max, align.RandomPermutation()), (int(4**5), _create_frequency_permutation(5)), @@ -111,8 +109,8 @@ def test_min_max(kmer_range, permutation): TOLERANCE = 0.001 N_KMERS = 100_000 - np.random.seed(0) - kmers = np.random.randint(kmer_range, size=N_KMERS, dtype=np.int64) + rng = np.random.default_rng(0) + kmers = rng.integers(kmer_range, size=N_KMERS, dtype=np.int64) order = permutation.permute(kmers) assert permutation.max > permutation.min diff --git a/tests/sequence/align/test_selector.py b/tests/sequence/align/test_selector.py index a062df7eb..ec9c6c951 100644 --- a/tests/sequence/align/test_selector.py +++ b/tests/sequence/align/test_selector.py @@ -2,17 +2,16 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools import numpy as np import pytest import biotite.sequence as seq import biotite.sequence.align as align -@pytest.mark.parametrize( - "seed, window, from_sequence, use_permutation", - itertools.product(range(20), [2, 5, 10, 25], [False, True], [False, True]), -) +@pytest.mark.parametrize("seed", range(20)) +@pytest.mark.parametrize("window", [2, 5, 10, 25]) +@pytest.mark.parametrize("from_sequence", [False, True]) +@pytest.mark.parametrize("use_permutation", [False, True]) def test_minimizer(seed, window, from_sequence, use_permutation): """ Compare the fast minimizer identification algorithm against @@ -22,8 +21,8 @@ def test_minimizer(seed, window, from_sequence, use_permutation): LENGTH = 1000 sequence = seq.NucleotideSequence(ambiguous=False) - np.random.seed(seed) - sequence.code = np.random.randint(len(sequence.alphabet), size=LENGTH) + rng = np.random.default_rng(seed) + sequence.code = rng.integers(len(sequence.alphabet), size=LENGTH) kmer_alph = align.KmerAlphabet(sequence.alphabet, K) kmers = kmer_alph.create_kmers(sequence.code) @@ -54,18 +53,16 @@ def test_minimizer(seed, window, from_sequence, use_permutation): assert test_minimizers.tolist() == ref_minimizers.tolist() +@pytest.mark.parametrize("seed", range(20)) +@pytest.mark.parametrize("s", [2, 3, 5, 7]) @pytest.mark.parametrize( - "seed, s, offset, from_sequence, use_permutation", - itertools.product( - range(20), - [2, 3, 5, 7], - [(0,), (0, 1, 2), (0, -1), (-2, -1)], - [False, True], - [False, True], - ), + "offset", + [(0,), (0, 1, 2), (0, -1), (-2, -1)], # Print tuples in name of test - ids=lambda x: str(x).replace(" ", "") if isinstance(x, tuple) else None, + ids=lambda offset: str(offset).replace(" ", ""), ) +@pytest.mark.parametrize("from_sequence", [False, True]) +@pytest.mark.parametrize("use_permutation", [False, True]) def test_syncmer(seed, s, offset, from_sequence, use_permutation): """ Compare the fast syncmer identification algorithm against @@ -76,8 +73,8 @@ def test_syncmer(seed, s, offset, from_sequence, use_permutation): sequence = seq.NucleotideSequence(ambiguous=False) alphabet = sequence.alphabet - np.random.seed(seed) - sequence.code = np.random.randint(len(alphabet), size=LENGTH) + rng = np.random.default_rng(seed) + sequence.code = rng.integers(len(alphabet), size=LENGTH) kmers = align.KmerAlphabet(alphabet, K).create_kmers(sequence.code) smers = align.KmerAlphabet(alphabet, s).create_kmers(sequence.code) @@ -128,8 +125,8 @@ def test_cached_syncmer(): LENGTH = 1000 sequence = seq.NucleotideSequence(ambiguous=False) - np.random.seed(0) - sequence.code = np.random.randint(len(sequence.alphabet), size=LENGTH) + rng = np.random.default_rng(0) + sequence.code = rng.integers(len(sequence.alphabet), size=LENGTH) syncmer_selector = align.SyncmerSelector(sequence.alphabet, K, S) ref_syncmer_pos, ref_syncmers = syncmer_selector.select(sequence) @@ -142,7 +139,7 @@ def test_cached_syncmer(): @pytest.mark.parametrize( - "offset, exception_type", + ["offset", "exception_type"], [ # Duplicate values ((1, 1), ValueError), @@ -180,7 +177,6 @@ def test_mincode(use_permutation): COMPRESSION = 4 kmer_alph = align.KmerAlphabet(seq.NucleotideSequence.alphabet_unamb, K) - np.random.seed(0) kmers = np.arange(len(kmer_alph), dtype=np.int64) if use_permutation: diff --git a/tests/sequence/align/test_statistics.py b/tests/sequence/align/test_statistics.py index cb0840a16..84843bbad 100644 --- a/tests/sequence/align/test_statistics.py +++ b/tests/sequence/align/test_statistics.py @@ -44,7 +44,7 @@ @pytest.mark.parametrize( - "matrix_name, gap_penalty, ref_lam, ref_k", + ["matrix_name", "gap_penalty", "ref_lam", "ref_k"], [ ("BLOSUM62", (-10000, -10000), 0.318, 0.130), ("BLOSUM62", (-12, -2), 0.300, 0.090), diff --git a/tests/sequence/align/util.py b/tests/sequence/align/util.py index 57d862a7f..81ea3ed54 100644 --- a/tests/sequence/align/util.py +++ b/tests/sequence/align/util.py @@ -5,7 +5,6 @@ import io import json import tarfile -from os.path import join from tests.util import data_dir @@ -23,12 +22,12 @@ def legacy_alignments(): # if a Cython module is not compiled yet import biotite.sequence.io.fasta as fasta - base_dir = join(data_dir("sequence"), "legacy_consistency") - with open(join(base_dir, "params.json")) as file: + base_dir = data_dir("sequence") / "legacy_consistency" + with open(base_dir / "params.json") as file: params = json.load(file) pairs = [] - with tarfile.open(join(base_dir, "legacy_alignments.tar.gz"), "r:gz") as tar: + with tarfile.open(base_dir / "legacy_alignments.tar.gz", "r:gz") as tar: for file_name in sorted(params): entry = params[file_name] with io.TextIOWrapper( diff --git a/tests/sequence/test_alphabet.py b/tests/sequence/test_alphabet.py index 266563b1f..c9bf0c744 100644 --- a/tests/sequence/test_alphabet.py +++ b/tests/sequence/test_alphabet.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools import pickle import numpy as np import pytest @@ -26,7 +25,7 @@ def alphabet_symbols(): @pytest.mark.parametrize( - "symbols, exp_code, use_letter_alphabet", + ["symbols", "exp_code", "use_letter_alphabet"], zip( list(test_cases.keys()) * 2, list(test_cases.values()) * 2, @@ -46,7 +45,7 @@ def test_encoding(alphabet_symbols, symbols, exp_code, use_letter_alphabet): @pytest.mark.parametrize( - "exp_symbols, code, use_letter_alphabet", + ["exp_symbols", "code", "use_letter_alphabet"], zip( list(test_cases.keys()) * 2, list(test_cases.values()) * 2, @@ -66,10 +65,8 @@ def test_decoding(alphabet_symbols, exp_symbols, code, use_letter_alphabet): assert list(alph.decode_multiple(code)) == list(exp_symbols) -@pytest.mark.parametrize( - "use_letter_alphabet, is_single_val", - itertools.product([False, True], [False, True]), -) +@pytest.mark.parametrize("use_letter_alphabet", [False, True]) +@pytest.mark.parametrize("is_single_val", [False, True]) def test_error(alphabet_symbols, use_letter_alphabet, is_single_val): if use_letter_alphabet: alph = seq.LetterAlphabet(alphabet_symbols) @@ -145,7 +142,7 @@ def test_contains(alphabet_symbols, use_letter_alphabet): @pytest.mark.parametrize( - "source_alph_symbols, target_alph_symbols", + ["source_alph_symbols", "target_alph_symbols"], [ ("A", "AB"), (["foo", "bar"], ["bar", "foo", 42]), @@ -161,8 +158,8 @@ def test_alphabet_mapper(source_alph_symbols, target_alph_symbols): mapper = seq.AlphabetMapper(source_alph, target_alph) ref_sequence = seq.GeneralSequence(source_alph) - np.random.seed(0) - ref_sequence.code = np.random.randint(len(source_alph), size=CODE_LENGTH, dtype=int) + rng = np.random.default_rng(0) + ref_sequence.code = rng.integers(len(source_alph), size=CODE_LENGTH, dtype=int) test_sequence = seq.GeneralSequence(target_alph) test_sequence.code = mapper[ref_sequence.code] @@ -171,7 +168,7 @@ def test_alphabet_mapper(source_alph_symbols, target_alph_symbols): @pytest.mark.parametrize( - "alphabets, common_alph", + ["alphabets", "common_alph"], [ ( [ diff --git a/tests/sequence/test_annotation.py b/tests/sequence/test_annotation.py index b1159933f..eed5ccd18 100644 --- a/tests/sequence/test_annotation.py +++ b/tests/sequence/test_annotation.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import biotite.sequence as seq import biotite.sequence.io.genbank as gb from biotite.sequence import AnnotatedSequence, Annotation, Feature, Location @@ -98,6 +97,6 @@ def test_annotated_sequence(): def test_reverse_complement(): - gb_file = gb.GenBankFile.read(join(data_dir("sequence"), "ec_bl21.gb")) + gb_file = gb.GenBankFile.read(data_dir("sequence") / "ec_bl21.gb") annot_seq = gb.get_annotated_sequence(gb_file) assert annot_seq == annot_seq.reverse_complement().reverse_complement() diff --git a/tests/sequence/test_clustal.py b/tests/sequence/test_clustal.py index 57440ebad..557eeee9f 100644 --- a/tests/sequence/test_clustal.py +++ b/tests/sequence/test_clustal.py @@ -3,8 +3,6 @@ # information. import io -import os -import os.path import pytest import biotite.sequence as seq import biotite.sequence.io.clustal as clustal @@ -13,7 +11,7 @@ def test_access_low_level(): """Test reading a ClustalW file and accessing sequences by name.""" - path = os.path.join(data_dir("sequence"), "clustal.aln") + path = data_dir("sequence") / "clustal.aln" file = clustal.ClustalFile.read(path) assert file["seq1"] == "ADTRCGTARDCGTR-DRTCGRAGD" assert file["seq2"] == "ADTRCGT---CGTRADRTCGRAGD" @@ -23,7 +21,7 @@ def test_access_low_level(): def test_access_multi_block(): """Test reading a ClustalW file with multiple blocks.""" - path = os.path.join(data_dir("sequence"), "clustal_multi.aln") + path = data_dir("sequence") / "clustal_multi.aln" file = clustal.ClustalFile.read(path) assert file["seq1"] == ("ADTRCGTARDCGTR-DRTCGRAGDADTRCGTARDCGTR-DRTCGRAGD") assert file["seq2"] == ("ADTRCGT---CGTRADRTCGRAGDADTRCGT---CGTRADRTCGRAGD") @@ -45,7 +43,7 @@ def test_dict_interface(): def test_alignment_conversion(): """Test conversion between ClustalFile and Alignment object.""" - path = os.path.join(data_dir("sequence"), "clustal.aln") + path = data_dir("sequence") / "clustal.aln" file = clustal.ClustalFile.read(path) alignment = clustal.get_alignment(file) assert str(alignment) == ( @@ -57,7 +55,7 @@ def test_alignment_conversion(): def test_round_trip(): """Test writing an alignment and reading it back.""" - path = os.path.join(data_dir("sequence"), "clustal.aln") + path = data_dir("sequence") / "clustal.aln" file = clustal.ClustalFile.read(path) alignment = clustal.get_alignment(file) @@ -71,7 +69,7 @@ def test_round_trip(): def test_write_read_round_trip(): """Test that writing to a file and reading back yields the same alignment.""" - path = os.path.join(data_dir("sequence"), "clustal.aln") + path = data_dir("sequence") / "clustal.aln" file = clustal.ClustalFile.read(path) alignment = clustal.get_alignment(file) @@ -90,7 +88,7 @@ def test_write_read_round_trip(): def test_name_count_mismatch(): """Test that mismatched name count raises ValueError.""" - path = os.path.join(data_dir("sequence"), "clustal.aln") + path = data_dir("sequence") / "clustal.aln" file = clustal.ClustalFile.read(path) alignment = clustal.get_alignment(file) @@ -102,7 +100,7 @@ def test_name_count_mismatch(): @pytest.mark.parametrize("seq_type", (None, seq.ProteinSequence)) def test_seq_type(seq_type): """Test explicit sequence type parameter.""" - path = os.path.join(data_dir("sequence"), "clustal.aln") + path = data_dir("sequence") / "clustal.aln" file = clustal.ClustalFile.read(path) alignment = clustal.get_alignment(file, seq_type=seq_type) # Should produce a valid alignment regardless of type diff --git a/tests/sequence/test_fasta.py b/tests/sequence/test_fasta.py index 15da23417..1e23f0c0d 100644 --- a/tests/sequence/test_fasta.py +++ b/tests/sequence/test_fasta.py @@ -2,11 +2,7 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob import io -import itertools -import os -import os.path import numpy as np import pytest import biotite.sequence as seq @@ -15,7 +11,7 @@ def test_access_low_level(): - path = os.path.join(data_dir("sequence"), "nuc.fasta") + path = data_dir("sequence") / "nuc.fasta" file = fasta.FastaFile.read(path) assert file["dna sequence"] == "ACGCTACGT" assert file["another dna sequence"] == "A" @@ -41,7 +37,7 @@ def test_access_low_level(): @pytest.mark.parametrize("seq_type", (None, seq.NucleotideSequence)) def test_access_high_level(seq_type): - path = os.path.join(data_dir("sequence"), "nuc.fasta") + path = data_dir("sequence") / "nuc.fasta" file = fasta.FastaFile.read(path) sequences = fasta.get_sequences(file, seq_type=seq_type) assert sequences == { @@ -57,7 +53,7 @@ def test_access_high_level(seq_type): "seq_type", (None, seq.NucleotideSequence, seq.ProteinSequence) ) def test_sequence_conversion_ambiguous(seq_type): - path = os.path.join(data_dir("sequence"), "nuc.fasta") + path = data_dir("sequence") / "nuc.fasta" file = fasta.FastaFile.read(path) sequence = "ACGCTACGT" @@ -94,7 +90,7 @@ def test_sequence_conversion_ambiguous(seq_type): "seq_type", (None, seq.NucleotideSequence, seq.ProteinSequence) ) def test_sequence_conversion_protein(seq_type): - path = os.path.join(data_dir("sequence"), "prot.fasta") + path = data_dir("sequence") / "prot.fasta" file4 = fasta.FastaFile.read(path) if seq_type != seq.NucleotideSequence: @@ -113,7 +109,7 @@ def test_sequence_conversion_protein(seq_type): "seq_type", (None, seq.NucleotideSequence, seq.ProteinSequence) ) def test_sequence_conversion_invalid(seq_type): - path = os.path.join(data_dir("sequence"), "invalid.fasta") + path = data_dir("sequence") / "invalid.fasta" file = fasta.FastaFile.read(path) with pytest.raises(ValueError): seq.NucleotideSequence(fasta.get_sequence(file), seq_type=seq_type) @@ -129,7 +125,7 @@ def test_rna_conversion(): def test_alignment_conversion(): - path = os.path.join(data_dir("sequence"), "alignment.fasta") + path = data_dir("sequence") / "alignment.fasta" file = fasta.FastaFile.read(path) alignment = fasta.get_alignment(file) assert str(alignment) == ( @@ -149,9 +145,7 @@ def test_a3m_alignment_conversion(): Check if reading alignments from file and writing them again gives the same file content. """ - a3m_file = fasta.FastaFile.read( - os.path.join(data_dir("sequence"), "1a00_A_uniref90.a3m") - ) + a3m_file = fasta.FastaFile.read(data_dir("sequence") / "1a00_A_uniref90.a3m") ref_content = a3m_file.lines labels = list(a3m_file.keys()) @@ -164,7 +158,9 @@ def test_a3m_alignment_conversion(): @pytest.mark.parametrize( - "file_name", glob.glob(os.path.join(data_dir("sequence"), "*.fasta")) + "file_name", + sorted(data_dir("sequence").glob("*.fasta")), + ids=lambda path: path.name, ) def test_read_iter(file_name): ref_dict = dict(fasta.FastaFile.read(file_name).items()) @@ -174,9 +170,8 @@ def test_read_iter(file_name): assert test_dict == ref_dict -@pytest.mark.parametrize( - "chars_per_line, n_sequences", itertools.product([80, 200], [1, 10]) -) +@pytest.mark.parametrize("chars_per_line", [80, 200]) +@pytest.mark.parametrize("n_sequences", [1, 10]) def test_write_iter(chars_per_line, n_sequences): """ Test whether :class:`FastaFile.write()` and @@ -186,13 +181,11 @@ def test_write_iter(chars_per_line, n_sequences): LENGTH_RANGE = (50, 150) # Generate random sequences and scores - np.random.seed(0) + rng = np.random.default_rng(0) sequences = [] for i in range(n_sequences): - seq_length = np.random.randint(*LENGTH_RANGE) - code = np.random.randint( - len(seq.NucleotideSequence.alphabet_unamb), size=seq_length - ) + seq_length = rng.integers(*LENGTH_RANGE) + code = rng.integers(len(seq.NucleotideSequence.alphabet_unamb), size=seq_length) sequence = seq.NucleotideSequence() sequence.code = code sequences.append(sequence) diff --git a/tests/sequence/test_fastq.py b/tests/sequence/test_fastq.py index 9634b3e5d..6cc41e8d5 100644 --- a/tests/sequence/test_fastq.py +++ b/tests/sequence/test_fastq.py @@ -2,11 +2,7 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob import io -import itertools -import os -import os.path from tempfile import TemporaryFile import numpy as np import pytest @@ -17,7 +13,7 @@ @pytest.mark.parametrize("chars_per_line", [None, 80]) def test_access(chars_per_line): - path = os.path.join(data_dir("sequence"), "random.fastq") + path = data_dir("sequence") / "random.fastq" file = fastq.FastqFile.read(path, offset=33, chars_per_line=chars_per_line) assert len(file) == 20 assert list(file.keys()) == [f"Read:{i + 1:02d}" for i in range(20)] @@ -37,7 +33,7 @@ def test_access(chars_per_line): @pytest.mark.parametrize("chars_per_line", [None, 80]) def test_conversion(chars_per_line): - path = os.path.join(data_dir("sequence"), "random.fastq") + path = data_dir("sequence") / "random.fastq" fasta_file = fastq.FastqFile.read(path, offset=33, chars_per_line=chars_per_line) ref_content = dict(fasta_file.items()) @@ -70,7 +66,9 @@ def test_rna_conversion(): @pytest.mark.parametrize( - "file_name", glob.glob(os.path.join(data_dir("sequence"), "*.fastq")) + "file_name", + sorted(data_dir("sequence").glob("*.fastq")), + ids=lambda path: path.name, ) def test_read_iter(file_name): ref_dict = dict(fastq.FastqFile.read(file_name, offset="Sanger").items()) @@ -85,10 +83,9 @@ def test_read_iter(file_name): assert (test_sc == ref_sc).all() -@pytest.mark.parametrize( - "offset, chars_per_line, n_sequences", - itertools.product([33, 42, "Solexa"], [None, 80], [1, 10]), -) +@pytest.mark.parametrize("offset", [33, 42, "Solexa"]) +@pytest.mark.parametrize("chars_per_line", [None, 80]) +@pytest.mark.parametrize("n_sequences", [1, 10]) def test_write_iter(offset, chars_per_line, n_sequences): """ Test whether :class:`FastqFile.write()` and @@ -99,18 +96,16 @@ def test_write_iter(offset, chars_per_line, n_sequences): SCORE_RANGE = (10, 60) # Generate random sequences and scores - np.random.seed(0) + rng = np.random.default_rng(0) sequences = [] scores = [] for i in range(n_sequences): - seq_length = np.random.randint(*LENGTH_RANGE) - code = np.random.randint( - len(seq.NucleotideSequence.alphabet_unamb), size=seq_length - ) + seq_length = rng.integers(*LENGTH_RANGE) + code = rng.integers(len(seq.NucleotideSequence.alphabet_unamb), size=seq_length) sequence = seq.NucleotideSequence() sequence.code = code sequences.append(sequence) - score = np.random.randint(*SCORE_RANGE, size=seq_length) + score = rng.integers(*SCORE_RANGE, size=seq_length) scores.append(score) fastq_file = fastq.FastqFile(offset, chars_per_line) diff --git a/tests/sequence/test_genbank.py b/tests/sequence/test_genbank.py index d96cbddc6..3f73f605d 100644 --- a/tests/sequence/test_genbank.py +++ b/tests/sequence/test_genbank.py @@ -2,8 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob -from os.path import join from tempfile import TemporaryFile import pytest import biotite.sequence as seq @@ -13,8 +11,9 @@ @pytest.mark.parametrize( "path", - glob.glob(join(data_dir("sequence"), "*.gb")) - + glob.glob(join(data_dir("sequence"), "[!multifile]*.gp")), + sorted(data_dir("sequence").glob("*.gb")) + + sorted(data_dir("sequence").glob("[!multifile]*.gp")), + ids=lambda path: path.name, ) def test_contiguous_field_pos(path): """ @@ -51,8 +50,9 @@ def test_file_access(): @pytest.mark.parametrize( "path", - glob.glob(join(data_dir("sequence"), "*.gb")) - + glob.glob(join(data_dir("sequence"), "[!multifile]*.gp")), + sorted(data_dir("sequence").glob("*.gb")) + + sorted(data_dir("sequence").glob("[!multifile]*.gp")), + ids=lambda path: path.name, ) def test_conversion_lowlevel(path): """ @@ -77,8 +77,9 @@ def test_conversion_lowlevel(path): @pytest.mark.parametrize( "path", - glob.glob(join(data_dir("sequence"), "*.gb")) - + glob.glob(join(data_dir("sequence"), "[!multifile]*.gp")), + sorted(data_dir("sequence").glob("*.gb")) + + sorted(data_dir("sequence").glob("[!multifile]*.gp")), + ids=lambda path: path.name, ) def test_conversion_highlevel(path): """ @@ -86,7 +87,7 @@ def test_conversion_highlevel(path): the locus, annotation and sequence from GenBank file and write these properties to a file, without data changing. """ - suffix = path[-2:] + suffix = path.suffix[1:] gb_file = gb.GenBankFile.read(path) ref_locus = gb.get_locus(gb_file) ref_annot_seq = gb.get_annotated_sequence(gb_file, format=suffix) @@ -113,7 +114,7 @@ def test_genbank_utility_gb(): Check whether the high-level utility functions return the expected content of a known GenBank file. """ - gb_file = gb.GenBankFile.read(join(data_dir("sequence"), "ec_bl21.gb")) + gb_file = gb.GenBankFile.read(data_dir("sequence") / "ec_bl21.gb") assert gb.get_locus(gb_file) == ( "CP001509", 4558953, @@ -154,7 +155,7 @@ def test_genbank_utility_gp(): Check whether the high-level utility functions return the expected content of a known GenPept file. """ - gp_file = gb.GenBankFile.read(join(data_dir("sequence"), "bt_lysozyme.gp")) + gp_file = gb.GenBankFile.read(data_dir("sequence") / "bt_lysozyme.gp") # [print(e) for e in gp_file._field_pos] assert gb.get_locus(gp_file) == ("AAC37312", 147, None, False, "MAM", "27-APR-1993") assert gb.get_definition(gp_file) == "lysozyme [Bos taurus]." @@ -185,13 +186,13 @@ def test_genbank_utility_gp(): def test_multi_file(): - multi_file = gb.MultiFile.read(join(data_dir("sequence"), "multifile.gp")) + multi_file = gb.MultiFile.read(data_dir("sequence") / "multifile.gp") accessions = [gb.get_accession(f) for f in multi_file] assert accessions == ["1L2Y_A", "3O5R_A", "5UGO_A"] @pytest.mark.parametrize( - "locus_content, expected_result", + ["locus_content", "expected_result"], [ ( "AJ311647LOOOOOOOOOOOOOOOOOOOOOOOOOONGID 1224 bp DNA linear VRT 14-NOV-2006", diff --git a/tests/sequence/test_generalio.py b/tests/sequence/test_generalio.py index 36ba1a7e4..127280659 100644 --- a/tests/sequence/test_generalio.py +++ b/tests/sequence/test_generalio.py @@ -2,61 +2,62 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob -from os.path import join -from tempfile import NamedTemporaryFile import pytest import biotite.sequence.io as seqio from tests.util import data_dir -@pytest.mark.parametrize("path", glob.glob(join(data_dir("sequence"), "random.*"))) +@pytest.mark.parametrize( + "path", + sorted(data_dir("sequence").glob("random.*")), + ids=lambda path: path.name, +) def test_loading_single(path): - ref_sequence = seqio.load_sequence(join(data_dir("sequence"), "random.fasta")) + ref_sequence = seqio.load_sequence(data_dir("sequence") / "random.fasta") sequence = seqio.load_sequence(path) assert ref_sequence == sequence @pytest.mark.parametrize("suffix", ["fasta", "fastq"]) -def test_saving_single(suffix): - ref_sequence = seqio.load_sequence(join(data_dir("sequence"), "random.fasta")) - temp = NamedTemporaryFile("w+", suffix=f".{suffix}") +def test_saving_single(suffix, tmp_path): + ref_sequence = seqio.load_sequence(data_dir("sequence") / "random.fasta") try: - seqio.save_sequence(temp.name, ref_sequence) + seqio.save_sequence(tmp_path / f"test.{suffix}", ref_sequence) except PermissionError: # This error might occur on AppVeyor pytest.skip("Permission is denied") -@pytest.mark.parametrize("path", glob.glob(join(data_dir("sequence"), "random.*"))) +@pytest.mark.parametrize( + "path", + sorted(data_dir("sequence").glob("random.*")), + ids=lambda path: path.name, +) def test_loading_multiple(path): - ref_sequences = seqio.load_sequences(join(data_dir("sequence"), "random.fasta")) + ref_sequences = seqio.load_sequences(data_dir("sequence") / "random.fasta") sequences = seqio.load_sequences(path) assert ref_sequences == sequences @pytest.mark.parametrize("suffix", ["fasta", "fastq"]) -def test_saving_multiple(suffix): - ref_sequences = seqio.load_sequences(join(data_dir("sequence"), "random.fasta")) - temp = NamedTemporaryFile("w+", suffix=f".{suffix}") +def test_saving_multiple(suffix, tmp_path): + ref_sequences = seqio.load_sequences(data_dir("sequence") / "random.fasta") try: - seqio.save_sequences(temp.name, ref_sequences) + seqio.save_sequences(tmp_path / f"test.{suffix}", ref_sequences) except PermissionError: # This error might occur on AppVeyor pytest.skip("Permission is denied") @pytest.mark.parametrize("file_name", ["gg_avidin.gb", "bt_lysozyme.gp"]) -def test_genbank(file_name): +def test_genbank(file_name, tmp_path): """ Simply test whether reading or writing a GenBank/GenPept file raises an exception. """ - - temp = NamedTemporaryFile("w+", suffix=".gb") - sequence = seqio.load_sequence(join(data_dir("sequence"), file_name)) + sequence = seqio.load_sequence(data_dir("sequence") / file_name) try: - seqio.save_sequence(temp.name, sequence) + seqio.save_sequence(tmp_path / "test.gb", sequence) except PermissionError: # This error might occur on AppVeyor pytest.skip("Permission is denied") diff --git a/tests/sequence/test_gff.py b/tests/sequence/test_gff.py index 0713a8324..01eb05417 100644 --- a/tests/sequence/test_gff.py +++ b/tests/sequence/test_gff.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join from tempfile import TemporaryFile import pytest import biotite.sequence as seq @@ -19,7 +18,7 @@ def test_conversion_lowlevel(path): Test whether the low-level GFF3 interface can properly read a GenBank file and write a file, without data changing. """ - gff_file = gff.GFFFile.read(join(data_dir("sequence"), path)) + gff_file = gff.GFFFile.read(data_dir("sequence") / path) ref_entries = [entry for entry in gff_file] gff_file = gff.GFFFile() @@ -46,7 +45,7 @@ def test_conversion_highlevel(path): The 'phase' is tested additionally, since it is not part of a `Feature` object. """ - gff_file = gff.GFFFile.read(join(data_dir("sequence"), path)) + gff_file = gff.GFFFile.read(data_dir("sequence") / path) ref_annot = gff.get_annotation(gff_file) ref_phases = [] for _, _, type, _, _, _, _, phase, _ in gff_file: @@ -79,10 +78,10 @@ def test_genbank_consistency(path): Test whether the same annotation (if reasonable) can be read from a GFF3 file and a GenBank file. """ - gb_file = gb.GenBankFile.read(join(data_dir("sequence"), path)) + gb_file = gb.GenBankFile.read(data_dir("sequence") / path) ref_annot = gb.get_annotation(gb_file) - gff_file = gff.GFFFile.read(join(data_dir("sequence"), path[:-3] + ".gff3")) + gff_file = gff.GFFFile.read((data_dir("sequence") / path).with_suffix(".gff3")) test_annot = gff.get_annotation(gff_file) # Remove qualifiers, since they will be different @@ -134,7 +133,7 @@ def test_entry_indexing(): test file with multiple directives, including '##FASTA'. """ with pytest.warns(UserWarning): - file = gff.GFFFile.read(join(data_dir("sequence"), "indexing_test.gff3")) + file = gff.GFFFile.read(data_dir("sequence") / "indexing_test.gff3") assert file._directives == [ ("directive 1", 1), ("directive 2", 2), @@ -149,7 +148,7 @@ def test_percent_encoding(): Test whether percent encoding is working correctly based on an artificial test file. """ - file = gff.GFFFile.read(join(data_dir("sequence"), "percent_test.gff3")) + file = gff.GFFFile.read(data_dir("sequence") / "percent_test.gff3") seqid, source, type, start, end, score, strand, phase, attrib = file[0] assert seqid == "123,456" assert source == "ääh" diff --git a/tests/sequence/test_graphics.py b/tests/sequence/test_graphics.py index 18070be28..21f6d4c81 100644 --- a/tests/sequence/test_graphics.py +++ b/tests/sequence/test_graphics.py @@ -2,8 +2,7 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob -from os.path import abspath, dirname, join +from pathlib import Path import pytest import biotite.sequence as seq import biotite.structure.alphabet as struc_alph @@ -13,9 +12,8 @@ @pytest.mark.skipif(cannot_import("matplotlib"), reason="Matplotlib is not installed") @pytest.mark.parametrize( "scheme_path", - glob.glob( - join(dirname(abspath(seq.__file__)), "graphics", "color_schemes", "*.json") - ), + sorted((Path(seq.__file__).parent / "graphics" / "color_schemes").glob("*.json")), + ids=lambda path: path.name, ) def test_load_color_scheme(scheme_path): from matplotlib.colors import to_rgb diff --git a/tests/sequence/test_phylo.py b/tests/sequence/test_phylo.py index 0943d6002..67d41e287 100644 --- a/tests/sequence/test_phylo.py +++ b/tests/sequence/test_phylo.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import numpy as np import pytest import biotite @@ -16,14 +15,14 @@ def distances(): # "Dendrogram of the BLOSUM62 matrix" # with the small modification M[i,j] += i+j # to reduce ambiguity in the tree construction. - return np.loadtxt(join(data_dir("sequence"), "distances.txt"), dtype=int) + return np.loadtxt(data_dir("sequence") / "distances.txt", dtype=int) @pytest.fixture def upgma_newick(): # Newick notation of the tree created from 'distances.txt', # created via DendroUPGMA - with open(join(data_dir("sequence"), "newick_upgma.txt"), "r") as file: + with open(data_dir("sequence") / "newick_upgma.txt", "r") as file: newick = file.read().strip() return newick @@ -194,7 +193,7 @@ def test_immutability(): @pytest.mark.parametrize( - "newick, labels, error", + ["newick", "labels", "error"], [ # Reference index out of range ("((1,0),4),2);", None, biotite.InvalidFileError), @@ -279,7 +278,7 @@ def test_newick_rounding(): @pytest.mark.parametrize( - "newick_in, exp_newick_out", + ["newick_in", "exp_newick_out"], [ ("(0:1.0, 1:2.0);", "(0:1.0,1:2.0):0.0;"), ("(0:1.0, 1:2.0, 2:3.0);", "((0:1.0,1:2.0):0.0,2:3.0):0.0;"), diff --git a/tests/sequence/test_seqtypes.py b/tests/sequence/test_seqtypes.py index a58e25c3d..d7ed914f5 100644 --- a/tests/sequence/test_seqtypes.py +++ b/tests/sequence/test_seqtypes.py @@ -31,7 +31,7 @@ def test_stop_removal(): @pytest.mark.parametrize( - "dna_str, protein_str", [("CACATAGCATGA", "HIA*"), ("ATGTAGCTA", "M*L")] + ["dna_str", "protein_str"], [("CACATAGCATGA", "HIA*"), ("ATGTAGCTA", "M*L")] ) def test_full_translation(dna_str, protein_str): dna = seq.NucleotideSequence(dna_str) @@ -40,7 +40,7 @@ def test_full_translation(dna_str, protein_str): @pytest.mark.parametrize( - "dna_str, protein_str_list", + ["dna_str", "protein_str_list"], [ ("CA", []), ("GAATGCACTGAGATGCAATAG", ["MH*", "MQ*"]), @@ -79,7 +79,7 @@ def test_letter_conversion(): @pytest.mark.parametrize( - "monoisotopic, expected_mol_weight_protein", + ["monoisotopic", "expected_mol_weight_protein"], # Reference values taken from https://web.expasy.org/compute_pi/ [(True, 2231.06), (False, 2232.56)], ) @@ -100,11 +100,9 @@ def test_positional_sequence(): """ SEQ_LENGTH = 100 - np.random.seed(0) + rng = np.random.default_rng(0) orig_sequence = seq.ProteinSequence() - orig_sequence.code = np.random.randint( - 0, len(orig_sequence.alphabet), size=SEQ_LENGTH - ) + orig_sequence.code = rng.integers(0, len(orig_sequence.alphabet), size=SEQ_LENGTH) positional_sequence = seq.PositionalSequence(orig_sequence) assert str(positional_sequence) == str(orig_sequence) diff --git a/tests/structure/alphabet/test_i3d.py b/tests/structure/alphabet/test_i3d.py index 4bef2a17e..692275386 100644 --- a/tests/structure/alphabet/test_i3d.py +++ b/tests/structure/alphabet/test_i3d.py @@ -1,5 +1,4 @@ import re -from pathlib import Path import numpy as np import pytest import biotite.sequence.io.fasta as fasta @@ -15,7 +14,7 @@ def _get_ref_3di_sequence(pdb_id, chain_id): PDB ID and chain ID. """ ref_3di_file = fasta.FastaFile.read( - Path(data_dir("structure")) / "alphabet" / "i3d.fasta" + data_dir("structure") / "alphabet" / "i3d.fasta" ) for header, seq_string in ref_3di_file.items(): # The first model of a structure is also the first sequence to appear @@ -31,7 +30,9 @@ def _get_ref_3di_sequence(pdb_id, chain_id): @pytest.mark.parametrize( - "path", Path(data_dir("structure")).glob("*.bcif"), ids=lambda path: path.stem + "path", + sorted((data_dir("structure") / "pdb").glob("*.bcif")), + ids=lambda path: path.name, ) def test_to_3di(path): """ @@ -80,7 +81,9 @@ def test_missing_residues(): MAX_MISMATCH_PERCENTAGE = 0.1 UNDEFINED_SYMBOL = strucalph.I3DSequence.undefined_symbol - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / f"{PDB_ID}.bcif") + pdbx_file = pdbx.BinaryCIFFile.read( + data_dir("structure") / "pdb" / f"{PDB_ID}.bcif" + ) atoms = pdbx.get_structure(pdbx_file, model=1) atoms = atoms[struc.filter_amino_acids(atoms)] diff --git a/tests/structure/alphabet/test_pb.py b/tests/structure/alphabet/test_pb.py index 521317cda..7f4a99cc3 100644 --- a/tests/structure/alphabet/test_pb.py +++ b/tests/structure/alphabet/test_pb.py @@ -1,4 +1,3 @@ -from pathlib import Path import numpy as np import pytest import biotite.sequence.io.fasta as fasta @@ -14,7 +13,7 @@ def reference_sequence(): Get the reference Protein Blocks sequence for the alphabet example structure. """ _, seq_string = next( - fasta.FastaFile.read_iter(Path(data_dir("structure")) / "alphabet" / "pb.fasta") + fasta.FastaFile.read_iter(data_dir("structure") / "alphabet" / "pb.fasta") ) return strucalph.ProteinBlocksSequence(seq_string) @@ -22,7 +21,7 @@ def reference_sequence(): @pytest.fixture def reference_chain(): pdbx_file = pdbx.BinaryCIFFile.read( - Path(data_dir("structure")) / "alphabet" / "1ay7.bcif" + data_dir("structure") / "alphabet" / "1ay7.bcif" ) atoms = pdbx.get_structure(pdbx_file, model=1) atoms = atoms[struc.filter_amino_acids(atoms)] diff --git a/tests/structure/data/1aki.bcif b/tests/structure/data/pdb/1aki.bcif similarity index 100% rename from tests/structure/data/1aki.bcif rename to tests/structure/data/pdb/1aki.bcif diff --git a/tests/structure/data/1aki.cif b/tests/structure/data/pdb/1aki.cif similarity index 100% rename from tests/structure/data/1aki.cif rename to tests/structure/data/pdb/1aki.cif diff --git a/tests/structure/data/1aki.gro b/tests/structure/data/pdb/1aki.gro similarity index 100% rename from tests/structure/data/1aki.gro rename to tests/structure/data/pdb/1aki.gro diff --git a/tests/structure/data/1aki.pdb b/tests/structure/data/pdb/1aki.pdb similarity index 100% rename from tests/structure/data/1aki.pdb rename to tests/structure/data/pdb/1aki.pdb diff --git a/tests/structure/data/1crr.bcif b/tests/structure/data/pdb/1crr.bcif similarity index 100% rename from tests/structure/data/1crr.bcif rename to tests/structure/data/pdb/1crr.bcif diff --git a/tests/structure/data/1crr.cif b/tests/structure/data/pdb/1crr.cif similarity index 100% rename from tests/structure/data/1crr.cif rename to tests/structure/data/pdb/1crr.cif diff --git a/tests/structure/data/1crr.gro b/tests/structure/data/pdb/1crr.gro similarity index 100% rename from tests/structure/data/1crr.gro rename to tests/structure/data/pdb/1crr.gro diff --git a/tests/structure/data/1crr.pdb b/tests/structure/data/pdb/1crr.pdb similarity index 100% rename from tests/structure/data/1crr.pdb rename to tests/structure/data/pdb/1crr.pdb diff --git a/tests/structure/data/1dix.bcif b/tests/structure/data/pdb/1dix.bcif similarity index 100% rename from tests/structure/data/1dix.bcif rename to tests/structure/data/pdb/1dix.bcif diff --git a/tests/structure/data/1dix.cif b/tests/structure/data/pdb/1dix.cif similarity index 100% rename from tests/structure/data/1dix.cif rename to tests/structure/data/pdb/1dix.cif diff --git a/tests/structure/data/1dix.gro b/tests/structure/data/pdb/1dix.gro similarity index 100% rename from tests/structure/data/1dix.gro rename to tests/structure/data/pdb/1dix.gro diff --git a/tests/structure/data/1dix.pdb b/tests/structure/data/pdb/1dix.pdb similarity index 100% rename from tests/structure/data/1dix.pdb rename to tests/structure/data/pdb/1dix.pdb diff --git a/tests/structure/data/1f2n.bcif b/tests/structure/data/pdb/1f2n.bcif similarity index 100% rename from tests/structure/data/1f2n.bcif rename to tests/structure/data/pdb/1f2n.bcif diff --git a/tests/structure/data/1f2n.cif b/tests/structure/data/pdb/1f2n.cif similarity index 100% rename from tests/structure/data/1f2n.cif rename to tests/structure/data/pdb/1f2n.cif diff --git a/tests/structure/data/1f2n.gro b/tests/structure/data/pdb/1f2n.gro similarity index 100% rename from tests/structure/data/1f2n.gro rename to tests/structure/data/pdb/1f2n.gro diff --git a/tests/structure/data/1f2n.pdb b/tests/structure/data/pdb/1f2n.pdb similarity index 100% rename from tests/structure/data/1f2n.pdb rename to tests/structure/data/pdb/1f2n.pdb diff --git a/tests/structure/data/1gya.bcif b/tests/structure/data/pdb/1gya.bcif similarity index 100% rename from tests/structure/data/1gya.bcif rename to tests/structure/data/pdb/1gya.bcif diff --git a/tests/structure/data/1gya.cif b/tests/structure/data/pdb/1gya.cif similarity index 100% rename from tests/structure/data/1gya.cif rename to tests/structure/data/pdb/1gya.cif diff --git a/tests/structure/data/1gya.gro b/tests/structure/data/pdb/1gya.gro similarity index 100% rename from tests/structure/data/1gya.gro rename to tests/structure/data/pdb/1gya.gro diff --git a/tests/structure/data/1gya.pdb b/tests/structure/data/pdb/1gya.pdb similarity index 100% rename from tests/structure/data/1gya.pdb rename to tests/structure/data/pdb/1gya.pdb diff --git a/tests/structure/data/1igy.bcif b/tests/structure/data/pdb/1igy.bcif similarity index 100% rename from tests/structure/data/1igy.bcif rename to tests/structure/data/pdb/1igy.bcif diff --git a/tests/structure/data/1igy.cif b/tests/structure/data/pdb/1igy.cif similarity index 100% rename from tests/structure/data/1igy.cif rename to tests/structure/data/pdb/1igy.cif diff --git a/tests/structure/data/1igy.gro b/tests/structure/data/pdb/1igy.gro similarity index 100% rename from tests/structure/data/1igy.gro rename to tests/structure/data/pdb/1igy.gro diff --git a/tests/structure/data/1igy.pdb b/tests/structure/data/pdb/1igy.pdb similarity index 100% rename from tests/structure/data/1igy.pdb rename to tests/structure/data/pdb/1igy.pdb diff --git a/tests/structure/data/1k6p.bcif b/tests/structure/data/pdb/1k6p.bcif similarity index 100% rename from tests/structure/data/1k6p.bcif rename to tests/structure/data/pdb/1k6p.bcif diff --git a/tests/structure/data/1k6p.cif b/tests/structure/data/pdb/1k6p.cif similarity index 100% rename from tests/structure/data/1k6p.cif rename to tests/structure/data/pdb/1k6p.cif diff --git a/tests/structure/data/1k6p.pdb b/tests/structure/data/pdb/1k6p.pdb similarity index 100% rename from tests/structure/data/1k6p.pdb rename to tests/structure/data/pdb/1k6p.pdb diff --git a/tests/structure/data/1l2y.bcif b/tests/structure/data/pdb/1l2y.bcif similarity index 100% rename from tests/structure/data/1l2y.bcif rename to tests/structure/data/pdb/1l2y.bcif diff --git a/tests/structure/data/1l2y.cif b/tests/structure/data/pdb/1l2y.cif similarity index 100% rename from tests/structure/data/1l2y.cif rename to tests/structure/data/pdb/1l2y.cif diff --git a/tests/structure/data/1l2y.dcd b/tests/structure/data/pdb/1l2y.dcd similarity index 100% rename from tests/structure/data/1l2y.dcd rename to tests/structure/data/pdb/1l2y.dcd diff --git a/tests/structure/data/1l2y.gro b/tests/structure/data/pdb/1l2y.gro similarity index 100% rename from tests/structure/data/1l2y.gro rename to tests/structure/data/pdb/1l2y.gro diff --git a/tests/structure/data/1l2y.netcdf b/tests/structure/data/pdb/1l2y.netcdf similarity index 100% rename from tests/structure/data/1l2y.netcdf rename to tests/structure/data/pdb/1l2y.netcdf diff --git a/tests/structure/data/1l2y.pdb b/tests/structure/data/pdb/1l2y.pdb similarity index 100% rename from tests/structure/data/1l2y.pdb rename to tests/structure/data/pdb/1l2y.pdb diff --git a/tests/structure/data/1l2y.trr b/tests/structure/data/pdb/1l2y.trr similarity index 100% rename from tests/structure/data/1l2y.trr rename to tests/structure/data/pdb/1l2y.trr diff --git a/tests/structure/data/1l2y.xtc b/tests/structure/data/pdb/1l2y.xtc similarity index 100% rename from tests/structure/data/1l2y.xtc rename to tests/structure/data/pdb/1l2y.xtc diff --git a/tests/structure/data/1ncb.bcif b/tests/structure/data/pdb/1ncb.bcif similarity index 100% rename from tests/structure/data/1ncb.bcif rename to tests/structure/data/pdb/1ncb.bcif diff --git a/tests/structure/data/1ncb.cif b/tests/structure/data/pdb/1ncb.cif similarity index 100% rename from tests/structure/data/1ncb.cif rename to tests/structure/data/pdb/1ncb.cif diff --git a/tests/structure/data/1o1z.bcif b/tests/structure/data/pdb/1o1z.bcif similarity index 100% rename from tests/structure/data/1o1z.bcif rename to tests/structure/data/pdb/1o1z.bcif diff --git a/tests/structure/data/1o1z.cif b/tests/structure/data/pdb/1o1z.cif similarity index 100% rename from tests/structure/data/1o1z.cif rename to tests/structure/data/pdb/1o1z.cif diff --git a/tests/structure/data/1o1z.gro b/tests/structure/data/pdb/1o1z.gro similarity index 100% rename from tests/structure/data/1o1z.gro rename to tests/structure/data/pdb/1o1z.gro diff --git a/tests/structure/data/1o1z.pdb b/tests/structure/data/pdb/1o1z.pdb similarity index 100% rename from tests/structure/data/1o1z.pdb rename to tests/structure/data/pdb/1o1z.pdb diff --git a/tests/structure/data/2axd.bcif b/tests/structure/data/pdb/2axd.bcif similarity index 100% rename from tests/structure/data/2axd.bcif rename to tests/structure/data/pdb/2axd.bcif diff --git a/tests/structure/data/2axd.cif b/tests/structure/data/pdb/2axd.cif similarity index 100% rename from tests/structure/data/2axd.cif rename to tests/structure/data/pdb/2axd.cif diff --git a/tests/structure/data/2axd.pdb b/tests/structure/data/pdb/2axd.pdb similarity index 100% rename from tests/structure/data/2axd.pdb rename to tests/structure/data/pdb/2axd.pdb diff --git a/tests/structure/data/2d0f.bcif b/tests/structure/data/pdb/2d0f.bcif similarity index 100% rename from tests/structure/data/2d0f.bcif rename to tests/structure/data/pdb/2d0f.bcif diff --git a/tests/structure/data/2d0f.cif b/tests/structure/data/pdb/2d0f.cif similarity index 100% rename from tests/structure/data/2d0f.cif rename to tests/structure/data/pdb/2d0f.cif diff --git a/tests/structure/data/2d0f.pdb b/tests/structure/data/pdb/2d0f.pdb similarity index 100% rename from tests/structure/data/2d0f.pdb rename to tests/structure/data/pdb/2d0f.pdb diff --git a/tests/structure/data/3o5r.bcif b/tests/structure/data/pdb/3o5r.bcif similarity index 100% rename from tests/structure/data/3o5r.bcif rename to tests/structure/data/pdb/3o5r.bcif diff --git a/tests/structure/data/3o5r.cif b/tests/structure/data/pdb/3o5r.cif similarity index 100% rename from tests/structure/data/3o5r.cif rename to tests/structure/data/pdb/3o5r.cif diff --git a/tests/structure/data/3o5r.gro b/tests/structure/data/pdb/3o5r.gro similarity index 100% rename from tests/structure/data/3o5r.gro rename to tests/structure/data/pdb/3o5r.gro diff --git a/tests/structure/data/3o5r.pdb b/tests/structure/data/pdb/3o5r.pdb similarity index 100% rename from tests/structure/data/3o5r.pdb rename to tests/structure/data/pdb/3o5r.pdb diff --git a/tests/structure/data/3wip.bcif b/tests/structure/data/pdb/3wip.bcif similarity index 100% rename from tests/structure/data/3wip.bcif rename to tests/structure/data/pdb/3wip.bcif diff --git a/tests/structure/data/3wip.cif b/tests/structure/data/pdb/3wip.cif similarity index 100% rename from tests/structure/data/3wip.cif rename to tests/structure/data/pdb/3wip.cif diff --git a/tests/structure/data/3wip.pdb b/tests/structure/data/pdb/3wip.pdb similarity index 100% rename from tests/structure/data/3wip.pdb rename to tests/structure/data/pdb/3wip.pdb diff --git a/tests/structure/data/4gxy.bcif b/tests/structure/data/pdb/4gxy.bcif similarity index 100% rename from tests/structure/data/4gxy.bcif rename to tests/structure/data/pdb/4gxy.bcif diff --git a/tests/structure/data/4gxy.cif b/tests/structure/data/pdb/4gxy.cif similarity index 100% rename from tests/structure/data/4gxy.cif rename to tests/structure/data/pdb/4gxy.cif diff --git a/tests/structure/data/4gxy.gro b/tests/structure/data/pdb/4gxy.gro similarity index 100% rename from tests/structure/data/4gxy.gro rename to tests/structure/data/pdb/4gxy.gro diff --git a/tests/structure/data/4gxy.pdb b/tests/structure/data/pdb/4gxy.pdb similarity index 100% rename from tests/structure/data/4gxy.pdb rename to tests/structure/data/pdb/4gxy.pdb diff --git a/tests/structure/data/4i39.bcif b/tests/structure/data/pdb/4i39.bcif similarity index 100% rename from tests/structure/data/4i39.bcif rename to tests/structure/data/pdb/4i39.bcif diff --git a/tests/structure/data/4i39.cif b/tests/structure/data/pdb/4i39.cif similarity index 100% rename from tests/structure/data/4i39.cif rename to tests/structure/data/pdb/4i39.cif diff --git a/tests/structure/data/4p5j.bcif b/tests/structure/data/pdb/4p5j.bcif similarity index 100% rename from tests/structure/data/4p5j.bcif rename to tests/structure/data/pdb/4p5j.bcif diff --git a/tests/structure/data/4p5j.cif b/tests/structure/data/pdb/4p5j.cif similarity index 100% rename from tests/structure/data/4p5j.cif rename to tests/structure/data/pdb/4p5j.cif diff --git a/tests/structure/data/4p5j.gro b/tests/structure/data/pdb/4p5j.gro similarity index 100% rename from tests/structure/data/4p5j.gro rename to tests/structure/data/pdb/4p5j.gro diff --git a/tests/structure/data/4p5j.pdb b/tests/structure/data/pdb/4p5j.pdb similarity index 100% rename from tests/structure/data/4p5j.pdb rename to tests/structure/data/pdb/4p5j.pdb diff --git a/tests/structure/data/4zxb.bcif b/tests/structure/data/pdb/4zxb.bcif similarity index 100% rename from tests/structure/data/4zxb.bcif rename to tests/structure/data/pdb/4zxb.bcif diff --git a/tests/structure/data/4zxb.cif b/tests/structure/data/pdb/4zxb.cif similarity index 100% rename from tests/structure/data/4zxb.cif rename to tests/structure/data/pdb/4zxb.cif diff --git a/tests/structure/data/5eil.bcif b/tests/structure/data/pdb/5eil.bcif similarity index 100% rename from tests/structure/data/5eil.bcif rename to tests/structure/data/pdb/5eil.bcif diff --git a/tests/structure/data/5eil.cif b/tests/structure/data/pdb/5eil.cif similarity index 100% rename from tests/structure/data/5eil.cif rename to tests/structure/data/pdb/5eil.cif diff --git a/tests/structure/data/5eil.pdb b/tests/structure/data/pdb/5eil.pdb similarity index 100% rename from tests/structure/data/5eil.pdb rename to tests/structure/data/pdb/5eil.pdb diff --git a/tests/structure/data/5h73.bcif b/tests/structure/data/pdb/5h73.bcif similarity index 100% rename from tests/structure/data/5h73.bcif rename to tests/structure/data/pdb/5h73.bcif diff --git a/tests/structure/data/5h73.cif b/tests/structure/data/pdb/5h73.cif similarity index 100% rename from tests/structure/data/5h73.cif rename to tests/structure/data/pdb/5h73.cif diff --git a/tests/structure/data/5h73.gro b/tests/structure/data/pdb/5h73.gro similarity index 100% rename from tests/structure/data/5h73.gro rename to tests/structure/data/pdb/5h73.gro diff --git a/tests/structure/data/5h73.pdb b/tests/structure/data/pdb/5h73.pdb similarity index 100% rename from tests/structure/data/5h73.pdb rename to tests/structure/data/pdb/5h73.pdb diff --git a/tests/structure/data/5ugo.bcif b/tests/structure/data/pdb/5ugo.bcif similarity index 100% rename from tests/structure/data/5ugo.bcif rename to tests/structure/data/pdb/5ugo.bcif diff --git a/tests/structure/data/5ugo.cif b/tests/structure/data/pdb/5ugo.cif similarity index 100% rename from tests/structure/data/5ugo.cif rename to tests/structure/data/pdb/5ugo.cif diff --git a/tests/structure/data/5ugo.gro b/tests/structure/data/pdb/5ugo.gro similarity index 100% rename from tests/structure/data/5ugo.gro rename to tests/structure/data/pdb/5ugo.gro diff --git a/tests/structure/data/5ugo.pdb b/tests/structure/data/pdb/5ugo.pdb similarity index 100% rename from tests/structure/data/5ugo.pdb rename to tests/structure/data/pdb/5ugo.pdb diff --git a/tests/structure/data/5zng.bcif b/tests/structure/data/pdb/5zng.bcif similarity index 100% rename from tests/structure/data/5zng.bcif rename to tests/structure/data/pdb/5zng.bcif diff --git a/tests/structure/data/5zng.cif b/tests/structure/data/pdb/5zng.cif similarity index 100% rename from tests/structure/data/5zng.cif rename to tests/structure/data/pdb/5zng.cif diff --git a/tests/structure/data/5zng.pdb b/tests/structure/data/pdb/5zng.pdb similarity index 100% rename from tests/structure/data/5zng.pdb rename to tests/structure/data/pdb/5zng.pdb diff --git a/tests/structure/data/7gsa.bcif b/tests/structure/data/pdb/7gsa.bcif similarity index 100% rename from tests/structure/data/7gsa.bcif rename to tests/structure/data/pdb/7gsa.bcif diff --git a/tests/structure/data/7gsa.cif b/tests/structure/data/pdb/7gsa.cif similarity index 100% rename from tests/structure/data/7gsa.cif rename to tests/structure/data/pdb/7gsa.cif diff --git a/tests/structure/data/README.rst b/tests/structure/data/pdb/README.rst similarity index 100% rename from tests/structure/data/README.rst rename to tests/structure/data/pdb/README.rst diff --git a/tests/structure/data/create_test_structures.py b/tests/structure/data/pdb/create_test_structures.py similarity index 80% rename from tests/structure/data/create_test_structures.py rename to tests/structure/data/pdb/create_test_structures.py index 249e55e62..b2a1d0d22 100644 --- a/tests/structure/data/create_test_structures.py +++ b/tests/structure/data/pdb/create_test_structures.py @@ -2,9 +2,10 @@ import shutil import subprocess from pathlib import Path +from tempfile import NamedTemporaryFile import biotite import biotite.database.rcsb as rcsb -import biotite.structure.io as strucio +import biotite.structure.io.pdb as pdb import biotite.structure.io.pdbx as pdbx from biotite.database import RequestError @@ -20,7 +21,7 @@ def create(pdb_id, directory, include_gro): # Create *.gro files using GROMACS if include_gro: try: - pdbx_file = pdbx.BinaryCIFFile.read(directory / pdb_id + ".bcif") + pdbx_file = pdbx.BinaryCIFFile.read(directory / f"{pdb_id}.bcif") atoms = pdbx.get_structure(pdbx_file) except biotite.InvalidFileError: # Structure probably contains multiple models with different @@ -29,20 +30,24 @@ def create(pdb_id, directory, include_gro): # -> Skip writing GRO file return # Clean PDB file -> remove inscodes and altlocs - cleaned_file_name = biotite.temp_file("pdb") - strucio.save_structure(cleaned_file_name, atoms) + cleaned_file_name = NamedTemporaryFile("w", suffix=".pdb", delete=False) + pdb_file = pdb.PDBFile() + pdb_file.set_structure(atoms) + pdb_file.write(cleaned_file_name.name) + cleaned_file_name.close() # Run GROMACS for file conversion subprocess.run( [ "gmxeditconf", "-f", - cleaned_file_name, + cleaned_file_name.name, "-o", - str(directory / pdb_id + ".gro"), + str(directory / f"{pdb_id}.gro"), ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) + Path(cleaned_file_name.name).unlink() if __name__ == "__main__": @@ -75,6 +80,7 @@ def create(pdb_id, directory, include_gro): args = parser.parse_args() if __name__ == "__main__": + # This script resides in the same 'pdb' directory as the created files data_dir = Path(__file__).parent include_gro = shutil.which("gmx") is not None with open(data_dir / "ids.txt", "r") as file: diff --git a/tests/structure/data/ids.txt b/tests/structure/data/pdb/ids.txt similarity index 100% rename from tests/structure/data/ids.txt rename to tests/structure/data/pdb/ids.txt diff --git a/tests/structure/io/test_gro.py b/tests/structure/io/test_gro.py index 1794ca2a4..e5fcbf7f5 100644 --- a/tests/structure/io/test_gro.py +++ b/tests/structure/io/test_gro.py @@ -2,13 +2,9 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob -import itertools -from os.path import join, splitext from tempfile import TemporaryFile import numpy as np import pytest -from pytest import approx import biotite import biotite.structure.io.gro as gro import biotite.structure.io.pdb as pdb @@ -17,16 +13,18 @@ def test_get_model_count(): - gro_file = gro.GROFile.read(join(data_dir("structure"), "1l2y.gro")) + gro_file = gro.GROFile.read(data_dir("structure") / "pdb" / "1l2y.gro") test_model_count = gro_file.get_model_count() ref_model_count = gro_file.get_structure().stack_depth() assert test_model_count == ref_model_count @pytest.mark.parametrize( - "path, model", - itertools.product(glob.glob(join(data_dir("structure"), "*.gro")), [None, 1, -1]), + "path", + sorted((data_dir("structure") / "pdb").glob("*.gro")), + ids=lambda path: path.name, ) +@pytest.mark.parametrize("model", [None, 1, -1]) def test_array_conversion(path, model): gro_file = gro.GROFile.read(path) array1 = gro_file.get_structure(model=model) @@ -45,10 +43,12 @@ def test_array_conversion(path, model): @pytest.mark.parametrize( - "path", glob.glob(join(data_dir("structure"), "[!(waterbox)]*.gro")) + "path", + sorted((data_dir("structure") / "pdb").glob("*.gro")), + ids=lambda path: path.name, ) def test_pdb_consistency(path): - pdb_path = splitext(path)[0] + ".pdb" + pdb_path = path.with_suffix(".pdb") pdb_file = pdb.PDBFile.read(pdb_path) a1 = pdb_file.get_structure(model=1) gro_file = gro.GROFile.read(path) @@ -62,13 +62,17 @@ def test_pdb_consistency(path): ) # Mind rounding errors when converting pdb to gro (A -> nm) - assert a1.coord.flatten().tolist() == approx(a2.coord.flatten().tolist(), abs=1e-2) + assert a1.coord.flatten().tolist() == pytest.approx( + a2.coord.flatten().tolist(), abs=1e-2 + ) @pytest.mark.parametrize( - "path, model", - itertools.product(glob.glob(join(data_dir("structure"), "*.pdb")), [None, 1, -1]), + "path", + sorted((data_dir("structure") / "pdb").glob("*.pdb")), + ids=lambda path: path.name, ) +@pytest.mark.parametrize("model", [None, 1, -1]) def test_pdb_to_gro(path, model): """ Converting stacks between formats should not change data @@ -106,7 +110,9 @@ def test_pdb_to_gro(path, model): ) # Mind rounding errors when converting pdb to gro (A -> nm) - assert a1.coord.flatten().tolist() == approx(a2.coord.flatten().tolist(), abs=1e-2) + assert a1.coord.flatten().tolist() == pytest.approx( + a2.coord.flatten().tolist(), abs=1e-2 + ) def test_gro_id_overflow(): @@ -167,7 +173,7 @@ def test_multiple_atom_array(): Setting the structure with an :class:`AtomArrayStack` and with an equivalent list of :class:`AtomArray` objects should result in the same file. """ - stack = gro.GROFile.read(join(data_dir("structure"), "1l2y.gro")).get_structure() + stack = gro.GROFile.read(data_dir("structure") / "pdb" / "1l2y.gro").get_structure() ref_file = gro.GROFile() ref_file.set_structure(stack) diff --git a/tests/structure/io/test_mol.py b/tests/structure/io/test_mol.py index ba3b66558..f1ac9e0d5 100644 --- a/tests/structure/io/test_mol.py +++ b/tests/structure/io/test_mol.py @@ -3,9 +3,6 @@ # information. import datetime -import glob -import itertools -from os.path import join, splitext from tempfile import TemporaryFile import numpy as np import pytest @@ -21,17 +18,17 @@ def list_v2000_sdf_files(): return [ path - for path in glob.glob(join(data_dir("structure"), "molecules", "*.sdf")) - if "v3000" not in path + for path in sorted((data_dir("structure") / "molecules").glob("*.sdf")) + if "v3000" not in path.name ] def list_v3000_sdf_files(): - return glob.glob(join(data_dir("structure"), "molecules", "*v3000.sdf")) + return sorted((data_dir("structure") / "molecules").glob("*v3000.sdf")) def list_cif_files(): - return glob.glob(join(data_dir("structure"), "molecules", "*.cif")) + return sorted((data_dir("structure") / "molecules").glob("*.cif")) def toy_atom_array(n_atoms): @@ -74,16 +71,11 @@ def test_header_conversion(): assert test_header == ref_header -@pytest.mark.parametrize( - "FileClass, path, version, omit_charge, use_charge_property", - itertools.product( - [mol.MOLFile, mol.SDFile], - list_v2000_sdf_files(), - ["V2000", "V3000"], - [False, True], - [False, True], - ), -) +@pytest.mark.parametrize("FileClass", [mol.MOLFile, mol.SDFile]) +@pytest.mark.parametrize("path", list_v2000_sdf_files(), ids=lambda path: path.name) +@pytest.mark.parametrize("version", ["V2000", "V3000"]) +@pytest.mark.parametrize("omit_charge", [False, True]) +@pytest.mark.parametrize("use_charge_property", [False, True]) def test_structure_conversion_from_file( FileClass, # noqa: N803 path, @@ -126,21 +118,19 @@ def test_structure_conversion_from_file( assert test_atoms == ref_atoms +@pytest.mark.parametrize("FileClass", [mol.MOLFile, mol.SDFile]) @pytest.mark.parametrize( - "FileClass, component_name, version, omit_charge, use_charge_property", - itertools.product( - [mol.MOLFile, mol.SDFile], - [ - "ALA", # Alanine - "BNZ", # Benzene (has aromatic bonds) - "3P8", # Methylammonium ion (has charge) - "MCH", # Trichloromethane (has element with multiple letters) - ], - ["V2000", "V3000"], - [False, True], - [False, True], - ), + "component_name", + [ + "ALA", # Alanine + "BNZ", # Benzene (has aromatic bonds) + "3P8", # Methylammonium ion (has charge) + "MCH", # Trichloromethane (has element with multiple letters) + ], ) +@pytest.mark.parametrize("version", ["V2000", "V3000"]) +@pytest.mark.parametrize("omit_charge", [False, True]) +@pytest.mark.parametrize("use_charge_property", [False, True]) def test_structure_conversion_to_file( FileClass, # noqa: N803 component_name, @@ -183,8 +173,11 @@ def test_structure_conversion_to_file( [ file for file in list_v2000_sdf_files() + list_v3000_sdf_files() - if file.split(".")[0] + ".cif" in list_cif_files() + # The base name is the part before the first '.', i.e. without the + # '.sdf' and optional '.v3000' suffix + if file.with_name(file.name.split(".")[0] + ".cif") in list_cif_files() ], + ids=lambda path: path.name, ) def test_pdbx_consistency(path): """ @@ -192,7 +185,7 @@ def test_pdbx_consistency(path): structure read in PDBx format. """ # Remove '.sdf' and optional '.v3000' suffix - cif_path = splitext(splitext(path)[0])[0] + ".cif" + cif_path = path.with_name(path.name.split(".")[0] + ".cif") pdbx_file = pdbx.CIFFile.read(cif_path) ref_atoms = pdbx.get_component(pdbx_file) @@ -213,8 +206,9 @@ def test_pdbx_consistency(path): @pytest.mark.parametrize( - "v2000_path, v3000_path", + ["v2000_path", "v3000_path"], zip(sorted(list_v2000_sdf_files()), sorted(list_v3000_sdf_files())), + ids=lambda path: path.name, ) def test_version_consistency(v2000_path, v3000_path): """ @@ -238,7 +232,7 @@ def test_multi_record_files(): ref_atom_arrays = [ mol.SDFile.read( - join(data_dir("structure"), "molecules", f"{res_name}.sdf") + data_dir("structure") / "molecules" / f"{res_name}.sdf" ).record.get_structure() for res_name in RES_NAMES ] @@ -263,7 +257,7 @@ def test_metadata_parsing(): """ Check if metadata is parsed correctly based on a known example. """ - sdf_file = mol.SDFile.read(join(data_dir("structure"), "molecules", "13136.sdf")) + sdf_file = mol.SDFile.read(data_dir("structure") / "molecules" / "13136.sdf") metadata = sdf_file.record.metadata assert metadata["PUBCHEM_COMPOUND_CID"] == "13136" @@ -291,7 +285,7 @@ def test_metadata_conversion(): @pytest.mark.parametrize( - "key_string, ref_key_attributes", + ["key_string", "ref_key_attributes"], [ # Cases from Dalby1992 ("> ", (None, "MELTING.POINT", None, None)), @@ -316,7 +310,7 @@ def test_metadata_key_parsing(key_string, ref_key_attributes): assert test_key == ref_key -@pytest.mark.parametrize("path", list_v2000_sdf_files()) +@pytest.mark.parametrize("path", list_v2000_sdf_files(), ids=lambda path: path.name) def test_structure_bond_type_fallback(path): """ Check if a bond with a type not supported by MOL files will be translated diff --git a/tests/structure/io/test_pdb.py b/tests/structure/io/test_pdb.py index 5c5e91bdd..b306a349a 100644 --- a/tests/structure/io/test_pdb.py +++ b/tests/structure/io/test_pdb.py @@ -2,16 +2,11 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob -import itertools import sys import warnings -from os.path import join, splitext -from pathlib import Path from tempfile import TemporaryFile import numpy as np import pytest -from pytest import approx import biotite import biotite.structure as struc import biotite.structure.io.pdb as pdb @@ -25,7 +20,7 @@ def test_get_model_count(): - pdb_file = pdb.PDBFile.read(join(data_dir("structure"), "1l2y.pdb")) + pdb_file = pdb.PDBFile.read(data_dir("structure") / "pdb" / "1l2y.pdb") # Test also the thin wrapper around the method # 'get_model_count()' test_model_count = pdb.get_model_count(pdb_file) @@ -34,14 +29,13 @@ def test_get_model_count(): @pytest.mark.parametrize( - "path, model, hybrid36, include_bonds", - itertools.product( - glob.glob(join(data_dir("structure"), "*.pdb")), - [None, 1, -1], - [False, True], - [False, True], - ), + "path", + sorted((data_dir("structure") / "pdb").glob("*.pdb")), + ids=lambda path: path.name, ) +@pytest.mark.parametrize("model", [None, 1, -1]) +@pytest.mark.parametrize("hybrid36", [False, True]) +@pytest.mark.parametrize("include_bonds", [False, True]) def test_array_conversion(path, model, hybrid36, include_bonds): pdb_file = pdb.PDBFile.read(path) # Test also the thin wrapper around the methods @@ -84,7 +78,8 @@ def test_array_conversion(path, model, hybrid36, include_bonds): @pytest.mark.parametrize( "path", - glob.glob(join(data_dir("structure"), "*.pdb")), + sorted((data_dir("structure") / "pdb").glob("*.pdb")), + ids=lambda path: path.name, ) def test_space_group(path): """ @@ -124,11 +119,11 @@ def test_space_group(path): @pytest.mark.parametrize("model", [None, 1, -1]) @pytest.mark.parametrize( "path", - Path(data_dir("structure")).glob("*.pdb"), - ids=lambda p: p.stem, + sorted((data_dir("structure") / "pdb").glob("*.pdb")), + ids=lambda path: path.name, ) def test_pdbx_consistency(model, path): - bcif_path = splitext(path)[0] + ".bcif" + bcif_path = path.with_suffix(".bcif") pdbx_file = pdbx.BinaryCIFFile.read(bcif_path) try: ref_atoms = pdbx.get_structure(pdbx_file, model=model, include_bonds=True) @@ -181,9 +176,11 @@ def test_pdbx_consistency(model, path): @pytest.mark.parametrize( - "path, model", - itertools.product(glob.glob(join(data_dir("structure"), "*.pdb")), [None, 1]), + "path", + sorted((data_dir("structure") / "pdb").glob("*.pdb")), + ids=lambda path: path.name, ) +@pytest.mark.parametrize("model", [None, 1]) def test_pdbx_consistency_assembly(path, model): """ Check whether :func:`get_assembly()` gives the same result for the @@ -201,7 +198,7 @@ def test_pdbx_consistency_assembly(path, model): else: raise - bcif_path = splitext(path)[0] + ".bcif" + bcif_path = path.with_suffix(".bcif") pdbx_file = pdbx.BinaryCIFFile.read(bcif_path) ref_assembly = pdbx.get_assembly(pdbx_file, model=model) @@ -210,14 +207,14 @@ def test_pdbx_consistency_assembly(path, model): test_assembly.get_annotation(category).tolist() == ref_assembly.get_annotation(category).tolist() ) - assert test_assembly.coord.flatten().tolist() == approx( + assert test_assembly.coord.flatten().tolist() == pytest.approx( ref_assembly.coord.flatten().tolist(), abs=1e-3 ) @pytest.mark.parametrize("hybrid36", [False, True]) def test_extra_fields(hybrid36): - path = join(data_dir("structure"), "1l2y.pdb") + path = data_dir("structure") / "pdb" / "1l2y.pdb" pdb_file = pdb.PDBFile.read(path) stack1 = pdb_file.get_structure( extra_fields=["atom_id", "b_factor", "occupancy", "charge"] @@ -240,8 +237,8 @@ def test_extra_fields(hybrid36): assert stack1.ins_code.tolist() == stack2.ins_code.tolist() assert stack1.atom_id.tolist() == stack2.atom_id.tolist() - assert stack1.b_factor.tolist() == approx(stack2.b_factor.tolist()) - assert stack1.occupancy.tolist() == approx(stack2.occupancy.tolist()) + assert stack1.b_factor.tolist() == pytest.approx(stack2.b_factor.tolist()) + assert stack1.occupancy.tolist() == pytest.approx(stack2.occupancy.tolist()) assert stack1.charge.tolist() == stack2.charge.tolist() assert stack1 == stack2 @@ -249,7 +246,7 @@ def test_extra_fields(hybrid36): @pytest.mark.filterwarnings("ignore") def test_inferred_elements(): # Read valid pdb file - path = join(data_dir("structure"), "1l2y.pdb") + path = data_dir("structure") / "pdb" / "1l2y.pdb" pdb_file = pdb.PDBFile.read(path) stack = pdb_file.get_structure() @@ -273,9 +270,11 @@ def test_inferred_elements(): @pytest.mark.parametrize( - "path, model", - itertools.product(glob.glob(join(data_dir("structure"), "*.pdb")), [None, 1, -1]), + "path", + sorted((data_dir("structure") / "pdb").glob("*.pdb")), + ids=lambda path: path.name, ) +@pytest.mark.parametrize("model", [None, 1, -1]) def test_box_shape(path, model): pdb_file = pdb.PDBFile.read(path) try: @@ -297,14 +296,16 @@ def test_box_shape(path, model): def test_box_parsing(): - path = join(data_dir("structure"), "1igy.pdb") + path = data_dir("structure") / "pdb" / "1igy.pdb" pdb_file = pdb.PDBFile.read(path) a = pdb_file.get_structure() expected_box = np.array( [[[66.65, 0.00, 0.00], [0.00, 190.66, 0.00], [-24.59, 0.00, 68.84]]] ) - assert expected_box.flatten().tolist() == approx(a.box.flatten().tolist(), abs=1e-2) + assert expected_box.flatten().tolist() == pytest.approx( + a.box.flatten().tolist(), abs=1e-2 + ) def test_id_overflow(): @@ -363,7 +364,7 @@ def test_id_overflow(): def test_get_coord(model): # Choose a structure without inscodes and altlocs # to avoid atom filtering in reference atom array (stack) - path = join(data_dir("structure"), "1l2y.pdb") + path = data_dir("structure") / "pdb" / "1l2y.pdb" pdb_file = pdb.PDBFile.read(path) ref_coord = pdb_file.get_structure(model=model).coord @@ -378,7 +379,7 @@ def test_get_coord(model): def test_get_b_factor(model): # Choose a structure without inscodes and altlocs # to avoid atom filtering in reference atom array (stack) - path = join(data_dir("structure"), "1l2y.pdb") + path = data_dir("structure") / "pdb" / "1l2y.pdb" pdb_file = pdb.PDBFile.read(path) if model is None: @@ -401,24 +402,18 @@ def test_get_b_factor(model): assert (test_b_factor == ref_b_factor).all() -np.random.seed(0) N = 200 LENGTHS = [3, 4, 5] +_rng = np.random.default_rng(0) @pytest.mark.parametrize( - "number, length", - zip( - list( - itertools.chain( - *[ - np.random.randint(0, max_hybrid36_number(length), N) - for length in LENGTHS - ] - ) - ), - list(itertools.chain(*[[length] * N for length in LENGTHS])), - ), + ["number", "length"], + [ + (number, length) + for length in LENGTHS + for number in _rng.integers(0, max_hybrid36_number(length), N) + ], ) def test_hybrid36_codec(number, length): """ @@ -451,9 +446,8 @@ def test_bond_records(hybrid36): # Omit time consuming element guessing atoms.element[:] = "NA" - np.random.seed(0) # Create random bonds four times the number of atoms - bond_array = np.random.randint(n_atoms, size=(4 * n_atoms, 2)) + bond_array = np.random.default_rng(0).integers(n_atoms, size=(4 * n_atoms, 2)) # Remove bonds of atoms to themselves bond_array = bond_array[bond_array[:, 0] != bond_array[:, 1]] ref_bonds = struc.BondList(n_atoms, bond_array) @@ -473,7 +467,7 @@ def test_bond_parsing(): :func:`connect_via_residue_names()`. """ # Choose a structure with CONECT records to test these as well - path = join(data_dir("structure"), "3o5r.pdb") + path = data_dir("structure") / "pdb" / "3o5r.pdb" pdb_file = pdb.PDBFile.read(path) atoms = pdb.get_structure(pdb_file, model=1, include_bonds=True) @@ -494,7 +488,7 @@ def test_get_unit_cell(model): INVERSION_AXES = [(0, 0, 0), (0, 0, 1), (0, 1, 0), (1, 0, 0)] TRANSLATION_AXES = [(0, 0, 0), (1, 0, 1), (0, 1, 1), (1, 1, 0)] - path = join(data_dir("structure"), "1aki.pdb") + path = data_dir("structure") / "pdb" / "1aki.pdb" pdb_file = pdb.PDBFile.read(path) original_structure = pdb_file.get_structure(model=model) if model is None: @@ -525,13 +519,13 @@ def test_get_unit_cell(model): chain.get_annotation(category).tolist() == original_structure.get_annotation(category).tolist() ) - assert chain.coord.flatten().tolist() == approx( + assert chain.coord.flatten().tolist() == pytest.approx( original_structure.coord.flatten().tolist(), abs=1e-3 ) @pytest.mark.parametrize( - "annotation, value, warning_only", + ["annotation", "value", "warning_only"], [ ("coord", -1000, False), ("coord", 10000, False), @@ -596,7 +590,7 @@ def test_hetatm_intra_residue_bonds(): ], dtype=np.uint32, ) - path = join(data_dir("structure"), "edge_cases", "hetatm.pdb") + path = data_dir("structure") / "edge_cases" / "hetatm.pdb" pdb_file = pdb.PDBFile.read(path) structure = pdb.get_structure(pdb_file, model=1, include_bonds=True) @@ -610,7 +604,7 @@ def test_multiple_atom_array(): Setting the structure with an :class:`AtomArrayStack` and with an equivalent list of :class:`AtomArray` objects should result in the same file. """ - stack = pdb.PDBFile.read(join(data_dir("structure"), "1l2y.pdb")).get_structure() + stack = pdb.PDBFile.read(data_dir("structure") / "pdb" / "1l2y.pdb").get_structure() ref_file = pdb.PDBFile() ref_file.set_structure(stack) diff --git a/tests/structure/io/test_pdbqt.py b/tests/structure/io/test_pdbqt.py index 2f18b496c..cc8b4db85 100644 --- a/tests/structure/io/test_pdbqt.py +++ b/tests/structure/io/test_pdbqt.py @@ -2,9 +2,7 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob import warnings -from os.path import join from tempfile import TemporaryFile import numpy as np import pytest @@ -18,10 +16,11 @@ "path", [ path - for path in glob.glob(join(data_dir("structure"), "*.bcif")) + for path in sorted((data_dir("structure") / "pdb").glob("*.bcif")) # Skip this PDB ID as it contains 5-character residue names - if "7gsa" not in path + if "7gsa" not in path.name ], + ids=lambda path: path.name, ) def test_array_conversion(path): pdbx_file = pdbx.BinaryCIFFile.read(path) diff --git a/tests/structure/io/test_pdbx.py b/tests/structure/io/test_pdbx.py index c68f00eb6..e834a8ab2 100644 --- a/tests/structure/io/test_pdbx.py +++ b/tests/structure/io/test_pdbx.py @@ -2,16 +2,11 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob -import itertools import warnings from io import BytesIO -from os.path import join, splitext -from pathlib import Path import msgpack import numpy as np import pytest -from pytest import approx import biotite import biotite.sequence as seq import biotite.structure as struc @@ -28,34 +23,32 @@ def test_get_model_count(format): Check of :func:`get_model_count()`gives the same number of models as :func:`get_structure()`. """ - base_path = join(data_dir("structure"), "1l2y") + base_path = data_dir("structure") / "pdb" / "1l2y" if format == "cif": - pdbx_file = pdbx.CIFFile.read(base_path + ".cif") + pdbx_file = pdbx.CIFFile.read(base_path.with_suffix(".cif")) else: - pdbx_file = pdbx.BinaryCIFFile.read(base_path + ".bcif") + pdbx_file = pdbx.BinaryCIFFile.read(base_path.with_suffix(".bcif")) test_model_count = pdbx.get_model_count(pdbx_file) ref_model_count = pdbx.get_structure(pdbx_file).stack_depth() assert test_model_count == ref_model_count @pytest.mark.parametrize( - "string, looped", - itertools.product( - [ - "", - " ", - " ", - "te xt", - "'", - '"', - "te\nxt", - "\t", - """single"anddouble"marks""", - """single' and double" marks with whitespace""", - ], - [False, True], - ), + "string", + [ + "", + " ", + " ", + "te xt", + "'", + '"', + "te\nxt", + "\t", + """single"anddouble"marks""", + """single' and double" marks with whitespace""", + ], ) +@pytest.mark.parametrize("looped", [False, True]) def test_escape(string, looped): """ Test whether values that need to be escaped are properly escaped. @@ -75,7 +68,7 @@ def test_escape(string, looped): @pytest.mark.parametrize( - "cif_line, expected_fields", + ["cif_line", "expected_fields"], [ ["'' 'embed'quote' ", ["", "embed'quote"]], ['2 "embed"quote" "\t\n"', ["2", 'embed"quote', "\t\n"]], @@ -93,10 +86,12 @@ def test_split_one_line(cif_line, expected_fields): @pytest.mark.parametrize("find_matches_by_dict", [False, True]) @pytest.mark.parametrize("model", [None, 1, -1]) @pytest.mark.parametrize( - "path", Path(data_dir("structure")).glob("*.cif"), ids=lambda p: p.stem + "path", + sorted((data_dir("structure") / "pdb").glob("*.cif")), + ids=lambda path: path.name, ) @pytest.mark.parametrize("format", ["cif", "bcif"]) -def test_conversion(monkeypatch, tmpdir, format, path, model, find_matches_by_dict): +def test_conversion(monkeypatch, tmp_path, format, path, model, find_matches_by_dict): """ Test serializing and deserializing a structure from a file restores the same structure. @@ -107,12 +102,11 @@ def test_conversion(monkeypatch, tmpdir, format, path, model, find_matches_by_di # Lower the threshold to 0 to force usage of `_find_matches_by_dict()` monkeypatch.setattr(pdbx.convert, "FIND_MATCHES_SWITCH_THRESHOLD", 0) - base_path = splitext(path)[0] if format == "cif": - data_path = base_path + ".cif" + data_path = path.with_suffix(".cif") File = pdbx.CIFFile else: - data_path = base_path + ".bcif" + data_path = path.with_suffix(".bcif") File = pdbx.BinaryCIFFile pdbx_file = File.read(data_path) @@ -129,7 +123,7 @@ def test_conversion(monkeypatch, tmpdir, format, path, model, find_matches_by_di pdbx_file = File() pdbx.set_structure(pdbx_file, ref_atoms) - file_path = join(tmpdir, f"test.{format}") + file_path = tmp_path / f"test.{format}" pdbx_file.write(file_path) pdbx_file = File.read(file_path) @@ -163,7 +157,9 @@ def test_filter_altloc(pdb_id): """ Check if the different ``altloc`` options give the expected results. """ - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), f"{pdb_id}.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read( + data_dir("structure") / "pdb" / f"{pdb_id}.bcif" + ) atoms = {} for altloc in ["first", "occupancy", "all"]: atoms[altloc] = pdbx.get_structure(pdbx_file, model=1, altloc=altloc) @@ -194,9 +190,7 @@ def test_filter_altloc_edge_case(altloc): Expect that only one altloc ID is selected for the same residue, even if that residue in some altloc has a different residue name. """ - pdbx_file = pdbx.CIFFile.read( - join(data_dir("structure"), "edge_cases", "altloc.cif") - ) + pdbx_file = pdbx.CIFFile.read(data_dir("structure") / "edge_cases" / "altloc.cif") test_atoms = pdbx.get_structure(pdbx_file, model=1, altloc=altloc) ref_atoms = pdbx.get_structure(pdbx_file, model=1, altloc="all") @@ -221,7 +215,7 @@ def test_bonds_from_ccd(format): bonds, such as disulfide bridges or glycosylations, as those bonds would require explicit bond information. """ - path = join(data_dir("structure"), f"1l2y.{format}") + path = data_dir("structure") / "pdb" / f"1l2y.{format}" File = pdbx.CIFFile if format == "cif" else pdbx.BinaryCIFFile pdbx_file = File.read(path) @@ -239,7 +233,7 @@ def test_metal_coordination_bonds(): Test if metal coordination bonds are properly written and read, based on an known example. """ - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1crr.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1crr.bcif") atoms = pdbx.get_structure(pdbx_file, model=1, include_bonds=True) bond_array = atoms.bonds.as_array() @@ -272,7 +266,7 @@ def test_bond_sparsity(): This tests a previous bug, where duplicate intra-residue bonds were written (https://github.com/biotite-dev/biotite/issues/652). """ - path = join(data_dir("structure"), "1l2y.bcif") + path = data_dir("structure") / "pdb" / "1l2y.bcif" ref_pdbx_file = pdbx.BinaryCIFFile.read(path) ref_bond_number = ref_pdbx_file.block["chem_comp_bond"].row_count @@ -285,10 +279,10 @@ def test_bond_sparsity(): @pytest.mark.parametrize("format", ["cif", "bcif"]) -def test_extra_fields(tmpdir, format): +def test_extra_fields(tmp_path, format): EXTRA_FIELDS = ["atom_id", "b_factor", "occupancy", "charge", "entity_id"] - path = join(data_dir("structure"), f"1l2y.{format}") + path = data_dir("structure") / "pdb" / f"1l2y.{format}" if format == "cif": File = pdbx.CIFFile else: @@ -299,7 +293,7 @@ def test_extra_fields(tmpdir, format): pdbx_file = File() pdbx.set_structure(pdbx_file, ref_atoms, data_block="test") - file_path = join(tmpdir, "test") + file_path = tmp_path / "test" pdbx_file.write(file_path) pdbx_file = File.read(path) @@ -307,8 +301,8 @@ def test_extra_fields(tmpdir, format): assert test_atoms.ins_code.tolist() == ref_atoms.ins_code.tolist() assert test_atoms.atom_id.tolist() == ref_atoms.atom_id.tolist() - assert test_atoms.b_factor.tolist() == approx(ref_atoms.b_factor.tolist()) - assert test_atoms.occupancy.tolist() == approx(ref_atoms.occupancy.tolist()) + assert test_atoms.b_factor.tolist() == pytest.approx(ref_atoms.b_factor.tolist()) + assert test_atoms.occupancy.tolist() == pytest.approx(ref_atoms.occupancy.tolist()) assert test_atoms.charge.tolist() == ref_atoms.charge.tolist() assert test_atoms.entity_id.tolist() == ref_atoms.entity_id.tolist() assert test_atoms == ref_atoms @@ -324,7 +318,7 @@ def test_hetero_residue_borders(): The created residue IDs are manually checked, based on the CIF file, representing this edge case. """ - path = join(data_dir("structure"), "edge_cases", "res_ids.cif") + path = data_dir("structure") / "edge_cases" / "res_ids.cif" pdbx_file = pdbx.CIFFile.read(path) # The issue does not exist for author fields, hence they represent the reference ref_atoms = pdbx.get_structure(pdbx_file, model=1, use_author_fields=True) @@ -354,7 +348,7 @@ def test_dynamic_dtype(): """ CHAIN_ID = "LONG_ID" - path = join(data_dir("structure"), "1l2y.bcif") + path = data_dir("structure") / "pdb" / "1l2y.bcif" pdbx_file = pdbx.BinaryCIFFile.read(path) category = pdbx_file.block["atom_site"] category["label_asym_id"] = np.full(len(category["label_asym_id"]), CHAIN_ID) @@ -365,7 +359,7 @@ def test_dynamic_dtype(): @pytest.mark.parametrize("format", ["cif", "bcif"]) -def test_any_bonds(tmpdir, format): +def test_any_bonds(tmp_path, format): """ Check if ``BondType.ANY`` bonds can be written and read from a PDBx file, i.e. the ``chem_comp_bond`` and ``struct_conn`` categories. @@ -392,7 +386,7 @@ def test_any_bonds(tmpdir, format): pdbx_file = File() pdbx.set_structure(pdbx_file, atoms) - file_path = join(tmpdir, f"test.{format}") + file_path = tmp_path / f"test.{format}" pdbx_file.write(file_path) pdbx_file = File.read(file_path) @@ -459,7 +453,7 @@ def test_list_assemblies(format): Test the :func:`list_assemblies()` function based on a known example. """ - path = join(data_dir("structure"), f"1f2n.{format}") + path = data_dir("structure") / "pdb" / f"1f2n.{format}" if format == "cif": File = pdbx.CIFFile else: @@ -478,10 +472,9 @@ def test_list_assemblies(format): } -@pytest.mark.parametrize( - "format, pdb_id, model", - itertools.product(["cif", "bcif"], ["1f2n", "5zng"], [None, 1, -1]), -) +@pytest.mark.parametrize("format", ["cif", "bcif"]) +@pytest.mark.parametrize("pdb_id", ["1f2n", "5zng"]) +@pytest.mark.parametrize("model", [None, 1, -1]) def test_assembly_chain_count(format, pdb_id, model): """ Test whether the :func:`get_assembly()` function produces the same @@ -490,7 +483,7 @@ def test_assembly_chain_count(format, pdb_id, model): Furthermore, check if the number of atoms in the entire assembly is a multiple of the numbers of atoms in a monomer. """ - path = join(data_dir("structure"), f"{pdb_id}.{format}") + path = data_dir("structure") / "pdb" / f"{pdb_id}.{format}" if format == "cif": File = pdbx.CIFFile else: @@ -531,7 +524,7 @@ def test_assembly_chain_count(format, pdb_id, model): @pytest.mark.parametrize( - "pdb_id, assembly_id, symmetric_unit_count", + ["pdb_id", "assembly_id", "symmetric_unit_count"], [ # Single operation ("5zng", "1", 1), @@ -552,7 +545,9 @@ def test_assembly_sym_id(pdb_id, assembly_id, symmetric_unit_count): Check if the :func:`get_assembly()` function returns the correct number of symmetry IDs for a known example. """ - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), f"{pdb_id}.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read( + data_dir("structure") / "pdb" / f"{pdb_id}.bcif" + ) assembly = pdbx.get_assembly( pdbx_file, assembly_id=assembly_id, use_author_fields=False ) @@ -571,7 +566,7 @@ def test_unit_cell_trivial(model, center): The 'P 1' space group has no symmetries. Hence the unit cell from this space group should be the same as the asymmetric unit. """ - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") # Give the structure the 'P 1' space group pdbx_file.block["symmetry"] = pdbx.BinaryCIFCategory( {"space_group_name_H-M": "P 1"} @@ -600,13 +595,15 @@ def test_unit_cell_trivial(model, center): unit_cell.get_annotation(category).tolist() == asymmetric_unit.get_annotation(category).tolist() ) - assert unit_cell.coord.flatten().tolist() == approx( + assert unit_cell.coord.flatten().tolist() == pytest.approx( asymmetric_unit.coord.flatten().tolist() ) @pytest.mark.parametrize( - "pdb_path", Path(data_dir("structure")).glob("*.pdb"), ids=lambda p: p.stem + "pdb_path", + sorted((data_dir("structure") / "pdb").glob("*.pdb")), + ids=lambda path: path.name, ) def test_unit_cell_pdb_consistency(pdb_path): """ @@ -647,12 +644,12 @@ def test_unit_cell_pdb_consistency(pdb_path): @pytest.mark.parametrize( - "path, use_ideal_coord", - itertools.product( - glob.glob(join(data_dir("structure"), "molecules", "*.cif")), [False, True] - ), + "path", + sorted((data_dir("structure") / "molecules").glob("*.cif")), + ids=lambda path: path.name, ) -def test_component_conversion(tmpdir, path, use_ideal_coord): +@pytest.mark.parametrize("use_ideal_coord", [False, True]) +def test_component_conversion(tmp_path, path, use_ideal_coord): """ After reading a component from a CIF file, writing the component back to a new file and reading it again should give the same @@ -663,7 +660,7 @@ def test_component_conversion(tmpdir, path, use_ideal_coord): cif_file = pdbx.CIFFile() pdbx.set_component(cif_file, ref_atoms, data_block="test") - file_path = join(tmpdir, "test") + file_path = tmp_path / "test" cif_file.write(file_path) cif_file = pdbx.CIFFile.read(path) @@ -683,9 +680,9 @@ def test_get_sequence(format): else: File = pdbx.BinaryCIFFile - pdbx_file = File.read(join(data_dir("structure"), f"5ugo.{format}")) + pdbx_file = File.read(data_dir("structure") / "pdb" / f"5ugo.{format}") sequences_1 = pdbx.get_sequence(pdbx_file) - pdbx_file = File.read(join(data_dir("structure"), f"4gxy.{format}")) + pdbx_file = File.read(data_dir("structure") / "pdb" / f"4gxy.{format}") sequences_2 = pdbx.get_sequence(pdbx_file) assert str(sequences_1["T"]) == "CCGACGGCGCATCAGC" assert type(sequences_1["T"]) is seq.NucleotideSequence @@ -742,13 +739,17 @@ def test_get_sse(): for sheet_range in SHEET_RANGES: ref_sse[sheet_range[0] - 1 : sheet_range[1]] = "b" - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1aki.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1aki.bcif") test_sse = pdbx.get_sse(pdbx_file)["A"] assert test_sse.tolist() == ref_sse.tolist() -@pytest.mark.parametrize("path", glob.glob(join(data_dir("structure"), "*.bcif"))) +@pytest.mark.parametrize( + "path", + sorted((data_dir("structure") / "pdb").glob("*.bcif")), + ids=lambda path: path.name, +) def test_get_sse_length(path): """ If `match_model` is set in :func:`get_sse()`, the length of the returned array @@ -785,7 +786,9 @@ def test_bcif_encoding(): ] } - bcif_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), f"{PDB_ID}.bcif")) + bcif_file = pdbx.BinaryCIFFile.read( + data_dir("structure") / "pdb" / f"{PDB_ID}.bcif" + ) for category_name, category in bcif_file[PDB_ID.upper()].items(): for column_name in category.keys(): try: @@ -827,14 +830,14 @@ def test_bcif_quantization_encoding(): MAX = 42 NUM_STEPS = 100 - np.random.seed(0) + rng = np.random.default_rng(0) ref_data = np.linspace(MIN, MAX, NUM_STEPS) - np.random.shuffle(ref_data) + rng.shuffle(ref_data) encoding = pdbx.IntervalQuantizationEncoding(MIN, MAX, NUM_STEPS) test_data = encoding.decode(encoding.encode(ref_data)) - assert test_data.tolist() == approx(ref_data.tolist()) + assert test_data.tolist() == pytest.approx(ref_data.tolist()) def test_bcif_cif_consistency(): @@ -849,9 +852,9 @@ def test_bcif_cif_consistency(): ] PDB_ID = "1aki" - base_path = join(data_dir("structure"), PDB_ID) - cif_file = pdbx.CIFFile.read(base_path + ".cif") - bcif_file = pdbx.BinaryCIFFile.read(base_path + ".bcif") + base_path = data_dir("structure") / "pdb" / PDB_ID + cif_file = pdbx.CIFFile.read(base_path.with_suffix(".cif")) + bcif_file = pdbx.BinaryCIFFile.read(base_path.with_suffix(".bcif")) cif_block = cif_file.block bcif_block = bcif_file.block @@ -900,7 +903,7 @@ def test_bcif_cif_consistency(): @pytest.mark.parametrize( - "format, create_new_encoding", + ["format", "create_new_encoding"], [ ("cif", None), ("bcif", False), @@ -917,7 +920,7 @@ def test_serialization_consistency(format, create_new_encoding): """ PDB_ID = "1aki" - path = join(data_dir("structure"), f"{PDB_ID}.{format}") + path = data_dir("structure") / "pdb" / f"{PDB_ID}.{format}" if format == "cif": file = pdbx.CIFFile.read(path) elif format == "bcif": @@ -940,10 +943,9 @@ def test_serialization_consistency(format, create_new_encoding): raise Exception(f"Comparison failed for '{category_name}.{key}'") -@pytest.mark.parametrize( - "format, level", itertools.product(["cif", "bcif"], ["block", "category", "column"]) -) -def test_editing(tmpdir, format, level): +@pytest.mark.parametrize("format", ["cif", "bcif"]) +@pytest.mark.parametrize("level", ["block", "category", "column"]) +def test_editing(tmp_path, format, level): """ Check if editing an existing PDBx file works, by checking if replacing some category/block/column with a copy of itself does not affect the content. @@ -957,9 +959,9 @@ def test_editing(tmpdir, format, level): category = Category({"foo_col": column, "bar_col": column, "baz_col": column}) block = Block({"foo_cat": category, "bar_cat": category, "baz_cat": category}) ref_pdbx_file = File({"foo_block": block, "bar_block": block, "baz_block": block}) - ref_pdbx_file.write(join(tmpdir, f"original.{format}")) + ref_pdbx_file.write(tmp_path / f"original.{format}") - pdbx_file = File.read(join(tmpdir, f"original.{format}")) + pdbx_file = File.read(tmp_path / f"original.{format}") if level == "block": # Replace block in the mid, # to check if the block before and after remain the same @@ -970,9 +972,9 @@ def test_editing(tmpdir, format, level): pdbx_file["bar_block"]["bar_cat"]["bar_col"] = pdbx_file["bar_block"][ "bar_cat" ]["bar_col"] - pdbx_file.write(join(tmpdir, f"edited.{format}")) + pdbx_file.write(tmp_path / f"edited.{format}") - test_pdbx_file = File.read(join(tmpdir, f"edited.{format}")) + test_pdbx_file = File.read(tmp_path / f"edited.{format}") # As the content should not have changed, the serialized files should be identical assert test_pdbx_file.serialize() == ref_pdbx_file.serialize() @@ -984,7 +986,7 @@ def test_compress_data(): """ PDB_ID = "1aki" - path = join(data_dir("structure"), f"{PDB_ID}.bcif") + path = data_dir("structure") / "pdb" / f"{PDB_ID}.bcif" bcif_file = pdbx.BinaryCIFFile.read(path) for category_name, category in bcif_file.block.items(): for column_name, column in category.items(): @@ -1024,7 +1026,8 @@ def test_compress_data(): @pytest.mark.parametrize( "path", - glob.glob(join(data_dir("structure"), "*.bcif")), + sorted((data_dir("structure") / "pdb").glob("*.bcif")), + ids=lambda path: path.name, ) def test_compress_file(path): """ @@ -1101,7 +1104,7 @@ def test_extreme_float_compression(value): @pytest.mark.parametrize( - "number, ref_decimals", + ["number", "ref_decimals"], [ (1.0, 0), (1.23, 2), @@ -1145,12 +1148,12 @@ def _file_size(bcif_file): return len(written_file.read()) -def test_writing_and_reading_extra_fields(tmpdir): +def test_writing_and_reading_extra_fields(tmp_path): """ Check if writing and reading extra fields works. """ # Set up a custom atom array with an additional annotation - cif_file = pdbx.CIFFile.read(join(data_dir("structure"), "5ugo.cif")) + cif_file = pdbx.CIFFile.read(data_dir("structure") / "pdb" / "5ugo.cif") atoms = pdbx.get_structure(cif_file) custom_annotation = np.arange(atoms.array_length()) atoms.set_annotation("my_custom_annotation", custom_annotation) @@ -1158,11 +1161,11 @@ def test_writing_and_reading_extra_fields(tmpdir): # Write to file new_cif_file = pdbx.CIFFile() pdbx.set_structure(new_cif_file, atoms, extra_fields=["my_custom_annotation"]) - new_cif_file.write(join(tmpdir, "test.cif")) + new_cif_file.write(tmp_path / "test.cif") # Read again atoms = pdbx.get_structure( - pdbx.CIFFile.read(join(tmpdir, "test.cif")), + pdbx.CIFFile.read(tmp_path / "test.cif"), extra_fields=["my_custom_annotation"], ) assert "my_custom_annotation" in atoms.get_annotation_categories() @@ -1177,11 +1180,11 @@ def test_get_structure_missing_ins_code(format): :func:`get_structure()` should handle a missing ``pdbx_PDB_ins_code`` column gracefully, since it is optional in the PDBx dictionary. """ - base_path = join(data_dir("structure"), "1l2y") + base_path = data_dir("structure") / "pdb" / "1l2y" if format == "cif": - pdbx_file = pdbx.CIFFile.read(base_path + ".cif") + pdbx_file = pdbx.CIFFile.read(base_path.with_suffix(".cif")) else: - pdbx_file = pdbx.BinaryCIFFile.read(base_path + ".bcif") + pdbx_file = pdbx.BinaryCIFFile.read(base_path.with_suffix(".bcif")) del pdbx_file.block["atom_site"]["pdbx_PDB_ins_code"] atoms = pdbx.get_structure(pdbx_file, model=1) @@ -1196,7 +1199,7 @@ def test_multiple_atom_array(format): Setting the structure with an :class:`AtomArrayStack` and with an equivalent list of :class:`AtomArray` objects should result in an equivalent output file. """ - base_path = join(data_dir("structure"), "1gya") + base_path = data_dir("structure") / "pdb" / "1gya" if format == "cif": File = pdbx.CIFFile else: diff --git a/tests/structure/io/test_trajectory.py b/tests/structure/io/test_trajectory.py index ece43f9db..846d322a3 100644 --- a/tests/structure/io/test_trajectory.py +++ b/tests/structure/io/test_trajectory.py @@ -3,7 +3,6 @@ # information. import itertools -from pathlib import Path import numpy as np import pytest import biotite.structure as struc @@ -17,7 +16,7 @@ @pytest.mark.parametrize("format", ["trr", "xtc", "dcd", "netcdf"]) def test_array_conversion(format, tmp_path): - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1l2y.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") template = pdbx.get_structure(pdbx_file, model=1) # Add fake box template.box = np.diag([1, 2, 3]) @@ -29,7 +28,7 @@ def test_array_conversion(format, tmp_path): traj_file_cls = dcd.DCDFile if format == "netcdf": traj_file_cls = netcdf.NetCDFFile - traj_file = traj_file_cls.read(Path(data_dir("structure")) / f"1l2y.{format}") + traj_file = traj_file_cls.read(data_dir("structure") / "pdb" / f"1l2y.{format}") ref_array = traj_file.get_structure(template) traj_file = traj_file_cls() @@ -45,16 +44,11 @@ def test_array_conversion(format, tmp_path): assert ref_array.coord == pytest.approx(array.coord, abs=1e-2) -@pytest.mark.parametrize( - "format, start, stop, step, chunk_size", - itertools.product( - ["trr", "xtc", "dcd", "netcdf"], - [None, 2], - [None, 17], - [None, 2], - [None, 3], - ), -) +@pytest.mark.parametrize("format", ["trr", "xtc", "dcd", "netcdf"]) +@pytest.mark.parametrize("start", [None, 2]) +@pytest.mark.parametrize("stop", [None, 17]) +@pytest.mark.parametrize("step", [None, 2]) +@pytest.mark.parametrize("chunk_size", [None, 3]) def test_bcif_consistency(format, start, stop, step, chunk_size): if format == "netcdf" and stop is not None and step is not None: # Currently, there is an inconsistency in biotraj's @@ -63,7 +57,7 @@ def test_bcif_consistency(format, start, stop, step, chunk_size): # is dependent on the 'stride' parameter return - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1l2y.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") ref_traj = pdbx.get_structure(pdbx_file) ref_traj = ref_traj[slice(start, stop, step)] @@ -78,7 +72,7 @@ def test_bcif_consistency(format, start, stop, step, chunk_size): if format == "netcdf": traj_file_cls = netcdf.NetCDFFile traj_file = traj_file_cls.read( - Path(data_dir("structure")) / f"1l2y.{format}", + data_dir("structure") / "pdb" / f"1l2y.{format}", start, stop, step, @@ -105,16 +99,11 @@ def test_bcif_consistency(format, start, stop, step, chunk_size): assert test_traj.coord == pytest.approx(ref_traj.coord, abs=1e-2) -@pytest.mark.parametrize( - "format, start, stop, step, stack_size", - itertools.product( - ["trr", "xtc", "dcd", "netcdf"], - [None, 2], - [None, 17], - [None, 2], - [None, 2, 3], - ), -) +@pytest.mark.parametrize("format", ["trr", "xtc", "dcd", "netcdf"]) +@pytest.mark.parametrize("start", [None, 2]) +@pytest.mark.parametrize("stop", [None, 17]) +@pytest.mark.parametrize("step", [None, 2]) +@pytest.mark.parametrize("stack_size", [None, 2, 3]) def test_read_iter(format, start, stop, step, stack_size): """ Compare aggregated yields of :func:`read_iter()` with the values @@ -135,7 +124,7 @@ def test_read_iter(format, start, stop, step, stack_size): traj_file_cls = dcd.DCDFile if format == "netcdf": traj_file_cls = netcdf.NetCDFFile - input_path = Path(data_dir("structure")) / f"1l2y.{format}" + input_path = data_dir("structure") / "pdb" / f"1l2y.{format}" traj_file = traj_file_cls.read(input_path, start, stop, step) ref_coord = traj_file.get_coord() @@ -175,16 +164,11 @@ def test_read_iter(format, start, stop, step, stack_size): assert test_time.tolist() == ref_time.tolist() -@pytest.mark.parametrize( - "format, start, stop, step, stack_size", - itertools.product( - ["trr", "xtc", "dcd", "netcdf"], - [None, 2], - [None, 17], - [None, 2], - [None, 2, 3], - ), -) +@pytest.mark.parametrize("format", ["trr", "xtc", "dcd", "netcdf"]) +@pytest.mark.parametrize("start", [None, 2]) +@pytest.mark.parametrize("stop", [None, 17]) +@pytest.mark.parametrize("step", [None, 2]) +@pytest.mark.parametrize("stack_size", [None, 2, 3]) def test_read_iter_structure(format, start, stop, step, stack_size): """ Compare aggregated yields of :func:`read_iter_structure()` with the @@ -198,7 +182,7 @@ def test_read_iter_structure(format, start, stop, step, stack_size): # is dependent on the 'stride' parameter return - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1l2y.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") template = pdbx.get_structure(pdbx_file) if format == "trr": @@ -209,7 +193,7 @@ def test_read_iter_structure(format, start, stop, step, stack_size): traj_file_cls = dcd.DCDFile if format == "netcdf": traj_file_cls = netcdf.NetCDFFile - input_path = Path(data_dir("structure")) / f"1l2y.{format}" + input_path = data_dir("structure") / "pdb" / f"1l2y.{format}" traj_file = traj_file_cls.read(input_path, start, stop, step) ref_traj = traj_file.get_structure(template) @@ -231,16 +215,11 @@ def test_read_iter_structure(format, start, stop, step, stack_size): assert test_traj == ref_traj -@pytest.mark.parametrize( - "format, n_models, n_atoms, include_box, include_time", - itertools.product( - ["trr", "xtc", "dcd", "netcdf"], - [1, 100], - [1, 1000], - [False, True], - [False, True], - ), -) +@pytest.mark.parametrize("format", ["trr", "xtc", "dcd", "netcdf"]) +@pytest.mark.parametrize("n_models", [1, 100]) +@pytest.mark.parametrize("n_atoms", [1, 1000]) +@pytest.mark.parametrize("include_box", [False, True]) +@pytest.mark.parametrize("include_time", [False, True]) def test_write_iter(format, n_models, n_atoms, include_box, include_time, tmp_path): """ Expect that `write_iter()` and `write()` create files with equal content. @@ -257,10 +236,10 @@ def test_write_iter(format, n_models, n_atoms, include_box, include_time, tmp_pa traj_file_cls = netcdf.NetCDFFile # Generate random coordinate dataset content - np.random.seed(0) - coord = np.random.rand(n_models, n_atoms, 3) * 100 - box = np.random.rand(n_models, 3, 3) * 100 if include_box else None - time = np.random.rand(n_models) * 10 if include_time else None + rng = np.random.default_rng(0) + coord = rng.random((n_models, n_atoms, 3)) * 100 + box = rng.random((n_models, 3, 3)) * 100 if include_box else None + time = rng.random(n_models) * 10 if include_time else None ref_file = tmp_path / f"ref.{format}" traj_file = traj_file_cls() diff --git a/tests/structure/test_basepairs.py b/tests/structure/test_basepairs.py index 8711b9971..38323e9a8 100644 --- a/tests/structure/test_basepairs.py +++ b/tests/structure/test_basepairs.py @@ -3,11 +3,10 @@ # information. import json -from os.path import join import numpy as np import pytest import biotite.structure as struc -import biotite.structure.io as strucio +import biotite.structure.io.pdbx as pdbx # For ``base_pairs_edge()`` differences to a reference can be ambiguous # as the number hydrogen bonds between two different edges can be equal. @@ -29,7 +28,9 @@ def reversed_iterator(iter): @pytest.fixture def nuc_sample_array(): - return strucio.load_structure(join(data_dir("structure"), "base_pairs", "1qxb.cif")) + return pdbx.get_structure( + pdbx.CIFFile.read(data_dir("structure") / "base_pairs" / "1qxb.cif"), model=1 + ) @pytest.fixture @@ -152,11 +153,11 @@ def test_base_pairs_reordered(nuc_sample_array, seed): """ # Randomly reorder the atoms in each residue nuc_sample_array_reordered = struc.AtomArray(0) - np.random.seed(seed) + rng = np.random.default_rng(seed) for res in struc.residue_iter(nuc_sample_array): bound = res.array_length() - indices = np.random.choice(np.arange(bound), bound, replace=False) + indices = rng.choice(np.arange(bound), bound, replace=False) nuc_sample_array_reordered += res[..., indices] assert np.all( @@ -204,12 +205,13 @@ def get_reference(pdb_id, suffix): Gets a reference structure and a related json array depending on a specified JSON-suffix and PDB ID. """ - structure = strucio.load_structure( - join(data_dir("structure"), "base_pairs", f"{pdb_id}.cif") + structure = pdbx.get_structure( + pdbx.CIFFile.read(data_dir("structure") / "base_pairs" / f"{pdb_id}.cif"), + model=1, ) with open( - join(data_dir("structure"), "base_pairs", f"{pdb_id}_{suffix}.json"), "r" + data_dir("structure") / "base_pairs" / f"{pdb_id}_{suffix}.json", "r" ) as file: reference = np.array(json.load(file)) @@ -311,8 +313,8 @@ def test_base_stacking(): helix. """ # Load the test structure (1BNA) - a DNA-double-helix - helix = strucio.load_structure( - join(data_dir("structure"), "base_pairs", "1bna.cif") + helix = pdbx.get_structure( + pdbx.CIFFile.read(data_dir("structure") / "base_pairs" / "1bna.cif"), model=1 ) residue_starts = struc.get_residue_starts(helix) diff --git a/tests/structure/test_bonds.py b/tests/structure/test_bonds.py index 2cf9eac3f..97fb89d38 100644 --- a/tests/structure/test_bonds.py +++ b/tests/structure/test_bonds.py @@ -4,11 +4,9 @@ import itertools import pickle -from os.path import join import numpy as np import pytest import biotite.structure as struc -import biotite.structure.io as strucio import biotite.structure.io.pdbx as pdbx from tests.util import data_dir @@ -17,10 +15,10 @@ def generate_random_bond_list(atom_count, bond_count, seed=0): """ Generate a random :class:`BondList`. """ - np.random.seed(seed) + rng = np.random.default_rng(seed) # Create random bonds between atoms of # a potential atom array of length ATOM_COUNT - bonds = np.random.randint(atom_count, size=(bond_count, 3)) + bonds = rng.integers(atom_count, size=(bond_count, 3)) # Clip bond types to allowed BondType values bonds[:, 2] %= len(struc.BondType) # Remove bonds of atoms to itself @@ -292,7 +290,7 @@ def test_get_all_bonds_typical(): The bonds are taken from a real structure, so no atom exceeds 4 bonds per atom. """ - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") atoms = pdbx.get_structure(pdbx_file, model=1, include_bonds=True) bonds, bond_types = atoms.bonds.get_all_bonds() @@ -399,7 +397,7 @@ def test_sorted_array_indexing(): # Create a sorted array of random indices for the BondList # Indices may not occur multiple times -> 'replace=False' index_array = np.sort( - np.random.choice(np.arange(ATOM_COUNT), INDEX_SIZE, replace=False) + np.random.default_rng().choice(np.arange(ATOM_COUNT), INDEX_SIZE, replace=False) ) test_bonds = bonds[index_array] @@ -428,8 +426,9 @@ def test_unsorted_array_indexing(): # instead of an atom array integers = np.arange(ATOM_COUNT) + rng = np.random.default_rng() # Create random bonds between the reference integers - bonds = np.random.randint(ATOM_COUNT, size=(BOND_COUNT, 2)) + bonds = rng.integers(ATOM_COUNT, size=(BOND_COUNT, 2)) # Remove bonds of elements to itself bonds = bonds[bonds[:, 0] != bonds[:, 1]] assert len(bonds) > 0 @@ -437,7 +436,7 @@ def test_unsorted_array_indexing(): # Create an unsorted array of random indices for the BondList # Indices should be unsorted -> 'replace=False' - unsorted_index = np.random.choice(np.arange(ATOM_COUNT), INDEX_SIZE, replace=False) + unsorted_index = rng.choice(np.arange(ATOM_COUNT), INDEX_SIZE, replace=False) test_bonds = bonds[unsorted_index] # Create a sorted variant of the index array @@ -482,7 +481,9 @@ def test_atom_array_consistency(): The boolean mask is constructed in a way that all bonded atoms are masked. """ - array = strucio.load_structure(join(data_dir("structure"), "1l2y.bcif"))[0] + array = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif"), model=1 + ) ca = array[array.atom_name == "CA"] # Just for testing, does not reflect real bonds bond_list = struc.BondList( diff --git a/tests/structure/test_box.py b/tests/structure/test_box.py index 785280cea..6e3f2016f 100644 --- a/tests/structure/test_box.py +++ b/tests/structure/test_box.py @@ -4,12 +4,10 @@ import itertools import warnings -from os.path import join import numpy as np import pytest import biotite.structure as struc import biotite.structure.io.pdbx as pdbx -from biotite.structure.io import load_structure from tests.util import data_dir SAMPLE_CELLS = [ @@ -31,7 +29,7 @@ @pytest.mark.parametrize( - "name, number", + ["name", "number"], [("P 1", 1), ("P 21 21 21", 19), ("I 41/a 2/c 2/d", 142), ("I a -3 d", 230)], ) def test_space_group_correct_index(name, number): @@ -47,7 +45,7 @@ def test_space_group_correct_index(name, number): @pytest.mark.parametrize( - "space_group, rotations, translations", + ["space_group", "rotations", "translations"], # Omit the trivial identity transformation, as they appear in every space group [ ( @@ -169,9 +167,9 @@ def test_volume(): @pytest.mark.parametrize( - "len_a, len_b, len_c, alpha, beta, gamma, x, y,z", - [box + coord for box, coord in itertools.product(SAMPLE_CELLS, SAMPLE_COORD)], + ["len_a", "len_b", "len_c", "alpha", "beta", "gamma"], SAMPLE_CELLS ) +@pytest.mark.parametrize(["x", "y", "z"], SAMPLE_COORD) def test_move_into_box(len_a, len_b, len_c, alpha, beta, gamma, x, y, z): box = struc.vectors_from_unitcell( len_a, len_b, len_c, np.deg2rad(alpha), np.deg2rad(beta), np.deg2rad(gamma) @@ -184,9 +182,9 @@ def test_move_into_box(len_a, len_b, len_c, alpha, beta, gamma, x, y, z): @pytest.mark.parametrize( - "len_a, len_b, len_c, alpha, beta, gamma, x, y,z", - [box + coord for box, coord in itertools.product(SAMPLE_CELLS, SAMPLE_COORD)], + ["len_a", "len_b", "len_c", "alpha", "beta", "gamma"], SAMPLE_CELLS ) +@pytest.mark.parametrize(["x", "y", "z"], SAMPLE_COORD) def test_conversion_to_fraction(len_a, len_b, len_c, alpha, beta, gamma, x, y, z): box = struc.vectors_from_unitcell( len_a, len_b, len_c, np.deg2rad(alpha), np.deg2rad(beta), np.deg2rad(gamma) @@ -210,7 +208,8 @@ def test_conversion_to_fraction(len_a, len_b, len_c, alpha, beta, gamma, x, y, z def test_repeat_box(multi_model): model = None if multi_model else 1 array = pdbx.get_structure( - pdbx.BinaryCIFFile.read(join(data_dir("structure"), "3o5r.bcif")), model=model + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "3o5r.bcif"), + model=model, ) repeat_array, _ = struc.repeat_box(array) assert repeat_array.array_length() == array.array_length() * 27 @@ -224,9 +223,11 @@ def test_remove_pbc_unsegmented(multi_model): when the structure is entirely in the box. """ model = None if multi_model else 1 - ref_array = load_structure( - join(data_dir("structure"), "3o5r.bcif"), model=model, include_bonds=True - ) + bcif_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "3o5r.bcif") + ref_array = pdbx.get_structure(bcif_file, model=model, include_bonds=True) + if isinstance(ref_array, struc.AtomArrayStack) and ref_array.stack_depth() == 1: + # `load_structure()` collapses a single-model stack to an AtomArray + ref_array = ref_array[0] # Center structure in box centroid = struc.centroid(ref_array) box_center = np.diag(ref_array.box) / 2 @@ -237,9 +238,8 @@ def test_remove_pbc_unsegmented(multi_model): assert np.allclose(ref_array.coord, test_array.coord) -@pytest.mark.parametrize( - "multi_model, seed", itertools.product([False, True], range(10)) -) +@pytest.mark.parametrize("multi_model", [False, True]) +@pytest.mark.parametrize("seed", range(10)) def test_remove_pbc_restore(multi_model, seed): BUFFER = 5 @@ -259,7 +259,10 @@ def get_distance_matrices(array): matrix_pbc = np.stack([m[1] for m in matrices]) return matrix, matrix_pbc - stack = load_structure(join(data_dir("structure"), "1l2y.bcif"), include_bonds=True) + stack = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif"), + include_bonds=True, + ) # Only consider a single molecule # -> remove all other atoms (in this case some unbound hydrogen) @@ -282,10 +285,10 @@ def get_distance_matrices(array): ref_matrix, ref_matrix_pbc = get_distance_matrices(array) ## Segment protein - np.random.seed(seed) + rng = np.random.default_rng(seed) size = (array.stack_depth(), 3) if isinstance(array, struc.AtomArrayStack) else 3 translation_vector = np.sum( - np.random.uniform(-5, 5, size)[:, np.newaxis] * array.box, axis=-2 + rng.uniform(-5, 5, size)[:, np.newaxis] * array.box, axis=-2 )[..., np.newaxis, :] # Move atoms over periodic boundary... array = struc.translate(array, translation_vector) @@ -316,7 +319,9 @@ def test_remove_pbc_selection(multi_model): This test makes no assertions, it only test whether an exception occurs, when the `selection` parameter is given in `remove_pbc()`. """ - array = load_structure(join(data_dir("structure"), "3o5r.bcif")) + array = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "3o5r.bcif"), model=1 + ) if multi_model: array = struc.stack([array, array]) diff --git a/tests/structure/test_celllist.py b/tests/structure/test_celllist.py index 1b7a5b781..558367ec2 100644 --- a/tests/structure/test_celllist.py +++ b/tests/structure/test_celllist.py @@ -3,7 +3,6 @@ # information. import pickle -from pathlib import Path import numpy as np import pytest import biotite.structure as struc @@ -13,7 +12,7 @@ @pytest.fixture def atoms(): - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1l2y.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") atoms = pdbx.get_structure(pdbx_file, model=1) atoms = atoms[struc.filter_heavy(atoms)] return atoms @@ -60,8 +59,8 @@ def test_adjacency_matrix(atoms, cell_size, threshold, periodic, use_selection): atoms.box = np.diag(np.max(atoms.coord, axis=-2) - np.min(atoms.coord, axis=-2)) if use_selection: - np.random.seed(0) - selection = np.random.choice((False, True), atoms.array_length()) + rng = np.random.default_rng(0) + selection = rng.choice((False, True), atoms.array_length()) else: selection = None diff --git a/tests/structure/test_chains.py b/tests/structure/test_chains.py index 3c7daa782..63bec5b49 100644 --- a/tests/structure/test_chains.py +++ b/tests/structure/test_chains.py @@ -2,17 +2,18 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import numpy as np import pytest import biotite.structure as struc -import biotite.structure.io as strucio +import biotite.structure.io.pdbx as pdbx from tests.util import data_dir @pytest.fixture def array(): - return strucio.load_structure(join(data_dir("structure"), "1igy.bcif")) + return pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1igy.bcif"), model=1 + ) def test_get_chain_starts(array): @@ -52,8 +53,8 @@ def test_spread_chain_wise(array): def test_get_chain_masks(array): SAMPLE_SIZE = 100 - np.random.seed(0) - indices = np.random.randint(0, array.array_length(), SAMPLE_SIZE) + rng = np.random.default_rng(0) + indices = rng.integers(0, array.array_length(), SAMPLE_SIZE) test_masks = struc.get_chain_masks(array, indices) for index, test_mask in zip(indices, test_masks): ref_mask = array.chain_id == array.chain_id[index] @@ -62,8 +63,8 @@ def test_get_chain_masks(array): def test_get_chain_starts_for(array): SAMPLE_SIZE = 100 - np.random.seed(0) - indices = np.random.randint(0, array.array_length(), SAMPLE_SIZE) + rng = np.random.default_rng(0) + indices = rng.integers(0, array.array_length(), SAMPLE_SIZE) ref_starts = np.array( [np.where(mask)[0][0] for mask in struc.get_chain_masks(array, indices)] ) diff --git a/tests/structure/test_charges.py b/tests/structure/test_charges.py index 191b570a2..e1a7fe48f 100644 --- a/tests/structure/test_charges.py +++ b/tests/structure/test_charges.py @@ -225,7 +225,7 @@ # For this purpose, parametrization via pytest is performed @pytest.mark.parametrize( - "molecule, expected_results", + ["molecule", "expected_results"], [ (methane, (-0.078,)), (ethane, (-0.068, -0.068)), diff --git a/tests/structure/test_compare.py b/tests/structure/test_compare.py index a235b3c09..133d538af 100644 --- a/tests/structure/test_compare.py +++ b/tests/structure/test_compare.py @@ -2,8 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools -from os.path import join import numpy as np import pytest import biotite.structure as struc @@ -42,7 +40,7 @@ def models(): """ Get a short multi-model structure """ - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") return pdbx.get_structure(pdbx_file) @@ -209,10 +207,9 @@ def test_rmsf_gmx(superimposed_models): assert np.allclose(rmsf, rmsf_gmx, atol=1e-02) -@pytest.mark.parametrize( - "multi_model, as_coord, exclude_same_residue", - itertools.product([False, True], [False, True], [False, True]), -) +@pytest.mark.parametrize("multi_model", [False, True]) +@pytest.mark.parametrize("as_coord", [False, True]) +@pytest.mark.parametrize("exclude_same_residue", [False, True]) def test_lddt_perfect(models, multi_model, as_coord, exclude_same_residue): """ Check if the lDDT of a structure with itself as reference is 1.0 @@ -285,10 +282,8 @@ def test_lddt_custom_aggregation(models, multi_model): assert test_lddt[..., 0].tolist() == ref_lddt.tolist() -@pytest.mark.parametrize( - "multi_model, aggregation", - itertools.product([False, True], ["chain", "residue", "atom"]), -) +@pytest.mark.parametrize("multi_model", [False, True]) +@pytest.mark.parametrize("aggregation", ["chain", "residue", "atom"]) def test_lddt_aggregation_levels(models, multi_model, aggregation): """ Check if the predefined aggregation levels :func:`lddt()` return the expected shape. @@ -435,7 +430,7 @@ def test_custom_lddt_symmetric(models): @pytest.mark.parametrize( - "what_is_nan, ref_lddt", + ["what_is_nan", "ref_lddt"], [ # No reference coordinates (except one atom) # -> set of reference contacts is empty diff --git a/tests/structure/test_connect.py b/tests/structure/test_connect.py index 36a689de7..6fc103aea 100644 --- a/tests/structure/test_connect.py +++ b/tests/structure/test_connect.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import numpy as np import pytest import biotite.structure as struc @@ -20,13 +19,12 @@ def test_find_connected(seed, as_mask): An exception are water molecules, which have the same chain ID, albeit not being bonded to each other. """ - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1k6p.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1k6p.bcif") atoms = pdbx.get_structure( pdbx_file, model=1, include_bonds=True, use_author_fields=False ) atoms = atoms[~struc.filter_solvent(atoms)] - np.random.seed(seed) rng = np.random.default_rng(seed) root = rng.integers(atoms.array_length()) chain_id = atoms.chain_id[root] @@ -40,7 +38,7 @@ def test_find_connected(seed, as_mask): @pytest.mark.parametrize( - "res_name, expected_bonds", + ["res_name", "expected_bonds"], [ # Easy ligand visualization at: # https://www.rcsb.org/ligand/ @@ -88,13 +86,14 @@ def test_find_rotatable_bonds(res_name, expected_bonds): @pytest.mark.parametrize( - "cif_path, expected_bond_indices", + ["cif_path", "expected_bond_indices"], [ ( - join(data_dir("structure"), "3o5r.cif"), + data_dir("structure") / "pdb" / "3o5r.cif", [252, 257], # Carbonyl carbon and subsequent backbone nitrogen ) ], + ids=["3o5r.cif"], ) def test_canonical_bonds_with_altloc_occupancy(cif_path, expected_bond_indices): """ @@ -122,7 +121,7 @@ def test_method_consistency(periodic): THRESHOLD_PERCENTAGE = 0.99 # Structure with peptide, nucleotide, small molecules and water - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "5ugo.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "5ugo.bcif") atoms = pdbx.get_structure(pdbx_file, model=1) if periodic: # Add large dummy box to test parameter diff --git a/tests/structure/test_dotbracket.py b/tests/structure/test_dotbracket.py index cbe5ef2e6..e8b78d61c 100644 --- a/tests/structure/test_dotbracket.py +++ b/tests/structure/test_dotbracket.py @@ -2,11 +2,10 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import numpy as np import pytest import biotite.structure as struc -import biotite.structure.io as strucio +import biotite.structure.io.pdbx as pdbx from tests.util import data_dir @@ -15,7 +14,9 @@ def nuc_sample_array(): """ Sample structure. """ - nuc_sample_array = strucio.load_structure(join(data_dir("structure"), "4p5j.cif")) + nuc_sample_array = pdbx.get_structure( + pdbx.CIFFile.read(data_dir("structure") / "pdb" / "4p5j.cif"), model=1 + ) return nuc_sample_array[struc.filter_nucleotides(nuc_sample_array)] diff --git a/tests/structure/test_filter.py b/tests/structure/test_filter.py index 3dbc0e0bd..cd997a0c4 100644 --- a/tests/structure/test_filter.py +++ b/tests/structure/test_filter.py @@ -2,43 +2,53 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import numpy as np import pytest import biotite.structure as struc -import biotite.structure.io as strucio +import biotite.structure.io.pdbx as pdbx from tests.util import data_dir @pytest.fixture def canonical_sample_protein(): - return strucio.load_structure(join(data_dir("structure"), "3o5r.bcif")) + return pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "3o5r.bcif"), model=1 + ) @pytest.fixture def sample_protein(): - return strucio.load_structure(join(data_dir("structure"), "5eil.bcif")) + return pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "5eil.bcif"), model=1 + ) @pytest.fixture def canonical_sample_nucleotide(): - return strucio.load_structure(join(data_dir("structure"), "5ugo.bcif")) + return pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "5ugo.bcif"), model=1 + ) @pytest.fixture def sample_nucleotide(): - return strucio.load_structure(join(data_dir("structure"), "4p5j.bcif")) + return pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "4p5j.bcif"), model=1 + ) @pytest.fixture def sample_carbohydrate(): - return strucio.load_structure(join(data_dir("structure"), "2d0f.bcif")) + return pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "2d0f.bcif"), model=1 + ) @pytest.fixture def all_atloc_structure(): - return strucio.load_structure( - join(data_dir("structure"), "1o1z.bcif"), + return pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1o1z.bcif"), + model=1, extra_fields=["occupancy"], altloc="all", ) diff --git a/tests/structure/test_generalio.py b/tests/structure/test_generalio.py index d98781d59..aa0c1a3c7 100644 --- a/tests/structure/test_generalio.py +++ b/tests/structure/test_generalio.py @@ -2,25 +2,25 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob -import os -from os.path import join, splitext -from tempfile import NamedTemporaryFile import pytest import biotite.structure as struc import biotite.structure.io as strucio from tests.util import data_dir -@pytest.mark.parametrize("path", glob.glob(join(data_dir("structure"), "1l2y.*"))) +@pytest.mark.parametrize( + "path", + sorted((data_dir("structure") / "pdb").glob("1l2y.*")), + ids=lambda path: path.name, +) def test_loading(path): """ Just check if :func:`load_structure()` does not raise an exception and returns an object of appropriate type. """ - suffix = splitext(path)[1] + suffix = path.suffix if suffix in [".trr", ".xtc", ".dcd", ".netcdf"]: - template = strucio.load_structure(join(data_dir("structure"), "1l2y.bcif")) + template = strucio.load_structure(data_dir("structure") / "pdb" / "1l2y.bcif") array = strucio.load_structure(path, template) else: array = strucio.load_structure(path) @@ -38,8 +38,8 @@ def test_loading_template_with_trj(): raise an exception and returns an object of appropriate type and shape. """ - template = join(data_dir("structure"), "1l2y.pdb") - trajectory = join(data_dir("structure"), "1l2y.xtc") + template = str(data_dir("structure") / "pdb" / "1l2y.pdb") + trajectory = data_dir("structure") / "pdb" / "1l2y.xtc" stack = strucio.load_structure(trajectory, template) assert isinstance(stack, struc.AtomArrayStack) assert len(stack) > 1 @@ -50,8 +50,8 @@ def test_loading_with_extra_args(): Check if :func:`load_structure()` witt optional arguments does not raise an exception and returns an object of appropriate type. """ - template = join(data_dir("structure"), "1l2y.pdb") - trajectory = join(data_dir("structure"), "1l2y.xtc") + template = str(data_dir("structure") / "pdb" / "1l2y.pdb") + trajectory = data_dir("structure") / "pdb" / "1l2y.xtc" # test if arguments are passed to text files as get_structure arg structure = strucio.load_structure(template, extra_fields=["b_factor"]) @@ -74,26 +74,24 @@ def test_loading_with_extra_args(): "suffix", ["pdb", "pdbx", "cif", "bcif", "gro", "trr", "xtc", "dcd", "netcdf"], ) -def test_saving(suffix): +def test_saving(suffix, tmp_path): """ Check if loading a structure from a file written via :func:`save_structure()` gives the same result as the input to :func:`save_structure()`. """ - path = join(data_dir("structure"), "1l2y.bcif") + path = data_dir("structure") / "pdb" / "1l2y.bcif" ref_array = strucio.load_structure(path) if suffix in ("trr", "xtc", "dcd", "netcdf"): # Reading a trajectory file requires a template - template = path + template = str(path) else: template = None - temp = NamedTemporaryFile("w", suffix=f".{suffix}", delete=False) - temp.close() - strucio.save_structure(temp.name, ref_array) + out_path = tmp_path / f"test.{suffix}" + strucio.save_structure(out_path, ref_array) - test_array = strucio.load_structure(temp.name, template) - os.remove(temp.name) + test_array = strucio.load_structure(out_path, template) for category in ref_array.get_annotation_categories(): if category == "chain_id" and suffix == "gro": @@ -112,32 +110,29 @@ def test_saving(suffix): "suffix", ["pdb", "pdbx", "cif", "bcif", "gro", "trr", "xtc", "dcd", "netcdf"], ) -def test_saving_with_extra_args(suffix): +def test_saving_with_extra_args(suffix, tmp_path): """ Test if giving a wrong optional parameter to :func:`save_structure()` raises a :class:`TypeError` """ - array = strucio.load_structure(join(data_dir("structure"), "1l2y.bcif")) - temp = NamedTemporaryFile("w+", suffix=f".{suffix}") + array = strucio.load_structure(data_dir("structure") / "pdb" / "1l2y.bcif") + out_path = tmp_path / f"test.{suffix}" with pytest.raises(TypeError): - strucio.save_structure(temp.name, array, answer=42) - temp.close() + strucio.save_structure(out_path, array, answer=42) @pytest.mark.parametrize("format", [".mol", ".sdf"]) -def test_small_molecule(format): +def test_small_molecule(format, tmp_path): """ Check if loading a small molecule file written via :func:`save_structure()` gives the same result as the input to :func:`save_structure()`. """ - path = join(data_dir("structure"), "molecules", "TYR.sdf") + path = data_dir("structure") / "molecules" / "TYR.sdf" ref_array = strucio.load_structure(path) - temp = NamedTemporaryFile("w", suffix=format, delete=False) - strucio.save_structure(temp.name, ref_array) - temp.close() + out_path = tmp_path / f"test{format}" + strucio.save_structure(out_path, ref_array) - test_array = strucio.load_structure(temp.name) - os.remove(temp.name) + test_array = strucio.load_structure(out_path) assert test_array == ref_array diff --git a/tests/structure/test_geometry.py b/tests/structure/test_geometry.py index d7ff1c003..46f47e4be 100644 --- a/tests/structure/test_geometry.py +++ b/tests/structure/test_geometry.py @@ -2,14 +2,10 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools import json -from os.path import join import numpy as np -import numpy.random as random import pytest import biotite.structure as struc -import biotite.structure.io as strucio import biotite.structure.io.pdbx as pdbx from tests.util import data_dir @@ -42,7 +38,9 @@ def test_dihedral(): @pytest.mark.parametrize("multi_model", [False, True]) def test_dihedral_backbone_general(multi_model): - stack = strucio.load_structure(join(data_dir("structure"), "1l2y.bcif")) + stack = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") + ) n_models = stack.stack_depth() n_res = stack.res_id[-1] @@ -74,13 +72,13 @@ def test_dihedral_backbone_consistency(multi_model): Check if the computed dihedral angles are equal to the reference computed with MDTraj. """ - with open(join(data_dir("structure"), "misc", "dihedrals.json")) as file: + with open(data_dir("structure") / "misc" / "dihedrals.json") as file: ref_dihedrals = json.load(file) ref_phi = np.array(ref_dihedrals["phi"]) ref_psi = np.array(ref_dihedrals["psi"]) ref_omega = np.array(ref_dihedrals["omega"]) - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") atoms = pdbx.get_structure(pdbx_file, model=1) if multi_model: # Use models with equal coordinates @@ -106,7 +104,7 @@ def test_dihedral_side_chain_consistency(multi_model): Check if the computed dihedral angles are equal to the reference computed with MDTraj. """ - with open(join(data_dir("structure"), "misc", "dihedrals.json")) as file: + with open(data_dir("structure") / "misc" / "dihedrals.json") as file: ref_dihedrals = json.load(file) ref_chi = np.stack( [ @@ -116,7 +114,7 @@ def test_dihedral_side_chain_consistency(multi_model): axis=-1, ) - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") atoms = pdbx.get_structure(pdbx_file, model=1) if multi_model: # Use models with equal coordinates @@ -137,10 +135,10 @@ def test_nucleotide_dihedral_consistency(multi_model): :func:`nucleotide_dihedral_backbone()` and :func:`nucleotide_dihedral_side_chain()` are equal to the reference computed with ``barnaba``. """ - with open(join(data_dir("structure"), "misc", "nucleotide_dihedrals.json")) as file: + with open(data_dir("structure") / "misc" / "nucleotide_dihedrals.json") as file: ref_dihedrals = json.load(file) - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "4p5j.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "4p5j.bcif") atoms = pdbx.get_structure(pdbx_file, model=1) if multi_model: atoms = struc.stack([atoms] * 2) @@ -171,7 +169,7 @@ def test_nucleotide_dihedrals_full_structure(): For non-nucleotide residues all angles should be NaN. """ # `4p5j` contains a non-canonical nucleotide at the terminus - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "4p5j.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "4p5j.bcif") atoms = pdbx.get_structure(pdbx_file, model=1) # This mask will also include the non-canonical nucleotide nucleotide_mask = struc.filter_nucleotides(atoms[struc.get_residue_starts(atoms)]) @@ -193,7 +191,9 @@ def test_index_distance_non_periodic(): Without PBC the result should be equal to the normal distance calculation. """ - array = strucio.load_structure(join(data_dir("structure"), "3o5r.bcif")) + array = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "3o5r.bcif"), model=1 + ) ref_dist = struc.distance( array.coord[np.newaxis, :, :], array.coord[:, np.newaxis, :] ).flatten() @@ -209,23 +209,26 @@ def test_index_distance_non_periodic(): @pytest.mark.parametrize( - "shift, angles", - itertools.product( - [np.array([10, 20, 30]), np.array([-8, 12, 28]), np.array([0, 99, 54])], - [ - np.array([90, 90, 90]), - np.array([50, 90, 90]), - np.array([90, 90, 120]), - np.array([60, 60, 60]), - ], - ), + "shift", + [np.array([10, 20, 30]), np.array([-8, 12, 28]), np.array([0, 99, 54])], +) +@pytest.mark.parametrize( + "angles", + [ + np.array([90, 90, 90]), + np.array([50, 90, 90]), + np.array([90, 90, 120]), + np.array([60, 60, 60]), + ], ) def test_index_distance_periodic(shift, angles): """ The PBC aware computation, should give the same results, irrespective of which atoms are centered in the box """ - array = strucio.load_structure(join(data_dir("structure"), "3o5r.bcif")) + array = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "3o5r.bcif"), model=1 + ) # Use a box based on the boundaries of the structure # '+1' to add a margin boundaries = np.max(array.coord, axis=0) - np.min(array.coord, axis=0) + 1 @@ -262,13 +265,15 @@ def test_index_functions(): The `index_xxx()` functions should give the same result as the corresponding `xxx` functions. """ - stack = strucio.load_structure(join(data_dir("structure"), "1l2y.bcif")) + stack = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") + ) array = stack[0] # Test for atom array, stack and raw coordinates samples = (array, stack, struc.coord(array), struc.coord(stack)) # Generate random indices - random.seed(42) - indices = random.randint(array.array_length(), size=(100, 4), dtype=int) + rng = np.random.default_rng(42) + indices = rng.integers(array.array_length(), size=(100, 4), dtype=int) for sample in samples: if isinstance(sample, np.ndarray): atoms1 = sample[..., indices[:, 0], :] diff --git a/tests/structure/test_hbond.py b/tests/structure/test_hbond.py index 834b20386..0d9e773b5 100644 --- a/tests/structure/test_hbond.py +++ b/tests/structure/test_hbond.py @@ -2,20 +2,20 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools import json -from os.path import join import numpy as np import pytest import biotite.structure as struc +import biotite.structure.io.gro as gro import biotite.structure.io.pdbx as pdbx -from biotite.structure.io import load_structure from tests.util import data_dir @pytest.fixture() def stack(request): - stack = load_structure(join(data_dir("structure"), "1l2y.bcif")) + stack = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") + ) if request.param: # Use connect_via_distances, since 1l2y has invalidly bonded # N-terminal hydrogen atoms @@ -23,21 +23,22 @@ def stack(request): return stack -@pytest.mark.parametrize( - "pdb_id, use_all_models, use_bond_list", - itertools.product(["1l2y", "1gya", "1igy"], [False, True], [False, True]), -) +@pytest.mark.parametrize("pdb_id", ["1l2y", "1gya", "1igy"]) +@pytest.mark.parametrize("use_all_models", [False, True]) +@pytest.mark.parametrize("use_bond_list", [False, True]) def test_hbond_consistency(pdb_id, use_all_models, use_bond_list): """ Compare hydrogen bond detection with MDTraj. """ # Load precomputed hydrogen bond triplets from MDTraj - with open(join(data_dir("structure"), "misc", "hbond.json")) as file: + with open(data_dir("structure") / "misc" / "hbond.json") as file: ref_data = json.load(file) key = "single_model" if not use_all_models else "all_models" ref_triplets = ref_data[pdb_id][key] - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), pdb_id + ".bcif")) + pdbx_file = pdbx.BinaryCIFFile.read( + data_dir("structure") / "pdb" / f"{pdb_id}.bcif" + ) model = None if use_all_models else 1 atoms = pdbx.get_structure(pdbx_file, model=model) @@ -170,7 +171,7 @@ def test_hbond_periodicity(translation_vector): Then the position of the periodic boundary is changed and it is expected that all hydrogen bonds are still the same """ - stack = load_structure(join(data_dir("structure"), "waterbox.gro")) + stack = gro.GROFile.read(data_dir("structure") / "waterbox.gro").get_structure() array = stack[0] ref_hbonds = struc.hbond(array, periodic=True) # Put H-bond triplets into as stack for faster comparison with diff --git a/tests/structure/test_info.py b/tests/structure/test_info.py index 63dd63bf1..2a742609c 100644 --- a/tests/structure/test_info.py +++ b/tests/structure/test_info.py @@ -2,15 +2,12 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools -from os.path import join import numpy as np import pytest import biotite.structure as struc import biotite.structure.info as strucinfo import biotite.structure.io.pdbx as pdbx from biotite.structure.info.ccd import _CCD_FILE as INTERNAL_CCD_FILE -from biotite.structure.io import load_structure from tests.util import cannot_import, data_dir @@ -33,7 +30,7 @@ def fake_ccd_path(tmp_path): @pytest.mark.parametrize( - "function, included, excluded", + ["function", "included", "excluded"], [ (strucinfo.amino_acid_names, ["ALA", "ARG", "ASN", "ASP"], ["HOH"]), (strucinfo.nucleotide_names, ["A", "C", "G", "U"], ["HOH", "ALA"]), @@ -59,7 +56,9 @@ def test_mass(): Test whether the mass of a residue is the same as the sum of the masses of its contained atoms. """ - array = load_structure(join(data_dir("structure"), "1l2y.bcif"))[0] + array = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") + )[0] _, res_names = struc.get_residues(array) water_mass = strucinfo.mass("H") * 2 + strucinfo.mass("O") # Mass of water must be subtracted @@ -86,7 +85,9 @@ def test_protor_radii(): glycosylation. This means, that none of the resulting radii should be the None. """ - array = load_structure(join(data_dir("structure"), "1gya.bcif")) + array = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1gya.bcif") + ) array = array[..., struc.filter_heavy(array)] array = array[..., struc.filter_amino_acids(array)] for res_name, atom_name in zip(array.res_name, array.atom_name): @@ -121,7 +122,7 @@ def test_link_type(): @pytest.mark.parametrize( - "residues, should_have_one_letter, exception_ratio", + ["residues", "should_have_one_letter", "exception_ratio"], [ pytest.param(strucinfo.amino_acid_names(), True, 0.5, id="amino_acid_names"), pytest.param(strucinfo.nucleotide_names(), True, 0.4, id="nucleotide_names"), @@ -200,11 +201,12 @@ def test_ion_names_in_periodic_table(): ) -@pytest.mark.parametrize( - "multi_model, seed", itertools.product([False, True], range(10)) -) +@pytest.mark.parametrize("multi_model", [False, True]) +@pytest.mark.parametrize("seed", range(10)) def test_standardize_order(multi_model, seed): - original = load_structure(join(data_dir("structure"), "1l2y.bcif")) + original = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") + ) if not multi_model: original = original[0] # The box is not preserved when concatenating atom arrays later @@ -212,14 +214,14 @@ def test_standardize_order(multi_model, seed): original.box = None # Randomly reorder the atoms in each residue - np.random.seed(seed) + rng = np.random.default_rng(seed) if multi_model: reordered = struc.AtomArrayStack(original.stack_depth(), 0) else: reordered = struc.AtomArray(0) for residue in struc.residue_iter(original): bound = residue.array_length() - indices = np.random.choice(np.arange(bound), bound, replace=False) + indices = rng.choice(np.arange(bound), bound, replace=False) reordered += residue[..., indices] # Restore the original PDB standard order @@ -246,7 +248,7 @@ def test_set_ccd_path(fake_ccd_path): @pytest.mark.parametrize( - "res_name, allow_missing_coord", + ["res_name", "allow_missing_coord"], [ ("ALA", False), ("A1IQW", True), diff --git a/tests/structure/test_integrity.py b/tests/structure/test_integrity.py index af2c14bde..b4ee1303c 100644 --- a/tests/structure/test_integrity.py +++ b/tests/structure/test_integrity.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import numpy as np import pytest import biotite.structure as struc @@ -12,7 +11,7 @@ @pytest.fixture def sample_array(): - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") return pdbx.get_structure(pdbx_file, model=1) diff --git a/tests/structure/test_mechanics.py b/tests/structure/test_mechanics.py index 34c5d23c3..8e1f76e98 100644 --- a/tests/structure/test_mechanics.py +++ b/tests/structure/test_mechanics.py @@ -1,12 +1,13 @@ -from os.path import join import pytest import biotite.structure as struc -import biotite.structure.io as strucio +import biotite.structure.io.pdbx as pdbx from tests.util import data_dir def test_gyration_radius(): - stack = strucio.load_structure(join(data_dir("structure"), "1l2y.bcif")) + stack = pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") + ) radii = struc.gyration_radius(stack) # Compare with results from MDTraj exp_radii = [ diff --git a/tests/structure/test_molecules.py b/tests/structure/test_molecules.py index 6880cd8cd..585339c17 100644 --- a/tests/structure/test_molecules.py +++ b/tests/structure/test_molecules.py @@ -26,15 +26,15 @@ def array(): ] N_MOLECULES = 20 - np.random.seed(0) + rng = np.random.default_rng(0) atom_array = struc.AtomArray(0) - for i, mol_name in enumerate(np.random.choice(MOL_NAMES, N_MOLECULES)): + for i, mol_name in enumerate(rng.choice(MOL_NAMES, N_MOLECULES)): molecule = info.residue(mol_name) molecule.res_id[:] = i + 1 atom_array += molecule - reordered_indices = np.random.choice( + reordered_indices = rng.choice( np.arange(atom_array.array_length()), atom_array.array_length(), replace=False ) atom_array = atom_array[reordered_indices] @@ -43,7 +43,7 @@ def array(): @pytest.mark.parametrize( - "as_stack, as_bonds", [(False, False), (True, False), (False, True)] + ["as_stack", "as_bonds"], [(False, False), (True, False), (False, True)] ) def test_get_molecule_indices(array, as_stack, as_bonds): """ @@ -74,7 +74,7 @@ def test_get_molecule_indices(array, as_stack, as_bonds): @pytest.mark.parametrize( - "as_stack, as_bonds", [(False, False), (True, False), (False, True)] + ["as_stack", "as_bonds"], [(False, False), (True, False), (False, True)] ) def test_get_molecule_masks(array, as_stack, as_bonds): """ diff --git a/tests/structure/test_pseudoknots.py b/tests/structure/test_pseudoknots.py index b263db5af..2eaeb8151 100644 --- a/tests/structure/test_pseudoknots.py +++ b/tests/structure/test_pseudoknots.py @@ -3,11 +3,10 @@ # information. import json -from os.path import join import numpy as np import pytest import biotite.structure as struc -import biotite.structure.io as strucio +import biotite.structure.io.pdbx as pdbx from tests.util import data_dir @@ -16,7 +15,9 @@ def nuc_sample_array(): """ Sample structure for pseudoknot detection. """ - return strucio.load_structure(join(data_dir("structure"), "4p5j.cif")) + return pdbx.get_structure( + pdbx.CIFFile.read(data_dir("structure") / "pdb" / "4p5j.cif"), model=1 + ) def test_pseudoknots(nuc_sample_array): @@ -63,13 +64,11 @@ def load_test(name): Load sample base pair arrays and reference solutions from file. """ # Base pairs as numpy array (input for `pseudoknots()`) - with open( - join(data_dir("structure"), "pseudoknots", f"{name}_knotted.json"), "r" - ) as f: + with open(data_dir("structure") / "pseudoknots" / f"{name}_knotted.json", "r") as f: basepairs = np.array(json.load(f)) # List of solutions (set of tuples) with open( - join(data_dir("structure"), "pseudoknots", f"{name}_unknotted.json"), "rb" + data_dir("structure") / "pseudoknots" / f"{name}_unknotted.json", "rb" ) as f: solutions = json.load(f) for i, solution in enumerate(solutions): @@ -122,9 +121,9 @@ def test_pseudoknot_orders(seed): pairs. """ # Generate Random set of basepairs - np.random.seed(seed) + rng = np.random.default_rng(seed) bases = range(100) - basepairs = np.random.choice(bases, size=(20, 2), replace=False) + basepairs = rng.choice(bases, size=(20, 2), replace=False) # Get pseudoknot order for each basepair solutions = struc.pseudoknots(basepairs) diff --git a/tests/structure/test_rdf.py b/tests/structure/test_rdf.py index eb651b027..bc6f0d313 100644 --- a/tests/structure/test_rdf.py +++ b/tests/structure/test_rdf.py @@ -1,14 +1,12 @@ import json -from os.path import join import numpy as np import pytest import biotite.structure.io.gro as gro from biotite.structure.box import vectors_from_unitcell -from biotite.structure.io import load_structure from biotite.structure.rdf import rdf from tests.util import data_dir -TEST_FILE = join(data_dir("structure"), "waterbox.gro") +TEST_FILE = data_dir("structure") / "waterbox.gro" def test_rdf_consistency(): @@ -19,7 +17,7 @@ def test_rdf_consistency(): N_BINS = 100 # Load precomputed RDF from MDTraj - with open(join(data_dir("structure"), "misc", "rdf.json")) as file: + with open(data_dir("structure") / "misc" / "rdf.json") as file: ref_data = json.load(file) ref_bins = ref_data["bins"] ref_g_r = ref_data["g_r"] @@ -40,7 +38,7 @@ def test_rdf_bins(): """ Test if RDF produce correct bin ranges. """ - stack = load_structure(TEST_FILE) + stack = gro.GROFile.read(TEST_FILE).get_structure() center = stack[:, 0] num_bins = 44 bin_range = (0, 11.7) @@ -54,7 +52,7 @@ def test_rdf_with_selection(): """ Test if the selection argument of rdf function works as expected. """ - stack = load_structure(TEST_FILE) + stack = gro.GROFile.read(TEST_FILE).get_structure() # calculate oxygen RDF for water with and without a selection oxygen = stack[:, stack.atom_name == "OW"] @@ -86,7 +84,7 @@ def test_rdf_atom_argument(): """ Test if the first argument allows using AtomArrayStack. """ - stack = load_structure(TEST_FILE) + stack = gro.GROFile.read(TEST_FILE).get_structure() # calculate oxygen RDF for water with and without a selection oxygen = stack[:, stack.atom_name == "OW"] @@ -106,7 +104,7 @@ def test_rdf_multiple_center(): """ Test if the first argument allows using multiple centers. """ - stack = load_structure(TEST_FILE) + stack = gro.GROFile.read(TEST_FILE).get_structure() # calculate oxygen RDF for water with and without a selection oxygen = stack[:, stack.atom_name == "OW"] @@ -146,7 +144,7 @@ def test_rdf_box(): """ Test correct use of simulation boxes. """ - stack = load_structure(TEST_FILE) + stack = gro.GROFile.read(TEST_FILE).get_structure() box = vectors_from_unitcell(1, 1, 1, 90, 90, 90) box_stack = np.repeat(box[np.newaxis, :, :], len(stack), axis=0) @@ -176,7 +174,7 @@ def test_rdf_normalized(): Assert that the RDF tail is normalized to 1. """ test_file = TEST_FILE - stack = load_structure(test_file) + stack = gro.GROFile.read(test_file).get_structure() # calculate oxygen RDF for water oxygen = stack[:, stack.atom_name == "OW"] diff --git a/tests/structure/test_repair.py b/tests/structure/test_repair.py index c55ec682c..02b45831b 100644 --- a/tests/structure/test_repair.py +++ b/tests/structure/test_repair.py @@ -2,7 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import numpy as np import pytest import biotite.structure as struc @@ -12,7 +11,7 @@ @pytest.fixture def single_chain(): - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") return pdbx.get_structure(pdbx_file, model=1) @@ -48,7 +47,7 @@ def test_create_continuous_res_ids(multi_chain, restart_each_chain): @pytest.mark.parametrize("as_atom_array", [False, True]) @pytest.mark.parametrize( - "elements,expected", + ["elements", "expected"], [ (["C", "C", "O"], ["C1", "C2", "O1"]), # Single atoms are named after their element @@ -74,7 +73,7 @@ def test_create_atom_names(elements, expected, as_atom_array): @pytest.mark.filterwarnings("ignore:Could not infer element") @pytest.mark.parametrize( - "name,expected", + ["name", "expected"], [ ("CA", "C"), ("C", "C"), diff --git a/tests/structure/test_residues.py b/tests/structure/test_residues.py index 4546ffbe4..39aead932 100644 --- a/tests/structure/test_residues.py +++ b/tests/structure/test_residues.py @@ -2,17 +2,18 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -from os.path import join import numpy as np import pytest import biotite.structure as struc -import biotite.structure.io as strucio +import biotite.structure.io.pdbx as pdbx from tests.util import data_dir @pytest.fixture def array(): - return strucio.load_structure(join(data_dir("structure"), "1l2y.bcif"))[0] + return pdbx.get_structure( + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") + )[0] def test_apply_residue_wise(array): @@ -28,8 +29,8 @@ def test_spread_residue_wise(array): def test_get_residue_masks(array): SAMPLE_SIZE = 100 - np.random.seed(0) - indices = np.random.randint(0, array.array_length(), SAMPLE_SIZE) + rng = np.random.default_rng(0) + indices = rng.integers(0, array.array_length(), SAMPLE_SIZE) masks = struc.get_residue_masks(array, indices) for index, mask in zip(indices, masks): ref_mask = array.res_id == array.res_id[index] @@ -38,8 +39,8 @@ def test_get_residue_masks(array): def test_get_residue_starts_for(array): SAMPLE_SIZE = 100 - np.random.seed(0) - indices = np.random.randint(0, array.array_length(), SAMPLE_SIZE) + rng = np.random.default_rng(0) + indices = rng.integers(0, array.array_length(), SAMPLE_SIZE) ref_starts = np.array( [np.where(mask)[0][0] for mask in struc.get_residue_masks(array, indices)] ) diff --git a/tests/structure/test_rings.py b/tests/structure/test_rings.py index 63bb334ce..fcaacdcd2 100644 --- a/tests/structure/test_rings.py +++ b/tests/structure/test_rings.py @@ -1,6 +1,4 @@ -import itertools from enum import IntEnum -from pathlib import Path import numpy as np import pytest import biotite.structure as struc @@ -15,14 +13,13 @@ def riboswitch_structure(): Get a nucleotide structure with a complex fold, to include a variety of aromatic ring interactions. """ - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "4gxy.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "4gxy.bcif") atoms = pdbx.get_structure(pdbx_file, model=1, include_bonds=True) atoms = atoms[struc.filter_nucleotides(atoms)] # The CCD does not flag the 6-cycles in nucleobases as aromatic -> correct this aromatic_atom_names = [ - element + str(number) - for element, number in itertools.product(["C", "N"], range(1, 10)) + f"{element}{number}" for element in ("C", "N") for number in range(1, 10) ] bond_array = atoms.bonds.as_array() # Convert single and double bonds between those atoms into aromatic bonds @@ -36,7 +33,7 @@ def riboswitch_structure(): @pytest.mark.parametrize( - "res_name, ref_ring_members", + ["res_name", "ref_ring_members"], [ # No aromatic rings at all ("ALA", []), @@ -92,7 +89,7 @@ def test_find_known_aromatic_rings(res_name, ref_ring_members): @pytest.mark.parametrize( - "stacking_type, included_interactions, excluded_interactions", + ["stacking_type", "included_interactions", "excluded_interactions"], [ ( struc.PiStacking.PARALLEL, @@ -196,7 +193,7 @@ def test_find_pi_cation_interactions(): Uses PDB 3wip known to have pi-cation interactions between tryptophan/tyrosine residues and acetylcholine (ACH) ligand. """ - pdbx_file = pdbx.CIFFile.read(Path(data_dir("structure")) / "3wip.cif") + pdbx_file = pdbx.CIFFile.read(data_dir("structure") / "pdb" / "3wip.cif") atoms = pdbx.get_structure( pdbx_file, model=1, include_bonds=True, extra_fields=["charge"] ) diff --git a/tests/structure/test_sasa.py b/tests/structure/test_sasa.py index 88900b0c8..d11bb6064 100644 --- a/tests/structure/test_sasa.py +++ b/tests/structure/test_sasa.py @@ -3,7 +3,6 @@ # information. import json -from os.path import join import numpy as np import pytest import biotite.structure as struc @@ -18,11 +17,11 @@ def test_sasa_consistency(pdb_id): Check that SASA computation for a single model reproduces results from MDTraj. """ # Load precomputed hydrogen bond triplets from MDTraj - with open(join(data_dir("structure"), "misc", "sasa.json")) as file: + with open(data_dir("structure") / "misc" / "sasa.json") as file: ref_data = json.load(file) ref_sasa = np.array(ref_data[pdb_id]) - file = pdb.PDBFile.read(join(data_dir("structure"), pdb_id + ".pdb")) + file = pdb.PDBFile.read(data_dir("structure") / "pdb" / f"{pdb_id}.pdb") array = file.get_structure(model=1) test_sasa = struc.sasa(array, vdw_radii="Single", point_number=5000) @@ -40,7 +39,7 @@ def test_sasa_consistency(pdb_id): def test_coarse_grained(pdb_id): # Multi atom SASA (ProtOr), compare with single atom SASA # on residue level - file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), pdb_id + ".bcif")) + file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / f"{pdb_id}.bcif") array = pdbx.get_structure(file, model=1) array = array[struc.filter_amino_acids(array)] sasa = struc.apply_residue_wise( diff --git a/tests/structure/test_sequence.py b/tests/structure/test_sequence.py index f85c6db0d..baea32f2d 100644 --- a/tests/structure/test_sequence.py +++ b/tests/structure/test_sequence.py @@ -2,8 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob -from os.path import join import numpy as np import pytest import biotite.sequence.align as align @@ -12,13 +10,17 @@ from tests.util import data_dir -@pytest.mark.parametrize("path", glob.glob(join(data_dir("structure"), "*.bcif"))) +@pytest.mark.parametrize( + "path", + sorted((data_dir("structure") / "pdb").glob("*.bcif")), + ids=lambda path: path.name, +) def test_pdbx_sequence_consistency(path): """ Check if sequences created with :func:`to_sequence()` are equal to the ones already stored in the PDBx file. """ - if "4gxy" in path: + if "4gxy" in path.name: pytest.skip( "Edge case: contains 'GTP' which has one-letter code, " "but is a 'NON-POLYMER' in the CCD" diff --git a/tests/structure/test_sse.py b/tests/structure/test_sse.py index 543175ff3..7c2c8aa70 100644 --- a/tests/structure/test_sse.py +++ b/tests/structure/test_sse.py @@ -2,8 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob -from os.path import join import numpy as np import pytest import biotite.sequence.io.fasta as fasta @@ -23,13 +21,13 @@ def test_sse(): matches = 0 total = 0 - ref_psea_file = fasta.FastaFile.read(join(data_dir("structure"), "psea.fasta")) + ref_psea_file = fasta.FastaFile.read(data_dir("structure") / "psea.fasta") for pdb_id in ref_psea_file: ref_sse = np.array(list(ref_psea_file[pdb_id])) atoms = pdbx.get_structure( - pdbx.BinaryCIFFile.read(join(data_dir("structure"), f"{pdb_id}.bcif")), + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / f"{pdb_id}.bcif"), model=1, ) atoms = atoms[struc.filter_canonical_amino_acids(atoms)] @@ -46,10 +44,9 @@ def test_sse(): assert matches / total >= THRESHOLD -np.random.seed(0) - - -@pytest.mark.parametrize("discont_pos", np.random.randint(2, 105, size=100)) +@pytest.mark.parametrize( + "discont_pos", np.random.default_rng(0).integers(2, 105, size=100), ids=str +) def test_sse_discontinuity(discont_pos): """ Check if discontinuities are properly handled by inserting a @@ -57,7 +54,7 @@ def test_sse_discontinuity(discont_pos): proximity becomes 'coil'. """ atoms = pdbx.get_structure( - pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1gya.bcif")), model=1 + pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1gya.bcif"), model=1 ) atoms = atoms[struc.filter_canonical_amino_acids(atoms)] @@ -84,7 +81,11 @@ def test_sse_discontinuity(discont_pos): assert (test_sse[discont_proximity] == "c").all() -@pytest.mark.parametrize("file_name", glob.glob(join(data_dir("structure"), "*.bcif"))) +@pytest.mark.parametrize( + "file_name", + sorted((data_dir("structure") / "pdb").glob("*.bcif")), + ids=lambda path: path.name, +) def test_sse_non_peptide(file_name): """ Test whether only amino acids get SSE annotated. diff --git a/tests/structure/test_superimpose.py b/tests/structure/test_superimpose.py index c2562bd71..6f1d7f412 100755 --- a/tests/structure/test_superimpose.py +++ b/tests/structure/test_superimpose.py @@ -2,19 +2,15 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import glob -import itertools -from os.path import join import numpy as np import pytest import biotite.structure as struc -import biotite.structure.io as strucio +import biotite.structure.io.pdbx as pdbx from tests.util import data_dir -@pytest.mark.parametrize( - "seed, multi_model", itertools.product(range(10), [False, True]) -) +@pytest.mark.parametrize("seed", range(10)) +@pytest.mark.parametrize("multi_model", [False, True]) def test_restoration(seed, multi_model): """ Check if randomly relocated coordinates can be restored to their @@ -23,15 +19,15 @@ def test_restoration(seed, multi_model): N_MODELS = 10 N_COORD = 100 - np.random.seed(seed) + rng = np.random.default_rng(seed) if multi_model: - ref_coord = np.random.rand(N_MODELS, N_COORD, 3) + ref_coord = rng.random((N_MODELS, N_COORD, 3)) else: - ref_coord = np.random.rand(N_COORD, 3) - ref_coord = _transform_random_affine(ref_coord) + ref_coord = rng.random((N_COORD, 3)) + ref_coord = _transform_random_affine(ref_coord, rng) # Try to restore original coordinates after random relocation - test_coord = _transform_random_affine(ref_coord) + test_coord = _transform_random_affine(ref_coord, rng) test_coord, _ = struc.superimpose(ref_coord, test_coord) assert test_coord.flatten().tolist() == pytest.approx( @@ -50,8 +46,8 @@ def test_rotation_matrix(): # A rotation matrix that rotates 90 degrees around the z-axis ref_rotation = np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) - np.random.seed(0) - original_coord = np.random.rand(N_COORD, 3) + rng = np.random.default_rng(0) + original_coord = rng.random((N_COORD, 3)) # Rotate about 90 degrees around z-axis rotated_coord = struc.rotate(original_coord, angles=(0, 0, np.pi / 2)) _, transform = struc.superimpose(rotated_coord, original_coord) @@ -63,9 +59,11 @@ def test_rotation_matrix(): @pytest.mark.parametrize( - "path, coord_only", - itertools.product(glob.glob(join(data_dir("structure"), "*.bcif")), [False, True]), + "path", + sorted((data_dir("structure") / "pdb").glob("*.bcif")), + ids=lambda path: path.name, ) +@pytest.mark.parametrize("coord_only", [False, True]) def test_superimposition_array(path, coord_only): """ Take a structure and rotate and translate a copy of it, so that they @@ -73,7 +71,7 @@ def test_superimposition_array(path, coord_only): Then superimpose these structure onto each other and expect an almost perfect match. """ - fixed = strucio.load_structure(path, model=1) + fixed = pdbx.get_structure(pdbx.BinaryCIFFile.read(path), model=1) mobile = fixed.copy() mobile = struc.rotate(mobile, (1, 2, 3)) @@ -103,8 +101,8 @@ def test_superimposition_stack(ca_only): (optimally) superimposed onto each other. Then superimpose and expect an improved RMSD. """ - path = join(data_dir("structure"), "1l2y.bcif") - stack = strucio.load_structure(path) + path = data_dir("structure") / "pdb" / "1l2y.bcif" + stack = pdbx.get_structure(pdbx.BinaryCIFFile.read(path)) fixed = stack[0] mobile = stack[1:] if ca_only: @@ -133,14 +131,14 @@ def test_masked_superimposition(seed): Since two atoms can be superimposed perfectly, the distance between the atom in both models should be 0. """ - path = join(data_dir("structure"), "1l2y.bcif") - fixed = strucio.load_structure(path, model=1) - mobile = strucio.load_structure(path, model=2) + path = data_dir("structure") / "pdb" / "1l2y.bcif" + fixed = pdbx.get_structure(pdbx.BinaryCIFFile.read(path), model=1) + mobile = pdbx.get_structure(pdbx.BinaryCIFFile.read(path), model=2) # Create random mask for a single atom - np.random.seed(seed) + rng = np.random.default_rng(seed) mask = np.full(fixed.array_length(), False) - mask[np.random.randint(fixed.array_length())] = True + mask[rng.integers(fixed.array_length())] = True # The distance between the atom in both models should not be # already 0 prior to superimposition @@ -155,17 +153,16 @@ def test_masked_superimposition(seed): struc.distance(fixed[mask], fitted[mask])[0] == pytest.approx(0, abs=5e-4) -@pytest.mark.parametrize( - "single_model, single_atom", itertools.product([False, True], [False, True]) -) +@pytest.mark.parametrize("single_model", [False, True]) +@pytest.mark.parametrize("single_atom", [False, True]) def test_input_shapes(single_model, single_atom): """ Test whether :func:`superimpose()` infers the correct output shape, even if the input :class:`AtomArrayStack` contains only a single model or a single atom. """ - path = join(data_dir("structure"), "1l2y.bcif") - stack = strucio.load_structure(path) + path = data_dir("structure") / "pdb" / "1l2y.bcif" + stack = pdbx.get_structure(pdbx.BinaryCIFFile.read(path)) fixed = stack[0] mobile = stack @@ -194,16 +191,16 @@ def test_outlier_detection(seed): NOISE = 0.1 OUTLIER_MAGNITUDE = 100 - np.random.seed(seed) - fixed_coord = np.random.rand(N_COORD, 3) + rng = np.random.default_rng(seed) + fixed_coord = rng.random((N_COORD, 3)) # Add a little bit of noise - mobile_coord = fixed_coord + np.random.rand(N_COORD, 3) * NOISE + mobile_coord = fixed_coord + rng.random((N_COORD, 3)) * NOISE # Make some coordinates outliers - ref_outlier_mask = np.random.choice( + ref_outlier_mask = rng.choice( [False, True], size=N_COORD, p=[1 - P_OUTLIERS, P_OUTLIERS] ) mobile_coord[ref_outlier_mask] += OUTLIER_MAGNITUDE - mobile_coord = _transform_random_affine(mobile_coord) + mobile_coord = _transform_random_affine(mobile_coord, rng) superimposed_coord, _, anchors = struc.superimpose_without_outliers( # Increase the threshold a bit, @@ -225,16 +222,15 @@ def test_outlier_detection(seed): ) -@pytest.mark.parametrize( - "multi_model, coord_only", itertools.product([False, True], [False, True]) -) +@pytest.mark.parametrize("multi_model", [False, True]) +@pytest.mark.parametrize("coord_only", [False, True]) def test_superimpose_without_outliers_inputs(multi_model, coord_only): """ Ensure that :meth:`superimpose_without_outliers()` is able to handle single models, multiple models and pure coordinates. """ - path = join(data_dir("structure"), "1l2y.bcif") - atoms = strucio.load_structure(path) + path = data_dir("structure") / "pdb" / "1l2y.bcif" + atoms = pdbx.get_structure(pdbx.BinaryCIFFile.read(path)) if not multi_model: atoms = atoms[0] if coord_only: @@ -256,7 +252,7 @@ def test_superimpose_without_outliers_inputs(multi_model, coord_only): @pytest.mark.parametrize( - "pdb_id, chain_id, as_stack", + ["pdb_id", "chain_id", "as_stack"], [ ("1aki", "A", False), # is a protein ("1aki", "A", True), @@ -274,17 +270,17 @@ def test_superimpose_homologs(pdb_id, chain_id, as_stack): RMSD should be 0. """ P_CONSERVATION = 0.9 - path = join(data_dir("structure"), f"{pdb_id}.bcif") - atoms = strucio.load_structure(path) + path = data_dir("structure") / "pdb" / f"{pdb_id}.bcif" + atoms = pdbx.get_structure(pdbx.BinaryCIFFile.read(path), model=1) atoms = atoms[atoms.chain_id == chain_id] if as_stack: atoms = struc.stack([atoms]) # Delete random residues - np.random.seed(0) - fixed_atoms = _delete_random_residues(atoms, P_CONSERVATION) - mobile_atoms = _delete_random_residues(atoms, P_CONSERVATION) - mobile_atoms.coord = _transform_random_affine(mobile_atoms.coord) + rng = np.random.default_rng(0) + fixed_atoms = _delete_random_residues(atoms, P_CONSERVATION, rng) + mobile_atoms = _delete_random_residues(atoms, P_CONSERVATION, rng) + mobile_atoms.coord = _transform_random_affine(mobile_atoms.coord, rng) super_atoms, _, fix_anchors, mob_anchors = struc.superimpose_homologs( fixed_atoms, mobile_atoms @@ -304,15 +300,15 @@ def test_superimpose_homologs(pdb_id, chain_id, as_stack): ) == pytest.approx(0, abs=1e-3) -def _transform_random_affine(coord): - coord = struc.translate(coord, np.random.rand(3)) - coord = struc.rotate(coord, np.random.uniform(low=0, high=2 * np.pi, size=3)) +def _transform_random_affine(coord, rng): + coord = struc.translate(coord, rng.random(3)) + coord = struc.rotate(coord, rng.uniform(low=0, high=2 * np.pi, size=3)) return coord -def _delete_random_residues(atoms, p_conservation): +def _delete_random_residues(atoms, p_conservation, rng): residue_starts = struc.get_residue_starts(atoms) - conserved_residue_starts = np.random.choice( + conserved_residue_starts = rng.choice( residue_starts, size=int(p_conservation * len(residue_starts)), replace=False ) conservation_mask = np.any( diff --git a/tests/structure/test_tm.py b/tests/structure/test_tm.py index e39bddcef..8dc93736a 100644 --- a/tests/structure/test_tm.py +++ b/tests/structure/test_tm.py @@ -1,5 +1,4 @@ import json -from pathlib import Path import numpy as np import pytest import biotite.structure as struc @@ -27,7 +26,7 @@ def test_tm_score_perfect(reference_length): Test different reference lengths here as well. """ - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1l2y.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") atoms = pdbx.get_structure(pdbx_file, model=1) ca_indices = np.where(atoms.atom_name == "CA")[0] @@ -42,10 +41,12 @@ def test_tm_score_consistency(pdb_id): To decouple TM-score calculation from :func:`superimpose_structural_homologs()`, the TM-score is calculated for two models of the same length. """ - with open(Path(data_dir("structure")) / "tm" / "tm_scores.json", "r") as file: + with open(data_dir("structure") / "tm" / "tm_scores.json", "r") as file: ref_tm_scores = json.load(file)[pdb_id] - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / f"{pdb_id}.bcif") + pdbx_file = pdbx.BinaryCIFFile.read( + data_dir("structure") / "pdb" / f"{pdb_id}.bcif" + ) atoms = pdbx.get_structure(pdbx_file) atoms = atoms[:, struc.filter_amino_acids(atoms)] reference = atoms[0] @@ -73,7 +74,7 @@ def test_superimpose_identical(without_tm_gap_penalty, seed, structural_alphabet """ P_CONSERVATION = 0.8 - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1aki.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1aki.bcif") atoms = pdbx.get_structure(pdbx_file, model=1) atoms = atoms[struc.filter_amino_acids(atoms)] @@ -115,7 +116,7 @@ def test_superimpose_identical(without_tm_gap_penalty, seed, structural_alphabet @pytest.mark.parametrize( - "fixed_pdb_id, mobile_pdb_id, ref_tm_score", + ["fixed_pdb_id", "mobile_pdb_id", "ref_tm_score"], [ ("1p4k", "4osx", 0.87), ("3lsj", "3rd3", 0.78), @@ -142,10 +143,10 @@ def test_superimpose_consistency(fixed_pdb_id, mobile_pdb_id, ref_tm_score): SCORE_TOLERANCE = 0.05 fixed = _get_peptide_assembly( - Path(data_dir("structure")) / "homologs" / f"{fixed_pdb_id}.bcif" + data_dir("structure") / "homologs" / f"{fixed_pdb_id}.bcif" ) mobile = _get_peptide_assembly( - Path(data_dir("structure")) / "homologs" / f"{mobile_pdb_id}.bcif" + data_dir("structure") / "homologs" / f"{mobile_pdb_id}.bcif" ) super, _, fix_indices, mob_indices = struc.superimpose_structural_homologs( @@ -167,7 +168,7 @@ def test_short_structure(): before. """ - pdbx_file = pdbx.BinaryCIFFile.read(Path(data_dir("structure")) / "1l2y.bcif") + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") atoms = pdbx.get_structure(pdbx_file, model=1) atoms = atoms[atoms.res_id < 15] diff --git a/tests/structure/test_transform.py b/tests/structure/test_transform.py index 28c4d4097..3ca2afc16 100644 --- a/tests/structure/test_transform.py +++ b/tests/structure/test_transform.py @@ -2,8 +2,6 @@ # under the 3-Clause BSD License. Please see 'LICENSE.rst' for further # information. -import itertools -from os.path import join import numpy as np import pytest import biotite.structure as struc @@ -13,15 +11,12 @@ @pytest.fixture( - params=itertools.product( - [1, 2, 3], # ndim - [False, True], # as_coord - ) + params=[(ndim, as_coord) for ndim in (1, 2, 3) for as_coord in (False, True)] ) def input_atoms(request): ndim, as_coord = request.param - pdbx_file = pdbx.BinaryCIFFile.read(join(data_dir("structure"), "1l2y.bcif")) + pdbx_file = pdbx.BinaryCIFFile.read(data_dir("structure") / "pdb" / "1l2y.bcif") atoms = pdbx.get_structure(pdbx_file) if ndim == 2: @@ -46,14 +41,14 @@ def test_transform_as_matrix(): N_MODELS = 10 N_COORD = 100 - np.random.seed(0) - orig_coord = np.random.rand(N_MODELS, N_COORD, 3) + rng = np.random.default_rng(0) + orig_coord = rng.random((N_MODELS, N_COORD, 3)) transform = struc.AffineTransformation( - center_translation=np.random.rand(N_MODELS, 3), + center_translation=rng.random((N_MODELS, 3)), # This is not really a rotation matrix, # but the same maths apply - rotation=np.random.rand(N_MODELS, 3, 3), - target_translation=np.random.rand(N_MODELS, 3), + rotation=rng.random((N_MODELS, 3, 3)), + target_translation=rng.random((N_MODELS, 3)), ) ref_coord = transform.apply(orig_coord) @@ -82,8 +77,8 @@ def test_translate(input_atoms, ndim, as_list, random_seed): # as input coordinates/atoms return - np.random.seed(random_seed) - vectors = np.random.rand(*struc.coord(input_atoms).shape[-ndim:]) + rng = np.random.default_rng(random_seed) + vectors = rng.random(struc.coord(input_atoms).shape[-ndim:]) vectors *= 10 neg_vectors = -vectors if as_list: @@ -107,9 +102,9 @@ def test_rotate(input_atoms, as_list, axis, random_seed, centered): Rotate and rotate back and check if the coordinates are still the same. """ - np.random.seed(random_seed) + rng = np.random.default_rng(random_seed) angles = np.zeros(3) - angles[axis] = np.random.rand() * 2 * np.pi + angles[axis] = rng.random() * 2 * np.pi neg_angles = -angles if as_list: angles = angles.tolist() @@ -177,10 +172,10 @@ def test_rotate_measure(axis, random_seed): Rotate and measure resulting angle that should be equal to the input angle. """ - np.random.seed(random_seed) + rng = np.random.default_rng(random_seed) # Maximum rotation is only 180 degrees, # as with higher angles the measured angle would decrease again - ref_angle = np.random.rand() * np.pi + ref_angle = rng.random() * np.pi angles = np.zeros(3) angles[axis] = ref_angle @@ -205,11 +200,11 @@ def test_rotate_about_axis(input_atoms, as_list, use_support, random_seed): Rotate and rotate back and check if the coordinates are still the same. """ - np.random.seed(random_seed) - axis = np.random.rand(3) - angle = np.random.rand() + rng = np.random.default_rng(random_seed) + axis = rng.random(3) + angle = rng.random() neg_angle = -angle - support = np.random.rand(3) if use_support else None + support = rng.random(3) if use_support else None if as_list: axis = axis.tolist() support = support.tolist() if support is not None else None @@ -229,8 +224,8 @@ def test_rotate_about_axis_consistency(input_atoms, axis, random_seed): Compare the outcome of :func:`rotate_about_axis()` with :func:`rotate()`. """ - np.random.seed(random_seed) - angle = np.random.rand() * 2 * np.pi + rng = np.random.default_rng(random_seed) + angle = rng.random() * 2 * np.pi angles = np.zeros(3) angles[axis] = angle @@ -238,7 +233,7 @@ def test_rotate_about_axis_consistency(input_atoms, axis, random_seed): rot_axis = np.zeros(3) # Length of axis should be irrelevant - rot_axis[axis] = np.random.rand() + rot_axis[axis] = rng.random() test_rotated = struc.rotate_about_axis( input_atoms, rot_axis, @@ -257,9 +252,9 @@ def test_rotate_about_axis_360(input_atoms, random_seed, use_support): Rotate by 360 degrees around an arbitrary axis and expect that the coordinates have not changed. """ - np.random.seed(random_seed) - axis = np.random.rand(3) - support = np.random.rand(3) if use_support else None + rng = np.random.default_rng(random_seed) + axis = rng.random(3) + support = rng.random(3) if use_support else None rotated = struc.rotate_about_axis(input_atoms, axis, 2 * np.pi, support) @@ -318,12 +313,12 @@ def test_align_vectors(input_atoms, as_list, use_support, random_seed): Align, swap alignment vectors and align again. Expect that the original coordinates have been restored. """ - np.random.seed(random_seed) - source_direction = np.random.rand(3) - target_direction = np.random.rand(3) + rng = np.random.default_rng(random_seed) + source_direction = rng.random(3) + target_direction = rng.random(3) if use_support: - source_position = np.random.rand(3) - target_position = np.random.rand(3) + source_position = rng.random(3) + target_position = rng.random(3) else: source_position = None target_position = None @@ -359,16 +354,16 @@ def test_align_vectors_non_vector_inputs(input_atoms): """ Ensure input vectors to ``struct.align_vectors`` have the correct shape. """ - source_direction = np.random.rand(2, 3) - target_direction = np.random.rand(2, 3) + source_direction = np.random.default_rng().random((2, 3)) + target_direction = np.random.default_rng().random((2, 3)) with pytest.raises(ValueError): struc.align_vectors( input_atoms, source_direction, target_direction, ) - source_direction = np.random.rand(4) - target_direction = np.random.rand(4) + source_direction = np.random.default_rng().random(4) + target_direction = np.random.default_rng().random(4) with pytest.raises(ValueError): struc.align_vectors( input_atoms, diff --git a/tests/test_doctest.py b/tests/test_doctest.py index 3428d934d..961de5f56 100644 --- a/tests/test_doctest.py +++ b/tests/test_doctest.py @@ -7,10 +7,10 @@ import doctest import tempfile from importlib import import_module -from os.path import join +from pathlib import Path import numpy as np import pytest -import biotite.structure.io as strucio +import biotite.structure.io.pdbx as pdbx from tests.util import cannot_connect_to, cannot_import, is_not_installed NCBI_URL = "https://eutils.ncbi.nlm.nih.gov/entrez/" @@ -193,7 +193,7 @@ @pytest.mark.parametrize( - "package_name, context_package_names", + ["package_name", "context_package_names"], TEST_PARAMETERS, ids=[param.values[0] for param in TEST_PARAMETERS], ) @@ -213,13 +213,15 @@ def test_doctest(package_name, context_package_names): # Add fixed names for certain paths globs["path_to_directory"] = tempfile.gettempdir() - globs["path_to_structures"] = join(".", "tests", "structure", "data") - globs["path_to_sequences"] = join(".", "tests", "sequence", "data") + globs["path_to_structure_data"] = Path(".") / "tests" / "structure" / "data" + globs["path_to_structures"] = globs["path_to_structure_data"] / "pdb" + globs["path_to_sequences"] = Path(".") / "tests" / "sequence" / "data" # Add frequently used modules globs["np"] = np # Add frequently used objects - atoms = strucio.load_structure( - join(".", "tests", "structure", "data", "1l2y.bcif"), include_bonds=True + atoms = pdbx.get_structure( + pdbx.BinaryCIFFile.read(globs["path_to_structures"] / "1l2y.bcif"), + include_bonds=True, ) globs["atom_array_stack"] = atoms globs["atom_array"] = globs["atom_array_stack"][0] diff --git a/tests/test_modname.py b/tests/test_modname.py index 67f83d952..6c411ca4a 100644 --- a/tests/test_modname.py +++ b/tests/test_modname.py @@ -4,7 +4,7 @@ import importlib import pkgutil -from os.path import dirname, join +from pathlib import Path import pytest from tests.util import cannot_import @@ -15,7 +15,7 @@ def find_all_modules(package_name, src_dir): (Sub-)Packages are not considered as modules. """ module_names = [] - for _, module_name, is_package in pkgutil.iter_modules([src_dir]): + for _, module_name, is_package in pkgutil.iter_modules([str(src_dir)]): if module_name == "setup_ccd": # This script is not intended to be imported continue @@ -25,7 +25,7 @@ def find_all_modules(package_name, src_dir): full_module_name = f"{package_name}.{module_name}" if is_package: module_names.extend( - find_all_modules(full_module_name, join(src_dir, module_name)) + find_all_modules(full_module_name, src_dir / module_name) ) else: module_names.append(full_module_name) @@ -38,7 +38,7 @@ def find_all_modules(package_name, src_dir): ) @pytest.mark.parametrize( "module_name", - find_all_modules("biotite", join(dirname(dirname(__file__)), "src", "biotite")), + find_all_modules("biotite", Path(__file__).parent.parent / "src" / "biotite"), ) def test_module_name(module_name): """ diff --git a/tests/util.py b/tests/util.py index 99cf24741..98352e26d 100644 --- a/tests/util.py +++ b/tests/util.py @@ -6,11 +6,11 @@ import shutil import urllib.error import urllib.request -from os.path import dirname, join, realpath +from pathlib import Path def data_dir(subdir): - return join(dirname(realpath(__file__)), subdir, "data") + return Path(__file__).resolve().parent / subdir / "data" ### Functions for conditional test skips ###