diff --git a/docs/release_notes.rst b/docs/release_notes.rst index e79ba32e..8ad90cb5 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -1,6 +1,10 @@ Release Notes ============= +v4.2.0 +------ +* Guard dxpy imports in utils so you can use stor without either dxpy or swift installed. (#143) + v4.1.0 ------ * Replace ``multiprocessing.ThreadPool`` with ``concurrent.futures.ThreadPoolExecutor`` diff --git a/pyproject.toml b/pyproject.toml index 90501510..97f97082 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "stor" -version = "4.1.0" +version = "4.2.0" description = "Cross-compatible API for accessing Posix and OBS storage systems" authors = ["Counsyl Inc. "] license = "MIT" diff --git a/stor/obs.py b/stor/obs.py index a071f4d8..7c30f6a8 100644 --- a/stor/obs.py +++ b/stor/obs.py @@ -3,10 +3,6 @@ import posixpath import sys -import dxpy -from swiftclient.service import SwiftError -from swiftclient.service import SwiftUploadObject - from stor.base import Path from stor.posix import PosixPath from stor import utils @@ -28,6 +24,17 @@ def wrapper(self, *args, **kwargs): return wrapper +try: + from swiftclient.service import SwiftUploadObject +except ImportError: # pragma: no cover + class SwiftUploadObject(object): + """Give 90% of the utility of SwiftUploadObject class without swiftclient!""" + def __init__(self, source, object_name=None, options=None): + self.source = source + self.object_name = object_name + self.options = options + + class OBSUploadObject(SwiftUploadObject): """ An upload object similar to swiftclient's SwiftUploadObject that allows the user @@ -41,6 +48,8 @@ def __init__(self, source, object_name, options=None): source (str): A path that specifies a source file. dest (str): A path that specifies a destination file name (full key) """ + from swiftclient.service import SwiftError + try: super(OBSUploadObject, self).__init__(source, object_name=object_name, options=options) except SwiftError as exc: @@ -452,6 +461,8 @@ def close(self): self.closed = True def _wait_on_close(self): + import dxpy + if isinstance(self._path, stor.dx.DXPath): wait_on_close = stor.settings.get()['dx']['wait_on_close'] if wait_on_close: diff --git a/stor/utils.py b/stor/utils.py index 01a6f073..2ef4de8f 100644 --- a/stor/utils.py +++ b/stor/utils.py @@ -8,9 +8,6 @@ from subprocess import check_call import tempfile -from dxpy.bindings import verify_string_dxid -from dxpy.exceptions import DXError - from stor import exceptions logger = logging.getLogger(__name__) @@ -190,8 +187,7 @@ def is_swift_path(p): Returns: bool: True if p is a Swift path, False otherwise. """ - from stor.swift import SwiftPath - return p.startswith(SwiftPath.drive) + return p.startswith('swift://') def is_filesystem_path(p): @@ -217,8 +213,7 @@ def is_s3_path(p): Returns bool: True if p is a S3 path, False otherwise. """ - from stor.s3 import S3Path - return p.startswith(S3Path.drive) + return p.startswith('s3://') def is_obs_path(p): @@ -244,8 +239,7 @@ def is_dx_path(p): Returns bool: True if p is a DX path, False otherwise. """ - from stor.dx import DXPath - return p.startswith(DXPath.drive) + return p.startswith('dx://') def is_valid_dxid(dxid, expected_classes): @@ -257,6 +251,9 @@ def is_valid_dxid(dxid, expected_classes): Returns bool: Whether given dxid is a valid path of one of expected_classes """ + from dxpy.bindings import verify_string_dxid + from dxpy.exceptions import DXError + try: return verify_string_dxid(dxid, expected_classes) is None except DXError: