Skip to content

Commit e01ca72

Browse files
committed
Allow CLI-only recursion into file paths
provides an implementation that allows a CLI user to pass in an optional --recurse argument. * closes #19
1 parent af31e46 commit e01ca72

2 files changed

Lines changed: 67 additions & 1 deletion

File tree

dvuploader/cli.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import yaml
22
import typer
33

4+
from pathlib import Path
45
from pydantic import BaseModel
56
from typing import List, Optional
67
from dvuploader import DVUploader, File
8+
from dvuploader.utils import add_directory
79

810

911
class CliInput(BaseModel):
@@ -27,6 +29,29 @@ class CliInput(BaseModel):
2729

2830
app = typer.Typer()
2931

32+
def _enumerate_filepaths(filepaths: List[str], recurse: bool) -> List[File]:
33+
"""
34+
Take a list of filepaths and transform it into a list of File objects, optionally recursing into each of them.
35+
36+
Args:
37+
filepaths (List[str]): a list of files or paths for upload
38+
recurse (bool): whether to recurse into each given filepath
39+
40+
Returns:
41+
List[File]: A list of File objects representing the files extracted from all filepaths.
42+
43+
Raises:
44+
FileNotFoundError: If a filepath does not exist.
45+
IsADirectoryError: If recurse is False and a filepath points to a directory instead of a file.
46+
"""
47+
if not recurse:
48+
return [File(filepath=filepath) for filepath in filepaths]
49+
50+
files = []
51+
for filepath in filepaths:
52+
files.append(*add_directory(filepath) if Path(filepath).is_dir() else File(filepath=filepath))
53+
return files
54+
3055

3156
def _parse_yaml_config(path: str) -> CliInput:
3257
"""
@@ -50,6 +75,7 @@ def _validate_inputs(
5075
pid: str,
5176
dataverse_url: str,
5277
api_token: str,
78+
recurse: bool,
5379
config_path: Optional[str],
5480
) -> None:
5581
"""
@@ -62,17 +88,25 @@ def _validate_inputs(
6288
pid (str): Persistent identifier of the dataset
6389
dataverse_url (str): URL of the Dataverse instance
6490
api_token (str): API token for authentication
91+
recurse (bool): Whether to recurse into filepaths
6592
config_path (Optional[str]): Path to configuration file
6693
94+
6795
Raises:
6896
typer.BadParameter: If both config file and filepaths are specified
97+
typer.BadParameter: If both config file and recurse are specified
6998
typer.BadParameter: If neither config file nor required parameters are provided
7099
"""
71100
if config_path is not None and len(filepaths) > 0:
72101
raise typer.BadParameter(
73102
"Cannot specify both a JSON/YAML file and a list of filepaths."
74103
)
75104

105+
if config_path is not None and recurse:
106+
raise typer.BadParameter(
107+
"Cannot specify both a JSON/YAML file and recurse into filepaths."
108+
)
109+
76110
_has_meta_params = all(arg is not None for arg in [pid, dataverse_url, api_token])
77111
_has_config_file = config_path is not None
78112

@@ -94,6 +128,10 @@ def main(
94128
default=None,
95129
help="A list of filepaths to upload.",
96130
),
131+
recurse: Optional[bool] = typer.Option(
132+
default=False,
133+
help="Enable recursion into filepaths.",
134+
),
97135
pid: str = typer.Option(
98136
default=None,
99137
help="The persistent identifier of the Dataverse dataset.",
@@ -123,6 +161,7 @@ def main(
123161
124162
If using command line arguments, you must specify:
125163
- One or more filepaths to upload
164+
- (Optional) whether to recurse into the filepaths
126165
- The dataset's persistent identifier
127166
- A valid API token
128167
- The Dataverse repository URL
@@ -150,6 +189,7 @@ def main(
150189
pid=pid,
151190
dataverse_url=dataverse_url,
152191
api_token=api_token,
192+
recurse=recurse,
153193
config_path=config_path,
154194
)
155195

@@ -161,7 +201,7 @@ def main(
161201
api_token=api_token,
162202
dataverse_url=dataverse_url,
163203
persistent_id=pid,
164-
files=[File(filepath=filepath) for filepath in filepaths],
204+
files=_enumerate_filepaths(filepaths=filepaths, recurse=recurse),
165205
)
166206

167207
uploader = DVUploader(files=cli_input.files)

tests/unit/test_cli.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,32 @@ def test_kwarg_arg_input(self, credentials):
5757
)
5858
assert result.exit_code == 0
5959

60+
def test_recurse(self, credentials):
61+
# Arrange
62+
BASE_URL, API_TOKEN = credentials
63+
pid = create_dataset(
64+
parent="Root",
65+
server_url=BASE_URL,
66+
api_token=API_TOKEN,
67+
)
68+
69+
# Act
70+
result = runner.invoke(
71+
app,
72+
[
73+
"./tests/fixtures/create_dataset.json",
74+
"./tests/fixtures/add_dir_files",
75+
"--recurse",
76+
"--pid",
77+
pid,
78+
"--api-token",
79+
API_TOKEN,
80+
"--dataverse-url",
81+
BASE_URL,
82+
],
83+
)
84+
assert result.exit_code == 0
85+
6086
def test_yaml_input(self, credentials):
6187
# Arrange
6288
BASE_URL, API_TOKEN = credentials

0 commit comments

Comments
 (0)