pyjess 0.4.0__pp37-pypy37_pp73-win_amd64.whl → 0.5.0__pp37-pypy37_pp73-win_amd64.whl
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.
Potentially problematic release.
This version of pyjess might be problematic. Click here for more details.
- pyjess/_jess.pyi +5 -6
- pyjess/_jess.pypy37-pp73-win_amd64.pyd +0 -0
- pyjess/_jess.pyx +107 -21
- pyjess/tests/test_atom.py +10 -1
- pyjess/tests/test_jess.py +33 -0
- pyjess/tests/test_molecule.py +16 -0
- pyjess/tests/test_template.py +26 -9
- pyjess/tests/test_template_atom.py +18 -2
- {pyjess-0.4.0.dist-info → pyjess-0.5.0.dist-info}/METADATA +23 -12
- {pyjess-0.4.0.dist-info → pyjess-0.5.0.dist-info}/RECORD +12 -12
- {pyjess-0.4.0.dist-info → pyjess-0.5.0.dist-info}/licenses/COPYING +1 -1
- {pyjess-0.4.0.dist-info → pyjess-0.5.0.dist-info}/WHEEL +0 -0
pyjess/_jess.pyi
CHANGED
|
@@ -39,7 +39,7 @@ class Molecule(Sequence[Atom]):
|
|
|
39
39
|
|
|
40
40
|
class Atom:
|
|
41
41
|
@classmethod
|
|
42
|
-
def load(cls, file: Union[
|
|
42
|
+
def load(cls, file: Union[TextIO, str, os.PathLike[str]]) -> Atom: ...
|
|
43
43
|
@classmethod
|
|
44
44
|
def loads(cls, text: str) -> Atom: ...
|
|
45
45
|
def __init__(
|
|
@@ -100,7 +100,7 @@ class Atom:
|
|
|
100
100
|
|
|
101
101
|
class TemplateAtom:
|
|
102
102
|
@classmethod
|
|
103
|
-
def load(cls, file: Union[
|
|
103
|
+
def load(cls, file: Union[TextIO, str, os.PathLike[str]]) -> Atom: ...
|
|
104
104
|
@classmethod
|
|
105
105
|
def loads(cls, text: str) -> TemplateAtom: ...
|
|
106
106
|
def __init__(
|
|
@@ -134,9 +134,9 @@ class TemplateAtom:
|
|
|
134
134
|
@property
|
|
135
135
|
def z(self) -> float: ...
|
|
136
136
|
@property
|
|
137
|
-
def atom_names(self) ->
|
|
137
|
+
def atom_names(self) -> Tuple[str, ...]: ...
|
|
138
138
|
@property
|
|
139
|
-
def residue_names(self) ->
|
|
139
|
+
def residue_names(self) -> Tuple[str, ...]: ...
|
|
140
140
|
@property
|
|
141
141
|
def distance_weight(self) -> float: ...
|
|
142
142
|
def copy(self: _SELF) -> _SELF: ...
|
|
@@ -188,14 +188,13 @@ class Hit(Generic[_T]):
|
|
|
188
188
|
@property
|
|
189
189
|
def template(self) -> _T: ...
|
|
190
190
|
@property
|
|
191
|
-
def molecule(self) -> Molecule: ...
|
|
192
|
-
@property
|
|
193
191
|
def determinant(self) -> float: ...
|
|
194
192
|
@property
|
|
195
193
|
def log_evalue(self) -> float: ...
|
|
196
194
|
@property
|
|
197
195
|
def evalue(self) -> float: ...
|
|
198
196
|
def atoms(self, transform: bool = True) -> List[Atom]: ...
|
|
197
|
+
def molecule(self, transform: bool = False) -> Molecule: ...
|
|
199
198
|
|
|
200
199
|
class Jess(Generic[_T], Sequence[_T]):
|
|
201
200
|
def __init__(self, templates: Iterable[_T] = ()): ...
|
|
Binary file
|
pyjess/_jess.pyx
CHANGED
|
@@ -461,7 +461,7 @@ cdef class Atom:
|
|
|
461
461
|
return NotImplemented
|
|
462
462
|
other_ = other
|
|
463
463
|
# FIXME: it should be possible to do a memcmp here.
|
|
464
|
-
return self._state() == other_._state()
|
|
464
|
+
return self._state() == other_._state()
|
|
465
465
|
|
|
466
466
|
def __hash__(self):
|
|
467
467
|
return hash(tuple(self._state().values()))
|
|
@@ -598,11 +598,16 @@ cdef class TemplateAtom:
|
|
|
598
598
|
"""Load a template atom from the given file.
|
|
599
599
|
|
|
600
600
|
Arguments:
|
|
601
|
-
file (file-like object): A file-like object
|
|
602
|
-
text mode to read the template atom from.
|
|
601
|
+
file (str, os.PathLike or file-like object): A file-like object
|
|
602
|
+
opened in text or binary mode to read the template atom from.
|
|
603
603
|
|
|
604
604
|
"""
|
|
605
|
-
|
|
605
|
+
try:
|
|
606
|
+
handle = open(file)
|
|
607
|
+
except TypeError:
|
|
608
|
+
handle = nullcontext(file)
|
|
609
|
+
with handle as f:
|
|
610
|
+
return cls.loads(f.read())
|
|
606
611
|
|
|
607
612
|
@classmethod
|
|
608
613
|
def loads(cls, text):
|
|
@@ -828,7 +833,11 @@ cdef class TemplateAtom:
|
|
|
828
833
|
|
|
829
834
|
@property
|
|
830
835
|
def atom_names(self):
|
|
831
|
-
"""`
|
|
836
|
+
"""`tuple` of `str`: The different atom names for this atom.
|
|
837
|
+
|
|
838
|
+
.. versionchanged:: 0.4.1
|
|
839
|
+
Property now returns a `tuple` rather than a `list`.
|
|
840
|
+
|
|
832
841
|
"""
|
|
833
842
|
assert self._atom is not NULL
|
|
834
843
|
|
|
@@ -837,11 +846,15 @@ cdef class TemplateAtom:
|
|
|
837
846
|
|
|
838
847
|
for i in range(self._atom.nameCount):
|
|
839
848
|
l.append(self._atom.name[i].replace(b'_', b'').decode())
|
|
840
|
-
return l
|
|
849
|
+
return tuple(l)
|
|
841
850
|
|
|
842
851
|
@property
|
|
843
852
|
def residue_names(self):
|
|
844
|
-
"""`
|
|
853
|
+
"""`tuple` of `str`: The different residue names for this atom.
|
|
854
|
+
|
|
855
|
+
.. versionchanged:: 0.4.1
|
|
856
|
+
Property now returns a `tuple` rather than a `list`.
|
|
857
|
+
|
|
845
858
|
"""
|
|
846
859
|
assert self._atom is not NULL
|
|
847
860
|
|
|
@@ -850,7 +863,7 @@ cdef class TemplateAtom:
|
|
|
850
863
|
|
|
851
864
|
for i in range(self._atom.resNameCount):
|
|
852
865
|
l.append(self._atom.resName[i].replace(b'_', b'').decode())
|
|
853
|
-
return l
|
|
866
|
+
return tuple(l)
|
|
854
867
|
|
|
855
868
|
@property
|
|
856
869
|
def distance_weight(self):
|
|
@@ -863,11 +876,11 @@ cdef class TemplateAtom:
|
|
|
863
876
|
"""Create a copy of this template atom.
|
|
864
877
|
|
|
865
878
|
Returns:
|
|
866
|
-
`~pyjess.TemplateAtom`: A new template atom object with
|
|
879
|
+
`~pyjess.TemplateAtom`: A new template atom object with
|
|
867
880
|
identical attributes.
|
|
868
|
-
|
|
881
|
+
|
|
869
882
|
.. versionadded:: 0.4.0
|
|
870
|
-
|
|
883
|
+
|
|
871
884
|
"""
|
|
872
885
|
return type(self)(**self._state())
|
|
873
886
|
|
|
@@ -886,10 +899,42 @@ cdef class Template:
|
|
|
886
899
|
|
|
887
900
|
@classmethod
|
|
888
901
|
def loads(cls, text, str id = None):
|
|
902
|
+
"""Load a template from a string.
|
|
903
|
+
|
|
904
|
+
Arguments:
|
|
905
|
+
file (`str`, `os.PathLike`, or file-like object): Either the path
|
|
906
|
+
to a file, or a file-like object opened in **text mode**
|
|
907
|
+
containing the template.
|
|
908
|
+
id (`str`, optional): The identifier of the template. By default,
|
|
909
|
+
the parser will take the one from the ``PDB_ID`` remark if
|
|
910
|
+
found in the header.
|
|
911
|
+
|
|
912
|
+
Returns:
|
|
913
|
+
`~pyjess.Template`: The template parsed from the given string.
|
|
914
|
+
|
|
915
|
+
See Also:
|
|
916
|
+
`Template.load` to load a template from a file-like object or
|
|
917
|
+
from a path.
|
|
918
|
+
|
|
919
|
+
"""
|
|
889
920
|
return cls.load(io.StringIO(text), id=id)
|
|
890
921
|
|
|
891
922
|
@classmethod
|
|
892
923
|
def load(cls, file, str id = None):
|
|
924
|
+
"""Load a template from the given file.
|
|
925
|
+
|
|
926
|
+
Arguments:
|
|
927
|
+
file (`str`, `os.PathLike` or file-like object): Either the
|
|
928
|
+
path to a file, or a file-like object opened in **text mode**
|
|
929
|
+
to read the template from.
|
|
930
|
+
id (`str`, optional): The identifier of the template. By default,
|
|
931
|
+
the parser will take the one from the ``PDB_ID`` remark if
|
|
932
|
+
found in the header.
|
|
933
|
+
|
|
934
|
+
Returns:
|
|
935
|
+
`~pyjess.Template`: The template parsed from the given file.
|
|
936
|
+
|
|
937
|
+
"""
|
|
893
938
|
try:
|
|
894
939
|
handle = open(file)
|
|
895
940
|
except TypeError:
|
|
@@ -1037,10 +1082,10 @@ cdef class Template:
|
|
|
1037
1082
|
return all(x == y for x,y in zip(self, other_))
|
|
1038
1083
|
|
|
1039
1084
|
def __hash__(self):
|
|
1040
|
-
return hash(
|
|
1085
|
+
return hash((
|
|
1041
1086
|
self.id,
|
|
1042
1087
|
*(hash(x) for x in self)
|
|
1043
|
-
)
|
|
1088
|
+
))
|
|
1044
1089
|
|
|
1045
1090
|
def __reduce__(self):
|
|
1046
1091
|
return type(self), (list(self), self.id)
|
|
@@ -1173,7 +1218,7 @@ cdef class Query:
|
|
|
1173
1218
|
hit._sup = NULL
|
|
1174
1219
|
hit._atoms = NULL
|
|
1175
1220
|
hit.rmsd = INFINITY
|
|
1176
|
-
hit.
|
|
1221
|
+
hit._molecule = self.molecule
|
|
1177
1222
|
hit_tpl = NULL
|
|
1178
1223
|
|
|
1179
1224
|
# search the next hit without the GIL to allow parallel queries.
|
|
@@ -1253,7 +1298,7 @@ cdef class Hit:
|
|
|
1253
1298
|
|
|
1254
1299
|
cdef readonly double rmsd
|
|
1255
1300
|
cdef readonly Template template
|
|
1256
|
-
cdef
|
|
1301
|
+
cdef Molecule _molecule
|
|
1257
1302
|
|
|
1258
1303
|
def __dealloc__(self):
|
|
1259
1304
|
jess.super.Superposition_free(self._sup)
|
|
@@ -1284,7 +1329,7 @@ cdef class Hit:
|
|
|
1284
1329
|
cdef double e
|
|
1285
1330
|
|
|
1286
1331
|
with nogil:
|
|
1287
|
-
n = jess.molecule.Molecule_count(self.
|
|
1332
|
+
n = jess.molecule.Molecule_count(self._molecule._mol)
|
|
1288
1333
|
e = self.template._tpl.logE(self.template._tpl, self.rmsd, n)
|
|
1289
1334
|
return e
|
|
1290
1335
|
|
|
@@ -1296,7 +1341,7 @@ cdef class Hit:
|
|
|
1296
1341
|
cdef double e
|
|
1297
1342
|
|
|
1298
1343
|
with nogil:
|
|
1299
|
-
n = jess.molecule.Molecule_count(self.
|
|
1344
|
+
n = jess.molecule.Molecule_count(self._molecule._mol)
|
|
1300
1345
|
e = exp(self.template._tpl.logE(self.template._tpl, self.rmsd, n))
|
|
1301
1346
|
return e
|
|
1302
1347
|
|
|
@@ -1341,6 +1386,45 @@ cdef class Hit:
|
|
|
1341
1386
|
|
|
1342
1387
|
return atoms
|
|
1343
1388
|
|
|
1389
|
+
cpdef Molecule molecule(self, bint transform=False):
|
|
1390
|
+
"""Get the molecule matching the template.
|
|
1391
|
+
|
|
1392
|
+
Arguments:
|
|
1393
|
+
transform (`bool`): Whether or not to transform coordinates
|
|
1394
|
+
of the molecule atoms into template frame.
|
|
1395
|
+
|
|
1396
|
+
Returns:
|
|
1397
|
+
`~pyjess.Molecule`: The matching molecule, optionally
|
|
1398
|
+
rotated to match the template coordinate.
|
|
1399
|
+
|
|
1400
|
+
.. versionadded:: 0.5.0
|
|
1401
|
+
|
|
1402
|
+
"""
|
|
1403
|
+
assert self.template._tpl is not NULL
|
|
1404
|
+
assert self._sup is not NULL
|
|
1405
|
+
|
|
1406
|
+
cdef _Atom* atom
|
|
1407
|
+
cdef Molecule mol
|
|
1408
|
+
cdef size_t i
|
|
1409
|
+
cdef size_t j
|
|
1410
|
+
cdef size_t k
|
|
1411
|
+
cdef const double* M = jess.super.Superposition_rotation(self._sup)
|
|
1412
|
+
cdef const double* c = jess.super.Superposition_centroid(self._sup, 0)
|
|
1413
|
+
cdef const double* v = jess.super.Superposition_centroid(self._sup, 1)
|
|
1414
|
+
|
|
1415
|
+
if not transform:
|
|
1416
|
+
return self._molecule
|
|
1417
|
+
|
|
1418
|
+
mol = self._molecule.copy()
|
|
1419
|
+
for k in range(mol._mol.count):
|
|
1420
|
+
atom = mol._mol.atom[i]
|
|
1421
|
+
for i in range(3):
|
|
1422
|
+
atom.x[i] = v[i]
|
|
1423
|
+
for j in range(3):
|
|
1424
|
+
atom.x[i] += M[3*i + j] * (self._molecule._mol.atom[k].x[j] - c[j])
|
|
1425
|
+
|
|
1426
|
+
return mol
|
|
1427
|
+
|
|
1344
1428
|
|
|
1345
1429
|
cdef class Jess:
|
|
1346
1430
|
"""A handle to run Jess over a list of templates.
|
|
@@ -1351,7 +1435,7 @@ cdef class Jess:
|
|
|
1351
1435
|
"""
|
|
1352
1436
|
cdef _Jess* _jess
|
|
1353
1437
|
cdef dict _indices
|
|
1354
|
-
cdef
|
|
1438
|
+
cdef tuple _templates
|
|
1355
1439
|
cdef size_t length
|
|
1356
1440
|
|
|
1357
1441
|
def __cinit__(self):
|
|
@@ -1379,10 +1463,10 @@ cdef class Jess:
|
|
|
1379
1463
|
"""
|
|
1380
1464
|
cdef Template template
|
|
1381
1465
|
cdef _Template* tpl
|
|
1466
|
+
cdef list _templates = []
|
|
1382
1467
|
|
|
1383
1468
|
self._jess = jess.jess.Jess_create()
|
|
1384
1469
|
self._indices = {}
|
|
1385
|
-
self._templates = []
|
|
1386
1470
|
|
|
1387
1471
|
for template in templates:
|
|
1388
1472
|
# NOTE: the Jess storage owns the data, so we make a copy of the
|
|
@@ -1390,9 +1474,11 @@ cdef class Jess:
|
|
|
1390
1474
|
tpl = template._tpl.copy(template._tpl)
|
|
1391
1475
|
jess.jess.Jess_addTemplate(self._jess, tpl)
|
|
1392
1476
|
self._indices[<size_t> tpl] = self.length
|
|
1393
|
-
|
|
1477
|
+
_templates.append(template)
|
|
1394
1478
|
self.length += 1
|
|
1395
1479
|
|
|
1480
|
+
self._templates = tuple(_templates)
|
|
1481
|
+
|
|
1396
1482
|
def __copy__(self):
|
|
1397
1483
|
return self.copy()
|
|
1398
1484
|
|
|
@@ -1407,7 +1493,7 @@ cdef class Jess:
|
|
|
1407
1493
|
return self._templates == other_._templates
|
|
1408
1494
|
|
|
1409
1495
|
def __hash__(self):
|
|
1410
|
-
return hash(
|
|
1496
|
+
return hash((Jess, self._templates))
|
|
1411
1497
|
|
|
1412
1498
|
def __len__(self):
|
|
1413
1499
|
return self.length
|
pyjess/tests/test_atom.py
CHANGED
|
@@ -59,9 +59,18 @@ class TestAtom(unittest.TestCase):
|
|
|
59
59
|
def test_hash(self):
|
|
60
60
|
a1 = self._create_atom()
|
|
61
61
|
a2 = self._create_atom()
|
|
62
|
-
self.assertEqual(a1, a2)
|
|
63
62
|
self.assertEqual(hash(a1), hash(a2))
|
|
64
63
|
self.assertIsNot(a1, a2)
|
|
64
|
+
a3 = self._create_atom(x=1.0)
|
|
65
|
+
self.assertNotEqual(hash(a1), hash(a3))
|
|
66
|
+
|
|
67
|
+
def test_eq(self):
|
|
68
|
+
a1 = self._create_atom()
|
|
69
|
+
a2 = self._create_atom()
|
|
70
|
+
self.assertEqual(a1, a2)
|
|
71
|
+
self.assertIsNot(a1, a2)
|
|
72
|
+
a3 = self._create_atom(x=1.0)
|
|
73
|
+
self.assertNotEqual(a1, a3)
|
|
65
74
|
|
|
66
75
|
def test_repr_roundtrip(self):
|
|
67
76
|
atom = self._create_atom()
|
pyjess/tests/test_jess.py
CHANGED
|
@@ -21,6 +21,13 @@ class TestJess(unittest.TestCase):
|
|
|
21
21
|
hits = jess.query(mol, 2, 2, 4)
|
|
22
22
|
self.assertRaises(StopIteration, next, hits)
|
|
23
23
|
|
|
24
|
+
def test_hash_empty(self):
|
|
25
|
+
j1 = Jess()
|
|
26
|
+
j2 = Jess()
|
|
27
|
+
self.assertEqual(j1, j2)
|
|
28
|
+
self.assertEqual(hash(j1), hash(j2))
|
|
29
|
+
self.assertIsNot(j1, j2)
|
|
30
|
+
|
|
24
31
|
@unittest.skipUnless(files, "importlib.resources not available")
|
|
25
32
|
def test_copy(self):
|
|
26
33
|
with files(data).joinpath("template_01.qry").open() as f:
|
|
@@ -31,6 +38,32 @@ class TestJess(unittest.TestCase):
|
|
|
31
38
|
copy = jess.copy()
|
|
32
39
|
self.assertEqual(jess, copy)
|
|
33
40
|
|
|
41
|
+
@unittest.skipUnless(files, "importlib.resources not available")
|
|
42
|
+
def test_hash(self):
|
|
43
|
+
with files(data).joinpath("template_01.qry").open() as f:
|
|
44
|
+
template1 = Template.load(f)
|
|
45
|
+
with files(data).joinpath("template_02.qry").open() as f:
|
|
46
|
+
template2 = Template.load(f)
|
|
47
|
+
j1 = Jess([template1, template2])
|
|
48
|
+
j2 = Jess([template1, template2])
|
|
49
|
+
self.assertEqual(hash(j1), hash(j2))
|
|
50
|
+
self.assertIsNot(j1, j2)
|
|
51
|
+
j3 = Jess([template1])
|
|
52
|
+
self.assertNotEqual(hash(j1), hash(j3))
|
|
53
|
+
|
|
54
|
+
@unittest.skipUnless(files, "importlib.resources not available")
|
|
55
|
+
def test_eq(self):
|
|
56
|
+
with files(data).joinpath("template_01.qry").open() as f:
|
|
57
|
+
template1 = Template.load(f)
|
|
58
|
+
with files(data).joinpath("template_02.qry").open() as f:
|
|
59
|
+
template2 = Template.load(f)
|
|
60
|
+
j1 = Jess([template1, template2])
|
|
61
|
+
j2 = Jess([template1, template2])
|
|
62
|
+
self.assertEqual(j1, j2)
|
|
63
|
+
self.assertIsNot(j1, j2)
|
|
64
|
+
j3 = Jess([template1])
|
|
65
|
+
self.assertNotEqual(j1, j3)
|
|
66
|
+
|
|
34
67
|
@unittest.skipUnless(files, "importlib.resources not available")
|
|
35
68
|
def test_pickle_roundtrip(self):
|
|
36
69
|
with files(data).joinpath("template_01.qry").open() as f:
|
pyjess/tests/test_molecule.py
CHANGED
|
@@ -96,7 +96,23 @@ class TestMolecule(unittest.TestCase):
|
|
|
96
96
|
mol1 = Molecule(atoms)
|
|
97
97
|
mol2 = Molecule(atoms)
|
|
98
98
|
self.assertEqual(hash(mol1), hash(mol2))
|
|
99
|
+
self.assertIsNot(mol1, mol2)
|
|
100
|
+
mol3 = Molecule(atoms[:-1])
|
|
101
|
+
self.assertNotEqual(hash(mol1), hash(mol3))
|
|
102
|
+
|
|
103
|
+
def test_eq(self):
|
|
104
|
+
atoms = [
|
|
105
|
+
self._create_atom(serial=1, name='N'),
|
|
106
|
+
self._create_atom(serial=2, name='CA'),
|
|
107
|
+
self._create_atom(serial=3, name='C'),
|
|
108
|
+
self._create_atom(serial=4, name='O'),
|
|
109
|
+
]
|
|
110
|
+
mol1 = Molecule(atoms)
|
|
111
|
+
mol2 = Molecule(atoms)
|
|
99
112
|
self.assertEqual(mol1, mol2)
|
|
113
|
+
self.assertIsNot(mol1, mol2)
|
|
114
|
+
mol3 = Molecule(atoms[:-1])
|
|
115
|
+
self.assertNotEqual(mol1, mol3)
|
|
100
116
|
|
|
101
117
|
def test_copy(self):
|
|
102
118
|
atoms = [
|
pyjess/tests/test_template.py
CHANGED
|
@@ -30,9 +30,9 @@ class TestTemplate(unittest.TestCase):
|
|
|
30
30
|
template = Template.loads(TEMPLATE)
|
|
31
31
|
self.assertEqual(len(template), 11)
|
|
32
32
|
self.assertEqual(template.dimension, 5)
|
|
33
|
-
self.assertEqual(template[0].residue_names,
|
|
34
|
-
self.assertEqual(template[0].atom_names,
|
|
35
|
-
self.assertEqual(template[1].atom_names,
|
|
33
|
+
self.assertEqual(template[0].residue_names, ("LYS",))
|
|
34
|
+
self.assertEqual(template[0].atom_names, ("NZ",))
|
|
35
|
+
self.assertEqual(template[1].atom_names, ("CG",))
|
|
36
36
|
self.assertEqual(template[2].residue_number, 1132)
|
|
37
37
|
self.assertEqual(template[-1].residue_number, 1150)
|
|
38
38
|
|
|
@@ -42,9 +42,9 @@ class TestTemplate(unittest.TestCase):
|
|
|
42
42
|
f.write(TEMPLATE)
|
|
43
43
|
f.flush()
|
|
44
44
|
template = Template.load(f.name)
|
|
45
|
-
self.assertEqual(template[0].residue_names,
|
|
46
|
-
self.assertEqual(template[0].atom_names,
|
|
47
|
-
self.assertEqual(template[1].atom_names,
|
|
45
|
+
self.assertEqual(template[0].residue_names, ("LYS",))
|
|
46
|
+
self.assertEqual(template[0].atom_names, ("NZ",))
|
|
47
|
+
self.assertEqual(template[1].atom_names, ("CG",))
|
|
48
48
|
self.assertEqual(template[2].residue_number, 1132)
|
|
49
49
|
self.assertEqual(template[-1].residue_number, 1150)
|
|
50
50
|
|
|
@@ -55,9 +55,9 @@ class TestTemplate(unittest.TestCase):
|
|
|
55
55
|
f.flush()
|
|
56
56
|
f.seek(0)
|
|
57
57
|
template = Template.load(f)
|
|
58
|
-
self.assertEqual(template[0].residue_names,
|
|
59
|
-
self.assertEqual(template[0].atom_names,
|
|
60
|
-
self.assertEqual(template[1].atom_names,
|
|
58
|
+
self.assertEqual(template[0].residue_names, ("LYS",))
|
|
59
|
+
self.assertEqual(template[0].atom_names, ("NZ",))
|
|
60
|
+
self.assertEqual(template[1].atom_names, ("CG",))
|
|
61
61
|
self.assertEqual(template[2].residue_number, 1132)
|
|
62
62
|
self.assertEqual(template[-1].residue_number, 1150)
|
|
63
63
|
|
|
@@ -87,6 +87,23 @@ class TestTemplate(unittest.TestCase):
|
|
|
87
87
|
tpl2 = tpl1[1:4]
|
|
88
88
|
self.assertEqual(len(tpl2), 3)
|
|
89
89
|
|
|
90
|
+
def test_hash(self):
|
|
91
|
+
tpl1 = Template.loads(TEMPLATE, id="tpl1")
|
|
92
|
+
tpl2 = Template.loads(TEMPLATE, id="tpl1")
|
|
93
|
+
self.assertEqual(tpl1, tpl2)
|
|
94
|
+
self.assertEqual(hash(tpl1), hash(tpl2))
|
|
95
|
+
self.assertIsNot(tpl1, tpl2)
|
|
96
|
+
tpl3 = Template.loads(TEMPLATE, id="tpl3")
|
|
97
|
+
self.assertNotEqual(hash(tpl1), hash(tpl3))
|
|
98
|
+
|
|
99
|
+
def test_eq(self):
|
|
100
|
+
tpl1 = Template.loads(TEMPLATE, id="tpl1")
|
|
101
|
+
tpl2 = Template.loads(TEMPLATE, id="tpl1")
|
|
102
|
+
self.assertEqual(tpl1, tpl2)
|
|
103
|
+
self.assertIsNot(tpl1, tpl2)
|
|
104
|
+
tpl3 = Template.loads(TEMPLATE, id="tpl3")
|
|
105
|
+
self.assertNotEqual(tpl1, tpl3)
|
|
106
|
+
|
|
90
107
|
def test_copy(self):
|
|
91
108
|
tpl1 = Template.loads(TEMPLATE, id="tpl1")
|
|
92
109
|
tpl2 = tpl1.copy()
|
|
@@ -10,8 +10,8 @@ class TestTemplateAtom(unittest.TestCase):
|
|
|
10
10
|
def test_load(self):
|
|
11
11
|
atom = TemplateAtom.loads("ATOM 1 NE ARG A1136 3.953 0.597 -1.721 K")
|
|
12
12
|
self.assertEqual(atom.match_mode, 1)
|
|
13
|
-
self.assertEqual(atom.atom_names,
|
|
14
|
-
self.assertEqual(atom.residue_names,
|
|
13
|
+
self.assertEqual(atom.atom_names, ("NE",))
|
|
14
|
+
self.assertEqual(atom.residue_names, ("ARG", "LYS",))
|
|
15
15
|
self.assertEqual(atom.chain_id, "A")
|
|
16
16
|
self.assertEqual(atom.residue_number, 1136)
|
|
17
17
|
self.assertEqual(atom.x, 3.953)
|
|
@@ -32,6 +32,22 @@ class TestTemplateAtom(unittest.TestCase):
|
|
|
32
32
|
default.update(kwargs)
|
|
33
33
|
return TemplateAtom(**default)
|
|
34
34
|
|
|
35
|
+
def test_hash(self):
|
|
36
|
+
a1 = self._create_atom()
|
|
37
|
+
a2 = self._create_atom()
|
|
38
|
+
self.assertEqual(hash(a1), hash(a2))
|
|
39
|
+
self.assertIsNot(a1, a2)
|
|
40
|
+
a3 = self._create_atom(x=1.0)
|
|
41
|
+
self.assertNotEqual(hash(a1), hash(a3))
|
|
42
|
+
|
|
43
|
+
def test_eq(self):
|
|
44
|
+
a1 = self._create_atom()
|
|
45
|
+
a2 = self._create_atom()
|
|
46
|
+
self.assertEqual(a1, a2)
|
|
47
|
+
self.assertIsNot(a1, a2)
|
|
48
|
+
a3 = self._create_atom(x=1.0)
|
|
49
|
+
self.assertNotEqual(a1, a3)
|
|
50
|
+
|
|
35
51
|
@unittest.skipUnless(sys.implementation.name == "cpython", "only available on CPython")
|
|
36
52
|
def test_sizeof(self):
|
|
37
53
|
atom = self._create_atom()
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyjess
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Cython bindings and Python interface to JESS, a 3D template matching software.
|
|
5
5
|
Keywords: bioinformatics,structure,template,matching
|
|
6
6
|
Author-Email: Martin Larralde <martin.larralde@embl.de>
|
|
7
7
|
License: MIT License
|
|
8
8
|
|
|
9
|
-
Copyright (c) 2024 Martin Larralde <martin.larralde@embl.de>
|
|
9
|
+
Copyright (c) 2024-2025 Martin Larralde <martin.larralde@embl.de>
|
|
10
10
|
|
|
11
11
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
12
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -51,6 +51,7 @@ Project-URL: Changelog, https://github.com/althonos/pyjess/blob/master/CHANGELOG
|
|
|
51
51
|
Project-URL: Coverage, https://codecov.io/gh/althonos/pyjess/
|
|
52
52
|
Project-URL: Builds, https://github.com/althonos/pyjess/actions
|
|
53
53
|
Project-URL: Pypi, https://pypi.org/project/pyjess
|
|
54
|
+
Project-URL: Piwheels, https://piwheels.org/project/pyjess/
|
|
54
55
|
Requires-Python: >=3.7
|
|
55
56
|
Provides-Extra: test
|
|
56
57
|
Requires-Dist: importlib-resources; python_version < "3.9" and extra == "test"
|
|
@@ -83,8 +84,8 @@ Jess is an algorithm for constraint-based structural template matching
|
|
|
83
84
|
proposed by Jonathan Barker *et al.*[\[1\]](#ref1). It can be used to identify
|
|
84
85
|
catalytic residues from a known template inside a protein structure. Jess
|
|
85
86
|
is an evolution of TESS, a geometric hashing algorithm developed by
|
|
86
|
-
Andrew Wallace *et al.*[\[2\]](#ref2), removing some pre-computation and
|
|
87
|
-
structural requirements from the original algorithm. Jess was further
|
|
87
|
+
Andrew Wallace *et al.*[\[2\]](#ref2), removing some pre-computation and
|
|
88
|
+
structural requirements from the original algorithm. Jess was further
|
|
88
89
|
updated and maintained by [Ioannis Riziotis](https://github.com/iriziotis)
|
|
89
90
|
during his PhD in the [Thornton group](https://www.ebi.ac.uk/research/thornton/).
|
|
90
91
|
|
|
@@ -105,34 +106,44 @@ as well as the code required to compile from source with Cython:
|
|
|
105
106
|
$ pip install pyjess
|
|
106
107
|
```
|
|
107
108
|
|
|
108
|
-
|
|
109
|
+
Otherwise, PyJess is also available as a [Bioconda](https://bioconda.github.io/)
|
|
109
110
|
package:
|
|
110
111
|
```console
|
|
111
112
|
$ conda install -c bioconda pyjess
|
|
112
|
-
```
|
|
113
|
+
```
|
|
113
114
|
|
|
114
115
|
Check the [*install* page](https://pyjess.readthedocs.io/en/stable/install.html)
|
|
115
116
|
of the documentation for other ways to install PyJess on your machine.
|
|
116
117
|
|
|
118
|
+
|
|
119
|
+
## 🔖 Citation
|
|
120
|
+
|
|
121
|
+
PyJess is scientific software, and builds on top of Jess. Please cite
|
|
122
|
+
Jess if you are using it in an academic work, for instance as:
|
|
123
|
+
|
|
124
|
+
> PyJess, a Python library binding to Jess (Barker *et al.*, 2003).
|
|
125
|
+
|
|
126
|
+
|
|
117
127
|
## 💡 Example
|
|
118
128
|
|
|
119
129
|
Load templates to be used as references from different template files:
|
|
120
130
|
|
|
121
131
|
```python
|
|
122
|
-
import
|
|
132
|
+
import pathlib
|
|
123
133
|
import pyjess
|
|
124
134
|
|
|
125
135
|
templates = []
|
|
126
|
-
for path in sorted(
|
|
127
|
-
|
|
136
|
+
for path in sorted(pathlib.Path("vendor/jess/examples").glob("template_*.qry")):
|
|
137
|
+
with path.open() as file:
|
|
138
|
+
templates.append(pyjess.Template.load(file, id=path.stem))
|
|
128
139
|
```
|
|
129
140
|
|
|
130
141
|
Create a `Jess` instance and use it to query a molecule (a PDB structure)
|
|
131
142
|
against the stored templates:
|
|
132
143
|
|
|
133
144
|
```python
|
|
134
|
-
jess = Jess(templates)
|
|
135
|
-
mol = Molecule("vendor/jess/examples/test_pdbs/pdb1a0p.ent")
|
|
145
|
+
jess = pyjess.Jess(templates)
|
|
146
|
+
mol = pyjess.Molecule.load("vendor/jess/examples/test_pdbs/pdb1a0p.ent")
|
|
136
147
|
query = jess.query(mol, rmsd_threshold=2.0, distance_cutoff=3.0, max_dynamic_distance=3.0)
|
|
137
148
|
```
|
|
138
149
|
|
|
@@ -162,7 +173,7 @@ with multiprocessing.ThreadPool() as pool:
|
|
|
162
173
|
hits = pool.map(jess.query, molecules)
|
|
163
174
|
```
|
|
164
175
|
|
|
165
|
-
*⚠️ Prior to PyJess `v0.2.1`, the Jess code was running some thread-unsafe operations which have now been patched.
|
|
176
|
+
*⚠️ Prior to PyJess `v0.2.1`, the Jess code was running some thread-unsafe operations which have now been patched.
|
|
166
177
|
If running Jess in parallel, make sure to use `v0.2.1` or later to use the code patched with re-entrant functions*.
|
|
167
178
|
|
|
168
179
|
<!-- ## ⏱️ Benchmarks -->
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
pyjess/.gitignore,sha256=u14v4OOy8U50Kp9SUKU8DupCG-mQIuel47gdbNDmAwg,21
|
|
2
2
|
pyjess/__init__.py,sha256=Xe9GBQUBm9ik-ty5tcE3UQ9Ip1p-C_IGvTPuGULolng,766
|
|
3
|
-
pyjess/_jess.pyi,sha256=
|
|
4
|
-
pyjess/_jess.pypy37-pp73-win_amd64.pyd,sha256=
|
|
5
|
-
pyjess/_jess.pyx,sha256=
|
|
3
|
+
pyjess/_jess.pyi,sha256=oWlqpqnhOKFcq2UCpGJrUJaZZF1ZlLodKxINwoR3vrw,7118
|
|
4
|
+
pyjess/_jess.pypy37-pp73-win_amd64.pyd,sha256=nOid-PzdGDGaCBnLr9SFpIDfnP0OaQYzqA4n6U3ZQtQ,240640
|
|
5
|
+
pyjess/_jess.pyx,sha256=rC2Dd_mNy4OhlN7QtQv0IZE7Q82TlXKkpq82v-gCiZY,51537
|
|
6
6
|
pyjess/CMakeLists.txt,sha256=H9eXbrFcGF2OLP8muQctb4cOb27Qp2uZj5KRjoDAROg,36
|
|
7
7
|
pyjess/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
pyjess/tests/__init__.py,sha256=h83kBzYKH_i1InkM8mOA23zE3PBaB9lrNDCuhC8LOl4,544
|
|
@@ -13,13 +13,13 @@ pyjess/tests/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
|
13
13
|
pyjess/tests/data/pdb1lnb.pdb,sha256=F8A6OdRly5c-pxsDw1LjTFINI75r1HaKaYB_eXv1QuM,273388
|
|
14
14
|
pyjess/tests/data/template_01.qry,sha256=izCIhUUTEk-IvQowhSVLiJaCAhpPbyvrfyoR4Q6-Pm0,616
|
|
15
15
|
pyjess/tests/data/template_02.qry,sha256=5IYRTqsvO_roB2INLwfFDEaWJW9VRcXdbK4oe8VKMxE,618
|
|
16
|
-
pyjess/tests/test_atom.py,sha256=
|
|
17
|
-
pyjess/tests/test_jess.py,sha256=
|
|
18
|
-
pyjess/tests/test_molecule.py,sha256=
|
|
19
|
-
pyjess/tests/test_template.py,sha256=
|
|
20
|
-
pyjess/tests/test_template_atom.py,sha256=
|
|
16
|
+
pyjess/tests/test_atom.py,sha256=clLN9IVuivadztGtagDhdPBDGoMkUgs41lEWuTCCmFA,4741
|
|
17
|
+
pyjess/tests/test_jess.py,sha256=kBmsFAl7tmCAFamk3JqGAwttI6SJiAl3aLJUO-9FmOA,9976
|
|
18
|
+
pyjess/tests/test_molecule.py,sha256=9k6uiTeOWc5NiO7epyxY9lm_GgksPb7-o-ZcNFNxutw,5452
|
|
19
|
+
pyjess/tests/test_template.py,sha256=AIN-ba5-YTnGdT9SGPU4q45AZ03QnPE769WyItSpoPs,4657
|
|
20
|
+
pyjess/tests/test_template_atom.py,sha256=oK8cfKe4_k3Pm1PqoTTxTzAoeUVLiCFsg6QmiTQ-RCQ,3496
|
|
21
21
|
pyjess/tests/utils.py,sha256=Z7rUPC-D8dZlRfHAnLaXHUg6M10D3zFvNiwDvvHA3xc,202
|
|
22
|
-
pyjess-0.
|
|
23
|
-
pyjess-0.
|
|
24
|
-
pyjess-0.
|
|
25
|
-
pyjess-0.
|
|
22
|
+
pyjess-0.5.0.dist-info/METADATA,sha256=qsP6LSYDn0V3i8bpx2KZw3w8pmH5zad6gIAnjfL053o,11121
|
|
23
|
+
pyjess-0.5.0.dist-info/WHEEL,sha256=GcwMogo_AzsTpUmdn3_tpF9yY9Mv6Ok8VFrdrPp_Aoc,111
|
|
24
|
+
pyjess-0.5.0.dist-info/licenses/COPYING,sha256=Iyx2bRDPnLgoEzW2KVanb61cjhW8lnhJNU-mjS-KhIY,1124
|
|
25
|
+
pyjess-0.5.0.dist-info/RECORD,,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2024 Martin Larralde <martin.larralde@embl.de>
|
|
3
|
+
Copyright (c) 2024-2025 Martin Larralde <martin.larralde@embl.de>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
File without changes
|