Skip to content

Commit 31e34c6

Browse files
author
y-p
committed
Merge pull request #3410 from y-p/GH3363
Period.strftime should return unicode strings always
2 parents 9dccc21 + 80549c4 commit 31e34c6

File tree

4 files changed

+88
-76
lines changed

4 files changed

+88
-76
lines changed

pandas/tseries/period.py

+39-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import pandas.core.common as com
1515
from pandas.core.common import isnull
16+
from pandas.util import py3compat
1617

1718
from pandas.lib import Timestamp
1819
import pandas.lib as lib
@@ -264,12 +265,49 @@ def __repr__(self):
264265
base, mult = _gfc(self.freq)
265266
formatted = tslib.period_format(self.ordinal, base)
266267
freqstr = _freq_mod._reverse_period_code_map[base]
268+
269+
if not py3compat.PY3:
270+
encoding = com.get_option("display.encoding")
271+
formatted = formatted.encode(encoding)
272+
267273
return "Period('%s', '%s')" % (formatted, freqstr)
268274

269275
def __str__(self):
276+
"""
277+
Return a string representation for a particular DataFrame
278+
279+
Invoked by str(df) in both py2/py3.
280+
Yields Bytestring in Py2, Unicode String in py3.
281+
"""
282+
283+
if py3compat.PY3:
284+
return self.__unicode__()
285+
return self.__bytes__()
286+
287+
def __bytes__(self):
288+
"""
289+
Return a string representation for a particular DataFrame
290+
291+
Invoked by bytes(df) in py3 only.
292+
Yields a bytestring in both py2/py3.
293+
"""
294+
encoding = com.get_option("display.encoding")
295+
return self.__unicode__().encode(encoding, 'replace')
296+
297+
def __unicode__(self):
298+
"""
299+
Return a string representation for a particular DataFrame
300+
301+
Invoked by unicode(df) in py2 only. Yields a Unicode String in both
302+
py2/py3.
303+
"""
270304
base, mult = _gfc(self.freq)
271305
formatted = tslib.period_format(self.ordinal, base)
272-
return ("%s" % formatted)
306+
value = (u"%s" % formatted)
307+
assert type(value) == unicode
308+
309+
return value
310+
273311

274312
def strftime(self, fmt):
275313
"""

pandas/tseries/tests/test_period.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,9 @@ def test_repr(self):
206206

207207
def test_strftime(self):
208208
p = Period('2000-1-1 12:34:12', freq='S')
209-
self.assert_(p.strftime('%Y-%m-%d %H:%M:%S') ==
210-
'2000-01-01 12:34:12')
209+
res = p.strftime('%Y-%m-%d %H:%M:%S')
210+
self.assert_( res == '2000-01-01 12:34:12')
211+
self.assert_( isinstance(res,unicode)) # GH3363
211212

212213
def test_sub_delta(self):
213214
left, right = Period('2011', freq='A'), Period('2007', freq='A')

pandas/tslib.pyx

+5
Original file line numberDiff line numberDiff line change
@@ -2283,6 +2283,7 @@ cdef list extra_fmts = [(b"%q", b"^`AB`^"),
22832283
cdef list str_extra_fmts = ["^`AB`^", "^`CD`^", "^`EF`^"]
22842284

22852285
cdef _period_strftime(int64_t value, int freq, object fmt):
2286+
import sys
22862287
cdef:
22872288
Py_ssize_t i
22882289
date_info dinfo
@@ -2325,6 +2326,10 @@ cdef _period_strftime(int64_t value, int freq, object fmt):
23252326
if not PyString_Check(result):
23262327
result = str(result)
23272328

2329+
# GH3363
2330+
if sys.version_info[0] == 2:
2331+
result = result.decode('utf-8','strict')
2332+
23282333
return result
23292334

23302335
# period accessors

scripts/use_build_cache.py

+41-73
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,21 @@
1515
Tested on releases back to 0.7.0.
1616
1717
"""
18+
import argparse
19+
argparser = argparse.ArgumentParser(description="""
20+
'Program description.
21+
""".strip())
1822

