PageRenderTime 28ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/dev-python/pandas/files/pandas-0.19.1-seqf.patch

https://gitlab.com/argent/portage
Patch | 357 lines | 332 code | 25 blank | 0 comment | 0 complexity | 47f8a799f830e5ee2727dc33daf2395a MD5 | raw file
  1. From f8bd08e9c2fc6365980f41b846bbae4b40f08b83 Mon Sep 17 00:00:00 2001
  2. From: Jeff Reback <jeff@reback.net>
  3. Date: Sat, 12 Nov 2016 10:58:54 -0500
  4. Subject: [PATCH] BUG: segfault manifesting with dateutil=2.6 w.r.t. replace
  5. when timezones are present
  6. closes #14621
  7. Author: Jeff Reback <jeff@reback.net>
  8. Closes #14631 from jreback/replace and squashes the following commits:
  9. 3f95042 [Jeff Reback] BUG: segfault manifesting with dateutil=2.6 w.r.t. replace when timezones are present
  10. ---
  11. ci/requirements-3.5_OSX.pip | 2 +-
  12. doc/source/whatsnew/v0.19.2.txt | 3 ++
  13. pandas/tseries/offsets.py | 1 +
  14. pandas/tseries/tests/test_offsets.py | 20 ++++---
  15. pandas/tseries/tests/test_timezones.py | 89 +++++++++++++++++++++++++++++--
  16. pandas/tseries/tests/test_tslib.py | 5 +-
  17. pandas/tslib.pyx | 95 ++++++++++++++++++++++++++++------
  18. 7 files changed, 188 insertions(+), 27 deletions(-)
  19. diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py
  20. index 051cc8aa4..2e3852a7e 100644
  21. --- a/pandas/tseries/offsets.py
  22. +++ b/pandas/tseries/offsets.py
  23. @@ -68,6 +68,7 @@ def apply_wraps(func):
  24. other = other.tz_localize(None)
  25. result = func(self, other)
  26. +
  27. if self._adjust_dst:
  28. result = tslib._localize_pydatetime(result, tz)
  29. diff --git a/pandas/tseries/tests/test_offsets.py b/pandas/tseries/tests/test_offsets.py
  30. index 1735ac4e2..768e9212e 100644
  31. --- a/pandas/tseries/tests/test_offsets.py
  32. +++ b/pandas/tseries/tests/test_offsets.py
  33. @@ -1,4 +1,5 @@
  34. import os
  35. +from distutils.version import LooseVersion
  36. from datetime import date, datetime, timedelta
  37. from dateutil.relativedelta import relativedelta
  38. from pandas.compat import range, iteritems
  39. @@ -4851,6 +4852,7 @@ class TestDST(tm.TestCase):
  40. def _test_offset(self, offset_name, offset_n, tstart, expected_utc_offset):
  41. offset = DateOffset(**{offset_name: offset_n})
  42. +
  43. t = tstart + offset
  44. if expected_utc_offset is not None:
  45. self.assertTrue(get_utc_offset_hours(t) == expected_utc_offset)
  46. @@ -4890,17 +4892,23 @@ class TestDST(tm.TestCase):
  47. return Timestamp(string + offset_string).tz_convert(tz)
  48. def test_fallback_plural(self):
  49. - """test moving from daylight savings to standard time"""
  50. + # test moving from daylight savings to standard time
  51. + import dateutil
  52. for tz, utc_offsets in self.timezone_utc_offsets.items():
  53. hrs_pre = utc_offsets['utc_offset_daylight']
  54. hrs_post = utc_offsets['utc_offset_standard']
  55. - self._test_all_offsets(
  56. - n=3, tstart=self._make_timestamp(self.ts_pre_fallback,
  57. - hrs_pre, tz),
  58. - expected_utc_offset=hrs_post)
  59. +
  60. + if dateutil.__version__ != LooseVersion('2.6.0'):
  61. + # buggy ambiguous behavior in 2.6.0
  62. + # GH 14621
  63. + # https://github.com/dateutil/dateutil/issues/321
  64. + self._test_all_offsets(
  65. + n=3, tstart=self._make_timestamp(self.ts_pre_fallback,
  66. + hrs_pre, tz),
  67. + expected_utc_offset=hrs_post)
  68. def test_springforward_plural(self):
  69. - """test moving from standard to daylight savings"""
  70. + # test moving from standard to daylight savings
  71. for tz, utc_offsets in self.timezone_utc_offsets.items():
  72. hrs_pre = utc_offsets['utc_offset_standard']
  73. hrs_post = utc_offsets['utc_offset_daylight']
  74. diff --git a/pandas/tseries/tests/test_timezones.py b/pandas/tseries/tests/test_timezones.py
  75. index 00e8ee631..db8cda5c7 100644
  76. --- a/pandas/tseries/tests/test_timezones.py
  77. +++ b/pandas/tseries/tests/test_timezones.py
  78. @@ -4,7 +4,7 @@ import nose
  79. import numpy as np
  80. import pytz
  81. -
  82. +from distutils.version import LooseVersion
  83. from pandas.types.dtypes import DatetimeTZDtype
  84. from pandas import (Index, Series, DataFrame, isnull, Timestamp)
  85. @@ -518,8 +518,12 @@ class TestTimeZoneSupportPytz(tm.TestCase):
  86. times = date_range("2013-10-26 23:00", "2013-10-27 01:00", freq="H",
  87. tz=tz, ambiguous='infer')
  88. - self.assertEqual(times[0], Timestamp('2013-10-26 23:00', tz=tz))
  89. - self.assertEqual(times[-1], Timestamp('2013-10-27 01:00', tz=tz))
  90. + self.assertEqual(times[0], Timestamp('2013-10-26 23:00', tz=tz,
  91. + freq="H"))
  92. + if dateutil.__version__ != LooseVersion('2.6.0'):
  93. + # GH 14621
  94. + self.assertEqual(times[-1], Timestamp('2013-10-27 01:00', tz=tz,
  95. + freq="H"))
  96. def test_ambiguous_nat(self):
  97. tz = self.tz('US/Eastern')
  98. @@ -1163,6 +1167,85 @@ class TestTimeZones(tm.TestCase):
  99. def setUp(self):
  100. tm._skip_if_no_pytz()
  101. + def test_replace(self):
  102. + # GH 14621
  103. + # GH 7825
  104. + # replacing datetime components with and w/o presence of a timezone
  105. + dt = Timestamp('2016-01-01 09:00:00')
  106. + result = dt.replace(hour=0)
  107. + expected = Timestamp('2016-01-01 00:00:00')
  108. + self.assertEqual(result, expected)
  109. +
  110. + for tz in self.timezones:
  111. + dt = Timestamp('2016-01-01 09:00:00', tz=tz)
  112. + result = dt.replace(hour=0)
  113. + expected = Timestamp('2016-01-01 00:00:00', tz=tz)
  114. + self.assertEqual(result, expected)
  115. +
  116. + # we preserve nanoseconds
  117. + dt = Timestamp('2016-01-01 09:00:00.000000123', tz=tz)
  118. + result = dt.replace(hour=0)
  119. + expected = Timestamp('2016-01-01 00:00:00.000000123', tz=tz)
  120. + self.assertEqual(result, expected)
  121. +
  122. + # test all
  123. + dt = Timestamp('2016-01-01 09:00:00.000000123', tz=tz)
  124. + result = dt.replace(year=2015, month=2, day=2, hour=0, minute=5,
  125. + second=5, microsecond=5, nanosecond=5)
  126. + expected = Timestamp('2015-02-02 00:05:05.000005005', tz=tz)
  127. + self.assertEqual(result, expected)
  128. +
  129. + # error
  130. + def f():
  131. + dt.replace(foo=5)
  132. + self.assertRaises(ValueError, f)
  133. +
  134. + def f():
  135. + dt.replace(hour=0.1)
  136. + self.assertRaises(ValueError, f)
  137. +
  138. + # assert conversion to naive is the same as replacing tzinfo with None
  139. + dt = Timestamp('2013-11-03 01:59:59.999999-0400', tz='US/Eastern')
  140. + self.assertEqual(dt.tz_localize(None), dt.replace(tzinfo=None))
  141. +
  142. + def test_ambiguous_compat(self):
  143. + # validate that pytz and dateutil are compat for dst
  144. + # when the transition happens
  145. + tm._skip_if_no_dateutil()
  146. + tm._skip_if_no_pytz()
  147. +
  148. + pytz_zone = 'Europe/London'
  149. + dateutil_zone = 'dateutil/Europe/London'
  150. + result_pytz = (Timestamp('2013-10-27 01:00:00')
  151. + .tz_localize(pytz_zone, ambiguous=0))
  152. + result_dateutil = (Timestamp('2013-10-27 01:00:00')
  153. + .tz_localize(dateutil_zone, ambiguous=0))
  154. + self.assertEqual(result_pytz.value, result_dateutil.value)
  155. + self.assertEqual(result_pytz.value, 1382835600000000000)
  156. +
  157. + # dateutil 2.6 buggy w.r.t. ambiguous=0
  158. + if dateutil.__version__ != LooseVersion('2.6.0'):
  159. + # GH 14621
  160. + # https://github.com/dateutil/dateutil/issues/321
  161. + self.assertEqual(result_pytz.to_pydatetime().tzname(),
  162. + result_dateutil.to_pydatetime().tzname())
  163. + self.assertEqual(str(result_pytz), str(result_dateutil))
  164. +
  165. + # 1 hour difference
  166. + result_pytz = (Timestamp('2013-10-27 01:00:00')
  167. + .tz_localize(pytz_zone, ambiguous=1))
  168. + result_dateutil = (Timestamp('2013-10-27 01:00:00')
  169. + .tz_localize(dateutil_zone, ambiguous=1))
  170. + self.assertEqual(result_pytz.value, result_dateutil.value)
  171. + self.assertEqual(result_pytz.value, 1382832000000000000)
  172. +
  173. + # dateutil < 2.6 is buggy w.r.t. ambiguous timezones
  174. + if dateutil.__version__ > LooseVersion('2.5.3'):
  175. + # GH 14621
  176. + self.assertEqual(str(result_pytz), str(result_dateutil))
  177. + self.assertEqual(result_pytz.to_pydatetime().tzname(),
  178. + result_dateutil.to_pydatetime().tzname())
  179. +
  180. def test_index_equals_with_tz(self):
  181. left = date_range('1/1/2011', periods=100, freq='H', tz='utc')
  182. right = date_range('1/1/2011', periods=100, freq='H', tz='US/Eastern')
  183. diff --git a/pandas/tseries/tests/test_tslib.py b/pandas/tseries/tests/test_tslib.py
  184. index 21cfe84f1..b45f867be 100644
  185. --- a/pandas/tseries/tests/test_tslib.py
  186. +++ b/pandas/tseries/tests/test_tslib.py
  187. @@ -327,8 +327,9 @@ class TestTimestamp(tm.TestCase):
  188. # dateutil zone change (only matters for repr)
  189. import dateutil
  190. - if dateutil.__version__ >= LooseVersion(
  191. - '2.3') and dateutil.__version__ <= LooseVersion('2.4.0'):
  192. + if (dateutil.__version__ >= LooseVersion('2.3') and
  193. + (dateutil.__version__ <= LooseVersion('2.4.0') or
  194. + dateutil.__version__ >= LooseVersion('2.6.0'))):
  195. timezones = ['UTC', 'Asia/Tokyo', 'US/Eastern',
  196. 'dateutil/US/Pacific']
  197. else:
  198. diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx
  199. index d4eaaa0b5..685de214c 100644
  200. --- a/pandas/tslib.pyx
  201. +++ b/pandas/tslib.pyx
  202. @@ -98,6 +98,7 @@ except NameError: # py3
  203. cdef inline object create_timestamp_from_ts(
  204. int64_t value, pandas_datetimestruct dts,
  205. object tz, object freq):
  206. + """ convenience routine to construct a Timestamp from its parts """
  207. cdef _Timestamp ts_base
  208. ts_base = _Timestamp.__new__(Timestamp, dts.year, dts.month,
  209. dts.day, dts.hour, dts.min,
  210. @@ -112,6 +113,7 @@ cdef inline object create_timestamp_from_ts(
  211. cdef inline object create_datetime_from_ts(
  212. int64_t value, pandas_datetimestruct dts,
  213. object tz, object freq):
  214. + """ convenience routine to construct a datetime.datetime from its parts """
  215. return datetime(dts.year, dts.month, dts.day, dts.hour,
  216. dts.min, dts.sec, dts.us, tz)
  217. @@ -378,7 +380,6 @@ class Timestamp(_Timestamp):
  218. # Mixing pydatetime positional and keyword arguments is forbidden!
  219. cdef _TSObject ts
  220. - cdef _Timestamp ts_base
  221. if offset is not None:
  222. # deprecate offset kwd in 0.19.0, GH13593
  223. @@ -412,17 +413,7 @@ class Timestamp(_Timestamp):
  224. from pandas.tseries.frequencies import to_offset
  225. freq = to_offset(freq)
  226. - # make datetime happy
  227. - ts_base = _Timestamp.__new__(cls, ts.dts.year, ts.dts.month,
  228. - ts.dts.day, ts.dts.hour, ts.dts.min,
  229. - ts.dts.sec, ts.dts.us, ts.tzinfo)
  230. -
  231. - # fill out rest of data
  232. - ts_base.value = ts.value
  233. - ts_base.freq = freq
  234. - ts_base.nanosecond = ts.dts.ps / 1000
  235. -
  236. - return ts_base
  237. + return create_timestamp_from_ts(ts.value, ts.dts, ts.tzinfo, freq)
  238. def _round(self, freq, rounder):
  239. @@ -660,8 +651,80 @@ class Timestamp(_Timestamp):
  240. astimezone = tz_convert
  241. def replace(self, **kwds):
  242. - return Timestamp(datetime.replace(self, **kwds),
  243. - freq=self.freq)
  244. + """
  245. + implements datetime.replace, handles nanoseconds
  246. +
  247. + Parameters
  248. + ----------
  249. + kwargs: key-value dict
  250. +
  251. + accepted keywords are:
  252. + year, month, day, hour, minute, second, microsecond, nanosecond, tzinfo
  253. +
  254. + values must be integer, or for tzinfo, a tz-convertible
  255. +
  256. + Returns
  257. + -------
  258. + Timestamp with fields replaced
  259. + """
  260. +
  261. + cdef:
  262. + pandas_datetimestruct dts
  263. + int64_t value
  264. + object tzinfo, result, k, v
  265. + _TSObject ts
  266. +
  267. + # set to naive if needed
  268. + tzinfo = self.tzinfo
  269. + value = self.value
  270. + if tzinfo is not None:
  271. + value = tz_convert_single(value, 'UTC', tzinfo)
  272. +
  273. + # setup components
  274. + pandas_datetime_to_datetimestruct(value, PANDAS_FR_ns, &dts)
  275. + dts.ps = self.nanosecond * 1000
  276. +
  277. + # replace
  278. + def validate(k, v):
  279. + """ validate integers """
  280. + if not isinstance(v, int):
  281. + raise ValueError("value must be an integer, received {v} for {k}".format(v=type(v), k=k))
  282. + return v
  283. +
  284. + for k, v in kwds.items():
  285. + if k == 'year':
  286. + dts.year = validate(k, v)
  287. + elif k == 'month':
  288. + dts.month = validate(k, v)
  289. + elif k == 'day':
  290. + dts.day = validate(k, v)
  291. + elif k == 'hour':
  292. + dts.hour = validate(k, v)
  293. + elif k == 'minute':
  294. + dts.min = validate(k, v)
  295. + elif k == 'second':
  296. + dts.sec = validate(k, v)
  297. + elif k == 'microsecond':
  298. + dts.us = validate(k, v)
  299. + elif k == 'nanosecond':
  300. + dts.ps = validate(k, v) * 1000
  301. + elif k == 'tzinfo':
  302. + tzinfo = v
  303. + else:
  304. + raise ValueError("invalid name {} passed".format(k))
  305. +
  306. + # reconstruct & check bounds
  307. + value = pandas_datetimestruct_to_datetime(PANDAS_FR_ns, &dts)
  308. + if value != NPY_NAT:
  309. + _check_dts_bounds(&dts)
  310. +
  311. + # set tz if needed
  312. + if tzinfo is not None:
  313. + value = tz_convert_single(value, tzinfo, 'UTC')
  314. +
  315. + result = create_timestamp_from_ts(value, dts, tzinfo, self.freq)
  316. +
  317. + return result
  318. def isoformat(self, sep='T'):
  319. base = super(_Timestamp, self).isoformat(sep=sep)
  320. @@ -5041,7 +5104,9 @@ cpdef normalize_date(object dt):
  321. -------
  322. normalized : datetime.datetime or Timestamp
  323. """
  324. - if PyDateTime_Check(dt):
  325. + if is_timestamp(dt):
  326. + return dt.replace(hour=0, minute=0, second=0, microsecond=0, nanosecond=0)
  327. + elif PyDateTime_Check(dt):
  328. return dt.replace(hour=0, minute=0, second=0, microsecond=0)
  329. elif PyDate_Check(dt):
  330. return datetime(dt.year, dt.month, dt.day)
  331. --
  332. 2.11.0