pyjess 0.4.0__cp312-cp312-win_amd64.whl → 0.5.0__cp312-cp312-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.

Binary file
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[str, bytes, os.PathLike[Any], TextIO]) -> Atom: ...
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[str, bytes, os.PathLike[Any], TextIO]) -> Atom: ...
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) -> List[str]: ...
137
+ def atom_names(self) -> Tuple[str, ...]: ...
138
138
  @property
139
- def residue_names(self) -> List[str]: ...
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] = ()): ...
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 opened in
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
- return cls.loads(file.read())
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
- """`list` of `str`: The different atom names for this atom.
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
- """`list` of `str`: The different residue names for this atom.
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.molecule = self.molecule
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 readonly Molecule molecule
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.molecule._mol)
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.molecule._mol)
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 list _templates
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
- self._templates.append(template)
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(tuple(hash(t) for t in self._templates))
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:
@@ -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 = [
@@ -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, ["LYS"])
34
- self.assertEqual(template[0].atom_names, ["NZ"])
35
- self.assertEqual(template[1].atom_names, ["CG"])
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, ["LYS"])
46
- self.assertEqual(template[0].atom_names, ["NZ"])
47
- self.assertEqual(template[1].atom_names, ["CG"])
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, ["LYS"])
59
- self.assertEqual(template[0].atom_names, ["NZ"])
60
- self.assertEqual(template[1].atom_names, ["CG"])
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, ["NE"])
14
- self.assertEqual(atom.residue_names, ["ARG", "LYS"])
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,30 +1,31 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: pyjess
3
- Version: 0.4.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
-
9
- Copyright (c) 2024 Martin Larralde <martin.larralde@embl.de>
10
-
11
- Permission is hereby granted, free of charge, to any person obtaining a copy
12
- of this software and associated documentation files (the "Software"), to deal
13
- in the Software without restriction, including without limitation the rights
14
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
- copies of the Software, and to permit persons to whom the Software is
16
- furnished to do so, subject to the following conditions:
17
-
18
- The above copyright notice and this permission notice shall be included in all
19
- copies or substantial portions of the Software.
20
-
21
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
- SOFTWARE.
8
+
9
+ Copyright (c) 2024-2025 Martin Larralde <martin.larralde@embl.de>
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
28
29
  Classifier: Development Status :: 4 - Beta
29
30
  Classifier: Intended Audience :: Developers
30
31
  Classifier: Intended Audience :: Science/Research
@@ -46,11 +47,12 @@ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
46
47
  Classifier: Typing :: Typed
47
48
  Project-URL: Homepage, https://github.com/althonos/pyjess/
48
49
  Project-URL: Documentation, https://pyjess.readthedocs.io/en/stable/
49
- Project-URL: Bug tracker, https://github.com/althonos/pyjess/issues
50
+ Project-URL: Bug Tracker, https://github.com/althonos/pyjess/issues
50
51
  Project-URL: Changelog, https://github.com/althonos/pyjess/blob/master/CHANGELOG.md
51
52
  Project-URL: Coverage, https://codecov.io/gh/althonos/pyjess/
52
53
  Project-URL: Builds, https://github.com/althonos/pyjess/actions
53
- Project-URL: Pypi, https://pypi.org/project/pyjess
54
+ Project-URL: PyPI, https://pypi.org/project/pyjess
55
+ Project-URL: PiWheels, https://piwheels.org/project/pyjess/
54
56
  Requires-Python: >=3.7
55
57
  Provides-Extra: test
56
58
  Requires-Dist: importlib-resources; python_version < "3.9" and extra == "test"
