Source code for PseudoNetCDF.pncparse

from __future__ import print_function, unicode_literals
import os
import sys
from warnings import warn
from argparse import ArgumentParser, Action, RawDescriptionHelpFormatter
from ._getreader import getreaderdict, pncopen
from ._getwriter import getwriterdict
from .conventions.ioapi import add_ioapi_from_cf
from .conventions.ioapi import add_cf_from_wrfioapi, add_cf_from_ioapi
from PseudoNetCDF import PseudoNetCDFFile
from PseudoNetCDF.netcdf import NetCDFFile
try:
    from netCDF4 import MFDataset
except ImportError:
    pass


from .sci_var import reduce_dim, mesh_dim, slice_dim, getvarpnc, extract
from .sci_var import mask_vals, seqpncbo, pncexpr, stack_files, add_attr
from .sci_var import convolve_dim, manglenames, removesingleton, merge
from .sci_var import extract_from_file, pncrename, WrapPNC


from argparse import SUPPRESS


conv2conv = {}
conv2conv[('ioapi', 'cf')] = add_cf_from_ioapi
conv2conv[('wrfioapi', 'cf')] = add_cf_from_wrfioapi
conv2conv[('cf', 'ioapi')] = add_ioapi_from_cf

allreaders = getreaderdict()
allwriters = getwriterdict()
globals().update(allreaders)
_readernames = [(k.count('.') if k[:1] != '_' else 9999, k)
                for k in getreaderdict().keys()]
_readernames.sort()
_readernames = [k for c, k in _readernames]
_writernames = [(k.count('.'), k) for k in getwriterdict().keys()]
_writernames.sort()
_writernames = [k for c, k in _writernames]
_coordkeys = ("time time_bounds TFLAG ETFLAG latitude latitude_bounds " +
              "longitude longitude_bounds lat lat_bnds lon lon_bnds " +
              "etam_pressure etai_pressure layer_bounds layer47 layer").split()


class PNCArgumentParser(ArgumentParser):
    def exit(self, status=0, message=None):
        import sys as _sys
        if message:
            self._print_message(message, _sys.stderr)


class _HelpAction(Action):
    def __init__(self,
                 option_strings,
                 dest=SUPPRESS,
                 default=SUPPRESS,
                 help=None):
        super(_HelpAction, self).__init__(
            option_strings=option_strings,
            dest=dest,
            default=default,
            nargs=0,
            help=help)

    def __call__(self, parser, namespace, values, option_string=None):
        parser.print_help()
        setattr(namespace, self.dest, True)


class _HelpListFormats(Action):
    def __init__(self,
                 option_strings,
                 dest=SUPPRESS,
                 default=SUPPRESS,
                 help=None):
        super(_HelpListFormats, self).__init__(
            option_strings=option_strings,
            dest=dest,
            default=default,
            nargs=0,
            help=help)

    def __call__(self, parser, namespace, values, option_string=None):
        print('The formats listed below are available ' +
              'for the following options')
        print('-f FORMAT or --format FORMAT or --help-format FORMAT')
        print('where FORMAT is one of the options below')
        print('\t' + '\n\t'.join(_readernames))
        print('**Some readers are listed twice (e.g., without dotted form)')
        setattr(namespace, 'help', True)


class _HelpFormat(Action):
    def __init__(self,
                 option_strings,
                 dest=SUPPRESS,
                 default=SUPPRESS,
                 help=None):
        super(_HelpFormat, self).__init__(
            option_strings=option_strings,
            dest=dest,
            default=default,
            nargs=1,
            help=help)

    def __call__(self, parser, namespace, values, option_string=None):
        file_format = values[0].split(',')[0]
        print('')
        print('All formats require a "path". Some formats also ')
        print('take extra arguments. All arguments other than ')
        print('the input path must be specified using keyword')
        print('arguments.')
        print('')
        helpformat = allreaders[file_format]
        try:
            import inspect
            print('Example:')
            idef = inspect.getargspec(helpformat.__init__)
            args = idef.args[2:]
            if idef.defaults is None:
                defs = ['<VAL>'] * len(args)
            else:
                defs = (len(args) - len(idef.defaults)) * \
                    ['<VAL>'] + list(idef.defaults)
            longform = (
                'pncdump -f ' +
                ','.join([file_format] +
                         [karg + "=%s" % kdef
                          for karg, kdef in zip(args, defs)]) + ' path')
            shortform = (
                'pncdump -f ' +
                ','.join([file_format] +
                         [karg + "=%s" % kdef
                          for karg, kdef in zip(args, defs)
                          if kdef == '<VAL>']) +
                ' path')
            print(shortform)
            if longform != shortform:
                print('')
                print('Extended example with keywords:')
                print(longform)
                print('')
                print('* Any keyword with a non "<VAL>" default ' +
                      'can be omitted.')
            print('')
        except Exception:
            print('** Short help not available for ' + file_format)
            print('')

        if 'y' == input('Hit Y/y to see detailed help\n').lower():
            help(helpformat)
        setattr(namespace, self.dest, values)
        setattr(namespace, 'help', True)


class AggCommaString(Action):
    def __call__(self, parser, namespace, values, option_string=None):
        setattr(namespace, self.dest, values.split(','))


_plotcmds = ['plot', 'plot2d', 'plotts', 'plotprofile', 'plotscatter']
_allcmds = ['gen', 'dump', 'eval', 'map'] + _plotcmds


def add_basic_options(parser):
    padd = parser.add_argument
    parser.add_argument('ifiles', nargs='*',
                        help='path to a file formatted as type -f')
    parser.add_argument("--verbose", dest="verbose", action="count",
                        default=0, help="Provides verbosity with pncgen")

    try:
        padd("--help",
             dest="help", action=_HelpAction, default=False,
             help="Displays help")
    except Exception:
        pass

    padd('--pnc', action='append', default=[],
         help='Set of pseudonetcdf commands to be process separately')

    padd("-f", "--format", dest="format", default='netcdf',
         metavar='{see --list-formats for choices}',
         help=("File format (default netcdf), can be one of the choices " +
               "listed, or an expression that evaluates to a reader. " +
               "Keyword arguments are passed via ,kwd=value."))

    padd("--list-formats", dest="help", default=None,
         action=_HelpListFormats, help="Show format options for -f")

    padd("--help-format", dest='helpformat',
         action=_HelpFormat, default=None,
         help="Show help for file format (must be one of the options for -f)")

    padd("--sep", dest="separator", action='store_true',
         default=False,
         help=("Used to separate groups of arguments for parsing (e.g., " +
               "pncgen -- [options1] file(s)1 [--sep [options2] file(s)2 " +
               "[... [--sep [optionsN] file(s)N]] "))

    padd("--inherit", dest="inherit", action="store_true",
         default=False,
         help=("Allow subparsed sections (separated with -- and --sep) to " +
               "inherit from global options (-f, --format is always " +
               "inherited)."))

    padd("--diskless", dest="diskless", action="store_true",
         default=False,
         help="Load file into memory; useful for subsequent processing")

    padd("--mangle", dest="mangle", action="store_true",
         default=False, help="Remove non-standard ascii from names")

    padd("--rename", dest="rename", action="append", default=[],
         help=("Provide pairs of strings to be substituted " +
               "--rename=type,oldkey,newkey (type: v = variable; d = " +
               "dimension;)"))

    padd("--remove-singleton", dest="removesingleton",
         type=lambda x: [k for k in x.split(',') if k != ''], default=[],
         help="Remove singleton (length 1) dimensions")

    padd("--coordkeys", dest="coordkeys", type=str,
         action=AggCommaString, default=_coordkeys, metavar="key1,key2",
         help="Variables to be ignored in pncbo.")

    padd("-v", "--variables", dest="variables", default=None,
         action=AggCommaString, metavar='varname1[,varname2[,...,varnameN]',
         help=("Variable names or regular expressions (using match) " +
               "separated by ','. If a group(s) has been specified, only " +
               "variables in that (those) group(s) will be selected."))

    padd("-a", "--attribute", dest="attribute", type=str,
         action="append", default=[],
         metavar="att_nm,var_nm,mode,att_typ,att_val",
         help=("Variables have attributes that can be added following nco " +
               "syntax (--attribute att_nm,var_nm,mode,att_typ,att_val); " +
               "mode = a,c,d,m,o and att_typ = f,d,l,s,c,b; att_typ is any " +
               "valid numpy type."))

    padd("-m", "--mask", dest="masks", action="append",
         default=[],
         help=("Masks to apply (e.g., greater,0 or less,0 or values,0, or " +
               "where,(time[:]%%24<12)[:,None].repeat(10,1))"))

    padd("--from-convention", dest="fromconv", type=str,
         default=None, help="From convention currently only support ioapi")

    padd("--to-convention", dest="toconv", type=str,
         default="cf", help="To convention currently only supports cf")

    padd("--stack", dest="stack", type=str,
         help="Concatentate (stack) files on the dimension.")

    padd("--merge", dest="merge", action='store_true',
         help="Combine variables into one file")

    padd("-s", "--slice", dest="slice", type=str,
         action="append", default=[], metavar='dim,start[,stop[,step]]',
         help=("Variables have dimensions (time, layer, lat, lon), which " +
               "can be subset using dim,start,stop,stride (e.g., " +
               "--slice=layer,0,47,5 would sample every fifth layer " +
               "starting at 0)"))

    padd("-r", "--reduce", dest="reduce", type=str,
         action="append", default=[], metavar='dim,function[,weight]',
         help=("Variable dimensions can be reduced using dim,function," +
               "weight syntax (e.g., --reduce=layer,mean,weight). " +
               "Weighting is not fully functional."))

    padd("--mesh", dest="mesh", type=str, action="append",
         default=[], metavar='dim,weight,function',
         help=("Variable dimensions can be meshed using dim,function," +
               "weight syntax (e.g., --mesh=time,0.5,mean)."))

    padd("-c", "--convolve", dest="convolve", type=str,
         action="append", default=[], metavar='dim,mode,wgt1,wgt2,...wgtN',
         help=("Variable dimension is reduced by convolve function " +
               "(dim,mode,wgt1,wgt2,...wgtN)"))

    padd("-e", "--extract", dest="extract", action="append",
         default=[],
         help=("lon/lat coordinates to extract lon1,lat1/lon2,lat2/lon3," +
               "lat3/.../lonN,latN"))

    padd("--extract-file", dest="extractfile", action="append",
         default=[], help="pncparse options for file")

    padd("--extractmethod", dest="extractmethod", type=str,
         default='nn', help="Method for extraction",
         choices=['ll2ij', 'nn', 'linear', 'cubic', 'quintic', 'KDTree'])

    padd("--op-typ", dest="operators", type=str,
         action='append', default=[],
         help=("Operator for binary file operations. Binary file operations " +
               "use the first two files, then the result and the next file, " +
               "etc. Use " +
               " or ".join(['//', '<=', '%%', 'is not', '>>', '&', '==', '!=',
                            '+', '*', '-', '/', '<', '>=', '**', '>', '<<',
                            '|', 'is', '^'])))

    padd("--expr", dest="expressions", type=str,
         action='append', default=[],
         help="Generic expressions to execute in the context of the file.")

    padd("--exprscript", dest="expressionscripts", type=str,
         action='append', default=[],
         help="Generic expressions to execute in the context of the file.")


def add_2d_options(parser):
    parser.add_argument('--swapaxes', action='store_true',
                        help='Swap x-y axes')


def add_eval_options(parser):
    padd = parser.add_argument
    from .pnceval import __all__ as evalfuncs
    add_interactive_options(parser)
    padd('--funcs', default=evalfuncs,
         type=lambda x: x.split(','),
         help=('Functions to evaluate split by , (default: %s)' %
               ','.join(evalfuncs)))


