add new command cp.

This commit is contained in:
Yoshihiro OKUMURA 2023-07-20 11:43:07 +09:00
parent 08d8a0626a
commit 4d87b55b40
Signed by: orrisroot
GPG Key ID: 470AA444C92904B2
9 changed files with 152 additions and 10 deletions

View File

@ -1,7 +1,9 @@
# mdrs-client-python # mdrs-client-python
The mdrs-client-python is python library and a command-line client for up- and downloading files to and from MDRS based repository. The mdrs-client-python is python library and a command-line client for up- and downloading files to and from MDRS based repository.
## Installing ## Installing
``` ```
pip install -e . pip install -e .
``` ```
@ -9,13 +11,17 @@ pip install -e .
## Example Usage ## Example Usage
### config create ### config create
Create remote host configuration Create remote host configuration
``` ```
$ mdrs config create neurodata https://neurodata.riken.jp/api $ mdrs config create neurodata https://neurodata.riken.jp/api
``` ```
### login ### login
Login to remote host Login to remote host
``` ```
$ mdrs login neurodata: $ mdrs login neurodata:
Username: (enter your login name) Username: (enter your login name)
@ -23,25 +29,33 @@ Password: (enter your password)
``` ```
### logout ### logout
Logout from remote host Logout from remote host
``` ```
$ mdrs logout neurodata: $ mdrs logout neurodata:
``` ```
### whoami ### whoami
Print current user name Print current user name
``` ```
$ mdrs whoami neurodata: $ mdrs whoami neurodata:
``` ```
### labs ### labs
List all laboratories List all laboratories
``` ```
$ mdrs labs neurodata: $ mdrs labs neurodata:
``` ```
### ls ### ls
List the folder contents List the folder contents
``` ```
$ mdrs ls neurodata:/NIU/Repository/ $ mdrs ls neurodata:/NIU/Repository/
$ mdrs ls -p PW_OPEN_PASSWORD neurodata:/NIU/Repository/PW_Open/ $ mdrs ls -p PW_OPEN_PASSWORD neurodata:/NIU/Repository/PW_Open/
@ -50,20 +64,26 @@ $ mdrs ls -J -r neurodata:/NIU/Repository/Dataset1/
``` ```
### mkdir ### mkdir
Create a new folder Create a new folder
``` ```
$ mdrs mkdir neurodata:/NIU/Repository/TEST $ mdrs mkdir neurodata:/NIU/Repository/TEST
``` ```
### upload ### upload
Upload the file or directory Upload the file or directory
``` ```
$ mdrs upload ./sample.dat neurodata:/NIU/Repository/TEST/ $ mdrs upload ./sample.dat neurodata:/NIU/Repository/TEST/
$ mdrs upload -r ./dataset neurodata:/NIU/Repository/TEST/ $ mdrs upload -r ./dataset neurodata:/NIU/Repository/TEST/
``` ```
### download ### download
Download the file or folder Download the file or folder
``` ```
$ mdrs download neurodata:/NIU/Repository/TEST/sample.dat ./ $ mdrs download neurodata:/NIU/Repository/TEST/sample.dat ./
$ mdrs download -r neurodata:/NIU/Repository/TEST/dataset/ ./ $ mdrs download -r neurodata:/NIU/Repository/TEST/dataset/ ./
@ -71,21 +91,36 @@ $ mdrs download -p PW_OPEN_PASSWORD neurodata:/NIU/Repository/PW_Open/Readme.dat
``` ```
### mv ### mv
Move or rename the file or folder Move or rename the file or folder
``` ```
$ mdrs move neurodata:/NIU/Repository/TEST/sample.dat neurodata:/NIU/Repository/TEST2/sample2.dat $ mdrs mv neurodata:/NIU/Repository/TEST/sample.dat neurodata:/NIU/Repository/TEST2/sample2.dat
$ mdrs move neurodata:/NIU/Repository/TEST/dataset neurodata:/NIU/Repository/TEST2/ $ mdrs mv neurodata:/NIU/Repository/TEST/dataset neurodata:/NIU/Repository/TEST2/
```
### cp
Copy the file and folder
```
$ mdrs cp neurodata:/NIU/Repository/TEST/sample.dat neurodata:/NIU/Repository/TEST2/sample2.dat
$ mdrs cp -r neurodata:/NIU/Repository/TEST/dataset neurodata:/NIU/Repository/TEST2/
``` ```
### rm ### rm
Remove the file or folder Remove the file or folder
``` ```
$ mdrs rm neurodata:/NIU/Repository/TEST2/sample2.dat $ mdrs rm neurodata:/NIU/Repository/TEST2/sample2.dat
$ mdrs rm -r neurodata:/NIU/Repository/TEST2/dataset $ mdrs rm -r neurodata:/NIU/Repository/TEST2/dataset
``` ```
### chacl ### chacl
Change the folder access level Change the folder access level
``` ```
$ mdrs chacl private neurodata:/NIU/Repository/Private $ mdrs chacl private neurodata:/NIU/Repository/Private
$ mdrs chacl cbs_open -r neurodata:/NIU/Repository/CBS_Open $ mdrs chacl cbs_open -r neurodata:/NIU/Repository/CBS_Open
@ -93,21 +128,27 @@ $ mdrs chacl pw_open -r -p FOLDER_PASSWORD neurodata:/NIU/Repository/PW_Open
``` ```
### metadata ### metadata
Get a folder metadata Get a folder metadata
``` ```
$ mdrs metadata neurodata:/NIU/Repository/TEST/ $ mdrs metadata neurodata:/NIU/Repository/TEST/
$ mdrs metadata -p PW_OPEN_PASSWORD neurodata:/NIU/Repository/PW_Open/ $ mdrs metadata -p PW_OPEN_PASSWORD neurodata:/NIU/Repository/PW_Open/
``` ```
### file-metadata ### file-metadata
Get the file metadata Get the file metadata
``` ```
$ mdrs file-metadata neurodata:/NIU/Repository/TEST/dataset/sample.dat $ mdrs file-metadata neurodata:/NIU/Repository/TEST/dataset/sample.dat
$ mdrs file-metadata -p PW_OPEN_PASSWORD neurodata:/NIU/Repository/PW_Open/Readme.txt $ mdrs file-metadata -p PW_OPEN_PASSWORD neurodata:/NIU/Repository/PW_Open/Readme.txt
``` ```
### help ### help
Show the help message and exit Show the help message and exit
``` ```
$ mdrs -h $ mdrs -h
``` ```

View File

@ -1 +1 @@
1.0.0 1.1.0

View File

@ -4,6 +4,7 @@ import sys
from mdrsclient.commands import ( from mdrsclient.commands import (
ChaclCommand, ChaclCommand,
ConfigCommand, ConfigCommand,
CpCommand,
DownloadCommand, DownloadCommand,
FileMetadataCommand, FileMetadataCommand,
LabsCommand, LabsCommand,
@ -36,6 +37,7 @@ def main() -> None:
UploadCommand.register(parsers) UploadCommand.register(parsers)
DownloadCommand.register(parsers) DownloadCommand.register(parsers)
MvCommand.register(parsers) MvCommand.register(parsers)
CpCommand.register(parsers)
RmCommand.register(parsers) RmCommand.register(parsers)
ChaclCommand.register(parsers) ChaclCommand.register(parsers)
MetadataCommand.register(parsers) MetadataCommand.register(parsers)

View File

@ -65,10 +65,19 @@ class FileApi(BaseApi):
self._raise_response_error(response) self._raise_response_error(response)
return True return True
def move(self, file: File, folder_id: str) -> bool: def move(self, file: File, folder_id: str, name: str) -> bool:
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name) # print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
url = self.ENTRYPOINT + file.id + "/move/" url = self.ENTRYPOINT + file.id + "/move/"
data: dict[str, str | int] = {"folder": folder_id, "name": file.name} data: dict[str, str | int] = {"folder": folder_id, "name": name}
token_check(self.connection)
response = self.connection.post(url, data=data)
self._raise_response_error(response)
return True
def copy(self, file: File, folder_id: str, name: str) -> bool:
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
url = self.ENTRYPOINT + file.id + "/copy/"
data: dict[str, str | int] = {"folder": folder_id, "name": name}
token_check(self.connection) token_check(self.connection)
response = self.connection.post(url, data=data) response = self.connection.post(url, data=data)
self._raise_response_error(response) self._raise_response_error(response)

View File

@ -103,6 +103,15 @@ class FolderApi(BaseApi):
self._raise_response_error(response) self._raise_response_error(response)
return True return True
def copy(self, folder: FolderSimple, folder_id: str, name: str) -> bool:
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
url = self.ENTRYPOINT + folder.id + "/copy/"
data: dict[str, str | int] = {"parent": folder_id, "name": name}
token_check(self.connection)
response = self.connection.post(url, data=data)
self._raise_response_error(response)
return True
def metadata(self, id: str) -> dict[str, Any]: def metadata(self, id: str) -> dict[str, Any]:
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name) # print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
url = self.ENTRYPOINT + id + "/metadata/" url = self.ENTRYPOINT + id + "/metadata/"

