pyjess 0.3.3__cp39-cp39-macosx_10_12_x86_64.whl → 0.4.0__cp39-cp39-macosx_10_12_x86_64.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
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  import typing
3
- from typing import Any, Generic, Union, Optional, Sequence, Iterator, Iterable, List, TextIO, Sized, TypeVar
3
+ from typing import Any, Generic, Union, Optional, Sequence, Iterator, Iterable, List, TextIO, Sized, TypeVar, Tuple
4
4
 
5
5
  try:
6
6
  from typing import Literal
@@ -13,6 +13,8 @@ MATCH_MODE = Literal[
13
13
 
14
14
  _SELF = TypeVar("_SELF")
15
15
 
16
+ __version__: str
17
+
16
18
  class Molecule(Sequence[Atom]):
17
19
  @classmethod
18
20
  def load(cls, file: Union[TextIO, str, os.PathLike[str]], id: Optional[str] = None, ignore_endmdl: bool = False) -> Molecule: ...
@@ -26,9 +28,14 @@ class Molecule(Sequence[Atom]):
26
28
  def __getitem__(self: _SELF, index: slice) -> _SELF: ...
27
29
  @typing.overload
28
30
  def __getitem__(self: _SELF, index: Union[int, slice]) -> Union[Atom, _SELF]: ...
31
+ def __copy__(self) -> Molecule: ...
32
+ def __eq__(self, other: object) -> bool: ...
33
+ def __hash__(self) -> int: ...
34
+ def __reduce__(self) -> Tuple[Any, ...]: ...
29
35
  @property
30
36
  def id(self) -> Optional[str]: ...
31
37
  def conserved(self: _SELF, cutoff: float = 0.0) -> _SELF: ...
38
+ def copy(self) -> Molecule: ...
32
39
 
33
40
  class Atom:
34
41
  @classmethod
@@ -54,7 +61,11 @@ class Atom:
54
61
  element: str = "",
55
62
  charge: int = 0,
56
63
  ): ...
64
+ def __eq__(self, other: object) -> bool: ...
65
+ def __hash__(self) -> int: ...
66
+ def __reduce__(self) -> Tuple[Any, ...]: ...
57
67
  def __repr__(self) -> str: ...
68
+ def __copy__(self) -> Atom: ...
58
69
  @property
59
70
  def serial(self) -> int: ...
60
71
  @property
@@ -85,6 +96,7 @@ class Atom:
85
96
  def y(self) -> float: ...
86
97
  @property
87
98
  def z(self) -> float: ...
99
+ def copy(self) -> Atom: ...
88
100
 
89
101
  class TemplateAtom:
90
102
  @classmethod
@@ -105,6 +117,10 @@ class TemplateAtom:
105
117
  match_mode: MATCH_MODE = 0,
106
118
  ): ...
107
119
  def __repr__(self) -> str: ...
120
+ def __eq__(self, other: object) -> bool: ...
121
+ def __hash__(self) -> int: ...
122
+ def __reduce__(self) -> Tuple[Any, ...]: ...
123
+ def __copy__(self: _SELF) -> _SELF: ...
108
124
  @property
109
125
  def match_mode(self) -> MATCH_MODE: ...
110
126
  @property
@@ -123,6 +139,7 @@ class TemplateAtom:
123
139
  def residue_names(self) -> List[str]: ...
124
140
  @property
125
141
  def distance_weight(self) -> float: ...
142
+ def copy(self: _SELF) -> _SELF: ...
126
143
 
127
144
  class Template(Sequence[TemplateAtom]):
128
145
  @classmethod
@@ -137,10 +154,15 @@ class Template(Sequence[TemplateAtom]):
137
154
  def __getitem__(self: _SELF, index: slice) -> _SELF: ...
138
155
  @typing.overload
139
156
  def __getitem__(self: _SELF, index: Union[int, slice]) -> Union[TemplateAtom, _SELF]: ...
157
+ def __eq__(self, other: object) -> bool: ...
158
+ def __hash__(self) -> int: ...
159
+ def __reduce__(self) -> Tuple[Any, ...]: ...
160
+ def __copy__(self) -> Template: ...
140
161
  @property
141
162
  def id(self) -> str: ...
142
163
  @property
143
164
  def dimension(self) -> int: ...
165
+ def copy(self) -> Template: ...
144
166
 
145
167
  _T = TypeVar("_T", bound=Template)
146
168
 
@@ -184,6 +206,10 @@ class Jess(Generic[_T], Sequence[_T]):
184
206
  def __getitem__(self, index: slice) -> Jess[_T]: ...
185
207
  @typing.overload
186
208
  def __getitem__(self, index: Union[int, slice]) -> Union[_T, Jess[_T]]: ...
