pyjess 0.7.0a2__tar.gz → 0.7.0a3__tar.gz

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.

Files changed (139) hide show
  1. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/CHANGELOG.md +12 -1
  2. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/PKG-INFO +9 -7
  3. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/README.md +6 -6
  4. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/atom.pxd +2 -2
  5. pyjess-0.7.0a3/patches/Atom.c.patch +13 -0
  6. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/pyproject.toml +2 -1
  7. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/_jess.pyi +20 -4
  8. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/_jess.pyx +160 -27
  9. pyjess-0.7.0a3/src/pyjess/tests/data/1AMY.cif +6259 -0
  10. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/test_molecule.py +30 -0
  11. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/.github/workflows/package.yml +0 -0
  12. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/.github/workflows/requirements.txt +0 -0
  13. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/.github/workflows/test.yml +0 -0
  14. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/.gitignore +0 -0
  15. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/.gitmodules +0 -0
  16. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/.readthedocs.yaml +0 -0
  17. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/CMakeLists.txt +0 -0
  18. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/CONTRIBUTING.md +0 -0
  19. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/COPYING +0 -0
  20. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/.gitignore +0 -0
  21. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/Makefile +0 -0
  22. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/_static/css/main.css +0 -0
  23. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/_static/js/custom-icon.js +0 -0
  24. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/_static/json/switcher.json +0 -0
  25. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/api/index.rst +0 -0
  26. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/api/jess.rst +0 -0
  27. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/api/molecule.rst +0 -0
  28. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/api/template.rst +0 -0
  29. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/conf.py +0 -0
  30. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/guide/changes.md +0 -0
  31. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/guide/contributing.md +0 -0
  32. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/guide/copyright.rst +0 -0
  33. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/guide/index.rst +0 -0
  34. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/guide/install.rst +0 -0
  35. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/guide/optimizations.rst +0 -0
  36. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/index.rst +0 -0
  37. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/make.bat +0 -0
  38. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/docs/requirements.txt +0 -0
  39. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/__init__.pxd +0 -0
  40. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/annulus.pxd +0 -0
  41. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/candidate_set.pxd +0 -0
  42. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/jess.pxd +0 -0
  43. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/join.pxd +0 -0
  44. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/kdtree.pxd +0 -0
  45. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/molecule.pxd +0 -0
  46. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/region.pxd +0 -0
  47. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/res_index.pxd +0 -0
  48. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/scanner.pxd +0 -0
  49. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/super.pxd +0 -0
  50. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/template.pxd +0 -0
  51. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/tess_atom.pxd +0 -0
  52. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/include/jess/tess_template.pxd +0 -0
  53. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Annulus.c.patch +0 -0
  54. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Annulus.h.patch +0 -0
  55. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Atom.h.patch +0 -0
  56. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Box.c.patch +0 -0
  57. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Box.h.patch +0 -0
  58. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/CMakeLists.txt.patch +0 -0
  59. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/CandidateSet.c.patch +0 -0
  60. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/CandidateSet.h.patch +0 -0
  61. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Jess.c.patch +0 -0
  62. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Jess.h.patch +0 -0
  63. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Join.c.patch +0 -0
  64. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Join.h.patch +0 -0
  65. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/KdTree.c.patch +0 -0
  66. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/KdTree.h.patch +0 -0
  67. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Main.c.patch +0 -0
  68. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Molecule.c.patch +0 -0
  69. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Molecule.h.patch +0 -0
  70. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/ResIndex.c.patch +0 -0
  71. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/ResIndex.h.patch +0 -0
  72. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Scanner.c.patch +0 -0
  73. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Scanner.h.patch +0 -0
  74. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Super.c.patch +0 -0
  75. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/Template.h.patch +0 -0
  76. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/TessAtom.c.patch +0 -0
  77. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/TessAtom.h.patch +0 -0
  78. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/TessTemplate.c.patch +0 -0
  79. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/TessTemplate.h.patch +0 -0
  80. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/patches/qselect.h.patch +0 -0
  81. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/pkg/aur/PKGBUILD.in +0 -0
  82. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/CMakeLists.txt +0 -0
  83. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/jess/CMakeLists.txt +0 -0
  84. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/.gitignore +0 -0
  85. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/CMakeLists.txt +0 -0
  86. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/__init__.py +0 -0
  87. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/py.typed +0 -0
  88. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/__init__.py +0 -0
  89. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/data/1.3.3.tpl +0 -0
  90. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/data/1AMY+1.3.3.txt +0 -0
  91. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/data/1AMY.pdb +0 -0
  92. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/data/1sur.qry +0 -0
  93. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/data/4.1.2.tpl +0 -0
  94. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/data/5ayx.EF.pdb +0 -0
  95. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/data/__init__.py +0 -0
  96. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/data/pdb1lnb.pdb +0 -0
  97. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/data/template_01.qry +0 -0
  98. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/data/template_02.qry +0 -0
  99. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/test_atom.py +0 -0
  100. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/test_hit.py +0 -0
  101. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/test_jess.py +0 -0
  102. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/test_template.py +0 -0
  103. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/test_template_atom.py +0 -0
  104. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/pyjess/tests/utils.py +0 -0
  105. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/scripts/apply_patch.py +0 -0
  106. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/scripts/cmake/CythonExtension.cmake +0 -0
  107. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/scripts/cmake/pystate_patch.h +0 -0
  108. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/src/scripts/generate_patches.py +0 -0
  109. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/.gitignore +0 -0
  110. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/README.md +0 -0
  111. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/examples/template_01.qry +0 -0
  112. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/examples/template_02.qry +0 -0
  113. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/examples/test_pdbs/pdb1lnb.ent +0 -0
  114. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/filter_output.py +0 -0
  115. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Annulus.c +0 -0
  116. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Annulus.h +0 -0
  117. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Atom.c +0 -0
  118. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Atom.h +0 -0
  119. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Jess.c +0 -0
  120. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Jess.h +0 -0
  121. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Join.c +0 -0
  122. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Join.h +0 -0
  123. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/KdTree.c +0 -0
  124. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/KdTree.h +0 -0
  125. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Main.c +0 -0
  126. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Molecule.c +0 -0
  127. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Molecule.h +0 -0
  128. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Region.c +0 -0
  129. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Region.h +0 -0
  130. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Scanner.c +0 -0
  131. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Scanner.h +0 -0
  132. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Super.c +0 -0
  133. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Super.h +0 -0
  134. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/Template.h +0 -0
  135. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/TessAtom.c +0 -0
  136. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/TessAtom.h +0 -0
  137. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/TessTemplate.c +0 -0
  138. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/TessTemplate.h +0 -0
  139. {pyjess-0.7.0a2 → pyjess-0.7.0a3}/vendor/jess/src/jess +0 -0
@@ -6,7 +6,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6
6
 
7
7
 
8
8
  ## [Unreleased]
9
- [Unreleased]: https://github.com/althonos/pyjess/compare/v0.7.0-alpha.2...HEAD
9
+ [Unreleased]: https://github.com/althonos/pyjess/compare/v0.7.0-alpha.3...HEAD
10
+
11
+
12
+ ## [v0.7.0-alpha.3] - 2025-09-03
13
+ [v0.7.0-alpha.3]: https://github.com/althonos/pyjess/compare/v0.7.0-alpha.2...v0.7.0-alpha.3
14
+
15
+ ### Added
16
+ - Support for parsing `Molecule` objects from CIF files using [`gemmi`](https://gemmi.readthedocs.io/).
17
+
18
+ ### Changed
19
+ - **breaking**: Make `id` and `ignore_endmdl` arguments of `Molecule.load` and `Molecule.loads` keyword-only.
20
+ - Make `altloc` and `insertion_code` arguments of `Atom` optional.
10
21
 
11
22
 
12
23
  ## [v0.7.0-alpha.2] - 2025-09-02
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pyjess
3
- Version: 0.7.0a2
3
+ Version: 0.7.0a3
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
 
@@ -183,9 +185,9 @@ If running Jess in parallel, make sure to use `v0.2.1` or later to use the code
183
185
 
184
186
  ## ⏱️ Benchmarks
185
187
 
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.
188
+ The following table reports the runtime of PyJess to match N=132 protein
189
+ structures to the M=7607 templates of
190
+ [EnzyMM](https://github.com/RayHackett/enzymm), using J=12 threads to parallelize.
189
191
 
190
192
  | Version | Runtime (s) | Match Speed (N * M / s * J) | Speedup |
191
193
  | ----------- | ----------- | --------------------------- | ----------- |
@@ -196,8 +198,8 @@ structures to the $m=7607$ templates of
196
198
  | ``v0.6.0`` | 54.5 | 1535.4 | x11.34 |
197
199
  | ``v0.7.0`` | 52.4 | 1597.5 | **x11.80** |
198
200
 
199
- *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
200
- cores.*
201
+ *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)
202
+ CPU running @4.70GHz with 10 physical cores / 12 logical cores.*
201
203
 
202
204
  ## 💭 Feedback
203
205
 
@@ -55,7 +55,7 @@ package:
55
55
  $ conda install -c bioconda pyjess
56
56
  ```
57
57
 
58
- Check the [*install* page](https://pyjess.readthedocs.io/en/stable/install.html)
58
+ Check the [*install* page](https://pyjess.readthedocs.io/en/stable/guide/install.html)
59
59
  of the documentation for other ways to install PyJess on your machine.
60
60
 
61
61
 
@@ -123,9 +123,9 @@ If running Jess in parallel, make sure to use `v0.2.1` or later to use the code
123
123
 
124
124
  ## ⏱️ Benchmarks
125
125
 
126
- The following table reports the runtime of PyJess to match $n=132$ protein
127
- structures to the $m=7607$ templates of
128
- [EnzyMM](https://github.com/RayHackett/enzymm), using $J=12$ threads to parallelize.
126
+ The following table reports the runtime of PyJess to match N=132 protein
127
+ structures to the M=7607 templates of
128
+ [EnzyMM](https://github.com/RayHackett/enzymm), using J=12 threads to parallelize.
129
129
 
130
130
  | Version | Runtime (s) | Match Speed (N * M / s * J) | Speedup |
131
131
  | ----------- | ----------- | --------------------------- | ----------- |
@@ -136,8 +136,8 @@ structures to the $m=7607$ templates of
136
136
  | ``v0.6.0`` | 54.5 | 1535.4 | x11.34 |
137
137
  | ``v0.7.0`` | 52.4 | 1597.5 | **x11.80** |
138
138
 
139
- *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
140
- cores.*
139
+ *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)
140
+ CPU running @4.70GHz with 10 physical cores / 12 logical cores.*
141
141
 
142
142
  ## 💭 Feedback
143
143
 
@@ -10,8 +10,8 @@ cdef extern from "Atom.h" nogil:
10
10
  int resSeq
11
11
  char iCode
12
12
  double[3] x
13
- double occupancy
14
- double tempFactor
13
+ float occupancy
14
+ float tempFactor
15
15
  char[4] segID
16
16
  char[3] element
17
17
  int charge
@@ -0,0 +1,13 @@
1
+ diff --git a/src/Atom.c b/src/Atom.c
2
+ index ab602d7..6285beb 100644
3
+ --- a/src/Atom.c
4
+ +++ b/src/Atom.c
5
+ @@ -25,7 +25,7 @@ static void Atom_copyToken(char *d,const char *s,int n)
6
+ d[n]=0;
7
+ for(i=0; i<n; i++)
8
+ {
9
+ - if(isspace(d[i])) d[i]='_';
10
+ + if(isspace(d[i]) || !d[i]) d[i]='_';
11
+ }
12
+ }
13
+
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "pyjess"
7
- version = "0.7.0-alpha.2"
7
+ version = "0.7.0-alpha.3"
8
8
  description = "Cython bindings and Python interface to JESS, a 3D template matching software."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.7"
@@ -47,6 +47,7 @@ classifiers = [
47
47
 
48
48
  [project.optional-dependencies]
49
49
  test = ["importlib-resources ; python_version < '3.9'"]
50
+ cif = ["gemmi ~=0.7.0"]
50
51
 
51
52
  [tool.scikit-build]
52
53
  build-dir = "build/{build_type}"
@@ -17,9 +17,25 @@ __version__: str
17
17
 
18
18
  class Molecule(Sequence[Atom]):
19
19
  @classmethod
20
- def load(cls, file: Union[TextIO, str, os.PathLike[str]], id: Optional[str] = None, ignore_endmdl: bool = False) -> Molecule: ...
20
+ def load(
21
+ cls,
22
+ file: Union[TextIO, str, os.PathLike[str]],
23
+ format: Literal["pdb", "cif"] = "pdb",
24
+ *,
25
+ id: Optional[str] = None,
26
+ ignore_endmdl: bool = False,
27
+ use_author: bool = False,
28
+ ) -> Molecule: ...
21
29
  @classmethod
22
- def loads(cls, text: str, id: Optional[str] = None, ignore_endmdl: bool = False) -> Molecule: ...
30
+ def loads(
31
+ cls,
32
+ text: str,
33
+ format: Literal["pdb", "cif"] = "pdb",
34
+ *,
35
+ id: Optional[str] = None,
36
+ ignore_endmdl: bool = False,
37
+ use_author: bool = False,
38
+ ) -> Molecule: ...
23
39
  def __init__(self, atoms: Sequence[Atom] = (), id: Optional[str] = None): ...
24
40
  def __len__(self) -> int: ...
25
41
  @typing.overload
@@ -47,16 +63,16 @@ class Atom:
47
63
  *,
48
64
  serial: int,
49
65
  name: str,
50
- altloc: str,
51
66
  residue_name: str,
52
67
  chain_id: str,
53
68
  residue_number: int,
54
- insertion_code: str,
55
69
  x: float,
56
70
  y: float,
57
71
  z: float,
58
72
  occupancy: float = 0.0,
59
73
  temperature_factor: float = 0.0,
74
+ altloc: str = " ",
75
+ insertion_code: str = " ",
60
76
  segment: str = "",
61
77
  element: str = "",
62
78
  charge: int = 0,
@@ -70,6 +70,111 @@ def nullcontext(return_value=None):
70
70
 
71
71
  # --- Classes ----------------------------------------------------------------
72
72
 
73
+ cdef class _MoleculeParser:
74
+ cdef str id
75
+
76
+ def __init__(self, str id = None):
77
+ self.id = id
78
+
79
+ cdef class _PDBMoleculeParser(_MoleculeParser):
80
+ cdef bint ignore_endmdl
81
+
82
+ def __init__(self, str id = None, bint ignore_endmdl = False):
83
+ super().__init__(id=id)
84
+ self.ignore_endmdl = ignore_endmdl
85
+
86
+ def loads(self, text, molecule_type):
87
+ return self.load(io.StringIO(text), molecule_type)
88
+
89
+ def load(self, file, molecule_type):
90
+ cdef str id = self.id
91
+ try:
92
+ handle = open(file)
93
+ except TypeError:
94
+ handle = nullcontext(file)
95
+ with handle as f:
96
+ atoms = []
97
+ for line in f:
98
+ if line.startswith("HEADER"):
99
+ if id is None:
100
+ id = line[62:66].strip() or None
101
+ elif line.startswith(("ATOM", "HETATM")):
102
+ atoms.append(Atom.loads(line))
103
+ elif line.startswith("ENDMDL"):
104
+ if not self.ignore_endmdl:
105
+ break
106
+ return molecule_type(atoms, id=id)
107
+
108
+
109
+ cdef class _CIFMoleculeParser(_MoleculeParser):
110
+ cdef object gemmi
111
+ cdef bint use_author
112
+
113
+ _PRIMARY_COLUMNS = [
114
+ 'id', 'type_symbol', 'label_atom_id', 'label_alt_id', 'label_comp_id',
115
+ 'label_asym_id', 'label_seq_id', '?pdbx_PDB_ins_code', 'Cartn_x',
116
+ 'Cartn_y', 'Cartn_z', 'occupancy', 'B_iso_or_equiv',
117
+ '?pdbx_formal_charge', '?group_PDB',
118
+ ]
119
+
120
+ _AUTH_COLUMNS = [
121
+ 'id', 'type_symbol', 'auth_atom_id', 'label_alt_id', 'auth_comp_id',
122
+ 'auth_asym_id', 'auth_seq_id', '?pdbx_PDB_ins_code', 'Cartn_x',
123
+ 'Cartn_y', 'Cartn_z', 'occupancy', 'B_iso_or_equiv',
124
+ '?pdbx_formal_charge', '?group_PDB',
125
+ ]
126
+
127
+ def __init__(self, str id = None, bint use_author = False):
128
+ super().__init__(id=id)
129
+ self.gemmi = __import__('gemmi')
130
+ self.use_author = use_author
131
+
132
+ def _load_block(self, document, molecule_type):
133
+ block = document.sole_block()
134
+ cols = self._AUTH_COLUMNS if self.use_author else self._PRIMARY_COLUMNS
135
+ table = block.find('_atom_site.', cols)
136
+
137
+ if not table:
138
+ raise ValueError("missing columns in CIF files")
139
+
140
+ atoms = []
141
+ for row in table:
142
+ if table.has_column(14) and row[14] != "ATOM":
143
+ continue
144
+ atom = Atom(
145
+ serial=int(row[0]),
146
+ element=row[1],
147
+ name=row[2],
148
+ altloc=' ' if row[3] == "." else row[3], # FIXME: replace with None?
149
+ residue_name=row[4],
150
+ chain_id=row[5],
151
+ residue_number=int(row[6]),
152
+ insertion_code=' ' if not row.has(7) or row[7] == "?" else row[7],
153
+ x=float(row[8]),
154
+ y=float(row[9]),
155
+ z=float(row[10]),
156
+ occupancy=float(row[11]),
157
+ temperature_factor=float(row[12]),
158
+ # str segment = '', # FIXME?
159
+ charge=0 if not row.has(13) or row[13] == "?" else int(row[13]),
160
+ )
161
+ atoms.append(atom)
162
+
163
+ id = block.name if self.id is None else self.id
164
+ return molecule_type(atoms, id=id)
165
+
166
+ def loads(self, text, molecule_type):
167
+ document = self.gemmi.cif.read_string(text)
168
+ return self._load_block(document, molecule_type)
169
+
170
+ def load(self, file, molecule_type):
171
+ if hasattr(file, "read"):
172
+ document = self.gemmi.cif.read_string(file.read())
173
+ else:
174
+ document = self.gemmi.cif.read_file(file)
175
+ return self._load_block(document, molecule_type)
176
+
177
+
73
178
  cdef class Molecule:
74
179
  """A molecule structure, as a sequence of `Atom` objects.
75
180
 
@@ -84,20 +189,31 @@ cdef class Molecule:
84
189
  cdef str _id
85
190
 
86
191
  @classmethod
87
- def loads(cls, text, str id = None, bint ignore_endmdl = False):
192
+ def loads(cls, text, str format = "pdb", *, str id = None, bint ignore_endmdl = False):
88
193
  """Load a molecule from a PDB string.
89
194
 
90
195
  Arguments:
91
196
  file (`str`, `os.PathLike`, or file-like object): Either the path
92
197
  to a file, or a file-like object opened in **text mode**
93
- containing a PDB molecule.
198
+ containing a molecule.
199
+ format (`str`): The format to parse the file. Supported formats
200
+ are: ``pdb`` for the Protein Data Bank format, or ``cif``
201
+ for Crystallographic Information File format (additionally
202
+ requires the `gemmi` module).
203
+
204
+ Keyword Arguments:
94
205
  id (`str`, optional): The identifier of the molecule. If `None`
95
206
  given, the parser will attempt to extract it from the
96
- ``HEADER`` line.
207
+ ``HEADER`` line (for PDB files) or the block name (for CIF
208
+ files).
97
209
  ignore_endmdl (`bool`): Pass `True` to make the parser read all
98
210
  the atoms from the PDB file. By default, the parser only
99
211
  reads the atoms of the first model, and stops at the first
100
- ``ENDMDL`` line.
212
+ ``ENDMDL`` line. *Ignored for CIF files.*
213
+ use_author (`bool`): Pass `True` to use the author-defined
214
+ labels while parsing CIF files, e.g. read the chain name
215
+ from ``_atom_site.auth_asym_id`` rather than
216
+ ``_atom_site.label_asym_id``.
101
217
 
102
218
  Returns:
103
219
  `~pyjess.Molecule`: The molecule parsed from the PDB file.
@@ -106,45 +222,61 @@ cdef class Molecule:
106
222
  `Molecule.load` to load a PDB molecule from a file-like
107
223
  object or from a path.
108
224
 
225
+ .. versionadded:: 0.7.0
226
+ The ``format`` argument, and support for CIF parsing.
227
+
109
228
  """
110
229
  return cls.load(io.StringIO(text), id=id, ignore_endmdl=ignore_endmdl)
111
230
 
112
231
  @classmethod
113
- def load(cls, file, str id = None, bint ignore_endmdl = False):
232
+ def load(
233
+ cls,
234
+ file, str format = "pdb",
235
+ *,
236
+ str id = None,
237
+ bint ignore_endmdl = False,
238
+ bint use_author = False,
239
+ ):
114
240
  """Load a molecule from a PDB file.
115
241
 
116
242
  Arguments:
117
243
  file (`str`, `os.PathLike`, or file-like object): Either the path
118
244
  to a file, or a file-like object opened in **text mode**
119
- containing a PDB molecule.
245
+ containing a molecule.
246
+ format (`str`): The format to parse the file. Supported formats
247
+ are: ``pdb`` for the Protein Data Bank format, or ``cif``
248
+ for Crystallographic Information File format (additionally
249
+ requires the `gemmi` module).
250
+
251
+ Keyword Arguments:
120
252
  id (`str`, optional): The identifier of the molecule. If `None`
121
253
  given, the parser will attempt to extract it from the
122
- ``HEADER`` line.
254
+ ``HEADER`` line (for PDB files) or the block name (for CIF
255
+ files).
123
256
  ignore_endmdl (`bool`): Pass `True` to make the parser read all
124
257
  the atoms from the PDB file. By default, the parser only
125
258
  reads the atoms of the first model, and stops at the first
126
- ``ENDMDL`` line.
259
+ ``ENDMDL`` line. *Ignored for CIF files.*
260
+ use_author (`bool`): Pass `True` to use the author-defined
261
+ labels while parsing CIF files, e.g. read the chain name
262
+ from ``_atom_site.auth_asym_id`` rather than
263
+ ``_atom_site.label_asym_id``.
127
264
 
128
265
  Returns:
129
266
  `~pyjess.Molecule`: The molecule parsed from the PDB file.
130
267
 
268
+ .. versionadded:: 0.7.0
269
+ The ``format`` argument, and support for CIF parsing.
270
+
131
271
  """
132
- try:
133
- handle = open(file)
134
- except TypeError:
135
- handle = nullcontext(file)
136
- with handle as f:
137
- atoms = []
138
- for line in f:
139
- if line.startswith("HEADER"):
140
- if id is None:
141
- id = line[62:66].strip() or None
142
- elif line.startswith(("ATOM", "HETATM")):
143
- atoms.append(Atom.loads(line))
144
- elif line.startswith("ENDMDL"):
145
- if not ignore_endmdl:
146
- break
147
- return cls(atoms, id=id)
272
+ cdef _MoleculeParser parser
273
+ if format == "pdb":
274
+ parser = _PDBMoleculeParser(id=id, ignore_endmdl=ignore_endmdl)
275
+ elif format == "cif":
276
+ parser = _CIFMoleculeParser(id=id, use_author=use_author)
277
+ else:
278
+ raise ValueError(f"invalid value for `format` argument: {format!r}")
279
+ return parser.load(file, molecule_type=cls)
148
280
 
149
281
  def __cinit__(self):
150
282
  self._mol = NULL
@@ -367,21 +499,21 @@ cdef class Atom:
367
499
  *,
368
500
  int serial,
369
501
  str name,
370
- str altloc,
371
502
  str residue_name,
372
503
  str chain_id,
373
504
  int residue_number,
374
- str insertion_code,
375
505
  double x,
376
506
  double y,
377
507
  double z,
378
508
  double occupancy = 0.0,
379
509
  double temperature_factor = 0.0,
510
+ str altloc = ' ',
511
+ str insertion_code = ' ',
380
512
  str segment = '',
381
513
  str element = '',
382
514
  int charge = 0,
383
515
  ):
384
- """__init__(self, *, serial, name, altloc, residue_name, chain_id, residue_number, insertion_code, x, y, z, occupancy=0.0, temperature_factor=0.0, segment='', element='', charge=0)\n--\n
516
+ """__init__(self, *, serial, name, residue_name, chain_id, residue_number, x, y, z, occupancy=0.0, temperature_factor=0.0, altloc=' ', insertion_code=' ', segment='', element='', charge=0)\n--\n
385
517
 
386
518
  Create a new atom.
387
519
 
@@ -424,6 +556,7 @@ cdef class Atom:
424
556
  copy_token(self._atom.segID, segment.encode('ascii').ljust(3, b'\0'), 3)
425
557
  copy_token(self._atom.element, element.encode('ascii').ljust(2, b'\0'), 2)
426
558
 
559
+ # FIXME
427
560
  _name = bytearray(name, 'ascii')
428
561
  if len(_name) < 4:
429
562
  _name.insert(0, ord('_'))