pidibble 1.2.2__tar.gz → 1.3.0__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.
- {pidibble-1.2.2 → pidibble-1.3.0}/PKG-INFO +8 -3
- {pidibble-1.2.2 → pidibble-1.3.0}/README.md +7 -2
- pidibble-1.3.0/docs/source/_static/css/custom.css +12 -0
- pidibble-1.3.0/docs/source/api/API.rst +7 -0
- pidibble-1.3.0/docs/source/api/pidibble.baseparsers.rst +7 -0
- pidibble-1.3.0/docs/source/api/pidibble.baserecord.rst +7 -0
- pidibble-1.3.0/docs/source/api/pidibble.hex.rst +7 -0
- pidibble-1.3.0/docs/source/api/pidibble.mmcif_parse.rst +7 -0
- pidibble-1.3.0/docs/source/api/pidibble.pdbparse.rst +7 -0
- pidibble-1.3.0/docs/source/api/pidibble.pdbrecord.rst +7 -0
- pidibble-1.3.0/docs/source/api/pidibble.resources.rst +7 -0
- pidibble-1.3.0/docs/source/api/pidibble.rst +28 -0
- pidibble-1.3.0/docs/source/conf.py +85 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/docs/source/index.rst +7 -5
- pidibble-1.3.0/docs/source/installation.rst +16 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/docs/source/usage.rst +0 -17
- pidibble-1.3.0/pidibble/__init__.py +8 -0
- pidibble-1.3.0/pidibble/baseparsers.py +180 -0
- pidibble-1.3.0/pidibble/baserecord.py +131 -0
- pidibble-1.3.0/pidibble/hex.py +40 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/pidibble/mmcif_parse.py +128 -1
- {pidibble-1.2.2 → pidibble-1.3.0}/pidibble/pdbparse.py +159 -49
- {pidibble-1.2.2 → pidibble-1.3.0}/pidibble/pdbrecord.py +226 -20
- pidibble-1.3.0/pidibble/resources/__init__.py +6 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/pyproject.toml +1 -1
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb.py +6 -5
- pidibble-1.2.2/docs/source/api.rst +0 -7
- pidibble-1.2.2/docs/source/conf.py +0 -35
- pidibble-1.2.2/docs/source/generated/pidibble.rst +0 -6
- pidibble-1.2.2/pidibble/baseparsers.py +0 -76
- pidibble-1.2.2/pidibble/baserecord.py +0 -53
- pidibble-1.2.2/pidibble/hex.py +0 -19
- pidibble-1.2.2/pidibble/resources/__init__.py +0 -0
- pidibble-1.2.2/tests/__init__.py +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/.github/workflows/release.yaml +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/.gitignore +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/.readthedocs.yaml +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/LICENSE +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/MANIFEST.in +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/docs/Makefile +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/docs/make.bat +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/docs/requirements.txt +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/docs/source/notes.md +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/pidibble/resources/mmcif_format.yaml +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/pidibble/resources/pdb_format.yaml +0 -0
- {pidibble-1.2.2/pidibble → pidibble-1.3.0/tests}/__init__.py +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/conftest.py +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_hex/my_system.pdb +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_hex.py +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/4tvp.cif +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/4tvp.pdb +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/4zmj-newresnames.pdb +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/4zmj.cif +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/4zmj.pdb +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/6m0j.pdb +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/8fae.cif +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/G.pdb +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/GG.pdb +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/test.pdb +0 -0
- {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/test_pdb_format.yaml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pidibble
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: A complete Protein Data Bank (PDB) file parser
|
|
5
5
|
Project-URL: Source, https://github.com/cameronabrams/pidibble
|
|
6
6
|
Project-URL: Documentation, https://pidibble.readthedocs.io/en/latest/
|
|
@@ -16,7 +16,10 @@ Requires-Dist: numpy>=1.24
|
|
|
16
16
|
Requires-Dist: pyyaml>=6
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
|
|
19
|
-
# Pidibble
|
|
19
|
+
# Pidibble
|
|
20
|
+
> a complete PDB-file parser
|
|
21
|
+
|
|
22
|
+
[](https://pepy.tech/projects/pidibble)
|
|
20
23
|
|
|
21
24
|
Pidibble is a Python package for parsing standard Protein Data Bank (PDB) files. It conforms to the [most recent standard](https://www.wwpdb.org/documentation/file-format-content/format33/v3.3.html) (v.3.3 Atomic Coordinate Entry Format, ca. 2011).
|
|
22
25
|
|
|
@@ -88,7 +91,9 @@ ATOM
|
|
|
88
91
|
```
|
|
89
92
|
|
|
90
93
|
## Release History
|
|
91
|
-
* 1.
|
|
94
|
+
* 1.3.0
|
|
95
|
+
* streamline class attribute usage; full API documentation
|
|
96
|
+
* 1.2.3:
|
|
92
97
|
* bugfix: negative resids allowed
|
|
93
98
|
* 1.2.1:
|
|
94
99
|
* bugfix: hex issues AGAIN
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
# Pidibble
|
|
1
|
+
# Pidibble
|
|
2
|
+
> a complete PDB-file parser
|
|
3
|
+
|
|
4
|
+
[](https://pepy.tech/projects/pidibble)
|
|
2
5
|
|
|
3
6
|
Pidibble is a Python package for parsing standard Protein Data Bank (PDB) files. It conforms to the [most recent standard](https://www.wwpdb.org/documentation/file-format-content/format33/v3.3.html) (v.3.3 Atomic Coordinate Entry Format, ca. 2011).
|
|
4
7
|
|
|
@@ -70,7 +73,9 @@ ATOM
|
|
|
70
73
|
```
|
|
71
74
|
|
|
72
75
|
## Release History
|
|
73
|
-
* 1.
|
|
76
|
+
* 1.3.0
|
|
77
|
+
* streamline class attribute usage; full API documentation
|
|
78
|
+
* 1.2.3:
|
|
74
79
|
* bugfix: negative resids allowed
|
|
75
80
|
* 1.2.1:
|
|
76
81
|
* bugfix: hex issues AGAIN
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
.github-icon svg,
|
|
2
|
+
.linkedin-icon svg {
|
|
3
|
+
transition: fill 0.3s;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.github-icon svg:hover {
|
|
7
|
+
fill: #4078c0; /* GitHub icon turns blue on hover */
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.linkedin-icon svg:hover {
|
|
11
|
+
fill: #0A66C2; /* LinkedIn icon turns its official blue on hover */
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
pidibble package
|
|
2
|
+
================
|
|
3
|
+
|
|
4
|
+
.. automodule:: pidibble
|
|
5
|
+
:members:
|
|
6
|
+
:show-inheritance:
|
|
7
|
+
:undoc-members:
|
|
8
|
+
|
|
9
|
+
Subpackages
|
|
10
|
+
-----------
|
|
11
|
+
|
|
12
|
+
.. toctree::
|
|
13
|
+
:maxdepth: 4
|
|
14
|
+
|
|
15
|
+
pidibble.resources
|
|
16
|
+
|
|
17
|
+
Submodules
|
|
18
|
+
----------
|
|
19
|
+
|
|
20
|
+
.. toctree::
|
|
21
|
+
:maxdepth: 4
|
|
22
|
+
|
|
23
|
+
pidibble.baseparsers
|
|
24
|
+
pidibble.baserecord
|
|
25
|
+
pidibble.hex
|
|
26
|
+
pidibble.mmcif_parse
|
|
27
|
+
pidibble.pdbparse
|
|
28
|
+
pidibble.pdbrecord
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
|
|
3
|
+
# -- Project information
|
|
4
|
+
|
|
5
|
+
import importlib.metadata
|
|
6
|
+
|
|
7
|
+
project = 'pidibble'
|
|
8
|
+
release = importlib.metadata.version(project)
|
|
9
|
+
version = '.'.join(release.split('.')[:2]) # major.minor
|
|
10
|
+
copyright = '2023-2025, Cameron F. Abrams'
|
|
11
|
+
author = 'cfa22@drexel.edu'
|
|
12
|
+
|
|
13
|
+
# -- General configuration
|
|
14
|
+
|
|
15
|
+
extensions = [
|
|
16
|
+
'sphinx.ext.duration',
|
|
17
|
+
'sphinx.ext.doctest',
|
|
18
|
+
'sphinx.ext.autodoc',
|
|
19
|
+
'sphinx.ext.autosummary',
|
|
20
|
+
'sphinx.ext.intersphinx',
|
|
21
|
+
'sphinx_copybutton',
|
|
22
|
+
'sphinxcontrib.mermaid',
|
|
23
|
+
'sphinx.ext.napoleon',
|
|
24
|
+
'sphinx.ext.viewcode',
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
autosummary_generate = True # Enable autosummary tables
|
|
28
|
+
|
|
29
|
+
intersphinx_mapping = {
|
|
30
|
+
'python': ('https://docs.python.org/3/', None),
|
|
31
|
+
'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
|
|
32
|
+
'numpy': ('https://numpy.org/doc/stable/', None),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
intersphinx_disabled_domains = ['std']
|
|
36
|
+
|
|
37
|
+
templates_path = ['_templates']
|
|
38
|
+
|
|
39
|
+
# -- Options for HTML output
|
|
40
|
+
|
|
41
|
+
html_theme = 'furo'
|
|
42
|
+
html_theme_options = {
|
|
43
|
+
"light_css_variables": {
|
|
44
|
+
"color-icon": "#000000" # Black for light mode
|
|
45
|
+
},
|
|
46
|
+
"dark_css_variables": {
|
|
47
|
+
"color-icon": "#FFFFFF" # White for dark mode
|
|
48
|
+
},
|
|
49
|
+
"footer_icons": [
|
|
50
|
+
{
|
|
51
|
+
"name": "GitHub",
|
|
52
|
+
"url": "https://github.com/cameronabrams/ycleptic",
|
|
53
|
+
"html": """
|
|
54
|
+
<svg role="img" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
|
|
55
|
+
<title>GitHub</title>
|
|
56
|
+
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.3 3.44 9.8 8.21 11.39.6.11.82-.26.82-.58v-2.03c-3.34.73-4.04-1.61-4.04-1.61-.54-1.38-1.33-1.75-1.33-1.75-1.09-.75.08-.74.08-.74 1.2.08 1.83 1.23 1.83 1.23 1.07 1.83 2.81 1.3 3.5.99.11-.77.42-1.3.76-1.6-2.67-.3-5.47-1.34-5.47-5.98 0-1.32.47-2.4 1.24-3.24-.12-.3-.54-1.51.12-3.14 0 0 1.01-.32 3.3 1.23a11.38 11.38 0 0 1 3 0c2.28-1.55 3.3-1.23 3.3-1.23.66 1.63.24 2.84.12 3.14.77.84 1.24 1.92 1.24 3.24 0 4.65-2.8 5.68-5.47 5.98.43.37.81 1.1.81 2.22v3.29c0 .32.22.69.82.58C20.56 21.8 24 17.3 24 12c0-6.63-5.37-12-12-12z"/>
|
|
57
|
+
</svg>
|
|
58
|
+
""",
|
|
59
|
+
|
|
60
|
+
"class": "github-icon",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "LinkedIn",
|
|
64
|
+
"url": "https://linkedin.com/in/cameron-abrams-b0143398",
|
|
65
|
+
"html": """
|
|
66
|
+
<svg role="img" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
|
|
67
|
+
<title>LinkedIn</title>
|
|
68
|
+
<path d="M20.447 20.452h-3.554v-5.403c0-1.288-.025-2.945-1.796-2.945-1.796 0-2.071 1.4-2.071 2.847v5.501h-3.554V9.001h3.414v1.561h.05c.475-.9 1.637-1.797 3.368-1.797 3.599 0 4.262 2.368 4.262 5.446v6.241zM5.337 7.433c-1.144 0-2.072-.93-2.072-2.072 0-1.142.928-2.07 2.072-2.07 1.142 0 2.07.928 2.07 2.07 0 1.144-.928 2.072-2.07 2.072zM6.814 20.452H3.859V9.001h2.955v11.451zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.543C0 23.225.792 24 1.771 24h20.451C23.208 24 24 23.225 24 22.272V1.729C24 .774 23.208 0 22.225 0z"/>
|
|
69
|
+
</svg>
|
|
70
|
+
""",
|
|
71
|
+
"class": "linkedin-icon",
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
html_static_path = ['_static']
|
|
77
|
+
|
|
78
|
+
# -- Options for EPUB output
|
|
79
|
+
epub_show_urls = 'footnote'
|
|
80
|
+
|
|
81
|
+
mermaid_params = ['--theme', 'dark', '--width', '600']
|
|
82
|
+
|
|
83
|
+
def setup(app):
|
|
84
|
+
print("✅ Setting up custom directives...")
|
|
85
|
+
app.add_css_file("css/custom.css")
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Welcome to Pidibble's documentation!
|
|
2
2
|
====================================
|
|
3
3
|
|
|
4
|
-
**Pidibble** is a Python package for parsing Protein Data Bank (PDB) files. It conforms to the `most recent standard <https://www.wwpdb.org/documentation/file-format-content/format33/v3.3.html>`_ (v.3.3 Atomic Coordinate Entry Format, ca. 2011).
|
|
4
|
+
**Pidibble** is a Python package for parsing Protein Data Bank (PDB) files in both legacy PDB and modern PDBx/mmCIF formats. It conforms to the `most recent standard <https://www.wwpdb.org/documentation/file-format-content/format33/v3.3.html>`_ (v.3.3 Atomic Coordinate Entry Format, ca. 2011).
|
|
5
5
|
|
|
6
|
-
Unlike parsers like that found in packages like `BioPython <https://biopython.org/wiki/PDBParser>`_, ``pidibble`` provides meaningfully parsed objects from *all* standard PDB record types, not just
|
|
6
|
+
Unlike parsers like that found in packages like `BioPython <https://biopython.org/wiki/PDBParser>`_, ``pidibble`` provides meaningfully parsed objects from *all* standard PDB record types, not just ``ATOM`` and ``CONECT`` records.
|
|
7
7
|
|
|
8
|
-
Once installed, the user has access to the
|
|
8
|
+
Once installed, the user has access to the :class:`PDBParser` class in the :mod:`pidibble.pdbparser` module.
|
|
9
9
|
|
|
10
|
-
Pidibble can fetch coordinate files from the RSCB PDB and from the AlphaFold model database.
|
|
10
|
+
Pidibble can fetch coordinate files from the `RSCB PDB <https://www.rcsb.org/>`_ and from the `AlphaFold model database <https://alphafold.ebi.ac.uk/>`_.
|
|
11
11
|
|
|
12
12
|
Pidibble can handle hexadecimal serial numbers in all record types.
|
|
13
13
|
|
|
@@ -25,6 +25,8 @@ Contents
|
|
|
25
25
|
--------
|
|
26
26
|
|
|
27
27
|
.. toctree::
|
|
28
|
+
:maxdepth: 2
|
|
28
29
|
|
|
30
|
+
installation
|
|
29
31
|
usage
|
|
30
|
-
api
|
|
32
|
+
API <api/API>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.. _installation:
|
|
2
|
+
|
|
3
|
+
Installation
|
|
4
|
+
============
|
|
5
|
+
|
|
6
|
+
To use ``Pidibble``, install it from PyPI:
|
|
7
|
+
|
|
8
|
+
.. code-block:: bash
|
|
9
|
+
|
|
10
|
+
(.venv) $ pip install pidibble
|
|
11
|
+
|
|
12
|
+
Pidibble is under active development, so you can also install the latest version from the GitHub repository:
|
|
13
|
+
|
|
14
|
+
.. code-block:: bash
|
|
15
|
+
|
|
16
|
+
(.venv) $ pip install git+https://github.com/cfa22/pidibble.git
|
|
@@ -1,23 +1,6 @@
|
|
|
1
1
|
Usage
|
|
2
2
|
=====
|
|
3
3
|
|
|
4
|
-
.. _installation:
|
|
5
|
-
|
|
6
|
-
Installation
|
|
7
|
-
------------
|
|
8
|
-
|
|
9
|
-
To use Pidibble, install it from PyPI:
|
|
10
|
-
|
|
11
|
-
.. code-block:: console
|
|
12
|
-
|
|
13
|
-
(.venv) $ pip install pidibble
|
|
14
|
-
|
|
15
|
-
Pidibble is also available via ``conda``:
|
|
16
|
-
|
|
17
|
-
.. code-block:: console
|
|
18
|
-
|
|
19
|
-
(conda-env) $ conda install -c conda-forge pidibble
|
|
20
|
-
|
|
21
4
|
Usage Example
|
|
22
5
|
-------------
|
|
23
6
|
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
.. module:: baseparsers
|
|
4
|
+
:synopsis: defines some basic string and list parsing functions
|
|
5
|
+
|
|
6
|
+
.. moduleauthor: Cameron F. Abrams, <cfa22@drexel.edu>
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
import logging
|
|
10
|
+
logger=logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
class ListParser:
|
|
13
|
+
"""
|
|
14
|
+
A simple parser for lists of strings, with a customizable delimiter.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self,d=','):
|
|
18
|
+
self.d=d
|
|
19
|
+
|
|
20
|
+
def parse(self,string):
|
|
21
|
+
"""
|
|
22
|
+
Parse a string into a list of strings, using the specified delimiter.
|
|
23
|
+
If no delimiter is specified, it splits on whitespace.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
string : str
|
|
28
|
+
The string to parse.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
list
|
|
33
|
+
A list of strings parsed from the input string.
|
|
34
|
+
"""
|
|
35
|
+
if self.d==None:
|
|
36
|
+
return [x for x in string.split() if x.strip()!='']
|
|
37
|
+
else:
|
|
38
|
+
return [x.strip() for x in string.split(self.d) if x.strip()!='']
|
|
39
|
+
|
|
40
|
+
def list_parse(obj,d):
|
|
41
|
+
"""
|
|
42
|
+
A factory function to create a ListParser with a specific delimiter.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
obj : type
|
|
47
|
+
The class to instantiate (should be ListParser).
|
|
48
|
+
d : str or None
|
|
49
|
+
The delimiter to use for parsing. If None, it will split on whitespace.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
function
|
|
54
|
+
A function that takes a string and returns a list of parsed strings.
|
|
55
|
+
"""
|
|
56
|
+
return obj(d).parse
|
|
57
|
+
|
|
58
|
+
"""
|
|
59
|
+
Define a dictionary of parsers for different list formats
|
|
60
|
+
"""
|
|
61
|
+
ListParsers={
|
|
62
|
+
'CList':list_parse(ListParser,','),
|
|
63
|
+
'SList':list_parse(ListParser,';'),
|
|
64
|
+
'WList':list_parse(ListParser,None),
|
|
65
|
+
'DList':list_parse(ListParser,':'),
|
|
66
|
+
'LList':list_parse(ListParser,'\n')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_cols="""
|
|
70
|
+
1 2 3 4 5 6 7 8
|
|
71
|
+
12345678901234567890123456789012345678901234567890123456789012345678901234567890"""
|
|
72
|
+
class StringParser:
|
|
73
|
+
"""
|
|
74
|
+
A parser for fixed-width strings, with a customizable field map.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
fmtdict : dict
|
|
79
|
+
A dictionary mapping field names to tuples of (type, byte_range).
|
|
80
|
+
typemap : dict
|
|
81
|
+
A dictionary mapping type names to Python types.
|
|
82
|
+
allowed : dict, optional
|
|
83
|
+
A dictionary mapping field values to allowed values, for validation.
|
|
84
|
+
"""
|
|
85
|
+
def __init__(self,fmtdict,typemap,allowed={}):
|
|
86
|
+
self.typemap=typemap
|
|
87
|
+
self.fields={k:v for k,v in fmtdict.items()}
|
|
88
|
+
self.allowed=allowed
|
|
89
|
+
|
|
90
|
+
def parse(self,record):
|
|
91
|
+
"""
|
|
92
|
+
Parse a fixed-width string record into a dictionary of fields.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
record : str
|
|
97
|
+
The fixed-width string record to parse.
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
dict
|
|
102
|
+
A dictionary of fields parsed from the input record.
|
|
103
|
+
"""
|
|
104
|
+
if len(record)>80:
|
|
105
|
+
logger.warning('The following record exceeds 80 bytes in length:')
|
|
106
|
+
self.report_record_error(record)
|
|
107
|
+
assert len(record)<=80,f'Record is too long; something wrong with your PDB file?'
|
|
108
|
+
input_dict={}
|
|
109
|
+
record+=' '*(80-len(record)) # pad
|
|
110
|
+
for k,v in self.fields.items():
|
|
111
|
+
typestring,byte_range=v
|
|
112
|
+
typ=self.typemap[typestring]
|
|
113
|
+
assert byte_range[1]<=len(record),f'{record} {byte_range}'
|
|
114
|
+
# using columns beginning with "1" not "0"
|
|
115
|
+
fieldstring=record[byte_range[0]-1:byte_range[1]]
|
|
116
|
+
fieldstring=fieldstring.rstrip()
|
|
117
|
+
try:
|
|
118
|
+
# if len(fieldstring)>0 and not typ==str:
|
|
119
|
+
# fieldstring=''
|
|
120
|
+
input_dict[k]='' if fieldstring=='' else typ(fieldstring)
|
|
121
|
+
except:
|
|
122
|
+
self.report_field_error(record,k)
|
|
123
|
+
input_dict[k]=''
|
|
124
|
+
if typ==str:
|
|
125
|
+
input_dict[k]=input_dict[k].strip()
|
|
126
|
+
if fieldstring in self.allowed:
|
|
127
|
+
assert input_dict[k] in self.allowed[fieldstring],f'Value {input_dict[k]} is not allowed for field {k}; allowed values are {self.allowed[fieldstring]}'
|
|
128
|
+
return input_dict
|
|
129
|
+
|
|
130
|
+
def report_record_error(self,record,byte_range=[]):
|
|
131
|
+
"""
|
|
132
|
+
Report an error in parsing a fixed-width string record.
|
|
133
|
+
|
|
134
|
+
Parameters
|
|
135
|
+
----------
|
|
136
|
+
record : str
|
|
137
|
+
The fixed-width string record that caused the error.
|
|
138
|
+
byte_range : list, optional
|
|
139
|
+
A list of byte ranges to highlight in the error message.
|
|
140
|
+
If empty, the entire record is reported.
|
|
141
|
+
"""
|
|
142
|
+
if byte_range:
|
|
143
|
+
record=record[:byte_range[0]-1]+'\033[91m'+record[byte_range[0]:byte_range[1]+1]+'\033[0m'+record[byte_range[1]+1:]
|
|
144
|
+
repstr=_cols+'\n'+record
|
|
145
|
+
logger.warning(repstr)
|
|
146
|
+
|
|
147
|
+
def report_field_error(self,record,k):
|
|
148
|
+
"""
|
|
149
|
+
Report an error in parsing a specific field from a fixed-width string record.
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
record : str
|
|
154
|
+
The fixed-width string record that caused the error.
|
|
155
|
+
k : str
|
|
156
|
+
The field name that caused the error.
|
|
157
|
+
"""
|
|
158
|
+
byte_range=self.fields[k][1]
|
|
159
|
+
logger.warning(f'Could not parse field {k} from bytes {byte_range}:')
|
|
160
|
+
self.report_record_error(record,byte_range=byte_range)
|
|
161
|
+
|
|
162
|
+
def safe_float(x):
|
|
163
|
+
"""
|
|
164
|
+
Convert a string to a float, returning 0.0 if the string is 'nan'.
|
|
165
|
+
"""
|
|
166
|
+
if x=='nan':
|
|
167
|
+
return 0.0
|
|
168
|
+
return float(x)
|
|
169
|
+
|
|
170
|
+
def str2int_sig(arg:str):
|
|
171
|
+
"""
|
|
172
|
+
Convert a string to an integer, returning -1 if the string is not numeric.
|
|
173
|
+
If the string starts with a '-', it is returned as an integer.
|
|
174
|
+
"""
|
|
175
|
+
if not arg.strip().isnumeric():
|
|
176
|
+
if arg.strip()[0]=='-':
|
|
177
|
+
return int(arg)
|
|
178
|
+
else:
|
|
179
|
+
return -1
|
|
180
|
+
return int(arg)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
.. module:: baserecord
|
|
4
|
+
:synopsis: defines the BaseRecord class
|
|
5
|
+
|
|
6
|
+
.. moduleauthor: Cameron F. Abrams, <cfa22@drexel.edu>
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
from .baseparsers import StringParser
|
|
10
|
+
import logging
|
|
11
|
+
logger=logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
def rstr(d,excludes,pad):
|
|
14
|
+
"""
|
|
15
|
+
Generate a formatted string representation of a dictionary, excluding specified keys.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
d : dict
|
|
20
|
+
The dictionary to format.
|
|
21
|
+
excludes : list
|
|
22
|
+
A list of keys to exclude from the output.
|
|
23
|
+
pad : int
|
|
24
|
+
The padding for the keys in the output string.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
str
|
|
29
|
+
A formatted string representation of the dictionary.
|
|
30
|
+
"""
|
|
31
|
+
retstr=''
|
|
32
|
+
kfstr=r'{:>'+str(pad)+r's}:'
|
|
33
|
+
for k,v in d.items():
|
|
34
|
+
if not k in excludes:
|
|
35
|
+
retstr+=kfstr.format(k)
|
|
36
|
+
if type(v)==dict:
|
|
37
|
+
retstr+='\n'
|
|
38
|
+
retstr+=rstr(v,excludes,pad+5)
|
|
39
|
+
elif hasattr(v,'__len__') and not type(v)==str:
|
|
40
|
+
ch=['','']
|
|
41
|
+
if hasattr(v[0],'__dict__'):
|
|
42
|
+
ch=['[',']']
|
|
43
|
+
retstr+=' '+', '.join([f'{ch[0]}{str(x)}{ch[1]}' for x in v])+'\n'
|
|
44
|
+
else: # type(v)==str:
|
|
45
|
+
retstr+=f' {str(v)}'+'\n'
|
|
46
|
+
return retstr
|
|
47
|
+
|
|
48
|
+
class BaseRecord:
|
|
49
|
+
"""
|
|
50
|
+
A class representing a base record with fields and methods for parsing and displaying.
|
|
51
|
+
"""
|
|
52
|
+
def __init__(self,input_dict):
|
|
53
|
+
self.__dict__.update(input_dict)
|
|
54
|
+
|
|
55
|
+
def empty(self):
|
|
56
|
+
"""
|
|
57
|
+
Check if the record is empty, meaning all fields are empty strings.
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
bool
|
|
62
|
+
True if all fields are empty strings, False otherwise.
|
|
63
|
+
"""
|
|
64
|
+
isempty=True
|
|
65
|
+
for v in self.__dict__.values():
|
|
66
|
+
isempty&=(v=='')
|
|
67
|
+
return isempty
|
|
68
|
+
|
|
69
|
+
def __str__(self):
|
|
70
|
+
"""
|
|
71
|
+
Generate a string representation of the BaseRecord instance.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
str
|
|
76
|
+
A string representation of the BaseRecord instance, showing its attributes and values.
|
|
77
|
+
"""
|
|
78
|
+
return '; '.join([f'{k}: {v}' for k,v in self.__dict__.items()])
|
|
79
|
+
|
|
80
|
+
def pstr(self,excludes=['key','format','continuation'],pad=20):
|
|
81
|
+
"""
|
|
82
|
+
Generate a formatted string representation of the BaseRecord instance, excluding specified keys.
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
excludes : list, optional
|
|
87
|
+
A list of keys to exclude from the output (default is ['key', 'format', 'continuation']).
|
|
88
|
+
pad : int, optional
|
|
89
|
+
The padding for the keys in the output string (default is 20).
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
str
|
|
94
|
+
A formatted string representation of the BaseRecord instance, excluding specified keys.
|
|
95
|
+
"""
|
|
96
|
+
retstr=f'{self.key}'+'\n'
|
|
97
|
+
retstr+=rstr(self.__dict__,excludes,pad)
|
|
98
|
+
return retstr
|
|
99
|
+
|
|
100
|
+
class BaseRecordParser(StringParser):
|
|
101
|
+
"""
|
|
102
|
+
A parser for fixed-width string records that generates BaseRecord instances. Inherits from :class:`StringParser`.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def add_fields(self,fields):
|
|
106
|
+
"""
|
|
107
|
+
Add fields to the parser's field map.
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
fields : dict
|
|
112
|
+
A dictionary of fields to add to the parser's field map.
|
|
113
|
+
"""
|
|
114
|
+
self.fields.update(fields)
|
|
115
|
+
|
|
116
|
+
def parse(self,record):
|
|
117
|
+
"""
|
|
118
|
+
Parse a fixed-width string record into a BaseRecord instance.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
record : str
|
|
123
|
+
The fixed-width string record to parse.
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
BaseRecord
|
|
128
|
+
A BaseRecord instance containing the parsed fields.
|
|
129
|
+
"""
|
|
130
|
+
input_dict=super().parse(record)
|
|
131
|
+
return BaseRecord(input_dict)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Author: Cameron F. Abrams <cfa22@drexel.edu>
|
|
2
|
+
"""
|
|
3
|
+
Function for detecting a switch from decimal to hexadecimal in integer parsing.
|
|
4
|
+
This is used to determine if a string should be parsed as a hexadecimal number or a plain decimal integer.
|
|
5
|
+
"""
|
|
6
|
+
__hex_tripped__=False
|
|
7
|
+
def str2atomSerial(arg):
|
|
8
|
+
"""
|
|
9
|
+
Convert a string representation of an atom serial number to an integer. Should be used in cases were an integer series changes format from decimal representation to hexadecimal representation beyond 99999. The transition is signaled by the presence of hexadecimal characters in the string, which sets a global flag to indicate that subsequent strings should be parsed as hexadecimal numbers.
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
arg : str
|
|
14
|
+
The string representation of the atom serial number.
|
|
15
|
+
|
|
16
|
+
Returns
|
|
17
|
+
-------
|
|
18
|
+
int
|
|
19
|
+
The integer representation of the atom serial number.
|
|
20
|
+
"""
|
|
21
|
+
global __hex_tripped__
|
|
22
|
+
assert type(arg)==str
|
|
23
|
+
if arg=='nan':
|
|
24
|
+
return_object=0
|
|
25
|
+
elif __hex_tripped__ or any([(x in arg) for x in 'abcdefABCDEF']):
|
|
26
|
+
return_object=int(arg,16)
|
|
27
|
+
elif '*' in arg:
|
|
28
|
+
return_object=0
|
|
29
|
+
else:
|
|
30
|
+
return_object=int(arg)
|
|
31
|
+
if return_object>99999 and not __hex_tripped__:
|
|
32
|
+
__hex_tripped__=True
|
|
33
|
+
return return_object
|
|
34
|
+
|
|
35
|
+
def hex_reset():
|
|
36
|
+
"""
|
|
37
|
+
Reset the hexadecimal parsing flag.
|
|
38
|
+
"""
|
|
39
|
+
global __hex_tripped__
|
|
40
|
+
__hex_tripped__=False
|