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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mhctools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def __getattr__(name):
raise AttributeError(
"module %r has no attribute %r" % (__name__, name))

__version__ = "3.31.6"
__version__ = "3.31.7"

__all__ = [
"Prediction",
Expand Down
14 changes: 14 additions & 0 deletions mhctools/base_commandline_predictor.py
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,18 @@ def predict_pairs_dataframe(
return pd.DataFrame(columns=COLUMNS)
return pd.concat(dfs, ignore_index=True)

def _require_alleles(self):
"""Guard the allele-consuming predict paths. alleles is optional at
construction so a predictor can score explicit (peptide, allele) pairs
via predict_pairs(); but predict()/predict_peptides() score against
self.alleles, and with none configured they would silently return
nothing (see _allele_groups). Fail loud instead."""
if not self.alleles:
raise ValueError(
"%s was constructed without alleles; pass alleles=[...] to the "
"constructor, or use predict_pairs(peptides, alleles) to score "
"explicit (peptide, allele) pairs." % type(self).__name__)

def predict(self, peptides, n_flanks=None, c_flanks=None):
"""
Predict for a list of peptide sequences.
Expand All @@ -842,6 +854,7 @@ def predict(self, peptides, n_flanks=None, c_flanks=None):
available, parses directly to Pred objects. Otherwise falls back
to converting from BindingPrediction.
"""
self._require_alleles()
peptides, _, _ = self._check_flank_inputs(
peptides, n_flanks, c_flanks)
return self._predict_for_alleles(
Expand All @@ -850,6 +863,7 @@ def predict(self, peptides, n_flanks=None, c_flanks=None):
allele_cli_names=getattr(self, "_allele_cli_names", None))

def predict_peptides(self, peptides):
self._require_alleles()
return self._predict_binding_predictions_for_alleles(
peptides=peptides,
alleles=self.alleles,
Expand Down
7 changes: 6 additions & 1 deletion mhctools/base_predictor.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class BasePredictor(object):

def __init__(
self,
alleles,
alleles=None,
valid_alleles=None,
default_peptide_lengths=None,
min_peptide_length=8,
Expand Down Expand Up @@ -154,6 +154,11 @@ def __init__(
against the tool's own supported-allele list, so that non-human
alleles like H-2-Qa1 or BoLA-amani.1 can still be requested).
"""
# alleles is optional: a predictor built without one can still score
# explicit (peptide, allele) pairs via predict_pairs(). The
# allele-consuming predict()/predict_peptides() path guards on empty.
if alleles is None:
alleles = []
# I find myself often constructing a predictor with just one allele
# so as a convenience, allow user to not wrap that allele as a list
if type(alleles) is str:
Expand Down
2 changes: 1 addition & 1 deletion mhctools/netmhc.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from .netmhc3 import NetMHC3
from .netmhc4 import NetMHC4

def NetMHC(alleles,
def NetMHC(alleles=None,
default_peptide_lengths=[9],
program_name="netMHC"):
"""
Expand Down
2 changes: 1 addition & 1 deletion mhctools/netmhc3.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
class NetMHC3(BaseCommandlinePredictor):
def __init__(
self,
alleles,
alleles=None,
program_name="netMHC",
default_peptide_lengths=[9]):
BaseCommandlinePredictor.__init__(
Expand Down
2 changes: 1 addition & 1 deletion mhctools/netmhc4.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
class NetMHC4(BaseCommandlinePredictor):
def __init__(
self,
alleles,
alleles=None,
program_name="netMHC",
process_limit=0,
default_peptide_lengths=[9]):
Expand Down
2 changes: 1 addition & 1 deletion mhctools/netmhc_cons.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
class NetMHCcons(BaseCommandlinePredictor):
def __init__(
self,
alleles,
alleles=None,
program_name="netMHCcons",
process_limit=0,
default_peptide_lengths=[9]):
Expand Down
2 changes: 1 addition & 1 deletion mhctools/netmhc_pan.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def _parse_version(version_str):


def NetMHCpan(
alleles,
alleles=None,
program_name="netMHCpan",
process_limit=-1,
default_peptide_lengths=[9],
Expand Down
2 changes: 1 addition & 1 deletion mhctools/netmhc_pan28.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
class NetMHCpan28(BaseCommandlinePredictor):
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[9],
program_name="netMHCpan",
process_limit=-1,
Expand Down
2 changes: 1 addition & 1 deletion mhctools/netmhc_pan3.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
class NetMHCpan3(BaseCommandlinePredictor):
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[9],
program_name="netMHCpan",
process_limit=-1,
Expand Down
6 changes: 3 additions & 3 deletions mhctools/netmhc_pan4.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
class NetMHCpan4(BaseCommandlinePredictor):
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[9],
program_name="netMHCpan",
process_limit=-1,
Expand Down Expand Up @@ -78,7 +78,7 @@ class NetMHCpan4_EL(NetMHCpan4):
"""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[9],
program_name="netMHCpan",
process_limit=-1,
Expand All @@ -98,7 +98,7 @@ class NetMHCpan4_BA(NetMHCpan4):
"""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[9],
program_name="netMHCpan",
process_limit=-1,
Expand Down
6 changes: 3 additions & 3 deletions mhctools/netmhc_pan41.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
class NetMHCpan41(BaseCommandlinePredictor):
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[9],
program_name="netMHCpan",
process_limit=-1,
Expand Down Expand Up @@ -80,7 +80,7 @@ class NetMHCpan41_EL(NetMHCpan41):
"""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[9],
program_name="netMHCpan",
process_limit=-1,
Expand All @@ -101,7 +101,7 @@ class NetMHCpan41_BA(NetMHCpan41):
"""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[9],
program_name="netMHCpan",
process_limit=-1,
Expand Down
6 changes: 3 additions & 3 deletions mhctools/netmhc_pan42.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
class NetMHCpan42(BaseCommandlinePredictor):
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[9],
program_name="netMHCpan",
process_limit=-1,
Expand Down Expand Up @@ -79,7 +79,7 @@ class NetMHCpan42_EL(NetMHCpan42):
"""NetMHCpan 4.2 in elution score mode."""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[9],
program_name="netMHCpan",
process_limit=-1,
Expand All @@ -98,7 +98,7 @@ class NetMHCpan42_BA(NetMHCpan42):
"""NetMHCpan 4.2 in binding affinity mode."""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[9],
program_name="netMHCpan",
process_limit=-1,
Expand Down
16 changes: 8 additions & 8 deletions mhctools/netmhcii_pan.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class NetMHCIIpan3(NetMHCIIpanBase):
"""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[15, 16, 17, 18, 19, 20],
program_name="netMHCIIpan",
process_limit=-1,
Expand All @@ -126,7 +126,7 @@ class NetMHCIIpan4(NetMHCIIpanBase):
"""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[15, 16, 17, 18, 19, 20],
program_name="netMHCIIpan",
process_limit=-1,
Expand Down Expand Up @@ -161,7 +161,7 @@ class NetMHCIIpan4_EL(NetMHCIIpan4):
"""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[15, 16, 17, 18, 19, 20],
program_name="netMHCIIpan",
process_limit=-1,
Expand All @@ -182,7 +182,7 @@ class NetMHCIIpan4_BA(NetMHCIIpan4):
"""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[15, 16, 17, 18, 19, 20],
program_name="netMHCIIpan",
process_limit=-1,
Expand All @@ -198,7 +198,7 @@ def __init__(


def NetMHCIIpan(
alleles,
alleles=None,
process_limit=-1,
program_name="netMHCIIpan",
default_peptide_lengths=[15, 16, 17, 18, 19, 20],
Expand Down Expand Up @@ -236,7 +236,7 @@ class NetMHCIIpan43(NetMHCIIpanBase):
"""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[15, 16, 17, 18, 19, 20],
program_name="netMHCIIpan",
process_limit=-1,
Expand Down Expand Up @@ -270,7 +270,7 @@ class NetMHCIIpan43_EL(NetMHCIIpan43):
"""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[15, 16, 17, 18, 19, 20],
program_name="netMHCIIpan",
process_limit=-1,
Expand All @@ -291,7 +291,7 @@ class NetMHCIIpan43_BA(NetMHCIIpan43):
"""
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[15, 16, 17, 18, 19, 20],
program_name="netMHCIIpan",
process_limit=-1,
Expand Down
2 changes: 1 addition & 1 deletion mhctools/netmhcstabpan.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
class NetMHCstabpan(BaseCommandlinePredictor):
def __init__(
self,
alleles,
alleles=None,
default_peptide_lengths=[],
program_name="netMHCstabpan",
process_limit=-1,
Expand Down
33 changes: 33 additions & 0 deletions tests/test_commandline_predict_pairs.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,36 @@ def test_predict_pairs_dataframe_skip_unsupported_all_unsupported_is_empty():

assert list(df.columns) == list(COLUMNS)
assert df.empty


# ---- alleles optional at construction ---------------------------------------

def test_base_predictor_alleles_optional_normalizes_to_empty():
from mhctools.base_predictor import BasePredictor

assert BasePredictor(default_peptide_lengths=[9]).alleles == []
assert BasePredictor(alleles=None, default_peptide_lengths=[9]).alleles == []
# an allele passed as a bare string still works
assert BasePredictor(
alleles="HLA-A*02:01", default_peptide_lengths=[9]).alleles == ["HLA-A*02:01"]


def test_predict_pairs_works_without_constructor_alleles():
predictor = _PairStubPredictor()
predictor.alleles = [] # built for the pairs workflow, no default alleles

results = predictor.predict_pairs(
["SIINFEKLL", "GILGFVFTL"], ["HLA-A*02:01", "HLA-B*07:02"])

assert [r.preds[0].peptide for r in results] == ["SIINFEKLL", "GILGFVFTL"]


def test_predict_without_alleles_raises_rather_than_returning_nothing():
predictor = _PairStubPredictor()
predictor.alleles = []

with pytest.raises(ValueError, match="without alleles"):
predictor.predict(["SIINFEKLL"])
with pytest.raises(ValueError, match="without alleles"):
predictor.predict_peptides(["SIINFEKLL"])
assert predictor.calls == [] # guarded before running any command
Loading