def add_map_options(parser):
    padd = parser.add_argument
    padd("--iter", dest="iter", action='append', default=[],
         help=("Create plots for each element of specified dimension " +
               "(e.g., --iter=time)."))

    padd("--resolution", dest="resolution",
         choices=['c', 'i', 'h'], default='c',
         help=("Use coarse (c), intermediate (i), or high (h) " +
               "resolution for maps."))

    padd("--no-coastlines", dest="coastlines",
         action='store_false',
         help="Disable coastlines by setting equal to False")

    padd("--no-countries", dest="countries",
         action='store_false',
         help="Disable countries by setting equal to False")

    padd("--states", dest="states", action='store_true',
         help="Enable states by setting equal to True")

    padd("--counties", dest="counties", action='store_true',
         help="Enable counties by setting equal to True")

    padd("--shapefile", "--shapefiles", dest="shapefiles",
         type=str, action='append', default=[],
         help=("Enable custom shapefiles (must be lon, lat). Keyword " +
               "arguments for display can be added with commas (see -f " +
               "for syntax)."))


def add_dump_options(parser):
    padd = parser.add_argument
    parser.add_argument("-H", "--header", dest="header",
                        action="store_true", default=False)

    parser.add_argument("-t", "--timestring", dest="timestring",
                        action="store_true", default=False)

    padd("--full-indices", dest="full_indices", default=None,
         metavar="[c|f]", choices=['c', 'f'],
         help=("Provide indices in CDL using either C or Fortran style " +
               "indexes. C style is 0-based and ordered from slowest " +
               "iterating dimension to fastest. Fortran style is 1-based " +
               "and ordered from fastest to slowest iterating dimension"))

    padd("-l", "--length", dest="line_length", type=int,
         default=80, metavar="LEN",
         help="CDL line length (pncdump only)")

    padd("--float-precision", dest="float_precision", type=int,
         default=8, metavar="FDIG",
         help="single precision digitis (default 8; pncdump only)")

    padd("--double-precision", dest="double_precision",
         type=int, default=16, metavar="PDIG",
         help="pdig double precision digits (default 16; pncdump only)")

    padd("--dump-name", dest="cdlname", type=str,
         default=None, help="Name for display in ncdump")


def add_output_options(parser):
    padd = parser.add_argument
    padd("-O", "--clobber", dest="clobber",
         action='store_true', default=False,
         help="Overwrite existing file if necessary.")

    ncex = "NETCDF3_CLASSIC, NETCDF4_CLASSIC, NETCDF4"
    padd("--out-format", dest="outformat",
         default="NETCDF4_CLASSIC", type=str,
         help="File output format (e.g., " + ncex + ";pncgen only)",
         choices=ncex.split(', ') + _writernames)

    padd("--mode", dest="mode", type=str, default="w",
         choices='w a r+ ws as r+s'.split(),
         help=("File mode for writing (w, a or r+ or with unbuffered " +
               "writes ws, as, or r+s; pncgen only)."))

    padd('outpath', default=None, type=str,
         help='path to a output file formatted as --out-format')


def add_interactive_options(parser):
    try:
        parser.add_argument("-i", "--interactive", dest="interactive",
                            action='store_true', default=False,
                            help="Use interactive mode")
    except Exception:
        pass


def add_plot_options(parser):
    padd = parser.add_argument
    add_interactive_options(parser)
    padd("--matplotlibrc", dest="matplotlibrc",
         type=lambda x: x.split(';'), action='append', default=[],
         help='rc options for matplotlib')
    padd("--figure-keywords", dest='figure_keywords',
         type=lambda x: eval('dict(' + x + ')'), default=dict(),
         help='options for figure')

    padd("--axes-keywords", dest='axes_keywords',
         type=lambda x: eval('dict(' + x + ')'),
         default=dict(), help='options for axes')

    padd("--plot-commands", dest="plotcommands", type=str,
         action='append', default=[],
         help=("Plotting functions to call for all variables expressions " +
               "to execute in the context of the file. The figure object " +
               "will always be 'fig'."))

    padd("--figformat", dest="figformat", type=str,
         default='png', help="Any format supported by matplotlib")

    padd("--norm", dest="normalize", type=str, default=None,
         help=("Typical examples Normalize(), LogNorm(), BoundaryNorm([0, " +
               "10, 20, 30, 40], ncolors = 256)"))

    padd("--colorbar-formatter", dest="colorbarformatter",
         type=str, default=None,
         help=("Typical examples LogFormatter(labelOnlyBase = False), " +
               "ScalarFormatter(), '%%3g'"))

    padd("--overlay", dest="overlay", action='store_true',
         help="Enable overlay by setting equal to True")

    padd('--no-squeeze', dest='squeeze', default=True,
         action='store_false',
         help=('Squeeze automatically removes singleton dimensions; ' +
               'disabling requires user to remove singleton dimensions ' +
               'with --remove-singleton option'))

    padd("--figroot", help='Path for base of figure (suffixed by action)')


_pncepilog = """
Detailed Steps
--------------

PseudoNetCDF has many operations and the order often matters. The order is
consistent with the order of options in the formatted help. The default order
is summarized as:

1. Open with specified reader (-f)
2. Select subset of variables (-v)
2. Add attributes (-a)
4. Apply masks (--mask)
5. Add conventions to support later operations (--to-conv, --from-conv)
6. Combine files via stacking on dimensions (--stack)
7. Slice dimensions (-s --slice)
8. Reduce dimensions (-r --reduce)
9. Convolve dimensions (-c)
10. Extract specific coordinates (--extract)
11. Remove singleton dimensions (--remove-singleton)
12. Apply expressions (--expr then --exprscripts)
13. Apply binary operators (--op_typ)

To impose your own order, use standard options (global options) and then
use -- to force positional interpretation of remaining options. In remaining
options, use --sep to separate groups of files and options to be evaluated
before any global operations."""


def getparser2(actions=False):
    rdesc = RawDescriptionHelpFormatter
    parent_parser = PNCArgumentParser(description="PseudoNetCDF",
                                      formatter_class=rdesc,
                                      add_help=False,
                                      prog='PNC')
    if actions:
        add_action_commands(
            parent_parser, withbasic=True, add_help=False)
    else:
        add_basic_options(parent_parser)
    return parent_parser


def getparser(has_ofile, plot_options=False, map_options=False,
              interactive=False, actions=False):
    """getparser produces a parser for PseudoNetCDF

Parameters
----------
has_ofile : boolean
            Requires the outpath option and processes existence check
plot_options : boolean, optional
               Adds options for plotting including matplotlibrc, spatial
               overlays, and normalization options, default is False
interactive : boolean, optional
              Adds for interactive option, default is False

Returns
-------
parser : ArgumentParser with options that are processable with pncparse

    """
    parser = PNCArgumentParser(description="""PseudoNetCDF Argument Parsing


""", formatter_class=RawDescriptionHelpFormatter)
    parser.epilog = _pncepilog
    add_basic_options(parser)
    if interactive:
        add_interactive_options(parser)

    if actions:
        add_action_commands(parser, add_help=False)
    else:
        # Only has output file if pncgen is called.
        if has_ofile:
            add_output_options(parser)
        else:
            add_dump_options(parser)

        if plot_options:
            add_plot_options(parser)
        if map_options:
            add_map_options(parser)

    return parser


def pncparse(has_ofile=False, plot_options=False, map_options=False,
             interactive=False, args=None, parser=None, ifiles=[]):
    """
Parameters
----------
has_ofile : boolean, optional
            Requires the outpath option and processes existence check
            default is False
plot_options : boolean, optional
               Processes matplotlib options before loading matplotlib
               (preprocessing important for backend), default is False.
interactive : boolean, optional
              Only relevant if parser is not provided (see getparser),
              default is False.
args : list or string, optional
       args are usually taken from the command-line, but can be provided
       in a function call, default is None.
parser : AgumentParser object, optional
         pncparser parser, default getparser(has_ofile,
                                             plot_options = plot_options,
                                             interactive = interactive)

Returns
-------
(ifiles, args)

ifiles : list of PseudoNetCDFFiles
args : args as parsed

    """
    inputargs = args
    if parser is None:
        parser = getparser(has_ofile, plot_options=plot_options,
                           map_options=map_options, interactive=interactive)
    subparser = getparser(
        has_ofile=False, plot_options=plot_options, interactive=interactive)
    try:
        args = parser.parse_args(args=args)
    except Exception as e:
        raise e
    except BaseException:
        return [], args

    args.inputargs = inputargs or sys.argv
    inpaths = getattr(args, 'ifiles', [])
    inpnc = getattr(args, 'pnc', [])
    inopts = len(inpaths + inpnc)
    if len(ifiles) == 0 and inopts == 0:
        if not getattr(args, 'help', False):
            parser.print_help()

        print('\n*ifile or pnc are required')
        return [], args

    subargs = split_positionals(subparser, args)
    ifiles = [ifile for ifile in ifiles]
    ipaths = ['unknown'] * len(ifiles)
    for subarg in subargs:
        ipaths.extend(subarg.ifiles)
        ifiles.extend(pncprep(subarg)[0])

    # ifile is set to results from parsing
    # this includes the standard ifile
    args.ifiles = ifiles
    args.ipath = ipaths
    # if args.stack is not None:
    #    pass
    # elif (len(args.ifiles) == 0 or
    #       (len(args.ifiles) - len(args.operators) - has_ofile) > 1):
    #    print('Too many arguments', len(args.ifiles),
    #          len(args.operators), has_ofile)
    #    parser.print_help()
    #    return
    if has_ofile or not getattr(args, 'outpath', None) is None:
        if not args.clobber and os.path.exists(args.outpath):
            parser.error(message=(('Output path (%s) exists; enable ' +
                                   'clobber (--clobber or -O) to ' +
                                   'overwrite.') % (args.outpath,)))

    if plot_options or hasattr(args, 'matplotlibrc'):
        from matplotlib import rcParams
        for rcassigns in args.matplotlibrc:
            for rcassign in rcassigns:
                key, value = rcassign.split('=')
                rcParams[key] = value

    out = pncprep(args)
    return out


def add_action_commands(parser, withbasic=False, add_help=True):
    subparsers = parser.add_subparsers(dest='subcommand',
                                       help='Adding commands for subparsing')
    dumpparser = subparsers.add_parser('dump',
                                       description=('Output Common Data ' +
                                                    'Language (like from ' +
                                                    'ncdump)'),
                                       help='sub-command help',
                                       add_help=add_help)
    add_dump_options(dumpparser)
    if withbasic:
        add_basic_options(dumpparser)

    genparser = subparsers.add_parser('gen',
                                      description=('Like ncgen with more ' +
                                                   'format support'),
                                      help='sub-command help',
                                      add_help=add_help)
    if withbasic:
        add_basic_options(genparser)
    add_output_options(genparser)

    evalparser = subparsers.add_parser('eval',
                                       description='Evaluation options.',
                                       help='sub-command help',
                                       add_help=add_help)
    add_eval_options(evalparser)
    if withbasic:
        add_basic_options(evalparser)

    mapparser = subparsers.add_parser('map',
                                      description='Make maps quickly',
                                      help='sub-command help',
                                      add_help=add_help)
    add_plot_options(mapparser)
    add_map_options(mapparser)
    if withbasic:
        add_basic_options(mapparser)

    for plotcmd in _plotcmds:
        plotparser = subparsers.add_parser(
            plotcmd, description='Plot data', add_help=add_help)
        add_plot_options(plotparser)
        if withbasic:
            add_basic_options(plotparser)
        if plotcmd == 'plotprofile':
            from .plotutil import add_vertprofile_options
            add_vertprofile_options(plotparser)


def do_actions(outargs):
    if 'subcommand' not in outargs:
        return
    if outargs.subcommand == 'dump':
        from .pncdump import pncdump
        for ifile in outargs.ifiles:
            pncdump(ifile, header=outargs.header,
                    full_indices=outargs.full_indices,
                    line_length=outargs.line_length,
                    float_precision=outargs.float_precision,
                    double_precision=outargs.double_precision,
                    timestring=outargs.timestring, name=outargs.cdlname)

    elif outargs.subcommand == 'gen':
        from .pncgen import pncgen
        nout = len(outargs.ifiles)
        if nout != 1:
            raise IOError(
                'pncgen can output only 1 file; user requested %d' % nout)
        ifile, = outargs.ifiles
        pncgen(ifile, outargs.outpath, outmode=outargs.mode,
               format=outargs.outformat, verbose=outargs.verbose)
    elif outargs.subcommand == 'eval':
        from .pnceval import pnceval
        nin = len(outargs.ifiles)
        if nin != 2:
            raise IOError(
                'pnceval requires 2 files; user requested %d' % nin)
        pnceval(outargs)
    elif outargs.subcommand == 'map':
        if getattr(outargs, 'outpath', None) is None:
            outargs.outpath = outargs.figroot
        from .plotutil.pncmap import makemaps
        makemaps(outargs)
    elif outargs.subcommand in _plotcmds:
        if getattr(outargs, 'outpath', None) is None:
            outargs.outpath = outargs.figroot
        from . import plotutil
        getattr(plotutil, outargs.subcommand)(outargs)


[docs]def PNC(*args, **kwds): """ Arguments: args - Command Line arguments/options for PseudoNetCDF for full list of potential args PNC('--help') ifiles - (optional) pre-loaded input files actions - (default: False) False: only open files do not make outputs True: enable dump,gen,map,etc output actions for action options see subparsers help e.g., PNC('dump', '--help', actions = True) Returns: out - Namespace object with parsed arguments including a list of processed files (out.ifiles) Example: # Single File out = PNC('--format=netcdf', inpath) infile = out.ifiles[0] O3 = infile.variables['O3'] # Multiple Files out = PNC('--format=netcdf', inpath1, inpath2) infile1, infile2 = out.ifiles O3_1 = infile1.variables['O3'] O3_2 = infile2.variables['O3'] # With Actions out = PNC('dump', '--variables=O3', '--format=netcdf', inpath) netcdf icartt/dc3-mrg60-dc8_merge_20120518_R7_thru20120622.nc { dimensions: POINTS = 6817 ; variables: double O3(TSTEP, LAY, ROW, COL); O3_ESRL:units = "ppbV" ; O3_ESRL:standard_name = "Ozone" ; O3_ESRL:missing_value = -999999 ; ... """ ifiles = kwds.pop('ifiles', []) actions = kwds.pop('actions', None) if actions is None: actions = args[0] in _allcmds parser = getparser2(actions=actions) nfiles = len(ifiles) ifiles, outargs = pncparse(args=args, ifiles=ifiles, parser=parser, **kwds) if nfiles > 0 and len(ifiles) != nfiles: raise IOError('PNC can only use files or paths, not both at this time') do_actions(outargs) return outargs
[docs]def pnc(*args, **kwds): """ Arguments - see PNC Returns: file(s) - single file or, if more than 1 file is returned a list of files """ ifiles = kwds.pop('ifiles', []) actions = kwds.pop('actions', None) out = PNC(*args, ifiles=ifiles, actions=actions, **kwds).ifiles if len(out) == 1: return out[0] else: return out
def split_positionals(parser, args): import shlex positionals = args.ifiles parser.set_defaults( **dict([(k, v) for k, v in args._get_kwargs() if args.inherit or k == 'format'])) ins = [shlex.split(pnc) for pnc in args.pnc] last_split = 0 for i in range(len(positionals)): if positionals[i] in ('--', '--sep'): ins.append(positionals[last_split:i]) last_split = i + 1 ins.append(positionals[last_split:]) try: outs = list(map(parser.parse_args, ins)) for outns, inargs in zip(outs, ins): outns.inputargs = inargs except Exception as e: print(str(e)) raise e except BaseException as be: print(str(be)) return [], args for out in outs: if out.cdlname is None: out.cdlname = ', '.join(out.ifiles) if len(outs) == 1 and getattr(args, 'cdlname', None): args.cdlname = outs[0].cdlname return outs def pncprep(args): # nifiles = len(args.ifiles) - has_ofile ipaths = args.ifiles[:] fs = getfiles(ipaths, args) fs = subsetfiles(fs, args) fs = seqpncbo(args.operators, fs, coordkeys=args.coordkeys) for expr in args.expressions: fs = [pncexpr(expr, f) for f in fs] for script in args.expressionscripts: expr = open(script).read() fs = [pncexpr(expr, f) for f in fs] args.ifiles = fs if getattr(args, 'cdlname', None) is None: try: args.cdlname = args.ipath[0] except Exception: args.cdlname = 'unknown' return fs, args def subsetfiles(ifiles, args): fs = [] for f in ifiles: for opts in args.slice: f = slice_dim(f, opts) for opts in args.reduce: f = reduce_dim(f, opts, metakeys=args.coordkeys) for opts in args.mesh: f = mesh_dim(f, opts) for opts in args.convolve: f = convolve_dim(f, opts) if len(args.extract) > 0: f = extract(f, args.extract, method=args.extractmethod) if len(args.extractfile) > 0: extractfiles = [] import shlex for extractfile in args.extractfile: extractfile, options = pncparse( has_ofile=False, args=shlex.split(extractfile)) extractfiles.extend(extractfile) f = extract_from_file(f, extractfiles, method=args.extractmethod) for rd in args.removesingleton: f = removesingleton(f, rd) fs.append(f) return fs def getfiles(ipaths, args): fs = [] for ipath in ipaths: format_options = args.format.split(',') file_format = format_options.pop(0) format_options = eval('dict(' + ', '.join(format_options) + ')') if isinstance(ipath, (PseudoNetCDFFile, NetCDFFile, MFDataset)): f = ipath elif isinstance(ipath, (str,)): try: # Most common usecase; open like pncopen f = pncopen(ipath, format=file_format, **format_options) except Exception: try: allreaders = getreaderdict() if file_format in allreaders: # Unknown problem, try reader directly allreaders[file_format](ipath, **format_options) else: # Maybe the format is an expression? allreaders[file_format](ipath, **format_options) f = eval(file_format)(ipath, **format_options) except Exception as e: etmp = ('Unable to open path with %s(path, **%s)\n\t' + 'path="%s"\n\terror="%s"') oute = IOError(etmp % (file_format, str(format_options), ipath, str(e))) raise oute # from e else: warn('File is type %s, which is unknown' % type(ipath)) f = ipath history = getattr(f, 'history', getattr(f, 'HISTORY', '')) history += ' '.join(args.inputargs) + ';' laddconv = args.fromconv is not None and args.toconv is not None # Need to add backward compatibility # lslice = len(args.slice + args.reduce) > 0 # lexpr = len(args.expressions) > 0 if args.variables is not None: f = getvarpnc(f, args.variables, coordkeys=args.coordkeys) elif args.diskless: f = getvarpnc(f, None) for opts in args.attribute: add_attr(f, opts) for opts in args.masks: f = mask_vals(f, opts, metakeys=args.coordkeys) if laddconv: if not args.diskless: f = WrapPNC(f) try: conv2conv[args.fromconv, args.toconv]( f, coordkeys=args.coordkeys) except KeyError as e: warn('Cannot add %s from %s; %s' % (args.toconv, args.fromconv, str(e))) try: setattr(f, 'history', history) except Exception: pass if args.mangle: f = manglenames(f) for rename in args.rename: f = pncrename(f, rename) fs.append(f) if args.stack is not None: fs = [stack_files(fs, args.stack, coordkeys=args.coordkeys)] if args.merge: fs = [merge(fs)] return fs