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.
Files changed (60) hide show
  1. {pidibble-1.2.2 → pidibble-1.3.0}/PKG-INFO +8 -3
  2. {pidibble-1.2.2 → pidibble-1.3.0}/README.md +7 -2
  3. pidibble-1.3.0/docs/source/_static/css/custom.css +12 -0
  4. pidibble-1.3.0/docs/source/api/API.rst +7 -0
  5. pidibble-1.3.0/docs/source/api/pidibble.baseparsers.rst +7 -0
  6. pidibble-1.3.0/docs/source/api/pidibble.baserecord.rst +7 -0
  7. pidibble-1.3.0/docs/source/api/pidibble.hex.rst +7 -0
  8. pidibble-1.3.0/docs/source/api/pidibble.mmcif_parse.rst +7 -0
  9. pidibble-1.3.0/docs/source/api/pidibble.pdbparse.rst +7 -0
  10. pidibble-1.3.0/docs/source/api/pidibble.pdbrecord.rst +7 -0
  11. pidibble-1.3.0/docs/source/api/pidibble.resources.rst +7 -0
  12. pidibble-1.3.0/docs/source/api/pidibble.rst +28 -0
  13. pidibble-1.3.0/docs/source/conf.py +85 -0
  14. {pidibble-1.2.2 → pidibble-1.3.0}/docs/source/index.rst +7 -5
  15. pidibble-1.3.0/docs/source/installation.rst +16 -0
  16. {pidibble-1.2.2 → pidibble-1.3.0}/docs/source/usage.rst +0 -17
  17. pidibble-1.3.0/pidibble/__init__.py +8 -0
  18. pidibble-1.3.0/pidibble/baseparsers.py +180 -0
  19. pidibble-1.3.0/pidibble/baserecord.py +131 -0
  20. pidibble-1.3.0/pidibble/hex.py +40 -0
  21. {pidibble-1.2.2 → pidibble-1.3.0}/pidibble/mmcif_parse.py +128 -1
  22. {pidibble-1.2.2 → pidibble-1.3.0}/pidibble/pdbparse.py +159 -49
  23. {pidibble-1.2.2 → pidibble-1.3.0}/pidibble/pdbrecord.py +226 -20
  24. pidibble-1.3.0/pidibble/resources/__init__.py +6 -0
  25. {pidibble-1.2.2 → pidibble-1.3.0}/pyproject.toml +1 -1
  26. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb.py +6 -5
  27. pidibble-1.2.2/docs/source/api.rst +0 -7
  28. pidibble-1.2.2/docs/source/conf.py +0 -35
  29. pidibble-1.2.2/docs/source/generated/pidibble.rst +0 -6
  30. pidibble-1.2.2/pidibble/baseparsers.py +0 -76
  31. pidibble-1.2.2/pidibble/baserecord.py +0 -53
  32. pidibble-1.2.2/pidibble/hex.py +0 -19
  33. pidibble-1.2.2/pidibble/resources/__init__.py +0 -0
  34. pidibble-1.2.2/tests/__init__.py +0 -0
  35. {pidibble-1.2.2 → pidibble-1.3.0}/.github/workflows/release.yaml +0 -0
  36. {pidibble-1.2.2 → pidibble-1.3.0}/.gitignore +0 -0
  37. {pidibble-1.2.2 → pidibble-1.3.0}/.readthedocs.yaml +0 -0
  38. {pidibble-1.2.2 → pidibble-1.3.0}/LICENSE +0 -0
  39. {pidibble-1.2.2 → pidibble-1.3.0}/MANIFEST.in +0 -0
  40. {pidibble-1.2.2 → pidibble-1.3.0}/docs/Makefile +0 -0
  41. {pidibble-1.2.2 → pidibble-1.3.0}/docs/make.bat +0 -0
  42. {pidibble-1.2.2 → pidibble-1.3.0}/docs/requirements.txt +0 -0
  43. {pidibble-1.2.2 → pidibble-1.3.0}/docs/source/notes.md +0 -0
  44. {pidibble-1.2.2 → pidibble-1.3.0}/pidibble/resources/mmcif_format.yaml +0 -0
  45. {pidibble-1.2.2 → pidibble-1.3.0}/pidibble/resources/pdb_format.yaml +0 -0
  46. {pidibble-1.2.2/pidibble → pidibble-1.3.0/tests}/__init__.py +0 -0
  47. {pidibble-1.2.2 → pidibble-1.3.0}/tests/conftest.py +0 -0
  48. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_hex/my_system.pdb +0 -0
  49. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_hex.py +0 -0
  50. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/4tvp.cif +0 -0
  51. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/4tvp.pdb +0 -0
  52. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/4zmj-newresnames.pdb +0 -0
  53. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/4zmj.cif +0 -0
  54. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/4zmj.pdb +0 -0
  55. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/6m0j.pdb +0 -0
  56. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/8fae.cif +0 -0
  57. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/G.pdb +0 -0
  58. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/GG.pdb +0 -0
  59. {pidibble-1.2.2 → pidibble-1.3.0}/tests/unit/test_rcsb/test.pdb +0 -0
  60. {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.2.2
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 - a complete PDB parser
19
+ # Pidibble
20
+ > a complete PDB-file parser
21
+
22
+ [![PyPI Downloads](https://static.pepy.tech/badge/pidibble)](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.2.2:
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 - a complete PDB parser
1
+ # Pidibble
2
+ > a complete PDB-file parser
3
+
4
+ [![PyPI Downloads](https://static.pepy.tech/badge/pidibble)](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.2.2:
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,7 @@
1
+ pidibble
2
+ ========
3
+
4
+ .. toctree::
5
+ :maxdepth: 4
6
+
7
+ pidibble
@@ -0,0 +1,7 @@
1
+ pidibble.baseparsers module
2
+ ===========================
3
+
4
+ .. automodule:: pidibble.baseparsers
5
+ :members:
6
+ :show-inheritance:
7
+ :undoc-members:
@@ -0,0 +1,7 @@
1
+ pidibble.baserecord module
2
+ ==========================
3
+
4
+ .. automodule:: pidibble.baserecord
5
+ :members:
6
+ :show-inheritance:
7
+ :undoc-members:
@@ -0,0 +1,7 @@
1
+ pidibble.hex module
2
+ ===================
3
+
4
+ .. automodule:: pidibble.hex
5
+ :members:
6
+ :show-inheritance:
7
+ :undoc-members:
@@ -0,0 +1,7 @@
1
+ pidibble.mmcif\_parse module
2
+ ============================
3
+
4
+ .. automodule:: pidibble.mmcif_parse
5
+ :members:
6
+ :show-inheritance:
7
+ :undoc-members:
@@ -0,0 +1,7 @@
1
+ pidibble.pdbparse module
2
+ ========================
3
+
4
+ .. automodule:: pidibble.pdbparse
5
+ :members:
6
+ :show-inheritance:
7
+ :undoc-members:
@@ -0,0 +1,7 @@
1
+ pidibble.pdbrecord module
2
+ =========================
3
+
4
+ .. automodule:: pidibble.pdbrecord
5
+ :members:
6
+ :show-inheritance:
7
+ :undoc-members:
@@ -0,0 +1,7 @@
1
+ pidibble.resources package
2
+ ==========================
3
+
4
+ .. automodule:: pidibble.resources
5
+ :members:
6
+ :show-inheritance:
7
+ :undoc-members:
@@ -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 ATOMs and CONECTs.
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 ``PDBParser`` class in the ``pidibble.pdbparser`` module.
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,8 @@
1
+ # Author: Cameron F. Abrams <cfa22@drexel.edu>
2
+
3
+ from importlib.metadata import version, PackageNotFoundError
4
+
5
+ try:
6
+ __version__ = version("pidibble")
7
+ except PackageNotFoundError:
8
+ __version__ = "unknown"
@@ -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