From a5d46037511799b1d078c975ba474850d163432c Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Tue, 7 Sep 2021 14:12:31 +0800 Subject: [PATCH 01/17] Raise exception for non-existent parent directory in pandas.io.common.get_handle --- pandas/io/common.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pandas/io/common.py b/pandas/io/common.py index 5c5b9c65b8abd..0ed8436c53f78 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -632,6 +632,12 @@ def get_handle( compression_args = dict(ioargs.compression) compression = compression_args.pop("method") + # GH 24306 + if is_path: + dirname = os.path.dirname(handle) + if len(dirname)!=0 and not os.path.isdir(dirname): + raise ValueError(fr'Cannot save file into non-existent directory {dirname}') + if compression: # compression libraries do not like an explicit text-mode ioargs.mode = ioargs.mode.replace("t", "") From 7424516f78bf2655f25133df54ef41e32d788d4d Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Tue, 7 Sep 2021 14:21:53 +0800 Subject: [PATCH 02/17] Black autoformatting --- pandas/io/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/io/common.py b/pandas/io/common.py index 0ed8436c53f78..0b1d8df808cf9 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -635,8 +635,8 @@ def get_handle( # GH 24306 if is_path: dirname = os.path.dirname(handle) - if len(dirname)!=0 and not os.path.isdir(dirname): - raise ValueError(fr'Cannot save file into non-existent directory {dirname}') + if len(dirname) != 0 and not os.path.isdir(dirname): + raise ValueError(fr"Cannot save file into non-existent directory {dirname}") if compression: # compression libraries do not like an explicit text-mode From 1673b9bec2adb09a7a266e713e0ecfb9be2a37b4 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Tue, 7 Sep 2021 14:55:10 +0800 Subject: [PATCH 03/17] Raise exception only for to_* methods --- pandas/io/common.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pandas/io/common.py b/pandas/io/common.py index 0b1d8df808cf9..452e00ecbba9d 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -633,10 +633,13 @@ def get_handle( compression = compression_args.pop("method") # GH 24306 - if is_path: - dirname = os.path.dirname(handle) - if len(dirname) != 0 and not os.path.isdir(dirname): - raise ValueError(fr"Cannot save file into non-existent directory {dirname}") + if mode not in ["r", "rb"]: # Only for write methods + if is_path: + dirname = os.path.dirname(handle) + if len(dirname) != 0 and not os.path.isdir(dirname): + raise ValueError( + fr"Cannot save file into non-existent directory {dirname}" + ) if compression: # compression libraries do not like an explicit text-mode From 8f0e5130f35716934bb580247900c7db7d8dd732 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Tue, 7 Sep 2021 18:42:00 +0800 Subject: [PATCH 04/17] Change ValueError to IOError, fix error in test_to_xml --- pandas/io/common.py | 4 ++-- pandas/tests/io/xml/test_to_xml.py | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pandas/io/common.py b/pandas/io/common.py index 452e00ecbba9d..a6f9b7eb873b1 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -637,8 +637,8 @@ def get_handle( if is_path: dirname = os.path.dirname(handle) if len(dirname) != 0 and not os.path.isdir(dirname): - raise ValueError( - fr"Cannot save file into non-existent directory {dirname}" + raise OSError( + fr"Cannot save file into a non-existent directory: '{dirname}'" ) if compression: diff --git a/pandas/tests/io/xml/test_to_xml.py b/pandas/tests/io/xml/test_to_xml.py index 4f4815b9008ad..0d699d48a632f 100644 --- a/pandas/tests/io/xml/test_to_xml.py +++ b/pandas/tests/io/xml/test_to_xml.py @@ -202,10 +202,14 @@ def test_str_output(datapath, parser): def test_wrong_file_path(parser): + fpath = "/my/fake/path/output.xml" + dirname = os.path.dirname(fpath) + with pytest.raises( - FileNotFoundError, match=("No such file or directory|没有那个文件或目录") + OSError, + match=(fr"Cannot save file into a non-existent directory: '{dirname}'"), ): - geom_df.to_xml("/my/fake/path/output.xml", parser=parser) + geom_df.to_xml(fpath, parser=parser) # INDEX From bf5db42f42c1945e37777ba402b6e82f38db4443 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Tue, 7 Sep 2021 20:06:14 +0800 Subject: [PATCH 05/17] Add tests for all to_* methods, fix message in to_html --- pandas/io/common.py | 23 ++++++++++++++++------- pandas/io/formats/format.py | 6 +++++- pandas/tests/io/test_common.py | 27 +++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/pandas/io/common.py b/pandas/io/common.py index a6f9b7eb873b1..ea98aa972db7c 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -520,6 +520,20 @@ def infer_compression( raise ValueError(msg) +def check_parent_directory(fpath: str): + """Check if parent directory of a file exists, raise OSError if it does not + + Parameters + ---------- + fpath: str + File path + + """ + dirname = os.path.dirname(fpath) + if len(dirname) != 0 and not os.path.isdir(dirname): + raise OSError(fr"Cannot save file into a non-existent directory: '{dirname}'") + + def get_handle( path_or_buf: FilePathOrBuffer, mode: str, @@ -633,13 +647,8 @@ def get_handle( compression = compression_args.pop("method") # GH 24306 - if mode not in ["r", "rb"]: # Only for write methods - if is_path: - dirname = os.path.dirname(handle) - if len(dirname) != 0 and not os.path.isdir(dirname): - raise OSError( - fr"Cannot save file into a non-existent directory: '{dirname}'" - ) + if mode not in ["r", "rb"] and is_path: # Only for write methods + check_parent_directory(handle) if compression: # compression libraries do not like an explicit text-mode diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 3fd3d84f90161..78e9bb6f0beda 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -96,7 +96,10 @@ from pandas.core.indexes.timedeltas import TimedeltaIndex from pandas.core.reshape.concat import concat -from pandas.io.common import stringify_path +from pandas.io.common import ( + check_parent_directory, + stringify_path, +) from pandas.io.formats.printing import ( adjoin, justify, @@ -1147,6 +1150,7 @@ def get_buffer(buf: FilePathOrBuffer[str] | None, encoding: str | None = None): if hasattr(buf, "write"): yield buf elif isinstance(buf, str): + check_parent_directory(buf) with open(buf, "w", encoding=encoding, newline="") as f: # GH#30034 open instead of codecs.open prevents a file leak # if we have an invalid encoding argument. diff --git a/pandas/tests/io/test_common.py b/pandas/tests/io/test_common.py index fc834c7acf39f..035bcde755a2f 100644 --- a/pandas/tests/io/test_common.py +++ b/pandas/tests/io/test_common.py @@ -227,6 +227,33 @@ def test_read_non_existent(self, reader, module, error_class, fn_ext): ): reader(path) + @pytest.mark.parametrize( + "method, module, error_class, fn_ext", + [ + (pd.DataFrame.to_csv, "os", OSError, "csv"), + (pd.DataFrame.to_html, "os", OSError, "html"), + (pd.DataFrame.to_excel, "xlrd", OSError, "xlsx"), + (pd.DataFrame.to_feather, "pyarrow", OSError, "feather"), + (pd.DataFrame.to_stata, "os", OSError, "dta"), + (pd.DataFrame.to_json, "os", OSError, "json"), + (pd.DataFrame.to_pickle, "os", OSError, "pickle"), + ], + ) + # NOTE: Missing parent directory for pd.DataFrame.to_hdf is handled by PyTables + def test_write_missing_parent_directory(self, method, module, error_class, fn_ext): + pytest.importorskip(module) + + dummy_frame = pd.DataFrame({"a": [1, 2, 3], "b": [2, 3, 4], "c": [3, 4, 5]}) + + path = os.path.join(HERE, "data", "missing_folder", "does_not_exist." + fn_ext) + dirname = os.path.dirname(path) + + with pytest.raises( + error_class, + match=fr"Cannot save file into a non-existent directory: '{dirname}'", + ): + method(dummy_frame, path) + @pytest.mark.parametrize( "reader, module, error_class, fn_ext", [ From 7267cdaf59f1823b2a8bf4ecc0003d2cfdbd3f18 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Tue, 7 Sep 2021 20:10:07 +0800 Subject: [PATCH 06/17] Make mypy happy --- pandas/io/common.py | 2 +- pandas/io/formats/format.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/io/common.py b/pandas/io/common.py index ea98aa972db7c..ce96544634508 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -648,7 +648,7 @@ def get_handle( # GH 24306 if mode not in ["r", "rb"] and is_path: # Only for write methods - check_parent_directory(handle) + check_parent_directory(str(handle)) if compression: # compression libraries do not like an explicit text-mode diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 78e9bb6f0beda..d636838d21d0e 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1150,7 +1150,7 @@ def get_buffer(buf: FilePathOrBuffer[str] | None, encoding: str | None = None): if hasattr(buf, "write"): yield buf elif isinstance(buf, str): - check_parent_directory(buf) + check_parent_directory(str(buf)) with open(buf, "w", encoding=encoding, newline="") as f: # GH#30034 open instead of codecs.open prevents a file leak # if we have an invalid encoding argument. From 27840ddad44de156d70a0769d573d7c8f06f5403 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Tue, 7 Sep 2021 20:12:14 +0800 Subject: [PATCH 07/17] Minor docstring formatting --- pandas/io/common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/io/common.py b/pandas/io/common.py index ce96544634508..d1ee891a9e821 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -521,7 +521,8 @@ def infer_compression( def check_parent_directory(fpath: str): - """Check if parent directory of a file exists, raise OSError if it does not + """ + Check if parent directory of a file exists, raise OSError if it does not Parameters ---------- From 6b80259b212b2381c11badbb6cc4f05a0724baa1 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Tue, 7 Sep 2021 20:55:43 +0800 Subject: [PATCH 08/17] Add parquest to test parametrizations --- pandas/tests/io/test_common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/tests/io/test_common.py b/pandas/tests/io/test_common.py index 035bcde755a2f..deac365d8fc11 100644 --- a/pandas/tests/io/test_common.py +++ b/pandas/tests/io/test_common.py @@ -234,6 +234,7 @@ def test_read_non_existent(self, reader, module, error_class, fn_ext): (pd.DataFrame.to_html, "os", OSError, "html"), (pd.DataFrame.to_excel, "xlrd", OSError, "xlsx"), (pd.DataFrame.to_feather, "pyarrow", OSError, "feather"), + (pd.DataFrame.to_parquet, "pyarrow", OSError, "parquet"), (pd.DataFrame.to_stata, "os", OSError, "dta"), (pd.DataFrame.to_json, "os", OSError, "json"), (pd.DataFrame.to_pickle, "os", OSError, "pickle"), From de78922b8a4226f7e836fee01787e002064a2cb4 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Tue, 7 Sep 2021 20:55:59 +0800 Subject: [PATCH 09/17] Update whatsnew --- doc/source/whatsnew/v1.4.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 7b9997e8f0bd6..f82f355b6be09 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -107,6 +107,7 @@ Other enhancements - :meth:`DataFrame.to_stata` and :meth:`StataWriter` now accept the keyword only argument ``value_labels`` to save labels for non-categorical columns - Methods that relied on hashmap based algos such as :meth:`DataFrameGroupBy.value_counts`, :meth:`DataFrameGroupBy.count` and :func:`factorize` ignored imaginary component for complex numbers (:issue:`17927`) - Add :meth:`Series.str.removeprefix` and :meth:`Series.str.removesuffix` introduced in Python 3.9 to remove pre-/suffixes from string-type :class:`Series` (:issue:`36944`) +- Attempting to write into a file in missing parent directory with :meth:`DataFrame.to_csv`, :meth:`DataFrame.to_html`, :meth:`DataFrame.to_excel`, :meth:`DataFrame.to_feather`, :meth:`DataFrame.to_parquet`, :meth:`DataFrame.to_stata`, :meth:`DataFrame.to_json`, :meth:`DataFrame.to_pickle`, and :meth:`DataFrame.to_xml` now explicitly mentions missing parent directory, the same is true for their :class:`Series` method counterparts (:issue:`24306`) .. --------------------------------------------------------------------------- From 63970356f041a826c0792ee91f6e805afa390c3c Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Tue, 7 Sep 2021 21:47:03 +0800 Subject: [PATCH 10/17] Fix regex error in Windows --- pandas/tests/io/test_common.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/tests/io/test_common.py b/pandas/tests/io/test_common.py index deac365d8fc11..ad0b25d26d6f6 100644 --- a/pandas/tests/io/test_common.py +++ b/pandas/tests/io/test_common.py @@ -247,11 +247,10 @@ def test_write_missing_parent_directory(self, method, module, error_class, fn_ex dummy_frame = pd.DataFrame({"a": [1, 2, 3], "b": [2, 3, 4], "c": [3, 4, 5]}) path = os.path.join(HERE, "data", "missing_folder", "does_not_exist." + fn_ext) - dirname = os.path.dirname(path) with pytest.raises( error_class, - match=fr"Cannot save file into a non-existent directory: '{dirname}'", + match=r"Cannot save file into a non-existent directory: .*missing_folder", ): method(dummy_frame, path) From 8dc7d8a894bc6dd260663b42a7c3075af45a844e Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Wed, 8 Sep 2021 12:14:11 +0800 Subject: [PATCH 11/17] Minor refactoring and reformatting --- pandas/io/common.py | 17 +++++++++-------- pandas/io/formats/format.py | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pandas/io/common.py b/pandas/io/common.py index d1ee891a9e821..4647a0c4be1a4 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -18,6 +18,7 @@ import mmap import os import tempfile +from pathlib import Path from typing import ( IO, Any, @@ -520,19 +521,19 @@ def infer_compression( raise ValueError(msg) -def check_parent_directory(fpath: str): +def check_parent_directory(path: Path | str) -> None: """ Check if parent directory of a file exists, raise OSError if it does not Parameters ---------- - fpath: str + path: Path | str File path """ - dirname = os.path.dirname(fpath) - if len(dirname) != 0 and not os.path.isdir(dirname): - raise OSError(fr"Cannot save file into a non-existent directory: '{dirname}'") + parent = Path(path).parent + if not parent.is_dir(): + raise OSError(fr"Cannot save file into a non-existent directory: '{parent}'") def get_handle( @@ -647,9 +648,9 @@ def get_handle( compression_args = dict(ioargs.compression) compression = compression_args.pop("method") - # GH 24306 - if mode not in ["r", "rb"] and is_path: # Only for write methods - check_parent_directory(str(handle)) + # Only for write methods + if "r" not in mode and is_path: + check_parent_directory(handle) if compression: # compression libraries do not like an explicit text-mode diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index d636838d21d0e..78e9bb6f0beda 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1150,7 +1150,7 @@ def get_buffer(buf: FilePathOrBuffer[str] | None, encoding: str | None = None): if hasattr(buf, "write"): yield buf elif isinstance(buf, str): - check_parent_directory(str(buf)) + check_parent_directory(buf) with open(buf, "w", encoding=encoding, newline="") as f: # GH#30034 open instead of codecs.open prevents a file leak # if we have an invalid encoding argument. From efadcd0e64e3b4eef8f98408c4233098e5c1d78c Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Wed, 8 Sep 2021 12:42:19 +0800 Subject: [PATCH 12/17] Make mypy happy again, minor refactoring using is_path instead of checking isinstance(handle, str) twice --- pandas/io/common.py | 4 ++-- pandas/io/formats/format.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/io/common.py b/pandas/io/common.py index 4647a0c4be1a4..d04c8aabfb9b2 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -650,7 +650,7 @@ def get_handle( # Only for write methods if "r" not in mode and is_path: - check_parent_directory(handle) + check_parent_directory(str(handle)) if compression: # compression libraries do not like an explicit text-mode @@ -715,7 +715,7 @@ def get_handle( assert not isinstance(handle, str) handles.append(handle) - elif isinstance(handle, str): + elif is_path: # Check whether the filename is to be opened in binary mode. # Binary mode does not support 'encoding' and 'newline'. if ioargs.encoding and "b" not in ioargs.mode: diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 78e9bb6f0beda..d636838d21d0e 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1150,7 +1150,7 @@ def get_buffer(buf: FilePathOrBuffer[str] | None, encoding: str | None = None): if hasattr(buf, "write"): yield buf elif isinstance(buf, str): - check_parent_directory(buf) + check_parent_directory(str(buf)) with open(buf, "w", encoding=encoding, newline="") as f: # GH#30034 open instead of codecs.open prevents a file leak # if we have an invalid encoding argument. From 051b98f79b3887c86f6dd91f1ed4ce5fa0db3a11 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Wed, 8 Sep 2021 13:49:46 +0800 Subject: [PATCH 13/17] Fix mypy and Windows path error --- pandas/io/common.py | 2 +- pandas/tests/io/xml/test_to_xml.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pandas/io/common.py b/pandas/io/common.py index d04c8aabfb9b2..0070ba393d74e 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -715,7 +715,7 @@ def get_handle( assert not isinstance(handle, str) handles.append(handle) - elif is_path: + elif isinstance(handle, str): # Check whether the filename is to be opened in binary mode. # Binary mode does not support 'encoding' and 'newline'. if ioargs.encoding and "b" not in ioargs.mode: diff --git a/pandas/tests/io/xml/test_to_xml.py b/pandas/tests/io/xml/test_to_xml.py index 0d699d48a632f..319658f983433 100644 --- a/pandas/tests/io/xml/test_to_xml.py +++ b/pandas/tests/io/xml/test_to_xml.py @@ -5,6 +5,7 @@ StringIO, ) import os +from pathlib import Path import numpy as np import pytest @@ -202,14 +203,14 @@ def test_str_output(datapath, parser): def test_wrong_file_path(parser): - fpath = "/my/fake/path/output.xml" - dirname = os.path.dirname(fpath) + path = "/my/fake/path/output.xml" + parent = Path(path).parent with pytest.raises( OSError, - match=(fr"Cannot save file into a non-existent directory: '{dirname}'"), + match=(fr"Cannot save file into a non-existent directory: '{parent}'"), ): - geom_df.to_xml(fpath, parser=parser) + geom_df.to_xml(path, parser=parser) # INDEX From 40cf966d67acf34dd184111d1635369571e77a45 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Wed, 8 Sep 2021 21:27:03 +0800 Subject: [PATCH 14/17] Fix Windows path regex error --- pandas/tests/io/xml/test_to_xml.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas/tests/io/xml/test_to_xml.py b/pandas/tests/io/xml/test_to_xml.py index 319658f983433..b8d146c597d2c 100644 --- a/pandas/tests/io/xml/test_to_xml.py +++ b/pandas/tests/io/xml/test_to_xml.py @@ -5,7 +5,6 @@ StringIO, ) import os -from pathlib import Path import numpy as np import pytest @@ -204,11 +203,10 @@ def test_str_output(datapath, parser): def test_wrong_file_path(parser): path = "/my/fake/path/output.xml" - parent = Path(path).parent with pytest.raises( OSError, - match=(fr"Cannot save file into a non-existent directory: '{parent}'"), + match=(r"Cannot save file into a non-existent directory: .*path"), ): geom_df.to_xml(path, parser=parser) From ddb7bafa3c9c8553186b64b2fd4247ad84aac3dd Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Thu, 9 Sep 2021 11:09:30 +0800 Subject: [PATCH 15/17] Slight docstring fix --- pandas/io/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/io/common.py b/pandas/io/common.py index 0070ba393d74e..b60d2897a46ec 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -527,8 +527,8 @@ def check_parent_directory(path: Path | str) -> None: Parameters ---------- - path: Path | str - File path + path: Path or str + Path to check parent directory of """ parent = Path(path).parent From 069a2de5836affed0eb400dd011dc586731fd8c1 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Thu, 9 Sep 2021 16:39:47 +0800 Subject: [PATCH 16/17] Retrigger timed-out test --- doc/source/whatsnew/v1.4.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index f82f355b6be09..328499a4ae98e 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -107,7 +107,7 @@ Other enhancements - :meth:`DataFrame.to_stata` and :meth:`StataWriter` now accept the keyword only argument ``value_labels`` to save labels for non-categorical columns - Methods that relied on hashmap based algos such as :meth:`DataFrameGroupBy.value_counts`, :meth:`DataFrameGroupBy.count` and :func:`factorize` ignored imaginary component for complex numbers (:issue:`17927`) - Add :meth:`Series.str.removeprefix` and :meth:`Series.str.removesuffix` introduced in Python 3.9 to remove pre-/suffixes from string-type :class:`Series` (:issue:`36944`) -- Attempting to write into a file in missing parent directory with :meth:`DataFrame.to_csv`, :meth:`DataFrame.to_html`, :meth:`DataFrame.to_excel`, :meth:`DataFrame.to_feather`, :meth:`DataFrame.to_parquet`, :meth:`DataFrame.to_stata`, :meth:`DataFrame.to_json`, :meth:`DataFrame.to_pickle`, and :meth:`DataFrame.to_xml` now explicitly mentions missing parent directory, the same is true for their :class:`Series` method counterparts (:issue:`24306`) +- Attempting to write into a file in missing parent directory with :meth:`DataFrame.to_csv`, :meth:`DataFrame.to_html`, :meth:`DataFrame.to_excel`, :meth:`DataFrame.to_feather`, :meth:`DataFrame.to_parquet`, :meth:`DataFrame.to_stata`, :meth:`DataFrame.to_json`, :meth:`DataFrame.to_pickle`, and :meth:`DataFrame.to_xml` now explicitly mentions missing parent directory, the same is true for :class:`Series` counterparts (:issue:`24306`) .. --------------------------------------------------------------------------- From fbb1ec33cfe0c55b468fbbfc142d952d23150cfe Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Sat, 11 Sep 2021 12:31:28 +0800 Subject: [PATCH 17/17] Pre-commit run --- pandas/io/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/io/common.py b/pandas/io/common.py index b60d2897a46ec..46be1f9bb09b2 100644 --- a/pandas/io/common.py +++ b/pandas/io/common.py @@ -17,8 +17,8 @@ ) import mmap import os -import tempfile from pathlib import Path +import tempfile from typing import ( IO, Any,