Skip to content

Commit b9cfc2c

Browse files
authored
Merge pull request #123 from templateflow/enh/standalone-cli
ENH: Add a command line interface
2 parents ead040a + 6f27171 commit b9cfc2c

File tree

8 files changed

+180
-3
lines changed

8 files changed

+180
-3
lines changed

docs/api.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ Information on specific functions, classes, and methods.
44

55
.. toctree::
66

7+
api/templateflow.cli
78
api/templateflow.api
89
api/templateflow.conf

docs/cli.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Using the client with the Command-line interface (CLI)
2+
======================================================
3+
4+
.. click:: templateflow.cli:main
5+
:prog: templateflow
6+
:nested: full
7+
8+
Examples
9+
--------
10+
Listing all the compressed NIfTI files in ``fsaverage``::
11+
12+
$ templateflow ls fsaverage -x .nii.gz
13+
~/.cache/templateflow/tpl-fsaverage/tpl-fsaverage_res-01_den-41k_T1w.nii.gz
14+
~/.cache/templateflow/tpl-fsaverage/tpl-fsaverage_res-01_desc-brain_mask.nii.gz
15+
~/.cache/templateflow/tpl-fsaverage/tpl-fsaverage_res-01_T1w.nii.gz

docs/conf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# -- Project information -----------------------------------------------------
1717
project = __packagename__
1818
copyright = __copyright__
19-
author = "The TemplateFlow Developers"
19+
author = "The NiPreps Developers"
2020

2121
# The full version, including alpha/beta/rc tags
2222
release = __version__
@@ -40,6 +40,7 @@
4040
"sphinx.ext.viewcode",
4141
"sphinxcontrib.apidoc",
4242
"nbsphinx",
43+
"sphinx_click",
4344
]
4445

4546
autodoc_mock_imports = [

docs/environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,4 @@ dependencies:
206206
- hatchling
207207
- hatch-vcs
208208
- nipreps-versions
209+
- sphinx-click

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Contents
1212

1313
installation
1414
examples
15+
cli
1516
datalad
1617
api
1718
changes

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ doc = [
5050
"packaging",
5151
"pydot>=1.2.3",
5252
"pydotplus",
53-
"sphinx-argparse",
53+
"sphinx-click",
5454
"sphinx ~= 4.0",
5555
"sphinx_rtd_theme >= 0.4.3",
5656
"sphinxcontrib-apidoc",
@@ -61,6 +61,9 @@ tests = ["templateflow[test]"]
6161
docs = ["templateflow[doc]"]
6262
all = ["templateflow[datalad,doc,test]"]
6363

64+
[project.scripts]
65+
templateflow = "templateflow.cli:main"
66+
6467
#
6568
# Hatch configurations
6669
#

templateflow/cli.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
#
4+
# Copyright 2024 The NiPreps Developers <[email protected]>
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# We support and encourage derived works from this project, please read
19+
# about our expectations at
20+
#
21+
# https://www.nipreps.org/community/licensing/
22+
#
23+
"""The TemplateFlow Python Client command-line interface (CLI)."""
24+
from __future__ import annotations
25+
26+
import json
27+
from pathlib import Path
28+
29+
import click
30+
from click.decorators import FC, Option, _param_memo
31+
32+
from templateflow import __package__, api
33+
from templateflow._loader import Loader as _Loader
34+
from templateflow.conf import TF_HOME, TF_USE_DATALAD
35+
36+
load_data = _Loader(__package__)
37+
38+
ENTITY_SHORTHANDS = {
39+
# 'template': ('--tpl', '-t'),
40+
'resolution': ('--res', ),
41+
'density': ('--den', ),
42+
'atlas': ('-a', ),
43+
'suffix': ('-s', ),
44+
'desc': ('-d', '--description'),
45+
'extension': ('--ext', '-x'),
46+
'label': ('-l', ),
47+
'segmentation': ('--seg', ),
48+
}
49+
ENTITY_EXCLUDE = {'template', 'description'}
50+
TEMPLATE_LIST = api.get_templates()
51+
52+
53+
def _nulls(s):
54+
return None if s == 'null' else s
55+
56+
57+
def entity_opts():
58+
"""Attaches all entities as options to the command."""
59+
60+
entities = json.loads(
61+
Path(load_data('conf/config.json')).read_text()
62+
)['entities']
63+
64+
args = [
65+
(
66+
f"--{e['name']}",
67+
*ENTITY_SHORTHANDS.get(e['name'], ())
68+
)
69+
for e in entities if e['name'] not in ENTITY_EXCLUDE
70+
]
71+
72+
def decorator(f: FC) -> FC:
73+
for arg in reversed(args):
74+
_param_memo(f, Option(arg, type=str, default=[], multiple=True))
75+
return f
76+
77+
return decorator
78+
79+
80+
@click.group()
81+
@click.version_option(message='TemplateFlow Python Client %(version)s')
82+
def main():
83+
"""The TemplateFlow Python Client command-line interface (CLI)."""
84+
pass
85+
86+
87+
@main.command()
88+
def config():
89+
"""Print-out configuration."""
90+
click.echo(f"""Current TemplateFlow settings:
91+
92+
TEMPLATEFLOW_HOME={TF_HOME}
93+
TEMPLATEFLOW_USE_DATALAD={'on' if TF_USE_DATALAD else 'off'}
94+
""")
95+
96+
97+
@main.command()
98+
def wipe():
99+
"""Wipe out a local S3 (direct-download) TemplateFlow Archive."""
100+
click.echo(f'This will wipe out all data downloaded into {TF_HOME}.')
101+
102+
if click.confirm('Do you want to continue?'):
103+
value = click.prompt(
104+
f'Please write the path of your local archive ({TF_HOME})',
105+
default='(abort)',
106+
show_default=False,
107+
)
108+
if value.strip() == str(TF_HOME):
109+
from templateflow.conf import wipe
110+
111+
wipe()
112+
click.echo(f'{TF_HOME} was wiped out.')
113+
return
114+
click.echo(f'Aborted! {TF_HOME} WAS NOT wiped out.')
115+
116+
117+
@main.command()
118+
@click.option('--local', is_flag=True)
119+
@click.option('--overwrite/--no-overwrite', default=True)
120+
def update(local, overwrite):
121+
"""Update the local TemplateFlow Archive."""
122+
from templateflow.conf import update as _update
123+
124+
click.echo(
125+
f'Successfully updated local TemplateFlow Archive: {TF_HOME}.'
126+
if _update(local=local, overwrite=overwrite)
127+
else 'TemplateFlow Archive not updated.'
128+
)
129+
130+
131+
@main.command()
132+
@entity_opts()
133+
@click.argument('template', type=click.Choice(TEMPLATE_LIST))
134+
def ls(template, **kwargs):
135+
"""List the assets corresponding to template and optional filters."""
136+
entities = {k: _nulls(v) for k, v in kwargs.items() if v != ''}
137+
click.echo(
138+
'\n'.join(f'{match}' for match in api.ls(template, **entities))
139+
)
140+
141+
142+
@main.command()
143+
@entity_opts()
144+
@click.argument('template', type=click.Choice(TEMPLATE_LIST))
145+
def get(template, **kwargs):
146+
"""Fetch the assets corresponding to template and optional filters."""
147+
entities = {k: _nulls(v) for k, v in kwargs.items() if v != ''}
148+
click.echo(
149+
'\n'.join(f'{match}' for match in api.get(template, **entities))
150+
)
151+
152+
153+
if __name__ == '__main__':
154+
""" Install entry-point """
155+
main()

templateflow/conf/bids.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"""Extending pyBIDS for querying TemplateFlow."""
2424
from bids.layout import BIDSLayout, add_config_paths
2525

26-
from . import load_data
26+
from templateflow.conf import load_data
2727

2828
add_config_paths(templateflow=load_data('config.json'))
2929

0 commit comments

Comments
 (0)