├── .bumpversion.cfg
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── tool-request.md
└── workflows
│ ├── build_and_test.yml
│ ├── bump-version-on-push.yml
│ └── yaml_linter.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── MANIFEST.in
├── PACKAGING.md
├── README.md
├── citation.tex
├── docs
├── _config.yml
├── assets
│ └── css
│ │ └── style.scss
├── cookbook.md
├── doctools.py
├── index.md
├── index.md.template
└── logo
│ └── pdb-tools.png
├── pdbtools
├── __init__.py
├── pdb_b.py
├── pdb_chain.py
├── pdb_chainbows.py
├── pdb_chainxseg.py
├── pdb_chkensemble.py
├── pdb_delchain.py
├── pdb_delelem.py
├── pdb_delhetatm.py
├── pdb_delinsertion.py
├── pdb_delres.py
├── pdb_delresname.py
├── pdb_element.py
├── pdb_fetch.py
├── pdb_fixinsert.py
├── pdb_fromcif.py
├── pdb_gap.py
├── pdb_head.py
├── pdb_intersect.py
├── pdb_keepcoord.py
├── pdb_merge.py
├── pdb_mkensemble.py
├── pdb_occ.py
├── pdb_reatom.py
├── pdb_reres.py
├── pdb_rplchain.py
├── pdb_rplresname.py
├── pdb_seg.py
├── pdb_segxchain.py
├── pdb_selaltloc.py
├── pdb_selatom.py
├── pdb_selchain.py
├── pdb_selelem.py
├── pdb_selhetatm.py
├── pdb_selmodel.py
├── pdb_selres.py
├── pdb_selresname.py
├── pdb_selseg.py
├── pdb_shiftres.py
├── pdb_sort.py
├── pdb_splitchain.py
├── pdb_splitmodel.py
├── pdb_splitseg.py
├── pdb_tidy.py
├── pdb_tocif.py
├── pdb_tofasta.py
├── pdb_uniqname.py
├── pdb_validate.py
└── pdb_wc.py
├── setup.cfg
├── setup.py
└── tests
├── README.md
├── __init__.py
├── config.py
├── data
├── anisou.pdb
├── anisou_altloc.pdb
├── anisou_missing.pdb
├── dummy.pdb
├── dummy_altloc.pdb
├── dummy_altloc2.pdb
├── dummy_altloc3.pdb
├── dummy_az09.pdb
├── dummy_insertions.pdb
├── dummy_nohead.pdb
├── ensemble_OK.cif
├── ensemble_OK.pdb
├── ensemble_error_1.pdb
├── ensemble_error_2.pdb
├── ensemble_error_3.pdb
├── ensemble_error_4.pdb
├── ensemble_error_MODEL.pdb
├── ensemble_more_OK.pdb
├── hetatm.pdb
├── hetatm_bad.pdb
├── hetatm_ensemble.pdb
└── vu7.pdb
├── test_pdb_b.py
├── test_pdb_chain.py
├── test_pdb_chainbows.py
├── test_pdb_chainxseg.py
├── test_pdb_chkensemble.py
├── test_pdb_delchain.py
├── test_pdb_delelem.py
├── test_pdb_delhetatm.py
├── test_pdb_delinsertion.py
├── test_pdb_delres.py
├── test_pdb_delresname.py
├── test_pdb_element.py
├── test_pdb_fixinsert.py
├── test_pdb_fromcif.py
├── test_pdb_gap.py
├── test_pdb_head.py
├── test_pdb_intersect.py
├── test_pdb_keepcoord.py
├── test_pdb_merge.py
├── test_pdb_mkensemble.py
├── test_pdb_occ.py
├── test_pdb_reatom.py
├── test_pdb_reres.py
├── test_pdb_rplchain.py
├── test_pdb_rplresname.py
├── test_pdb_seg.py
├── test_pdb_segxchain.py
├── test_pdb_selaltloc.py
├── test_pdb_selatom.py
├── test_pdb_selchain.py
├── test_pdb_selelem.py
├── test_pdb_selhetatm.py
├── test_pdb_selmodel.py
├── test_pdb_selres.py
├── test_pdb_selresname.py
├── test_pdb_selseg.py
├── test_pdb_shiftres.py
├── test_pdb_sort.py
├── test_pdb_splitchain.py
├── test_pdb_splitmodel.py
├── test_pdb_splitseg.py
├── test_pdb_tidy.py
├── test_pdb_tocif.py
├── test_pdb_tofasta.py
├── test_pdb_uniqname.py
├── test_pdb_validate.py
├── test_pdb_wc.py
└── utils.py
/.bumpversion.cfg:
--------------------------------------------------------------------------------
1 | [bumpversion]
2 | current_version = 2.5.1
3 | commit = True
4 | message = [SKIP] version bump {current_version} -> {new_version}
5 | tag = True
6 |
7 | [bumpversion:file:setup.py]
8 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set LF for Python files
2 | *py text eol=lf
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is. Ex. My PDB file is blank after I use this tool!
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior. Preferrably, a code snippet.
15 | ```
16 | pdb_fetch.py 1xxx.pdb | pdb_oopsy.py > result
17 | cat result # result is empty!
18 | ```
19 |
20 | **Expected behavior**
21 | The output PDB file should not be blank, but should have YY replaced!
22 |
23 |
24 | **Desktop (please complete the following information):**
25 | - OS: [e.g. Mac OS, Linux, Windows]
26 | - Python Version [e.g. 2.7, 3.6, 3.7]
27 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/tool-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Tool request
3 | about: Suggest an idea for a new pdb-tool
4 | title: ''
5 | labels: Tool Request
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your tool request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always trying to do [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen. Ex. I would like a tool that did [...]
15 |
16 | **Does any of the existing tools or a combination of them do what you want?**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Remember**
20 | Each `pdb-tools` should do one and one job only. If you have to describe what you want using an _and_, probably it is the job for at least _two_ tools. We want simple tools that can be combined, for flexibility.
21 |
--------------------------------------------------------------------------------
/.github/workflows/build_and_test.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: build_and_test
3 |
4 | on: # yamllint disable-line rule:truthy
5 | pull_request:
6 | branches:
7 | - master
8 | push:
9 | branches:
10 | - master
11 |
12 | jobs:
13 | # Only lint in one platform to save compute time
14 | linter:
15 | runs-on: ${{ matrix.platform }}
16 | strategy:
17 | matrix:
18 | platform: [ubuntu-latest]
19 | python-version: ["3.10"]
20 |
21 | steps:
22 | - uses: actions/checkout@v2
23 |
24 | - name: Set up Python ${{ matrix.python-version }}
25 | uses: actions/setup-python@v2
26 | with:
27 | python-version: ${{ matrix.python-version }}
28 |
29 | - name: Install dependencies
30 | run: |
31 | python -m pip install --upgrade pip setuptools wheel
32 | shell: bash
33 |
34 | - name: Linter
35 | run: |
36 | python -m pip install flake8
37 | flake8 .
38 |
39 | build_and_test:
40 | name: Build & Test
41 | runs-on: ${{ matrix.platform }}
42 | needs: [linter]
43 | strategy:
44 | matrix:
45 | platform: [ubuntu-latest, macos-latest, windows-latest]
46 | python-version: [3.7, 3.8, 3.9, "3.10"]
47 |
48 | steps:
49 | - uses: actions/checkout@v2
50 |
51 | - name: Set up Python ${{ matrix.python-version }}
52 | uses: actions/setup-python@v2
53 | with:
54 | python-version: ${{ matrix.python-version }}
55 |
56 | - name: Install dependencies
57 | run: |
58 | python -m pip install --upgrade pip setuptools wheel
59 | python -m pip install --upgrade bump2version twine
60 | python -m pip install --upgrade coverage
61 |
62 | - name: Build
63 | run: |
64 | python --version
65 | python setup.py sdist bdist_wheel
66 | twine check dist/*.whl
67 | twine check dist/*.tar.gz
68 | bump2version --dry-run --verbose --allow-dirty patch
69 | bump2version --dry-run --verbose --allow-dirty minor
70 | bump2version --dry-run --verbose --allow-dirty major
71 |
72 | - name: Install pdb-tools
73 | run: |
74 | python -m pip install .
75 |
76 | - name: Test on Linux
77 | run: |
78 | script -q -e -c "coverage run -p setup.py test"
79 | shell: bash
80 | if: matrix.os == 'ubuntu-latest'
81 |
82 | - name: Test on MacOS / Windows
83 | run: |
84 | coverage run -p setup.py test
85 | shell: bash
86 | if: matrix.os != 'ubuntu-latest'
87 | env:
88 | SKIP_TTY_TESTS: true
89 |
90 | # Store coverage data for later
91 | # https://hynek.me/articles/ditch-codecov-python/
92 | - name: Store coverage data
93 | uses: actions/upload-artifact@v3
94 | with:
95 | name: coverage-data
96 | path: .coverage.*
97 | if-no-files-found: ignore
98 | retention-days: 2
99 |
100 | # Combine and analyze coverage
101 | # https://hynek.me/articles/ditch-codecov-python/
102 | coverage:
103 | name: Combine & Check coverage.
104 | runs-on: ubuntu-latest
105 | needs: [build_and_test]
106 | steps:
107 | - uses: actions/checkout@v2
108 | - uses: actions/setup-python@v2
109 | with:
110 | python-version: "3.10"
111 |
112 | - run: |
113 | python -m pip install --upgrade pip setuptools wheel
114 | python -m pip install --upgrade coverage
115 |
116 | - name: Download coverage data.
117 | uses: actions/download-artifact@v2
118 | with:
119 | name: coverage-data
120 |
121 | # We should update the threshold as we write more tests.
122 | # It should be the minimum we are comfortable with.
123 | - name: Combine coverage & fail if it's < threshold %.
124 | run: |
125 | python -m coverage combine
126 | python -m coverage html --skip-covered --skip-empty
127 | python -m coverage report --fail-under=80
128 |
129 | - name: Upload HTML report if check failed.
130 | uses: actions/upload-artifact@v2
131 | with:
132 | name: html-report
133 | path: htmlcov
134 | if: ${{ failure() }}
135 |
--------------------------------------------------------------------------------
/.github/workflows/bump-version-on-push.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # yamllint disable rule:line-length
3 | name: Bump_and_Package
4 |
5 | on: # yamllint disable-line rule:truthy
6 | push:
7 | branches:
8 | - master
9 |
10 | jobs:
11 | bump-version:
12 |
13 | runs-on: ubuntu-latest
14 | if: "!startsWith(github.event.head_commit.message, '[SKIP]')"
15 |
16 | steps:
17 |
18 | - uses: actions/checkout@v1
19 |
20 | - name: Set up Python
21 | uses: actions/setup-python@v1
22 | with:
23 | python-version: '3.x'
24 |
25 | - name: Setup Git
26 | run: |
27 | git config user.name "JoaoRodrigues"
28 | git config user.email 'joaorodrigues@users.noreply.github.com'
29 | git remote set-url origin \https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
30 | git checkout "${GITHUB_REF:11}"
31 |
32 | - name: Create skip flag
33 | run: |
34 | echo "SKIPBUMP=FALSE" >> $GITHUB_ENV
35 |
36 | - name: Install dependencies
37 | run: |
38 | python -m pip install --upgrade pip
39 | pip install bump2version setuptools wheel twine
40 |
41 | - name: Bump Minor Version
42 | run: |
43 | bump2version minor
44 | echo "SKIPBUMP=TRUE" >> $GITHUB_ENV
45 | if: "startsWith(github.event.head_commit.message, '[FEATURE]')"
46 |
47 | # Default action
48 | - name: Bump Patch Version
49 | run: |
50 | bump2version patch
51 | if: env.SKIPBUMP == 'FALSE'
52 |
53 | # No major version change should go through automatically.
54 |
55 | - name: Commit version change to master
56 | run: |
57 | git push --follow-tags
58 |
59 | - name: Build and publish
60 | env:
61 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
62 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
63 | run: |
64 | python setup.py sdist bdist_wheel
65 | twine upload dist/*
66 |
--------------------------------------------------------------------------------
/.github/workflows/yaml_linter.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Yaml Lint
3 | on: [push, pull_request] # yamllint disable-line rule:truthy
4 | jobs:
5 | lintAllTheThings:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v2
9 | - name: yaml-lint
10 | uses: ibiqlik/action-yamllint@v3
11 | with:
12 | file_or_dir: .github/workflows/*.yml
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Python setuptools
2 | build/
3 | dist/
4 | *egg-info/
5 | *__pycache__
6 | *.pyc
7 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at j.p.g.l.m.rodrigues@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | graft pdbtools
2 |
3 | include LICENSE
4 | include README.md
5 | include citation.tex
6 |
7 | exclude .appveyor.yml
8 | exclude .bumpversion.cfg
9 | exclude .coveragerc
10 | exclude .travis.yml
11 | exclude CODE_OF_CONDUCT.md
12 | exclude CONTRIBUTING.md
13 | exclude MANIFEST.in
14 | exclude PACKING.md
15 | exclude tox.ini
16 |
17 | prune docs
18 | prune tests
19 |
20 | global-exclude *.py[cod] __pycache__/* *.so *.dylib
21 | global-exclude *.swp
22 | global-exclude tags
23 |
--------------------------------------------------------------------------------
/PACKAGING.md:
--------------------------------------------------------------------------------
1 | # Packaging pdb-tools
2 | This guide describes (roughly) how to package `pdb-tools` into something `pip`
3 | can handle.
4 |
5 | ## Versioning
6 | `pdb-tools` uses semantic versioning: MAJOR.MINOR.PATCH (e.g. 2.0.0). It works
7 | more or less like this:
8 |
9 | * For every new release fixing bugs, increment the PATCH counter (e.g. 2.0.1).
10 | * For every release adding minor features to tools or new tools that do not
11 | impact any of the existing ones, increment MINOR (e.g. 2.1.0).
12 | * For major changes that will most likely impact the usage of existing tools,
13 | increment MAJOR (e.g. 3.0.0).
14 |
15 | In practice, when packaging the distribution we should update the version number
16 | in `setup.py` accordingly (otherwise `twine` will give an error).
17 |
18 | ## Packaging
19 | This is a rough guide to building a distributable package:
20 |
21 | ### (1) Ensure all tests pass:
22 | ```bash
23 | python setup.py test
24 | ```
25 |
26 | 2. Ensure there are no warnings from flake8:
27 | ```bash
28 | flake8 --ignore=E501,E731
29 | ```
30 |
31 | 3. Build the distributable package:
32 | ```bash
33 | python setup.py sdist bdist_wheel
34 |
35 | # Upload to PyPI: testing repo for now.
36 | #twine upload --repository-url https://test.pypi.org/legacy/ dist/*
37 | twine upload dist/*
38 | ```
39 |
--------------------------------------------------------------------------------
/citation.tex:
--------------------------------------------------------------------------------
1 | @article {Rodrigues483305,
2 | author = {Rodrigues, Jo{\~a}o P.G.L.M. and Teixeira, Jo{\~a}o M.C. and Trellet, Mika{\"e}l and Bonvin, Alexandre M.J.J.},
3 | title = {pdb-tools: a swiss army knife for molecular structures},
4 | elocation-id = {483305},
5 | year = {2018},
6 | doi = {10.1101/483305},
7 | publisher = {Cold Spring Harbor Laboratory},
8 | abstract = {The pdb-tools are a collection of Python scripts for working with molecular structure data in the PDB format. They allow users to edit, convert, and validate PDB files, from the command-line, in a simple but efficient manner. The pdb-tools are implemented in Python, without any external dependencies, and are freely available under the open-source Apache License at https://github.com/haddocking/pdb-tools/ and on PyPI (https://pypi.org/project/pdb-tools/).},
9 | URL = {https://www.biorxiv.org/content/early/2018/12/04/483305},
10 | eprint = {https://www.biorxiv.org/content/early/2018/12/04/483305.full.pdf},
11 | journal = {bioRxiv}
12 | }
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | title: 'pdb-tools'
2 | headline: ''
3 |
4 | logo: /logo/pdb-tools.png
5 | show_downloads: true
6 | description: 'A swiss army knife for editing PDB files.'
7 | theme: jekyll-theme-minimal
8 | github:
9 | zip_url: https://github.com/haddocking/pdb-tools/archive/2.0.0-rc1.zip
10 | tar_url: https://github.com/haddocking/pdb-tools/archive/2.0.0-rc1.tar.gz
11 |
--------------------------------------------------------------------------------
/docs/assets/css/style.scss:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | @import "{{ site.theme }}";
5 |
6 | /* Pad image bottom */
7 | .wrapper img {
8 | padding-bottom: 1%;
9 | }
10 |
11 | /* Remove bullets from ordered lists */
12 | ul {
13 | list-style-type: none;
14 | padding: 0;
15 | margin: 0;
16 | }
17 |
18 | /* Make content wider */
19 | .wrapper {
20 | width: 1200px;
21 | }
22 |
23 | section {
24 | width: 860px;
25 | }
26 |
27 | footer {
28 | width: 280px;
29 | }
30 |
--------------------------------------------------------------------------------
/docs/cookbook.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 | * Removing hydrogens, renaming `HIP` to `HIS`, and renumbering atoms, for all PDBs from a folder to a new folder:
6 |
7 | ```bash
8 | mkdir folder_new
9 | for i in folder/*pdb; do pdb_delelem -H $i | pdb_rplresname -HIP:HIS | pdb_reatom -1 | pdb_tidy > folder_new/$(basename $i); done
10 | ```
11 |
--------------------------------------------------------------------------------
/docs/doctools.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | """
4 | Scrapes tool headers for documentation.
5 | """
6 |
7 | import importlib
8 | from pathlib import Path
9 |
10 | with open('index.md', 'w') as handle:
11 |
12 | with open('index.md.template') as template:
13 | print(template.read(), file=handle)
14 |
15 | import pdbtools
16 | modfile = Path(pdbtools.__file__)
17 | for f in sorted(list(modfile.parent.iterdir())):
18 | # ignore __init__.py and others.
19 | if f.stem.startswith('_') or f.suffix != '.py':
20 | continue
21 |
22 | # Dynamically import tool to get __doc__
23 | name = f.stem
24 | try:
25 | tool = importlib.import_module(f'pdbtools.{name}')
26 | except ModuleNotFoundError:
27 | print(f'Could not import module: {name}')
28 | continue
29 |
30 | # Parse documentation from docstrings
31 | # Preserve white-space as best as possible.
32 | # First non-empty line is always short description.
33 | # Last lines are always licensing disclaimer
34 | summary = None
35 | long_description = []
36 |
37 | doctext = tool.__doc__.replace('<', '<').replace('>', '>')
38 | for line in doctext.split('\n'):
39 | if summary is None and not line.strip():
40 | continue
41 | if line.startswith('This program is part of the'):
42 | break
43 | elif summary is None:
44 | summary = line
45 | else:
46 | long_description.append(line)
47 |
48 | long_description = '\n'.join(long_description)
49 | print('
', file=handle)
50 | print('
', file=handle)
51 | print(f"{name} {summary}
", file=handle)
52 | print(
53 | f'{long_description} ',
54 | file=handle
55 | )
56 | print(' ', file=handle)
57 | print('
', file=handle)
58 |
--------------------------------------------------------------------------------
/docs/logo/pdb-tools.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/haddocking/pdb-tools/e4368bcdbb4d862606f12f2456bf888db6ded4a0/docs/logo/pdb-tools.png
--------------------------------------------------------------------------------
/pdbtools/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | """The pdb-tools library.
18 |
19 | A Swiss army knife for manipulating and editing PDB files.
20 |
21 | You can use pdb-tools as a library or as a series of convenient
22 | command-line applications. The complete documentation is available at:
23 |
24 | http://www.bonvinlab.org/pdb-tools/
25 |
26 | Examples at the command-line
27 | ----------------------------
28 |
29 | $ pdb_fetch 1brs > 1brs.pdb
30 | $ pdb_reres -1 1ctf.pdb > 1ctf_renumbered.pdb
31 | $ pdb_selchain -A,D 1brs.pdb | pdb_delhetatm | pdb_tidy > 1brs_AD_noHET.pdb
32 |
33 |
34 | Examples using pdb-tools as library
35 | -----------------------------------
36 |
37 | You can import according to your needs:
38 |
39 | >>> import pdbtools
40 | >>> from pdbtools import *
41 | >>> from pdbtools import MODULE
42 | >>> from pdbtools import pdb_selchain
43 |
44 | Chain the different functionalities conveniently:
45 |
46 | >>> from pdbtools import pdb_selchain, pdb_selatom, pdb_keepcoord
47 | >>> with open('dummy.pdb') as fh:
48 | >>> chain_a = pdb_selchain.run(fh, ['A'])
49 | >>> only_N = pdb_selatom.run(chain_a, ['N'])
50 | >>> coords = pdb_keepcoord.run(only_N)
51 | >>> final = pdb_reres.run(coords, 5)
52 | >>> print(''.join(final))
53 |
54 | The list of MODULEs is specified bellow.
55 |
56 | All packages have three functions: `check_input`, `main`, and `run`.
57 | The latter executes the logic of each package. `check_input` checks and
58 | prepares potential input parameters to feed `run`. Use `check_input` in
59 | case you are not sure the received input is correct. You can chain both
60 | functions:
61 |
62 | >>> MODULE.run(**MODULE.check_input(*args))
63 |
64 | If you control the input parameters use `run` directly. In general,
65 | `run` functions are generators yielding the modified PDB data
66 | line-by-line. `main` is used solely in the context of the command-line
67 | interface.
68 |
69 | All MODULEs and `run` functions provide comprehensive documentation.
70 |
71 | >>> help(MODULE)
72 | >>> help(MODULE.run)
73 | """
74 |
75 |
76 | __all__ = [
77 | 'pdb_b',
78 | 'pdb_chainbows',
79 | 'pdb_chain',
80 | 'pdb_chainxseg',
81 | 'pdb_chkensemble',
82 | 'pdb_delchain',
83 | 'pdb_delelem',
84 | 'pdb_delhetatm',
85 | 'pdb_delinsertion',
86 | 'pdb_delresname',
87 | 'pdb_delres',
88 | 'pdb_element',
89 | 'pdb_fetch',
90 | 'pdb_fixinsert',
91 | 'pdb_fromcif',
92 | 'pdb_gap',
93 | 'pdb_head',
94 | 'pdb_intersect',
95 | 'pdb_keepcoord',
96 | 'pdb_merge',
97 | 'pdb_mkensemble',
98 | 'pdb_occ',
99 | 'pdb_reatom',
100 | 'pdb_reres',
101 | 'pdb_rplchain',
102 | 'pdb_rplresname',
103 | 'pdb_seg',
104 | 'pdb_segxchain',
105 | 'pdb_selaltloc',
106 | 'pdb_selatom',
107 | 'pdb_selchain',
108 | 'pdb_selelem',
109 | 'pdb_selhetatm',
110 | 'pdb_selresname',
111 | 'pdb_selres',
112 | 'pdb_selseg',
113 | 'pdb_shiftres',
114 | 'pdb_sort',
115 | 'pdb_splitchain',
116 | 'pdb_splitmodel',
117 | 'pdb_splitseg',
118 | 'pdb_tidy',
119 | 'pdb_tocif',
120 | 'pdb_tofasta',
121 | 'pdb_uniqname',
122 | 'pdb_validate',
123 | 'pdb_wc',
124 | ]
125 |
--------------------------------------------------------------------------------
/pdbtools/pdb_chainbows.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2020 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Renames chain identifiers sequentially, based on TER records.
20 |
21 | Since HETATM records are not separated by TER records and usually come together
22 | at the end of the PDB file, this script will attempt to reassign their chain
23 | identifiers based on the changes it made to ATOM lines. This might lead to bad
24 | output in certain corner cases.
25 |
26 | Usage:
27 | python pdb_chainbows.py
28 |
29 | Example:
30 | python pdb_chainbows.py 1CTF.pdb
31 |
32 | This program is part of the `pdb-tools` suite of utilities and should not be
33 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
34 | files using the terminal, and can be used sequentially, with one tool streaming
35 | data to another. They are based on old FORTRAN77 code that was taking too much
36 | effort to maintain and compile. RIP.
37 | """
38 |
39 | import os
40 | import string
41 | import sys
42 |
43 | __author__ = "Joao Rodrigues"
44 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
45 |
46 |
47 | def check_input(args):
48 | """Checks whether to read from stdin/file and validates user input/options.
49 | """
50 |
51 | # Defaults
52 | fh = sys.stdin # file handle
53 |
54 | if not len(args):
55 | # Reading from pipe with default option
56 | if sys.stdin.isatty():
57 | sys.stderr.write(__doc__)
58 | sys.exit(1)
59 |
60 | elif len(args) == 1:
61 | if not os.path.isfile(args[0]):
62 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
63 | sys.stderr.write(emsg.format(args[0]))
64 | sys.stderr.write(__doc__)
65 | sys.exit(1)
66 |
67 | fh = open(args[0], 'r')
68 |
69 | else: # Whatever ...
70 | emsg = 'ERROR!! Script takes 1 argument, not \'{}\'\n'
71 | sys.stderr.write(emsg.format(len(args)))
72 | sys.stderr.write(__doc__)
73 | sys.exit(1)
74 |
75 | return fh
76 |
77 |
78 | def run(fhandle):
79 | """
80 | Set chains sequentially based on existing TER records.
81 |
82 | Follow sequence [ABC...abc...012...].
83 |
84 | This function is a generator.
85 |
86 | Parameters
87 | ----------
88 | fhandle : an iterable giving the PDB file line-by-line
89 |
90 | Yields
91 | ------
92 | str (line-by-line)
93 | The modified (or not) PDB line.
94 | """
95 | chainlist = list(
96 | string.digits[::-1] + string.ascii_lowercase[::-1] + string.ascii_uppercase[::-1]
97 | ) # 987...zyx...cbaZYX...BCA.
98 | max_chains = len(chainlist)
99 |
100 | chain_map = {} # for HETATM.
101 |
102 | curchain = chainlist.pop()
103 | records = ('ATOM', 'TER', 'ANISOU')
104 | for line in fhandle:
105 | if line.startswith(records):
106 | chain_map[line[21]] = curchain
107 | line = line[:21] + curchain + line[22:]
108 |
109 | if line.startswith('TER'):
110 | try:
111 | curchain = chainlist.pop()
112 | except IndexError:
113 | emsg = 'ERROR!! Structure contains more than {} TER records.\n'
114 | sys.stderr.write(emsg.format(max_chains))
115 | sys.stderr.write(__doc__)
116 | sys.exit(1)
117 |
118 | elif line.startswith('HETATM'):
119 | hetchain = chain_map[line[21]]
120 | line = line[:21] + hetchain + line[22:]
121 |
122 | yield line
123 |
124 |
125 | set_chain_sequence = run
126 |
127 |
128 | def main():
129 | # Check Input
130 | pdbfh = check_input(sys.argv[1:])
131 |
132 | # Do the job
133 | new_pdb = run(pdbfh)
134 |
135 | try:
136 | _buffer = []
137 | _buffer_size = 5000 # write N lines at a time
138 | for lineno, line in enumerate(new_pdb):
139 | if not (lineno % _buffer_size):
140 | sys.stdout.write(''.join(_buffer))
141 | _buffer = []
142 | _buffer.append(line)
143 |
144 | sys.stdout.write(''.join(_buffer))
145 | sys.stdout.flush()
146 | except IOError:
147 | # This is here to catch Broken Pipes
148 | # for example to use 'head' or 'tail' without
149 | # the error message showing up
150 | pass
151 |
152 | # last line of the script
153 | # We can close it even if it is sys.stdin
154 | pdbfh.close()
155 | sys.exit(0)
156 |
157 |
158 | if __name__ == '__main__':
159 | main()
160 |
--------------------------------------------------------------------------------
/pdbtools/pdb_chainxseg.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Swaps the segment identifier for the chain identifier.
20 |
21 | Usage:
22 | python pdb_chainxseg.py
23 |
24 | Example:
25 | python pdb_chainxseg.py 1CTF.pdb
26 |
27 | This program is part of the `pdb-tools` suite of utilities and should not be
28 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
29 | files using the terminal, and can be used sequentially, with one tool streaming
30 | data to another. They are based on old FORTRAN77 code that was taking too much
31 | effort to maintain and compile. RIP.
32 | """
33 |
34 | import os
35 | import sys
36 |
37 | __author__ = "Joao Rodrigues"
38 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
39 |
40 |
41 | def check_input(args):
42 | """Checks whether to read from stdin/file and validates user input/options.
43 | """
44 |
45 | # Defaults
46 | fh = sys.stdin # file handle
47 |
48 | if not len(args):
49 | # Reading from pipe
50 | if sys.stdin.isatty():
51 | sys.stderr.write(__doc__)
52 | sys.exit(1)
53 |
54 | elif len(args) == 1:
55 | # Reading from file
56 | if not os.path.isfile(args[0]):
57 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
58 | sys.stderr.write(emsg.format(args[0]))
59 | sys.stderr.write(__doc__)
60 | sys.exit(1)
61 |
62 | fh = open(args[0], 'r')
63 |
64 | else: # Whatever ...
65 | emsg = 'ERROR!! Script takes 1 argument, not \'{}\'\n'
66 | sys.stderr.write(emsg.format(len(args)))
67 | sys.stderr.write(__doc__)
68 | sys.exit(1)
69 |
70 | return fh
71 |
72 |
73 | def pad_line(line):
74 | """Helper function to pad line to 80 characters in case it is shorter"""
75 | size_of_line = len(line)
76 | if size_of_line < 80:
77 | padding = 80 - size_of_line + 1
78 | line = line.strip('\n') + ' ' * padding + '\n'
79 | return line[:81] # 80 + newline character
80 |
81 |
82 | def run(fhandle):
83 | """
84 | Replace the segment identifier with the contents of the chain identifier.
85 |
86 | Acts on ATOM/HETATM/ANISOU.
87 |
88 | This function is a generator.
89 |
90 | Parameters
91 | ----------
92 | fhandle : a line-by-line iterator of the original PDB file.
93 |
94 | Yields
95 | ------
96 | str (line-by-line)
97 | The modified (or not) PDB line.
98 | """
99 |
100 | _pad_line = pad_line
101 |
102 | records = ('ATOM', 'HETATM', 'ANISOU')
103 | for line in fhandle:
104 | if line.startswith(records):
105 | line = _pad_line(line)
106 | yield line[:72] + line[21].ljust(4) + line[76:]
107 | else:
108 | yield line
109 |
110 |
111 | place_chain_on_seg = run
112 |
113 |
114 | def main():
115 | # Check Input
116 | pdbfh = check_input(sys.argv[1:])
117 |
118 | # Do the job
119 | new_pdb = run(pdbfh)
120 |
121 | try:
122 | _buffer = []
123 | _buffer_size = 5000 # write N lines at a time
124 | for lineno, line in enumerate(new_pdb):
125 | if not (lineno % _buffer_size):
126 | sys.stdout.write(''.join(_buffer))
127 | _buffer = []
128 | _buffer.append(line)
129 |
130 | sys.stdout.write(''.join(_buffer))
131 | sys.stdout.flush()
132 | except IOError:
133 | # This is here to catch Broken Pipes
134 | # for example to use 'head' or 'tail' without
135 | # the error message showing up
136 | pass
137 |
138 | # last line of the script
139 | # We can close it even if it is sys.stdin
140 | pdbfh.close()
141 | sys.exit(0)
142 |
143 |
144 | if __name__ == '__main__':
145 | main()
146 |
--------------------------------------------------------------------------------
/pdbtools/pdb_delhetatm.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Removes all HETATM records in the PDB file.
20 |
21 | Usage:
22 | python pdb_delhetatm.py
23 |
24 | Example:
25 | python pdb_delhetatm.py 1CTF.pdb
26 |
27 | This program is part of the `pdb-tools` suite of utilities and should not be
28 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
29 | files using the terminal, and can be used sequentially, with one tool streaming
30 | data to another. They are based on old FORTRAN77 code that was taking too much
31 | effort to maintain and compile. RIP.
32 | """
33 |
34 | import os
35 | import sys
36 |
37 | __author__ = "Joao Rodrigues"
38 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
39 |
40 |
41 | def check_input(args):
42 | """Checks whether to read from stdin/file and validates user input/options.
43 | """
44 |
45 | # Defaults
46 | fh = sys.stdin # file handle
47 |
48 | if not len(args):
49 | # Reading from pipe with default option
50 | if sys.stdin.isatty():
51 | sys.stderr.write(__doc__)
52 | sys.exit(1)
53 |
54 | elif len(args) == 1:
55 | if not os.path.isfile(args[0]):
56 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
57 | sys.stderr.write(emsg.format(args[0]))
58 | sys.stderr.write(__doc__)
59 | sys.exit(1)
60 |
61 | fh = open(args[0], 'r')
62 |
63 | else: # Whatever ...
64 | emsg = 'ERROR!! Script takes 1 argument, not \'{}\'\n'
65 | sys.stderr.write(emsg.format(len(args)))
66 | sys.stderr.write(__doc__)
67 | sys.exit(1)
68 |
69 | return fh
70 |
71 |
72 | def run(fhandle):
73 | """
74 | Remove all HETATM and associated records from the PDB file.
75 |
76 | This function is a generator.
77 |
78 | Parameters
79 | ----------
80 | fhandle : a line-by-line iterator of the original PDB file.
81 |
82 | Yields
83 | ------
84 | str (line-by-line)
85 | The modified (or not) PDB line.
86 | """
87 |
88 | # CONECT 1179 746 1184 1195 1203
89 | char_ranges = (slice(6, 11), slice(11, 16),
90 | slice(16, 21), slice(21, 26), slice(26, 31))
91 |
92 | het_serials = set()
93 | for line in fhandle:
94 | if line.startswith('HETATM'):
95 | het_serials.add(line[6:11])
96 | continue
97 | elif line.startswith('ANISOU'):
98 | if line[6:11] in het_serials:
99 | continue
100 | elif line.startswith('CONECT'):
101 | if any(line[cr] in het_serials for cr in char_ranges):
102 | continue
103 |
104 | yield line
105 |
106 |
107 | remove_hetatm = run
108 |
109 |
110 | def main():
111 | # Check Input
112 | pdbfh = check_input(sys.argv[1:])
113 |
114 | # Do the job
115 | new_pdb = run(pdbfh)
116 |
117 | try:
118 | _buffer = []
119 | _buffer_size = 5000 # write N lines at a time
120 | for lineno, line in enumerate(new_pdb):
121 | if not (lineno % _buffer_size):
122 | sys.stdout.write(''.join(_buffer))
123 | _buffer = []
124 | _buffer.append(line)
125 |
126 | sys.stdout.write(''.join(_buffer))
127 | sys.stdout.flush()
128 | except IOError:
129 | # This is here to catch Broken Pipes
130 | # for example to use 'head' or 'tail' without
131 | # the error message showing up
132 | pass
133 |
134 | # last line of the script
135 | # We can close it even if it is sys.stdin
136 | pdbfh.close()
137 | sys.exit(0)
138 |
139 |
140 | if __name__ == '__main__':
141 | main()
142 |
--------------------------------------------------------------------------------
/pdbtools/pdb_delinsertion.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Deletes insertion codes in a PDB file.
20 |
21 | Deleting an insertion code shifts the residue numbering of downstream
22 | residues. Allows for picking specific residues to delete insertion codes for.
23 |
24 | Usage:
25 | python pdb_delinsertion.py [-]
26 |
27 | Example:
28 | python pdb_delinsertion.py 1CTF.pdb # delete ALL insertion codes
29 | python pdb_delinsertion.py -A9,B12 1CTF.pdb # deletes ins. codes for res
30 | # 9 of chain A and 12 of chain B.
31 |
32 | This program is part of the `pdb-tools` suite of utilities and should not be
33 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
34 | files using the terminal, and can be used sequentially, with one tool streaming
35 | data to another. They are based on old FORTRAN77 code that was taking too much
36 | effort to maintain and compile. RIP.
37 | """
38 |
39 | import sys
40 | import warnings
41 |
42 | from pdbtools import pdb_fixinsert
43 |
44 | __author__ = "Joao Rodrigues"
45 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
46 |
47 |
48 | # Monkeypatch warnings output
49 | def simpleformat(msg, *args, **kwargs):
50 | return str(msg) + '\n' # just the message
51 |
52 |
53 | warnings.formatwarning = simpleformat
54 |
55 |
56 | def main():
57 |
58 | # Add deprecation warning
59 | # We could use warnings.warn but I don't like the output
60 | # that much. This makes it stand out more.
61 | warnings.warn(
62 | "\n\n** WARNING **\n"
63 | " This tool will be deprecated in a future release.\n"
64 | " Please use pdb_fixinsertion.py instead.\n\n"
65 | )
66 |
67 | try:
68 | pdb_fixinsert.main()
69 | except SystemExit as e:
70 | sys.exit(e.code)
71 |
72 |
73 | if __name__ == '__main__':
74 | main()
75 |
--------------------------------------------------------------------------------
/pdbtools/pdb_gap.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Finds gaps between consecutive protein residues in the PDB.
20 |
21 | Detects gaps both by a distance criterion or discontinuous residue numbering.
22 | Only applies to protein residues.
23 |
24 | Usage:
25 | python pdb_gap.py
26 |
27 | Example:
28 | python pdb_gap.py 1CTF.pdb
29 |
30 | This program is part of the `pdb-tools` suite of utilities and should not be
31 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
32 | files using the terminal, and can be used sequentially, with one tool streaming
33 | data to another. They are based on old FORTRAN77 code that was taking too much
34 | effort to maintain and compile. RIP.
35 | """
36 |
37 | import os
38 | import sys
39 |
40 | __author__ = "Joao Rodrigues"
41 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
42 |
43 |
44 | def check_input(args):
45 | """Checks whether to read from stdin/file and validates user input/options.
46 | """
47 |
48 | # Defaults
49 | fh = sys.stdin # file handle
50 |
51 | if not len(args):
52 | # Reading from pipe with default option
53 | if sys.stdin.isatty():
54 | sys.stderr.write(__doc__)
55 | sys.exit(1)
56 |
57 | elif len(args) == 1:
58 | if not os.path.isfile(args[0]):
59 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
60 | sys.stderr.write(emsg.format(args[0]))
61 | sys.stderr.write(__doc__)
62 | sys.exit(1)
63 |
64 | fh = open(args[0], 'r')
65 |
66 | else: # Whatever ...
67 | emsg = 'ERROR!! Script takes 1 argument, not \'{}\'\n'
68 | sys.stderr.write(emsg.format(len(args)))
69 | sys.stderr.write(__doc__)
70 | sys.exit(1)
71 |
72 | return fh
73 |
74 |
75 | def run(fhandle):
76 | """
77 | Detect gaps between residues in the PDB file.
78 |
79 | Parameters
80 | ----------
81 | fhandle : a line-by-line iterator of the original PDB file.
82 |
83 | Returns
84 | -------
85 | None
86 | Writes to the sys.stdout.
87 | """
88 |
89 | fmt_GAPd = "{0[1]}:{0[3]}{0[2]} < {2:7.2f}A > {1[1]}:{1[3]}{1[2]}\n"
90 | fmt_GAPs = "{0[1]}:{0[3]}{0[2]} < Seq. Gap > {1[1]}:{1[3]}{1[2]}\n"
91 |
92 | centroid = ' CA ' # respect spacing. 'CA ' != ' CA '
93 | distance_threshold = 4.0 * 4.0
94 |
95 | def calculate_sq_atom_distance(i, j):
96 | """Squared euclidean distance between two 3d points"""
97 | return (i[0] - j[0]) * (i[0] - j[0]) + \
98 | (i[1] - j[1]) * (i[1] - j[1]) + \
99 | (i[2] - j[2]) * (i[2] - j[2])
100 |
101 | prev_at = (None, None, None, None, (None, None, None))
102 | model = 0
103 | n_gaps = 0
104 | for line in fhandle:
105 |
106 | if line.startswith('MODEL'):
107 | model = int(line[10:14])
108 |
109 | elif line.startswith('ATOM'):
110 | atom_name = line[12:16]
111 | if atom_name != centroid:
112 | continue
113 |
114 | resn = line[17:20]
115 | resi = int(line[22:26])
116 | chain = line[21]
117 | x = float(line[30:38])
118 | y = float(line[38:46])
119 | z = float(line[46:54])
120 |
121 | at_uid = (model, chain, resi, resn, atom_name, (x, y, z))
122 | if prev_at[0] == at_uid[0] and prev_at[1] == at_uid[1]:
123 | d = calculate_sq_atom_distance(at_uid[5], prev_at[5])
124 | if d > distance_threshold:
125 | sys.stdout.write(fmt_GAPd.format(prev_at, at_uid, d**0.5))
126 | n_gaps += 1
127 | elif prev_at[2] + 1 != at_uid[2]:
128 | sys.stdout.write(fmt_GAPs.format(prev_at, at_uid))
129 | n_gaps += 1
130 |
131 | prev_at = at_uid
132 |
133 | sys.stdout.write('Found {} gap(s) in the structure\n'.format(n_gaps))
134 |
135 |
136 | detect_gaps = run
137 |
138 |
139 | def main():
140 | # Check Input
141 | pdbfh = check_input(sys.argv[1:])
142 |
143 | # Do the job
144 | run(pdbfh)
145 |
146 | # last line of the script
147 | # We can close it even if it is sys.stdin
148 | pdbfh.close()
149 | sys.exit(0)
150 |
151 |
152 | if __name__ == '__main__':
153 | main()
154 |
--------------------------------------------------------------------------------
/pdbtools/pdb_intersect.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Returns a new PDB file only with atoms in common to all input PDB files.
20 |
21 | Atoms are judged equal is their name, altloc, res. name, res. num, insertion
22 | code and chain fields are the same. Coordinates are taken from the first input
23 | file. Keeps matching TER/ANISOU records.
24 |
25 | Usage:
26 | python pdb_intersect.py
27 |
28 | Example:
29 | python pdb_intersect.py 1XYZ.pdb 1ABC.pdb
30 |
31 | This program is part of the `pdb-tools` suite of utilities and should not be
32 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
33 | files using the terminal, and can be used sequentially, with one tool streaming
34 | data to another. They are based on old FORTRAN77 code that was taking too much
35 | effort to maintain and compile. RIP.
36 | """
37 |
38 | import collections
39 | import os
40 | import sys
41 |
42 | __author__ = "Joao Rodrigues"
43 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
44 |
45 |
46 | def check_input(args):
47 | """Checks whether to read from stdin/file and validates user input/options.
48 | """
49 |
50 | # Defaults
51 | fl = [] # file list
52 |
53 | if len(args) >= 1:
54 | for fn in args:
55 | if not os.path.isfile(fn):
56 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
57 | sys.stderr.write(emsg.format(fn))
58 | sys.stderr.write(__doc__)
59 | sys.exit(1)
60 |
61 | fh = open(fn, 'r')
62 | fl.append(fh)
63 |
64 | else: # no arguments
65 | sys.stderr.write(__doc__)
66 | sys.exit(1)
67 |
68 | return fl
69 |
70 |
71 | def run(flist):
72 | """
73 | Returns atoms common to all input files.
74 |
75 | This function is a generator.
76 |
77 | Parameters
78 | ----------
79 | flist : list of file-obj
80 | The first item is the reference PBD files to which others will
81 | be compared to. Items in this list should handle `.close()`
82 | attribute.
83 |
84 | Yields
85 | ------
86 | str (line-by-line)
87 | The modified (or not) PDB line.
88 | """
89 | atom_data = collections.OrderedDict() # atom_uid: line
90 | records = ('ATOM', 'HETATM', 'ANISOU', 'TER')
91 |
92 | ref = flist[0]
93 | for line in ref:
94 |
95 | if line.startswith(records):
96 | atom_uid = line[12:27]
97 | atom_data[atom_uid] = line
98 |
99 | ref.close()
100 |
101 | common_atoms = set(atom_data.keys())
102 | for fhandle in flist[1:]:
103 | file_atoms = set()
104 |
105 | for line in fhandle:
106 | atom_uid = line[12:27]
107 | file_atoms.add(atom_uid)
108 |
109 | fhandle.close()
110 |
111 | common_atoms = common_atoms & file_atoms
112 |
113 | for atom in atom_data:
114 | if atom in common_atoms:
115 | yield atom_data[atom]
116 |
117 |
118 | intersect_pdb_files = run
119 |
120 |
121 | def main():
122 | # Check Input
123 | pdbflist = check_input(sys.argv[1:])
124 |
125 | # Do the job
126 | new_pdb = run(pdbflist)
127 |
128 | try:
129 | _buffer = []
130 | _buffer_size = 5000 # write N lines at a time
131 | for lineno, line in enumerate(new_pdb):
132 | if not (lineno % _buffer_size):
133 | sys.stdout.write(''.join(_buffer))
134 | _buffer = []
135 | _buffer.append(line)
136 |
137 | sys.stdout.write(''.join(_buffer))
138 | sys.stdout.flush()
139 | except IOError:
140 | # This is here to catch Broken Pipes
141 | # for example to use 'head' or 'tail' without
142 | # the error message showing up
143 | pass
144 |
145 | sys.exit(0)
146 |
147 |
148 | if __name__ == '__main__':
149 | main()
150 |
--------------------------------------------------------------------------------
/pdbtools/pdb_keepcoord.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Removes all non-coordinate records from the file.
20 |
21 | Keeps only MODEL, ENDMDL, END, ATOM, HETATM, CONECT.
22 |
23 | Usage:
24 | python pdb_keepcoord.py
25 |
26 | Example:
27 | python pdb_keepcoord.py 1CTF.pdb
28 |
29 | This program is part of the `pdb-tools` suite of utilities and should not be
30 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
31 | files using the terminal, and can be used sequentially, with one tool streaming
32 | data to another. They are based on old FORTRAN77 code that was taking too much
33 | effort to maintain and compile. RIP.
34 | """
35 |
36 | import os
37 | import sys
38 |
39 | __author__ = "Joao Rodrigues"
40 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
41 |
42 |
43 | def check_input(args):
44 | """Checks whether to read from stdin/file and validates user input/options.
45 | """
46 |
47 | # Defaults
48 | fh = sys.stdin # file handle
49 |
50 | if not len(args):
51 | # Reading from pipe with default option
52 | if sys.stdin.isatty():
53 | sys.stderr.write(__doc__)
54 | sys.exit(1)
55 |
56 | elif len(args) == 1:
57 | if not os.path.isfile(args[0]):
58 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
59 | sys.stderr.write(emsg.format(args[0]))
60 | sys.stderr.write(__doc__)
61 | sys.exit(1)
62 |
63 | fh = open(args[0], 'r')
64 |
65 | else: # Whatever ...
66 | emsg = 'ERROR!! Script takes 1 argument, not \'{}\'\n'
67 | sys.stderr.write(emsg.format(len(args)))
68 | sys.stderr.write(__doc__)
69 | sys.exit(1)
70 |
71 | return fh
72 |
73 |
74 | def run(fhandle):
75 | """
76 | Keep only coordinate records in the PDB file.
77 |
78 | This function is a generator.
79 |
80 | Parameters
81 | ----------
82 | fhandle : a line-by-line iterator of the original PDB file.
83 |
84 | Yields
85 | ------
86 | str (line-by-line)
87 | Only the coordinate records in the PDB file.
88 | """
89 |
90 | records = ('MODEL ', 'ATOM ', 'HETATM',
91 | 'ENDMDL', 'END ',
92 | 'TER ', 'CONECT')
93 | for line in fhandle:
94 | if line.startswith(records):
95 | yield line
96 |
97 |
98 | keep_coordinates = run
99 |
100 |
101 | def main():
102 | # Check Input
103 | pdbfh = check_input(sys.argv[1:])
104 |
105 | # Do the job
106 | new_pdb = run(pdbfh)
107 |
108 | try:
109 | _buffer = []
110 | _buffer_size = 5000 # write N lines at a time
111 | for lineno, line in enumerate(new_pdb):
112 | if not (lineno % _buffer_size):
113 | sys.stdout.write(''.join(_buffer))
114 | _buffer = []
115 | _buffer.append(line)
116 |
117 | sys.stdout.write(''.join(_buffer))
118 | sys.stdout.flush()
119 | except IOError:
120 | # This is here to catch Broken Pipes
121 | # for example to use 'head' or 'tail' without
122 | # the error message showing up
123 | pass
124 |
125 | # last line of the script
126 | # We can close it even if it is sys.stdin
127 | pdbfh.close()
128 | sys.exit(0)
129 |
130 |
131 | if __name__ == '__main__':
132 | main()
133 |
--------------------------------------------------------------------------------
/pdbtools/pdb_merge.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João M. C. Teixeira
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Merges several PDB files into one.
20 |
21 | The contents are not sorted and no lines are deleted (e.g. END, TER
22 | statements) so we recommend piping the results through `pdb_tidy.py`.
23 |
24 | Usage:
25 | python pdb_merge.py
26 |
27 | Example:
28 | python pdb_merge.py 1ABC.pdb 1XYZ.pdb
29 |
30 | This program is part of the `pdb-tools` suite of utilities and should not be
31 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
32 | files using the terminal, and can be used sequentially, with one tool streaming
33 | data to another. They are based on old FORTRAN77 code that was taking too much
34 | effort to maintain and compile. RIP.
35 | """
36 |
37 | import os
38 | import sys
39 |
40 | __author__ = "Joao M.C. Teixeira"
41 | __email__ = "joaomcteixeira@gmail.com"
42 |
43 |
44 | def check_input(args):
45 | """Checks whether to read from stdin/file and validates user input/options.
46 | """
47 |
48 | # Defaults
49 | fl = [] # file list
50 |
51 | if len(args) >= 1:
52 | for fn in args:
53 | if not os.path.isfile(fn):
54 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
55 | sys.stderr.write(emsg.format(fn))
56 | sys.stderr.write(__doc__)
57 | sys.exit(1)
58 |
59 | fh = open(fn, 'r')
60 | fl.append(fh)
61 |
62 | else: # Whatever ...
63 | sys.stderr.write(__doc__)
64 | sys.exit(1)
65 |
66 | return fl
67 |
68 |
69 | def run(flist):
70 | """
71 | Iterate over a list of files and yields each line sequentially.
72 |
73 | Parameters
74 | ----------
75 | flist : list of file-like objects
76 | Must handle `.close()` attribute.
77 |
78 | Yields
79 | ------
80 | str (line-by-line)
81 | Lines from the concatenated PDB files.
82 | """
83 |
84 | for fhandle in flist:
85 | for line in fhandle:
86 | yield line
87 | fhandle.close()
88 |
89 |
90 | concatenate_files = run
91 |
92 |
93 | def main():
94 | # Check Input
95 | pdbfh = check_input(sys.argv[1:])
96 |
97 | # Do the job
98 | new_pdb = run(pdbfh)
99 |
100 | try:
101 | _buffer = []
102 | _buffer_size = 5000 # write N lines at a time
103 | for lineno, line in enumerate(new_pdb):
104 | if not (lineno % _buffer_size):
105 | sys.stdout.write(''.join(_buffer))
106 | _buffer = []
107 | _buffer.append(line)
108 |
109 | sys.stdout.write(''.join(_buffer))
110 | sys.stdout.flush()
111 | except IOError:
112 | # This is here to catch Broken Pipes
113 | # for example to use 'head' or 'tail' without
114 | # the error message showing up
115 | pass
116 |
117 | sys.exit(0)
118 |
119 |
120 | if __name__ == '__main__':
121 | main()
122 |
--------------------------------------------------------------------------------
/pdbtools/pdb_mkensemble.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Merges several PDB files into one multi-model (ensemble) file.
20 |
21 | Strips all HEADER information and adds REMARK statements with the provenance
22 | of each conformer.
23 |
24 | Usage:
25 | python pdb_mkensemble.py
26 |
27 | Example:
28 | python pdb_mkensemble.py 1ABC.pdb 1XYZ.pdb
29 |
30 | This program is part of the `pdb-tools` suite of utilities and should not be
31 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
32 | files using the terminal, and can be used sequentially, with one tool streaming
33 | data to another. They are based on old FORTRAN77 code that was taking too much
34 | effort to maintain and compile. RIP.
35 | """
36 |
37 | import os
38 | import sys
39 |
40 | __author__ = "Joao Rodrigues"
41 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
42 |
43 |
44 | def check_input(args):
45 | """
46 | Checks whether to read from stdin/file and validates user input/options.
47 | """
48 |
49 | if len(args) >= 1:
50 | for fn in args:
51 | if not os.path.isfile(fn):
52 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
53 | sys.stderr.write(emsg.format(fn))
54 | sys.stderr.write(__doc__)
55 | sys.exit(1)
56 |
57 | else: # Whatever ...
58 | sys.stderr.write(__doc__)
59 | sys.exit(1)
60 |
61 | return args
62 |
63 |
64 | def pad_line(line):
65 | """Helper function to pad line to 80 characters in case it is shorter"""
66 | size_of_line = len(line)
67 | if size_of_line < 80:
68 | padding = 80 - size_of_line + 1
69 | line = line.strip('\n') + ' ' * padding + '\n'
70 | return line[:81] # 80 + newline character
71 |
72 |
73 | def run(f_name_list):
74 | """
75 | Combine several PDB files into a multi-model ensemble file.
76 |
77 | This function is a generator.
78 |
79 | Parameters
80 | ----------
81 | f_name_list : list
82 | List of paths to PDB files.
83 |
84 | Yields
85 | ------
86 | str (line-by-line)
87 | The new ensemble PDB file.
88 | """
89 | _pad_line = pad_line
90 |
91 | # REMARK THIS ENTRY
92 | fmt_REMARK = "REMARK {:<67s}\n"
93 |
94 | # MODEL 1
95 | fmt_MODEL = "MODEL {:>5d}\n"
96 |
97 | for fileno, file_name in enumerate(f_name_list, start=1):
98 | fpath = os.path.basename(file_name)
99 | yield fmt_REMARK.format("MODEL {} FROM {}".format(fileno, fpath))
100 |
101 | conect = []
102 | records = ('ATOM', 'HETATM', 'TER')
103 | for fileno, file_name in enumerate(f_name_list, start=1):
104 |
105 | yield fmt_MODEL.format(fileno)
106 |
107 | with open(file_name, 'r') as fhandle:
108 |
109 | for line in fhandle:
110 | if line.startswith(records):
111 | yield _pad_line(line)
112 |
113 | # only store CONECT for first model
114 | elif fileno == 1 and line.startswith('CONECT'):
115 | conect.append(line)
116 |
117 | yield 'ENDMDL\n'
118 |
119 | # Write CONECT
120 | for line in conect:
121 | yield _pad_line(line)
122 |
123 | yield 'END\n'
124 |
125 |
126 | make_ensemble = run
127 |
128 |
129 | def main():
130 | # Check Input
131 | pdbfile_list = check_input(sys.argv[1:])
132 |
133 | # Do the job
134 | new_pdb = run(pdbfile_list)
135 |
136 | try:
137 | _buffer = []
138 | _buffer_size = 5000 # write N lines at a time
139 | for lineno, line in enumerate(new_pdb):
140 | if not (lineno % _buffer_size):
141 | sys.stdout.write(''.join(_buffer))
142 | _buffer = []
143 | _buffer.append(line)
144 |
145 | sys.stdout.write(''.join(_buffer))
146 | sys.stdout.flush()
147 | except IOError:
148 | # This is here to catch Broken Pipes
149 | # for example to use 'head' or 'tail' without
150 | # the error message showing up
151 | pass
152 |
153 | sys.exit(0)
154 |
155 |
156 | if __name__ == '__main__':
157 | main()
158 |
--------------------------------------------------------------------------------
/pdbtools/pdb_occ.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Modifies the occupancy column of a PDB file (default 1.0).
20 |
21 | Usage:
22 | python pdb_occ.py -
23 |
24 | Example:
25 | python pdb_occ.py -1.0 1CTF.pdb
26 |
27 | This program is part of the `pdb-tools` suite of utilities and should not be
28 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
29 | files using the terminal, and can be used sequentially, with one tool streaming
30 | data to another. They are based on old FORTRAN77 code that was taking too much
31 | effort to maintain and compile. RIP.
32 | """
33 |
34 | import os
35 | import sys
36 |
37 | __author__ = "Joao Rodrigues"
38 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
39 |
40 |
41 | def check_input(args):
42 | """Checks whether to read from stdin/file and validates user input/options.
43 | """
44 |
45 | # Defaults
46 | option = 1.0
47 | fh = sys.stdin # file handle
48 |
49 | if not len(args):
50 | # Reading from pipe with default option
51 | if sys.stdin.isatty():
52 | sys.stderr.write(__doc__)
53 | sys.exit(1)
54 |
55 | elif len(args) == 1:
56 | # One of two options: option & Pipe OR file & default option
57 | if args[0].startswith('-'):
58 | option = args[0][1:]
59 | if sys.stdin.isatty(): # ensure the PDB data is streamed in
60 | emsg = 'ERROR!! No data to process!\n'
61 | sys.stderr.write(emsg)
62 | sys.stderr.write(__doc__)
63 | sys.exit(1)
64 |
65 | else:
66 | if not os.path.isfile(args[0]):
67 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
68 | sys.stderr.write(emsg.format(args[0]))
69 | sys.stderr.write(__doc__)
70 | sys.exit(1)
71 |
72 | fh = open(args[0], 'r')
73 |
74 | elif len(args) == 2:
75 | # Two options: option & File
76 | if not args[0].startswith('-'):
77 | emsg = 'ERROR! First argument is not an option: \'{}\'\n'
78 | sys.stderr.write(emsg.format(args[0]))
79 | sys.stderr.write(__doc__)
80 | sys.exit(1)
81 |
82 | if not os.path.isfile(args[1]):
83 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
84 | sys.stderr.write(emsg.format(args[1]))
85 | sys.stderr.write(__doc__)
86 | sys.exit(1)
87 |
88 | option = args[0][1:]
89 | fh = open(args[1], 'r')
90 |
91 | else: # Whatever ...
92 | sys.stderr.write(__doc__)
93 | sys.exit(1)
94 |
95 | # Validate option
96 | try:
97 | option = float(option)
98 | except ValueError:
99 | emsg = 'ERROR!! You provided an invalid occupancy value: \'{}\'\n'
100 | sys.stderr.write(emsg.format(option))
101 | sys.exit(1)
102 |
103 | return (fh, option)
104 |
105 |
106 | def run(fhandle, occupancy):
107 | """
108 | Set the occupancy column in all ATOM/HETATM records to a given value.
109 |
110 | Non-ATOM/HETATM lines are give as are. This function is a generator.
111 |
112 | Parameters
113 | ----------
114 | fhandle : a line-by-line iterator of the original PDB file.
115 |
116 | occupancy : float
117 | The desired occupancy value
118 |
119 | Yields
120 | ------
121 | str (line-by-line)
122 | The modified (or not) PDB line.
123 | """
124 |
125 | records = ('ATOM', 'HETATM')
126 | occupancy = "{0:>6.2f}".format(occupancy)
127 | for line in fhandle:
128 | if line.startswith(records):
129 | yield line[:54] + occupancy + line[60:]
130 | else:
131 | yield line
132 |
133 |
134 | alter_occupancy = run
135 |
136 |
137 | def main():
138 | # Check Input
139 | pdbfh, occupancy = check_input(sys.argv[1:])
140 |
141 | # Do the job
142 | new_pdb = run(pdbfh, occupancy)
143 |
144 | # Output results
145 | try:
146 | _buffer = []
147 | _buffer_size = 5000 # write N lines at a time
148 | for lineno, line in enumerate(new_pdb):
149 | if not (lineno % _buffer_size):
150 | sys.stdout.write(''.join(_buffer))
151 | _buffer = []
152 | _buffer.append(line)
153 |
154 | sys.stdout.write(''.join(_buffer))
155 | sys.stdout.flush()
156 | except IOError:
157 | # This is here to catch Broken Pipes
158 | # for example to use 'head' or 'tail' without
159 | # the error message showing up
160 | pass
161 |
162 | # last line of the script
163 | # Close file handle even if it is sys.stdin, no problem here.
164 | pdbfh.close()
165 | sys.exit(0)
166 |
167 |
168 | if __name__ == '__main__':
169 | main()
170 |
--------------------------------------------------------------------------------
/pdbtools/pdb_segxchain.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Swaps the chain identifier by the segment identifier.
20 |
21 | If the segment identifier is longer than one character, the script will
22 | truncate it. Does not ensure unique chain IDs.
23 |
24 | Usage:
25 | python pdb_segxchain.py
26 |
27 | Example:
28 | python pdb_segxchain.py 1CTF.pdb
29 |
30 | This program is part of the `pdb-tools` suite of utilities and should not be
31 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
32 | files using the terminal, and can be used sequentially, with one tool streaming
33 | data to another. They are based on old FORTRAN77 code that was taking too much
34 | effort to maintain and compile. RIP.
35 | """
36 |
37 | import os
38 | import sys
39 |
40 | __author__ = "Joao Rodrigues"
41 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
42 |
43 |
44 | def check_input(args):
45 | """Checks whether to read from stdin/file and validates user input/options.
46 | """
47 |
48 | # Defaults
49 | fh = sys.stdin # file handle
50 |
51 | if not len(args):
52 | # Reading from pipe
53 | if sys.stdin.isatty():
54 | sys.stderr.write(__doc__)
55 | sys.exit(1)
56 |
57 | elif len(args) == 1:
58 | # Reading from file
59 | if not os.path.isfile(args[0]):
60 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
61 | sys.stderr.write(emsg.format(args[0]))
62 | sys.stderr.write(__doc__)
63 | sys.exit(1)
64 |
65 | fh = open(args[0], 'r')
66 |
67 | else: # Whatever ...
68 | emsg = 'ERROR!! Script takes 1 argument, not \'{}\'\n'
69 | sys.stderr.write(emsg.format(len(args)))
70 | sys.stderr.write(__doc__)
71 | sys.exit(1)
72 |
73 | return fh
74 |
75 |
76 | def pad_line(line):
77 | """Helper function to pad line to 80 characters in case it is shorter"""
78 | size_of_line = len(line)
79 | if size_of_line < 80:
80 | padding = 80 - size_of_line + 1
81 | line = line.strip('\n') + ' ' * padding + '\n'
82 | return line[:81] # 80 + newline character
83 |
84 |
85 | def run(fhandle):
86 | """
87 | Replace the chain identifier with the contents of the segment identifier.
88 |
89 | Truncates the segment identifier to its first character.
90 |
91 | This function is a generator.
92 |
93 | Parameters
94 | ----------
95 | fhandle : a line-by-line iterator of the original PDB file.
96 |
97 | Yields
98 | ------
99 | str (line-by-line)
100 | The modified (or not) PDB line.
101 | """
102 |
103 | prev_line = None
104 |
105 | _pad_line = pad_line
106 | records = ('ATOM', 'HETATM', 'ANISOU')
107 | for line in fhandle:
108 | if line.startswith(records):
109 | line = _pad_line(line)
110 | # trick to pick first non-empty character of segid OR empty space
111 | # [0] on '' gives error, [:1] returns '', with ljust(1) == ' '
112 | segid = line[72:76].strip()[:1]
113 | yield line[:21] + segid.ljust(1) + line[22:]
114 | prev_line = line
115 | elif line.startswith('TER'): # use previous chain ID
116 | line = _pad_line(line)
117 | segid = prev_line[72:76].strip()[:1]
118 | yield line[:21] + segid.ljust(1) + line[22:]
119 | else:
120 | yield line
121 |
122 |
123 | place_seg_on_chain = run
124 |
125 |
126 | def main():
127 | # Check Input
128 | pdbfh = check_input(sys.argv[1:])
129 |
130 | # Do the job
131 | new_pdb = run(pdbfh)
132 |
133 | try:
134 | _buffer = []
135 | _buffer_size = 5000 # write N lines at a time
136 | for lineno, line in enumerate(new_pdb):
137 | if not (lineno % _buffer_size):
138 | sys.stdout.write(''.join(_buffer))
139 | _buffer = []
140 | _buffer.append(line)
141 |
142 | sys.stdout.write(''.join(_buffer))
143 | sys.stdout.flush()
144 | except IOError:
145 | # This is here to catch Broken Pipes
146 | # for example to use 'head' or 'tail' without
147 | # the error message showing up
148 | pass
149 |
150 | # last line of the script
151 | # We can close it even if it is sys.stdin
152 | pdbfh.close()
153 | sys.exit(0)
154 |
155 |
156 | if __name__ == '__main__':
157 | main()
158 |
--------------------------------------------------------------------------------
/pdbtools/pdb_selhetatm.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Selects all HETATM records in the PDB file.
20 |
21 | Usage:
22 | python pdb_selhetatm.py
23 |
24 | Example:
25 | python pdb_selhetatm.py 1CTF.pdb
26 |
27 | This program is part of the `pdb-tools` suite of utilities and should not be
28 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
29 | files using the terminal, and can be used sequentially, with one tool streaming
30 | data to another. They are based on old FORTRAN77 code that was taking too much
31 | effort to maintain and compile. RIP.
32 | """
33 |
34 | import os
35 | import sys
36 |
37 | __author__ = "Joao Rodrigues"
38 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
39 |
40 |
41 | def check_input(args):
42 | """Checks whether to read from stdin/file and validates user input/options.
43 | """
44 |
45 | # Defaults
46 | fh = sys.stdin # file handle
47 |
48 | if not len(args):
49 | # Reading from pipe with default option
50 | if sys.stdin.isatty():
51 | sys.stderr.write(__doc__)
52 | sys.exit(1)
53 |
54 | elif len(args) == 1:
55 | if not os.path.isfile(args[0]):
56 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
57 | sys.stderr.write(emsg.format(args[0]))
58 | sys.stderr.write(__doc__)
59 | sys.exit(1)
60 |
61 | fh = open(args[0], 'r')
62 |
63 | else: # Whatever ...
64 | emsg = 'ERROR!! Script takes 1 argument, not \'{}\'\n'
65 | sys.stderr.write(emsg.format(len(args)))
66 | sys.stderr.write(__doc__)
67 | sys.exit(1)
68 |
69 | return fh
70 |
71 |
72 | def run(fhandle):
73 | """
74 | Select all HETATM and associated records from the PDB file.
75 |
76 | This function is a generator.
77 |
78 | Parameters
79 | ----------
80 | fhandle : a line-by-line iterator of the original PDB file.
81 |
82 | Yields
83 | ------
84 | str (line-by-line)
85 | The HETATM lines.
86 | """
87 | # CONECT 1179 746 1184 1195 1203
88 | char_ranges = (slice(6, 11), slice(11, 16),
89 | slice(16, 21), slice(21, 26), slice(26, 31))
90 |
91 | het_serials = set()
92 | for line in fhandle:
93 | if line.startswith('HETATM'):
94 | het_serials.add(line[6:11])
95 | yield line
96 | elif line.startswith('ANISOU'):
97 | if line[6:11] in het_serials:
98 | yield line
99 | elif line.startswith('CONECT'):
100 | if any(line[cr] in het_serials for cr in char_ranges):
101 | yield line
102 |
103 |
104 | select_hetatm = run
105 |
106 |
107 | def main():
108 | # Check Input
109 | pdbfh = check_input(sys.argv[1:])
110 |
111 | # Do the job
112 | new_pdb = run(pdbfh)
113 |
114 | try:
115 | _buffer = []
116 | _buffer_size = 5000 # write N lines at a time
117 | for lineno, line in enumerate(new_pdb):
118 | if not (lineno % _buffer_size):
119 | sys.stdout.write(''.join(_buffer))
120 | _buffer = []
121 | _buffer.append(line)
122 |
123 | sys.stdout.write(''.join(_buffer))
124 | sys.stdout.flush()
125 | except IOError:
126 | # This is here to catch Broken Pipes
127 | # for example to use 'head' or 'tail' without
128 | # the error message showing up
129 | pass
130 |
131 | # last line of the script
132 | # We can close it even if it is sys.stdin
133 | pdbfh.close()
134 | sys.exit(0)
135 |
136 |
137 | if __name__ == '__main__':
138 | main()
139 |
--------------------------------------------------------------------------------
/pdbtools/pdb_splitchain.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Splits a PDB file into several, each containing one chain.
20 |
21 | Usage:
22 | python pdb_splitchain.py
23 |
24 | Example:
25 | python pdb_splitchain.py 1CTF.pdb
26 |
27 | This program is part of the `pdb-tools` suite of utilities and should not be
28 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
29 | files using the terminal, and can be used sequentially, with one tool streaming
30 | data to another. They are based on old FORTRAN77 code that was taking too much
31 | effort to maintain and compile. RIP.
32 | """
33 |
34 | import os
35 | import sys
36 |
37 |
38 | __author__ = "Joao Rodrigues"
39 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
40 |
41 | USAGE = __doc__.format(__author__, __email__)
42 |
43 |
44 | def check_input(args):
45 | """Checks whether to read from stdin/file and validates user input/options.
46 | """
47 |
48 | # Defaults
49 | fh = sys.stdin # file handle
50 |
51 | if not len(args):
52 | # Reading from pipe with default option
53 | if sys.stdin.isatty():
54 | sys.stderr.write(__doc__)
55 | sys.exit(1)
56 |
57 | elif len(args) == 1:
58 | if not os.path.isfile(args[0]):
59 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
60 | sys.stderr.write(emsg.format(args[0]))
61 | sys.stderr.write(__doc__)
62 | sys.exit(1)
63 |
64 | fh = open(args[0], 'r')
65 |
66 | else: # Whatever ...
67 | emsg = 'ERROR!! Script takes 1 argument, not \'{}\'\n'
68 | sys.stderr.write(emsg.format(len(args)))
69 | sys.stderr.write(__doc__)
70 | sys.exit(1)
71 |
72 | return fh
73 |
74 |
75 | def run(fhandle, outname=None):
76 | """
77 | Split the PDB into its different chains.
78 |
79 | Writes a new file to the disk for each chain. Non-record lines are
80 | ignored.
81 |
82 | Parameters
83 | ----------
84 | fhandle : an iterable giving the PDB file line-by-line
85 |
86 | outname : str
87 | The base name of the output files. If None is given, tries to
88 | extract a name from the `.name` attribute of `fhandler`. If
89 | `fhandler` has no attribute name, assigns `splitchains`.
90 | """
91 | _defname = 'splitchains'
92 | if outname is None:
93 | try:
94 | fn = fhandle.name
95 | outname = fn[:-4] if fn != '' else _defname
96 | except AttributeError:
97 | outname = _defname
98 |
99 | basename = os.path.basename(outname)
100 |
101 | chain_data = {} # {chain_id: lines}
102 |
103 | prev_chain = None
104 | records = ('ATOM', 'HETATM', 'ANISOU', 'TER')
105 | for line in fhandle:
106 | if line.startswith(records):
107 | line_chain = line[21]
108 | if line_chain != prev_chain:
109 | if line_chain not in chain_data:
110 | chain_data[line_chain] = []
111 | prev_chain = line_chain
112 | chain_data[line_chain].append(line)
113 |
114 | for chain_id in sorted(chain_data.keys()):
115 | lines = chain_data[chain_id]
116 | with open(basename + '_' + chain_id + '.pdb', 'w') as fh:
117 | fh.write(''.join(lines))
118 |
119 |
120 | split_chain = run
121 |
122 |
123 | def main():
124 | # Check Input
125 | pdbfh = check_input(sys.argv[1:])
126 |
127 | # Do the job
128 | run(pdbfh)
129 |
130 | # last line of the script
131 | # We can close it even if it is sys.stdin
132 | pdbfh.close()
133 | sys.exit(0)
134 |
135 |
136 | if __name__ == '__main__':
137 | main()
138 |
--------------------------------------------------------------------------------
/pdbtools/pdb_splitmodel.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Splits a PDB file into several, each containing one MODEL.
20 |
21 | Usage:
22 | python pdb_splitmodel.py
23 |
24 | Example:
25 | python pdb_splitmodel.py 1CTF.pdb
26 |
27 | This program is part of the `pdb-tools` suite of utilities and should not be
28 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
29 | files using the terminal, and can be used sequentially, with one tool streaming
30 | data to another. They are based on old FORTRAN77 code that was taking too much
31 | effort to maintain and compile. RIP.
32 | """
33 |
34 | import os
35 | import sys
36 |
37 |
38 | __author__ = "Joao Rodrigues"
39 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
40 |
41 | USAGE = __doc__.format(__author__, __email__)
42 |
43 |
44 | def check_input(args):
45 | """Checks whether to read from stdin/file and validates user input/options.
46 | """
47 |
48 | # Defaults
49 | fh = sys.stdin # file handle
50 |
51 | if not len(args):
52 | # Reading from pipe with default option
53 | if sys.stdin.isatty():
54 | sys.stderr.write(__doc__)
55 | sys.exit(1)
56 |
57 | elif len(args) == 1:
58 | if not os.path.isfile(args[0]):
59 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
60 | sys.stderr.write(emsg.format(args[0]))
61 | sys.stderr.write(__doc__)
62 | sys.exit(1)
63 |
64 | fh = open(args[0], 'r')
65 |
66 | else: # Whatever ...
67 | emsg = 'ERROR!! Script takes 1 argument, not \'{}\'\n'
68 | sys.stderr.write(emsg.format(len(args)))
69 | sys.stderr.write(__doc__)
70 | sys.exit(1)
71 |
72 | return fh
73 |
74 |
75 | def run(fhandle, outname=None):
76 | """
77 | Split PDB into MODELS.
78 |
79 | Each MODELS is saved to a different file. Non-records lines are
80 | ignored.
81 |
82 | Parameters
83 | ----------
84 | fhandle : a line-by-line iterator of the original PDB file.
85 |
86 | outname : str
87 | The base name of the output files. If None is given, tries to
88 | extract a name from the `.name` attribute of `fhandler`. If
89 | `fhandler` has no attribute name, assigns `splitmodels`.
90 | """
91 | _defname = 'splitmodels'
92 | if outname is None:
93 | try:
94 | fn = fhandle.name
95 | outname = fn[:-4] if fn != '' else _defname
96 | except AttributeError:
97 | outname = _defname
98 |
99 | basename = os.path.basename(outname)
100 |
101 | model_lines = []
102 | records = ('ATOM', 'HETATM', 'ANISOU', 'TER')
103 | for line in fhandle:
104 | if line.startswith('MODEL'):
105 | model_no = line[10:14].strip()
106 | fh = open(basename + '_' + model_no + '.pdb', 'w')
107 | model_lines = []
108 |
109 | elif line.startswith('ENDMDL'):
110 | fh.write(''.join(model_lines))
111 | fh.close()
112 |
113 | elif line.startswith(records):
114 | model_lines.append(line)
115 |
116 |
117 | split_model = run
118 |
119 |
120 | def main():
121 | # Check Input
122 | pdbfh = check_input(sys.argv[1:])
123 |
124 | # Do the job
125 | run(pdbfh)
126 |
127 | # last line of the script
128 | # We can close it even if it is sys.stdin
129 | pdbfh.close()
130 | sys.exit(0)
131 |
132 |
133 | if __name__ == '__main__':
134 | main()
135 |
--------------------------------------------------------------------------------
/pdbtools/pdb_splitseg.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Splits a PDB file into several, each containing one segment.
20 |
21 | Usage:
22 | python pdb_splitseg.py
23 |
24 | Example:
25 | python pdb_splitseg.py 1CTF.pdb
26 |
27 | This program is part of the `pdb-tools` suite of utilities and should not be
28 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
29 | files using the terminal, and can be used sequentially, with one tool streaming
30 | data to another. They are based on old FORTRAN77 code that was taking too much
31 | effort to maintain and compile. RIP.
32 | """
33 |
34 | import os
35 | import sys
36 |
37 |
38 | __author__ = "Joao Rodrigues"
39 | __email__ = "j.p.g.l.m.rodrigues@gmail.com"
40 |
41 | USAGE = __doc__.format(__author__, __email__)
42 |
43 |
44 | def check_input(args):
45 | """Checks whether to read from stdin/file and validates user input/options.
46 | """
47 |
48 | # Defaults
49 | fh = sys.stdin # file handle
50 |
51 | if not len(args):
52 | # Reading from pipe with default option
53 | if sys.stdin.isatty():
54 | sys.stderr.write(__doc__)
55 | sys.exit(1)
56 |
57 | elif len(args) == 1:
58 | if not os.path.isfile(args[0]):
59 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
60 | sys.stderr.write(emsg.format(args[0]))
61 | sys.stderr.write(__doc__)
62 | sys.exit(1)
63 |
64 | fh = open(args[0], 'r')
65 |
66 | else: # Whatever ...
67 | emsg = 'ERROR!! Script takes 1 argument, not \'{}\'\n'
68 | sys.stderr.write(emsg.format(len(args)))
69 | sys.stderr.write(__doc__)
70 | sys.exit(1)
71 |
72 | return fh
73 |
74 |
75 | def run(fhandle, outname=None):
76 | """
77 | Split PDB into segments.
78 |
79 | Each segment is saved to a different file. Non-records lines are
80 | ignored.
81 |
82 | Parameters
83 | ----------
84 | fhandle : a line-by-line iterator of the original PDB file.
85 |
86 | outname : str
87 | The base name of the output files. If None is given, tries to
88 | extract a name from the `.name` attribute of `fhandler`. If
89 | `fhandler` has no attribute name, assigns `splitsegs`.
90 | """
91 | _defname = 'splitsegs'
92 | if outname is None:
93 | try:
94 | fn = fhandle.name
95 | outname = fn[:-4] if fn != '' else _defname
96 | except AttributeError:
97 | outname = _defname
98 |
99 | basename = os.path.basename(outname)
100 |
101 | segment_data = {} # {segment_id: lines}
102 |
103 | prev_segment = None
104 | records = ('ATOM', 'HETATM', 'ANISOU', 'TER')
105 | for line in fhandle:
106 | if line.startswith(records):
107 | line_segment = line[72:76].strip()
108 | if line_segment != prev_segment:
109 | if line_segment not in segment_data:
110 | segment_data[line_segment] = []
111 | prev_segment = line_segment
112 | segment_data[line_segment].append(line)
113 |
114 | for segment_id in sorted(segment_data.keys()):
115 | if not segment_id:
116 | continue # skip empty segment
117 |
118 | lines = segment_data[segment_id]
119 | with open(basename + '_' + segment_id + '.pdb', 'w') as fh:
120 | fh.write(''.join(lines))
121 |
122 |
123 | split_segment = run
124 |
125 |
126 | def main():
127 | # Check Input
128 | pdbfh = check_input(sys.argv[1:])
129 |
130 | # Do the job
131 | run(pdbfh)
132 |
133 | # last line of the script
134 | # We can close it even if it is sys.stdin
135 | pdbfh.close()
136 | sys.exit(0)
137 |
138 |
139 | if __name__ == '__main__':
140 | main()
141 |
--------------------------------------------------------------------------------
/pdbtools/pdb_uniqname.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2020 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Renames atoms sequentially (C1, C2, O1, ...) for each HETATM residue.
20 |
21 | Relies on an element column being present (see pdb_element).
22 |
23 | Usage:
24 | python pdb_uniqname.py
25 |
26 | Example:
27 | python pdb_uniqname.py 1CTF.pdb
28 |
29 | This program is part of the `pdb-tools` suite of utilities and should not be
30 | distributed isolatedly. The `pdb-tools` were created to quickly manipulate PDB
31 | files using the terminal, and can be used sequentially, with one tool streaming
32 | data to another. They are based on old FORTRAN77 code that was taking too much
33 | effort to maintain and compile. RIP.
34 | """
35 |
36 | import collections
37 | import os
38 | import sys
39 |
40 | __author__ = ["Joao Rodrigues"]
41 | __email__ = ["j.p.g.l.m.rodrigues@gmail.com"]
42 |
43 |
44 | def check_input(args):
45 | """Checks whether to read from stdin/file.
46 | """
47 |
48 | # Defaults
49 | fh = sys.stdin # file handle
50 |
51 | if not len(args):
52 | # Reading from pipe
53 | if sys.stdin.isatty():
54 | sys.stderr.write(__doc__)
55 | sys.exit(1)
56 |
57 | elif len(args) == 1:
58 | # Input File
59 | if not os.path.isfile(args[0]):
60 | emsg = 'ERROR!! File not found or not readable: \'{}\'\n'
61 | sys.stderr.write(emsg.format(args[0]))
62 | sys.stderr.write(__doc__)
63 | sys.exit(1)
64 |
65 | fh = open(args[0], 'r')
66 |
67 | else: # Whatever ...
68 | sys.stderr.write(__doc__)
69 | sys.exit(1)
70 |
71 | return fh
72 |
73 |
74 | def run(fhandle):
75 | """
76 | Rename HETATM atoms on each residue based on their element.
77 |
78 | This function is a generator.
79 |
80 | Parameters
81 | ----------
82 | fhandle : a line-by-line iterator of the original PDB file.
83 |
84 | Yields
85 | ------
86 | str (line-by-line)
87 | The modified (or not) PDB line.
88 | """
89 | prev_res = None
90 | for line_idx, line in enumerate(fhandle):
91 | if line.startswith('HETATM'):
92 |
93 | element = line[76:78].strip()
94 |
95 | if not element:
96 | emsg = 'ERROR!! No element found in line {}'.format(line_idx)
97 | sys.stderr.write(emsg)
98 | sys.exit(1)
99 |
100 | resuid = line[17:27]
101 |
102 | if prev_res != resuid:
103 | prev_res = resuid
104 | element_idx = collections.defaultdict(lambda: 1) # i.e. a counter
105 |
106 | spacer = ' ' if len(element) == 1 else ''
107 | name = (spacer + element + str(element_idx[element])).ljust(4)
108 | line = line[:12] + name + line[16:]
109 |
110 | element_idx[element] += 1
111 |
112 | yield line
113 |
114 |
115 | rename_atoms = run
116 |
117 |
118 | def main():
119 | # Check Input
120 | pdbfh = check_input(sys.argv[1:])
121 |
122 | # Do the job
123 | new_pdb = run(pdbfh)
124 |
125 | try:
126 | _buffer = []
127 | _buffer_size = 5000 # write N lines at a time
128 | for lineno, line in enumerate(new_pdb):
129 | if not (lineno % _buffer_size):
130 | sys.stdout.write(''.join(_buffer))
131 | _buffer = []
132 | _buffer.append(line)
133 |
134 | sys.stdout.write(''.join(_buffer))
135 | sys.stdout.flush()
136 | except IOError:
137 | # This is here to catch Broken Pipes
138 | # for example to use 'head' or 'tail' without
139 | # the error message showing up
140 | pass
141 |
142 | # last line of the script
143 | # We can close it even if it is sys.stdin
144 | pdbfh.close()
145 | sys.exit(0)
146 |
147 |
148 | if __name__ == '__main__':
149 | main()
150 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | exclude =
3 | .git,
4 | .github,
5 | docs/,
6 | pdbtools/__pycache__,
7 | tests/,
8 | setup.py
9 |
10 | # Ignore:
11 | # E501: long lines
12 | # E731: lambda warnings
13 | # W503/W504: line breaks before/after operators
14 | ignore = E501,E731,W503,W504
15 |
16 | # Enable statistics at the end
17 | statistics = True
18 |
19 | [coverage:run]
20 | branch = True
21 | source = pdbtools/
22 |
23 | [coverage:report]
24 | exclude_lines =
25 | if self.debug:
26 | pragma: no cover
27 | raise NotImplementedError
28 | if __name__ == .__main__.:
29 | ignore_errors = True
30 | omit =
31 | tests/*
32 | docs/*
33 |
34 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | """A setuptools based setup module.
2 |
3 | See:
4 | https://packaging.python.org/en/latest/distributing.html
5 | https://github.com/pypa/sampleproject
6 | """
7 |
8 | # Always prefer setuptools over distutils
9 | from setuptools import setup, find_packages
10 | from os import listdir, path
11 | # io.open is needed for projects that support Python 2.7
12 | # It ensures open() defaults to text mode with universal newlines,
13 | # and accepts an argument to specify the text encoding
14 | # Python 3 only projects can skip this import
15 | from io import open
16 |
17 | here = path.abspath(path.dirname(__file__))
18 |
19 | # Get the long description from the README file
20 | with open(path.join(here, 'README.md'), encoding='utf-8') as f:
21 | long_description = f.read()
22 |
23 | # Collect names of bin/*py scripts
24 | # e.g. 'pdb_intersect=bin.pdb_intersect:main',
25 | binfiles = listdir(path.join(here, 'pdbtools'))
26 | bin_py = [f[:-3] + '=pdbtools.' + f[:-3] + ':main' for f in binfiles
27 | if f.endswith('.py')]
28 |
29 | setup(
30 | name='pdb-tools', # Required
31 | version='2.5.1', # Required
32 | description='A swiss army knife for PDB files.', # Optional
33 | long_description=long_description, # Optional
34 | long_description_content_type='text/markdown', # Optional (see note above)
35 | url='http://bonvinlab.org/pdb-tools', # Optional
36 | author='Joao Rodrigues', # Optional
37 | author_email='j.p.g.l.m.rodrigues@gmail.com', # Optional
38 | license='Apache Software License, version 2',
39 | classifiers=[ # Optional
40 | # How mature is this project? Common values are
41 | # 3 - Alpha
42 | # 4 - Beta
43 | # 5 - Production/Stable
44 | 'Development Status :: 5 - Production/Stable',
45 | #'Development Status :: 4 - Beta',
46 |
47 | # Indicate who your project is intended for
48 | 'Intended Audience :: Science/Research',
49 | 'Topic :: Scientific/Engineering',
50 | 'Topic :: Scientific/Engineering :: Chemistry',
51 | 'Topic :: Scientific/Engineering :: Bio-Informatics',
52 |
53 |
54 | # Pick your license as you wish
55 | 'License :: OSI Approved :: Apache Software License',
56 |
57 | # Specify the Python versions you support here. In particular, ensure
58 | # that you indicate whether you support Python 2, Python 3 or both.
59 | 'Programming Language :: Python :: 2',
60 | 'Programming Language :: Python :: 2.7',
61 | 'Programming Language :: Python :: 3',
62 | 'Programming Language :: Python :: 3.6',
63 | 'Programming Language :: Python :: 3.7',
64 | ],
65 |
66 | keywords='bioinformatics protein structural-biology pdb', # Optional
67 |
68 | # You can just specify package directories manually here if your project is
69 | # simple. Or you can use find_packages().
70 | #
71 | # Alternatively, if you just want to distribute a single Python file, use
72 | # the `py_modules` argument instead as follows, which will expect a file
73 | # called `my_module.py` to exist:
74 | #
75 | # py_modules=["my_module"],
76 | #
77 | packages=find_packages(), # Required
78 |
79 | # To provide executable scripts, use entry points in preference to the
80 | # "scripts" keyword. Entry points provide cross-platform support and allow
81 | # `pip` to create the appropriate form of executable for the target
82 | # platform.
83 | #
84 | # For example, the following would provide a command called `sample` which
85 | # executes the function `main` from this package when invoked:
86 | entry_points={ # Optional
87 | 'console_scripts': bin_py,
88 | },
89 |
90 | # scripts=[path.join('bin', f) for f in listdir(path.join(here, 'bin'))],
91 |
92 | # List additional URLs that are relevant to your project as a dict.
93 | #
94 | # This field corresponds to the "Project-URL" metadata fields:
95 | # https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use
96 | #
97 | # Examples listed include a pattern for specifying where the package tracks
98 | # issues, where the source is hosted, where to say thanks to the package
99 | # maintainers, and where to support the project financially. The key is
100 | # what's used to render the link text on PyPI.
101 | project_urls={ # Optional
102 | 'Bug Reports': 'https://github.com/haddocking/pdb-tools/issues',
103 | 'Source': 'https://github.com/haddocking/pdb-tools',
104 | },
105 |
106 | # Test Suite
107 | test_suite='tests.test_all',
108 | )
109 |
--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------
1 | Writing unit tests for `pdb-tools`
2 | ================================================
3 | If you want to write a new `pdb-tools` script, make sure to provide a test case
4 | that verifies the script is running as it should.
5 |
6 |
7 | How to create a unit test
8 | -------------------------
9 |
10 | 1. Create one file in `tests\` starting with `test_` and named after the script,
11 | e.g. `test_pdb_b.py`.
12 |
13 | 2. If necessary, add an input file to `data\`.
14 |
15 | 3. Place the expected result, as the tool would write it, in `output` and name
16 | it after the tool and ending with `.out`, e.g. `pdb_b.out`
17 |
18 | 4. Write the unit test in `test_pdb_b.py` and compare the output with `pdb_b.out`.
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for all pdb-tools scripts
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from .config import test_dir
27 |
28 |
29 | def test_all():
30 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
31 | sys.path.insert(0, mpath) # so we load dev files before any installation
32 |
33 | loader = unittest.defaultTestLoader
34 |
35 | tpath = os.path.join(mpath, 'tests')
36 | suite = loader.discover(tpath)
37 |
38 | return suite
39 |
--------------------------------------------------------------------------------
/tests/config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Configuration settings for the Tests.
20 | """
21 |
22 | import os
23 |
24 | test_dir = os.path.dirname(os.path.abspath(__file__))
25 | data_dir = os.path.join(test_dir, 'data')
26 |
--------------------------------------------------------------------------------
/tests/data/anisou.pdb:
--------------------------------------------------------------------------------
1 | ATOM 1 N ALA A 31 -12.806 6.423 23.735 1.00 70.81 N
2 | ANISOU 1 N ALA A 31 7836 7867 11203 2151 -675 -66 N
3 | ATOM 2 CA ALA A 31 -13.433 7.788 23.746 1.00 65.66 C
4 | ANISOU 2 CA ALA A 31 7296 7485 10167 1829 -642 -81 C
5 | ATOM 3 C ALA A 31 -12.448 8.891 24.124 1.00 63.44 C
6 | ANISOU 3 C ALA A 31 6818 7632 9654 1744 -656 20 C
7 | ATOM 4 O ALA A 31 -11.549 8.680 24.937 1.00 65.52 O
8 | ANISOU 4 O ALA A 31 6891 8010 9994 1853 -759 238 O
9 | ATOM 5 CB ALA A 31 -14.628 7.834 24.691 1.00 66.76 C
10 | ANISOU 5 CB ALA A 31 7636 7487 10242 1630 -748 143 C
11 | ATOM 6 N THR A 32 -12.659 10.075 23.550 1.00 58.59 N
12 | ANISOU 6 N THR A 32 6249 7244 8768 1532 -569 -122 N
13 | ATOM 7 CA THR A 32 -11.806 11.232 23.788 1.00 55.42 C
14 | ANISOU 7 CA THR A 32 5678 7217 8161 1398 -585 -53 C
15 | ATOM 8 C THR A 32 -12.119 11.857 25.148 1.00 52.59 C
16 | ANISOU 8 C THR A 32 5384 6947 7651 1206 -743 183 C
17 | ATOM 9 O THR A 32 -13.166 11.585 25.770 1.00 49.84 O
18 | ANISOU 9 O THR A 32 5232 6412 7293 1142 -803 278 O
19 | ATOM 10 CB THR A 32 -11.965 12.321 22.696 1.00 51.87 C
20 | ANISOU 10 CB THR A 32 5260 6948 7500 1220 -449 -254 C
21 | ATOM 11 OG1 THR A 32 -13.294 12.844 22.720 1.00 48.11 O
22 | ANISOU 11 OG1 THR A 32 5047 6344 6890 1015 -452 -282 O
23 | ATOM 12 CG2 THR A 32 -11.660 11.781 21.303 1.00 54.23 C
24 | ANISOU 12 CG2 THR A 32 5483 7248 7875 1398 -280 -508 C
25 |
--------------------------------------------------------------------------------
/tests/data/anisou_altloc.pdb:
--------------------------------------------------------------------------------
1 | ATOM 1 N AALA A 31 -12.806 6.423 23.735 0.40 70.81 N
2 | ANISOU 1 N AALA A 31 7836 7867 11203 2151 -675 -66 N
3 | ATOM 1 N BALA A 31 -12.806 6.423 23.735 0.60 70.81 N
4 | ANISOU 1 N BALA A 31 7836 7867 11203 2151 -675 -66 N
5 | ATOM 2 CA AALA A 31 -13.433 7.788 23.746 0.60 65.66 C
6 | ANISOU 2 CA AALA A 31 7296 7485 10167 1829 -642 -81 C
7 | ATOM 2 CA BALA A 31 -13.433 7.788 23.746 0.40 65.66 C
8 | ANISOU 2 CA BALA A 31 7296 7485 10167 1829 -642 -81 C
9 | ATOM 3 C ALA A 31 -12.448 8.891 24.124 1.00 63.44 C
10 | ANISOU 3 C ALA A 31 6818 7632 9654 1744 -656 20 C
11 | ATOM 4 O ALA A 31 -11.549 8.680 24.937 1.00 65.52 O
12 | ANISOU 4 O ALA A 31 6891 8010 9994 1853 -759 238 O
13 | ATOM 5 CB ALA A 31 -14.628 7.834 24.691 1.00 66.76 C
14 | ANISOU 5 CB ALA A 31 7636 7487 10242 1630 -748 143 C
15 | ATOM 6 N THR A 32 -12.659 10.075 23.550 1.00 58.59 N
16 | ANISOU 6 N THR A 32 6249 7244 8768 1532 -569 -122 N
17 | ATOM 7 CA THR A 32 -11.806 11.232 23.788 1.00 55.42 C
18 | ANISOU 7 CA THR A 32 5678 7217 8161 1398 -585 -53 C
19 | ATOM 8 C THR A 32 -12.119 11.857 25.148 1.00 52.59 C
20 | ANISOU 8 C THR A 32 5384 6947 7651 1206 -743 183 C
21 | ATOM 9 O THR A 32 -13.166 11.585 25.770 1.00 49.84 O
22 | ANISOU 9 O THR A 32 5232 6412 7293 1142 -803 278 O
23 | ATOM 10 CB ATHR A 32 -11.965 12.321 22.696 0.45 51.87 C
24 | ANISOU 10 CB ATHR A 32 5260 6948 7500 1220 -449 -254 C
25 | ATOM 10 CB BTHR A 32 -11.965 12.321 22.696 0.55 51.87 C
26 | ANISOU 10 CB BTHR A 32 5260 6948 7500 1220 -449 -254 C
27 | ATOM 11 OG1 THR A 32 -13.294 12.844 22.720 1.00 48.11 O
28 | ANISOU 11 OG1 THR A 32 5047 6344 6890 1015 -452 -282 O
29 | ATOM 12 CG2 THR A 32 -11.660 11.781 21.303 1.00 54.23 C
30 | ANISOU 12 CG2 THR A 32 5483 7248 7875 1398 -280 -508 C
31 |
--------------------------------------------------------------------------------
/tests/data/anisou_missing.pdb:
--------------------------------------------------------------------------------
1 | ATOM 1 N ALA A 31 -12.806 6.423 23.735 1.00 70.81 N
2 | ANISOU 1 N ALA A 31 7836 7867 11203 2151 -675 -66 N
3 | ATOM 2 CA ALA A 31 -13.433 7.788 23.746 1.00 65.66 C
4 | ANISOU 2 CA ALA A 31 7296 7485 10167 1829 -642 -81 C
5 | ATOM 3 C ALA A 31 -12.448 8.891 24.124 1.00 63.44 C
6 | ATOM 4 O ALA A 31 -11.549 8.680 24.937 1.00 65.52 O
7 | ANISOU 4 O ALA A 31 6891 8010 9994 1853 -759 238 O
8 |
--------------------------------------------------------------------------------
/tests/data/dummy_altloc.pdb:
--------------------------------------------------------------------------------
1 | REMARK ORIGINAL DATA FROM PDB ID 3U7T
2 | ATOM 308 N ASER A 22 -14.282 -3.023 -15.571 0.53 4.57 N
3 | ANISOU 308 N ASER A 22 594 482 661 7 -42 32 N
4 | ATOM 309 CA ASER A 22 -13.829 -3.816 -14.436 0.53 5.30 C
5 | ANISOU 309 CA ASER A 22 695 586 731 6 -19 117 C
6 | ATOM 310 C ASER A 22 -12.365 -3.540 -14.113 0.53 4.77 C
7 | ANISOU 310 C ASER A 22 582 560 669 34 31 98 C
8 | ATOM 311 O ASER A 22 -11.531 -3.326 -15.003 0.53 5.44 O
9 | ANISOU 311 O ASER A 22 602 786 679 59 46 138 O
10 | ATOM 318 N BPRO A 22 -14.305 -3.040 -15.563 0.47 5.18 N
11 | ANISOU 318 N BPRO A 22 713 566 690 -20 -45 8 N
12 | ATOM 319 CA BPRO A 22 -13.801 -3.851 -14.452 0.47 5.29 C
13 | ANISOU 319 CA BPRO A 22 701 599 711 -19 -20 59 C
14 | ATOM 320 C BPRO A 22 -12.344 -3.583 -14.119 0.47 4.84 C
15 | ANISOU 320 C BPRO A 22 590 585 664 23 27 64 C
16 | ATOM 321 O BPRO A 22 -11.499 -3.414 -15.007 0.47 5.37 O
17 | ANISOU 321 O BPRO A 22 603 762 675 65 45 100 O
18 | ATOM 332 N GLU A 23 -12.046 -3.614 -12.827 1.00 4.76 N
19 | ANISOU 332 N GLU A 23 558 607 642 79 39 69 N
20 | ATOM 333 CA GLU A 23 -10.679 -3.437 -12.387 1.00 4.89 C
21 | ANISOU 333 CA GLU A 23 576 620 663 31 42 45 C
22 | ATOM 334 C GLU A 23 -9.718 -4.412 -13.075 1.00 4.66 C
23 | ANISOU 334 C GLU A 23 509 598 663 8 21 49 C
24 | ATOM 335 O GLU A 23 -8.593 -4.031 -13.405 1.00 5.42 O
25 | ANISOU 335 O GLU A 23 569 765 725 -40 72 -21 O
26 | ATOM 347 N ALA A 24 -10.141 -5.661 -13.285 1.00 4.86 N
27 | ANISOU 347 N ALA A 24 551 614 679 31 34 65 N
28 | ATOM 348 CA ALA A 24 -9.236 -6.655 -13.860 1.00 5.12 C
29 | ANISOU 348 CA ALA A 24 659 547 738 54 48 91 C
30 | ATOM 349 C ALA A 24 -8.797 -6.289 -15.284 1.00 4.57 C
31 | ANISOU 349 C ALA A 24 574 482 682 38 -18 30 C
32 | ATOM 350 O ALA A 24 -7.651 -6.544 -15.670 1.00 5.51 O
33 | ANISOU 350 O ALA A 24 643 726 724 126 19 56 O
34 | ATOM 357 N ILE A 25 -9.702 -5.727 -16.084 0.61 4.36 N
35 | ANISOU 357 N ILE A 25 520 465 672 33 -18 2 N
36 | ATOM 358 CA ILE A 25 -9.340 -5.286 -17.428 0.61 4.45 C
37 | ANISOU 358 CA ILE A 25 533 529 630 -6 -50 -5 C
38 | ATOM 359 C ILE A 25 -8.471 -4.014 -17.377 0.61 4.39 C
39 | ANISOU 359 C ILE A 25 478 580 609 -1 -12 -14 C
40 | ATOM 360 O ILE A 25 -7.519 -3.868 -18.141 0.61 5.13 O
41 | ANISOU 360 O ILE A 25 566 725 660 -65 86 -77 O
42 | ATOM 376 N ALEU A 25 -9.706 -5.722 -16.071 0.99 4.68 N
43 | ANISOU 376 N ALEU A 25 553 548 676 10 -26 -1 N
44 | ATOM 377 CA ALEU A 25 -9.351 -5.281 -17.410 0.99 4.87 C
45 | ANISOU 377 CA ALEU A 25 554 645 651 -35 -48 -28 C
46 | ATOM 378 C ALEU A 25 -8.459 -4.039 -17.346 0.99 4.69 C
47 | ANISOU 378 C ALEU A 25 518 645 621 -13 -18 -33 C
48 | ATOM 379 O ALEU A 25 -7.460 -3.953 -18.055 0.99 5.25 O
49 | ANISOU 379 O ALEU A 25 583 759 652 -28 45 -75 O
50 | ATOM 333 CA AGLU A 26 -10.000 -3.000 -12.000 0.50 4.89 C
51 | ANISOU 333 CA AGLU A 26 576 620 663 31 42 45 C
52 | ATOM 333 CA CGLU A 26 -10.679 -3.437 -12.387 1.00 4.89 C
53 | ANISOU 333 CA CGLU A 26 576 620 663 31 42 45 C
54 | END
55 |
--------------------------------------------------------------------------------
/tests/data/dummy_altloc2.pdb:
--------------------------------------------------------------------------------
1 | REMARK ORIGINAL DATA FROM PDB ID 3U7T
2 | REMARK MODELS ALTLOC SEPARETING SEQUENCES OF RESIDUES
3 | ATOM 308 N ASER A 22 -14.282 -3.023 -15.571 0.53 4.57 N
4 | ANISOU 308 N ASER A 22 594 482 661 7 -42 32 N
5 | ATOM 309 CA ASER A 22 -13.829 -3.816 -14.436 0.53 5.30 C
6 | ANISOU 309 CA ASER A 22 695 586 731 6 -19 117 C
7 | ATOM 310 C ASER A 22 -12.365 -3.540 -14.113 0.53 4.77 C
8 | ANISOU 310 C ASER A 22 582 560 669 34 31 98 C
9 | ATOM 311 O ASER A 22 -11.531 -3.326 -15.003 0.53 5.44 O
10 | ANISOU 311 O ASER A 22 602 786 679 59 46 138 O
11 | ATOM 332 N AGLU A 23 -12.046 -3.614 -12.827 1.00 4.76 N
12 | ANISOU 332 N AGLU A 23 558 607 642 79 39 69 N
13 | ATOM 333 CA AGLU A 23 -10.679 -3.437 -12.387 1.00 4.89 C
14 | ANISOU 333 CA AGLU A 23 576 620 663 31 42 45 C
15 | ATOM 334 C AGLU A 23 -9.718 -4.412 -13.075 1.00 4.66 C
16 | ANISOU 334 C AGLU A 23 509 598 663 8 21 49 C
17 | ATOM 335 O AGLU A 23 -8.593 -4.031 -13.405 1.00 5.42 O
18 | ANISOU 335 O AGLU A 23 569 765 725 -40 72 -21 O
19 | ATOM 318 N BPRO A 22 -14.305 -3.040 -15.563 0.47 5.18 N
20 | ANISOU 318 N BPRO A 22 713 566 690 -20 -45 8 N
21 | ATOM 319 CA BPRO A 22 -13.801 -3.851 -14.452 0.47 5.29 C
22 | ANISOU 319 CA BPRO A 22 701 599 711 -19 -20 59 C
23 | ATOM 320 C BPRO A 22 -12.344 -3.583 -14.119 0.47 4.84 C
24 | ANISOU 320 C BPRO A 22 590 585 664 23 27 64 C
25 | ATOM 321 O BPRO A 22 -11.499 -3.414 -15.007 0.47 5.37 O
26 | ANISOU 321 O BPRO A 22 603 762 675 65 45 100 O
27 | ATOM 332 N BGLU A 23 -12.046 -3.614 -12.827 1.00 4.76 N
28 | ANISOU 332 N BGLU A 23 558 607 642 79 39 69 N
29 | ATOM 333 CA BGLU A 23 -10.679 -3.437 -12.387 1.00 4.89 C
30 | ANISOU 333 CA BGLU A 23 576 620 663 31 42 45 C
31 | ATOM 334 C BGLU A 23 -9.718 -4.412 -13.075 1.00 4.66 C
32 | ANISOU 334 C BGLU A 23 509 598 663 8 21 49 C
33 | ATOM 335 O BGLU A 23 -8.593 -4.031 -13.405 1.00 5.42 O
34 | ANISOU 335 O BGLU A 23 569 765 725 -40 72 -21 O
35 | ATOM 347 N ALA A 24 -10.141 -5.661 -13.285 1.00 4.86 N
36 | ANISOU 347 N ALA A 24 551 614 679 31 34 65 N
37 | ATOM 348 CA ALA A 24 -9.236 -6.655 -13.860 1.00 5.12 C
38 | ANISOU 348 CA ALA A 24 659 547 738 54 48 91 C
39 | ATOM 349 C ALA A 24 -8.797 -6.289 -15.284 1.00 4.57 C
40 | ANISOU 349 C ALA A 24 574 482 682 38 -18 30 C
41 | ATOM 350 O ALA A 24 -7.651 -6.544 -15.670 1.00 5.51 O
42 | ANISOU 350 O ALA A 24 643 726 724 126 19 56 O
43 | ATOM 357 N ILE A 25 -9.702 -5.727 -16.084 0.61 4.36 N
44 | ANISOU 357 N ILE A 25 520 465 672 33 -18 2 N
45 | ATOM 358 CA ILE A 25 -9.340 -5.286 -17.428 0.61 4.45 C
46 | ANISOU 358 CA ILE A 25 533 529 630 -6 -50 -5 C
47 | ATOM 359 C ILE A 25 -8.471 -4.014 -17.377 0.61 4.39 C
48 | ANISOU 359 C ILE A 25 478 580 609 -1 -12 -14 C
49 | ATOM 360 O ILE A 25 -7.519 -3.868 -18.141 0.61 5.13 O
50 | ANISOU 360 O ILE A 25 566 725 660 -65 86 -77 O
51 | ATOM 376 N ALEU A 25 -9.706 -5.722 -16.071 0.99 4.68 N
52 | ANISOU 376 N ALEU A 25 553 548 676 10 -26 -1 N
53 | ATOM 377 CA ALEU A 25 -9.351 -5.281 -17.410 0.99 4.87 C
54 | ANISOU 377 CA ALEU A 25 554 645 651 -35 -48 -28 C
55 | ATOM 378 C ALEU A 25 -8.459 -4.039 -17.346 0.99 4.69 C
56 | ANISOU 378 C ALEU A 25 518 645 621 -13 -18 -33 C
57 | ATOM 379 O ALEU A 25 -7.460 -3.953 -18.055 0.99 5.25 O
58 | ANISOU 379 O ALEU A 25 583 759 652 -28 45 -75 O
59 | ATOM 333 CA AGLU A 26 -10.000 -3.000 -12.000 0.50 4.89 C
60 | ANISOU 333 CA AGLU A 26 576 620 663 31 42 45 C
61 | ATOM 333 CA CGLU A 26 -10.679 -3.437 -12.387 1.00 4.89 C
62 | ANISOU 333 CA CGLU A 26 576 620 663 31 42 45 C
63 | END
64 |
--------------------------------------------------------------------------------
/tests/data/dummy_altloc3.pdb:
--------------------------------------------------------------------------------
1 | ATOM 153 N VAL A 18 -0.264 -17.574 22.788 0.00 0.00 N
2 | ATOM 154 CA VAL A 18 -0.201 -17.901 24.209 0.00 0.00 C
3 | ATOM 155 C VAL A 18 -1.047 -19.150 24.437 0.00 0.00 C
4 | ATOM 156 O VAL A 18 -2.260 -19.144 24.214 0.00 0.00 O
5 | ATOM 157 CB VAL A 18 -0.689 -16.724 25.084 0.00 0.00 C
6 | ATOM 161 N TRP A 19 -0.408 -20.230 24.882 0.00 0.00 N
7 | ATOM 162 CA TRP A 19 -1.091 -21.490 25.161 0.00 0.00 C
8 | ATOM 163 C TRP A 19 -1.303 -21.563 26.667 0.00 0.00 C
9 | ATOM 164 O TRP A 19 -0.357 -21.920 27.375 0.00 0.00 O
10 | ATOM 165 CB TRP A 19 -0.272 -22.670 24.635 0.00 0.00 C
11 | ATOM 176 N TYR A 20 -2.522 -21.226 27.083 0.00 0.00 N
12 | ATOM 177 CA TYR A 20 -2.898 -21.178 28.493 0.00 0.00 C
13 | ATOM 178 C TYR A 20 -3.718 -22.410 28.851 0.00 0.00 C
14 | ATOM 179 O TYR A 20 -4.629 -22.780 28.105 0.00 0.00 O
15 | ATOM 180 CB TYR A 20 -3.681 -19.898 28.795 0.00 0.00 C
16 | ATOM 189 N AVAL A 21 -3.396 -23.034 29.982 0.40 0.00 N
17 | ATOM 197 N BALA A 21 -5.676 -24.647 32.284 0.60 0.00 N
18 | ATOM 190 CA AVAL A 21 -4.121 -24.205 30.467 0.40 0.00 C
19 | ATOM 198 CA BALA A 21 -6.012 -24.814 33.696 0.60 0.00 C
20 | ATOM 191 C AVAL A 21 -4.530 -24.072 31.930 0.40 0.00 C
21 | ATOM 199 C BALA A 21 -4.990 -25.677 34.426 0.60 0.00 C
22 | ATOM 192 O AVAL A 21 -3.835 -23.461 32.747 0.40 0.00 O
23 | ATOM 200 O BALA A 21 -4.494 -26.675 33.897 0.60 0.00 O
24 | ATOM 193 CB AVAL A 21 -3.289 -25.497 30.298 0.40 0.00 C
25 | ATOM 201 CB BALA A 21 -7.405 -25.428 33.847 0.60 0.00 C
26 | ATOM 189 N APRO A 22 -3.396 -23.034 29.982 0.40 0.00 N
27 | ATOM 190 CA APRO A 22 -4.121 -24.205 30.467 0.40 0.00 C
28 | ATOM 191 C APRO A 22 -4.530 -24.072 31.930 0.40 0.00 C
29 | ATOM 192 O APRO A 22 -3.835 -23.461 32.747 0.40 0.00 O
30 | ATOM 193 CB APRO A 22 -3.289 -25.497 30.298 0.40 0.00 C
31 | ATOM 197 N BGLY A 22 -5.676 -24.647 32.284 0.60 0.00 N
32 | ATOM 198 CA BGLY A 22 -6.012 -24.814 33.696 0.60 0.00 C
33 | ATOM 199 C BGLY A 22 -4.990 -25.677 34.426 0.60 0.00 C
34 | ATOM 200 O BGLY A 22 -4.494 -26.675 33.897 0.60 0.00 O
35 | ATOM 201 CB BGLY A 22 -7.405 -25.428 33.847 0.60 0.00 C
36 |
--------------------------------------------------------------------------------
/tests/data/dummy_nohead.pdb:
--------------------------------------------------------------------------------
1 | TITLE A RANDOM PDB
2 | COMPND MOL_ID: 1;
3 | SOURCE MOL_ID: 1;
4 | KEYWDS PDB-TOOLS, TEST-PDB
5 | REMARK THIS STRUCTURE CONTAINS 4 CHAINS
6 | REMARK ALL ATOM SERIAL NUMBERS ARE 1 FOR ATOM
7 | REMARK CHAIN A HAS SEGID Z
8 | REMARK DOUBLE OCCUPANCIES ON THE FIRST RESIDUE OF CHAIN A
9 | REMARK CHAIN B HAS NON-CONTINUOUS RESIDUES (SHOULD HAVE A TER STATEMENT)
10 | REMARK CHAIN C DOES NOT HAVE ELEMENTS FOR LAST RESIDUE
11 | REMARK CHAIN C CONTAINS RESIDUES OUT OF ORDER
12 | REMARK CHAIN D IS NUCLEIC ACID AND LACKS A TER STATEMENT
13 | ATOM 3 CA ARG B 4 37.080 43.455 -3.421 1.00 0.00 C
14 | ATOM 3 CA GLU B 6 33.861 45.127 -2.233 1.00 0.00 C
15 | ATOM 3 CA ALA B 7 35.081 45.036 1.305 1.00 0.00 C
16 | TER 3 ALA B 7
17 | ATOM 3 CA BASN A 1 20.000 30.000 0.005 0.60 0.00 C
18 | ATOM 3 CA AASN A 1 21.411 39.311 0.054 0.40 0.00 C
19 | ATOM 3 CA ARG A 2 24.384 37.823 -1.757 1.00 0.00 C
20 | ATOM 3 CA GLU A 3 23.064 34.219 -1.780 1.00 0.00 C
21 | TER 3 GLU A 3
22 | ATOM 3 CA ARG C 5 37.080 43.455 -3.421 1.00 0.00 C
23 | ATOM 3 CA GLU C 2 33.861 45.127 -2.233 1.00 0.00 C
24 | ATOM 3 CA MET C -1 42.850 -16.494 70.506 1.00 52.98
25 | TER 3 MET C -1
26 | ATOM 3 P DT D 2 36.556 19.296 31.761 1.00247.39 Z
27 | ATOM 3 OP1 DT D 2 37.512 20.431 31.873 1.00246.92 Z
28 | ATOM 3 OP2 DT D 2 36.121 18.841 30.413 1.00249.27 Z
29 | ATOM 3 C4' DT D 2 36.894 16.884 34.720 1.00230.04 Z
30 | HETATM 4 CA CA A 301 44.698 -0.753 65.490 1.00 57.81
31 | HETATM 5 O HOH A 302 61.179 -8.803 36.085 1.00 51.61
32 | HETATM 6 O HOH A 303 64.052 21.644 20.397 1.00 65.18
33 | HETATM 7 O HOH B 301 11.052 -12.419 29.700 1.00 73.70
34 | HETATM 8 O HOH C 301 -8.172 -22.003 57.197 1.00 70.53
35 | HETATM 9 O HOH C 302 36.020 -23.583 73.186 1.00 24.82
36 | HETATM 10 O HOH C 303 41.203 -28.852 57.698 1.00 53.16
37 | HETATM 11 O HOH C 304 -4.491 -9.687 56.752 1.00 55.08
38 | HETATM 12 O HOH C 305 24.561 0.532 70.565 1.00 44.77
39 | CONECT 10 11
40 |
41 |
42 |
--------------------------------------------------------------------------------
/tests/data/ensemble_OK.cif:
--------------------------------------------------------------------------------
1 | # generated by PyMOL 2.1.1
2 | #
3 | data_ensemble_OK
4 | _entry.id ensemble_OK
5 | #
6 | loop_
7 | _atom_site.group_PDB
8 | _atom_site.id
9 | _atom_site.type_symbol
10 | _atom_site.label_atom_id
11 | _atom_site.label_alt_id
12 | _atom_site.label_comp_id
13 | _atom_site.label_asym_id
14 | _atom_site.label_entity_id
15 | _atom_site.label_seq_id
16 | _atom_site.pdbx_PDB_ins_code
17 | _atom_site.Cartn_x
18 | _atom_site.Cartn_y
19 | _atom_site.Cartn_z
20 | _atom_site.occupancy
21 | _atom_site.B_iso_or_equiv
22 | _atom_site.pdbx_formal_charge
23 | _atom_site.auth_asym_id
24 | _atom_site.pdbx_PDB_model_num
25 | ATOM 1 N N . ASN . . 1 ? 22.066 40.557 0.420 1.00 0.00 0 A 1
26 | ATOM 2 H H . ASN . . 1 ? 21.629 41.305 -0.098 1.00 0.00 0 A 1
27 | ATOM 1 N N . ASN . . 1 ? 22.066 40.557 0.420 1.00 0.00 0 A 2
28 | ATOM 2 H H . ASN . . 1 ? 21.629 41.305 -0.098 1.00 0.00 0 A 2
29 |
--------------------------------------------------------------------------------
/tests/data/ensemble_OK.pdb:
--------------------------------------------------------------------------------
1 | HEADER THIS A DUMMY PDB FOR PDB-TOOLS TESTING
2 | TITLE A RANDOM PDB
3 | MODEL 1
4 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
5 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
6 | ENDMDL
7 | MODEL 2
8 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
9 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
10 | ENDMDL
11 | END
--------------------------------------------------------------------------------
/tests/data/ensemble_error_1.pdb:
--------------------------------------------------------------------------------
1 | HEADER THIS A DUMMY PDB FOR PDB-TOOLS TESTING
2 | TITLE A RANDOM PDB
3 | REMARK MODEL 2 HAS ONE LESS ATOM
4 | MODEL 1
5 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
6 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
7 | ENDMDL
8 | MODEL 2
9 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
10 | ENDMDL
11 | END
--------------------------------------------------------------------------------
/tests/data/ensemble_error_2.pdb:
--------------------------------------------------------------------------------
1 | HEADER THIS A DUMMY PDB FOR PDB-TOOLS TESTING
2 | TITLE A RANDOM PDB
3 | REMARK MODEL RECORD MISSING ON MODEL 1
4 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
5 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
6 | ENDMDL
7 | MODEL 2
8 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
9 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
10 | ENDMDL
11 | END
--------------------------------------------------------------------------------
/tests/data/ensemble_error_3.pdb:
--------------------------------------------------------------------------------
1 | HEADER THIS A DUMMY PDB FOR PDB-TOOLS TESTING
2 | TITLE A RANDOM PDB
3 | REMARK ENDMDL RECORD MISSING ON MODEL 2
4 | MODEL 1
5 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
6 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
7 | ENDMDL
8 | MODEL 2
9 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
10 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
11 | END
--------------------------------------------------------------------------------
/tests/data/ensemble_error_4.pdb:
--------------------------------------------------------------------------------
1 | HEADER THIS A DUMMY PDB FOR PDB-TOOLS TESTING
2 | TITLE A RANDOM PDB
3 | REMARK ENDMDL RECORD MISSING ON MODEL 1
4 | MODEL 1
5 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
6 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
7 | MODEL 2
8 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
9 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
10 | END
--------------------------------------------------------------------------------
/tests/data/ensemble_error_MODEL.pdb:
--------------------------------------------------------------------------------
1 | HEADER THIS A DUMMY PDB FOR PDB-TOOLS TESTING
2 | TITLE A RANDOM PDB
3 | REMARK MODEL 1 HAS A BAD FORMATTED LINE
4 | REMARK MODEL 2 HAS NO NUMBER
5 | REMARK PDB_TIDY SHOULD ADD AND CORRECT THE MODEL NUMBERS
6 | MODEL 1
7 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
8 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
9 | ENDMDL
10 | MODEL
11 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
12 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
13 | ENDMDL
14 | END
15 |
--------------------------------------------------------------------------------
/tests/data/ensemble_more_OK.pdb:
--------------------------------------------------------------------------------
1 | HEADER THIS A DUMMY PDB FOR PDB-TOOLS TESTING
2 | TITLE A RANDOM PDB
3 | MODEL 1
4 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
5 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
6 | ATOM 3 N ASN A 2 22.066 40.557 0.420 1.00 0.00 N
7 | ATOM 4 H ASN A 2 21.629 41.305 -0.098 1.00 0.00 H
8 | ENDMDL
9 | MODEL 2
10 | ATOM 1 N ASN A 1 22.066 40.557 0.420 1.00 0.00 N
11 | ATOM 2 H ASN A 1 21.629 41.305 -0.098 1.00 0.00 H
12 | ENDMDL
13 | END
14 |
--------------------------------------------------------------------------------
/tests/data/hetatm.pdb:
--------------------------------------------------------------------------------
1 | HEADER THIS A DUMMY PDB FOR PDB-TOOLS TESTING
2 | TITLE A RANDOM PDB
3 | COMPND MOL_ID: 1;
4 | SOURCE MOL_ID: 1;
5 | KEYWDS PDB-TOOLS, TEST-PDB
6 | REMARK ATOMS HAVE REPEATED NAMES WITHIN EACH RESIDUE.
7 | HETATM 1 C UNK A 1 34.813 43.714 -0.371 1.00 0.00 C
8 | HETATM 2 C UNK A 1 35.081 45.036 1.305 1.00 0.00 C
9 | HETATM 3 H UNK A 1 34.200 45.604 1.876 1.00 0.00 H
10 | HETATM 4 C UNK A 1 35.381 43.810 2.140 1.00 0.00 C
11 | HETATM 5 CA UNK A 2 34.382 43.229 2.456 1.00 0.00 CA
12 | HETATM 6 CA UNK A 2 36.167 42.973 1.802 1.00 0.00 CA
13 | HETATM 7 H UNK A 2 35.847 44.180 3.181 1.00 0.00 H
14 | HETATM 8 H UNK A 2 36.213 46.067 1.258 1.00 0.00 H
15 | HETATM 9 CA UNK A 3 34.382 43.229 2.456 1.00 0.00 C
16 | HETATM 10 CA UNK A 3 36.167 42.973 1.802 1.00 0.00 C
17 | ATOM 11 H UNK A 4 35.847 44.180 3.181 1.00 0.00 H
18 | ATOM 12 H UNK A 4 36.213 46.067 1.258 1.00 0.00 H
19 | END
20 |
--------------------------------------------------------------------------------
/tests/data/hetatm_bad.pdb:
--------------------------------------------------------------------------------
1 | HEADER THIS A DUMMY PDB FOR PDB-TOOLS TESTING
2 | TITLE A RANDOM PDB
3 | COMPND MOL_ID: 1;
4 | SOURCE MOL_ID: 1;
5 | KEYWDS PDB-TOOLS, TEST-PDB
6 | REMARK ATOMS HAVE REPEATED NAMES WITHIN EACH RESIDUE.
7 | REMARK NO ELEMENTS ASSIGNED.
8 | HETATM 1 C UNK A 1 34.813 43.714 -0.371 1.00 0.00
9 | HETATM 2 C UNK A 1 35.081 45.036 1.305 1.00 0.00
10 | END
11 |
--------------------------------------------------------------------------------
/tests/data/hetatm_ensemble.pdb:
--------------------------------------------------------------------------------
1 | MODEL 1
2 | HETATM 1 C1 G39 B 500 -30.374 -53.143 6.122 1.00 31.18 C
3 | HETATM 2 O1A G39 B 500 -31.439 -52.533 5.850 1.00 30.30 O
4 | HETATM 3 O1B G39 B 500 -29.257 -52.625 5.879 1.00 31.23 O
5 | HETATM 4 C2 G39 B 500 -30.437 -54.486 6.725 1.00 30.48 C
6 | HETATM 5 C3 G39 B 500 -29.159 -55.188 7.132 1.00 30.04 C
7 | HETATM 6 C4 G39 B 500 -29.380 -56.162 8.294 1.00 30.37 C
8 | HETATM 7 N4 G39 B 500 -28.195 -56.994 8.430 1.00 30.09 N1+
9 | HETATM 8 C5 G39 B 500 -30.627 -57.033 8.097 1.00 29.53 C
10 | HETATM 9 N5 G39 B 500 -30.803 -57.955 9.206 1.00 28.16 N
11 | HETATM 10 C6 G39 B 500 -31.883 -56.178 7.937 1.00 30.53 C
12 | HETATM 11 C7 G39 B 500 -31.632 -55.093 6.901 1.00 30.75 C
13 | HETATM 12 O7 G39 B 500 -33.007 -56.989 7.531 1.00 31.80 O
14 | HETATM 13 C8 G39 B 500 -34.158 -56.905 8.398 1.00 30.52 C
15 | HETATM 14 C9 G39 B 500 -34.879 -55.553 8.283 1.00 31.36 C
16 | HETATM 15 C10 G39 B 500 -30.683 -59.283 9.087 1.00 27.13 C
17 | HETATM 16 O10 G39 B 500 -30.629 -59.863 8.010 1.00 26.14 O
18 | HETATM 17 C11 G39 B 500 -30.611 -60.040 10.380 1.00 26.05 C
19 | HETATM 18 C81 G39 B 500 -35.153 -58.020 8.079 1.00 29.88 C
20 | HETATM 19 C82 G39 B 500 -34.654 -59.395 8.467 1.00 28.23 C
21 | HETATM 20 C91 G39 B 500 -35.425 -55.279 6.891 1.00 32.33 C
22 | ENDMDL
23 | MODEL 2
24 | HETATM 1 C1 G39 B 500 -30.374 -53.143 6.122 1.00 31.18 C
25 | HETATM 2 O1A G39 B 500 -31.439 -52.533 5.850 1.00 30.30 O
26 | HETATM 3 O1B G39 B 500 -29.257 -52.625 5.879 1.00 31.23 O
27 | HETATM 4 C2 G39 B 500 -30.437 -54.486 6.725 1.00 30.48 C
28 | HETATM 5 C3 G39 B 500 -29.159 -55.188 7.132 1.00 30.04 C
29 | HETATM 6 C4 G39 B 500 -29.380 -56.162 8.294 1.00 30.37 C
30 | HETATM 7 N4 G39 B 500 -28.195 -56.994 8.430 1.00 30.09 N1+
31 | HETATM 8 C5 G39 B 500 -30.627 -57.033 8.097 1.00 29.53 C
32 | HETATM 9 N5 G39 B 500 -30.803 -57.955 9.206 1.00 28.16 N
33 | HETATM 10 C6 G39 B 500 -31.883 -56.178 7.937 1.00 30.53 C
34 | HETATM 11 C7 G39 B 500 -31.632 -55.093 6.901 1.00 30.75 C
35 | HETATM 12 O7 G39 B 500 -33.007 -56.989 7.531 1.00 31.80 O
36 | HETATM 13 C8 G39 B 500 -34.158 -56.905 8.398 1.00 30.52 C
37 | HETATM 14 C9 G39 B 500 -34.879 -55.553 8.283 1.00 31.36 C
38 | HETATM 15 C10 G39 B 500 -30.683 -59.283 9.087 1.00 27.13 C
39 | HETATM 16 O10 G39 B 500 -30.629 -59.863 8.010 1.00 26.14 O
40 | HETATM 17 C11 G39 B 500 -30.611 -60.040 10.380 1.00 26.05 C
41 | HETATM 18 C81 G39 B 500 -35.153 -58.020 8.079 1.00 29.88 C
42 | HETATM 19 C82 G39 B 500 -34.654 -59.395 8.467 1.00 28.23 C
43 | HETATM 20 C91 G39 B 500 -35.425 -55.279 6.891 1.00 32.33 C
44 | ENDMDL
45 | END
--------------------------------------------------------------------------------
/tests/data/vu7.pdb:
--------------------------------------------------------------------------------
1 | HETATM 2894 C1 VU7 A 403 -31.758 17.914 27.058 1.00 48.52 C
2 | HETATM 2895 C4 VU7 A 403 -30.891 21.105 25.901 1.00 27.04 C
3 | HETATM 2896 C6 VU7 A 403 -30.290 21.607 24.647 1.00 20.12 C
4 | HETATM 2897 C7 VU7 A 403 -28.912 21.863 24.551 1.00 19.79 C
5 | HETATM 2898 C8 VU7 A 403 -28.027 21.711 25.782 1.00 19.85 C
6 | HETATM 2899 N10 VU7 A 403 -29.224 22.454 22.214 1.00 17.91 N
7 | HETATM 2900 C11 VU7 A 403 -30.527 22.199 22.221 1.00 20.04 C
8 | HETATM 2901 C12 VU7 A 403 -31.311 22.354 21.045 1.00 19.38 C
9 | HETATM 2902 C16 VU7 A 403 -32.533 21.485 23.426 1.00 19.97 C
10 | HETATM 2903 C17 VU7 A 403 -31.146 21.759 23.446 1.00 19.01 C
11 | HETATM 2904 C18 VU7 A 403 -27.027 22.610 22.976 1.00 20.52 C
12 | HETATM 2905 C19 VU7 A 403 -26.412 21.910 21.910 1.00 18.57 C
13 | HETATM 2906 C21 VU7 A 403 -24.384 23.145 22.280 1.00 20.18 C
14 | HETATM 2907 C23 VU7 A 403 -26.310 23.599 23.677 1.00 20.64 C
15 | HETATM 2908 C24 VU7 A 403 -23.014 23.546 21.923 1.00 22.21 C
16 | HETATM 2909 C27AVU7 A 403 -20.473 24.388 21.073 0.30 23.87 C
17 | HETATM 2910 C27BVU7 A 403 -20.472 24.444 21.209 0.70 25.90 C
18 | HETATM 2911 O2 VU7 A 403 -31.199 19.197 27.357 1.00 55.96 O
19 | HETATM 2912 N3 VU7 A 403 -30.649 19.814 26.213 1.00 33.91 N
20 | HETATM 2913 O5 VU7 A 403 -31.603 21.787 26.592 1.00 26.21 O
21 | HETATM 2914 C9 VU7 A 403 -28.429 22.299 23.277 1.00 19.07 C
22 | HETATM 2915 C13 VU7 A 403 -32.643 22.059 21.082 1.00 19.17 C
23 | HETATM 2916 C14 VU7 A 403 -33.245 21.630 22.272 1.00 21.96 C
24 | HETATM 2917 F15 VU7 A 403 -34.549 21.337 22.285 1.00 25.50 F
25 | HETATM 2918 C20 VU7 A 403 -25.101 22.166 21.562 1.00 19.10 C
26 | HETATM 2919 C22 VU7 A 403 -25.005 23.859 23.317 1.00 20.89 C
27 | HETATM 2920 C25AVU7 A 403 -22.814 24.071 20.642 0.30 22.86 C
28 | HETATM 2921 C25BVU7 A 403 -21.980 23.567 22.880 0.70 25.16 C
29 | HETATM 2922 C26AVU7 A 403 -21.562 24.484 20.224 0.30 22.96 C
30 | HETATM 2923 C26BVU7 A 403 -20.710 24.002 22.511 0.70 29.65 C
31 | HETATM 2924 C28AVU7 A 403 -20.656 23.873 22.343 0.30 25.28 C
32 | HETATM 2925 C28BVU7 A 403 -21.503 24.451 20.265 0.70 24.53 C
33 | HETATM 2926 C29AVU7 A 403 -21.907 23.454 22.776 0.30 23.59 C
34 | HETATM 2927 C29BVU7 A 403 -22.775 24.020 20.615 0.70 24.22 C
35 | HETATM 2928 F30AVU7 A 403 -22.039 22.963 24.024 0.30 24.64 F
36 | HETATM 2929 F30BVU7 A 403 -23.749 24.031 19.675 0.70 26.05 F
37 |
--------------------------------------------------------------------------------
/tests/test_pdb_b.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_b`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_b'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_b data/dummy.pdb"""
58 |
59 | # Simulate input
60 | # pdb_b -20 dummy.pdb
61 | sys.argv = ['', os.path.join(data_dir, 'dummy.pdb')]
62 |
63 | # Execute the script
64 | self.exec_module()
65 |
66 | # Validate results
67 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
68 | self.assertEqual(len(self.stdout), 204) # no lines deleted
69 | self.assertEqual(len(self.stderr), 0) # no errors
70 |
71 | records = (('ATOM', 'HETATM'))
72 | bfactors = [l[60:66] for l in self.stdout if l.startswith(records)]
73 | unique_bfac = list(set(map(float, bfactors)))
74 | self.assertEqual(unique_bfac, [10.00]) # all bfactors changed
75 |
76 | def test_two_options(self):
77 | """$ pdb_b -20 data/dummy.pdb"""
78 |
79 | sys.argv = ['', '-20.0', os.path.join(data_dir, 'dummy.pdb')]
80 |
81 | self.exec_module()
82 |
83 | self.assertEqual(self.retcode, 0)
84 | self.assertEqual(len(self.stdout), 204)
85 | self.assertEqual(len(self.stderr), 0)
86 |
87 | records = (('ATOM', 'HETATM'))
88 | bfactors = [l[60:66] for l in self.stdout if l.startswith(records)]
89 | unique_bfac = list(set(map(float, bfactors)))
90 | self.assertEqual(unique_bfac, [20.00])
91 |
92 | def test_file_not_found(self):
93 | """$ pdb_b not_existing.pdb"""
94 |
95 | afile = os.path.join(data_dir, 'not_existing.pdb')
96 | sys.argv = ['', '-10.0', afile]
97 |
98 | self.exec_module()
99 |
100 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
101 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
102 | self.assertEqual(self.stderr[0][:22],
103 | "ERROR!! File not found") # proper error message
104 |
105 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
106 | def test_file_missing(self):
107 | """$ pdb_b -10"""
108 |
109 | sys.argv = ['', '-10.0']
110 |
111 | self.exec_module()
112 |
113 | self.assertEqual(self.retcode, 1)
114 | self.assertEqual(len(self.stdout), 0) # no output
115 | self.assertEqual(self.stderr[0],
116 | "ERROR!! No data to process!")
117 |
118 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
119 | def test_helptext(self):
120 | """$ pdb_b"""
121 |
122 | sys.argv = ['']
123 |
124 | self.exec_module()
125 |
126 | self.assertEqual(self.retcode, 1) # ensure the program exited gracefully.
127 | self.assertEqual(len(self.stdout), 0) # no output
128 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
129 |
130 | def test_invalid_option(self):
131 | """$ pdb_b -A data/dummy.pdb"""
132 |
133 | sys.argv = ['', '-A', os.path.join(data_dir, 'dummy.pdb')]
134 |
135 | self.exec_module()
136 |
137 | self.assertEqual(self.retcode, 1)
138 | self.assertEqual(len(self.stdout), 0)
139 | self.assertEqual(self.stderr[0][:47],
140 | "ERROR!! You provided an invalid b-factor value:")
141 |
142 | def test_not_an_option(self):
143 | """$ pdb_b 20 data/dummy.pdb"""
144 |
145 | sys.argv = ['', '20', os.path.join(data_dir, 'dummy.pdb')]
146 |
147 | self.exec_module()
148 |
149 | self.assertEqual(self.retcode, 1)
150 | self.assertEqual(len(self.stdout), 0)
151 | self.assertEqual(self.stderr[0],
152 | "ERROR! First argument is not an option: '20'")
153 |
154 |
155 | if __name__ == '__main__':
156 | from config import test_dir
157 |
158 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
159 | sys.path.insert(0, mpath) # so we load dev files before any installation
160 |
161 | unittest.main()
162 |
--------------------------------------------------------------------------------
/tests/test_pdb_chain.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_chain`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_chain'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_chain data/dummy.pdb"""
58 |
59 | # Simulate input
60 | sys.argv = ['', os.path.join(data_dir, 'dummy.pdb')]
61 |
62 | # Execute the script
63 | self.exec_module()
64 |
65 | # Validate results
66 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
67 | self.assertEqual(len(self.stdout), 204) # no lines deleted
68 | self.assertEqual(len(self.stderr), 0) # no errors
69 |
70 | records = (('ATOM', 'HETATM'))
71 | chain_ids = [l[21] for l in self.stdout if l.startswith(records)]
72 | unique_chain_ids = list(set(chain_ids))
73 | self.assertEqual(unique_chain_ids, [' '])
74 |
75 | def test_two_options(self):
76 | """$ pdb_chain -X data/dummy.pdb"""
77 |
78 | sys.argv = ['', '-X', os.path.join(data_dir, 'dummy.pdb')]
79 |
80 | self.exec_module()
81 |
82 | self.assertEqual(self.retcode, 0)
83 | self.assertEqual(len(self.stdout), 204)
84 | self.assertEqual(len(self.stderr), 0)
85 |
86 | records = (('ATOM', 'HETATM'))
87 | chain_ids = [l[21] for l in self.stdout if l.startswith(records)]
88 | unique_chain_ids = list(set(chain_ids))
89 | self.assertEqual(unique_chain_ids, ['X'])
90 |
91 | def test_file_not_found(self):
92 | """$ pdb_chain -A not_existing.pdb"""
93 |
94 | afile = os.path.join(data_dir, 'not_existing.pdb')
95 | sys.argv = ['', '-A', afile]
96 |
97 | self.exec_module()
98 |
99 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
100 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
101 | self.assertEqual(self.stderr[0][:22],
102 | "ERROR!! File not found") # proper error message
103 |
104 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
105 | def test_file_missing(self):
106 | """$ pdb_chain -A"""
107 |
108 | sys.argv = ['', '-A']
109 |
110 | self.exec_module()
111 |
112 | self.assertEqual(self.retcode, 1)
113 | self.assertEqual(len(self.stdout), 0) # no output
114 | self.assertEqual(self.stderr[0],
115 | "ERROR!! No data to process!")
116 |
117 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
118 | def test_helptext(self):
119 | """$ pdb_chain"""
120 |
121 | sys.argv = ['']
122 |
123 | self.exec_module()
124 |
125 | self.assertEqual(self.retcode, 1) # ensure the program exited gracefully.
126 | self.assertEqual(len(self.stdout), 0) # no output
127 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
128 |
129 | def test_invalid_option(self):
130 | """$ pdb_chain -AH data/dummy.pdb"""
131 |
132 | sys.argv = ['', '-AH', os.path.join(data_dir, 'dummy.pdb')]
133 |
134 | self.exec_module()
135 |
136 | self.assertEqual(self.retcode, 1)
137 | self.assertEqual(len(self.stdout), 0)
138 | self.assertEqual(self.stderr[0][:47],
139 | "ERROR!! Chain identifiers must be a single char")
140 |
141 | def test_not_an_option(self):
142 | """$ pdb_chain A data/dummy.pdb"""
143 |
144 | sys.argv = ['', 'A', os.path.join(data_dir, 'dummy.pdb')]
145 |
146 | self.exec_module()
147 |
148 | self.assertEqual(self.retcode, 1)
149 | self.assertEqual(len(self.stdout), 0)
150 | self.assertEqual(self.stderr[0],
151 | "ERROR! First argument is not an option: 'A'")
152 |
153 |
154 | if __name__ == '__main__':
155 | from config import test_dir
156 |
157 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
158 | sys.path.insert(0, mpath) # so we load dev files before any installation
159 |
160 | unittest.main()
161 |
--------------------------------------------------------------------------------
/tests/test_pdb_chainbows.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2020 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_chainbows`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_chainbows'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_chainbows data/dummy.pdb"""
58 |
59 | fpath = os.path.join(data_dir, 'dummy.pdb')
60 | sys.argv = ['', fpath]
61 |
62 | # Execute the script
63 | self.exec_module()
64 |
65 | # Validate results
66 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
67 | # CONECTs are ignored by issue #72, expected only 204 lines
68 | self.assertEqual(len(self.stdout), 204)
69 | self.assertEqual(len(self.stderr), 0) # no warnings/errors
70 |
71 | # Check chains were reassigned properly
72 | expected = ['A'] * 51 + ['B'] * 56 + ['C'] * 49 + ['D'] * 20
73 | chains = [
74 | l[21] for l in self.stdout if l.startswith('ATOM')
75 | ]
76 | self.assertEqual(chains, expected)
77 |
78 | # Check TERs
79 | expected = ['A', 'B', 'C']
80 | chains = [
81 | l[21] for l in self.stdout if l.startswith('TER')
82 | ]
83 | self.assertEqual(chains, expected)
84 |
85 | # Check HETATM were reassigned properly
86 | expected = ['B', 'B', 'B', 'A', 'C', 'C', 'C', 'C', 'C']
87 | chains = [
88 | l[21] for l in self.stdout if l.startswith('HETATM')
89 | ]
90 | self.assertEqual(chains, expected)
91 |
92 | def test_file_not_found(self):
93 | """$ pdb_chainbows not_existing.pdb"""
94 |
95 | # Error (file not found)
96 | afile = os.path.join(data_dir, 'not_existing.pdb')
97 | sys.argv = ['', afile]
98 |
99 | # Execute the script
100 | self.exec_module()
101 |
102 | self.assertEqual(self.retcode, 1)
103 | self.assertEqual(len(self.stdout), 0)
104 | self.assertEqual(self.stderr[0][:22],
105 | "ERROR!! File not found")
106 |
107 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
108 | def test_helptext(self):
109 | """$ pdb_chainbows"""
110 |
111 | sys.argv = ['']
112 |
113 | # Execute the script
114 | self.exec_module()
115 |
116 | self.assertEqual(self.retcode, 1)
117 | self.assertEqual(len(self.stdout), 0)
118 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
119 |
120 | def test_invalid_option(self):
121 | """$ pdb_chainbows -A data/dummy.pdb"""
122 |
123 | sys.argv = ['', '-A', os.path.join(data_dir, 'dummy.pdb')]
124 |
125 | # Execute the script
126 | self.exec_module()
127 |
128 | self.assertEqual(self.retcode, 1)
129 | self.assertEqual(len(self.stdout), 0)
130 | self.assertEqual(self.stderr[0][:36],
131 | "ERROR!! Script takes 1 argument, not")
132 |
133 |
134 | if __name__ == '__main__':
135 | from config import test_dir
136 |
137 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
138 | sys.path.insert(0, mpath) # so we load dev files before any installation
139 |
140 | unittest.main()
141 |
--------------------------------------------------------------------------------
/tests/test_pdb_delhetatm.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_delhetatm`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_delhetatm'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_delhetatm data/dummy.pdb"""
58 |
59 | # Simulate input
60 | # pdb_delhetatm dummy.pdb
61 | sys.argv = ['', os.path.join(data_dir, 'dummy.pdb')]
62 |
63 | # Execute the script
64 | self.exec_module()
65 |
66 | # Validate results
67 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
68 | self.assertEqual(len(self.stdout), 194) # 9 HETATM records + 2 CONECT
69 | self.assertEqual(len(self.stderr), 0) # no errors
70 |
71 | def test_file_not_found(self):
72 | """$ pdb_delhetatm not_existing.pdb"""
73 |
74 | afile = os.path.join(data_dir, 'not_existing.pdb')
75 | sys.argv = ['', afile]
76 |
77 | self.exec_module()
78 |
79 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
80 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
81 | self.assertEqual(self.stderr[0][:22],
82 | "ERROR!! File not found") # proper error message
83 |
84 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
85 | def test_helptext(self):
86 | """$ pdb_delhetatm"""
87 |
88 | sys.argv = ['']
89 |
90 | self.exec_module()
91 |
92 | self.assertEqual(self.retcode, 1) # ensure the program exited gracefully.
93 | self.assertEqual(len(self.stdout), 0) # no output
94 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
95 |
96 | def test_invalid_option(self):
97 | """$ pdb_delhetatm -A data/dummy.pdb"""
98 |
99 | sys.argv = ['', '-A', os.path.join(data_dir, 'dummy.pdb')]
100 |
101 | self.exec_module()
102 |
103 | self.assertEqual(self.retcode, 1)
104 | self.assertEqual(len(self.stdout), 0)
105 | self.assertEqual(self.stderr[0][:36],
106 | "ERROR!! Script takes 1 argument, not")
107 |
108 |
109 | if __name__ == '__main__':
110 | from config import test_dir
111 |
112 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
113 | sys.path.insert(0, mpath) # so we load dev files before any installation
114 |
115 | unittest.main()
116 |
--------------------------------------------------------------------------------
/tests/test_pdb_fromcif.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_fromcif`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_fromcif'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_conversion(self):
57 | """$ pdb_fromcif data/ensemble_OK.pdb"""
58 |
59 | fpath = os.path.join(data_dir, 'ensemble_OK.cif')
60 | sys.argv = ['', fpath]
61 |
62 | # Execute the script
63 | self.exec_module()
64 |
65 | # Validate results
66 | self.assertEqual(self.retcode, 0)
67 | self.assertEqual(len(self.stdout), 9)
68 | self.assertEqual(len(self.stderr), 0)
69 |
70 | # Check order of records
71 | expected = ['MODEL ', 'ATOM ', 'ATOM ', 'ENDMDL',
72 | 'MODEL ', 'ATOM ', 'ATOM ', 'ENDMDL', 'END ']
73 | records = [l[:6] for l in self.stdout]
74 | self.assertEqual(records, expected)
75 |
76 | def test_file_not_found(self):
77 | """$ pdb_fromcif not_existing.pdb"""
78 |
79 | # Error (file not found)
80 | afile = os.path.join(data_dir, 'not_existing.pdb')
81 | sys.argv = ['', afile]
82 |
83 | # Execute the script
84 | self.exec_module()
85 |
86 | self.assertEqual(self.retcode, 1)
87 | self.assertEqual(len(self.stdout), 0)
88 | self.assertEqual(self.stderr[0][:22],
89 | "ERROR!! File not found")
90 |
91 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
92 | def test_helptext(self):
93 | """$ pdb_fromcif"""
94 |
95 | sys.argv = ['']
96 |
97 | # Execute the script
98 | self.exec_module()
99 |
100 | self.assertEqual(self.retcode, 1)
101 | self.assertEqual(len(self.stdout), 0)
102 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
103 |
104 | def test_invalid_option(self):
105 | """$ pdb_fromcif -A data/dummy.pdb"""
106 |
107 | sys.argv = ['', '-A', os.path.join(data_dir, 'dummy.pdb')]
108 |
109 | # Execute the script
110 | self.exec_module()
111 |
112 | self.assertEqual(self.retcode, 1)
113 | self.assertEqual(len(self.stdout), 0)
114 | self.assertEqual(self.stderr[0][:36],
115 | "ERROR!! Script takes 1 argument, not")
116 |
117 |
118 | if __name__ == '__main__':
119 | from config import test_dir
120 |
121 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
122 | sys.path.insert(0, mpath) # so we load dev files before any installation
123 |
124 | unittest.main()
125 |
--------------------------------------------------------------------------------
/tests/test_pdb_gap.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_gap`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_gap'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_gap data/dummy.pdb"""
58 |
59 | # Simulate input
60 | sys.argv = ['', os.path.join(data_dir, 'dummy.pdb')]
61 |
62 | # Execute the script
63 | self.exec_module()
64 |
65 | # Validate results
66 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
67 | self.assertEqual(len(self.stdout), 5) # no lines deleted
68 | self.assertEqual(len(self.stderr), 0) # no errors
69 |
70 | self.assertEqual(self.stdout,
71 | ["B:ARG4 < Seq. Gap > B:GLU6",
72 | "A:ASN1 < 9.42A > A:ASN1",
73 | "C:ARG5 < Seq. Gap > C:GLU2",
74 | "C:GLU2 < 95.75A > C:MET-1",
75 | "Found 4 gap(s) in the structure"])
76 |
77 | def test_file_not_found(self):
78 | """$ pdb_gap not_existing.pdb"""
79 |
80 | # Error (file not found)
81 | afile = os.path.join(data_dir, 'not_existing.pdb')
82 | sys.argv = ['', afile]
83 |
84 | # Execute the script
85 | self.exec_module()
86 |
87 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
88 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
89 | self.assertEqual(self.stderr[0][:22],
90 | "ERROR!! File not found") # proper error message
91 |
92 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
93 | def test_helptext(self):
94 | """$ pdb_gap"""
95 |
96 | sys.argv = ['']
97 |
98 | # Execute the script
99 | self.exec_module()
100 |
101 | self.assertEqual(self.retcode, 1) # ensure the program exited gracefully.
102 | self.assertEqual(len(self.stdout), 0) # no output
103 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
104 |
105 | def test_invalid_option(self):
106 | """$ pdb_gap -A data/dummy.pdb"""
107 |
108 | sys.argv = ['', '-A', os.path.join(data_dir, 'dummy.pdb')]
109 |
110 | # Execute the script
111 | self.exec_module()
112 |
113 | self.assertEqual(self.retcode, 1)
114 | self.assertEqual(len(self.stdout), 0)
115 | self.assertEqual(self.stderr[0][:36],
116 | "ERROR!! Script takes 1 argument, not")
117 |
118 |
119 | if __name__ == '__main__':
120 | from config import test_dir
121 |
122 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
123 | sys.path.insert(0, mpath) # so we load dev files before any installation
124 |
125 | unittest.main()
126 |
--------------------------------------------------------------------------------
/tests/test_pdb_head.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_head`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_head'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_valid_option(self):
57 | """$ pdb_head -10 data/dummy.pdb"""
58 |
59 | # Simulate input
60 | sys.argv = ['', '-10', os.path.join(data_dir, 'dummy.pdb')]
61 |
62 | # Execute the script
63 | self.exec_module()
64 |
65 | # Validate results
66 | self.assertEqual(self.retcode, 0)
67 | self.assertEqual(len(self.stdout), 10)
68 | self.assertEqual(len(self.stderr), 0)
69 |
70 | def test_invalid_option(self):
71 | """$ pdb_head --10 data/dummy.pdb"""
72 |
73 | # Simulate input
74 | sys.argv = ['', '--10', os.path.join(data_dir, 'dummy.pdb')]
75 |
76 | # Execute the script
77 | self.exec_module()
78 |
79 | # Validate results
80 | self.assertEqual(self.retcode, 1)
81 | self.assertEqual(len(self.stdout), 0)
82 | self.assertEqual(self.stderr[0][:22],
83 | "ERROR!! Option must be")
84 |
85 | def test_file_not_found(self):
86 | """$ pdb_head not_existing.pdb"""
87 |
88 | afile = os.path.join(data_dir, 'not_existing.pdb')
89 | sys.argv = ['', afile]
90 |
91 | self.exec_module()
92 |
93 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
94 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
95 | self.assertEqual(self.stderr[0][:22],
96 | "ERROR!! File not found") # proper error message
97 |
98 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
99 | def test_file_missing(self):
100 | """$ pdb_head -10"""
101 |
102 | sys.argv = ['', '-10']
103 |
104 | self.exec_module()
105 |
106 | self.assertEqual(self.retcode, 1)
107 | self.assertEqual(len(self.stdout), 0) # no output
108 | self.assertEqual(self.stderr[0],
109 | "ERROR!! No data to process!")
110 |
111 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
112 | def test_helptext(self):
113 | """$ pdb_head"""
114 |
115 | sys.argv = ['']
116 |
117 | self.exec_module()
118 |
119 | self.assertEqual(self.retcode, 1) # ensure the program exited gracefully.
120 | self.assertEqual(len(self.stdout), 0) # no output
121 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
122 |
123 | def test_not_an_option(self):
124 | """$ pdb_head 20 data/dummy.pdb"""
125 |
126 | sys.argv = ['', '20', os.path.join(data_dir, 'dummy.pdb')]
127 |
128 | self.exec_module()
129 |
130 | self.assertEqual(self.retcode, 1)
131 | self.assertEqual(len(self.stdout), 0)
132 | self.assertEqual(self.stderr[0],
133 | "ERROR! First argument is not an option: '20'")
134 |
135 |
136 | if __name__ == '__main__':
137 | from config import test_dir
138 |
139 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
140 | sys.path.insert(0, mpath) # so we load dev files before any installation
141 |
142 | unittest.main()
143 |
--------------------------------------------------------------------------------
/tests/test_pdb_keepcoord.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_keepcoord`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_keepcoord'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_keepcoord data/dummy.pdb"""
58 |
59 | # Simulate input
60 | # pdb_keepcoord dummy.pdb
61 | sys.argv = ['', os.path.join(data_dir, 'dummy.pdb')]
62 |
63 | # Execute the script
64 | self.exec_module()
65 |
66 | # Validate results
67 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
68 | self.assertEqual(len(self.stdout), 190) # 13 HEADER records
69 | self.assertEqual(len(self.stderr), 0) # no errors
70 |
71 | def test_file_not_found(self):
72 | """$ pdb_keepcoord not_existing.pdb"""
73 |
74 | afile = os.path.join(data_dir, 'not_existing.pdb')
75 | sys.argv = ['', afile]
76 |
77 | self.exec_module()
78 |
79 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
80 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
81 | self.assertEqual(self.stderr[0][:22],
82 | "ERROR!! File not found") # proper error message
83 |
84 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
85 | def test_helptext(self):
86 | """$ pdb_keepcoord"""
87 |
88 | sys.argv = ['']
89 |
90 | self.exec_module()
91 |
92 | self.assertEqual(self.retcode, 1) # ensure the program exited gracefully.
93 | self.assertEqual(len(self.stdout), 0) # no output
94 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
95 |
96 | def test_invalid_option(self):
97 | """$ pdb_keepcoord -A data/dummy.pdb"""
98 |
99 | sys.argv = ['', '-A', os.path.join(data_dir, 'dummy.pdb')]
100 |
101 | self.exec_module()
102 |
103 | self.assertEqual(self.retcode, 1)
104 | self.assertEqual(len(self.stdout), 0)
105 | self.assertEqual(self.stderr[0][:36],
106 | "ERROR!! Script takes 1 argument, not")
107 |
108 |
109 | if __name__ == '__main__':
110 | from config import test_dir
111 |
112 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
113 | sys.path.insert(0, mpath) # so we load dev files before any installation
114 |
115 | unittest.main()
116 |
--------------------------------------------------------------------------------
/tests/test_pdb_merge.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_merge`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_merge'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_merge data/dummy.pdb"""
58 |
59 | # Simulate input
60 | sys.argv = ['', os.path.join(data_dir, 'dummy.pdb'),
61 | os.path.join(data_dir, 'dummy.pdb')]
62 |
63 | # Execute the script
64 | self.exec_module()
65 |
66 | # Validate results
67 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
68 | self.assertEqual(len(self.stdout), 408) # no lines deleted
69 | self.assertEqual(len(self.stderr), 0) # no errors
70 |
71 | def test_file_not_found(self):
72 | """$ pdb_merge not_existing.pdb"""
73 |
74 | # Error (file not found)
75 | afile = os.path.join(data_dir, 'not_existing.pdb')
76 | sys.argv = ['', afile]
77 |
78 | # Execute the script
79 | self.exec_module()
80 |
81 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
82 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
83 | self.assertEqual(self.stderr[0][:22],
84 | "ERROR!! File not found") # proper error message
85 |
86 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
87 | def test_helptext(self):
88 | """$ pdb_merge"""
89 |
90 | sys.argv = ['']
91 |
92 | # Execute the script
93 | self.exec_module()
94 |
95 | self.assertEqual(self.retcode, 1)
96 | self.assertEqual(len(self.stdout), 0) # no output
97 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
98 |
99 |
100 | if __name__ == '__main__':
101 | from config import test_dir
102 |
103 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
104 | sys.path.insert(0, mpath) # so we load dev files before any installation
105 |
106 | unittest.main()
107 |
--------------------------------------------------------------------------------
/tests/test_pdb_mkensemble.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_mkensemble`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_mkensemble'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_mkensemble data/dummy.pdb"""
58 |
59 | # Simulate input
60 | sys.argv = ['', os.path.join(data_dir, 'dummy.pdb'),
61 | os.path.join(data_dir, 'dummy.pdb')]
62 |
63 | # Execute the script
64 | self.exec_module()
65 |
66 | # Validate results
67 | self.assertEqual(self.retcode, 0)
68 | self.assertEqual(len(self.stdout), 385)
69 | self.assertEqual(len(self.stderr), 0)
70 |
71 | def test_default_multiple(self):
72 | """$ pdb_mkensemble data/dummy.pdb x20"""
73 |
74 | # Simulate input
75 | args = [os.path.join(data_dir, 'dummy.pdb') for _ in range(20)]
76 | sys.argv = [''] + args
77 |
78 | # Execute the script
79 | self.exec_module()
80 |
81 | # Validate results
82 | self.assertEqual(self.retcode, 0)
83 | self.assertEqual(len(self.stdout), 3823)
84 | self.assertEqual(len(self.stderr), 0)
85 |
86 | # Validate MODEL lines
87 | model_no = [
88 | int(line[10:14])
89 | for line in self.stdout
90 | if line.startswith('MODEL')
91 | ]
92 | model_no = sorted(set(model_no))
93 | n_models = len(model_no)
94 | self.assertEqual(n_models, 20)
95 | self.assertEqual(model_no, list(range(1, 21)))
96 |
97 | def test_file_not_found(self):
98 | """$ pdb_mkensemble not_existing.pdb"""
99 |
100 | # Error (file not found)
101 | afile = os.path.join(data_dir, 'not_existing.pdb')
102 | sys.argv = ['', afile]
103 |
104 | # Execute the script
105 | self.exec_module()
106 |
107 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
108 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
109 | self.assertEqual(self.stderr[0][:22],
110 | "ERROR!! File not found") # proper error message
111 |
112 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
113 | def test_helptext(self):
114 | """$ pdb_mkensemble"""
115 |
116 | sys.argv = ['']
117 |
118 | # Execute the script
119 | self.exec_module()
120 |
121 | self.assertEqual(self.retcode, 1)
122 | self.assertEqual(len(self.stdout), 0) # no output
123 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
124 |
125 |
126 | if __name__ == '__main__':
127 | from config import test_dir
128 |
129 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
130 | sys.path.insert(0, mpath) # so we load dev files before any installation
131 |
132 | unittest.main()
133 |
--------------------------------------------------------------------------------
/tests/test_pdb_occ.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_occ`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_occ'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_occ data/dummy.pdb"""
58 |
59 | # Simulate input
60 | # pdb_occ dummy.pdb
61 | sys.argv = ['', os.path.join(data_dir, 'dummy.pdb')]
62 |
63 | # Execute the script
64 | self.exec_module()
65 |
66 | # Validate results
67 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
68 | self.assertEqual(len(self.stdout), 204) # no lines deleted
69 | self.assertEqual(len(self.stderr), 0) # no errors
70 |
71 | records = (('ATOM', 'HETATM'))
72 | occ_list = [l[54:60] for l in self.stdout if l.startswith(records)]
73 | unique_occ = list(set(map(float, occ_list)))
74 | self.assertEqual(unique_occ, [1.0]) # all occ values changed
75 |
76 | def test_two_options(self):
77 | """$ pdb_occ -0.5 data/dummy.pdb"""
78 |
79 | sys.argv = ['', '-0.5', os.path.join(data_dir, 'dummy.pdb')]
80 |
81 | self.exec_module()
82 |
83 | self.assertEqual(self.retcode, 0)
84 | self.assertEqual(len(self.stdout), 204)
85 | self.assertEqual(len(self.stderr), 0)
86 |
87 | records = (('ATOM', 'HETATM'))
88 | occ_list = [l[54:60] for l in self.stdout if l.startswith(records)]
89 | unique_occ = list(set(map(float, occ_list)))
90 | self.assertEqual(unique_occ, [0.5])
91 |
92 | def test_file_not_found(self):
93 | """$ pdb_occ not_existing.pdb"""
94 |
95 | afile = os.path.join(data_dir, 'not_existing.pdb')
96 | sys.argv = ['', '-1.0', afile]
97 |
98 | self.exec_module()
99 |
100 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
101 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
102 | self.assertEqual(self.stderr[0][:22],
103 | "ERROR!! File not found") # proper error message
104 |
105 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
106 | def test_file_missing(self):
107 | """$ pdb_occ -1.0"""
108 |
109 | sys.argv = ['', '-1.0']
110 |
111 | self.exec_module()
112 |
113 | self.assertEqual(self.retcode, 1)
114 | self.assertEqual(len(self.stdout), 0) # no output
115 | self.assertEqual(self.stderr[0],
116 | "ERROR!! No data to process!")
117 |
118 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
119 | def test_helptext(self):
120 | """$ pdb_occ"""
121 |
122 | sys.argv = ['']
123 |
124 | self.exec_module()
125 |
126 | self.assertEqual(self.retcode, 1) # ensure the program exited gracefully.
127 | self.assertEqual(len(self.stdout), 0) # no output
128 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
129 |
130 | def test_invalid_option(self):
131 | """$ pdb_occ -A data/dummy.pdb"""
132 |
133 | sys.argv = ['', '-A', os.path.join(data_dir, 'dummy.pdb')]
134 |
135 | self.exec_module()
136 |
137 | self.assertEqual(self.retcode, 1)
138 | self.assertEqual(len(self.stdout), 0)
139 | self.assertEqual(self.stderr[0][:47],
140 | "ERROR!! You provided an invalid occupancy value")
141 |
142 | def test_not_an_option(self):
143 | """$ pdb_occ 20 data/dummy.pdb"""
144 |
145 | sys.argv = ['', '20', os.path.join(data_dir, 'dummy.pdb')]
146 |
147 | self.exec_module()
148 |
149 | self.assertEqual(self.retcode, 1)
150 | self.assertEqual(len(self.stdout), 0)
151 | self.assertEqual(self.stderr[0],
152 | "ERROR! First argument is not an option: '20'")
153 |
154 |
155 | if __name__ == '__main__':
156 | from config import test_dir
157 |
158 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
159 | sys.path.insert(0, mpath) # so we load dev files before any installation
160 |
161 | unittest.main()
162 |
--------------------------------------------------------------------------------
/tests/test_pdb_seg.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_seg`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_seg'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_seg data/dummy.pdb"""
58 |
59 | # Simulate input
60 | sys.argv = ['', os.path.join(data_dir, 'dummy.pdb')]
61 |
62 | # Execute the script
63 | self.exec_module()
64 |
65 | # Validate results
66 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
67 | self.assertEqual(len(self.stdout), 204) # no lines deleted
68 | self.assertEqual(len(self.stderr), 0) # no errors
69 |
70 | records = (('ATOM', 'HETATM'))
71 | seg_ids = [l[72:76] for l in self.stdout if l.startswith(records)]
72 | unique_seg_ids = list(set(seg_ids))
73 | self.assertEqual(unique_seg_ids, [' '])
74 |
75 | def test_two_options(self):
76 | """$ pdb_seg -X data/dummy.pdb"""
77 |
78 | sys.argv = ['', '-X', os.path.join(data_dir, 'dummy.pdb')]
79 |
80 | self.exec_module()
81 |
82 | self.assertEqual(self.retcode, 0)
83 | self.assertEqual(len(self.stdout), 204)
84 | self.assertEqual(len(self.stderr), 0)
85 |
86 | records = (('ATOM', 'HETATM'))
87 | seg_ids = [l[72:76] for l in self.stdout if l.startswith(records)]
88 | unique_seg_ids = list(set(seg_ids))
89 | self.assertEqual(unique_seg_ids, ['X '])
90 |
91 | def test_file_not_found(self):
92 | """$ pdb_seg -A not_existing.pdb"""
93 |
94 | afile = os.path.join(data_dir, 'not_existing.pdb')
95 | sys.argv = ['', '-A', afile]
96 |
97 | self.exec_module()
98 |
99 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
100 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
101 | self.assertEqual(self.stderr[0][:22],
102 | "ERROR!! File not found") # proper error message
103 |
104 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
105 | def test_file_missing(self):
106 | """$ pdb_seg -A"""
107 |
108 | sys.argv = ['', '-A']
109 |
110 | self.exec_module()
111 |
112 | self.assertEqual(self.retcode, 1)
113 | self.assertEqual(len(self.stdout), 0) # no output
114 | self.assertEqual(self.stderr[0],
115 | "ERROR!! No data to process!")
116 |
117 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
118 | def test_helptext(self):
119 | """$ pdb_seg"""
120 |
121 | sys.argv = ['']
122 |
123 | self.exec_module()
124 |
125 | self.assertEqual(self.retcode, 1) # ensure the program exited gracefully.
126 | self.assertEqual(len(self.stdout), 0) # no output
127 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
128 |
129 | def test_invalid_option(self):
130 | """$ pdb_seg -AHASX data/dummy.pdb"""
131 |
132 | sys.argv = ['', '-AHASX', os.path.join(data_dir, 'dummy.pdb')]
133 |
134 | self.exec_module()
135 |
136 | self.assertEqual(self.retcode, 1)
137 | self.assertEqual(len(self.stdout), 0)
138 | self.assertEqual(self.stderr[0][:47],
139 | "ERROR!! Segment id must be max. four characters")
140 |
141 | def test_not_an_option(self):
142 | """$ pdb_seg A data/dummy.pdb"""
143 |
144 | sys.argv = ['', 'A', os.path.join(data_dir, 'dummy.pdb')]
145 |
146 | self.exec_module()
147 |
148 | self.assertEqual(self.retcode, 1)
149 | self.assertEqual(len(self.stdout), 0)
150 | self.assertEqual(self.stderr[0],
151 | "ERROR! First argument is not an option: 'A'")
152 |
153 |
154 | if __name__ == '__main__':
155 | from config import test_dir
156 |
157 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
158 | sys.path.insert(0, mpath) # so we load dev files before any installation
159 |
160 | unittest.main()
161 |
--------------------------------------------------------------------------------
/tests/test_pdb_selelem.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_selelem`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_selelem'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_one_option(self):
57 | """$ pdb_selelem -C data/dummy.pdb"""
58 |
59 | # Simulate input
60 | # pdb_selelem dummy.pdb
61 | sys.argv = ['', '-C', os.path.join(data_dir, 'dummy.pdb')]
62 |
63 | # Execute the script
64 | self.exec_module()
65 |
66 | # Validate results
67 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
68 | self.assertEqual(len(self.stdout), 60) # selected C
69 | self.assertEqual(len(self.stderr), 0) # no errors
70 |
71 | def test_multiple(self):
72 | """
73 | $ pdb_selelem -C,O data/dummy.pdb
74 | """
75 |
76 | sys.argv = ['', '-C,O', os.path.join(data_dir, 'dummy.pdb')]
77 |
78 | self.exec_module()
79 |
80 | self.assertEqual(self.retcode, 0)
81 | self.assertEqual(len(self.stdout), 75)
82 | self.assertEqual(len(self.stderr), 0)
83 |
84 | def test_file_not_found(self):
85 | """$ pdb_selelem not_existing.pdb"""
86 |
87 | afile = os.path.join(data_dir, 'not_existing.pdb')
88 | sys.argv = ['', afile]
89 |
90 | self.exec_module()
91 |
92 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
93 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
94 | self.assertEqual(self.stderr[0][:22],
95 | "ERROR!! File not found") # proper error message
96 |
97 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
98 | def test_file_missing(self):
99 | """$ pdb_selelem -C"""
100 |
101 | sys.argv = ['', '-C']
102 |
103 | self.exec_module()
104 |
105 | self.assertEqual(self.retcode, 1)
106 | self.assertEqual(len(self.stdout), 0) # no output
107 | self.assertEqual(self.stderr[0],
108 | "ERROR!! No data to process!")
109 |
110 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
111 | def test_helptext(self):
112 | """$ pdb_selelem"""
113 |
114 | sys.argv = ['']
115 |
116 | self.exec_module()
117 |
118 | self.assertEqual(self.retcode, 1) # ensure the program exited gracefully.
119 | self.assertEqual(len(self.stdout), 0) # no output
120 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
121 |
122 | def test_invalid_option(self):
123 | """$ pdb_selelem data/dummy.pdb"""
124 |
125 | sys.argv = ['', os.path.join(data_dir, 'dummy.pdb')]
126 |
127 | self.exec_module()
128 |
129 | self.assertEqual(self.retcode, 1)
130 | self.assertEqual(len(self.stdout), 0)
131 | self.assertEqual(self.stderr[0][:37],
132 | "ERROR!! Element set cannot be empty")
133 |
134 | def test_invalid_option_2(self):
135 | """$ pdb_selelem -ABC data/dummy.pdb"""
136 |
137 | sys.argv = ['', '-ABC', os.path.join(data_dir, 'dummy.pdb')]
138 |
139 | self.exec_module()
140 |
141 | self.assertEqual(self.retcode, 1)
142 | self.assertEqual(len(self.stdout), 0)
143 | self.assertEqual(self.stderr[0][:38],
144 | "ERROR!! Element name is invalid: 'ABC'")
145 |
146 | def test_not_an_option(self):
147 | """$ pdb_selelem 20 data/dummy.pdb"""
148 |
149 | sys.argv = ['', '20', os.path.join(data_dir, 'dummy.pdb')]
150 |
151 | self.exec_module()
152 |
153 | self.assertEqual(self.retcode, 1)
154 | self.assertEqual(len(self.stdout), 0)
155 | self.assertEqual(self.stderr[0],
156 | "ERROR! First argument is not an option: '20'")
157 |
158 |
159 | if __name__ == '__main__':
160 | from config import test_dir
161 |
162 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
163 | sys.path.insert(0, mpath) # so we load dev files before any installation
164 |
165 | unittest.main()
166 |
--------------------------------------------------------------------------------
/tests/test_pdb_selhetatm.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_selhetatm`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_selhetatm'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_selhetatm data/dummy.pdb"""
58 |
59 | # Simulate input
60 | # pdb_selhetatm dummy.pdb
61 | sys.argv = ['', os.path.join(data_dir, 'dummy.pdb')]
62 |
63 | # Execute the script
64 | self.exec_module()
65 |
66 | # Validate results
67 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
68 | self.assertEqual(len(self.stdout), 10) # 9 HETATM records + 1 CONECT
69 | self.assertEqual(len(self.stderr), 0) # no errors
70 |
71 |
72 | def test_file_not_found(self):
73 | """$ pdb_selhetatm not_existing.pdb"""
74 |
75 | afile = os.path.join(data_dir, 'not_existing.pdb')
76 | sys.argv = ['', afile]
77 |
78 | self.exec_module()
79 |
80 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
81 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
82 | self.assertEqual(self.stderr[0][:22],
83 | "ERROR!! File not found") # proper error message
84 |
85 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
86 | def test_helptext(self):
87 | """$ pdb_selhetatm"""
88 |
89 | sys.argv = ['']
90 |
91 | self.exec_module()
92 |
93 | self.assertEqual(self.retcode, 1) # ensure the program exited gracefully.
94 | self.assertEqual(len(self.stdout), 0) # no output
95 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
96 |
97 | def test_invalid_option(self):
98 | """$ pdb_selhetatm -A data/dummy.pdb"""
99 |
100 | sys.argv = ['', '-A', os.path.join(data_dir, 'dummy.pdb')]
101 |
102 | self.exec_module()
103 |
104 | self.assertEqual(self.retcode, 1)
105 | self.assertEqual(len(self.stdout), 0)
106 | self.assertEqual(self.stderr[0][:36],
107 | "ERROR!! Script takes 1 argument, not")
108 |
109 |
110 | if __name__ == '__main__':
111 | from config import test_dir
112 |
113 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
114 | sys.path.insert(0, mpath) # so we load dev files before any installation
115 |
116 | unittest.main()
117 |
--------------------------------------------------------------------------------
/tests/test_pdb_tofasta.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_tofasta`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_tofasta'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_tofasta data/dummy.pdb"""
58 |
59 | fpath = os.path.join(data_dir, 'dummy.pdb')
60 | sys.argv = ['', fpath]
61 |
62 | # Execute the script
63 | self.exec_module()
64 |
65 | # Validate results
66 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
67 | self.assertEqual(len(self.stdout), 2)
68 | self.assertEqual(len(self.stderr), 0) # no errors
69 |
70 | self.assertEqual(self.stdout, ['>1DUM|ABCD', 'REANREREMXXXXXXXXXX'])
71 |
72 | def test_headerless(self):
73 | """$ pdb_tofasta data/dummy_nohead.pdb"""
74 |
75 | fpath = os.path.join(data_dir, 'dummy_nohead.pdb')
76 | sys.argv = ['', fpath]
77 |
78 | # Execute the script
79 | self.exec_module()
80 |
81 | # Validate results
82 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
83 | self.assertEqual(len(self.stdout), 2)
84 | self.assertEqual(len(self.stderr), 0) # no errors
85 |
86 | self.assertEqual(self.stdout, ['>PDB|ABCD', 'REANREREMXXXXXXXXXX'])
87 |
88 | def test_multi(self):
89 | """$ pdb_tofasta -multi data/dummy.pdb"""
90 |
91 | fpath = os.path.join(data_dir, 'dummy.pdb')
92 | sys.argv = ['', '-multi', fpath]
93 |
94 | # Execute the script
95 | self.exec_module()
96 |
97 | # Validate results
98 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
99 | self.assertEqual(len(self.stdout), 14)
100 | self.assertEqual(len(self.stderr), 0) # no errors
101 |
102 | self.assertEqual(self.stdout, ['>1DUM|B', 'REA',
103 | '>1DUM|A', 'NRE',
104 | '>1DUM|C', 'REM',
105 | '>1DUM|D', 'X',
106 | '>1DUM|A', 'XXX',
107 | '>1DUM|B', 'X',
108 | '>1DUM|C', 'XXXXX'])
109 |
110 | def test_file_not_found(self):
111 | """$ pdb_tofasta not_existing.pdb"""
112 |
113 | # Error (file not found)
114 | afile = os.path.join(data_dir, 'not_existing.pdb')
115 | sys.argv = ['', afile]
116 |
117 | # Execute the script
118 | self.exec_module()
119 |
120 | self.assertEqual(self.retcode, 1)
121 | self.assertEqual(len(self.stdout), 0)
122 | self.assertEqual(self.stderr[0][:22],
123 | "ERROR!! File not found")
124 |
125 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
126 | def test_helptext(self):
127 | """$ pdb_tofasta"""
128 |
129 | sys.argv = ['']
130 |
131 | # Execute the script
132 | self.exec_module()
133 |
134 | self.assertEqual(self.retcode, 1)
135 | self.assertEqual(len(self.stdout), 0)
136 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
137 |
138 | def test_invalid_option(self):
139 | """$ pdb_tofasta -A data/dummy.pdb"""
140 |
141 | sys.argv = ['', '-A', os.path.join(data_dir, 'dummy.pdb')]
142 |
143 | # Execute the script
144 | self.exec_module()
145 |
146 | self.assertEqual(self.retcode, 1)
147 | self.assertEqual(len(self.stdout), 0)
148 | self.assertEqual(self.stderr[0][:36],
149 | "ERROR!! You provided an invalid opti")
150 |
151 |
152 | if __name__ == '__main__':
153 | from config import test_dir
154 |
155 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
156 | sys.path.insert(0, mpath) # so we load dev files before any installation
157 |
158 | unittest.main()
159 |
--------------------------------------------------------------------------------
/tests/test_pdb_uniqname.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2020 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_uniqname`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_uniqname'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_uniqname data/hetatm.pdb"""
58 |
59 | # Simulate input
60 | sys.argv = ['', os.path.join(data_dir, 'hetatm.pdb')]
61 |
62 | # Execute the script
63 | self.exec_module()
64 |
65 | # Validate results
66 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
67 | self.assertEqual(len(self.stdout), 19) # no lines deleted
68 | self.assertEqual(len(self.stderr), 0) # no errors
69 |
70 | records = ('ATOM', 'HETATM')
71 | atnames = [l[12:16] for l in self.stdout if l.startswith(records)]
72 |
73 | self.assertEqual(
74 | atnames,
75 | [
76 | ' C1 ', ' C2 ', ' H1 ', ' C3 ',
77 | 'CA1 ', 'CA2 ', ' H1 ', ' H2 ',
78 | ' C1 ', ' C2 ', ' H ', ' H '
79 | ]
80 | )
81 |
82 | def test_error(self):
83 | """$ pdb_uniqname data/hetatm_bad.pdb"""
84 |
85 | # Simulate input
86 | sys.argv = ['', os.path.join(data_dir, 'hetatm_bad.pdb')]
87 |
88 | # Execute the script
89 | self.exec_module()
90 |
91 | # Validate results
92 | self.assertEqual(self.retcode, 1) # ensure the program threw an error
93 | self.assertEqual(len(self.stdout), 0) # no output
94 |
95 | self.assertEqual(self.stderr[0][:22],
96 | "ERROR!! No element fou") # proper error message
97 |
98 | def test_file_not_found(self):
99 | """$ pdb_uniqname not_existing.pdb"""
100 |
101 | afile = os.path.join(data_dir, 'not_existing.pdb')
102 | sys.argv = ['', afile]
103 |
104 | self.exec_module()
105 |
106 | self.assertEqual(self.retcode, 1) # exit code is 1 (error)
107 | self.assertEqual(len(self.stdout), 0) # nothing written to stdout
108 | self.assertEqual(self.stderr[0][:22],
109 | "ERROR!! File not found") # proper error message
110 |
111 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
112 | def test_file_missing(self):
113 | """$ pdb_uniqname"""
114 |
115 | sys.argv = ['']
116 |
117 | self.exec_module()
118 |
119 | self.assertEqual(self.retcode, 1)
120 | self.assertEqual(len(self.stdout), 0) # no output
121 | self.assertEqual(self.stderr[1],
122 | self.module.__doc__.split("\n")[1])
123 |
124 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
125 | def test_helptext(self):
126 | """$ pdb_uniqname"""
127 |
128 | sys.argv = ['']
129 |
130 | self.exec_module()
131 |
132 | self.assertEqual(self.retcode, 1) # ensure the program exited gracefully.
133 | self.assertEqual(len(self.stdout), 0) # no output
134 | self.assertEqual(self.stderr[1],
135 | self.module.__doc__.split("\n")[1])
136 |
137 | def test_invalid_option(self):
138 | """$ pdb_uniqname -A data/dummy.pdb"""
139 |
140 | sys.argv = ['', '-A', os.path.join(data_dir, 'dummy.pdb')]
141 |
142 | self.exec_module()
143 |
144 | self.assertEqual(self.retcode, 1)
145 | self.assertEqual(len(self.stdout), 0)
146 | self.assertEqual(self.stderr[1],
147 | self.module.__doc__.split("\n")[1])
148 |
149 |
150 | if __name__ == '__main__':
151 | from config import test_dir
152 |
153 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
154 | sys.path.insert(0, mpath) # so we load dev files before any installation
155 |
156 | unittest.main()
157 |
--------------------------------------------------------------------------------
/tests/test_pdb_validate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_validate`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_validate'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_warnings(self):
57 | """$ pdb_validate data/dummy.pdb"""
58 |
59 | fpath = os.path.join(data_dir, 'dummy.pdb')
60 | sys.argv = ['', fpath]
61 |
62 | # Execute the script
63 | self.exec_module()
64 |
65 | # Validate results
66 | self.assertEqual(self.retcode, 1)
67 | self.assertEqual(len(self.stdout), 253)
68 | self.assertEqual(len(self.stderr), 0)
69 |
70 | # Count no. of line length warnings
71 | n_warn = len([l for l in self.stdout if 'is short' in l])
72 | self.assertEqual(n_warn, 70)
73 |
74 | self.assertEqual(self.stdout[-4],
75 | "[!] Line 203 is short: 26 < 80")
76 |
77 | def test_valid(self):
78 | """$ pdb_validate data/ensemble_OK.pdb"""
79 |
80 | fpath = os.path.join(data_dir, 'ensemble_OK.pdb')
81 | sys.argv = ['', fpath]
82 |
83 | # Execute the script
84 | self.exec_module()
85 |
86 | # Validate results
87 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
88 | self.assertEqual(len(self.stderr), 0)
89 | self.assertEqual(len(self.stdout), 1) # no errors
90 |
91 | self.assertEqual(self.stdout,
92 | ["It *seems* everything is OK."])
93 |
94 | def test_file_not_found(self):
95 | """$ pdb_validate not_existing.pdb"""
96 |
97 | # Error (file not found)
98 | afile = os.path.join(data_dir, 'not_existing.pdb')
99 | sys.argv = ['', afile]
100 |
101 | # Execute the script
102 | self.exec_module()
103 |
104 | self.assertEqual(self.retcode, 1)
105 | self.assertEqual(len(self.stdout), 0)
106 | self.assertEqual(self.stderr[0][:22],
107 | "ERROR!! File not found")
108 |
109 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
110 | def test_helptext(self):
111 | """$ pdb_validate"""
112 |
113 | sys.argv = ['']
114 |
115 | # Execute the script
116 | self.exec_module()
117 |
118 | self.assertEqual(self.retcode, 1)
119 | self.assertEqual(len(self.stdout), 0)
120 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
121 |
122 | def test_invalid_option(self):
123 | """$ pdb_validate -A data/dummy.pdb"""
124 |
125 | sys.argv = ['', '-A', os.path.join(data_dir, 'dummy.pdb')]
126 |
127 | # Execute the script
128 | self.exec_module()
129 |
130 | self.assertEqual(self.retcode, 1)
131 | self.assertEqual(len(self.stdout), 0)
132 | self.assertEqual(self.stderr[0][:36],
133 | "ERROR!! Script takes 1 argument, not")
134 |
135 |
136 | if __name__ == '__main__':
137 | from config import test_dir
138 |
139 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
140 | sys.path.insert(0, mpath) # so we load dev files before any installation
141 |
142 | unittest.main()
143 |
--------------------------------------------------------------------------------
/tests/test_pdb_wc.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Unit Tests for `pdb_wc`.
20 | """
21 |
22 | import os
23 | import sys
24 | import unittest
25 |
26 | from config import data_dir
27 | from utils import OutputCapture
28 |
29 |
30 | class TestTool(unittest.TestCase):
31 | """
32 | Generic class for testing tools.
33 | """
34 |
35 | def setUp(self):
36 | # Dynamically import the module
37 | name = 'pdbtools.pdb_wc'
38 | self.module = __import__(name, fromlist=[''])
39 |
40 | def exec_module(self):
41 | """
42 | Execs module.
43 | """
44 |
45 | with OutputCapture() as output:
46 | try:
47 | self.module.main()
48 | except SystemExit as e:
49 | self.retcode = e.code
50 |
51 | self.stdout = output.stdout
52 | self.stderr = output.stderr
53 |
54 | return
55 |
56 | def test_default(self):
57 | """$ pdb_wc data/dummy.pdb"""
58 |
59 | fpath = os.path.join(data_dir, 'dummy.pdb')
60 | sys.argv = ['', fpath]
61 |
62 | # Execute the script
63 | self.exec_module()
64 |
65 | # Validate results
66 | self.assertEqual(self.retcode, 0)
67 | self.assertEqual(len(self.stdout), 7)
68 | self.assertEqual(len(self.stderr), 0)
69 |
70 | self.assertEqual(self.stdout,
71 | ["No. models:\t1",
72 | "No. chains:\t4\t( 4.0/model)",
73 | "No. residues:\t10\t( 10.0/model)",
74 | "No. atoms:\t176\t( 176.0/model)",
75 | "No. HETATM:\t9",
76 | "Multiple Occ.:\tTrue",
77 | "Res. Inserts:\tFalse"])
78 |
79 | def test_single_option_1(self):
80 | """$ pdb_wc -m data/ensemble_OK.pdb"""
81 |
82 | fpath = os.path.join(data_dir, 'ensemble_OK.pdb')
83 | sys.argv = ['', '-m', fpath]
84 |
85 | # Execute the script
86 | self.exec_module()
87 |
88 | # Validate results
89 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
90 | self.assertEqual(len(self.stdout), 2)
91 | self.assertEqual(len(self.stderr), 0) # no errors
92 |
93 | self.assertEqual(self.stdout,
94 | ["No. models:\t2", "\t->\t1,2"])
95 |
96 | def test_single_option_2(self):
97 | """$ pdb_wc -c data/dummy.pdb"""
98 |
99 | fpath = os.path.join(data_dir, 'dummy.pdb')
100 | sys.argv = ['', '-c', fpath]
101 |
102 | # Execute the script
103 | self.exec_module()
104 |
105 | # Validate results
106 | self.assertEqual(self.retcode, 0) # ensure the program exited OK.
107 | self.assertEqual(len(self.stdout), 2)
108 | self.assertEqual(len(self.stderr), 0) # no errors
109 |
110 | self.assertEqual(self.stdout,
111 | ['No. chains:\t4\t( 4.0/model)', '\t->\tA,B,C,D'])
112 |
113 | def test_file_not_found(self):
114 | """$ pdb_wc not_existing.pdb"""
115 |
116 | # Error (file not found)
117 | afile = os.path.join(data_dir, 'not_existing.pdb')
118 | sys.argv = ['', afile]
119 |
120 | # Execute the script
121 | self.exec_module()
122 |
123 | self.assertEqual(self.retcode, 1)
124 | self.assertEqual(len(self.stdout), 0)
125 | self.assertEqual(self.stderr[0][:22],
126 | "ERROR!! File not found")
127 |
128 | @unittest.skipIf(os.getenv('SKIP_TTY_TESTS'), 'skip on GHA - no TTY')
129 | def test_helptext(self):
130 | """$ pdb_wc"""
131 |
132 | sys.argv = ['']
133 |
134 | # Execute the script
135 | self.exec_module()
136 |
137 | self.assertEqual(self.retcode, 1)
138 | self.assertEqual(len(self.stdout), 0)
139 | self.assertEqual(self.stderr, self.module.__doc__.split("\n")[:-1])
140 |
141 | def test_invalid_option(self):
142 | """$ pdb_wc -X data/dummy.pdb"""
143 |
144 | sys.argv = ['', '-X', os.path.join(data_dir, 'dummy.pdb')]
145 |
146 | # Execute the script
147 | self.exec_module()
148 |
149 | self.assertEqual(self.retcode, 1)
150 | self.assertEqual(len(self.stdout), 0)
151 | self.assertEqual(self.stderr[0][:36],
152 | "ERROR!! The following options are no")
153 |
154 |
155 | if __name__ == '__main__':
156 | from config import test_dir
157 |
158 | mpath = os.path.abspath(os.path.join(test_dir, '..'))
159 | sys.path.insert(0, mpath) # so we load dev files before any installation
160 |
161 | unittest.main()
162 |
--------------------------------------------------------------------------------
/tests/utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Copyright 2018 João Pedro Rodrigues
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | """
19 | Utility classes/functions for the Tests.
20 | """
21 |
22 | try:
23 | from cStringIO import StringIO
24 | except ImportError: # Py 3.x?
25 | from io import StringIO
26 |
27 | import sys
28 |
29 |
30 | class OutputCapture(object):
31 | """Context manager to capture output usually redirected to stdout.
32 |
33 | Use as:
34 | >>> with OutputCapture() as output:
35 | >>> ....run_stuff()
36 | >>> print(output) # list with lines
37 | """
38 |
39 | def __enter__(self):
40 | self.stdout = []
41 | self.stderr = []
42 |
43 | self._stdout = sys.stdout
44 | self._stderr = sys.stderr
45 | sys.stdout = self._stringout = StringIO()
46 | sys.stderr = self._stringerr = StringIO()
47 | return self
48 |
49 | def __exit__(self, *args):
50 |
51 | self.stdout.extend(self._stringout.getvalue().splitlines())
52 | self.stderr.extend(self._stringerr.getvalue().splitlines())
53 | del self._stringout # free up some memory
54 | del self._stringerr # free up some memory
55 | sys.stdout = self._stdout
56 | sys.stderr = self._stderr
57 |
--------------------------------------------------------------------------------