mhctools 4.4.2__tar.gz → 4.4.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. {mhctools-4.4.2/mhctools.egg-info → mhctools-4.4.4}/PKG-INFO +1 -1
  2. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/__init__.py +1 -1
  3. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/process_helpers.py +21 -9
  4. {mhctools-4.4.2 → mhctools-4.4.4/mhctools.egg-info}/PKG-INFO +1 -1
  5. {mhctools-4.4.2 → mhctools-4.4.4}/scripts/build_iedb_smm_subset.py +18 -9
  6. {mhctools-4.4.2 → mhctools-4.4.4}/scripts/setup_test_backends.py +1 -1
  7. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_mixmhcpred_runtime_integration.py +64 -4
  8. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_process_helpers.py +47 -31
  9. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_smm_subset_provenance.py +81 -20
  10. {mhctools-4.4.2 → mhctools-4.4.4}/LICENSE +0 -0
  11. {mhctools-4.4.2 → mhctools-4.4.4}/MANIFEST.in +0 -0
  12. {mhctools-4.4.2 → mhctools-4.4.4}/README.md +0 -0
  13. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/_resources.py +0 -0
  14. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/_validation.py +0 -0
  15. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/allele_normalization.py +0 -0
  16. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/annotate.py +0 -0
  17. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/artifacts.py +0 -0
  18. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/base_commandline_predictor.py +0 -0
  19. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/base_predictor.py +0 -0
  20. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/benchmark.py +0 -0
  21. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/bigmhc.py +0 -0
  22. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/binding_prediction.py +0 -0
  23. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/binding_prediction_collection.py +0 -0
  24. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/calis.py +0 -0
  25. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/caphla.py +0 -0
  26. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cavaco.py +0 -0
  27. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cleanup_context.py +0 -0
  28. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cleavage.py +0 -0
  29. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cleavage_batch.py +0 -0
  30. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cleavenet.py +0 -0
  31. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cleavenet_sidecar.py +0 -0
  32. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/__init__.py +0 -0
  33. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/annotate_table.py +0 -0
  34. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/args.py +0 -0
  35. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/artifacts.py +0 -0
  36. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/benchmark.py +0 -0
  37. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/cleavage.py +0 -0
  38. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/cleavenet.py +0 -0
  39. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/errors.py +0 -0
  40. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/integrations.py +0 -0
  41. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/mixtcrpred.py +0 -0
  42. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/parsing_helpers.py +0 -0
  43. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/script.py +0 -0
  44. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/vaccine_compare.py +0 -0
  45. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/vaccine_report.py +0 -0
  46. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/vaccine_trajectory.py +0 -0
  47. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/cli/vaccine_windows.py +0 -0
  48. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/common.py +0 -0
  49. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/CAVACO_LICENSE.txt +0 -0
  50. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/ITCELL_LICENSE.txt +0 -0
  51. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/PHAGESCOUT_LICENSE.txt +0 -0
  52. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/SERUM_CALIBRATION_LICENSE.txt +0 -0
  53. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/__init__.py +0 -0
  54. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/cavaco_published_model.json +0 -0
  55. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/cleavage_reference.json +0 -0
  56. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/dpp4_keane2011.json +0 -0
  57. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/dpp4_qpisa.json +0 -0
  58. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/fap_keane2011.json +0 -0
  59. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/intracellular_cleavage_reference.json +0 -0
  60. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/intracellular_substrate_evidence.json +0 -0
  61. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/itcell_profiles.json +0 -0
  62. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/model_lineage.json +0 -0
  63. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/phagescout_profiles.json +0 -0
  64. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/serum_calibration.json +0 -0
  65. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/serum_cleavage_reference.json +0 -0
  66. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/serum_contributions.json +0 -0
  67. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/data/vaccine_trajectory_evidence.json +0 -0
  68. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/deepimmuno.py +0 -0
  69. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/deeptap.py +0 -0
  70. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/dpp4.py +0 -0
  71. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/enzyme_assay.py +0 -0
  72. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/enzyme_predictor.py +0 -0
  73. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/eramer.py +0 -0
  74. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/eramer_cleavage.py +0 -0
  75. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/iedb.py +0 -0
  76. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/input_file_formats.py +0 -0
  77. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/integrations.py +0 -0
  78. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/itcell_cleavage.py +0 -0
  79. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/logging.py +0 -0
  80. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/mhcflurry.py +0 -0
  81. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/mixmhc2pred.py +0 -0
  82. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/mixmhcpred.py +0 -0
  83. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/mixmhcpred_runtime.py +0 -0
  84. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/mixtcrpred.py +0 -0
  85. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/mixtcrpred_sidecar.py +0 -0
  86. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netchop.py +0 -0
  87. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netcleave.py +0 -0
  88. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhc.py +0 -0
  89. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhc3.py +0 -0
  90. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhc4.py +0 -0
  91. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhc_cons.py +0 -0
  92. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhc_pan.py +0 -0
  93. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhc_pan28.py +0 -0
  94. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhc_pan3.py +0 -0
  95. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhc_pan4.py +0 -0
  96. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhc_pan41.py +0 -0
  97. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhc_pan42.py +0 -0
  98. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhcii_pan.py +0 -0
  99. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/netmhcstabpan.py +0 -0
  100. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/nettcr.py +0 -0
  101. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/optional_backend.py +0 -0
  102. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/parsing.py +0 -0
  103. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/pepsickle.py +0 -0
  104. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/pepsickle_runtime.py +0 -0
  105. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/peptidases.py +0 -0
  106. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/peptide_input.py +0 -0
  107. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/peptiverse.py +0 -0
  108. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/peptiverse_cpp.py +0 -0
  109. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/peptiverse_cpp_benchmark.py +0 -0
  110. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/peptiverse_sidecar.py +0 -0
  111. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/phagescout.py +0 -0
  112. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/phagescout_artifacts.py +0 -0
  113. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/plifepred2.py +0 -0
  114. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/plifepred2_sidecar.py +0 -0
  115. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/pred.py +0 -0
  116. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/prime.py +0 -0
  117. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/processing_predictor.py +0 -0
  118. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/proteasome_predictor.py +0 -0
  119. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/random_predictor.py +0 -0
  120. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/sample.py +0 -0
  121. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/serum_calibration.py +0 -0
  122. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/serum_contributions.py +0 -0
  123. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/serum_degradation.py +0 -0
  124. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/smm.py +0 -0
  125. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/substrate_reference.py +0 -0
  126. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/target_cleavage.py +0 -0
  127. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/tcr.py +0 -0
  128. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/tlimmuno2.py +0 -0
  129. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/tulip.py +0 -0
  130. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/unsupported_allele.py +0 -0
  131. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/vaccine_exposure.py +0 -0
  132. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/vaccine_report.py +0 -0
  133. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/vaccine_trajectory.py +0 -0
  134. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/vaccine_trajectory_report.py +0 -0
  135. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/vaccine_windows.py +0 -0
  136. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/weirdo.py +0 -0
  137. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools/wrapper_base.py +0 -0
  138. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools.egg-info/SOURCES.txt +0 -0
  139. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools.egg-info/dependency_links.txt +0 -0
  140. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools.egg-info/entry_points.txt +0 -0
  141. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools.egg-info/requires.txt +0 -0
  142. {mhctools-4.4.2 → mhctools-4.4.4}/mhctools.egg-info/top_level.txt +0 -0
  143. {mhctools-4.4.2 → mhctools-4.4.4}/pyproject.toml +0 -0
  144. {mhctools-4.4.2 → mhctools-4.4.4}/scripts/evaluate_wada_cleavage.py +0 -0
  145. {mhctools-4.4.2 → mhctools-4.4.4}/scripts/osteosarc_fixtures.py +0 -0
  146. {mhctools-4.4.2 → mhctools-4.4.4}/scripts/record_cavaco_reference.js +0 -0
  147. {mhctools-4.4.2 → mhctools-4.4.4}/scripts/record_smm_fixtures.py +0 -0
  148. {mhctools-4.4.2 → mhctools-4.4.4}/scripts/test-backends/Dockerfile.netmhc-legacy +0 -0
  149. {mhctools-4.4.2 → mhctools-4.4.4}/scripts/test-backends/Dockerfile.pepsickle-legacy +0 -0
  150. {mhctools-4.4.2 → mhctools-4.4.4}/scripts/test-backends/run_netmhc.py +0 -0
  151. {mhctools-4.4.2 → mhctools-4.4.4}/setup.cfg +0 -0
  152. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/cavaco_source_reference.json +0 -0
  153. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/itcell_source_reference.json +0 -0
  154. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/README.md +0 -0
  155. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/inputs/class_i.fasta +0 -0
  156. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/inputs/class_i.txt +0 -0
  157. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/inputs/class_ii.fasta +0 -0
  158. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/inputs/class_ii.txt +0 -0
  159. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/inputs/cleavage.fasta +0 -0
  160. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/inputs/cleavage.txt +0 -0
  161. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/inputs/cons_9mer.fasta +0 -0
  162. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/inputs/cons_9mer.txt +0 -0
  163. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/inputs/panels.json +0 -0
  164. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/inputs/source.json +0 -0
  165. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/manifest.json +0 -0
  166. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/mixmhc2pred.stderr +0 -0
  167. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/mixmhc2pred.stdout +0 -0
  168. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/mixmhc2pred.tsv +0 -0
  169. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/mixmhcpred.stderr +0 -0
  170. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/mixmhcpred.stdout +0 -0
  171. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/mixmhcpred.tsv +0 -0
  172. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netchop-20s.stderr +0 -0
  173. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netchop-20s.stdout +0 -0
  174. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netchop-cterm.stderr +0 -0
  175. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netchop-cterm.stdout +0 -0
  176. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netmhccons-a0101.stderr +0 -0
  177. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netmhccons-a0101.stdout +0 -0
  178. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netmhccons-b0801.stderr +0 -0
  179. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netmhccons-b0801.stdout +0 -0
  180. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netmhciipan.stderr +0 -0
  181. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netmhciipan.stdout +0 -0
  182. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netmhcpan.stderr +0 -0
  183. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/netmhcpan.stdout +0 -0
  184. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/prime.stderr +0 -0
  185. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/prime.stdout +0 -0
  186. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/outputs/prime.tsv +0 -0
  187. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/README.md +0 -0
  188. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/manifest.json +0 -0
  189. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/peptides-10.fasta +0 -0
  190. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/peptides-9.fasta +0 -0
  191. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/smm-10.stderr +0 -0
  192. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/smm-10.tsv +0 -0
  193. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/smm-9.stderr +0 -0
  194. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/smm-9.tsv +0 -0
  195. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/smmpmbec-10.stderr +0 -0
  196. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/smmpmbec-10.tsv +0 -0
  197. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/smmpmbec-9.stderr +0 -0
  198. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/osteosarc/smm/smmpmbec-9.tsv +0 -0
  199. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/peptiverse_cpp_source_reference.json +0 -0
  200. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/peptiverse_cpp_validation_summary.json +0 -0
  201. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/tusar2023/README.md +0 -0
  202. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/tusar2023/catl-protected-peptides.json +0 -0
  203. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/wada2018/README.md +0 -0
  204. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/wada2018/figure2ac.json +0 -0
  205. {mhctools-4.4.2 → mhctools-4.4.4}/tests/data/wada2018/pepsickle-gb-evaluation.json +0 -0
  206. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_allele_batching.py +0 -0
  207. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_annotate_table.py +0 -0
  208. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_artifacts.py +0 -0
  209. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_backend_discovery.py +0 -0
  210. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_backend_requirements.py +0 -0
  211. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_benchmark.py +0 -0
  212. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_bigmhc.py +0 -0
  213. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_bigmhc_pairs.py +0 -0
  214. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_binding_prediction.py +0 -0
  215. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_binding_prediction_collection.py +0 -0
  216. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_calis.py +0 -0
  217. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_caphla.py +0 -0
  218. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cathepsin_reference.py +0 -0
  219. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cavaco.py +0 -0
  220. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_check_results.py +0 -0
  221. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_ci_selection.py +0 -0
  222. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_class2_allele_formats.py +0 -0
  223. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cleavage.py +0 -0
  224. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cleavage_batch.py +0 -0
  225. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cleavage_mechanism_scorecards.py +0 -0
  226. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cleavenet.py +0 -0
  227. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cli.py +0 -0
  228. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cli_embedding.py +0 -0
  229. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cli_filters.py +0 -0
  230. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cli_parsing_helpers.py +0 -0
  231. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cli_registry.py +0 -0
  232. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cli_subcommands.py +0 -0
  233. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_cli_user_errors.py +0 -0
  234. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_command_log_cleanup.py +0 -0
  235. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_commandline_diagnostics.py +0 -0
  236. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_commandline_predict_pairs.py +0 -0
  237. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_dataframe_schema_parity.py +0 -0
  238. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_deepimmuno.py +0 -0
  239. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_deeptap.py +0 -0
  240. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_deploy_script.py +0 -0
  241. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_docs_links.py +0 -0
  242. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_docs_predictor_matrix.py +0 -0
  243. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_docs_version.py +0 -0
  244. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_dpp4.py +0 -0
  245. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_enzyme_assay.py +0 -0
  246. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_enzyme_predictor.py +0 -0
  247. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_eramer.py +0 -0
  248. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_eramer_cleavage.py +0 -0
  249. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_exposure_result.py +0 -0
  250. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_focused_stability_report.py +0 -0
  251. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_iedb_local.py +0 -0
  252. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_input_validation.py +0 -0
  253. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_integrations.py +0 -0
  254. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_intracellular_cleavage.py +0 -0
  255. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_itcell_cleavage.py +0 -0
  256. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_known_class1_epitopes.py +0 -0
  257. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_known_class2_epitopes.py +0 -0
  258. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_lazy_load.py +0 -0
  259. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_logging.py +0 -0
  260. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_mhc_formats.py +0 -0
  261. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_mhcflurry.py +0 -0
  262. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_mhcflurry_cache_key.py +0 -0
  263. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_mhcflurry_key_lookup.py +0 -0
  264. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_mhcflurry_provenance.py +0 -0
  265. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_mixmhc2pred2.py +0 -0
  266. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_mixmhcpred.py +0 -0
  267. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_mixmhcpred_runtime.py +0 -0
  268. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_mixtcrpred.py +0 -0
  269. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_netchop.py +0 -0
  270. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_netchop_parse.py +0 -0
  271. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_netcleave.py +0 -0
  272. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_netmhc_cons.py +0 -0
  273. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_netmhc_pan.py +0 -0
  274. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_netmhc_program_defaults.py +0 -0
  275. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_netmhc_stabpan.py +0 -0
  276. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_netmhc_version.py +0 -0
  277. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_netmhcii_pan.py +0 -0
  278. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_netmhcii_pan32.py +0 -0
  279. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_nettcr.py +0 -0
  280. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_optional_backend.py +0 -0
  281. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_osteosarc_fixtures.py +0 -0
  282. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_osteosarc_visualization.py +0 -0
  283. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_packaging.py +0 -0
  284. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_pepsickle.py +0 -0
  285. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_pepsickle_legacy.py +0 -0
  286. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_pepsickle_runtime.py +0 -0
  287. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_peptidases.py +0 -0
  288. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_peptide_input.py +0 -0
  289. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_peptide_lengths.py +0 -0
  290. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_peptiverse.py +0 -0
  291. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_peptiverse_cpp.py +0 -0
  292. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_peptiverse_cpp_benchmark.py +0 -0
  293. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_phagescout.py +0 -0
  294. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_phagescout_artifacts.py +0 -0
  295. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_phagescout_full_profiles.py +0 -0
  296. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_plifepred2.py +0 -0
  297. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_pred.py +0 -0
  298. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_prediction_alignment.py +0 -0
  299. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_prime.py +0 -0
  300. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_processing_predictor.py +0 -0
  301. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_random.py +0 -0
  302. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_release_preflight.py +0 -0
  303. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_require_all.py +0 -0
  304. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_serum_calibration.py +0 -0
  305. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_serum_cleavage.py +0 -0
  306. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_serum_contributions.py +0 -0
  307. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_serum_degradation.py +0 -0
  308. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_smm.py +0 -0
  309. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_smm_integration.py +0 -0
  310. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_stability_route.py +0 -0
  311. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_supported_allele_cache.py +0 -0
  312. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_target_cascade_analysis.py +0 -0
  313. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_target_cleavage.py +0 -0
  314. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_tcr.py +0 -0
  315. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_tlimmuno2.py +0 -0
  316. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_tulip.py +0 -0
  317. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_unparseable_alleles.py +0 -0
  318. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_vaccine_comparison.py +0 -0
  319. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_vaccine_core_exposure.py +0 -0
  320. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_vaccine_report.py +0 -0
  321. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_vaccine_trajectory.py +0 -0
  322. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_vaccine_trajectory_report.py +0 -0
  323. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_vaccine_windows.py +0 -0
  324. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_wada_cleavage.py +0 -0
  325. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_weirdo.py +0 -0
  326. {mhctools-4.4.2 → mhctools-4.4.4}/tests/test_wrapper_base.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mhctools