209
+ def __eq__(self, other: object) -> bool: ...
210
+ def __hash__(self) -> int: ...
211
+ def __copy__(self: _SELF) -> _SELF: ...
212
+ def __reduce__(self) -> Tuple[Any, ...]: ...
187
213
  def query(
188
214
  self,
189
215
  molecule: Molecule,
pyjess/_jess.pyx CHANGED
@@ -43,7 +43,9 @@ from jess.tess_atom cimport TessAtom as _TessAtom
43
43
  # --- Python imports ---------------------------------------------------------
44
44
 
45
45
  import contextlib
46
+ import functools
46
47
  import io
48
+ import itertools
47
49
  import os
48
50
  import warnings
49
51
 
@@ -53,10 +55,11 @@ __version__ = PROJECT_VERSION
53
55
 
54
56
  cdef inline void copy_token(char* dst, const char* src, size_t n) noexcept nogil:
55
57
  cdef size_t i
56
- strncpy(dst, src, n)
57
58
  for i in range(n):
58
- if dst[i] == ord(' ') or dst[i] == 0:
59
+ if src[i] == ord(' ') or src[i] == 0:
59
60
  dst[i] = ord('_')
61
+ else:
62
+ dst[i] = src[i]
60
63
  dst[n] = 0
61
64
 
62
65
  @contextlib.contextmanager
@@ -68,9 +71,12 @@ def nullcontext(return_value=None):
68
71
  cdef class Molecule:
69
72
  """A molecule structure, as a sequence of `Atom` objects.
70
73
 
71
- .. versionchanged:: 0.2.2
74
+ .. versionadded:: 0.2.2
72
75
  Support identifiers of arbitrary length.
73
76
 
77
+ .. versionadded:: 0.4.0
78
+ Equality, hashing and pickle protocol support.
79
+
74
80
  """
75
81
  cdef _Molecule* _mol
76
82
  cdef str _id
@@ -204,6 +210,26 @@ cdef class Molecule:
204
210
  atom._atom = <_Atom*> jess.molecule.Molecule_atom(self._mol, index_)
205
211
  return atom
206
212
 
213
+ def __copy__(self):
214
+ return self.copy()
215
+
216
+ def __eq__(self, object other):
217
+ cdef Molecule other_
218
+ if not isinstance(other, Molecule):
219
+ return NotImplemented
220
+ other_ = other
221
+ if self._id != other_._id:
222
+ return False
223
+ if self._mol.count != other_._mol.count:
224
+ return False
225
+ return all(x == y for x,y in zip(self, other_))
226
+
227
+ def __hash__(self):
228
+ return hash((self._id, *(hash(x) for x in self)))
229
+
230
+ def __reduce__(self):
231
+ return type(self), (list(self), self.id)
232
+
207
233
  def __sizeof__(self):
208
234
  assert self._mol is not NULL
209
235
  return (
@@ -229,9 +255,44 @@ cdef class Molecule:
229
255
  ]
230
256
  )
231
257
 
258
+ cpdef Molecule copy(self):
259
+ """Create a copy of this molecule and its atoms.
260
+
261
+ Returns:
262
+ `~pyjess.Molecule`: A newly allocated molecule with the same
263
+ identifier and atoms.
264
+
265
+ .. versionadded:: 0.4.0
266
+
267
+ """
268
+ cdef Molecule copy = Molecule.__new__(Molecule)
269
+ cdef size_t size = sizeof(_Molecule) + self._mol.count * sizeof(_Atom*)
270
+
271
+ with nogil:
272
+ # allocate molecule storage
273
+ copy._mol = <_Molecule*> malloc(size)
274
+ if copy._mol is NULL:
275
+ raise MemoryError("Failed to allocate molecule")
276
+ # copy molecule attributes
277
+ copy._mol.count = self._mol.count
278
+ memset(copy._mol.id, b' ', 5)
279
+ # copy molecule atoms
280
+ for i in range(self._mol.count):
281
+ copy._mol.atom[i] = <_Atom*> malloc(sizeof(_Atom))
282
+ if copy._mol.atom[i] is NULL:
283
+ raise MemoryError("Failed to allocate atom")
284
+ memcpy(copy._mol.atom[i], self._mol.atom[i], sizeof(_Atom))
285
+
286
+ copy._id = self._id
287
+ return copy
288
+
232
289
 
233
290
  cdef class Atom:
234
291
  """A single atom in a molecule.
292
+
293
+ .. versionadded:: 0.4.0
294
+ Equality, hashing and pickle protocol support.
295
+
235
296
  """
236
297
  cdef object owner
237
298
  cdef bint owned
@@ -297,11 +358,11 @@ cdef class Atom:
297
358
  str chain_id,
298
359
  int residue_number,
299
360
  str insertion_code,
300
- float x,
301
- float y,
302
- float z,
303
- float occupancy = 0.0,
304
- float temperature_factor = 0.0,
361
+ double x,
362
+ double y,
363
+ double z,
364
+ double occupancy = 0.0,
365
+ double temperature_factor = 0.0,
305
366
  str segment = '',
306
367
  str element = '',
307
368
  int charge = 0,
@@ -336,7 +397,7 @@ cdef class Atom:
336
397
  self._atom.serial = serial
337
398
  self._atom.altLoc = ord(altloc)
338
399
  self._atom.chainID1 = ord(chain_id[0]) if len(chain_id) > 0 else 0
339
- self._atom.chainID2 = ord(chain_id[1]) if len(chain_id) > 1 else ord('0')
400
+ self._atom.chainID2 = ord(chain_id[1]) if len(chain_id) > 1 else ord(' ')
340
401
  self._atom.resSeq = residue_number
341
402
  self._atom.iCode = ord(insertion_code)
342
403
  self._atom.x[0] = x
@@ -354,29 +415,38 @@ cdef class Atom:
354
415
  _name.insert(0, ord('_'))
355
416
  copy_token(self._atom.name, _name.ljust(4, b'\0'), 4)
356
417
 
418
+ def __copy__(self):
419
+ return self.copy()
420
+
421
+ cdef dict _state(self):
422
+ return {
423
+ "serial": self.serial,
424
+ "name": self.name,
425
+ "altloc": self.altloc,
426
+ "residue_name": self.residue_name,
427
+ "chain_id": self.chain_id,
428
+ "residue_number": self.residue_number,
429
+ "insertion_code": self.insertion_code,
430
+ "x": self.x,
431
+ "y": self.y,
432
+ "z": self.z,
433
+ "temperature_factor": self.temperature_factor,
434
+ "occupancy": self.occupancy,
435
+ "segment": self.segment,
436
+ "element": self.element,
437
+ "charge": self.charge,
438
+ }
439
+
440
+ def __reduce__(self):
441
+ cdef dict state = self._state()
442
+ return functools.partial(type(self), **state), ()
443
+
357
444
  def __repr__(self):
358
445
  cdef str ty = type(self).__name__
359
- cdef list args = [
360
- f"serial={self.serial!r}",
361
- f"name={self.name!r}",
362
- f"altloc={self.altloc!r}",
363
- f"residue_name={self.residue_name!r}",
364
- f"chain_id={self.chain_id!r}",
365
- f"residue_number={self.residue_number!r}",
366
- f"x={self.x!r}",
367
- f"y={self.y!r}",
368
- f"z={self.z!r}",
369
- f"segment={self.segment!r}",
370
- f"insertion_code={self.insertion_code!r}",
371
- ]
372
- if self.occupancy:
373
- args.append(f"occupancy={self.occupancy!r}")
374
- if self.temperature_factor:
375
- args.append(f"temperature_factor={self.temperature_factor!r}")
376
- if self.element:
377
- args.append(f"element={self.element!r}")
378
- if self.charge:
379
- args.append(f"charge={self.charge!r}")
446
+ cdef list args = []
447
+ for k,v in self._state().items():
448
+ if v is not None:
449
+ args.append(f"{k}={v!r}")
380
450
  return f"{ty}({', '.join(args)})"
381
451
 
382
452
  def __sizeof__(self):
@@ -385,6 +455,17 @@ cdef class Atom:
385
455
  size += sizeof(_Atom)
386
456
  return size
387
457
 
458
+ def __eq__(self, object other):
459
+ cdef Atom other_
460
+ if not isinstance(other, Atom):
461
+ return NotImplemented
462
+ other_ = other
463
+ # FIXME: it should be possible to do a memcmp here.
464
+ return self._state() == other_._state()
465
+
466
+ def __hash__(self):
467
+ return hash(tuple(self._state().values()))
468
+
388
469
  @property
389
470
  def serial(self):
390
471
  """`int`: The atom serial number.
@@ -484,9 +565,29 @@ cdef class Atom:
484
565
  assert self._atom is not NULL
485
566
  return self._atom.x[2]
486
567
 
568
+ cpdef Atom copy(self):
569
+ """Create a copy of this atom.
570
+
571
+ Returns:
572
+ `~pyjess.Atom`: A newly allocated atom with identical attributes.
573
+
574
+ .. versionadded:: 0.4.0
575
+
576
+ """
577
+ cdef Atom copy = Atom.__new__(Atom)
578
+ copy._atom = <_Atom*> malloc(sizeof(_Atom))
579
+ if copy._atom is NULL:
580
+ raise MemoryError("Failed to allocate atom")
581
+ memcpy(copy._atom, self._atom, sizeof(_Atom))
582
+ return copy
583
+
487
584
 
488
585
  cdef class TemplateAtom:
489
586
  """A single template atom.
587
+
588
+ .. versionadded:: 0.4.0
589
+ Equality, hashing and pickle protocol support.
590
+
490
591
  """
491
592
  cdef object owner
492
593
  cdef bint owned
@@ -630,23 +731,42 @@ cdef class TemplateAtom:
630
731
  raise ValueError(f"Invalid residue name: {name!r}")
631
732
  copy_token(self._atom.resName[m], _name.ljust(3, b'\0'), 3)
632
733
 
734
+ cdef dict _state(self):
735
+ return {
736
+ "chain_id": self.chain_id,
737
+ "residue_number": self.residue_number,
738
+ "x": self.x,
739
+ "y": self.y,
740
+ "z": self.z,
741
+ "residue_names": self.residue_names,
742
+ "atom_names": self.atom_names,
743
+ "distance_weight": self.distance_weight,
744
+ "match_mode": self.match_mode,
745
+ }
746
+
633
747
  def __repr__(self):
634
748
  cdef str ty = type(self).__name__
635
- cdef list args = [
636
- f"chain_id={self.chain_id!r}",
637
- f"residue_number={self.residue_number!r}",
638
- f"x={self.x!r}",
639
- f"y={self.y!r}",
640
- f"z={self.z!r}",
641
- f"residue_names={self.residue_names!r}",
642
- f"atom_names={self.atom_names!r}",
643
- ]
644
- if self.distance_weight:
645
- args.append(f"distance_weight={self.distance_weight!r}")
646
- if self.match_mode:
647
- args.append(f"match_mode={self.match_mode!r}")
749
+ cdef list args = []
750
+ for k, v in self._state().items():
751
+ args.append(f"{k}={v!r}")
648
752
  return f"{ty}({', '.join(args)})"
649
753
 
754
+ def __copy__(self):
755
+ return self.copy()
756
+
757
+ def __eq__(self, object other):
758
+ cdef TemplateAtom other_
759
+ if not isinstance(other, TemplateAtom):
760
+ return NotImplemented
761
+ other_ = other
762
+ return self._state() == other_._state()
763
+
764
+ def __hash__(self):
765
+ return hash(tuple(self._state().values()))
766
+
767
+ def __reduce__(self):
768
+ return functools.partial(type(self), **self._state()), ()
769
+
650
770
  def __sizeof__(self):
651
771
  assert self._atom is not NULL
652
772
 
@@ -739,9 +859,25 @@ cdef class TemplateAtom:
739
859
  assert self._atom is not NULL
740
860
  return self._atom.distWeight
741
861
 
862
+ cpdef TemplateAtom copy(self):
863
+ """Create a copy of this template atom.
864
+
865
+ Returns:
866
+ `~pyjess.TemplateAtom`: A new template atom object with
867
+ identical attributes.
868
+
869
+ .. versionadded:: 0.4.0
870
+
871
+ """
872
+ return type(self)(**self._state())
873
+
742
874
 
743
875
  cdef class Template:
744
876
  """A template, as a sequence of `TemplateAtom` objects.
877
+
878
+ .. versionadded:: 0.4.0
879
+ Equality, hashing and pickle protocol support.
880
+
745
881
  """
746
882
  cdef object owner
747
883
  cdef bint owned
@@ -858,6 +994,9 @@ cdef class Template:
858
994
  residues = { self._tess.atom[i].resSeq for i in range(count) }
859
995
  self._tess.dim = len(residues)
860
996
 
997
+ def __copy__(self):
998
+ return self.copy()
999
+
861
1000
  def __len__(self):
862
1001
  assert self._tpl is not NULL
863
1002
  return self._tess.count
@@ -884,6 +1023,28 @@ cdef class Template:
884
1023
  atom._atom = self._tess.atom[index_]
885
1024
  return atom
886
1025
 
1026
+ def __eq__(self, object other):
1027
+ cdef Template other_
1028
+ if not isinstance(other, Template):
1029
+ return NotImplemented
1030
+ other_ = other
1031
+ if self.id != other_.id:
1032
+ return False
1033
+ if self.dimension != other_.dimension:
1034
+ return False
1035
+ if len(self) != len(other_):
1036
+ return False
1037
+ return all(x == y for x,y in zip(self, other_))
1038
+
1039
+ def __hash__(self):
1040
+ return hash(
1041
+ self.id,
1042
+ *(hash(x) for x in self)
1043
+ )
1044
+
1045
+ def __reduce__(self):
1046
+ return type(self), (list(self), self.id)
1047
+
887
1048
  def __sizeof__(self):
888
1049
  assert self._tess is not NULL
889
1050
 
@@ -927,6 +1088,12 @@ cdef class Template:
927
1088
  assert self._tess is not NULL
928
1089
  return self._tess.dim
929
1090
 
1091
+ cpdef Template copy(self):
1092
+ return Template(
1093
+ self,
1094
+ self.id
1095
+ )
1096
+
930
1097
 
931
1098
  cdef class Query:
932
1099
  """A query over templates with a given molecule.
