Skip to content

Add a showtable script - #6859

Merged
taldcroft merged 23 commits into
astropy:masterfrom
saimn:showtable
Dec 9, 2017
Merged

taldcroft merged 23 commits into
astropy:masterfrom
saimn:showtable

Conversation

@saimn

@saimn saimn commented Nov 15, 2017

Copy link
Copy Markdown
Contributor

Closes #3022. I often miss such a script and there was an agreement in #3022. Basically it is just a wrapper around Table.read and Table.pprint so it supports printing ascii, fits, votable and hdf5. I also added options specific to each format (see the script help below), suggestions welcome if you think some option is missing.

❯ showtable --help
usage: showtable [-h] [--max-lines MAX_LINES] [--max-width MAX_WIDTH]
                 [--hide-unit] [--show-dtype] [--format FORMAT] [--hdu HDU]
                 [--path PATH] [--table_id TABLE_ID]
                 filename [filename ...]

Print tables from ASCII, FITS, HDF5, VOTable file(s).

positional arguments:
  filename              Path to one or more files.

optional arguments:
  -h, --help            show this help message and exit
  --max-lines MAX_LINES
                        Maximum number of lines in table output.
  --max-width MAX_WIDTH
                        Maximum character width of output.
  --hide-unit           Hide the header row for unit (which is shown only if
                        one or more columns has a unit).
  --show-dtype          Include a header row for column dtypes.
  --format FORMAT       Input table format (only for ASCII files).
  --hdu HDU             Name of the HDU to show (only for FITS files).
  --path PATH           The path from which to read the table (only for HDF5
                        files).
  --table_id TABLE_ID   The table to read in (only for VOTable files).

@astropy-bot

astropy-bot Bot commented Nov 15, 2017

Copy link
Copy Markdown

Hi there @saimn 👋 - thanks for the pull request! I'm just a friendly 🤖 that checks for issues related to the changelog and making sure that this pull request is milestoned and labeled correctly. This is mainly intended for the maintainers, so if you are not a maintainer you can ignore this, and a maintainer will let you know if any action is required on your part 😃.

Everything looks good from my point of view! 👍

If there are any issues with this message, please report them here.

@saimn

saimn commented Nov 15, 2017

Copy link
Copy Markdown
Contributor Author

Missing:

  • changelog entry
  • mention in docs

Note that I created the file inside a new directory astropy/table/scripts/showtable.py as it looks logical but I can move it if needed.

@saimn saimn added this to the v3.0.0 milestone Nov 15, 2017
@pllim

pllim commented Nov 15, 2017

Copy link
Copy Markdown
Member

c/c @SaraOgaz in case this is of interest for STAK.

Comment thread astropy/table/scripts/showtable.py Outdated
try:
table = Table.read(filename, **read_kwargs)
table.pprint(max_lines=args.max_lines, max_width=args.max_width,
show_unit=not args.hide_unit, show_dtype=args.show_dtype)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using table.more? Or maybe we could have a flag to switch between 'cat' mode (pprint) and 'more' mode?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never use Table.more so I had a look and yes it could useful to have an option. I prefer the "cat mode " as default as personally I'm used to pipe the output to less (or more) which allows to search (not possible with Table.more).

@taldcroft taldcroft left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea @saimn !

Comment thread astropy/table/scripts/showtable.py Outdated
def main(args=None):
"""The main function called by the `showtable` script."""
parser = argparse.ArgumentParser(
description=('Print tables from ASCII, FITS, HDF5, VOTable file(s).'))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest putting some description here of the default output width / lines and how to control it. I think the fact that long/wide tables get clipped can be initially confusing. Something along the lines of:

The default behavior is make the table output fit onto a single screen page.  For a
long and wide table this will mean cutting out inner rows and columns.  To print
**all** the rows or columns use ``--max-lines=-1`` or ``max-width=-1``, respectively.

Comment thread astropy/table/scripts/showtable.py Outdated
addarg('--more', action='store_true',
help='Use the pager mode from Table.more.')
addarg('--max-lines', type=int,
help='Maximum number of lines in table output.')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest help='Max number of lines in table output (default=screen length, -1 for no limit).'

Same idea for --max-width. (I replaced "Maximum" with "Max" so it will format slightly better, but maybe that's not necessary.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestions, I have done the changes (using Maximum as it will use 2 lines anyway).

Comment thread astropy/table/scripts/showtable.py Outdated
help='Include a header row for column dtypes.')

# ASCII-specific arguments
# FIXME: add more args ? (delimiter, guess ?)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it would make sense to generalize this so that all the reader-specific kwargs are not initially pre-defined in the argparse parser. See e.g. for a hackish way to do this (which BTW doesn't totally work...)
https://stackoverflow.com/questions/37367331/is-it-possible-to-use-argparse-to-capture-an-arbitrary-set-of-optional-arguments