19-
try:
20-
import argparse
21-
argparser = argparse.ArgumentParser(description="""
22-
'Program description.
23-
""".strip())
24-
25-
argparser.add_argument('-f', '--force-overwrite',
23+
argparser.add_argument('-f', '--force-overwrite',
2624
default=False,
2725
help='Setting this will overwrite any existing cache results for the current commit',
2826
action='store_true')
29-
argparser.add_argument('-d', '--debug',
27+
argparser.add_argument('-d', '--debug',
3028
default=False,
3129
help='Report cache hits/misses',
3230
action='store_true')
3331

34-
args = argparser.parse_args()
35-
except:
36-
class Foo(object):
37-
debug=False
38-
force_overwrite=False
39-
40-
args = Foo() # for 2.6, no argparse
32+
args = argparser.parse_args()
4133

4234
#print args.accumulate(args.integers)
4335

@@ -78,28 +70,18 @@ class Foo(object):
7870
import shutil
7971
import multiprocessing
8072
pyver = "%d.%d" % (sys.version_info[:2])
81-
fileq = ["pandas"]
73+
files = ["pandas"]
8274
to_process = dict()
75+
orig_hashes= dict((f.split("-")[0],f) for f in os.listdir(BUILD_CACHE_DIR)
76+
if "-" in f and f.endswith(pyver))
77+
post_hashes= dict((f.split("-")[1],f) for f in os.listdir(BUILD_CACHE_DIR)
78+
if "-" in f and f.endswith(pyver))
8379
84-
# retrieve the hashes existing in the cache
85-
orig_hashes=dict()
86-
post_hashes=dict()
87-
for path,dirs,files in os.walk(os.path.join(BUILD_CACHE_DIR,'pandas')):
88-
for f in files:
89-
s=f.split(".py-")[-1]
90-
try:
91-
prev_h,post_h,ver = s.split('-')
92-
if ver == pyver:
93-
orig_hashes[prev_h] = os.path.join(path,f)
94-
post_hashes[post_h] = os.path.join(path,f)
95-
except:
96-
pass
97-
98-
while fileq:
99-
f = fileq.pop()
80+
while files:
81+
f = files.pop()
10082
10183
if os.path.isdir(f):
102-
fileq.extend([os.path.join(f,x) for x in os.listdir(f)])
84+
files.extend([os.path.join(f,x) for x in os.listdir(f)])
10385
else:
10486
if not f.endswith(".py"):
10587
continue
@@ -108,54 +90,40 @@ class Foo(object):
10890
h = sha1(open(f,"rb").read()).hexdigest()
10991
except IOError:
11092
to_process[h] = f
111-
else:
112-
if h in orig_hashes and not BC_FORCE_OVERWRITE:
113-
src = orig_hashes[h]
114-
if BC_DEBUG:
115-
print("2to3 cache hit %s,%s" % (f,h))
116-
shutil.copyfile(src,f)
117-
elif h not in post_hashes:
118-
# we're not in a dev dir with already processed files
119-
if BC_DEBUG:
120-
print("2to3 cache miss (will process) %s,%s" % (f,h))
121-
to_process[h] = f
93+
if h in orig_hashes and not BC_FORCE_OVERWRITE:
94+
src = os.path.join(BUILD_CACHE_DIR,orig_hashes[h])
95+
if BC_DEBUG:
96+
print("2to3 cache hit %s,%s" % (f,h))
97+
shutil.copyfile(src,f)
98+
elif h not in post_hashes:
99+
100+
# we're not in a dev dir with already processed files
101+
if BC_DEBUG:
102+
print("2to3 cache miss %s,%s" % (f,h))
103+
print("2to3 will process " + f)
104+
to_process[h] = f
122105
123106
avail_fixes = set(refactor.get_fixers_from_package("lib2to3.fixes"))
124107
avail_fixes.discard('lib2to3.fixes.fix_next')
125108
t=refactor.RefactoringTool(avail_fixes)
126-
if to_process:
127-
print("Starting 2to3 refactoring...")
128-
for orig_h,f in to_process.items():
109+
print("Starting 2to3 refactoring...")
110+
for f in to_process.values():
111+
if BC_DEBUG:
112+
print("2to3 on %s" % f)
113+
try:
114+
t.refactor([f],True)
115+
post_h = sha1(open(f, "rb").read()).hexdigest()
116+
cached_fname = f + "-" + post_h + "-" + pyver
129117
if BC_DEBUG:
130-
print("2to3 on %s" % f)
131-
try:
132-
t.refactor([f],True)
133-
post_h = sha1(open(f, "rb").read()).hexdigest()
134-
cached_fname = f + '-' + orig_h + '-' + post_h + '-' + pyver
135-
path = os.path.join(BUILD_CACHE_DIR, cached_fname)
136-
pathdir =os.path.dirname(path)
137-
if BC_DEBUG:
138-
print("cache put %s in %s" % (f, path))
139-
try:
140-
os.makedirs(pathdir)
141-
except OSError as exc:
142-
import errno
143-
if exc.errno == errno.EEXIST and os.path.isdir(pathdir):
144-
pass
145-
else:
146-
raise
147-
148-
shutil.copyfile(f, path)
118+
print("cache put %s,%s in %s" % (f, h, cached_fname))
119+
shutil.copyfile(f, os.path.join(BUILD_CACHE_DIR, cached_fname))
149120
150-
except Exception as e:
151-
print("While processing %s 2to3 raised: %s" % (f,str(e)))
152-
153-
pass
154-
print("2to3 done refactoring.")
121+
except:
122+
pass
123+
print("2to3 done refactoring.")
155124
156125
except Exception as e:
157-
if not isinstance(e,ZeroDivisionError):
158-
print( "Exception: " + str(e))
126+
print( "Exception: " + str(e))
159127
BUILD_CACHE_DIR = None
160128
161129
class CompilationCacheMixin(object):

0 commit comments

Comments
 (0)