3
- Version: 4.4.2
3
+ Version: 4.4.4
4
4
  Summary: Python interface to MHC binding, presentation, immunogenicity, and antigen processing predictors
5
5
  Author-email: Alex Rubinsteyn <alex@openvax.org>, Julia Kodysh <julia@openvax.org>, Tim O'Donnell <tim@openvax.org>
6
6
  License-Expression: Apache-2.0 AND LGPL-2.1-only AND CC-BY-4.0 AND CC0-1.0
@@ -203,7 +203,7 @@ def __getattr__(name):
203
203
  raise AttributeError(
204
204
  "module %r has no attribute %r" % (__name__, name))
205
205
 
206
- __version__ = "4.4.2"
206
+ __version__ = "4.4.4"
207
207
 
208
208
  __all__ = [
209
209
  "CleaveNet",
@@ -96,19 +96,31 @@ class AsyncProcess(object):
96
96
  return self.process.poll()
97
97
 
98
98
  def _terminate(self, grace_seconds):
99
- """Terminate the process, escalating to kill after a short grace."""
99
+ """Terminate owned processes, including children after the leader exits."""
100
100
  if self.terminate_process_group and os.name == "posix":
101
- process_group = os.getpgid(self.process.pid)
102
- os.killpg(process_group, signal.SIGTERM)
103
- else:
104
- self.process.terminate()
101
+ # start_new_session makes this PID the group ID. getpgid(pid) can
102
+ # fail after the leader exits while its children are still alive.
103
+ process_group = self.process.pid
104
+ deadline = time.monotonic() + grace_seconds
105
+ try:
106
+ os.killpg(process_group, signal.SIGTERM)
107
+ while True:
108
+ self.process.poll() # Reap the leader, but keep checking children.
109
+ os.killpg(process_group, 0)
110
+ remaining = deadline - time.monotonic()
111
+ if remaining <= 0:
112
+ os.killpg(process_group, signal.SIGKILL)
113
+ break
114
+ time.sleep(min(0.05, remaining))
115
+ except ProcessLookupError:
116
+ pass # The entire group has already exited.
117
+ self.process.wait()
118
+ return
119
+ self.process.terminate()
105
120
  try:
106
121
  self.process.wait(timeout=grace_seconds)
107
122
  except TimeoutExpired:
108
- if self.terminate_process_group and os.name == "posix":
109
- os.killpg(process_group, signal.SIGKILL)
110
- else:
111
- self.process.kill()
123
+ self.process.kill()
112
124
  self.process.wait()
113
125
 
114
126
  def wait(self, timeout=None, terminate_grace_seconds=5):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mhctools
3
- Version: 4.4.2
3
+ Version: 4.4.4
4
4
  Summary: Python interface to MHC binding, presentation, immunogenicity, and antigen processing predictors
5
5
  Author-email: Alex Rubinsteyn <alex@openvax.org>, Julia Kodysh <julia@openvax.org>, Tim O'Donnell <tim@openvax.org>
6
6
  License-Expression: Apache-2.0 AND LGPL-2.1-only AND CC-BY-4.0 AND CC0-1.0
@@ -19,8 +19,9 @@ LIAI_license.txt is included because the bundle is Non-Profit Open Software
19
19
  License 3.0 and the terms travel with the material.
20
20
 
21
21
  Reproducibility: entries are sorted, and mtime, mode, uid/gid and uname/gname
22
- are normalized. Rebuilding with the same compression runtime yields the same
23
- SHA-256; different zlib versions may produce different compressed bytes.
22
+ are normalized. The audit pins the uncompressed tar stream's SHA-256, so gzip
23
+ encoding differences do not affect it. The installer separately pins the exact
24
+ compressed bytes of the hosted artifact.
24
25
 
25
26
  python scripts/build_iedb_smm_subset.py \
26
27
  --archive env/test-backends/IEDB_MHC_I-3.1.7.tar.gz \
@@ -62,12 +63,13 @@ INCLUDED_PREFIXES = (
62
63
 
63
64
  EXCLUDED_NAMES = ("__pycache__", ".pyc")
64
65
 
65
- # The digest this script produces from the pinned archive. setup_test_backends
66
- # installs an artifact by checksum; asserting that checksum equals this one is
67
- # what ties the hosted copy to a build from the official release rather than to
68
- # whatever was uploaded.
66
+ # Exact hosted archive bytes, also pinned by setup_test_backends. A rebuild
67
+ # may use a different gzip encoding while preserving the normalized tar stream.
69
68
  EXPECTED_SUBSET_SHA256 = (
70
69
  "eef720a71991a76ceee64ce32fbb52e19684b91d9d44eb078c18c96f395e192e")
70
+ # SHA-256 after gzip decompression, including normalized tar headers and padding.
71
+ EXPECTED_SUBSET_TAR_SHA256 = (
72
+ "f1e3d6168644a33e64a8be00d77b4e56808db1bd9b72bcc704d4954828ad494e")
71
73
 
72
74
 
73
75
  def _digest(path):
@@ -78,6 +80,11 @@ def _digest(path):
78
80
  return sha256.hexdigest()
79
81
 
80
82
 
83
+ def _tar_digest(path):
84
+ with gzip.open(path, "rb") as handle:
85
+ return hashlib.file_digest(handle, "sha256").hexdigest()
86
+
87
+
81
88
  MARKER = "mhc_i/"
82
89
 
83
90
 
@@ -194,16 +201,18 @@ def main():
194
201
  files = [entry for entry in entries if entry[1] is not None]
195
202
  total = sum(len(entry[1]) for entry in files)
196
203
  digest = _digest(args.output)
204
+ tar_digest = _tar_digest(args.output)
197
205
  print("files: %d" % len(files))
198
206
  print("directories: %d" % (len(entries) - len(files)))
199
207
  print("bytes: %d (%.1f MB uncompressed)" % (total, total / 1e6))
200
208
  print("output: %s (%.1f MB)"
201
209
  % (args.output, args.output.stat().st_size / 1e6))
202
210
  print("sha256: %s" % digest)
203
- if EXPECTED_SUBSET_SHA256 not in ("PLACEHOLDER", digest):
211
+ print("tar_sha256: %s" % tar_digest)
212
+ if tar_digest != EXPECTED_SUBSET_TAR_SHA256:
204
213
  raise SystemExit(
205
- "Built subset does not match EXPECTED_SUBSET_SHA256 (%s)"
206
- % EXPECTED_SUBSET_SHA256)
214
+ "Built subset tar does not match EXPECTED_SUBSET_TAR_SHA256 (%s)"
215
+ % EXPECTED_SUBSET_TAR_SHA256)
207
216
 
208
217
 
209
218
  if __name__ == "__main__":
@@ -220,7 +220,7 @@ def gfeller(root, python, config):
220
220
  # the Actions cache evicted, which blocked unrelated merges. The release
221
221
  # unpacks to 1031 MB, mostly bundled DTU executables SMM never runs, against
222
222
  # 9.4 MB it needs. scripts/build_iedb_smm_subset.py
223
- # derives this file reproducibly and documents exactly what it contains; the
223
+ # audits the normalized tar contents independently of gzip encoding; the
224
224
  # upstream Non-Profit OSL 3.0 license ships inside it.
225
225
  SMM_SUBSET_URL = (
226
226
  "https://github.com/openvax/mhctools/releases/download/"
@@ -3,8 +3,11 @@
3
3
  import os
4
4
  from pathlib import Path
5
5
  import shutil
6
+ import signal
6
7
  import subprocess
7
8
  import sys
9
+ from tempfile import TemporaryFile
10
+ import time
8
11
 
9
12
  import pandas as pd
10
13
  import pytest
@@ -16,9 +19,10 @@ from mhctools.mixmhcpred import (
16
19
  resolve_mixmhcpred_path,
17
20
  )
18
21
  from mhctools.prime import parse_prime_results
22
+ from mhctools.process_helpers import run_command
23
+ from .common import process_is_running
19
24
 
20
25
 
21
- pytestmark = pytest.mark.requires_external_tool
22
26
  PEPTIDES = ["SIINFEKL", "MLDDFSAGA", "SIINFEKL"]
23
27
  ALLELES = ["HLA-A*02:01", "HLA-A*01:02"]
24
28
 
@@ -49,9 +53,62 @@ def native_runtime():
49
53
  assert changed == []
50
54
 
51
55
 
52
- def _native(args, environment):
53
- completed = subprocess.run(args, env=environment, capture_output=True, text=True, timeout=180)
54
- assert completed.returncode == 0, completed.stdout + completed.stderr
56
+ def _native(args, environment, timeout=180):
57
+ started = time.monotonic()
58
+ with TemporaryFile() as output:
59
+ try:
60
+ run_command(
61
+ args, env=environment, timeout=timeout,
62
+ terminate_process_group=True,
63
+ redirect_stdout_file=output, redirect_stderr_to_stdout=True)
64
+ except (subprocess.TimeoutExpired, subprocess.CalledProcessError) as error:
65
+ output.seek(0, os.SEEK_END)
66
+ output.seek(max(0, output.tell() - 2000))
67
+ tail = output.read().decode("utf-8", errors="replace").strip()
68
+ reason = (f"timed out (limit {timeout:g}s)"
69
+ if isinstance(error, subprocess.TimeoutExpired)
70
+ else f"exited with status {error.returncode}")
71
+ pytest.fail(
72
+ f"{args[0]} {reason} after {time.monotonic() - started:.2f}s.\n"
73
+ f"Output tail:\n{tail or '(empty output)'}", pytrace=False)
74
+
75
+
76
+ def test_native_reference_failure_reports_bounded_output():
77
+ command = [sys.executable, "-c",
78
+ "import sys; print('discarded-prefix' + 'x' * 5000, flush=True); "
79
+ "print('stdout marker', flush=True); "
80
+ "print('stderr marker', file=sys.stderr, flush=True); sys.exit(7)"]
81
+ with pytest.raises(pytest.fail.Exception) as error:
82
+ _native(command, dict(os.environ))
83
+ message = str(error.value)
84
+ assert "exited with status 7" in message
85
+ assert "stdout marker" in message and "stderr marker" in message
86
+ assert "discarded-prefix" not in message
87
+ assert len(message) < 2400
88
+
89
+
90
+ @pytest.mark.skipif(os.name != "posix", reason="POSIX process-group behavior")
91
+ def test_native_reference_timeout_stops_children(tmp_path):
92
+ child = tmp_path / "child.py"
93
+ child.write_text(
94
+ "import os, sys, time\nfrom pathlib import Path\n"
95
+ "print('reference child active', flush=True)\n"
96
+ "Path(sys.argv[1]).write_text(str(os.getpid()))\ntime.sleep(60)\n")
97
+ parent = tmp_path / "parent.py"
98
+ parent.write_text(
99
+ "import subprocess, sys\n"
100
+ "subprocess.run([sys.executable, sys.argv[1], sys.argv[2]])\n")
101
+ ready = tmp_path / "child.pid"
102
+ try:
103
+ with pytest.raises(pytest.fail.Exception, match="timed out \\(limit 3s\\)") as error:
104
+ _native([sys.executable, str(parent), str(child), str(ready)],
105
+ dict(os.environ), timeout=3)
106
+ assert ready.is_file(), "Reference child did not start before the timeout"
107
+ assert "reference child active" in str(error.value)
108
+ assert not process_is_running(int(ready.read_text()))
109
+ finally:
110
+ if ready.exists() and process_is_running(int(ready.read_text())):
111
+ os.kill(int(ready.read_text()), signal.SIGKILL)
55
112
 
56
113
 
57
114
  def _input(tmp_path):
@@ -60,6 +117,7 @@ def _input(tmp_path):
60
117
  return str(path)
61
118
 
62
119
 
120
+ @pytest.mark.requires_external_tool
63
121
  def test_isolated_multi_allele_rows_motifs_and_cli_match_native(native_runtime, tmp_path, monkeypatch):
64
122
  executable, selected, environment = native_runtime
65
123
  peptide_file = _input(tmp_path)
@@ -101,6 +159,7 @@ def test_isolated_multi_allele_rows_motifs_and_cli_match_native(native_runtime,
101
159
  pred.percentile_rank for row in expected.predictions for pred in row.preds], abs=5e-5)
102
160
 
103
161
 
162
+ @pytest.mark.requires_external_tool
104
163
  def test_isolated_sequence_alignment_prediction_and_artifacts_match_native(native_runtime, tmp_path):
105
164
  executable, selected, environment = native_runtime
106
165
  assert shutil.which("mafft"), "provision MAFFT for the sequence regression"
@@ -127,6 +186,7 @@ def test_isolated_sequence_alignment_prediction_and_artifacts_match_native(nativ
127
186
  assert alignment_only.runtime_info["backend"]["python_executable"] == selected
128
187
 
129
188
 
189
+ @pytest.mark.requires_external_tool
130
190
  def test_prime_nested_isolated_runtime_matches_native(native_runtime, tmp_path, monkeypatch):
131
191
  executable, selected, environment = native_runtime
132
192
  prime = os.environ.get("PRIME_EXECUTABLE")
@@ -10,7 +10,7 @@
10
10
  # See the License for the specific language governing permissions and
11
11
  # limitations under the License.
12
12
 
13
- """Tests for process_helpers retry logic."""
13
+ """Process lifecycle, concurrency limits and runtime probes."""
14
14
 
15
15
  import errno
16
16
  import os
@@ -18,10 +18,12 @@ import signal
18
18
  import sys
19
19
  from subprocess import TimeoutExpired
20
20
  import tempfile
21
+ import time
21
22
  from unittest.mock import patch, MagicMock
22
23
 
23
24
  import pytest
24
25
 
26
+ from .common import process_is_running
25
27
  from mhctools.process_helpers import (
26
28
  AsyncProcess,
27
29
  legacy_keras_runtime_available,
@@ -31,37 +33,51 @@ from mhctools.process_helpers import (
31
33
 
32
34
 
33
35
  @pytest.mark.skipif(os.name != "posix", reason="POSIX process-group behavior")
34
- def test_timeout_terminates_process_group():
35
- proc = AsyncProcess(["slow-command"], terminate_process_group=True)
36
- proc.process = MagicMock(pid=123)
37
- proc.process.wait.side_effect = [
38
- TimeoutExpired("slow-command", 1),
39
- 0,
40
- ]
41
- with patch("mhctools.process_helpers.os.getpgid", return_value=456), \
42
- patch("mhctools.process_helpers.os.killpg") as killpg:
36
+ @pytest.mark.parametrize("parent_ignores_term,child_ignores_term", [
37
+ (False, False), (False, True), (True, True),
38
+ ])
39
+ def test_timeout_stops_the_whole_process_group(
40
+ tmp_path, parent_ignores_term, child_ignores_term):
41
+ child = tmp_path / "child.py"
42
+ child.write_text(
43
+ "import os, signal, sys, time\nfrom pathlib import Path\n"
44
+ "signal.signal(signal.SIGTERM, signal.SIG_IGN if sys.argv[2] == '1' "
45
+ "else signal.SIG_DFL)\n"
46
+ "Path(sys.argv[1]).write_text(str(os.getpid()))\ntime.sleep(60)\n")
47
+ parent = tmp_path / "parent.py"
48
+ parent.write_text(
49
+ "import signal, subprocess, sys, time\n"
50
+ "signal.signal(signal.SIGTERM, signal.SIG_IGN if sys.argv[4] == '1' "
51
+ "else signal.SIG_DFL)\n"
52
+ "subprocess.Popen([sys.executable, sys.argv[1], sys.argv[2], sys.argv[3]])\n"
53
+ "time.sleep(60)\n")
54
+ ready = tmp_path / "child.pid"
55
+ proc = AsyncProcess(
56
+ [sys.executable, str(parent), str(child), str(ready),
57
+ str(int(child_ignores_term)), str(int(parent_ignores_term))],
58
+ terminate_process_group=True)
59
+ proc.start()
60
+ try:
61
+ deadline = time.monotonic() + 5
62
+ while not ready.exists():
63
+ assert proc.poll() is None
64
+ assert time.monotonic() < deadline, "Child did not become ready"
65
+ time.sleep(0.01)
66
+ child_pid = int(ready.read_text())
43
67
  with pytest.raises(TimeoutExpired):
44
- proc.wait(timeout=1, terminate_grace_seconds=0.1)
45
- killpg.assert_called_once_with(456, signal.SIGTERM)
46
-
47
-
48
- @pytest.mark.skipif(os.name != "posix", reason="POSIX process-group behavior")
49
- def test_timeout_kills_process_group_after_grace_period():
50
- proc = AsyncProcess(["stubborn-command"], terminate_process_group=True)
51
- proc.process = MagicMock(pid=123)
52
- proc.process.wait.side_effect = [
53
- TimeoutExpired("stubborn-command", 1),
54
- TimeoutExpired("stubborn-command", 0.1),
55
- 0,
56
- ]
57
- with patch("mhctools.process_helpers.os.getpgid", return_value=456), \
58
- patch("mhctools.process_helpers.os.killpg") as killpg:
59
- with pytest.raises(TimeoutExpired):
60
- proc.wait(timeout=1, terminate_grace_seconds=0.1)
61
- assert killpg.call_args_list == [
62
- ((456, signal.SIGTERM),),
63
- ((456, signal.SIGKILL),),
64
- ]
68
+ proc.wait(timeout=0.05, terminate_grace_seconds=0.1)
69
+ assert proc.process.returncode is not None
70
+ deadline = time.monotonic() + 2
71
+ while process_is_running(child_pid) and time.monotonic() < deadline:
72
+ time.sleep(0.01)
73
+ assert not process_is_running(child_pid), "Timeout left a running child"
74
+ finally:
75
+ # A failing regression must not leak its own sleeping child.
76
+ try:
77
+ os.killpg(proc.process.pid, signal.SIGKILL)
78
+ except ProcessLookupError:
79
+ pass
80
+ proc.process.wait()
65
81
 
66
82
 
67
83
  def test_start_retries_on_blocking_io_error():
@@ -12,16 +12,13 @@
12
12
 
13
13
  """The hosted SMM subset must stay traceable to one upstream IEDB release.
14
14
 
15
- The subset builder and the fixture recorder each pin the official archive by
16
- SHA-256, and the builder also pins the digest of the subset it derives from
17
- it. The installer pins only that derived digest, which carries no upstream
18
- version of its own -- so the chain from "what CI installs" back to "which
19
- IEDB release" holds only if the installer's digest equals the one the builder
20
- declares. Assert that, or a rebuilt-and-reuploaded asset could change what CI
21
- installs while the recorded fixtures keep describing the old release.
15
+ The source archive, normalized tar stream and exact hosted gzip artifact have
16
+ separate checksums. Rebuilds must preserve the normalized tar stream across gzip
17
+ encodings; installation must still reject changes to the hosted archive bytes.
22
18
  """
23
19
 
24
20
  import importlib.util
21
+ import gzip
25
22
  import hashlib
26
23
  import io
27
24
  from pathlib import Path
@@ -32,6 +29,10 @@ import tarfile
32
29
  import pytest
33
30
 
34
31
  SCRIPTS = Path(__file__).resolve().parent.parent / "scripts"
32
+ # Fixed digest of the normalized tar for source_files, independently checked
33
+ # against its member payloads and metadata below.
34
+ SOURCE_FILES_TAR_SHA256 = (
35
+ "1220dc57900fd4be42e64a8513cc0bae94bd11713a3f183ac8adcc02852b75e0")
35
36
 
36
37
 
37
38
  def _load(name):
@@ -63,18 +64,23 @@ def test_installer_pins_a_subset_checksum():
63
64
  assert re.fullmatch(r"[0-9a-f]{64}", installer.SMM_SUBSET_SHA256)
64
65
 
65
66
 
66
- def test_installer_digest_is_the_one_the_builder_produces():
67
- """Ties the installed artifact to a build from the pinned IEDB release.
68
-
69
- Without this, uploading a modified asset and updating only
70
- SMM_SUBSET_SHA256 passes every other check here.
71
- """
67
+ def test_installer_digest_matches_the_hosted_artifact_pin():
72
68
  builder = _load("build_iedb_smm_subset")
73
69
  installer = _load("setup_test_backends")
74
70
 
75
71
  assert installer.SMM_SUBSET_SHA256 == builder.EXPECTED_SUBSET_SHA256
76
72
 
77
73
 
74
+ @pytest.mark.requires_external_tool
75
+ def test_hosted_archive_matches_the_pinned_tar_content():
76
+ archive = SCRIPTS.parent / "env/test-backends/IEDB_MHC_I-3.1.7-smm-subset.tar.gz"
77
+ if not archive.is_file():
78
+ pytest.skip("Provision the SMM artifact: python scripts/setup_test_backends.py smm")
79
+ builder = _load("build_iedb_smm_subset")
80
+ assert builder._digest(archive) == builder.EXPECTED_SUBSET_SHA256
81
+ assert builder._tar_digest(archive) == builder.EXPECTED_SUBSET_TAR_SHA256
82
+
83
+
78
84
  def test_subset_carries_the_upstream_licenses():
79
85
  """NPOSL-3.0 material is redistributed, so its notices must travel."""
80
86
  builder = _load("build_iedb_smm_subset")
@@ -83,8 +89,7 @@ def test_subset_carries_the_upstream_licenses():
83
89
  assert notice in builder.INCLUDED_PREFIXES
84
90
 
85
91
 
86
- def test_installer_downloads_the_release_the_builder_produces():
87
- """The installed artifact must be the one build_iedb_smm_subset writes."""
92
+ def test_installer_downloads_the_pinned_subset_release():
88
93
  installer = _load("setup_test_backends")
89
94
 
90
95
  assert installer.SMM_SUBSET_URL.startswith(
@@ -143,8 +148,8 @@ def _build_subset(monkeypatch, archive, output, **pins):
143
148
  monkeypatch.setattr(builder, "ARCHIVE_SHA256", pins.get(
144
149
  "source", hashlib.sha256(archive.read_bytes()).hexdigest()))
145
150
  # Synthetic archives exercise the real builder without the 341 MB download.
146
- monkeypatch.setattr(builder, "EXPECTED_SUBSET_SHA256",
147
- pins.get("subset", "PLACEHOLDER"))
151
+ monkeypatch.setattr(builder, "EXPECTED_SUBSET_TAR_SHA256",
152
+ pins.get("subset", SOURCE_FILES_TAR_SHA256))
148
153
  monkeypatch.setattr(sys, "argv", [
149
154
  str(SCRIPTS / "build_iedb_smm_subset.py"),
150
155
  "--archive", str(archive), "--output", str(output)])
@@ -163,7 +168,7 @@ def test_builder_preserves_payload_and_normalizes_metadata(
163
168
  second = tmp_path / "second.tar.gz"
164
169
  _build_subset(monkeypatch, archive, first)
165
170
  _build_subset(monkeypatch, archive, second,
166
- subset=hashlib.sha256(first.read_bytes()).hexdigest())
171
+ subset=hashlib.sha256(gzip.decompress(first.read_bytes())).hexdigest())
167
172
  assert first.read_bytes() == second.read_bytes()
168
173
  with tarfile.open(first) as subset:
169
174
  assert subset.getnames() == sorted(source_files)
@@ -212,13 +217,69 @@ def test_builder_rejects_wrong_source_checksum(tmp_path, monkeypatch, source_fil
212
217
  assert not output.exists()
213
218
 
214
219
 
215
- def test_builder_rejects_wrong_subset_checksum(tmp_path, monkeypatch, source_files):
220
+ def test_builder_rejects_wrong_tar_checksum(tmp_path, monkeypatch, source_files):
216
221
  archive = tmp_path / "upstream.tar.gz"
217
222
  _write_archive(archive, source_files)
218
- with pytest.raises(SystemExit, match="does not match EXPECTED_SUBSET_SHA256"):
223
+ with pytest.raises(SystemExit, match="does not match EXPECTED_SUBSET_TAR_SHA256"):
219
224
  _build_subset(monkeypatch, archive, tmp_path / "subset.tar.gz", subset="0" * 64)
220
225
 
221
226
 
227
+ @pytest.mark.parametrize("encoding", [
228
+ {"compresslevel": 0},
229
+ {"compresslevel": 1},
230
+ {"filename": "another-name.tar", "mtime": 123456789},
231
+ ])
232
+ def test_builder_accepts_different_gzip_encodings(
233
+ tmp_path, monkeypatch, source_files, capsys, encoding):
234
+ archive = tmp_path / "upstream.tar.gz"
235
+ _write_archive(archive, source_files)
236
+ first, second = tmp_path / "first.tar.gz", tmp_path / "second.tar.gz"
237
+ _build_subset(monkeypatch, archive, first)
238
+ original_gzip = gzip.GzipFile
239
+
240
+ def alternate_gzip(*args, **kwargs):
241
+ if kwargs.get("mode") == "wb":
242
+ kwargs.update(encoding)
243
+ return original_gzip(*args, **kwargs)
244
+
245
+ monkeypatch.setattr(gzip, "GzipFile", alternate_gzip)
246
+ _build_subset(monkeypatch, archive, second)
247
+ assert first.read_bytes() != second.read_bytes()
248
+ assert gzip.decompress(first.read_bytes()) == gzip.decompress(second.read_bytes())
249
+ assert capsys.readouterr().out.count("tar_sha256: " + SOURCE_FILES_TAR_SHA256) == 2
250
+
251
+
252
+ @pytest.mark.parametrize("change", ["payload", "path"])
253
+ def test_builder_rejects_changed_tar_content(
254
+ tmp_path, monkeypatch, source_files, change):
255
+ if change == "payload":
256
+ source_files["mhc_i/src/setupinfo.template"] += b"changed\n"
257
+ else:
258
+ source_files["mhc_i/src/renamed.template"] = source_files.pop(
259
+ "mhc_i/src/setupinfo.template")
260
+ archive = tmp_path / "upstream.tar.gz"
261
+ _write_archive(archive, source_files)
262
+ with pytest.raises(SystemExit, match="does not match EXPECTED_SUBSET_TAR_SHA256"):
263
+ _build_subset(monkeypatch, archive, tmp_path / "subset.tar.gz")
264
+
265
+
266
+ def test_installer_rejects_reencoded_hosted_artifact(
267
+ tmp_path, monkeypatch, source_files):
268
+ archive = tmp_path / "upstream.tar.gz"
269
+ _write_archive(archive, source_files)
270
+ subset = tmp_path / "IEDB_MHC_I-3.1.7-smm-subset.tar.gz"
271
+ _build_subset(monkeypatch, archive, subset)
272
+ installer = _load("setup_test_backends")
273
+ monkeypatch.setattr(installer, "SMM_SUBSET_SHA256",
274
+ hashlib.sha256(subset.read_bytes()).hexdigest())
275
+ payload = gzip.decompress(subset.read_bytes())
276
+ subset.write_bytes(gzip.compress(payload, compresslevel=0, mtime=0))
277
+ assert _load("build_iedb_smm_subset")._tar_digest(subset) == SOURCE_FILES_TAR_SHA256
278
+ with pytest.raises(SystemExit, match="Unexpected IEDB SMM subset checksum"):
279
+ installer.smm(tmp_path, sys.executable, {})
280
+ assert not (tmp_path / "iedb-3.1.7").exists()
281
+
282
+
222
283
  def test_installer_replaces_stale_tree_after_verifying_checksum(
223
284
  tmp_path, monkeypatch, source_files):
224
285
  archive = tmp_path / "upstream.tar.gz"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes