pyjess 0.6.0__pp38-pypy38_pp73-win_amd64.whl → 0.7.0__pp38-pypy38_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.

@@ -0,0 +1,26 @@
1
+ REMARK TEMPLATE
2
+ REMARK CLUSTER 1_1_1
3
+ REMARK REPRESENTING 1 CATALYTIC SITES
4
+ REMARK ID 1sur_AA204-AA208-A204-A208
5
+ REMARK MCSA_ID 279
6
+ REMARK PDB_ID 1sur
7
+ REMARK UNIPROT_ID P17854
8
+ REMARK EC 1.8.4.8
9
+ REMARK ENZYME PAPS REDUCTASE
10
+ REMARK EXPERIMENTAL_METHOD X-ray diffraction
11
+ REMARK RESOLUTION 2.0
12
+ REMARK ORGANISM_NAME Escherichia coli
13
+ REMARK ORGANISM_ID 562
14
+ ATOM 0 NE1ZTRPAA 204 38.872 1.684 110.358 W 0.00
15
+ ATOM 0 CZ2ZTRPAA 204 41.345 1.476 110.794 W 0.00
16
+ ATOM 0 CH2ZTRPAA 204 42.567 1.626 110.193 W 0.00
17
+ ATOM 3 CE1ZTYRAA 208 40.509 -1.903 99.734 Y 0.00
18
+ ATOM 3 CZ ZTYRAA 208 41.503 -1.926 98.771 Y 0.00
19
+ ATOM 1 OH ZTYRAA 208 41.320 -2.643 97.607 Y 0.00
20
+ ATOM 0 NE1ZTRP A 204 43.868 1.684 54.342 W 0.00
21
+ ATOM 0 CZ2ZTRP A 204 41.395 1.476 53.906 W 0.00
22
+ ATOM 0 CH2ZTRP A 204 40.173 1.626 54.507 W 0.00
23
+ ATOM 3 CE1ZTYR A 208 42.231 -1.903 64.966 Y 0.00
24
+ ATOM 3 CZ ZTYR A 208 41.237 -1.926 65.929 Y 0.00
25
+ ATOM 1 OH ZTYR A 208 41.420 -2.643 67.093 Y 0.00
26
+ END
@@ -0,0 +1,23 @@
1
+ REMARK TEMPLATE
2
+ REMARK CLUSTER 4_1_2
3
+ REMARK REPRESENTING 10 CATALYTIC SITES
4
+ REMARK ID 2om2_A48-A43-A200-A204-A178
5
+ REMARK MCSA_ID 533
6
+ REMARK PDB_ID 2om2
7
+ REMARK UNIPROT_ID P63096
8
+ REMARK EC None
9
+ REMARK ENZYME Guanine nucleotide-binding protein G(i), alpha-1 subunit, Regulator of G-protein signalling 14 GoLoco motif peptide
10
+ REMARK EXPERIMENTAL_METHOD X-ray diffraction
11
+ REMARK RESOLUTION 2.2
12
+ REMARK ORGANISM_NAME Homo sapiens
13
+ REMARK ORGANISM_ID 9606
14
+ ATOM 3 CA ZTHR A 48 33.911 -23.654 9.552 T 0.53
15
+ ATOM 3 CB ZTHR A 48 34.739 -23.239 8.310 T 0.53
16
+ ATOM 1 OG1ZTHR A 48 36.129 -23.451 8.587 T 0.53
17
+ ATOM 3 CD ZGLU A 43 42.537 -29.462 7.690 E 2.08
18
+ ATOM 3 OE1ZGLU A 43 42.721 -29.948 8.832 E 2.08
19
+ ATOM 3 OE2ZGLU A 43 42.492 -28.227 7.487 E 2.08
20
+ ATOM 0 CZ ZARG A 178 43.760 -26.926 10.370 R 2.35
21
+ ATOM 3 NH1ZARG A 178 42.887 -26.457 9.483 R 2.35
22
+ ATOM 3 NH2ZARG A 178 44.119 -28.202 10.324 R 2.35
23
+ END
@@ -0,0 +1,63 @@
1
+ HEADER TRANSFERASE 14-SEP-15 XXXX
2
+ TITLE CRYSTAL STRUCTURE OF HUMAN QUINOLINATE PHOSPHORIBOSYLTRANSFERASE
3
+ KEYWDS QUINOLINATE PHOSPHORIBOSYLTRANSFERASE, NAD BIOSYNTHESIS, NADC,
4
+ KEYWDS 2 TRANSFERASE
5
+ EXPDTA X-RAY DIFFRACTION
6
+ ATOM 8512 CA GLU E 25 11.770 12.101 -30.191 1.00 63.41 C
7
+ ATOM 8513 C GLU E 25 10.568 12.472 -31.076 1.00 64.47 C
8
+ ATOM 8514 O GLU E 25 10.268 13.659 -31.267 1.00 66.51 O
9
+ ATOM 8515 CB GLU E 25 12.924 11.548 -31.040 1.00 54.61 C
10
+ ATOM 8516 CG GLU E 25 13.586 12.566 -31.990 1.00 63.15 C
11
+ ATOM 8517 CD GLU E 25 14.447 11.902 -33.089 1.00 67.48 C
12
+ ATOM 8518 OE1 GLU E 25 14.041 10.820 -33.594 1.00 62.27 O
13
+ ATOM 8519 OE2 GLU E 25 15.524 12.460 -33.441 1.00 60.34 O
14
+ ATOM 9342 N THR E 141 25.198 6.504 -40.031 1.00 49.91 N
15
+ ATOM 9343 CA THR E 141 24.509 7.667 -39.509 1.00 44.30 C
16
+ ATOM 9344 C THR E 141 24.749 7.734 -38.010 1.00 45.10 C
17
+ ATOM 9345 O THR E 141 24.587 6.721 -37.320 1.00 49.53 O
18
+ ATOM 9346 CB THR E 141 23.029 7.628 -39.780 1.00 40.72 C
19
+ ATOM 9347 OG1 THR E 141 22.810 7.411 -41.175 1.00 41.17 O
20
+ ATOM 9348 CG2 THR E 141 22.434 8.970 -39.391 1.00 45.90 C
21
+ ATOM 9371 N ARG E 145 23.925 3.473 -35.427 1.00 47.01 N
22
+ ATOM 9372 CA ARG E 145 24.909 2.456 -35.770 1.00 50.52 C
23
+ ATOM 9373 C ARG E 145 24.310 1.065 -35.674 1.00 48.71 C
24
+ ATOM 9374 O ARG E 145 24.441 0.255 -36.598 1.00 51.91 O
25
+ ATOM 9375 CB ARG E 145 26.130 2.594 -34.858 1.00 58.30 C
26
+ ATOM 9376 CG ARG E 145 27.168 1.485 -34.964 1.00 56.80 C
27
+ ATOM 9377 CD ARG E 145 28.472 1.971 -34.388 1.00 64.04 C
28
+ ATOM 9378 NE ARG E 145 29.055 3.032 -35.219 1.00 79.49 N
29
+ ATOM 9379 CZ ARG E 145 30.083 3.803 -34.855 1.00 77.12 C
30
+ ATOM 9380 NH1 ARG E 145 30.556 4.728 -35.687 1.00 73.39 N
31
+ ATOM 9381 NH2 ARG E 145 30.640 3.659 -33.657 1.00 73.96 N
32
+ TER 10377 GLU E 289
33
+ ATOM 10557 N GLU F 25 28.218 6.773 -28.434 1.00 49.27 N
34
+ ATOM 10558 CA GLU F 25 28.033 5.648 -29.351 1.00 50.31 C
35
+ ATOM 10559 C GLU F 25 29.332 5.286 -30.079 1.00 60.13 C
36
+ ATOM 10560 O GLU F 25 29.595 4.101 -30.330 1.00 65.02 O
37
+ ATOM 10561 CB GLU F 25 26.927 5.992 -30.358 1.00 47.14 C
38
+ ATOM 10562 CG GLU F 25 26.451 4.872 -31.285 1.00 50.18 C
39
+ ATOM 10563 CD GLU F 25 25.906 5.376 -32.657 1.00 59.80 C
40
+ ATOM 10564 OE1 GLU F 25 26.512 6.292 -33.298 1.00 59.65 O
41
+ ATOM 10565 OE2 GLU F 25 24.859 4.843 -33.098 1.00 56.23 O
42
+ ATOM 11388 N THR F 141 15.709 9.909 -40.611 1.00 43.47 N
43
+ ATOM 11389 CA THR F 141 16.342 8.887 -39.789 1.00 44.34 C
44
+ ATOM 11390 C THR F 141 16.089 9.169 -38.311 1.00 43.54 C
45
+ ATOM 11391 O THR F 141 16.316 10.292 -37.851 1.00 43.12 O
46
+ ATOM 11392 CB THR F 141 17.841 8.832 -40.073 1.00 43.56 C
47
+ ATOM 11393 OG1 THR F 141 18.060 8.851 -41.489 1.00 42.17 O
48
+ ATOM 11394 CG2 THR F 141 18.442 7.560 -39.505 1.00 42.11 C
49
+ ATOM 11417 N ARG F 145 16.934 13.489 -36.682 1.00 42.82 N
50
+ ATOM 11418 CA ARG F 145 15.914 14.290 -37.338 1.00 43.06 C
51
+ ATOM 11419 C ARG F 145 16.352 15.741 -37.468 1.00 47.81 C
52
+ ATOM 11420 O ARG F 145 16.166 16.358 -38.527 1.00 49.55 O
53
+ ATOM 11421 CB ARG F 145 14.611 14.145 -36.548 1.00 52.52 C
54
+ ATOM 11422 CG ARG F 145 13.463 15.093 -36.850 1.00 52.54 C
55
+ ATOM 11423 CD ARG F 145 12.613 15.162 -35.593 1.00 56.72 C
56
+ ATOM 11424 NE ARG F 145 11.200 15.447 -35.811 1.00 61.47 N
57
+ ATOM 11425 CZ ARG F 145 10.285 15.372 -34.842 1.00 73.92 C
58
+ ATOM 11426 NH1 ARG F 145 9.005 15.653 -35.090 1.00 71.75 N
59
+ ATOM 11427 NH2 ARG F 145 10.657 15.007 -33.615 1.00 69.36 N
60
+ TER 12486 GLU F 289
61
+ ENDMDL
62
+ MASTER 0 0 0 76 71 0 0 612499 6 0 144
63
+ END
@@ -0,0 +1,78 @@
1
+ # coding: utf-8
2
+ """Test doctest contained tests in every file of the module.
3
+ """
4
+
5
+ import configparser
6
+ import doctest
7
+ import importlib
8
+ import json
9
+ import gzip
10
+ import os
11
+ import pkgutil
12
+ import re
13
+ import shutil
14
+ import sys
15
+ import types
16
+ import warnings
17
+ from unittest import mock
18
+
19
+ import pyjess
20
+
21
+
22
+ def _load_tests_from_module(tests, module, globs, setUp=None, tearDown=None):
23
+ """Load tests from module, iterating through submodules."""
24
+ for attr in (getattr(module, x) for x in dir(module) if not x.startswith("_")):
25
+ if isinstance(attr, types.ModuleType):
26
+ suite = doctest.DocTestSuite(
27
+ attr,
28
+ globs,
29
+ setUp=setUp,
30
+ tearDown=tearDown,
31
+ optionflags=+doctest.ELLIPSIS,
32
+ )
33
+ tests.addTests(suite)
34
+ return tests
35
+
36
+
37
+ def load_tests(loader, tests, ignore):
38
+ """`load_test` function used by unittest to find the doctests."""
39
+ _current_cwd = os.getcwd()
40
+
41
+ def setUp(self):
42
+ warnings.simplefilter("ignore")
43
+ os.chdir(os.path.realpath(os.path.join(__file__, os.path.pardir, "data")))
44
+
45
+ def tearDown(self):
46
+ os.chdir(_current_cwd)
47
+ warnings.simplefilter(warnings.defaultaction)
48
+
49
+ # doctests are not compatible with `green`, so we may want to bail out
50
+ # early if `green` is running the tests
51
+ if sys.argv[0].endswith("green"):
52
+ return tests
53
+
54
+ # recursively traverse all library submodules and load tests from them
55
+ packages = [None, pyjess]
56
+ for pkg in iter(packages.pop, None):
57
+ for (_, subpkgname, subispkg) in pkgutil.walk_packages(pkg.__path__):
58
+ # do not import __main__ module to avoid side effects!
59
+ if subpkgname == "__main__" or subpkgname.startswith("tests") or subpkgname.startswith("cli"):
60
+ continue
61
+ # import the submodule and add it to the tests
62
+ module = importlib.import_module(".".join([pkg.__name__, subpkgname]))
63
+ globs = dict(pyjess=pyjess, **module.__dict__)
64
+ tests.addTests(
65
+ doctest.DocTestSuite(
66
+ module,
67
+ globs=globs,
68
+ setUp=setUp,
69
+ tearDown=tearDown,
70
+ optionflags=+doctest.ELLIPSIS,
71
+ )
72
+ )
73
+ # if the submodule is a package, we need to process its submodules
74
+ # as well, so we add it to the package queue
75
+ if subispkg and subpkgname != "tests":
76
+ packages.append(module)
77
+
78
+ return tests
pyjess/tests/test_hit.py CHANGED
@@ -2,6 +2,7 @@ import math
2
2
  import unittest
3
3
  import sys
4
4
  import pickle
5
+ import textwrap
5
6
 
6
7
  from .._jess import Template, Molecule, Jess
7
8
  from .utils import files
@@ -9,12 +10,14 @@ from . import data
9
10
 
10
11
 
11
12
  class TestHit(unittest.TestCase):
13
+
14
+ maxDiff = None
12
15
 
13
16
  @unittest.skipUnless(files, "importlib.resources not available")
14
17
  @classmethod
15
18
  def setUpClass(cls):
16
19
  with files(data).joinpath("template_01.qry").open() as f:
17
- template = Template.load(f)
20
+ template = Template.load(f, id="T1")
18
21
  jess = Jess([template])
19
22
  with files(data).joinpath("pdb1lnb.pdb").open() as f:
20
23
  molecule = Molecule.load(f)
@@ -30,4 +33,25 @@ class TestHit(unittest.TestCase):
30
33
  self.assertEqual(self.hit.template, hit.template)
31
34
  self.assertListEqual(self.hit.atoms(transform=True), hit.atoms(transform=True))
32
35
  self.assertListEqual(self.hit.atoms(transform=False), hit.atoms(transform=False))
33
- self.assertEqual(self.hit.molecule(transform=False), hit.molecule(transform=False))
36
+ self.assertEqual(self.hit.molecule(transform=False), hit.molecule(transform=False))
37
+
38
+ def test_dumps(self):
39
+ expected = textwrap.dedent(
40
+ """
41
+ REMARK 1LNB 0.555 T1 Det= 1.0 log(E)~ -2.04
42
+ ATOM 1112 ND1 HIS E 142 4.157 -2.574 4.091 1.00 10.22
43
+ ATOM 1115 NE2 HIS E 142 3.805 -2.316 1.890 1.00 16.00
44
+ ATOM 1123 OE1 GLU E 143 0.480 -4.544 -0.363 1.00 14.72
45
+ ATOM 1124 OE2 GLU E 143 1.870 -6.097 0.337 1.00 22.48
46
+ ATOM 1145 CG HIS E 146 0.031 0.017 0.019 1.00 12.45
47
+ ATOM 1146 ND1 HIS E 146 0.823 1.160 0.000 1.00 15.79
48
+ ATOM 1147 CD2 HIS E 146 0.878 -1.064 0.006 1.00 20.85
49
+ ATOM 1148 CE1 HIS E 146 2.115 0.777 -0.013 1.00 13.50
50
+ ATOM 1149 NE2 HIS E 146 2.181 -0.553 -0.032 1.00 12.44
51
+ ATOM 1298 OE1 GLU E 166 6.432 -0.605 1.056 1.00 16.03
52
+ ATOM 1299 OE2 GLU E 166 4.839 0.070 -0.343 1.00 19.31
53
+ ENDMDL
54
+ """
55
+ ).strip()
56
+ actual = self.hit.dumps().strip()
57
+ self.assertMultiLineEqual(actual, expected)
pyjess/tests/test_jess.py CHANGED
@@ -92,7 +92,6 @@ class TestJess(unittest.TestCase):
92
92
  self.assertEqual(len(jess[:1]), 1)
93
93
  self.assertEqual(len(jess[1:]), 0)
94
94
 
95
-
96
95
  @unittest.skipUnless(files, "importlib.resources not available")
97
96
  def test_multiple_query_split(self):
98
97
  with files(data).joinpath("template_01.qry").open() as f:
@@ -132,6 +131,29 @@ class TestJess(unittest.TestCase):
132
131
  hits = list(jess.query(molecule, 1, 2, 2))
133
132
  self.assertIsInstance(hits[0].template, MyTemplate)
134
133
 
134
+ @unittest.skipUnless(files, "importlib.resources not available")
135
+ def test_query_max_candidates(self):
136
+ with files(data).joinpath("template_01.qry").open() as f:
137
+ template = Template.load(f)
138
+ jess = Jess([template])
139
+ with files(data).joinpath("pdb1lnb.pdb").open() as f:
140
+ molecule = Molecule.load(f)
141
+
142
+ hits = list(jess.query(molecule, 2, 5, 5))
143
+ self.assertEqual(len(hits), 3)
144
+
145
+ hits = list(jess.query(molecule, 2, 5, 5, max_candidates=3))
146
+ self.assertEqual(len(hits), 3)
147
+
148
+ hits = list(jess.query(molecule, 2, 5, 5, max_candidates=2))
149
+ self.assertEqual(len(hits), 2)
150
+
151
+ hits = list(jess.query(molecule, 2, 5, 5, max_candidates=1))
152
+ self.assertEqual(len(hits), 1)
153
+
154
+ with self.assertRaises(ValueError):
155
+ hits = list(jess.query(molecule, 2, 5, 5, max_candidates=-1))
156
+
135
157
  @unittest.skipUnless(files, "importlib.resources not available")