Probably a better option is to pre-code allowed options but use subparsers to keep them tidy and compartmentalized. E.g.

showtable ascii filename.dat --delimiter='|' --quotechar='"' --data-start=5
showtable ascii --help . # shows ascii subparser options

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's useful to add all possible parameters, my idea was to add only the most frequent ones (delimiter is certainly useful). The issue with subparsers is that it prevents a more generic use where you don't have to know the file type in advance, Table.read is able to guess many formats. Also having to specify always a subparser is cumbersome !

@taldcroft

Copy link
Copy Markdown
Member

Also, you could probably use this interface to print the results of table.info (with an --info flag?).

@saimn

saimn commented Nov 17, 2017

Copy link
Copy Markdown
Contributor Author

Updated --help output with the new parameters (--delimiter, --info and --stats), I think it's still reasonably short:

❯ showtable --help
usage: showtable [-h] [--more] [--info] [--stats] [--max-lines MAX_LINES]
                 [--max-width MAX_WIDTH] [--hide-unit] [--show-dtype]
                 [--format FORMAT] [--delimiter DELIMITER] [--hdu HDU]
                 [--path PATH] [--table_id TABLE_ID]
                 filename [filename ...]

Print tables from ASCII, FITS, HDF5, VOTable file(s).The default behavior is
make the table output fit onto a single screen page. For a long and wide table
this will mean cutting out inner rows and columns. To print **all** the rows
or columns use ``--max-lines=-1`` or ``max-width=-1``, respectively.

positional arguments:
  filename              Path to one or more files.

optional arguments:
  -h, --help            show this help message and exit
  --more                Use the pager mode from Table.more.
  --info                Show information about the table columns.
  --stats               Show statistics about the table columns.
  --max-lines MAX_LINES
                        Maximum number of lines in table output
                        (default=screen length, -1 for no limit).
  --max-width MAX_WIDTH
                        Maximum width in table output (default=screen width,
                        -1 for no limit).
  --hide-unit           Hide the header row for unit (which is shown only if
                        one or more columns has a unit).
  --show-dtype          Include a header row for column dtypes.
  --format FORMAT       Input table format (only for ASCII files).
  --delimiter DELIMITER
                        Column delimiter string (only for ASCII files).
  --hdu HDU             Name of the HDU to show (only for FITS files).
  --path PATH           The path from which to read the table (only for HDF5
                        files).
  --table_id TABLE_ID   The table to read in (only for VOTable files).

@saimn

saimn commented Nov 17, 2017

Copy link
Copy Markdown
Contributor Author

I have also added a mention of the script in the unified I/O page :

screenshot-2017-11-17 unified file read write interface astropy v3 0 dev20629

@taldcroft

Copy link
Copy Markdown
Member

@saimn - About the subparser, or else generalized support for sub-format options, I think you need to think about this as a high-visibility script that will be around for 15 or 20 years.

Pretty quickly we will add support for reading ASDF files, so that will result in at least one (if not more) new params. And with the mem-mapping in FITS it would make sense to add support in the API for reading in only some of the columns. Then a user will want the astropy_native option, and another will say that they have an ASCII file with % for comments, and another will say that they have a file that starts on the 5th line, and so on.

So I think that it is worth thinking hard about this script and future-proofing the UI, even if it means typing showtable fits filename.fits instead of showtable filename.fits. Or maybe there is another option (subformat required only to specify subformat options?), but I think that having a flat set of options that handles every file reader for the next 15-20 years will get messy.

@saimn

saimn commented Nov 20, 2017

Copy link
Copy Markdown
Contributor Author

