@@ -355,8 +355,14 @@ def shape(self) -> Shape:
355355 def dtype (self ) -> DtypeObj :
356356 return self .values .dtype
357357
358- def iget (self , i ):
359- return self .values [i ]
358+ def iget (self , i : int | tuple [int , int ] | tuple [slice , int ]):
359+ # In the case where we have a tuple[slice, int], the slice will always
360+ # be slice(None)
361+ # Note: only reached with self.ndim == 2
362+ # Invalid index type "Union[int, Tuple[int, int], Tuple[slice, int]]"
363+ # for "Union[ndarray[Any, Any], ExtensionArray]"; expected type
364+ # "Union[int, integer[Any]]"
365+ return self .values [i ] # type: ignore[index]
360366
361367 def set_inplace (self , locs , values ) -> None :
362368 """
@@ -1166,19 +1172,17 @@ def where(self, other, cond) -> list[Block]:
11661172 values = values .T
11671173
11681174 icond , noop = validate_putmask (values , ~ cond )
1175+ if noop :
1176+ # GH-39595: Always return a copy; short-circuit up/downcasting
1177+ return self .copy ()
11691178
11701179 if other is lib .no_default :
11711180 other = self .fill_value
11721181
11731182 if is_valid_na_for_dtype (other , self .dtype ) and self .dtype != _dtype_obj :
11741183 other = self .fill_value
11751184
1176- if noop :
1177- # TODO: avoid the downcasting at the end in this case?
1178- # GH-39595: Always return a copy
1179- result = values .copy ()
1180-
1181- elif not self ._can_hold_element (other ):
1185+ if not self ._can_hold_element (other ):
11821186 # we cannot coerce, return a compat dtype
11831187 block = self .coerce_to_target_dtype (other )
11841188 blocks = block .where (orig_other , cond )
@@ -1350,11 +1354,7 @@ def where(self, other, cond) -> list[Block]:
13501354 try :
13511355 res_values = arr ._where (cond , other ).T
13521356 except (ValueError , TypeError ) as err :
1353- if isinstance (err , ValueError ):
1354- # TODO(2.0): once DTA._validate_setitem_value deprecation
1355- # is enforced, stop catching ValueError here altogether
1356- if "Timezones don't match" not in str (err ):
1357- raise
1357+ _catch_deprecated_value_error (err )
13581358
13591359 if is_interval_dtype (self .dtype ):
13601360 # TestSetitemFloatIntervalWithIntIntervalValues
@@ -1397,10 +1397,7 @@ def putmask(self, mask, new) -> list[Block]:
13971397 # Caller is responsible for ensuring matching lengths
13981398 values ._putmask (mask , new )
13991399 except (TypeError , ValueError ) as err :
1400- if isinstance (err , ValueError ) and "Timezones don't match" not in str (err ):
1401- # TODO(2.0): remove catching ValueError at all since
1402- # DTA raising here is deprecated
1403- raise
1400+ _catch_deprecated_value_error (err )
14041401
14051402 if is_interval_dtype (self .dtype ):
14061403 # Discussion about what we want to support in the general
@@ -1490,11 +1487,18 @@ def shape(self) -> Shape:
14901487 return (len (self .values ),)
14911488 return len (self ._mgr_locs ), len (self .values )
14921489
1493- def iget (self , col ):
1490+ def iget (self , i : int | tuple [int , int ] | tuple [slice , int ]):
1491+ # In the case where we have a tuple[slice, int], the slice will always
1492+ # be slice(None)
1493+ # We _could_ make the annotation more specific, but mypy would
1494+ # complain about override mismatch:
1495+ # Literal[0] | tuple[Literal[0], int] | tuple[slice, int]
14941496
1495- if self .ndim == 2 and isinstance (col , tuple ):
1497+ # Note: only reached with self.ndim == 2
1498+
1499+ if isinstance (i , tuple ):
14961500 # TODO(EA2D): unnecessary with 2D EAs
1497- col , loc = col
1501+ col , loc = i
14981502 if not com .is_null_slice (col ) and col != 0 :
14991503 raise IndexError (f"{ self } only contains one item" )
15001504 elif isinstance (col , slice ):
@@ -1503,7 +1507,7 @@ def iget(self, col):
15031507 return self .values [[loc ]]
15041508 return self .values [loc ]
15051509 else :
1506- if col != 0 :
1510+ if i != 0 :
15071511 raise IndexError (f"{ self } only contains one item" )
15081512 return self .values
15091513
@@ -1829,6 +1833,18 @@ def fillna(
18291833 return [self .make_block_same_class (values = new_values )]
18301834
18311835
1836+ def _catch_deprecated_value_error (err : Exception ) -> None :
1837+ """
1838+ We catch ValueError for now, but only a specific one raised by DatetimeArray
1839+ which will no longer be raised in version.2.0.
1840+ """
1841+ if isinstance (err , ValueError ):
1842+ # TODO(2.0): once DTA._validate_setitem_value deprecation
1843+ # is enforced, stop catching ValueError here altogether
1844+ if "Timezones don't match" not in str (err ):
1845+ raise
1846+
1847+
18321848class DatetimeLikeBlock (NDArrayBackedExtensionBlock ):
18331849 """Block for datetime64[ns], timedelta64[ns]."""
18341850
0 commit comments