Skip to content

Commit 3ff1959

Browse files
Merge pull request #2250 from prody/copilot/use-pdb-modres-conversion
Use PDB MODRES records for correct ncAA → canonical AA conversion in parsePDBHeader
2 parents b8cbcf6 + 1ed3ab6 commit 3ff1959

3 files changed

Lines changed: 52 additions & 0 deletions

File tree

prody/tests/datafiles/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,9 @@
456456
},
457457
'modres_ncaa': {
458458
'file': 'pdb_modres_ncaa.pdb'
459+
},
460+
'sep_modres': {
461+
'file': 'pdb5fc2_sep_modres.pdb'
459462
}
460463
}
461464

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
HEADER KINASE 16-JUL-26 5FC2
2+
MODRES 5FC2 SEP A 4 SER PHOSPHOSERINE
3+
MODRES 5FC2 6L3 A 9 INHIBITOR LIGAND
4+
SEQRES 1 A 9 ASP LYS SEP ILE GLU VAL GLY ARG 6L3
5+
ATOM 1 CA ASP A 1 10.000 10.000 10.000 1.00 20.00 C
6+
END

prody/tests/proteins/test_header.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,49 @@ def tearDown(self):
192192
self.polymers_long = None
193193

194194

195+
class TestParsePDBHeaderSEPModres(unittest.TestCase):
196+
"""Test that SEP (phosphoserine) is correctly converted to S (SER) via
197+
MODRES records, reproducing the 5FC2 scenario reported in GitHub issue
198+
comments: chain A has SEP at position 4 mapped to SER, and a ligand
199+
6L3 that should fall back to 'X'."""
200+
201+
def setUp(self):
202+
self.polymers = parsePDBHeader(
203+
pathDatafile('sep_modres'), 'polymers')
204+
self.polymers_long = parsePDBHeader(
205+
pathDatafile('sep_modres'), 'polymers', longSeq=True)
206+
207+
def testPolymerPresent(self):
208+
self.assertEqual(len(self.polymers), 1,
209+
'expected exactly one polymer chain')
210+
211+
def testOneLetterSequenceSEP(self):
212+
"""SEP should map to S (SER) and ligand 6L3 should fall back to X."""
213+
seq = self.polymers[0].sequence
214+
self.assertEqual(seq, 'DKSIEVGRX',
215+
'one-letter sequence incorrect; got {0!r}, expected "DKSIEVGRX"'
216+
.format(seq))
217+
218+
def testLongSequenceContainsSEP(self):
219+
"""Three-letter sequence should preserve the SEP residue name."""
220+
seq = self.polymers_long[0].sequence
221+
self.assertIn('SEP', seq,
222+
'long sequence should contain "SEP" residue name')
223+
224+
def testModifiedResidues(self):
225+
"""MODRES record for SEP should be stored in poly.modified."""
226+
modified = self.polymers[0].modified
227+
self.assertIsNotNone(modified,
228+
'poly.modified should not be None when MODRES records are present')
229+
resnames = [m[0] for m in modified]
230+
self.assertIn('SEP', resnames,
231+
'"SEP" should appear in poly.modified residue names')
232+
233+
def tearDown(self):
234+
self.polymers = None
235+
self.polymers_long = None
236+
237+
195238
class TestThreeLetterSequenceKwarg(unittest.TestCase):
196239

197240
def setUp(self):

0 commit comments

Comments
 (0)