@taldcroft , your arguments make perfectly sense, (and I agree it's good to think about this now before the is widely used), despite that I would prefer to avoid subparsers. I think subparsers makes the command more complicated than what it should be, you can read a table with Table.read('foo.fits') or Table.read('foo.csv'), it should be as simple for the script.
But that's just my own preference, I can live with subparsers if I really have to 😉, what do others think ?

@taldcroft

Copy link
Copy Markdown
Member

@saimn - agreed that we need to find a balance between overdesigning and getting into a mess down the road. I too am interested in other opinions.

If you put in a general mechanism for handling any kwargs that are not parsed by argparse, then you get back to the simplicity but full generality of Table.read(). What you lose is documentation of available kwargs, but this is a long-standing problem with Table.read() as well so it would be OK.

@saimn

saimn commented Nov 27, 2017

Copy link
Copy Markdown
Contributor Author

I have used argument groups to group arguments for each filetype, which gives a much better output I think (see below) and would allow to add a few more arguments while not being too messy.

What do you think, compared to the sub-parsers option mentioned above, @taldcroft (and others - @astrofrog , @pllim , ... ? 😉 )

❯ showtable --help
usage: showtable [-h] [--more] [--info] [--stats] [--max-lines MAX_LINES]
                 [--max-width MAX_WIDTH] [--hide-unit] [--show-dtype]
                 [--format FORMAT] [--delimiter DELIMITER] [--hdu HDU]
                 [--path PATH] [--table-id TABLE_ID]
                 filename [filename ...]

Print tables from ASCII, FITS, HDF5, VOTable file(s). The tables are read with
'astropy.table.Table.read' and are printed with 'astropy.table.Table.pprint'.
The default behavior is to make the table output fit onto a single screen
page. For a long and wide table this will mean cutting out inner rows and
columns. To print **all** the rows or columns use ``--max-lines=-1`` or ``max-
width=-1``, respectively.

positional arguments:
  filename              path to one or more files

optional arguments:
  -h, --help            show this help message and exit
  --more                use the pager mode from Table.more
  --info                show information about the table columns
  --stats               show statistics about the table columns

pprint arguments:
  --max-lines MAX_LINES
                        maximum number of lines in table output
                        (default=screen length, -1 for no limit)
  --max-width MAX_WIDTH
                        maximum width in table output (default=screen width,
                        -1 for no limit)
  --hide-unit           hide the header row for unit (which is shown only if
                        one or more columns has a unit)
  --show-dtype          include a header row for column dtypes

ASCII arguments:
  --format FORMAT       input table format
  --delimiter DELIMITER
                        column delimiter string

FITS arguments:
  --hdu HDU             name of the HDU to show

HDF5 arguments:
  --path PATH           the path from which to read the table

VOTable arguments:
  --table-id TABLE_ID   the table to read in

@taldcroft

Copy link
Copy Markdown
Member

Ah, I didn't know about argument groups. That works for me and looks nice.

I wonder if --format should be global in case some file fails to be auto-identified. E.g. a FITS file that doesn't have exactly the initial signature and an extension that doesn't match.

@saimn

saimn commented Nov 27, 2017

Copy link
Copy Markdown
Contributor Author

@taldcroft - Is it possible to force the Reader class used by the registry with a 'format' keyword ?

Also there is a test error with end of lines on appveyor, with Table.info's output. Why is it different from Table.pprint ?

@pllim

pllim commented Nov 27, 2017

Copy link
Copy Markdown
Member

@saimn , I like the grouping -- very nice! 👍

@taldcroft

Copy link
Copy Markdown
Member

Is it possible to force the Reader class used by the registry with a 'format' keyword ?

Are you talking about the Reader class in io.ascii or in the general unified I/O scheme? For io.ascii the answer is yes, via the format keyword which takes the "nice" ASCII representation of the Reader class name (with the caveat that it is prepended by 'ascii.' in unified I/O). In the latter the reader class is determined from the caller, i.e. Table.read and this cannot be overridden (AFAIK).

@saimn

saimn commented Nov 28, 2017

Copy link
Copy Markdown
Contributor Author

I mean for the general case, if I have an exotic fits extension is it possible to use format='fits' ? I also think it is not possible currently, but wasn't sure given your sentence above - ("I wonder if --format should be global in case some file fails to be auto-identified. E.g. a FITS file...")

@saimn

saimn commented Nov 28, 2017

Copy link
Copy Markdown
Contributor Author

Also the appveyor issue is fixed, so all tests pass, and argument groups seems to have the consensus. Anything else that should be added/modified ?

@taldcroft taldcroft left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More changes, but now I've looked through everything in some detail.

Comment thread CHANGES.rst Outdated

- Allowed to remove table rows through the ``__delitem__`` method. [#5839]

- Added a new ``showtable`` command-line script to view binary tables. [#6859]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to view binary or ASCII table files.

Comment thread astropy/table/scripts/showtable.py Outdated
# ASCII-specific arguments
ascii_args = parser.add_argument_group('ASCII arguments')
addarg = ascii_args.add_argument
addarg('--format', help='input table format')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be moved out of the ASCII-specific args, since indeed one can specify format='fits' or 'hdf5' or 'ascii.ipac' etc.

Comment thread astropy/table/scripts/showtable.py Outdated
table output fit onto a single screen page. For a long and wide
table this will mean cutting out inner rows and columns. To print
**all** the rows or columns use ``--max-lines=-1`` or
``max-width=-1``, respectively.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be useful for users to have a link to the complete list of available format values: http://astropy.readthedocs.io/en/latest/io/unified.html#built-in-table-readers-writers

if args.info:
print(table.info)
elif args.stats:
table.info('stats')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency you can write:

if args.info:
    table.info('attributes')
elif args.stats:
    table.info('stats')
else:
    ...

Comment thread astropy/table/tests/test_showtable.py Outdated
def test_info(capsys):
showtable.main([os.path.join(FITS_ROOT, 'data/table.fits'), '--info'])
out, err = capsys.readouterr()
assert out == ('<Table length=3>{0}'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference one can do out.splitlines() == ['line 1', 'line2', etc..] to get around the OS-dependence. This is a bit easier for writing tests because you can write failing tests, run in the debugger, and then print out.splitlines() to get the right answer to paste into code (after verifying by hand that it seems right!).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it's probably more handy for debugging, I will change.

Comment thread astropy/table/tests/test_showtable.py Outdated
def test_fits(capsys):
showtable.main([os.path.join(FITS_ROOT, 'data/table.fits')])
out, err = capsys.readouterr()
assert out == (' target V_mag\n'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see tests are passing, but why don't these need the os.linesep fix? Is this a bug in Table.pprint that it isn't using os.linesep?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The behavior differs (pprint just prints each line, whereas info writes to stdout using os.linesep explicitly) but I don't know what behavior is expected for terminal output (as far as I know the convention differs for files ?).

Comment thread docs/io/unified.rst
NGC1002 12.3
NGC1003 15.2


Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add something like

To get full documentation on the usage and available options do ``showtable --help``.

Comment thread astropy/table/tests/test_showtable.py Outdated
@@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On testing, I don't see tests for -max-lines, -hide-unit, and -show-dtype. Note that max_lines cannot be less than 7, and it applies to the header lines as well.

Comment thread astropy/table/scripts/showtable.py Outdated
else:
formatter = table.more if args.more else table.pprint
formatter(max_lines=args.max_lines, max_width=args.max_width,
show_unit=not args.hide_unit, show_dtype=args.show_dtype)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The show_unit arg is actually a 3-state argument:

  • True: always put in a line for the unit
  • False: never put in a line for the unit
  • None (default): put in a line for the unit if any of the columns has a unit

What is probably wanted here for default is None, so you don't get those blank lines in many of the typical cases (like in most of the tests). So show_unit=(False if args.hide_unit else None). This doesn't allow for forcing a line for units no matter what, but I think that is fine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I wanted None as default, thanks.

@saimn

saimn commented Dec 8, 2017

Copy link
Copy Markdown
Contributor Author

And now circleci fails because pytest-astropy requires pytest-mpl which installs matplotlib from source, oups! ping @drdavella (I think you already mentioned this issue somewhere?)

@saimn

saimn commented Dec 8, 2017

Copy link
Copy Markdown
Contributor Author

Tests are passing now, good for you @taldcroft ?

@taldcroft
taldcroft merged commit 33298a1 into astropy:master Dec 9, 2017
@taldcroft

Copy link
Copy Markdown
Member

Thanks @saimn !

@mhvk

mhvk commented Dec 10, 2017

Copy link
Copy Markdown
Contributor

This breaks my work on numpy 1.14 style changes (#6959), since it explicitly tests output - which depends on numpy version, with no allowance for floats being Are such tests this really needed? Obviously I can make the tests numpy-version dependent, but it is not for nothing that in our doctests we use +FLOAT_CMP. Could these tests use something similar?

p.s. It took me a while to figure out why a test_showtable.py was giving errors when I couldn't find it in my branch!

@mhvk mhvk mentioned this pull request Dec 10, 2017
8 tasks
@saimn
saimn deleted the showtable branch December 10, 2017 11:16
@taldcroft

Copy link
Copy Markdown
Member

See #6959 (comment) (which says):

I think this is a case where we should be testing non-nice floating point values. The whole point of showtable is creating human-readable output that is informative and "pleasant". So using real-world values is important to catch formatting issues that our users will end up seeing.

@SaOgaz

SaOgaz commented Jan 26, 2018

Copy link
Copy Markdown
Contributor

Hey @taldcroft, shouldn't we add this to the http://docs.astropy.org/en/stable/io/fits/usage/scripts.html doc page?

@pllim

pllim commented Jan 26, 2018

Copy link
Copy Markdown
Member

Minor correction: http://docs.astropy.org/en/latest/io/fits/usage/scripts.html (but I don't see it there either)

@saimn

saimn commented Jan 26, 2018

Copy link
Copy Markdown
Contributor Author

This page is for io.fits, but showtable is more general, hence the documentation is in the Unified I/O page: http://docs.astropy.org/en/latest/io/unified.html?highlight=showtable#command-line-utility
Maybe there should be a top-level page with all scripts instead ?

@taldcroft

Copy link
Copy Markdown
Member

👍 on adding to the FITS page for scripts, since it is definitely applicable there. Unlike with code, I think it's generally OK to repeat yourself in docs to help with discoverability. (Of course there is an issue of maintaining all the docs to be accurate, but these scripts don't change that much.)

I agree that having a top-level page with all scripts would be a fine thing as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants