test_constructors.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. from datetime import datetime
  2. import sys
  3. import numpy as np
  4. import pytest
  5. from pandas.compat import PYPY
  6. import pandas as pd
  7. from pandas import (
  8. DataFrame,
  9. Index,
  10. Series,
  11. )
  12. import pandas._testing as tm
  13. from pandas.core.accessor import PandasDelegate
  14. from pandas.core.base import (
  15. NoNewAttributesMixin,
  16. PandasObject,
  17. )
  18. def series_via_frame_from_dict(x, **kwargs):
  19. return DataFrame({"a": x}, **kwargs)["a"]
  20. def series_via_frame_from_scalar(x, **kwargs):
  21. return DataFrame(x, **kwargs)[0]
  22. @pytest.fixture(
  23. params=[
  24. Series,
  25. series_via_frame_from_dict,
  26. series_via_frame_from_scalar,
  27. Index,
  28. ],
  29. ids=["Series", "DataFrame-dict", "DataFrame-array", "Index"],
  30. )
  31. def constructor(request):
  32. return request.param
  33. class TestPandasDelegate:
  34. class Delegator:
  35. _properties = ["prop"]
  36. _methods = ["test_method"]
  37. def _set_prop(self, value):
  38. self.prop = value
  39. def _get_prop(self):
  40. return self.prop
  41. prop = property(_get_prop, _set_prop, doc="foo property")
  42. def test_method(self, *args, **kwargs):
  43. """a test method"""
  44. class Delegate(PandasDelegate, PandasObject):
  45. def __init__(self, obj) -> None:
  46. self.obj = obj
  47. def test_invalid_delegation(self):
  48. # these show that in order for the delegation to work
  49. # the _delegate_* methods need to be overridden to not raise
  50. # a TypeError
  51. self.Delegate._add_delegate_accessors(
  52. delegate=self.Delegator,
  53. accessors=self.Delegator._properties,
  54. typ="property",
  55. )
  56. self.Delegate._add_delegate_accessors(
  57. delegate=self.Delegator, accessors=self.Delegator._methods, typ="method"
  58. )
  59. delegate = self.Delegate(self.Delegator())
  60. msg = "You cannot access the property prop"
  61. with pytest.raises(TypeError, match=msg):
  62. delegate.prop
  63. msg = "The property prop cannot be set"
  64. with pytest.raises(TypeError, match=msg):
  65. delegate.prop = 5
  66. msg = "You cannot access the property prop"
  67. with pytest.raises(TypeError, match=msg):
  68. delegate.prop
  69. @pytest.mark.skipif(PYPY, reason="not relevant for PyPy")
  70. def test_memory_usage(self):
  71. # Delegate does not implement memory_usage.
  72. # Check that we fall back to in-built `__sizeof__`
  73. # GH 12924
  74. delegate = self.Delegate(self.Delegator())
  75. sys.getsizeof(delegate)
  76. class TestNoNewAttributesMixin:
  77. def test_mixin(self):
  78. class T(NoNewAttributesMixin):
  79. pass
  80. t = T()
  81. assert not hasattr(t, "__frozen")
  82. t.a = "test"
  83. assert t.a == "test"
  84. t._freeze()
  85. assert "__frozen" in dir(t)
  86. assert getattr(t, "__frozen")
  87. msg = "You cannot add any new attribute"
  88. with pytest.raises(AttributeError, match=msg):
  89. t.b = "test"
  90. assert not hasattr(t, "b")
  91. class TestConstruction:
  92. # test certain constructor behaviours on dtype inference across Series,
  93. # Index and DataFrame
  94. @pytest.mark.parametrize(
  95. "a",
  96. [
  97. np.array(["2263-01-01"], dtype="datetime64[D]"),
  98. np.array([datetime(2263, 1, 1)], dtype=object),
  99. np.array([np.datetime64("2263-01-01", "D")], dtype=object),
  100. np.array(["2263-01-01"], dtype=object),
  101. ],
  102. ids=[
  103. "datetime64[D]",
  104. "object-datetime.datetime",
  105. "object-numpy-scalar",
  106. "object-string",
  107. ],
  108. )
  109. def test_constructor_datetime_outofbound(
  110. self, a, constructor, request, using_infer_string
  111. ):
  112. # GH-26853 (+ bug GH-26206 out of bound non-ns unit)
  113. # No dtype specified (dtype inference)
  114. # datetime64[non-ns] raise error, other cases result in object dtype
  115. # and preserve original data
  116. if a.dtype.kind == "M":
  117. # Can't fit in nanosecond bounds -> get the nearest supported unit
  118. result = constructor(a)
  119. assert result.dtype == "M8[s]"
  120. else:
  121. result = constructor(a)
  122. if using_infer_string and "object-string" in request.node.callspec.id:
  123. assert result.dtype == "string"
  124. else:
  125. assert result.dtype == "object"
  126. tm.assert_numpy_array_equal(result.to_numpy(), a)
  127. # Explicit dtype specified
  128. # Forced conversion fails for all -> all cases raise error
  129. msg = "Out of bounds|Out of bounds .* present at position 0"
  130. with pytest.raises(pd.errors.OutOfBoundsDatetime, match=msg):
  131. constructor(a, dtype="datetime64[ns]")
  132. def test_constructor_datetime_nonns(self, constructor):
  133. arr = np.array(["2020-01-01T00:00:00.000000"], dtype="datetime64[us]")
  134. dta = pd.core.arrays.DatetimeArray._simple_new(arr, dtype=arr.dtype)
  135. expected = constructor(dta)
  136. assert expected.dtype == arr.dtype
  137. result = constructor(arr)
  138. tm.assert_equal(result, expected)
  139. # https://github.com/pandas-dev/pandas/issues/34843
  140. arr.flags.writeable = False
  141. result = constructor(arr)
  142. tm.assert_equal(result, expected)
  143. def test_constructor_from_dict_keys(self, constructor, using_infer_string):
  144. # https://github.com/pandas-dev/pandas/issues/60343
  145. d = {"a": 1, "b": 2}
  146. result = constructor(d.keys(), dtype="str")
  147. if using_infer_string:
  148. assert result.dtype == "str"
  149. else:
  150. assert result.dtype == "object"
  151. expected = constructor(list(d.keys()), dtype="str")
  152. tm.assert_equal(result, expected)