Skip to content

Commit dc45fba

Browse files
BUG: to_clipboard fails to format output for Excel (#21111)
1 parent 0b63e81 commit dc45fba

File tree

3 files changed

+27
-22
lines changed

3 files changed

+27
-22
lines changed

doc/source/whatsnew/v0.23.2.txt

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ Fixed Regressions
5757
- Bug in both :meth:`DataFrame.first_valid_index` and :meth:`Series.first_valid_index` raised for a row index having duplicate values (:issue:`21441`)
5858
- Fixed regression in unary negative operations with object dtype (:issue:`21380`)
5959
- Bug in :meth:`Timestamp.ceil` and :meth:`Timestamp.floor` when timestamp is a multiple of the rounding frequency (:issue:`21262`)
60+
- Fixed regression in :func:`to_clipboard` that defaulted to copying dataframes with space delimited instead of tab delimited (:issue:`21104`)
6061

6162
.. _whatsnew_0232.performance:
6263

pandas/io/clipboards.py

+26-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
""" io on the clipboard """
22
from pandas import compat, get_option, option_context, DataFrame
3-
from pandas.compat import StringIO, PY2
3+
from pandas.compat import StringIO, PY2, PY3
4+
import warnings
45

56

67
def read_clipboard(sep=r'\s+', **kwargs): # pragma: no cover
@@ -32,7 +33,7 @@ def read_clipboard(sep=r'\s+', **kwargs): # pragma: no cover
3233

3334
# try to decode (if needed on PY3)
3435
# Strange. linux py33 doesn't complain, win py33 does
35-
if compat.PY3:
36+
if PY3:
3637
try:
3738
text = compat.bytes_to_str(
3839
text, encoding=(kwargs.get('encoding') or
@@ -55,11 +56,27 @@ def read_clipboard(sep=r'\s+', **kwargs): # pragma: no cover
5556

5657
counts = {x.lstrip().count('\t') for x in lines}
5758
if len(lines) > 1 and len(counts) == 1 and counts.pop() != 0:
58-
sep = r'\t'
59+
sep = '\t'
5960

61+
# Edge case where sep is specified to be None, return to default
6062
if sep is None and kwargs.get('delim_whitespace') is None:
6163
sep = r'\s+'
6264

65+
# Regex separator currently only works with python engine.
66+
# Default to python if separator is multi-character (regex)
67+
if len(sep) > 1 and kwargs.get('engine') is None:
68+
kwargs['engine'] = 'python'
69+
elif len(sep) > 1 and kwargs.get('engine') == 'c':
70+
warnings.warn('read_clipboard with regex separator does not work'
71+
' properly with c engine')
72+
73+
# In PY2, the c table reader first encodes text with UTF-8 but Python
74+
# table reader uses the format of the passed string. For consistency,
75+
# encode strings for python engine so that output from python and c
76+
# engines produce consistent results
77+
if kwargs.get('engine') == 'python' and PY2:
78+
text = text.encode('utf-8')
79+
6380
return read_table(StringIO(text), sep=sep, **kwargs)
6481

6582

@@ -99,7 +116,7 @@ def to_clipboard(obj, excel=True, sep=None, **kwargs): # pragma: no cover
99116
if excel:
100117
try:
101118
if sep is None:
102-
sep = r'\t'
119+
sep = '\t'
103120
buf = StringIO()
104121
# clipboard_set (pyperclip) expects unicode
105122
obj.to_csv(buf, sep=sep, encoding='utf-8', **kwargs)
@@ -108,8 +125,11 @@ def to_clipboard(obj, excel=True, sep=None, **kwargs): # pragma: no cover
108125
text = text.decode('utf-8')
109126
clipboard_set(text)
110127
return
111-
except:
112-
pass
128+
except TypeError:
129+
warnings.warn('to_clipboard in excel mode requires a single '
130+
'character separator.')
131+
elif sep is not None:
132+
warnings.warn('to_clipboard with excel=False ignores the sep argument')
113133

114134
if isinstance(obj, DataFrame):
115135
# str(df) has various unhelpful defaults, like truncation

pandas/tests/io/test_clipboard.py

-16
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ def check_round_trip_frame(self, data, excel=None, sep=None,
8888
tm.assert_frame_equal(data, result, check_dtype=False)
8989

9090
# Test that default arguments copy as tab delimited
91-
@pytest.mark.xfail(reason='to_clipboard defaults to space delim. '
92-
'Issue in #21104, Fixed in #21111')
9391
def test_round_trip_frame(self, df):
9492
self.check_round_trip_frame(df)
9593

@@ -99,10 +97,6 @@ def test_round_trip_frame_sep(self, df, sep):
9997
self.check_round_trip_frame(df, sep=sep)
10098

10199
# Test white space separator
102-
@pytest.mark.xfail(reason="Fails on 'delims' df because quote escapes "
103-
"aren't handled correctly in default c engine. Fixed "
104-
"in #21111 by defaulting to python engine for "
105-
"whitespace separator")
106100
def test_round_trip_frame_string(self, df):
107101
df.to_clipboard(excel=False, sep=None)
108102
result = read_clipboard()
@@ -111,21 +105,17 @@ def test_round_trip_frame_string(self, df):
111105

112106
# Two character separator is not supported in to_clipboard
113107
# Test that multi-character separators are not silently passed
114-
@pytest.mark.xfail(reason="Not yet implemented. Fixed in #21111")
115108
def test_excel_sep_warning(self, df):
116109
with tm.assert_produces_warning():
117110
df.to_clipboard(excel=True, sep=r'\t')
118111

119112
# Separator is ignored when excel=False and should produce a warning
120-
@pytest.mark.xfail(reason="Not yet implemented. Fixed in #21111")
121113
def test_copy_delim_warning(self, df):
122114
with tm.assert_produces_warning():
123115
df.to_clipboard(excel=False, sep='\t')
124116

125117
# Tests that the default behavior of to_clipboard is tab
126118
# delimited and excel="True"
127-
@pytest.mark.xfail(reason="to_clipboard defaults to space delim. Issue in "
128-
"#21104, Fixed in #21111")
129119
@pytest.mark.parametrize('sep', ['\t', None, 'default'])
130120
@pytest.mark.parametrize('excel', [True, None, 'default'])
131121
def test_clipboard_copy_tabs_default(self, sep, excel, df):
@@ -139,10 +129,6 @@ def test_clipboard_copy_tabs_default(self, sep, excel, df):
139129
assert clipboard_get() == df.to_csv(sep='\t')
140130

141131
# Tests reading of white space separated tables
142-
@pytest.mark.xfail(reason="Fails on 'delims' df because quote escapes "
143-
"aren't handled correctly. in default c engine. Fixed "
144-
"in #21111 by defaulting to python engine for "
145-
"whitespace separator")
146132
@pytest.mark.parametrize('sep', [None, 'default'])
147133
@pytest.mark.parametrize('excel', [False])
148134
def test_clipboard_copy_strings(self, sep, excel, df):
@@ -193,8 +179,6 @@ def test_invalid_encoding(self, df):
193179
with pytest.raises(NotImplementedError):
194180
pd.read_clipboard(encoding='ascii')
195181

196-
@pytest.mark.xfail(reason='to_clipboard defaults to space delim. '
197-
'Issue in #21104, Fixed in #21111')
198182
@pytest.mark.parametrize('enc', ['UTF-8', 'utf-8', 'utf8'])
199183
def test_round_trip_valid_encodings(self, enc, df):
200184
self.check_round_trip_frame(df, encoding=enc)

0 commit comments

Comments
 (0)