View File

@ -1,5 +1,6 @@
from mdrsclient.commands.chacl import ChaclCommand from mdrsclient.commands.chacl import ChaclCommand
from mdrsclient.commands.config import ConfigCommand from mdrsclient.commands.config import ConfigCommand
from mdrsclient.commands.cp import CpCommand
from mdrsclient.commands.download import DownloadCommand from mdrsclient.commands.download import DownloadCommand
from mdrsclient.commands.file_metadata import FileMetadataCommand from mdrsclient.commands.file_metadata import FileMetadataCommand
from mdrsclient.commands.labs import LabsCommand from mdrsclient.commands.labs import LabsCommand
@ -16,6 +17,7 @@ from mdrsclient.commands.whoami import WhoamiCommand
__all__ = [ __all__ = [
"ConfigCommand", "ConfigCommand",
"ChaclCommand", "ChaclCommand",
"CpCommand",
"DownloadCommand", "DownloadCommand",
"FileMetadataCommand", "FileMetadataCommand",
"LabsCommand", "LabsCommand",

78
mdrsclient/commands/cp.py Normal file
View File

@ -0,0 +1,78 @@
import os
from argparse import Namespace
from typing import Any
from unicodedata import normalize
from mdrsclient.api import FileApi, FolderApi
from mdrsclient.commands.base import BaseCommand
from mdrsclient.exceptions import IllegalArgumentException
class CpCommand(BaseCommand):
@classmethod
def register(cls, parsers: Any) -> None:
cp_parser = parsers.add_parser("cp", help="copy the file and folder")
cp_parser.add_argument(
"-r", "--recursive", help="copy folders and their contents recursive", action="store_true"
)
cp_parser.add_argument("src_path", help="source remote path (remote:/lab/path/src)")
cp_parser.add_argument("dest_path", help="destination remote path (remote:/lab/path/dest)")
cp_parser.set_defaults(func=cls.func)
@classmethod
def func(cls, args: Namespace) -> None:
src_path = str(args.src_path)
dest_path = str(args.dest_path)
is_recursive = bool(args.recursive)
cls.cp(src_path, dest_path, is_recursive)
@classmethod
def cp(cls, src_path: str, dest_path: str, is_recursive: bool) -> None:
(s_remote, s_laboratory_name, s_path) = cls._parse_remote_host_with_path(src_path)
(d_remote, d_laboratory_name, d_path) = cls._parse_remote_host_with_path(dest_path)
if s_remote != d_remote:
raise IllegalArgumentException("Remote host mismatched.")
if s_laboratory_name != d_laboratory_name:
raise IllegalArgumentException("Laboratory mismatched.")
s_path = s_path.rstrip("/")
s_dirname = os.path.dirname(s_path)
s_basename = os.path.basename(s_path)
if d_path.endswith("/"):
d_dirname = d_path
d_basename = s_basename
else:
d_dirname = os.path.dirname(d_path)
d_basename = os.path.basename(d_path)
connection = cls._create_connection(s_remote)
laboratory = cls._find_laboratory(connection, s_laboratory_name)
s_parent_folder = cls._find_folder(connection, laboratory, s_dirname)
d_parent_folder = cls._find_folder(connection, laboratory, d_dirname)
s_file = s_parent_folder.find_file(s_basename)
if s_file is not None:
# source is file
d_file = d_parent_folder.find_file(d_basename)
if d_file is not None:
raise IllegalArgumentException(f"File `{d_basename}` already exists.")
d_sub_folder = d_parent_folder.find_sub_folder(d_basename)
if d_sub_folder is not None:
raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{d_path}`.")
file_api = FileApi(connection)
if s_parent_folder.id != d_parent_folder.id or d_basename != s_basename:
file_api.copy(s_file, d_parent_folder.id, normalize("NFC", d_basename))
else:
s_folder = s_parent_folder.find_sub_folder(s_basename)
if s_folder is None:
raise IllegalArgumentException(f"File or folder `{s_basename}` not found.")
# source is folder
if not is_recursive:
raise IllegalArgumentException(f"Cannot copy `{s_path}`: Is a folder.")
if d_parent_folder.find_file(d_basename) is not None:
raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{s_path}`.")
d_folder = d_parent_folder.find_sub_folder(d_basename)
if d_folder is not None:
if d_folder.id == s_folder.id:
raise IllegalArgumentException(f"`{s_path}` and `{s_path}` are the same folder.")
raise IllegalArgumentException(f"Cannot move `{s_path}` to `{d_path}`: Folder not empty.")
folder_api = FolderApi(connection)
if s_parent_folder.id != d_parent_folder.id or s_basename != d_basename:
folder_api.copy(s_folder, d_parent_folder.id, normalize("NFC", d_basename))

View File

@ -9,7 +9,7 @@ from pydantic import TypeAdapter
from mdrsclient.api import FileApi, FolderApi from mdrsclient.api import FileApi, FolderApi
from mdrsclient.commands.base import BaseCommand from mdrsclient.commands.base import BaseCommand
from mdrsclient.exceptions import IllegalArgumentException from mdrsclient.exceptions import IllegalArgumentException
from mdrsclient.models import File, FolderSimple from mdrsclient.models import FolderSimple
class MvCommand(BaseCommand): class MvCommand(BaseCommand):
@ -58,9 +58,7 @@ class MvCommand(BaseCommand):
raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{d_path}`.") raise IllegalArgumentException(f"Cannot overwrite non-folder `{d_basename}` with folder `{d_path}`.")
file_api = FileApi(connection) file_api = FileApi(connection)
if s_parent_folder.id != d_parent_folder.id or d_basename != s_basename: if s_parent_folder.id != d_parent_folder.id or d_basename != s_basename:
d_file_dict = dataclasses.asdict(s_file) | {"name": normalize("NFC", d_basename)} file_api.move(s_file, d_parent_folder.id, normalize("NFC", d_basename))
d_file = TypeAdapter(File).validate_python(d_file_dict)
file_api.move(d_file, d_parent_folder.id)
else: else:
s_folder = s_parent_folder.find_sub_folder(s_basename) s_folder = s_parent_folder.find_sub_folder(s_basename)
if s_folder is None: if s_folder is None:

View File

@ -1,10 +1,13 @@
import platform import platform
import threading import threading
from io import BufferedReader from io import BufferedReader
from typing import TypedDict, Unpack from typing import TypedDict
from requests import Response, Session from requests import Response, Session
# Unpack is new in 3.11
from typing_extensions import Unpack
from mdrsclient.__version__ import __version__ from mdrsclient.__version__ import __version__
from mdrsclient.cache import CacheFile from mdrsclient.cache import CacheFile
from mdrsclient.exceptions import MissingConfigurationException from mdrsclient.exceptions import MissingConfigurationException