From 9183b033d3b6e48d77e1d24ce0c311fbdd652329 Mon Sep 17 00:00:00 2001 From: Elias Sadek Date: Wed, 17 Aug 2022 16:55:42 +0100 Subject: [PATCH 1/8] Draft: Tilde's and wildcard can now be read by iris.save. Modifications and tests provisionally done, docstrings still need editing, with examples and doctests needing being added/editing. --- lib/iris/io/__init__.py | 48 ++++++++++--------- .../tests/unit/io/test_expand_filespecs.py | 7 +++ lib/iris/tests/unit/io/test_save.py | 6 +++ 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/lib/iris/io/__init__.py b/lib/iris/io/__init__.py index 8d5a2e05d2..17d41bc467 100644 --- a/lib/iris/io/__init__.py +++ b/lib/iris/io/__init__.py @@ -131,7 +131,7 @@ def decode_uri(uri, default="file"): return scheme, part -def expand_filespecs(file_specs): +def expand_filespecs(file_specs, files_expected=True): """ Find all matching file paths from a list of file-specs. @@ -139,12 +139,13 @@ def expand_filespecs(file_specs): * file_specs (iterable of string): File paths which may contain '~' elements or wildcards. + * files_expected (boolean): + A check to see if file is expected to exist (i.e. for load). Returns: A well-ordered list of matching absolute file paths. If any of the file-specs match no existing files, an exception is raised. - """ # Remove any hostname component - currently unused filenames = [ @@ -154,26 +155,28 @@ def expand_filespecs(file_specs): for fn in file_specs ] - # Try to expand all filenames as globs - glob_expanded = OrderedDict( - [[fn, sorted(glob.glob(fn))] for fn in filenames] - ) - - # If any of the specs expanded to an empty list then raise an error - all_expanded = glob_expanded.values() - - if not all(all_expanded): - msg = "One or more of the files specified did not exist:" - for pattern, expanded in glob_expanded.items(): - if expanded: - msg += '\n - "{}" matched {} file(s)'.format( - pattern, len(expanded) - ) - else: - msg += '\n * "{}" didn\'t match any files'.format(pattern) - raise IOError(msg) + if files_expected: + # Try to expand all filenames as globs + glob_expanded = OrderedDict( + [[fn, sorted(glob.glob(fn))] for fn in filenames] + ) - return [fname for fnames in all_expanded for fname in fnames] + # If any of the specs expanded to an empty list then raise an error + all_expanded = glob_expanded.values() + if not all(all_expanded): + msg = "One or more of the files specified did not exist:" + for pattern, expanded in glob_expanded.items(): + if expanded: + msg += '\n - "{}" matched {} file(s)'.format( + pattern, len(expanded) + ) + else: + msg += '\n * "{}" didn\'t match any files'.format(pattern) + raise IOError(msg) + result = [fname for fnames in all_expanded for fname in fnames] + else: + result = filenames + return result def load_files(filenames, callback, constraints=None): @@ -418,11 +421,12 @@ def save(source, target, saver=None, **kwargs): """ from iris.cube import Cube, CubeList - # Determine format from filename if isinstance(target, pathlib.PurePath): target = str(target) if isinstance(target, str) and saver is None: + # Converts tilde or wildcards to absolute path + target = expand_filespecs([str(target)], False)[0] saver = find_saver(target) elif hasattr(target, "name") and saver is None: saver = find_saver(target.name) diff --git a/lib/iris/tests/unit/io/test_expand_filespecs.py b/lib/iris/tests/unit/io/test_expand_filespecs.py index 0299a415b4..2a3a11de65 100644 --- a/lib/iris/tests/unit/io/test_expand_filespecs.py +++ b/lib/iris/tests/unit/io/test_expand_filespecs.py @@ -96,6 +96,13 @@ def test_files_and_none(self): self.assertMultiLineEqual(str(err.exception), expected) + def test_false_bool(self): + tempdir = self.tmpdir + msg = os.path.join(tempdir, "no_exist.txt") + result = iio.expand_filespecs([msg], False)[0] + self.assertEqual(result, msg) + + if __name__ == "__main__": tests.main() diff --git a/lib/iris/tests/unit/io/test_save.py b/lib/iris/tests/unit/io/test_save.py index b92e26f2d1..8ff4cf71ad 100755 --- a/lib/iris/tests/unit/io/test_save.py +++ b/lib/iris/tests/unit/io/test_save.py @@ -18,6 +18,7 @@ class TestSave(tests.IrisTest): def test_pathlib_save(self): + # DOESN'T CHECK TILDES WORK DUE TO MOCK. file_mock = mock.Mock() # Have to configure after creation because "name" is special file_mock.configure_mock(name="string") @@ -26,6 +27,11 @@ def test_pathlib_save(self): "iris.io.find_saver", return_value=(lambda *args, **kwargs: None) ) + def replace_expand(file_specs, files_expected=True): + return file_specs + + self.patch("iris.io.expand_filespecs", replace_expand) + test_variants = [ ("string", "string"), (Path("string/string"), "string/string"), From 6b2ff5a382d827e81a13b0d886e2eb1cfa41648a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Aug 2022 16:14:20 +0000 Subject: [PATCH 2/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- lib/iris/io/__init__.py | 5 ++++- lib/iris/tests/unit/io/test_expand_filespecs.py | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/iris/io/__init__.py b/lib/iris/io/__init__.py index 17d41bc467..a9993bf092 100644 --- a/lib/iris/io/__init__.py +++ b/lib/iris/io/__init__.py @@ -171,7 +171,9 @@ def expand_filespecs(file_specs, files_expected=True): pattern, len(expanded) ) else: - msg += '\n * "{}" didn\'t match any files'.format(pattern) + msg += '\n * "{}" didn\'t match any files'.format( + pattern + ) raise IOError(msg) result = [fname for fnames in all_expanded for fname in fnames] else: @@ -421,6 +423,7 @@ def save(source, target, saver=None, **kwargs): """ from iris.cube import Cube, CubeList + # Determine format from filename if isinstance(target, pathlib.PurePath): target = str(target) diff --git a/lib/iris/tests/unit/io/test_expand_filespecs.py b/lib/iris/tests/unit/io/test_expand_filespecs.py index 2a3a11de65..be73585974 100644 --- a/lib/iris/tests/unit/io/test_expand_filespecs.py +++ b/lib/iris/tests/unit/io/test_expand_filespecs.py @@ -103,6 +103,5 @@ def test_false_bool(self): self.assertEqual(result, msg) - if __name__ == "__main__": tests.main() From f3a956ca189f13e6606537730bb8c5266925bf6e Mon Sep 17 00:00:00 2001 From: Elias Sadek Date: Mon, 22 Aug 2022 09:01:49 +0100 Subject: [PATCH 3/8] Docstrings updated for expand_filespecs and save --- lib/iris/io/__init__.py | 174 ++++++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 80 deletions(-) diff --git a/lib/iris/io/__init__.py b/lib/iris/io/__init__.py index 17d41bc467..af39120d41 100644 --- a/lib/iris/io/__init__.py +++ b/lib/iris/io/__init__.py @@ -133,8 +133,12 @@ def decode_uri(uri, default="file"): def expand_filespecs(file_specs, files_expected=True): """ + Short Summary + ------------- Find all matching file paths from a list of file-specs. + Parameters + ---------- Args: * file_specs (iterable of string): @@ -142,10 +146,16 @@ def expand_filespecs(file_specs, files_expected=True): * files_expected (boolean): A check to see if file is expected to exist (i.e. for load). - Returns: - A well-ordered list of matching absolute file paths. - If any of the file-specs match no existing files, an - exception is raised. + Returns + ------- + if files_expected is True: + fnames : [string] + A well-ordered list of matching absolute file paths. + If any of the file-specs match no existing files, an + exception is raised. + if files_expected is False: + filename : [string] + A list of expanded file paths. """ # Remove any hostname component - currently unused filenames = [ @@ -344,82 +354,86 @@ def find_saver(filespec): def save(source, target, saver=None, **kwargs): """ - Save one or more Cubes to file (or other writeable). - - Iris currently supports three file formats for saving, which it can - recognise by filename extension: - - * netCDF - the Unidata network Common Data Format: - * see :func:`iris.fileformats.netcdf.save` - * GRIB2 - the WMO GRIdded Binary data format: - * see :func:`iris_grib.save_grib2`. - * PP - the Met Office UM Post Processing Format: - * see :func:`iris.fileformats.pp.save` - - A custom saver can be provided to the function to write to a different - file format. - - Args: - - * source: - :class:`iris.cube.Cube`, :class:`iris.cube.CubeList` or - sequence of cubes. - * target: - A filename (or writeable, depending on file format). - When given a filename or file, Iris can determine the - file format. Filename can be given as a string or - :class:`pathlib.PurePath`. - - Kwargs: - - * saver: - Optional. Specifies the file format to save. - If omitted, Iris will attempt to determine the format. - - If a string, this is the recognised filename extension - (where the actual filename may not have it). - Otherwise the value is a saver function, of the form: - ``my_saver(cube, target)`` plus any custom keywords. It - is assumed that a saver will accept an ``append`` keyword - if it's file format can handle multiple cubes. See also - :func:`iris.io.add_saver`. - - All other keywords are passed through to the saver function; see the - relevant saver documentation for more information on keyword arguments. - - Examples:: - - # Save a cube to PP - iris.save(my_cube, "myfile.pp") - - # Save a cube list to a PP file, appending to the contents of the file - # if it already exists - iris.save(my_cube_list, "myfile.pp", append=True) - - # Save a cube to netCDF, defaults to NETCDF4 file format - iris.save(my_cube, "myfile.nc") - - # Save a cube list to netCDF, using the NETCDF3_CLASSIC storage option - iris.save(my_cube_list, "myfile.nc", netcdf_format="NETCDF3_CLASSIC") - - .. warning:: - - Saving a cube whose data has been loaded lazily - (if `cube.has_lazy_data()` returns `True`) to the same file it expects - to load data from will cause both the data in-memory and the data on - disk to be lost. - - .. code-block:: python - - cube = iris.load_cube("somefile.nc") - # The next line causes data loss in 'somefile.nc' and the cube. - iris.save(cube, "somefile.nc") - - In general, overwriting a file which is the source for any lazily loaded - data can result in corruption. Users should proceed with caution when - attempting to overwrite an existing file. - - """ + Short Summary + ------------- + Save one or more Cubes to file (or other writeable). + + Iris currently supports three file formats for saving, which it can + recognise by filename extension: + + * netCDF - the Unidata network Common Data Format: + * see :func:`iris.fileformats.netcdf.save` + * GRIB2 - the WMO GRIdded Binary data format: + * see :func:`iris_grib.save_grib2`. + * PP - the Met Office UM Post Processing Format: + * see :func:`iris.fileformats.pp.save` + + A custom saver can be provided to the function to write to a different + file format. + + Parameters + ---------- + Args: + + * source: + :class:`iris.cube.Cube`, :class:`iris.cube.CubeList` or + sequence of cubes. + * target: + A filename (or writeable, depending on file format). + When given a filename or file, Iris can determine the + file format. Filename can be given as a string or + :class:`pathlib.PurePath`. + + Kwargs: + + ** saver: + Optional. Specifies the file format to save. + If omitted, Iris will attempt to determine the format. + + If a string, this is the recognised filename extension + (where the actual filename may not have it). + Otherwise the value is a saver function, of the form: + ``my_saver(cube, target)`` plus any custom keywords. It + is assumed that a saver will accept an ``append`` keyword + if it's file format can handle multiple cubes. See also + :func:`iris.io.add_saver`. + + All other keywords are passed through to the saver function; see the + relevant saver documentation for more information on keyword arguments. + + Examples + -------- + # Save a cube to PP + >>> iris.save(my_cube, "myfile.pp") + + # Save a cube list to a PP file, appending to the contents of the file + # if it already exists + >>> iris.save(my_cube_list, "myfile.pp", append=True) + + # Save a cube to netCDF, defaults to NETCDF4 file format + >>> iris.save(my_cube, "myfile.nc") + + # Save a cube list to netCDF, using the NETCDF3_CLASSIC storage option + >>> iris.save(my_cube_list, "myfile.nc", netcdf_format="NETCDF3_CLASSIC") + + .. warning:: + + Saving a cube whose data has been loaded lazily + (if `cube.has_lazy_data()` returns `True`) to the same file it expects + to load data from will cause both the data in-memory and the data on + disk to be lost. + + .. code-block:: python + + cube = iris.load_cube("somefile.nc") + # The next line causes data loss in 'somefile.nc' and the cube. + iris.save(cube, "somefile.nc") + + In general, overwriting a file which is the source for any lazily loaded + data can result in corruption. Users should proceed with caution when + attempting to overwrite an existing file. + + """ from iris.cube import Cube, CubeList # Determine format from filename if isinstance(target, pathlib.PurePath): From 362de1dff19931cb8168e821d6b8800e8d53dcaa Mon Sep 17 00:00:00 2001 From: Elias Sadek Date: Mon, 22 Aug 2022 11:47:49 +0100 Subject: [PATCH 4/8] docstrings for save and expand_filespecs updated to numpy formatting --- lib/iris/io/__init__.py | 142 +++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 76 deletions(-) diff --git a/lib/iris/io/__init__.py b/lib/iris/io/__init__.py index 0148888ed1..749e779bcd 100644 --- a/lib/iris/io/__init__.py +++ b/lib/iris/io/__init__.py @@ -133,28 +133,23 @@ def decode_uri(uri, default="file"): def expand_filespecs(file_specs, files_expected=True): """ - Short Summary - ------------- Find all matching file paths from a list of file-specs. Parameters ---------- - Args: - - * file_specs (iterable of string): - File paths which may contain '~' elements or wildcards. - * files_expected (boolean): - A check to see if file is expected to exist (i.e. for load). + file_specs : iterable of str + File paths which may contain ``~`` elements or wildcards. + files_expected : bool + Whether file is expected to exist (i.e. for load). Returns ------- - if files_expected is True: - fnames : [string] + list of str + if files_expected is ``True``: A well-ordered list of matching absolute file paths. If any of the file-specs match no existing files, an exception is raised. - if files_expected is False: - filename : [string] + if files_expected is ``False``: A list of expanded file paths. """ # Remove any hostname component - currently unused @@ -356,86 +351,81 @@ def find_saver(filespec): def save(source, target, saver=None, **kwargs): """ - Short Summary - ------------- - Save one or more Cubes to file (or other writeable). + Save one or more Cubes to file (or other writeable). - Iris currently supports three file formats for saving, which it can - recognise by filename extension: + Iris currently supports three file formats for saving, which it can + recognise by filename extension: - * netCDF - the Unidata network Common Data Format: - * see :func:`iris.fileformats.netcdf.save` - * GRIB2 - the WMO GRIdded Binary data format: - * see :func:`iris_grib.save_grib2`. - * PP - the Met Office UM Post Processing Format: - * see :func:`iris.fileformats.pp.save` + * netCDF - the Unidata network Common Data Format: + * see :func:`iris.fileformats.netcdf.save` + * GRIB2 - the WMO GRIdded Binary data format: + * see :func:`iris_grib.save_grib2`. + * PP - the Met Office UM Post Processing Format: + * see :func:`iris.fileformats.pp.save` - A custom saver can be provided to the function to write to a different - file format. - - Parameters - ---------- - Args: - - * source: - :class:`iris.cube.Cube`, :class:`iris.cube.CubeList` or - sequence of cubes. - * target: - A filename (or writeable, depending on file format). - When given a filename or file, Iris can determine the - file format. Filename can be given as a string or - :class:`pathlib.PurePath`. - - Kwargs: - - ** saver: - Optional. Specifies the file format to save. - If omitted, Iris will attempt to determine the format. - - If a string, this is the recognised filename extension - (where the actual filename may not have it). - Otherwise the value is a saver function, of the form: - ``my_saver(cube, target)`` plus any custom keywords. It - is assumed that a saver will accept an ``append`` keyword - if it's file format can handle multiple cubes. See also - :func:`iris.io.add_saver`. + A custom saver can be provided to the function to write to a different + file format. + Parameters + ---------- + source : :class:`iris.cube.Cube` or :class:`iris.cube.CubeList` + target : str or pathlib.PurePath or io.TextIOWrapper + When given a filename or file, Iris can determine the + file format. + saver : str or function, optional + Specifies the file format to save. + If omitted, Iris will attempt to determine the format. + If a string, this is the recognised filename extension + (where the actual filename may not have it). + + Otherwise the value is a saver function, of the form: + ``my_saver(cube, target)`` plus any custom keywords. It + is assumed that a saver will accept an ``append`` keyword + if its file format can handle multiple cubes. See also + :func:`iris.io.add_saver`. + **kwargs : dict, optional All other keywords are passed through to the saver function; see the relevant saver documentation for more information on keyword arguments. - Examples - -------- - # Save a cube to PP - >>> iris.save(my_cube, "myfile.pp") + Warnings + -------- + Saving a cube whose data has been loaded lazily + (if `cube.has_lazy_data()` returns `True`) to the same file it expects + to load data from will cause both the data in-memory and the data on + disk to be lost. - # Save a cube list to a PP file, appending to the contents of the file - # if it already exists - >>> iris.save(my_cube_list, "myfile.pp", append=True) + .. code-block:: python - # Save a cube to netCDF, defaults to NETCDF4 file format - >>> iris.save(my_cube, "myfile.nc") + cube = iris.load_cube("somefile.nc") + # The next line causes data loss in 'somefile.nc' and the cube. + iris.save(cube, "somefile.nc") - # Save a cube list to netCDF, using the NETCDF3_CLASSIC storage option - >>> iris.save(my_cube_list, "myfile.nc", netcdf_format="NETCDF3_CLASSIC") + In general, overwriting a file which is the source for any lazily loaded + data can result in corruption. Users should proceed with caution when + attempting to overwrite an existing file. - .. warning:: + Examples + -------- + >>> # Setting up + >>> import iris + >>> my_cube = iris.load_cube(iris.sample_data_path('air_temp.pp')) + >>> my_cube_list = iris.load(iris.sample_data_path('space_weather.nc')) - Saving a cube whose data has been loaded lazily - (if `cube.has_lazy_data()` returns `True`) to the same file it expects - to load data from will cause both the data in-memory and the data on - disk to be lost. + >>> # Save a cube to PP + >>> iris.save(my_cube, "myfile.pp") - .. code-block:: python + >>> # Save a cube list to a PP file, appending to the contents of the file + >>> # if it already exists + >>> iris.save(my_cube_list, "myfile.pp", append=True) - cube = iris.load_cube("somefile.nc") - # The next line causes data loss in 'somefile.nc' and the cube. - iris.save(cube, "somefile.nc") + >>> # Save a cube to netCDF, defaults to NETCDF4 file format + >>> iris.save(my_cube, "myfile.nc") - In general, overwriting a file which is the source for any lazily loaded - data can result in corruption. Users should proceed with caution when - attempting to overwrite an existing file. + >>> # Save a cube list to netCDF, using the NETCDF3_CLASSIC storage option + >>> iris.save(my_cube_list, "myfile.nc", netcdf_format="NETCDF3_CLASSIC") - """ + + """ from iris.cube import Cube, CubeList # Determine format from filename From 4f49ef0358b9759022967bbb88ea7d7a53a94c5f Mon Sep 17 00:00:00 2001 From: Elias Sadek Date: Mon, 22 Aug 2022 15:43:27 +0100 Subject: [PATCH 5/8] Reviewed: Added expansion and relative path tests to expand_filespecs. Ensured list indexing returns single item lists. Minor comment changes. --- lib/iris/io/__init__.py | 4 +-- .../tests/unit/io/test_expand_filespecs.py | 25 +++++++++++++++++-- lib/iris/tests/unit/io/test_save.py | 2 +- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/iris/io/__init__.py b/lib/iris/io/__init__.py index 749e779bcd..4659f70ae3 100644 --- a/lib/iris/io/__init__.py +++ b/lib/iris/io/__init__.py @@ -139,7 +139,7 @@ def expand_filespecs(file_specs, files_expected=True): ---------- file_specs : iterable of str File paths which may contain ``~`` elements or wildcards. - files_expected : bool + files_expected : bool, default=True Whether file is expected to exist (i.e. for load). Returns @@ -433,7 +433,7 @@ def save(source, target, saver=None, **kwargs): target = str(target) if isinstance(target, str) and saver is None: # Converts tilde or wildcards to absolute path - target = expand_filespecs([str(target)], False)[0] + (target,) = expand_filespecs([str(target)], False) saver = find_saver(target) elif hasattr(target, "name") and saver is None: saver = find_saver(target.name) diff --git a/lib/iris/tests/unit/io/test_expand_filespecs.py b/lib/iris/tests/unit/io/test_expand_filespecs.py index be73585974..b6a7fbedbf 100644 --- a/lib/iris/tests/unit/io/test_expand_filespecs.py +++ b/lib/iris/tests/unit/io/test_expand_filespecs.py @@ -7,6 +7,8 @@ # Import iris.tests first so that some things can be initialised before # importing anything else. +from pathlib import Path + import iris.tests as tests # isort:skip import os @@ -96,12 +98,31 @@ def test_files_and_none(self): self.assertMultiLineEqual(str(err.exception), expected) - def test_false_bool(self): + def test_false_bool_absolute(self): tempdir = self.tmpdir msg = os.path.join(tempdir, "no_exist.txt") - result = iio.expand_filespecs([msg], False)[0] + (result,) = iio.expand_filespecs([msg], False) + self.assertEqual(result, msg) + + def test_false_bool_home(self): + # ensure that not only does files_expected not error, + # but that the path is still expanded from a ~ + msg = str(Path().home() / "no_exist.txt") + (result,) = iio.expand_filespecs(["~/no_exist.txt"], False) self.assertEqual(result, msg) + def test_false_bool_relative(self): + cwd = os.getcwd() + try: + os.chdir(self.tmpdir) + item_out = iio.expand_filespecs(["no_exist.txt"], False) + item_in = [ + os.path.join(self.tmpdir, "no_exist.txt") + ] + self.assertEqual(item_out, item_in) + finally: + os.chdir(cwd) + if __name__ == "__main__": tests.main() diff --git a/lib/iris/tests/unit/io/test_save.py b/lib/iris/tests/unit/io/test_save.py index 8ff4cf71ad..623cf417f2 100755 --- a/lib/iris/tests/unit/io/test_save.py +++ b/lib/iris/tests/unit/io/test_save.py @@ -18,7 +18,6 @@ class TestSave(tests.IrisTest): def test_pathlib_save(self): - # DOESN'T CHECK TILDES WORK DUE TO MOCK. file_mock = mock.Mock() # Have to configure after creation because "name" is special file_mock.configure_mock(name="string") @@ -30,6 +29,7 @@ def test_pathlib_save(self): def replace_expand(file_specs, files_expected=True): return file_specs + # does not expand filepaths due to patch self.patch("iris.io.expand_filespecs", replace_expand) test_variants = [ From 197f1a5e15b0233546d1d6c8b2fdc819f11192f5 Mon Sep 17 00:00:00 2001 From: Elias Sadek Date: Mon, 22 Aug 2022 16:01:47 +0100 Subject: [PATCH 6/8] Updated latest What's New, and added ESadek-MO to common links --- docs/src/common_links.inc | 1 + docs/src/whatsnew/latest.rst | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/src/common_links.inc b/docs/src/common_links.inc index 7ae2463ca9..ec7e1efd6d 100644 --- a/docs/src/common_links.inc +++ b/docs/src/common_links.inc @@ -53,6 +53,7 @@ .. _@cpelley: https://github.com/cpelley .. _@djkirkham: https://github.com/djkirkham .. _@DPeterK: https://github.com/DPeterK +.. _@ESadek-MO: https://github.com/ESadek-MO .. _@esc24: https://github.com/esc24 .. _@jamesp: https://github.com/jamesp .. _@jonseddon: https://github.com/jonseddon diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 215e7d34f0..b94cd11517 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -25,13 +25,15 @@ This document explains the changes made to Iris for this release 📢 Announcements ================ -#. N/A +#. Welcome to `@ESadek-MO`_ who made their first contribution to Iris 🎉 ✨ Features =========== -#. N/A +#. `@ESadek-MO`_ edited :func:`~iris.io.expand_filespecs` to allow expansion of + non-existing paths, and added expansion functionality to :func:`~iris.io.save`. + (:issue:`4772`, :pull:`4913`) 🐛 Bugs Fixed From e06f0320a86db9474dee8f06980604a8afa69954 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 15:05:22 +0000 Subject: [PATCH 7/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- lib/iris/tests/unit/io/test_expand_filespecs.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/iris/tests/unit/io/test_expand_filespecs.py b/lib/iris/tests/unit/io/test_expand_filespecs.py index b6a7fbedbf..7e91d586ca 100644 --- a/lib/iris/tests/unit/io/test_expand_filespecs.py +++ b/lib/iris/tests/unit/io/test_expand_filespecs.py @@ -5,19 +5,21 @@ # licensing details. """Unit tests for the `iris.io.expand_filespecs` function.""" +import os + # Import iris.tests first so that some things can be initialised before # importing anything else. from pathlib import Path - -import iris.tests as tests # isort:skip - -import os import shutil import tempfile import textwrap import iris.io as iio +import iris.tests as tests # isort:skip + + + class TestExpandFilespecs(tests.IrisTest): def setUp(self): @@ -116,9 +118,7 @@ def test_false_bool_relative(self): try: os.chdir(self.tmpdir) item_out = iio.expand_filespecs(["no_exist.txt"], False) - item_in = [ - os.path.join(self.tmpdir, "no_exist.txt") - ] + item_in = [os.path.join(self.tmpdir, "no_exist.txt")] self.assertEqual(item_out, item_in) finally: os.chdir(cwd) From 125425f49e7d9adc419d3af8245aba0da283c207 Mon Sep 17 00:00:00 2001 From: Elias Sadek Date: Mon, 22 Aug 2022 16:44:07 +0100 Subject: [PATCH 8/8] Fixed isort conflicts --- lib/iris/tests/unit/io/test_expand_filespecs.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/iris/tests/unit/io/test_expand_filespecs.py b/lib/iris/tests/unit/io/test_expand_filespecs.py index 7e91d586ca..8720478153 100644 --- a/lib/iris/tests/unit/io/test_expand_filespecs.py +++ b/lib/iris/tests/unit/io/test_expand_filespecs.py @@ -5,10 +5,11 @@ # licensing details. """Unit tests for the `iris.io.expand_filespecs` function.""" -import os - # Import iris.tests first so that some things can be initialised before # importing anything else. +import iris.tests as tests # isort:skip + +import os from pathlib import Path import shutil import tempfile @@ -16,10 +17,6 @@ import iris.io as iio -import iris.tests as tests # isort:skip - - - class TestExpandFilespecs(tests.IrisTest): def setUp(self):