136
158
  def test_query(self):
137
159
  with files(data).joinpath("template_01.qry").open() as f:
@@ -311,3 +333,42 @@ class TestJess(unittest.TestCase):
311
333
  self.assertAlmostEqual(atom.occupancy, float(atom_line[55:61]), places=3)
312
334
  self.assertAlmostEqual(atom.temperature_factor, float(atom_line[61:67]), places=3)
313
335
 
336
+ @unittest.skipUnless(files, "importlib.resources not available")
337
+ def test_ignore_chain_none(self):
338
+ with files(data).joinpath("4.1.2.tpl").open() as f:
339
+ template = Template.load(f)
340
+ jess = Jess([template])
341
+ with files(data).joinpath("5ayx.EF.pdb").open() as f:
342
+ molecule = Molecule.load(f)
343
+
344
+ hits = list(jess.query(molecule, 3, 3, 3, ignore_chain=None))
345
+ self.assertEqual(len(hits), 0)
346
+
347
+ @unittest.skipUnless(files, "importlib.resources not available")
348
+ def test_ignore_chain_residues(self):
349
+ with files(data).joinpath("4.1.2.tpl").open() as f:
350
+ template = Template.load(f)
351
+ jess = Jess([template])
352
+ with files(data).joinpath("5ayx.EF.pdb").open() as f:
353
+ molecule = Molecule.load(f)
354
+
355
+ hits = list(jess.query(molecule, 3, 3, 3, ignore_chain="residues"))
356
+ self.assertEqual(len(hits), 2)
357
+ for hit in hits:
358
+ atoms = hit.atoms(transform=False)
359
+ for i in range(1, len(atoms)):
360
+ if atoms[i-1].residue_number == atoms[i].residue_number:
361
+ self.assertEqual(atoms[i-1].chain_id, atoms[i].chain_id)
362
+
363
+ @unittest.skipUnless(files, "importlib.resources not available")
364
+ def test_ignore_chain_atoms(self):
365
+ with files(data).joinpath("4.1.2.tpl").open() as f:
366
+ template = Template.load(f)
367
+ jess = Jess([template])
368
+ with files(data).joinpath("5ayx.EF.pdb").open() as f:
369
+ molecule = Molecule.load(f)
370
+
371
+ hits = list(jess.query(molecule, 3, 3, 3, ignore_chain="atoms"))
372
+ self.assertEqual(len(hits), 7)
373
+
374
+
@@ -1,12 +1,31 @@
1
+ import itertools
1
2
  import os
