diff --git a/.gitignore b/.gitignore index c3b06df8c7fd07fed9c5c001b4563673fa614471..b5df72d80a4c50f372b4c94c7060bddfde55feed 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ datastore_server/source/core-plugins openbis/source/core-plugins */bin/* **/.DS_Store -*__pycache__/ *$py.class **/.pydevproject **/.shredder diff --git a/pybis/src/python/.gitignore b/pybis/src/python/.gitignore index ac37d09509d50a26e266d8690a4fc0a3a67cd0d9..750c2aaaf7232dbd6614ad0a948fe1667767d521 100644 --- a/pybis/src/python/.gitignore +++ b/pybis/src/python/.gitignore @@ -5,3 +5,5 @@ dist deleteDatasetsAlreadyDeletedFromApplicationServerTaskLastSeen notebooks +*__pycache__/ +*.mypy_cache/ diff --git a/pybis/src/python/pybis/dataset.py b/pybis/src/python/pybis/dataset.py index b9dbcbab64d007d6ca52d15bff843ffce9c630ad..9325773d69b25c039208f43410df66f43cf003d6 100644 --- a/pybis/src/python/pybis/dataset.py +++ b/pybis/src/python/pybis/dataset.py @@ -1,6 +1,9 @@ import os +from functools import partialmethod +from pathlib import Path from threading import Thread from queue import Queue +from typing import Set, Optional, List from tabulate import tabulate from .openbis_object import OpenBisObject from .definitions import openbis_definitions, get_type_for_entity, get_fetchoption_for_entity @@ -121,7 +124,7 @@ class DataSet( 'add_attachment()', 'get_attachments()', 'download_attachments()', "get_files()", 'file_list', 'download()', - 'archive()', 'unarchive()' + 'archive()', 'unarchive()', 'sftp_source_dir', 'sftp_source_absolute_path', 'symlink_to()','is_symlink()','is_physical()' ] + super().__dir__() def __setattr__(self, name, value): @@ -163,9 +166,72 @@ class DataSet( except Exception: return None @property - def sftp_path(self): + def sftp_source_dir(self): return os.path.join(self.experiment.identifier[1:], self.permId) + + @property + def sftp_source_absolute_path(self): + # join mountpoint and source_dir + # if source_dir is absolute one has to remove "/" to be able to join it with mountpoint + # so far did not find way to achieve this in case they are pathlib.Path + if not self.openbis.is_mounted(): + raise ValueError("Not mounted.") + + source_dir = self.sftp_source_dir + if source_dir[0] == "/": + source_dir = source_dir[1:] + return os.path.join(self.openbis.mountpoint, source_dir) + + + def symlink_to(self, target_dir: str, replace_if_symlink_exists: bool = True): + # replace_if_symlink_exists will replace the the target_dir in case it is an existing symlink + + target_dir_path = Path(target_dir) + if target_dir_path.is_symlink() and replace_if_symlink_exists: + target_dir_path.unlink() + + target_dir_path.symlink_to(self.sftp_source_absolute_path, target_is_directory=True) + + + @staticmethod + def _file_set(target_dir: str) -> Set[str]: + target_dir_path = Path(target_dir) + return set( + str(el.relative_to(target_dir_path)) + for el in target_dir_path.glob("**/*") + if el.is_file() + ) + + + def _is_symlink_or_physical( + self, target_dir: str, what: str, expected_file_list: Optional[List[str]] = None, + ): + target_dir_path = Path(target_dir) + + target_file_set = self._file_set(target_dir) + + if expected_file_list is None: + source_file_set = set(self.file_list) + else: + source_file_set = set(expected_file_list) + + res = source_file_set.issubset(target_file_set) + if not res: + return res + elif what == "symlink": + return target_dir_path.exists() and target_dir_path.is_symlink() + elif what == "physical": + return target_dir_path.exists() and not target_dir_path.is_symlink() + else: + raise ValueError("Unexpected error") + + + is_symlink = partialmethod( + _is_symlink_or_physical, what="symlink", expected_file_list=None + ) + is_physical = partialmethod(_is_symlink_or_physical, what="physical") + def archive(self, remove_from_data_store=True): fetchopts = { "removeFromDataStore": remove_from_data_store, diff --git a/pybis/src/python/pybis/utils.py b/pybis/src/python/pybis/utils.py index 8c723056940c3111fe3dd53ba3aa5bd2b0bf42e4..24363d7ae95d3f9855a3c1674936f79f16740992 100644 --- a/pybis/src/python/pybis/utils.py +++ b/pybis/src/python/pybis/utils.py @@ -253,50 +253,4 @@ def extract_userId(user): elif isinstance(user, dict): return user['userId'] else: - return str(user) - - -def check_symlink(target_dir: str) -> Path: - # there are several options: basically resolvable symlink, non-resolvable symlink, does not exist at all or exists but it is not a symlink - - target_dir_path = Path(target_dir) - - if target_dir_path.is_symlink() and target_dir_path.exists(): - return target_dir_path.resolve() - elif target_dir_path.is_symlink() and not target_dir_path.exists(): - raise FileNotFoundError( - f"target_dir={target_dir} is non-resolvable symlink. No such file or directory: {target_dir_path.resolve()}" - ) - elif not target_dir_path.is_symlink() and not target_dir_path.exists(): - raise FileNotFoundError(f"target_dir={target_dir} does not exist") - else: - raise ValueError(f"target_dir={target_dir} exists but it is not a symlink") - - -def create_symlink( - target_dir: str, source_dir: str, mountpoint: str, replace_if_symlink: bool = True -): - # Path(mountpoint,ds.experiment.identifier[1:],ds.permId) - # replace_if_symlink will replace the the target_dir in case it is an existing symlink - - mountpoint_path = Path(mountpoint) - - # check mountpoint - if not mountpoint_path.is_mount(): - if not mountpoint_path.exists(): - raise FileNotFoundError(f"mountpoint={mountpoint} does not exist") - else: - raise ValueError(f"mountpoint={mountpoint} exists but it is not a mount") - - # join mountpoint and source_dir - # if source_dir is absolute we have to remove "/" to be able to join it with mountpoint - # I did not find any way to achieve this in case they are pathlib.Path - if source_dir[0] == "/": - source_dir = source_dir[1:] - source_dir_path = Path(mountpoint, source_dir) - - target_dir_path = Path(target_dir) - if target_dir_path.is_symlink() and replace_if_symlink: - target_dir_path.unlink() - - target_dir_path.symlink_to(source_dir_path, target_is_directory=True) \ No newline at end of file + return str(user) \ No newline at end of file diff --git a/pybis/src/python/setup.py b/pybis/src/python/setup.py index 2f9445da8d0d84d767e29137e4d175398dfd5001..ab75d770192f318243be04f70c57408fc1263940 100644 --- a/pybis/src/python/setup.py +++ b/pybis/src/python/setup.py @@ -28,7 +28,7 @@ setup( 'texttable', 'tabulate', ], - python_requires=">=3.3", + python_requires=">=3.6", classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: Apache Software License",