Skip to content

Commit 7ca980c

Browse files
authored
Merge pull request #28 from BerkeleyLibrary/recurse
Allow CLI-only recursion into file paths
2 parents af31e46 + b2a396c commit 7ca980c

2 files changed

Lines changed: 70 additions & 5 deletions

File tree

dvuploader/cli.py

Lines changed: 44 additions & 5 deletions
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 fp in filepaths:
52+
files.extend(add_directory(fp) if Path(fp).is_dir() else [File(filepath=fp)])
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,16 +88,23 @@ 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
6794
Raises:
6895
typer.BadParameter: If both config file and filepaths are specified
96+
typer.BadParameter: If both config file and recurse are specified
6997
typer.BadParameter: If neither config file nor required parameters are provided
7098
"""
71-
if config_path is not None and len(filepaths) > 0:
72-
raise typer.BadParameter(
73-
"Cannot specify both a JSON/YAML file and a list of filepaths."
74-
)
99+
if config_path is not None:
100+
if len(filepaths) > 0:
101+
raise typer.BadParameter(
102+
"Cannot specify both a JSON/YAML file and a list of filepaths."
103+
)
104+
if recurse:
105+
raise typer.BadParameter(
106+
"Cannot specify both a JSON/YAML file and recurse into filepaths."
107+
)
75108

76109
_has_meta_params = all(arg is not None for arg in [pid, dataverse_url, api_token])
77110
_has_config_file = config_path is not None
@@ -94,6 +127,10 @@ def main(
94127
default=None,
95128
help="A list of filepaths to upload.",
96129
),
130+
recurse: Optional[bool] = typer.Option(
131+
default=False,
132+
help="Enable recursion into filepaths.",
133+
),
97134
pid: str = typer.Option(
98135
default=None,
99136
help="The persistent identifier of the Dataverse dataset.",
@@ -123,6 +160,7 @@ def main(
123160
124161
If using command line arguments, you must specify:
125162
- One or more filepaths to upload
163+
- (Optional) whether to recurse into the filepaths
126164
- The dataset's persistent identifier
127165
- A valid API token
128166
- The Dataverse repository URL
@@ -150,6 +188,7 @@ def main(
150188
pid=pid,
151189
dataverse_url=dataverse_url,
152190
api_token=api_token,
191+
recurse=recurse,
153192
config_path=config_path,
154193
)
155194

@@ -161,7 +200,7 @@ def main(
161200
api_token=api_token,
162201
dataverse_url=dataverse_url,
163202
persistent_id=pid,
164-
files=[File(filepath=filepath) for filepath in filepaths],
203+
files=_enumerate_filepaths(filepaths=filepaths, recurse=recurse),
165204
)
166205

167206
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)