Skip to content

Commit 4b532c3

Browse files
committed
Save to file by default
Usability testing by a colleague identified this as a confusing aspect of the UX. For most people, download roughly equates to "save to a file", not "output to stdout". Resolve this by downloading to a file automatically unless specifically requests. This also requires bumping the logging level to INFO to allow us to output helpful information. Signed-off-by: Stephen Finucane <stephen@that.guru>
1 parent ef52f7b commit 4b532c3

File tree

7 files changed

+91
-23
lines changed

7 files changed

+91
-23
lines changed

git_pw/api.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
"""
44

55
import logging
6-
import os
6+
import re
77
import sys
8-
import tempfile
98

109
import requests
1110

@@ -138,17 +137,23 @@ def download(url, params=None):
138137
139138
GET /{resource}/{resourceID}/
140139
140+
The ``Content-Disposition`` header is assumed to be present and
141+
will be used for the output filename.
142+
141143
Arguments:
142144
url: The resource URL.
143145
params: Additional parameters.
144146
145147
Returns:
146148
A path to an output file containing the content.
147149
"""
148-
output_fd, output_path = tempfile.mkstemp(suffix='.patch')
149-
150150
rsp = get(url, params, stream=True)
151-
with os.fdopen(output_fd, 'wb') as output_file:
151+
152+
# we don't catch anything here because we should break if these are missing
153+
header = rsp.headers['content-disposition']
154+
output_path = re.search('filename=(.+)', header).group(1)
155+
156+
with open(output_path, 'wb') as output_file:
152157
LOG.debug('Saving to %s', output_path)
153158
# we use iter_content because patches can be binary
154159
for block in rsp.iter_content(1024):

git_pw/bundle.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,29 @@ def apply_cmd(bundle_id, args):
3232

3333
@click.command(name='download')
3434
@click.argument('bundle_id', type=click.INT)
35-
def download_cmd(bundle_id):
35+
@click.argument('output', type=click.File('wb'), required=False)
36+
def download_cmd(bundle_id, output):
3637
"""Download bundle in mbox format.
3738
38-
Download a bundle but do not apply it.
39+
Download a bundle but do not apply it. ``OUTPUT`` is optional and can be an
40+
output path or ``-`` to output to ``stdout``. If ``OUTPUT`` is not
41+
provided, the output path will be automatically chosen.
3942
"""
4043
LOG.debug('Downloading bundle: id=%d', bundle_id)
4144

45+
path = None
4246
bundle = api.detail('bundles', bundle_id)
43-
output = api.get(bundle['mbox']).text
4447

45-
click.echo_via_pager(output)
48+
if output:
49+
output.write(api.get(bundle['mbox']).text)
50+
51+
if output != sys.stdout:
52+
path = output.name
53+
else:
54+
path = api.download(bundle['mbox'])
55+
56+
if path:
57+
LOG.info('Downloaded bundle to %s', path)
4658

4759

4860
@click.command(name='show')

git_pw/logger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ def configure_verbosity(debug): # type: (bool) -> None
1111
level=logging.DEBUG,
1212
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
1313
else:
14-
logging.basicConfig(level=logging.ERROR, format='%(message)s')
14+
logging.basicConfig(level=logging.INFO, format='%(message)s')

git_pw/patch.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,25 +45,44 @@ def apply_cmd(patch_id, series, deps, args):
4545

4646
@click.command(name='download')
4747
@click.argument('patch_id', type=click.INT)
48+
@click.argument('output', type=click.File('wb'), required=False)
4849
@click.option('--diff', 'fmt', flag_value='diff',
4950
help='Show patch in diff format.')
5051
@click.option('--mbox', 'fmt', flag_value='mbox', default=True,
5152
help='Show patch in mbox format.')
52-
def download_cmd(patch_id, fmt):
53-
"""Download a patch diff/mbox.
53+
def download_cmd(patch_id, output, fmt):
54+
"""Download patch in diff or mbox format.
5455
55-
Download a patch but do not apply it.
56+
Download a patch but do not apply it. ``OUTPUT`` is optional and can be an
57+
output path or ``-`` to output to ``stdout``. If ``OUTPUT`` is not
58+
provided, the output path will be automatically chosen.
5659
"""
5760
LOG.debug('Downloading patch: id=%d, format=%s', patch_id, fmt)
5861

62+
path = None
5963
patch = api.detail('patches', patch_id)
6064

61-
if fmt == 'diff':
62-
output = patch['diff']
63-
else:
64-
output = api.get(patch['mbox']).text
65+
if output:
66+
if fmt == 'diff':
67+
content = patch['diff']
68+
else:
69+
content = api.get(patch['mbox']).text
70+
71+
output.write(content)
6572

66-
click.echo_via_pager(output)
73+
if output != sys.stdout:
74+
path = output.name
75+
else:
76+
if fmt == 'diff':
77+
# TODO(stephenfin): We discard the 'diff' field so we can get the
78+
# filename and save to the correct file. We should expose this
79+
# information via the API
80+
path = api.download(patch['mbox'].replace('mbox', 'raw'))
81+
else:
82+
path = api.download(patch['mbox'])
83+
84+
if path:
85+
LOG.info('Downloaded patch to %s', path)
6786

6887

6988
def _show_patch(patch):

git_pw/series.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,29 @@ def apply_cmd(series_id, args):
3333

3434
@click.command(name='download')
3535
@click.argument('series_id', type=click.INT)
36-
def download_cmd(series_id):
36+
@click.argument('output', type=click.File('wb'), required=False)
37+
def download_cmd(series_id, output):
3738
"""Download series in mbox format.
3839
39-
Download a series but do not apply it.
40+
Download a series but do not apply it. ``OUTPUT`` is optional and can be an
41+
output path or ``-`` to output to ``stdout``. If ``OUTPUT`` is not
42+
provided, the output path will be automatically chosen.
4043
"""
4144
LOG.debug('Downloading series: id=%d', series_id)
4245

46+
path = None
4347
series = api.detail('series', series_id)
44-
output = api.get(series['mbox']).text
4548

46-
click.echo_via_pager(output)
49+
if output:
50+
output.write(api.get(series['mbox']).text)
51+
52+
if output != sys.stdout:
53+
path = output.name
54+
else:
55+
path = api.download(series['mbox'])
56+
57+
if path:
58+
LOG.info('Downloaded series to %s', path)
4759

4860

4961
@click.command(name='show')
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
features:
3+
- |
4+
Patches, series and bundles downloaded using the ``download`` command will
5+
now be saved to a file by default instead of output to stdout. By default,
6+
this will use the name provided by Patchwork but you can override this by
7+
passing a specific filename. For example::
8+
9+
$ git pw patch download 1234 hello-world.patch
10+
11+
You can also output to ``stdout`` using the ``-`` shortcut. For example::
12+
13+
$ git pw patch download 1234 -
14+
upgrade:
15+
- |
16+
Patches, series and bundles downloaded using the ``download`` command will
17+
now be saved to a file by default. To continue outputing to ``stdout``,
18+
use ``-``. For example::
19+
20+
$ git pw patch download 1234 -

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ commands=
2323

2424
[testenv:docs]
2525
commands =
26-
python setup.py build_sphinx
26+
python setup.py build_sphinx {posargs}
2727

2828
[testenv:man]
2929
commands =

0 commit comments

Comments
 (0)