@@ -1177,6 +1344,10 @@ cdef class Hit:
1177
1344
 
1178
1345
  cdef class Jess:
1179
1346
  """A handle to run Jess over a list of templates.
1347
+
1348
+ .. versionadded:: 0.4.0
1349
+ Equality, hashing and pickle protocol support.
1350
+
1180
1351
  """
1181
1352
  cdef _Jess* _jess
1182
1353
  cdef dict _indices
@@ -1222,6 +1393,22 @@ cdef class Jess:
1222
1393
  self._templates.append(template)
1223
1394
  self.length += 1
1224
1395
 
1396
+ def __copy__(self):
1397
+ return self.copy()
1398
+
1399
+ def __reduce__(self):
1400
+ return type(self), (self._templates,)
1401
+
1402
+ def __eq__(self, object other):
1403
+ cdef Jess other_
1404
+ if not isinstance(other, Jess):
1405
+ return NotImplemented
1406
+ other_ = other
1407
+ return self._templates == other_._templates
1408
+
1409
+ def __hash__(self):
1410
+ return hash(tuple(hash(t) for t in self._templates))
1411
+
1225
1412
  def __len__(self):
1226
1413
  return self.length
1227
1414
 
@@ -1239,6 +1426,17 @@ cdef class Jess:
1239
1426
  raise IndexError(index)
1240
1427
  return self._templates[index_]
1241
1428
 
