Skip to content

Commit a3894fd

Browse files
meeseeksmachinemroeschkephofl
authored
Backport PR pandas-dev#51443 on branch 2.0.x (CI/TST: Error on PytestUnraisableExceptionWarning instead of using psutil to check open resources) (pandas-dev#51507)
Backport PR pandas-dev#51443: CI/TST: Error on PytestUnraisableExceptionWarning instead of using psutil to check open resources Co-authored-by: Matthew Roeschke <[email protected]> Co-authored-by: Patrick Hoefler <[email protected]>
1 parent fbb9b34 commit a3894fd

25 files changed

+47
-156
lines changed

ci/deps/actions-310.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ dependencies:
1212
- pytest>=7.0.0
1313
- pytest-cov
1414
- pytest-xdist>=2.2.0
15-
- psutil
1615
- pytest-asyncio>=0.17
1716
- boto3
1817

ci/deps/actions-311.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ dependencies:
1212
- pytest>=7.0
1313
- pytest-cov
1414
- pytest-xdist>=2.2.0
15-
- psutil
1615
- pytest-asyncio>=0.17
1716
- boto3
1817

ci/deps/actions-38-downstream_compat.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ dependencies:
1313
- pytest>=7.0.0
1414
- pytest-cov
1515
- pytest-xdist>=2.2.0
16-
- psutil
1716
- pytest-asyncio>=0.17
1817
- boto3
1918

ci/deps/actions-38-minimum_versions.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ dependencies:
1414
- pytest>=7.0.0
1515
- pytest-cov
1616
- pytest-xdist>=2.2.0
17-
- psutil
1817
- pytest-asyncio>=0.17
1918
- boto3
2019

ci/deps/actions-38.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ dependencies:
1212
- pytest>=7.0.0
1313
- pytest-cov
1414
- pytest-xdist>=2.2.0
15-
- psutil
1615
- pytest-asyncio>=0.17
1716
- boto3
1817

ci/deps/actions-39.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ dependencies:
1212
- pytest>=7.0.0
1313
- pytest-cov
1414
- pytest-xdist>=2.2.0
15-
- psutil
1615
- pytest-asyncio>=0.17
1716
- boto3
1817

ci/deps/circle-38-arm64.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ dependencies:
1212
- pytest>=7.0.0
1313
- pytest-cov
1414
- pytest-xdist>=2.2.0
15-
- psutil
1615
- pytest-asyncio>=0.17
1716
- boto3
1817

doc/source/whatsnew/v2.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,7 @@ I/O
13281328
- Bug in :meth:`DataFrame.to_html` with ``na_rep`` set when the :class:`DataFrame` contains non-scalar data (:issue:`47103`)
13291329
- Bug in :func:`read_xml` where file-like objects failed when iterparse is used (:issue:`50641`)
13301330
- Bug in :func:`read_xml` ignored repeated elements when iterparse is used (:issue:`51183`)
1331+
- Bug in :class:`ExcelWriter` leaving file handles open if an exception occurred during instantiation (:issue:`51443`)
13311332

13321333
Period
13331334
^^^^^^

environment.yml

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ dependencies:
1414
- pytest>=7.0.0
1515
- pytest-cov
1616
- pytest-xdist>=2.2.0
17-
- psutil
1817
- pytest-asyncio>=0.17
1918
- coverage
2019

pandas/_testing/_warnings.py

-17
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
nullcontext,
66
)
77
import re
8-
import sys
98
from typing import (
109
Generator,
1110
Literal,
@@ -164,22 +163,6 @@ def _assert_caught_no_extra_warnings(
164163

165164
for actual_warning in caught_warnings:
166165
if _is_unexpected_warning(actual_warning, expected_warning):
167-
# GH#38630 pytest.filterwarnings does not suppress these.
168-
if actual_warning.category == ResourceWarning:
169-
# GH 44732: Don't make the CI flaky by filtering SSL-related
170-
# ResourceWarning from dependencies
171-
unclosed_ssl = (
172-
"unclosed transport <asyncio.sslproto._SSLProtocolTransport",
173-
"unclosed <ssl.SSLSocket",
174-
)
175-
if any(msg in str(actual_warning.message) for msg in unclosed_ssl):
176-
continue
177-
# GH 44844: Matplotlib leaves font files open during the entire process
178-
# upon import. Don't make CI flaky if ResourceWarning raised
179-
# due to these open files.
180-
if any("matplotlib" in mod for mod in sys.modules):
181-
continue
182-
183166
extra_warnings.append(
184167
(
185168
actual_warning.category.__name__,

pandas/io/excel/_base.py

+11-19
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,17 @@ def __init__(
12101210
# the excel backend first read the existing file and then write any data to it
12111211
mode = mode.replace("a", "r+")
12121212

1213+
if if_sheet_exists not in (None, "error", "new", "replace", "overlay"):
1214+
raise ValueError(
1215+
f"'{if_sheet_exists}' is not valid for if_sheet_exists. "
1216+
"Valid options are 'error', 'new', 'replace' and 'overlay'."
1217+
)
1218+
if if_sheet_exists and "r+" not in mode:
1219+
raise ValueError("if_sheet_exists is only valid in append mode (mode='a')")
1220+
if if_sheet_exists is None:
1221+
if_sheet_exists = "error"
1222+
self._if_sheet_exists = if_sheet_exists
1223+
12131224
# cast ExcelWriter to avoid adding 'if self._handles is not None'
12141225
self._handles = IOHandles(
12151226
cast(IO[bytes], path), compression={"compression": None}
@@ -1231,17 +1242,6 @@ def __init__(
12311242

12321243
self._mode = mode
12331244

1234-
if if_sheet_exists not in (None, "error", "new", "replace", "overlay"):
1235-
raise ValueError(
1236-
f"'{if_sheet_exists}' is not valid for if_sheet_exists. "
1237-
"Valid options are 'error', 'new', 'replace' and 'overlay'."
1238-
)
1239-
if if_sheet_exists and "r+" not in mode:
1240-
raise ValueError("if_sheet_exists is only valid in append mode (mode='a')")
1241-
if if_sheet_exists is None:
1242-
if_sheet_exists = "error"
1243-
self._if_sheet_exists = if_sheet_exists
1244-
12451245
@property
12461246
def date_format(self) -> str:
12471247
"""
@@ -1602,11 +1602,3 @@ def __exit__(
16021602
traceback: TracebackType | None,
16031603
) -> None:
16041604
self.close()
1605-
1606-
def __del__(self) -> None:
1607-
# Ensure we don't leak file descriptors, but put in try/except in case
1608-
# attributes are already deleted
1609-
try:
1610-
self.close()
1611-
except AttributeError:
1612-
pass

pandas/io/excel/_odswriter.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ def __init__(
4848
if mode == "a":
4949
raise ValueError("Append mode is not supported with odf!")
5050

51+
engine_kwargs = combine_kwargs(engine_kwargs, kwargs)
52+
self._book = OpenDocumentSpreadsheet(**engine_kwargs)
53+
5154
super().__init__(
5255
path,
5356
mode=mode,
@@ -56,9 +59,6 @@ def __init__(
5659
engine_kwargs=engine_kwargs,
5760
)
5861

59-
engine_kwargs = combine_kwargs(engine_kwargs, kwargs)
60-
61-
self._book = OpenDocumentSpreadsheet(**engine_kwargs)
6262
self._style_dict: dict[str, str] = {}
6363

6464
@property

pandas/io/excel/_openpyxl.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,19 @@ def __init__(
7070
if "r+" in self._mode: # Load from existing workbook
7171
from openpyxl import load_workbook
7272

73-
self._book = load_workbook(self._handles.handle, **engine_kwargs)
73+
try:
74+
self._book = load_workbook(self._handles.handle, **engine_kwargs)
75+
except TypeError:
76+
self._handles.handle.close()
77+
raise
7478
self._handles.handle.seek(0)
7579
else:
7680
# Create workbook object with default optimized_write=True.
77-
self._book = Workbook(**engine_kwargs)
81+
try:
82+
self._book = Workbook(**engine_kwargs)
83+
except TypeError:
84+
self._handles.handle.close()
85+
raise
7886

7987
if self.book.worksheets:
8088
self.book.remove(self.book.worksheets[0])

pandas/tests/frame/test_api.py

-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
from pandas._config.config import option_context
99

10-
import pandas.util._test_decorators as td
1110
from pandas.util._test_decorators import (
1211
async_mark,
1312
skip_if_no,
@@ -293,7 +292,6 @@ def _check_f(base, f):
293292
_check_f(d.copy(), f)
294293

295294
@async_mark()
296-
@td.check_file_leaks
297295
async def test_tab_complete_warning(self, ip, frame_or_series):
298296
# GH 16409
299297
pytest.importorskip("IPython", minversion="6.0.0")

pandas/tests/io/excel/conftest.py

-26
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import pytest
22

3-
from pandas.compat import is_platform_windows
4-
import pandas.util._test_decorators as td
5-
63
import pandas._testing as tm
74

85
from pandas.io.parsers import read_csv
@@ -42,26 +39,3 @@ def read_ext(request):
4239
Valid extensions for reading Excel files.
4340
"""
4441
return request.param
45-
46-
47-
# Checking for file leaks can hang on Windows CI
48-
@pytest.fixture(autouse=not is_platform_windows())
49-
def check_for_file_leaks():
50-
"""
51-
Fixture to run around every test to ensure that we are not leaking files.
52-
53-
See also
54-
--------
55-
_test_decorators.check_file_leaks
56-
"""
57-
# GH#30162
58-
psutil = td.safe_import("psutil")
59-
if not psutil:
60-
yield
61-
62-
else:
63-
proc = psutil.Process()
64-
flist = proc.open_files()
65-
yield
66-
flist2 = proc.open_files()
67-
assert flist == flist2

pandas/tests/io/excel/test_readers.py

-2
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,6 @@ def test_read_from_pathlib_path(self, read_ext):
909909
tm.assert_frame_equal(expected, actual)
910910

911911
@td.skip_if_no("py.path")
912-
@td.check_file_leaks
913912
def test_read_from_py_localpath(self, read_ext):
914913
# GH12655
915914
from py.path import local as LocalPath
@@ -922,7 +921,6 @@ def test_read_from_py_localpath(self, read_ext):
922921

923922
tm.assert_frame_equal(expected, actual)
924923

925-
@td.check_file_leaks
926924
def test_close_from_py_localpath(self, read_ext):
927925
# GH31467
928926
str_path = os.path.join("test1" + read_ext)

pandas/tests/io/formats/test_format.py

-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
IS64,
2727
is_platform_windows,
2828
)
29-
import pandas.util._test_decorators as td
3029

3130
import pandas as pd
3231
from pandas import (
@@ -3400,7 +3399,6 @@ def test_format_percentiles_integer_idx():
34003399
assert result == expected
34013400

34023401

3403-
@td.check_file_leaks
34043402
def test_repr_html_ipython_config(ip):
34053403
code = textwrap.dedent(
34063404
"""\

pandas/tests/io/parser/common/test_file_buffer_url.py

+3-15
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
import pytest
1515

16-
from pandas.compat import is_ci_environment
1716
from pandas.errors import (
1817
EmptyDataError,
1918
ParserError,
@@ -404,25 +403,14 @@ def test_context_manageri_user_provided(all_parsers, datapath):
404403
assert not reader.handles.handle.closed
405404

406405

407-
def test_file_descriptor_leak(all_parsers, using_copy_on_write, request):
406+
def test_file_descriptor_leak(all_parsers, using_copy_on_write):
408407
# GH 31488
409-
if using_copy_on_write and is_ci_environment():
410-
mark = pytest.mark.xfail(
411-
reason="2023-02-12 frequent-but-flaky failures", strict=False
412-
)
413-
request.node.add_marker(mark)
414-
415408
parser = all_parsers
416409
with tm.ensure_clean() as path:
417-
418-
def test():
419-
with pytest.raises(EmptyDataError, match="No columns to parse from file"):
420-
parser.read_csv(path)
421-
422-
td.check_file_leaks(test)()
410+
with pytest.raises(EmptyDataError, match="No columns to parse from file"):
411+
parser.read_csv(path)
423412

424413

425-
@td.check_file_leaks
426414
def test_memory_map(all_parsers, csv_dir_path):
427415
mmap_file = os.path.join(csv_dir_path, "test_mmap.csv")
428416
parser = all_parsers

pandas/tests/io/parser/common/test_read_errors.py

-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
EmptyDataError,
1818
ParserError,
1919
)
20-
import pandas.util._test_decorators as td
2120

2221
from pandas import DataFrame
2322
import pandas._testing as tm
@@ -204,7 +203,6 @@ def test_null_byte_char(request, all_parsers):
204203
parser.read_csv(StringIO(data), names=names)
205204

206205

207-
@td.check_file_leaks
208206
def test_open_file(request, all_parsers):
209207
# GH 39024
210208
parser = all_parsers

pandas/tests/io/sas/test_xport.py

+3-11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import numpy as np
22
import pytest
33

4-
import pandas.util._test_decorators as td
5-
64
import pandas as pd
75
import pandas._testing as tm
86

@@ -21,11 +19,6 @@ def numeric_as_float(data):
2119

2220

2321
class TestXport:
24-
@pytest.fixture(autouse=True)
25-
def setup_method(self):
26-
with td.file_leak_context():
27-
yield
28-
2922
@pytest.fixture
3023
def file01(self, datapath):
3124
return datapath("io", "sas", "data", "DEMO_G.xpt")
@@ -138,10 +131,9 @@ def test2_binary(self, file02):
138131
numeric_as_float(data_csv)
139132

140133
with open(file02, "rb") as fd:
141-
with td.file_leak_context():
142-
# GH#35693 ensure that if we pass an open file, we
143-
# dont incorrectly close it in read_sas
144-
data = read_sas(fd, format="xport")
134+
# GH#35693 ensure that if we pass an open file, we
135+
# dont incorrectly close it in read_sas
136+
data = read_sas(fd, format="xport")
145137

146138
tm.assert_frame_equal(data, data_csv)
147139

pandas/tests/resample/test_resampler_grouper.py

-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import numpy as np
44
import pytest
55

6-
import pandas.util._test_decorators as td
76
from pandas.util._test_decorators import async_mark
87

98
import pandas as pd
@@ -24,7 +23,6 @@
2423

2524

2625
@async_mark()
27-
@td.check_file_leaks
2826
async def test_tab_complete_ipython6_warning(ip):
2927
from IPython.core.completer import provisionalcompleter
3028

0 commit comments

Comments
 (0)