def secure_filename(filename):
    r"""Borrowed from :mod:`werkzeug.utils`, under the BSD 3-clause license.

    Pass it a filename and it will return a secure version of it.  This
    filename can then safely be stored on a regular file system and passed
    to :func:`os.path.join`.  The filename returned is an ASCII only string
    for maximum portability.

    On windows systems the function also makes sure that the file is not
    named after one of the special device files.

    >>> secure_filename("My cool movie.mov")
    'My_cool_movie.mov'
    >>> secure_filename("../../../etc/passwd")
    'etc_passwd'
    >>> secure_filename(u'i contain cool \xfcml\xe4uts.txt')
    'i_contain_cool_umlauts.txt'

    The function might return an empty filename.  It's your responsibility
    to ensure that the filename is unique and that you generate random
    filename if the function returned an empty one.

    :param filename: the filename to secure
    """
    _filename_ascii_strip_re = re.compile(r'[^A-Za-z0-9_.-]')
    _windows_device_files = ('CON', 'AUX', 'COM1', 'COM2', 'COM3', 'COM4',
                             'LPT1', 'LPT2', 'LPT3', 'PRN', 'NUL')
    if isinstance(filename, text_type):
        from unicodedata import normalize
        filename = normalize('NFKD', filename).encode('ascii', 'ignore')
        if not PY2:
            filename = filename.decode('ascii')
    for sep in os.path.sep, os.path.altsep:
        if sep:
            filename = filename.replace(sep, ' ')
    filename = str(_filename_ascii_strip_re.sub('', '_'.join(
                   filename.split()))).strip('._')

    # on nt a couple of special files are present in each folder.  We
    # have to ensure that the target file is not such a filename.  In
    # this case we prepend an underline
    if os.name == 'nt' and filename and \
       filename.split('.')[0].upper() in _windows_device_files:
        filename = '_' + filename

    return filename
def read_example_file(
        netcdf_file_name, read_all_target_vars, target_name=None,
        metadata_only=False, targets_only=False, include_soundings=True,
        radar_field_names_to_keep=None, radar_heights_to_keep_m_agl=None,
        sounding_field_names_to_keep=None, sounding_heights_to_keep_m_agl=None,
        first_time_to_keep_unix_sec=None, last_time_to_keep_unix_sec=None,
        num_rows_to_keep=None, num_columns_to_keep=None,
        downsampling_dict=None):
    """Reads examples from NetCDF file.

    If `metadata_only == True`, later input args are ignored.
    If `targets_only == True`, later input args are ignored.

    :param netcdf_file_name: Path to input file.
    :param read_all_target_vars: Boolean flag.  If True, will read all target
        variables.  If False, will read only `target_name`.  Either way, if
        downsampling is done, it will be based only on `target_name`.
    :param target_name: Will read this target variable.  If
        `read_all_target_vars == True` and `downsampling_dict is None`, you can
        leave this alone.
    :param metadata_only: Boolean flag.  If False, this method will read
        everything.  If True, will read everything except predictor and target
        variables.
    :param targets_only: Boolean flag.  If False, this method will read
        everything.  If True, will read everything except predictors.
    :param include_soundings: Boolean flag.  If True and the file contains
        soundings, this method will return soundings.  Otherwise, no soundings.
    :param radar_field_names_to_keep: See doc for `_subset_radar_data`.
    :param radar_heights_to_keep_m_agl: Same.
    :param sounding_field_names_to_keep: See doc for `_subset_sounding_data`.
    :param sounding_heights_to_keep_m_agl: Same.
    :param first_time_to_keep_unix_sec: First time to keep.  If
        `first_time_to_keep_unix_sec is None`, all storm objects will be kept.
    :param last_time_to_keep_unix_sec: Last time to keep.  If
        `last_time_to_keep_unix_sec is None`, all storm objects will be kept.
    :param num_rows_to_keep: See doc for `_subset_radar_data`.
    :param num_columns_to_keep: Same.
    :param downsampling_dict: See doc for `_filter_examples_by_class`.
    :return: example_dict: If `read_all_target_vars == True`, dictionary will
        have all keys listed in doc for `write_example_file`.  If
        `read_all_target_vars == False`, key "target_names" will be replaced by
        "target_name" and "target_matrix" will be replaced by "target_values".

    example_dict['target_name']: Name of target variable.
    example_dict['target_values']: length-E list of target values (integer
        class labels), where E = number of examples.
    """

    # TODO(thunderhoser): Allow this method to read only soundings without radar
    # data.

    if (
            target_name ==
            'tornado_lead-time=0000-3600sec_distance=00000-10000m'
    ):
        target_name = (
            'tornado_lead-time=0000-3600sec_distance=00000-30000m_min-fujita=0'
        )

    error_checking.assert_is_boolean(read_all_target_vars)
    error_checking.assert_is_boolean(include_soundings)
    error_checking.assert_is_boolean(metadata_only)
    error_checking.assert_is_boolean(targets_only)

    example_dict, netcdf_dataset = _read_metadata_from_example_file(
        netcdf_file_name=netcdf_file_name, include_soundings=include_soundings)

    need_main_target_values = (
        not read_all_target_vars
        or downsampling_dict is not None
    )

    if need_main_target_values:
        target_index = example_dict[TARGET_NAMES_KEY].index(target_name)
    else:
        target_index = -1

    if not read_all_target_vars:
        example_dict[TARGET_NAME_KEY] = target_name
        example_dict.pop(TARGET_NAMES_KEY)

    if metadata_only:
        netcdf_dataset.close()
        return example_dict

    if need_main_target_values:
        main_target_values = numpy.array(
            netcdf_dataset.variables[TARGET_MATRIX_KEY][:, target_index],
            dtype=int
        )
    else:
        main_target_values = None

    if read_all_target_vars:
        example_dict[TARGET_MATRIX_KEY] = numpy.array(
            netcdf_dataset.variables[TARGET_MATRIX_KEY][:], dtype=int
        )
    else:
        example_dict[TARGET_VALUES_KEY] = main_target_values

    # Subset by time.
    if first_time_to_keep_unix_sec is None:
        first_time_to_keep_unix_sec = 0
    if last_time_to_keep_unix_sec is None:
        last_time_to_keep_unix_sec = int(1e12)

    error_checking.assert_is_integer(first_time_to_keep_unix_sec)
    error_checking.assert_is_integer(last_time_to_keep_unix_sec)
    error_checking.assert_is_geq(
        last_time_to_keep_unix_sec, first_time_to_keep_unix_sec)

    example_indices_to_keep = numpy.where(numpy.logical_and(
        example_dict[STORM_TIMES_KEY] >= first_time_to_keep_unix_sec,
        example_dict[STORM_TIMES_KEY] <= last_time_to_keep_unix_sec
    ))[0]

    if downsampling_dict is not None:
        subindices_to_keep = _filter_examples_by_class(
            target_values=main_target_values[example_indices_to_keep],
            downsampling_dict=downsampling_dict
        )
    elif not read_all_target_vars:
        subindices_to_keep = numpy.where(
            main_target_values[example_indices_to_keep] !=
            target_val_utils.INVALID_STORM_INTEGER
        )[0]
    else:
        subindices_to_keep = numpy.linspace(
            0, len(example_indices_to_keep) - 1,
            num=len(example_indices_to_keep), dtype=int
        )

    example_indices_to_keep = example_indices_to_keep[subindices_to_keep]
    if len(example_indices_to_keep) == 0:
        return None

    example_dict[FULL_IDS_KEY] = [
        example_dict[FULL_IDS_KEY][k] for k in example_indices_to_keep
    ]
    example_dict[STORM_TIMES_KEY] = (
        example_dict[STORM_TIMES_KEY][example_indices_to_keep]
    )

    if read_all_target_vars:
        example_dict[TARGET_MATRIX_KEY] = (
            example_dict[TARGET_MATRIX_KEY][example_indices_to_keep, :]
        )
    else:
        example_dict[TARGET_VALUES_KEY] = (
            example_dict[TARGET_VALUES_KEY][example_indices_to_keep]
        )

    if targets_only:
        netcdf_dataset.close()
        return example_dict

    example_dict = _subset_radar_data(
        example_dict=example_dict, netcdf_dataset_object=netcdf_dataset,
        example_indices_to_keep=example_indices_to_keep,
        field_names_to_keep=radar_field_names_to_keep,
        heights_to_keep_m_agl=radar_heights_to_keep_m_agl,
        num_rows_to_keep=num_rows_to_keep,
        num_columns_to_keep=num_columns_to_keep)

    if not include_soundings:
        netcdf_dataset.close()
        return example_dict

    example_dict = _subset_sounding_data(
        example_dict=example_dict, netcdf_dataset_object=netcdf_dataset,
        example_indices_to_keep=example_indices_to_keep,
        field_names_to_keep=sounding_field_names_to_keep,
        heights_to_keep_m_agl=sounding_heights_to_keep_m_agl)

    netcdf_dataset.close()
    return example_dict