From 50f62c61fb90fc65f0412066138888034db06111 Mon Sep 17 00:00:00 2001 From: Thomas Holder Date: Sun, 10 Dec 2023 11:59:21 +0100 Subject: [PATCH] Refactor deprecated pkg_resources usage - Locate resources relative to `__file__` - Support egg/zip with `open_file_for_reading` --- propka/bonds.py | 9 +++++---- propka/input.py | 19 +++++++++++++++---- propka/lib.py | 4 ++-- tests/test_input.py | 29 +++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 tests/test_input.py diff --git a/propka/bonds.py b/propka/bonds.py index 10190db..396ec86 100644 --- a/propka/bonds.py +++ b/propka/bonds.py @@ -8,7 +8,7 @@ import logging import math import json -import pkg_resources +from pathlib import Path import propka.calculations from typing import TYPE_CHECKING @@ -35,6 +35,8 @@ class BondMaker: TODO - the documentation for this class needs to be improved. """ def __init__(self): + from propka.input import open_file_for_reading + # predefined bonding distances self.distances = {'S-S': DISULFIDE_DISTANCE, 'F-F': FLUORIDE_DISTANCE} @@ -51,9 +53,8 @@ def __init__(self): + [self.default_dist_squared]) self.max_sq_distance = max(distances) # protein bonding data - self.data_file_name = ( - pkg_resources.resource_filename(__name__, 'protein_bonds.json')) - with open(self.data_file_name, 'rt') as json_file: + self.data_file_name = Path(__file__).parent / 'protein_bonds.json' + with open_file_for_reading(self.data_file_name) as json_file: self.protein_bonds = json.load(json_file) self.intra_residue_backbone_bonds = {'N': ['CA'], 'CA': ['N', 'C'], 'C': ['CA', 'O'], 'O': ['C']} diff --git a/propka/input.py b/propka/input.py index c62ff84..e5658d8 100644 --- a/propka/input.py +++ b/propka/input.py @@ -10,10 +10,11 @@ :func:`get_atom_lines_from_input`) have been removed. """ import typing -from typing import Iterator, Tuple +from typing import Iterator, Tuple, Union import contextlib +import io +import zipfile from pathlib import Path -from pkg_resources import resource_filename from propka.lib import protein_precheck from propka.atom import Atom from propka.conformation_container import ConformationContainer @@ -34,6 +35,16 @@ def open_file_for_reading( input_file.seek(0) return contextlib.nullcontext(input_file) + input_file = Path(input_file) + + if not input_file.is_file(): + for p in input_file.parents: + if not zipfile.is_zipfile(p): + continue + zf = zipfile.ZipFile(p) + stream = zf.open(str(input_file.relative_to(p))) + return io.TextIOWrapper(stream) + return contextlib.closing(open(input_file, 'rt')) @@ -122,7 +133,7 @@ def read_molecule_file(filename: str, mol_container: MolecularContainer, stream= return mol_container -def read_parameter_file(input_file, parameters: Parameters) -> Parameters: +def read_parameter_file(input_file: Union[Path, str], parameters: Parameters) -> Parameters: """Read a parameter file. Args: @@ -133,7 +144,7 @@ def read_parameter_file(input_file, parameters: Parameters) -> Parameters: """ # try to locate the parameter file try: - ifile = resource_filename(__name__, input_file) + ifile = Path(__file__).parent / input_file input_ = open_file_for_reading(ifile) except (IOError, FileNotFoundError, ValueError, KeyError): input_ = open_file_for_reading(input_file) diff --git a/propka/lib.py b/propka/lib.py index 5a3d204..2140027 100644 --- a/propka/lib.py +++ b/propka/lib.py @@ -8,7 +8,7 @@ import sys import logging import argparse -import pkg_resources +from pathlib import Path _LOGGER = logging.getLogger(__name__) @@ -246,7 +246,7 @@ def build_parser(parser=None): "--version", action="version", version=f"%(prog)s {propka.__version__}") group.add_argument( "-p", "--parameters", dest="parameters", - default=pkg_resources.resource_filename(__name__, "propka.cfg"), + default=str(Path(__file__).parent / "propka.cfg"), help="set the parameter file [{default:s}]") try: group.add_argument( diff --git a/tests/test_input.py b/tests/test_input.py new file mode 100644 index 0000000..af8ed5a --- /dev/null +++ b/tests/test_input.py @@ -0,0 +1,29 @@ +import propka.input as m +import zipfile + + +def test_open_file_for_reading(tmp_path): + path = tmp_path / "tmp.txt" + path.write_text("One\nTwo\nThree\n") + # str + with m.open_file_for_reading(str(path)) as outer: + assert outer.read() == "One\nTwo\nThree\n" + assert outer.closed + # Path + with m.open_file_for_reading(path) as outer: + # TextIO + with m.open_file_for_reading(outer) as inner: + assert inner.readline() == "One\n" + assert not outer.closed + assert outer.readline() == "Two\n" + assert outer.closed + + +def test_open_file_for_reading__zipfile(tmp_path): + zippath = tmp_path / "tmp.zip" + arcname = "foo/bar.txt" + with zipfile.ZipFile(zippath, "w") as ziphandle: + ziphandle.writestr(arcname, "One\nTwo\nThree\n") + with m.open_file_for_reading(zippath / arcname) as outer: + assert outer.readline() == "One\n" + assert outer.closed