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

View File

@ -1 +1 @@
1.0.0
1.1.0

View File

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

View File

@ -65,10 +65,19 @@ class FileApi(BaseApi):
self._raise_response_error(response)
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)
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)
response = self.connection.post(url, data=data)
self._raise_response_error(response)

View File

@ -103,6 +103,15 @@ class FolderApi(BaseApi):
self._raise_response_error(response)
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]:
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
url = self.ENTRYPOINT + id + "/metadata/"

View File

@ -1,5 +1,6 @@
from mdrsclient.commands.chacl import ChaclCommand
from mdrsclient.commands.config import ConfigCommand
from mdrsclient.commands.cp import CpCommand
from mdrsclient.commands.download import DownloadCommand
from mdrsclient.commands.file_metadata import FileMetadataCommand
from mdrsclient.commands.labs import LabsCommand
@ -16,6 +17,7 @@ from mdrsclient.commands.whoami import WhoamiCommand
__all__ = [
"ConfigCommand",
"ChaclCommand",
"CpCommand",
"DownloadCommand",
"FileMetadataCommand",
"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.commands.base import BaseCommand
from mdrsclient.exceptions import IllegalArgumentException
from mdrsclient.models import File, FolderSimple
from mdrsclient.models import FolderSimple
class MvCommand(BaseCommand):
@ -58,9 +58,7 @@ class MvCommand(BaseCommand):
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:
d_file_dict = dataclasses.asdict(s_file) | {"name": normalize("NFC", d_basename)}
d_file = TypeAdapter(File).validate_python(d_file_dict)
file_api.move(d_file, d_parent_folder.id)
file_api.move(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:

View File

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