1429
+ cpdef Jess copy(self):
1430
+ """Create a copy of the `Jess` object.
1431
+
1432
+ Returns:
1433
+ `~pyjess.Jess`: A `Jess` object containing the same templates.
1434
+
1435
+ .. versionadded:: 0.4.0
1436
+
1437
+ """
1438
+ return type(self)(self._templates)
1439
+
1242
1440
  def query(
1243
1441
  self,
1244
1442
  Molecule molecule,
pyjess/tests/test_atom.py CHANGED
@@ -1,4 +1,6 @@
1
+ import ctypes
1
2
  import unittest
3
+ import pickle
2
4
 
3
5
  from .._jess import Atom
4
6
 
@@ -25,6 +27,25 @@ class TestAtom(unittest.TestCase):
25
27
  self.assertEqual(atom.segment, '')
26
28
  self.assertEqual(atom.element, 'C')
27
29
  self.assertEqual(atom.charge, 0)
30
+
31
+ def test_init_consistency(self):
32
+ loaded = Atom.loads("ATOM 39 CA PRO A 469 -14.948 2.091 10.228 1.00 27.71 C")
33
+ created = Atom(
34
+ serial=loaded.serial,
35
+ name=loaded.name,
36
+ residue_name=loaded.residue_name,
37
+ chain_id=loaded.chain_id,
38
+ altloc=loaded.altloc,
39
+ insertion_code=loaded.insertion_code,
40
+ residue_number=loaded.residue_number,
41
+ x=loaded.x,
42
+ y=loaded.y,
43
+ z=loaded.z,
44
+ occupancy=loaded.occupancy,
45
+ temperature_factor=loaded.temperature_factor,
46
+ element=loaded.element,
47
+ )
48
+ self.assertEqual(loaded, created)
28
49
 
29
50
  def test_init_invalid_chain_id(self):
30
51
  self.assertRaises(ValueError, self._create_atom, chain_id="too long")
@@ -35,6 +56,13 @@ class TestAtom(unittest.TestCase):
35
56
  def test_init_invalid_residue_name(self):
36
57
  self.assertRaises(ValueError, self._create_atom, residue_name="too long")
37
58
 
59
+ def test_hash(self):
60
+ a1 = self._create_atom()
61
+ a2 = self._create_atom()
62
+ self.assertEqual(a1, a2)
63
+ self.assertEqual(hash(a1), hash(a2))
64
+ self.assertIsNot(a1, a2)
65
+
38
66
  def test_repr_roundtrip(self):
39
67
  atom = self._create_atom()
40
68
  copy = eval(repr(atom))
@@ -51,4 +79,24 @@ class TestAtom(unittest.TestCase):
51
79
  self.assertEqual(atom.charge, copy.charge)
52
80
  self.assertEqual(atom.x, copy.x)
53
81
  self.assertEqual(atom.y, copy.y)
54
- self.assertEqual(atom.z, copy.z)
82
+ self.assertEqual(atom.z, copy.z)
83
+ self.assertEqual(atom, copy)
84
+
85
+ def test_pickle_roundtrip(self):
86
+ atom = self._create_atom()
87
+ copy = pickle.loads(pickle.dumps(atom))
88
+ self.assertEqual(atom.serial, copy.serial)
89
+ self.assertEqual(atom.altloc, copy.altloc)
90
+ self.assertEqual(atom.name, copy.name)
91
+ self.assertEqual(atom.residue_name, copy.residue_name)
92
+ self.assertEqual(atom.residue_number, copy.residue_number)
93
+ self.assertEqual(atom.element, copy.element)
94
+ self.assertEqual(atom.insertion_code, copy.insertion_code)
95
+ self.assertEqual(atom.chain_id, copy.chain_id)
96
+ self.assertEqual(atom.occupancy, copy.occupancy)
97
+ self.assertEqual(atom.temperature_factor, copy.temperature_factor)
98
+ self.assertEqual(atom.charge, copy.charge)
99
+ self.assertEqual(atom.x, copy.x)
100
+ self.assertEqual(atom.y, copy.y)
101
+ self.assertEqual(atom.z, copy.z)
102
+ self.assertEqual(atom, copy)
pyjess/tests/test_jess.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import math
2
2
  import unittest
3
3
  import sys
4
+ import pickle
4
5
 
5
6
  from .._jess import Template, Molecule, Jess
6
7
  from .utils import files
@@ -20,6 +21,26 @@ class TestJess(unittest.TestCase):
20
21
  hits = jess.query(mol, 2, 2, 4)
21
22
  self.assertRaises(StopIteration, next, hits)
22
23
 
24
+ @unittest.skipUnless(files, "importlib.resources not available")
25
+ def test_copy(self):
26
+ with files(data).joinpath("template_01.qry").open() as f:
27
+ template1 = Template.load(f)
28
+ with files(data).joinpath("template_02.qry").open() as f:
29
+ template2 = Template.load(f)
30
+ jess = Jess([template1, template2])
31
+ copy = jess.copy()
32
+ self.assertEqual(jess, copy)
33
+
34
+ @unittest.skipUnless(files, "importlib.resources not available")
35
+ def test_pickle_roundtrip(self):
36
+ with files(data).joinpath("template_01.qry").open() as f:
37
+ template1 = Template.load(f)
38
+ with files(data).joinpath("template_02.qry").open() as f:
39
+ template2 = Template.load(f)
40
+ jess = Jess([template1, template2])
41
+ copy = pickle.loads(pickle.dumps(jess))
42
+ self.assertEqual(jess, copy)
43
+
23
44
  @unittest.skipUnless(sys.implementation.name == "cpython", "only available on CPython")
24
45
  @unittest.skipUnless(files, "importlib.resources not available")
25
46
  def test_sizeof(self):
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import pickle
2
3
  import unittest
3
4
  import tempfile
4
5
  import textwrap
@@ -83,4 +84,42 @@ class TestMolecule(unittest.TestCase):
83
84
  self.assertEqual(mol2.id, mol.id)
84
85
  self.assertEqual(len(mol2), 2)
85
86
  self.assertEqual(mol2[0].name, "CA")
86
- self.assertEqual(mol2[1].name, "C")
87
+ self.assertEqual(mol2[1].name, "C")
88
+
89
+ def test_hash(self):
90
+ atoms = [
91
+ self._create_atom(serial=1, name='N'),
92
+ self._create_atom(serial=2, name='CA'),
93
+ self._create_atom(serial=3, name='C'),
94
+ self._create_atom(serial=4, name='O'),
95
+ ]
96
+ mol1 = Molecule(atoms)
97
+ mol2 = Molecule(atoms)
98
+ self.assertEqual(hash(mol1), hash(mol2))
99
+ self.assertEqual(mol1, mol2)
100
+
101
+ def test_copy(self):
102
+ atoms = [
103
+ self._create_atom(serial=1, name='N'),
104
+ self._create_atom(serial=2, name='CA'),
105
+ self._create_atom(serial=3, name='C'),
106
+ self._create_atom(serial=4, name='O'),
107
+ ]
108
+ mol1 = Molecule(atoms)
109
+ mol2 = mol1.copy()
110
+ self.assertEqual(list(mol1), list(mol2))
111
+ self.assertEqual(mol1.id, mol2.id)
112
+ self.assertEqual(mol1, mol2)
113
+
114
+ def test_pickle_roundtrip(self):
115
+ atoms = [
116
+ self._create_atom(serial=1, name='N'),
117
+ self._create_atom(serial=2, name='CA'),
118
+ self._create_atom(serial=3, name='C'),
119
+ self._create_atom(serial=4, name='O'),
120
+ ]
121
+ mol1 = Molecule(atoms)
122
+ mol2 = pickle.loads(pickle.dumps(mol1))
123
+ self.assertEqual(list(mol1), list(mol2))
124
+ self.assertEqual(mol1.id, mol2.id)
125
+ self.assertEqual(mol1, mol2)
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import pickle
2
3
  import unittest
3
4
  import tempfile
4
5
  import textwrap
@@ -85,3 +86,15 @@ class TestTemplate(unittest.TestCase):
85
86
  tpl1 = Template.loads(TEMPLATE, id="tpl1")
86
87
  tpl2 = tpl1[1:4]
87
88
  self.assertEqual(len(tpl2), 3)
89
+
90
+ def test_copy(self):
91
+ tpl1 = Template.loads(TEMPLATE, id="tpl1")
92
+ tpl2 = tpl1.copy()
93
+ self.assertEqual(len(tpl1), len(tpl2))
94
+ self.assertEqual(tpl1, tpl2)
95
+
96
+ def test_pickle_roundtrip(self):
97
+ tpl1 = Template.loads(TEMPLATE, id="tpl1")
98
+ tpl2 = pickle.loads(pickle.dumps(tpl1))
99
+ self.assertEqual(len(tpl1), len(tpl2))
100
+ self.assertEqual(tpl1, tpl2)
@@ -1,5 +1,6 @@
1
1
  import unittest
2
2
  import sys
3
+ import pickle
3
4
 
4
5
  from .._jess import TemplateAtom
5
6
 
@@ -43,8 +44,33 @@ class TestTemplateAtom(unittest.TestCase):
43
44
  def test_init_invalid_residue_name(self):
44
45
  self.assertRaises(ValueError, self._create_atom, residue_names=["something"])
45
46
 
47
+ def test_init_consistency(self):
48
+ loaded = TemplateAtom.loads("ATOM 1 NE ARG A1136 3.953 0.597 -1.721 K")
49
+ created = TemplateAtom(
50
+ chain_id=loaded.chain_id,
51
+ residue_number=loaded.residue_number,
52
+ x=loaded.x,
53
+ y=loaded.y,
54
+ z=loaded.z,
55
+ residue_names=loaded.residue_names,
56
+ atom_names=loaded.atom_names,
57
+ distance_weight=loaded.distance_weight,
58
+ match_mode=loaded.match_mode
59
+ )
60
+ for attribute in ("atom_names", "residue_names", "chain_id", "x", "y", "z", "match_mode"):
61
+ self.assertEqual(getattr(loaded, attribute), getattr(created, attribute))
62
+ self.assertEqual(loaded, created)
63
+
46
64
  def test_repr_roundtrip(self):
47
65
  atom = self._create_atom()
48
66
  copy = eval(repr(atom))
49
67
  for attribute in ("atom_names", "residue_names", "chain_id", "x", "y", "z", "match_mode"):
50
- self.assertEqual(getattr(copy, attribute), getattr(atom, attribute))
68
+ self.assertEqual(getattr(copy, attribute), getattr(atom, attribute))
69
+ self.assertEqual(atom, copy)
70
+
71
+ def test_pickle_roundtrip(self):
72
+ atom = self._create_atom()
73
+ copy = pickle.loads(pickle.dumps(atom))
74
+ for attribute in ("atom_names", "residue_names", "chain_id", "x", "y", "z", "match_mode"):
75
+ self.assertEqual(getattr(copy, attribute), getattr(atom, attribute))
76
+ self.assertEqual(atom, copy)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyjess
3
- Version: 0.3.3
3
+ Version: 0.4.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>
@@ -25,7 +25,7 @@ License: MIT License
25
25
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
26
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
27
  SOFTWARE.
28
- Classifier: Development Status :: 3 - Alpha
28
+ Classifier: Development Status :: 4 - Beta
29
29
  Classifier: Intended Audience :: Developers
30
30
  Classifier: Intended Audience :: Science/Research
31
31
  Classifier: License :: OSI Approved :: MIT License
@@ -1,21 +1,21 @@
1
- pyjess-0.3.3.dist-info/RECORD,,
2
- pyjess-0.3.3.dist-info/WHEEL,sha256=wokco-bIt7hexeW1xGb7Dite8ygk3pPkIkE_c7M2i5M,114
3
- pyjess-0.3.3.dist-info/METADATA,sha256=wjCjJAgH4YolUjgoysZ9FIIPDcWeXQcNs7MY6nWp1Bc,10786
4
- pyjess-0.3.3.dist-info/licenses/COPYING,sha256=W3hXwpT6UtiSFrO8yeDddZLU5tKIAX238e0N5slPQGA,1098
5
- pyjess/_jess.pyi,sha256=kpvEKrznUbXs-q8RDiIWZmX8y55AE_ugrGR45a4SpD0,5849
1
+ pyjess-0.4.0.dist-info/RECORD,,
2
+ pyjess-0.4.0.dist-info/WHEEL,sha256=wokco-bIt7hexeW1xGb7Dite8ygk3pPkIkE_c7M2i5M,114
3
+ pyjess-0.4.0.dist-info/METADATA,sha256=vaDoU5JzpxXjRTl-M43Stw7evHX75QHWTexO_TF7_DE,10785
4
+ pyjess-0.4.0.dist-info/licenses/COPYING,sha256=W3hXwpT6UtiSFrO8yeDddZLU5tKIAX238e0N5slPQGA,1098
5
+ pyjess/_jess.pyi,sha256=MlI0vWppDF11o9iZs635jn7gUpebG9DF_KNvhZrymuI,6887
6
6
  pyjess/CMakeLists.txt,sha256=Oa0pniEQx9jXyFCJGyrswn9ahWSSVuW1madyeP6StoI,35
7
- pyjess/_jess.cpython-39-darwin.so,sha256=OBjDsTSKKUzUqlpUHYvXFyxkQ-BHckKeeYtmwq_CdIY,263496
7
+ pyjess/_jess.cpython-39-darwin.so,sha256=pS16s7CwBH84G8Up6IHOJL6JMkBH0lRwf8OQEGYYfrI,305120
8
8
  pyjess/__init__.py,sha256=h4XXLdS4FnyVa-MBs_k3eZMG1jWxeiOJnwfBaJA9gyQ,745
9
9
  pyjess/.gitignore,sha256=uQBOufp4v50qn0aZKv6zbSo00cjfB-v9KySog7rlmIU,19
10
- pyjess/_jess.pyx,sha256=EV6aLunqq8lmqXd5o0p_04YN6LmZJx8Td76Ep44ZSus,41745
10
+ pyjess/_jess.pyx,sha256=JFFC_PrVMAjaoLeLsyfkC9kFKdDCCNSwt7yvgQ6mnVo,47039
11
11
  pyjess/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  pyjess/tests/__init__.py,sha256=MdHWtr6A8S4TBWlkoj4olFK2FXAGc5uJdbWtgFrDLpk,528
13
- pyjess/tests/test_molecule.py,sha256=i9nWe7RRw06hND9rtGUkjIHzfAz3ZVKH0GVT1PjfK04,3407
13
+ pyjess/tests/test_molecule.py,sha256=qltjlOKvc2mvrW1DhA20AdKpu2DCEELx-cfbNmldvdo,4754
14
14
  pyjess/tests/utils.py,sha256=dsaphex7qomJCvSHWnVy79iYDPGiL59xqGAtRoVAeWc,196
15
- pyjess/tests/test_atom.py,sha256=RziIlTWteOVawhbRPTmcBDGjf6FrSlInqnMQLR9UDQY,2450
16
- pyjess/tests/test_jess.py,sha256=bWPsEGcNqr3gjj6iWqdwGX8IT5jaTTkTexOKhMPHWvw,7619
17
- pyjess/tests/test_template.py,sha256=WM5DRvFxa8NEUl_U4PWppsFq2Dw6pZQskbnCu6jgZm8,3465
18
- pyjess/tests/test_template_atom.py,sha256=diQ9TCsStLeUkk4lUHkL-oQZWgMzDhW_LMnjik5MM1Y,1781
15
+ pyjess/tests/test_atom.py,sha256=wkl45ctTU-7kWnGCjr1hHNEQBRRynzYyNoq-zPXR2V0,4348
16
+ pyjess/tests/test_jess.py,sha256=yAQEbqodzKhjzYd2nxx1_H6b4AbJjbYHHjqFnrHkSUM,8500
17
+ pyjess/tests/test_template.py,sha256=kUDJ-A9eNsL8aE51Jr1HLKmvKflTUuMGT2D3vKUxp-o,3887
18
+ pyjess/tests/test_template_atom.py,sha256=fO6ChincKARc86J2zpDc0xrybSb4CQnyzoo2kJCAkoU,2917
19
19
  pyjess/tests/data/pdb1lnb.pdb,sha256=E9Jjy4qQ75O1UKIXcVyVJHE1XDNx1Rb7ENPVrehW6N8,270054
20
20
  pyjess/tests/data/1AMY.pdb,sha256=t2CaGLdOyPrhyeMpe1TbwZ8u7QmfxCIG1Pit8-vzvgo,319221
21
21
  pyjess/tests/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
File without changes