2
3
  import pickle
3
4
  import unittest
4
5
  import tempfile
5
6
  import textwrap
6
7
  import sys
8
+ import warnings
7
9
 
10
+ from .utils import files
11
+ from . import data
8
12
  from .._jess import Atom, Molecule
9
13
 
14
+ try:
15
+ import gemmi
16
+ except ImportError:
17
+ gemmi = None
18
+
19
+ try:
20
+ import Bio.PDB
21
+ except ImportError:
22
+ Bio = None
23
+
24
+ try:
25
+ from biotite.structure.io.pdb import PDBFile
26
+ except ImportError:
27
+ PDBFile = None
28
+
10
29
  MOLECULE = textwrap.dedent(
11
30
  """
12
31
  HEADER DNA RECOMBINATION 05-DEC-97 1A0P
@@ -22,6 +41,38 @@ MOLECULE = textwrap.dedent(
22
41
 
23
42
  class TestMolecule(unittest.TestCase):
24
43
 
44
+ def assertHeteroAtomEqual(self, a1, a2):
45
+ # self.assertEqual(a1.serial, a2.serial)
46
+ self.assertEqual(a1.name, a2.name)
47
+ self.assertEqual(a1.altloc, a2.altloc)
48
+ self.assertEqual(a1.residue_name, a2.residue_name)
49
+ # self.assertEqual(a1.chain_id, a2.chain_id)
50
+ # self.assertEqual(a1.residue_number, a2.residue_number)
51
+ self.assertEqual(a1.insertion_code, a2.insertion_code)
52
+ self.assertEqual(a1.element, a2.element)
53
+ self.assertAlmostEqual(a1.x, a2.x, places=5)
54
+ self.assertAlmostEqual(a1.y, a2.y, places=5)
55
+ self.assertAlmostEqual(a1.z, a2.z, places=5)
56
+ self.assertAlmostEqual(a1.occupancy, a2.occupancy, places=5)
57
+ self.assertAlmostEqual(a1.temperature_factor, a2.temperature_factor, places=5)
58
+ self.assertAlmostEqual(a1.charge, a2.charge, places=5)
59
+
60
+ def assertAtomEqual(self, a1, a2):
61
+ self.assertEqual(a1.serial, a2.serial)
62
+ self.assertEqual(a1.name, a2.name)
63
+ self.assertEqual(a1.altloc, a2.altloc)
64
+ self.assertEqual(a1.residue_name, a2.residue_name)
65
+ self.assertEqual(a1.chain_id, a2.chain_id)
66
+ self.assertEqual(a1.residue_number, a2.residue_number)
67
+ self.assertEqual(a1.insertion_code, a2.insertion_code)
68
+ self.assertEqual(a1.element, a2.element)
69
+ self.assertAlmostEqual(a1.x, a2.x, places=5)
70
+ self.assertAlmostEqual(a1.y, a2.y, places=5)
71
+ self.assertAlmostEqual(a1.z, a2.z, places=5)
72
+ self.assertAlmostEqual(a1.occupancy, a2.occupancy, places=5)
73
+ self.assertAlmostEqual(a1.temperature_factor, a2.temperature_factor, places=5)
74
+ self.assertAlmostEqual(a1.charge, a2.charge, places=5)
75
+
25
76
  def _create_atom(self, **kwargs):
26
77
  default = dict(serial=1, name='N', altloc=' ', residue_name='GLN', chain_id='A', residue_number=3, x=8.171, y=-51.403, z=42.886, segment='', insertion_code=' ', occupancy=1.0, temperature_factor=55.63, charge=0, element='N')
27
78
  default.update(kwargs)
@@ -139,3 +190,98 @@ class TestMolecule(unittest.TestCase):
139
190
  self.assertEqual(list(mol1), list(mol2))
140
191
  self.assertEqual(mol1.id, mol2.id)
141
192
  self.assertEqual(mol1, mol2)
193
+
194
+ @unittest.skipUnless(files, "importlib.resources not available")
195
+ @unittest.skipUnless(gemmi, "gemmi not available")
196
+ def test_load_consistency_no_skip_hetatm(self):
197
+ with files(data).joinpath("1AMY.pdb").open() as f:
198
+ pdb_molecule = Molecule.load(f, format="pdb")
199
+ with files(data).joinpath("1AMY.cif").open() as f:
200
+ with warnings.catch_warnings():
201
+ warnings.simplefilter('ignore')
202
+ cif_molecule = Molecule.load(f, format="cif")
203
+ self.assertEqual(len(cif_molecule), 3339)
204
+ self.assertEqual(len(pdb_molecule), 3339)
205
+ for pdb_atom, cif_atom in itertools.islice(zip(pdb_molecule, cif_molecule), 3184):
206
+ self.assertAtomEqual(pdb_atom, cif_atom)
207
+ for pdb_atom, cif_atom in itertools.islice(zip(pdb_molecule, cif_molecule), 3184, 3339):
208
+ self.assertHeteroAtomEqual(pdb_atom, cif_atom)
209
+
210
+ @unittest.skipUnless(files, "importlib.resources not available")
211
+ @unittest.skipUnless(gemmi, "gemmi not available")
212
+ def test_load_consistency_no_skip_hetatm_use_author(self):
213
+ with files(data).joinpath("1AMY.pdb").open() as f:
214
+ pdb_molecule = Molecule.load(f, format="pdb")
215
+ with files(data).joinpath("1AMY.cif").open() as f:
216
+ cif_molecule = Molecule.load(f, format="cif", use_author=True)
217
+ self.assertEqual(len(cif_molecule), 3339)
218
+ self.assertEqual(len(pdb_molecule), 3339)
219
+ for pdb_atom, cif_atom in itertools.islice(zip(pdb_molecule, cif_molecule), 3184):
220
+ self.assertAtomEqual(pdb_atom, cif_atom)
221
+ for pdb_atom, cif_atom in itertools.islice(zip(pdb_molecule, cif_molecule), 3184, 3339):
222
+ self.assertHeteroAtomEqual(pdb_atom, cif_atom)
223
+
224
+ @unittest.skipUnless(files, "importlib.resources not available")
225
+ @unittest.skipUnless(gemmi, "gemmi not available")
226
+ def test_load_consistency_skip_hetatm(self):
227
+ with files(data).joinpath("1AMY.pdb").open() as f:
228
+ pdb_molecule = Molecule.load(f, format="pdb", skip_hetatm=True)
229
+ with files(data).joinpath("1AMY.cif").open() as f:
230
+ cif_molecule = Molecule.load(f, format="cif", skip_hetatm=True)
231
+ self.assertEqual(len(cif_molecule), 3184)
232
+ self.assertEqual(len(pdb_molecule), 3184)
233
+ for pdb_atom, cif_atom in itertools.islice(zip(pdb_molecule, cif_molecule), 3184):
234
+ self.assertAtomEqual(pdb_atom, cif_atom)
235
+
236
+ @unittest.skipUnless(files, "importlib.resources not available")
237
+ @unittest.skipUnless(gemmi, "gemmi not available")
238
+ def test_load_consistency_skip_hetatm_use_author(self):
239
+ with files(data).joinpath("1AMY.pdb").open() as f:
240
+ pdb_molecule = Molecule.load(f, format="pdb", skip_hetatm=True)
241
+ with files(data).joinpath("1AMY.cif").open() as f:
242
+ cif_molecule = Molecule.load(f, format="cif", skip_hetatm=True, use_author=True)
243
+ self.assertEqual(len(cif_molecule), 3184)
244
+ self.assertEqual(len(pdb_molecule), 3184)
245
+ for pdb_atom, cif_atom in itertools.islice(zip(pdb_molecule, cif_molecule), 3184):
246
+ self.assertAtomEqual(pdb_atom, cif_atom)
247
+
248
+ @unittest.skipUnless(files, "importlib.resources not available")
249
+ @unittest.skipUnless(Bio, "biopython not available")
250
+ def test_from_biopython(self):
251
+ parser = Bio.PDB.PDBParser()
252
+ with files(data).joinpath("1AMY.pdb").open() as f:
253
+ structure = parser.get_structure('1amy', f)
254
+ bio_mol = Molecule.from_biopython(structure)
255
+ with files(data).joinpath("1AMY.pdb").open() as f:
256
+ pdb_mol = Molecule.load(f, format="pdb")
257
+ self.assertEqual(len(pdb_mol), 3339)
258
+ self.assertEqual(len(bio_mol), 3339)
259
+ for pdb_atom, bio_atom in zip(pdb_mol, bio_mol):
260
+ self.assertAtomEqual(pdb_atom, bio_atom)
261
+
262
+ @unittest.skipUnless(files, "importlib.resources not available")
263
+ @unittest.skipUnless(gemmi, "gemmi not available")
264
+ def test_from_gemmi(self):
265
+ with files(data).joinpath("1AMY.pdb").open() as f:
266
+ structure = gemmi.read_pdb_string(f.read())
267
+ gemmi_mol = Molecule.from_gemmi(structure[0])
268
+ with files(data).joinpath("1AMY.pdb").open() as f:
269
+ pdb_mol = Molecule.load(f, format="pdb")
270
+ self.assertEqual(len(pdb_mol), 3339)
271
+ self.assertEqual(len(gemmi_mol), 3339)
272
+ for pdb_atom, gemmi_atom in zip(pdb_mol, gemmi_mol):
273
+ self.assertAtomEqual(pdb_atom, gemmi_atom)
274
+
275
+ @unittest.skipUnless(files, "importlib.resources not available")
276
+ @unittest.skipUnless(PDBFile, "biotite not available")
277
+ def test_from_biotite(self):
278
+ with files(data).joinpath("1AMY.pdb").open() as f:
279
+ pdb_file = PDBFile.read(f)
280
+ structure = pdb_file.get_structure(altloc="all", extra_fields=["atom_id", "b_factor", "occupancy", "charge"])
281
+ biotite_mol = Molecule.from_biotite(structure[0])
282
+ with files(data).joinpath("1AMY.pdb").open() as f:
283
+ pdb_mol = Molecule.load(f, format="pdb")
284
+ self.assertEqual(len(pdb_mol), 3339)
285
+ self.assertEqual(len(biotite_mol), 3339)
286
+ for pdb_atom, biotite_atom in zip(pdb_mol, biotite_mol):
287
+ self.assertAtomEqual(pdb_atom, biotite_atom)
@@ -6,6 +6,8 @@ import textwrap
6
6
  import sys
7
7
 
8
8
  from .._jess import Template
9
+ from .utils import files
10
+ from . import data
9
11
 
10
12
  TEMPLATE = textwrap.dedent(
11
13
  """
@@ -109,9 +111,16 @@ class TestTemplate(unittest.TestCase):
109
111
  tpl2 = tpl1.copy()
110
112
  self.assertEqual(len(tpl1), len(tpl2))
111
113
  self.assertEqual(tpl1, tpl2)
114
+ self.assertEqual(list(tpl1), list(tpl2))
112
115
 
113
116
  def test_pickle_roundtrip(self):
114
117
  tpl1 = Template.loads(TEMPLATE, id="tpl1")
115
118
  tpl2 = pickle.loads(pickle.dumps(tpl1))
116
119
  self.assertEqual(len(tpl1), len(tpl2))
117
- self.assertEqual(tpl1, tpl2)
120
+ self.assertEqual(tpl1, tpl2)
121
+
122
+ @unittest.skipUnless(files, "importlib.resources not available")
123
+ def test_dimension_multiple_chains(self):
124
+ with files(data).joinpath("1sur.qry").open() as f:
125
+ template1 = Template.load(f)
126
+ self.assertEqual(template1.dimension, 4)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pyjess
3
- Version: 0.6.0
3
+ Version: 0.7.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>
@@ -56,6 +56,8 @@ Project-URL: PiWheels, https://piwheels.org/project/pyjess/
56
56
  Requires-Python: >=3.7
57
57
  Provides-Extra: test
58
58
  Requires-Dist: importlib-resources; python_version < "3.9" and extra == "test"
59
+ Provides-Extra: cif
60
+ Requires-Dist: gemmi~=0.7.0; extra == "cif"
59
61
  Description-Content-Type: text/markdown
60
62
 
61
63
  # 🐍🔍 PyJess [![Stars](https://img.shields.io/github/stars/althonos/pyjess.svg?style=social&maxAge=3600&label=Star)](https://github.com/althonos/pyjess/stargazers)
@@ -115,7 +117,7 @@ package:
115
117
  $ conda install -c bioconda pyjess
116
118
  ```
117
119
 
118
- Check the [*install* page](https://pyjess.readthedocs.io/en/stable/install.html)
120
+ Check the [*install* page](https://pyjess.readthedocs.io/en/stable/guide/install.html)
119
121
  of the documentation for other ways to install PyJess on your machine.
120
122
 
121
123
 
@@ -129,7 +131,9 @@ Jess if you are using it in an academic work, for instance as:
129
131
 
130
132
  ## 💡 Example
131
133
 
132
- Load [`Template`](https://pyjess.readthedocs.io/en/latest/api/template.html#pyjess.Template)
134
+ #### Prepare templates
135
+
136
+ Load [`Template`](https://pyjess.readthedocs.io/en/latest/api/template.html#pyjess.Template)
133
137
  objects to be used as references from different template files:
134
138
 
135
139
  ```python
@@ -141,15 +145,46 @@ for path in sorted(pathlib.Path("vendor/jess/examples").glob("template_*.qry")):
141
145
  templates.append(pyjess.Template.load(path, id=path.stem))
142
146
  ```
143
147
 
144
- Create a [`Jess`](https://pyjess.readthedocs.io/en/latest/api/jess.html#pyjess.Jess) instance and use it to query a [`Molecule`](https://pyjess.readthedocs.io/en/latest/api/molecule.html#pyjess.Molecule) (a PDB structure)
145
- against the stored templates:
148
+ #### Prepare query structures
149
+
150
+ Load a [`Molecule`](https://pyjess.readthedocs.io/en/latest/api/molecule.html#pyjess.Molecule)
151
+ (a PDB structure) from a PDB file, create one with the Python API, or
152
+ convert it from a [`Bio.Model`](https://biopython.org/docs/1.76/api/Bio.PDB.Model.html),
153
+ [`gemmi.Model`](https://gemmi.readthedocs.io/en/latest/mol.html#model),
154
+ or [`biotite.structure.AtomArray`](https://www.biotite-python.org/latest/apidoc/biotite.structure.AtomArray.html)
155
+ object:
146
156
 
147
157
  ```python
148
- jess = pyjess.Jess(templates)
158
+ # load from PDB file or mmCIF file
149
159
  mol = pyjess.Molecule.load("vendor/jess/examples/test_pdbs/pdb1a0p.ent")
160
+
161
+ # load with BioPython
162
+ parser = Bio.PDB.PDBParser()
163
+ structure = parser.get_structure('pdb1a0p', "vendor/jess/examples/test_pdbs/pdb1a0p.ent")
164
+ mol = Molecule.from_biopython(structure, id="1a0p")
165
+
166
+ # load with Gemmi
167
+ structure = gemmi.read_pdb_string("vendor/jess/examples/test_pdbs/pdb1a0p.ent")
168
+ mol = Molecule.from_gemmi(structure[0], id="1a0p")
169
+
170
+ # load with Biotite
171
+ pdb_file = biotite.structure.io.pdb.PDBFile.read(f)
172
+ structure = pdb_file.get_structure(altloc="all", extra_fields=["atom_id", "b_factor", "occupancy", "charge"])
173
+ mol = Molecule.from_biotite(structure[0])
174
+ ```
175
+
176
+ ### Match templates
177
+
178
+ Create a [`Jess`](https://pyjess.readthedocs.io/en/latest/api/jess.html#pyjess.Jess)
179
+ instance and use it to query a against the stored templates:
180
+
181
+ ```python
182
+ jess = pyjess.Jess(templates)
150
183
  query = jess.query(mol, rmsd_threshold=2.0, distance_cutoff=3.0, max_dynamic_distance=3.0)
151
184
  ```
152
185
 
186
+ ### Process hits
187
+
153
188
  The hits are computed iteratively, and the different output statistics are
154
189
  computed on-the-fly when requested:
155
190
 
@@ -160,13 +195,19 @@ for hit in query:
160
195
  print(atom.name, atom.x, atom.y, atom.z)
161
196
  ```
162
197
 
198
+ Hits can also be rendered in PDB format like in the original Jess output,
199
+ either by writing to a file directly, or to a Python string:
200
+ ```python
201
+ for hit in query:
202
+ hit.dump(sys.stdout, format="pdb")
203
+ ```
163
204
 
164
205
  ## 🧶 Thread-safety
165
206
 
166
- Once a [`Jess`](https://pyjess.readthedocs.io/en/latest/api/jess.html#pyjess.Jess)
207
+ Once a [`Jess`](https://pyjess.readthedocs.io/en/latest/api/jess.html#pyjess.Jess)
167
208
  instance has been created, the templates cannot be edited anymore,
168
- making the [`Jess.query`](https://pyjess.readthedocs.io/en/latest/api/jess.html#pyjess.Jess.query) method re-entrant and thread-safe. This allows querying
169
- several molecules against the same templates in parallel using e.g a
209
+ making the [`Jess.query`](https://pyjess.readthedocs.io/en/latest/api/jess.html#pyjess.Jess.query) method re-entrant and thread-safe. This allows querying
210
+ several molecules against the same templates in parallel using e.g a
170
211
  [`ThreadPool`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.pool.ThreadPool):
171
212
 
172
213
  ```python
@@ -183,9 +224,9 @@ If running Jess in parallel, make sure to use `v0.2.1` or later to use the code
183
224
 
184
225
  ## ⏱️ Benchmarks
185
226
 
186
- The following table reports the runtime of PyJess to match $n=132$ protein
187
- structures to the $m=7607$ templates of
188
- [EnzyMM](https://github.com/RayHackett/enzymm), using $J=12$ threads to parallelize.
227
+ The following table reports the runtime of PyJess to match N=132 protein
228
+ structures to the M=7607 templates of
229
+ [EnzyMM](https://github.com/RayHackett/enzymm), using J=12 threads to parallelize.
189
230
 
190
231
  | Version | Runtime (s) | Match Speed (N * M / s * J) | Speedup |
191
232
  | ----------- | ----------- | --------------------------- | ----------- |
@@ -193,10 +234,11 @@ structures to the $m=7607$ templates of
193
234
  | ``v0.5.0`` | 586.3 | 142.7 | x1.05 |
194
235
  | ``v0.5.1`` | 365.6 | 228.9 | x1.69 |
195
236
  | ``v0.5.2`` | 327.2 | 255.7 | x1.88 |
196
- | ``v0.6.0`` | 54.5 | 1535.4 | **x11.34** |
237
+ | ``v0.6.0`` | 54.5 | 1535.4 | x11.34 |
238
+ | ``v0.7.0`` | 52.4 | 1597.5 | **x11.80** |
197
239
 
198
- *Benchmarks were run on a quiet [i7-1255U](https://www.intel.com/content/www/us/en/products/sku/226259/intel-core-i71255u-processor-12m-cache-up-to-4-70-ghz/specifications.html) CPU running @4.70GHz with 10 physical cores / 12 logical
199
- cores.*
240
+ *Benchmarks were run on a quiet [i7-1255U](https://www.intel.com/content/www/us/en/products/sku/226259/intel-core-i71255u-processor-12m-cache-up-to-4-70-ghz/specifications.html)
241
+ CPU running @4.70GHz with 10 physical cores / 12 logical cores.*
200
242
 
201
243
  ## 💭 Feedback
202
244
 
@@ -224,7 +266,8 @@ in the [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) format.
224
266
 
225
267
  ## ⚖️ License
226
268
 
227
- This library is provided under the [MIT License](https://choosealicense.com/licenses/mit/). The JESS code is distributed under the [MIT License](https://choosealicense.com/licenses/mit/) as well.
269
+ This library is provided under the [MIT License](https://choosealicense.com/licenses/mit/).
270
+ The JESS code is distributed under the [MIT License](https://choosealicense.com/licenses/mit/) as well.
228
271
 
229
272
  *This project is in no way not affiliated, sponsored, or otherwise endorsed
230
273
  by the JESS authors. It was developed