@@ -83,8 +85,8 @@ Jess is an algorithm for constraint-based structural template matching
83
85
  proposed by Jonathan Barker *et al.*[\[1\]](#ref1). It can be used to identify
84
86
  catalytic residues from a known template inside a protein structure. Jess
85
87
  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
88
+ Andrew Wallace *et al.*[\[2\]](#ref2), removing some pre-computation and
89
+ structural requirements from the original algorithm. Jess was further
88
90
  updated and maintained by [Ioannis Riziotis](https://github.com/iriziotis)
89
91
  during his PhD in the [Thornton group](https://www.ebi.ac.uk/research/thornton/).
90
92
 
@@ -105,34 +107,44 @@ as well as the code required to compile from source with Cython:
105
107
  $ pip install pyjess
106
108
  ```
107
109
 
108
- <!-- Otherwise, PyJess is also available as a [Bioconda](https://bioconda.github.io/)
110
+ Otherwise, PyJess is also available as a [Bioconda](https://bioconda.github.io/)
109
111
  package:
110
112
  ```console
111
113
  $ conda install -c bioconda pyjess
112
- ``` -->
114
+ ```
113
115
 
114
116
  Check the [*install* page](https://pyjess.readthedocs.io/en/stable/install.html)
115
117
  of the documentation for other ways to install PyJess on your machine.
116
118
 
119
+
120
+ ## 🔖 Citation
121
+
122
+ PyJess is scientific software, and builds on top of Jess. Please cite
123
+ Jess if you are using it in an academic work, for instance as:
124
+
125
+ > PyJess, a Python library binding to Jess (Barker *et al.*, 2003).
126
+
127
+
117
128
  ## 💡 Example
118
129
 
119
130
  Load templates to be used as references from different template files:
120
131
 
121
132
  ```python
122
- import glob
133
+ import pathlib
123
134
  import pyjess
124
135
 
125
136
  templates = []
126
- for path in sorted(glob.iglob("vendor/jess/examples/template_*.qry")):
127
- templates.append(Template.load(path, id=os.path.basename(path)))
137
+ for path in sorted(pathlib.Path("vendor/jess/examples").glob("template_*.qry")):
138
+ with path.open() as file:
139
+ templates.append(pyjess.Template.load(file, id=path.stem))
128
140
  ```
129
141
 
130
142
  Create a `Jess` instance and use it to query a molecule (a PDB structure)
131
143
  against the stored templates:
132
144
 
133
145
  ```python
134
- jess = Jess(templates)
135
- mol = Molecule("vendor/jess/examples/test_pdbs/pdb1a0p.ent")
146
+ jess = pyjess.Jess(templates)
147
+ mol = pyjess.Molecule.load("vendor/jess/examples/test_pdbs/pdb1a0p.ent")
136
148
  query = jess.query(mol, rmsd_threshold=2.0, distance_cutoff=3.0, max_dynamic_distance=3.0)
137
149
  ```
138
150
 
@@ -162,7 +174,7 @@ with multiprocessing.ThreadPool() as pool:
162
174
  hits = pool.map(jess.query, molecules)
163
175
  ```
164
176
 
165
- *⚠️ Prior to PyJess `v0.2.1`, the Jess code was running some thread-unsafe operations which have now been patched.
177
+ *⚠️ Prior to PyJess `v0.2.1`, the Jess code was running some thread-unsafe operations which have now been patched.
166
178
  If running Jess in parallel, make sure to use `v0.2.1` or later to use the code patched with re-entrant functions*.
167
179
 
168
180
  <!-- ## ⏱️ 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.cp312-win_amd64.pyd,sha256=zUtacQe8ErjZBNPq8r-wMZrDlxlP2_OY0KvOdbreUXI,291328
4
- pyjess/_jess.pyi,sha256=DD1ZMKSbgCBKPiTVZc8ojmkYJcgjSESqyMIHIqL1exg,7110
5
- pyjess/_jess.pyx,sha256=pU8KKFLyRHGwieDmG9AZNhCuctGCti3BkrOW-NyW-gM,48525
3
+ pyjess/_jess.cp312-win_amd64.pyd,sha256=KXJYtOC7PbHr6nni8lLPGy1-pAlcGtp7QAxP2M9a8zg,295424
4
+ pyjess/_jess.pyi,sha256=oWlqpqnhOKFcq2UCpGJrUJaZZF1ZlLodKxINwoR3vrw,7118
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=UJhb9VJR4Hm6g0eIlkypnnUf3jcGC6csbGuTL7VGF30,4449
17
- pyjess/tests/test_jess.py,sha256=XItj6F0XBe_cmsRmgGnyu81wah6bVdfFXEcmO2EZRg0,8686
18
- pyjess/tests/test_molecule.py,sha256=lT9ZdW1aWrrQR95iRBET3NJYIKlNT3IiRMadD_RUSgs,4879
19
- pyjess/tests/test_template.py,sha256=LJGKHc7BzDdl42nHVF0-6m92alBogWXQneeE0tkobQQ,3986
20
- pyjess/tests/test_template_atom.py,sha256=W6Iruh7f4gNiohkTHN1hcjDBByWiYTlyXcGmD_I-Pdw,2992
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.4.0.dist-info/METADATA,sha256=vaDoU5JzpxXjRTl-M43Stw7evHX75QHWTexO_TF7_DE,10785
23
- pyjess-0.4.0.dist-info/WHEEL,sha256=GgB_RydHGtp7zP9kXrVRu7kuGtdM7WtO3JhH95Vv87o,106
24
- pyjess-0.4.0.dist-info/licenses/COPYING,sha256=WpSMpnu8fCvYt4QNIp0SCsWgeXIRr1KEXBfqiPwrPZ4,1119
25
- pyjess-0.4.0.dist-info/RECORD,,
22
+ pyjess-0.5.0.dist-info/METADATA,sha256=sz5NWDSx0uyq_myc5StGdXX-lD3tNYVXz1K4efR5wZg,11151
23
+ pyjess-0.5.0.dist-info/WHEEL,sha256=NwgjyrmVpVzZ-osOl_ZcT-QXHLFroWlAkN7IC9J-0Pg,106
24
+ pyjess-0.5.0.dist-info/licenses/COPYING,sha256=Iyx2bRDPnLgoEzW2KVanb61cjhW8lnhJNU-mjS-KhIY,1124
25
+ pyjess-0.5.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: scikit-build-core 0.10.7
2
+ Generator: scikit-build-core 0.11.1
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp312-cp312-win_amd64
5
5
 
@@ -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