Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,14 @@ repos:
entry: python scripts/validate_min_versions_in_sync.py
language: python
files: ^(ci/deps/actions-.*-minimum_versions\.yaml|pandas/compat/_optional\.py)$
- id: validate-errors-locations
name: Validate errors locations
description: Validate errors are in approriate locations.
entry: python scripts/validate_exception_location.py
language: python
files: ^pandas/
exclude: ^(pandas/_libs/|pandas/tests/|pandas/errors/__init__.py$)
types: [python]
- id: flake8-pyi
name: flake8-pyi
entry: flake8 --extend-ignore=E301,E302,E305,E701,E704
Expand Down
5 changes: 5 additions & 0 deletions doc/source/reference/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ Exceptions and warnings
errors.IncompatibilityWarning
errors.IndexingError
errors.InvalidColumnName
errors.InvalidComparison
errors.InvalidIndexError
errors.InvalidVersion
errors.IntCastingNaNError
errors.LossySetitemError
errors.MergeError
errors.NoBufferPresent
errors.NotThisMethod
errors.NullFrequencyError
errors.NumbaUtilError
errors.NumExprClobberingError
Expand Down
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v1.5.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ Other enhancements
- A :class:`errors.PerformanceWarning` is now thrown when using ``string[pyarrow]`` dtype with methods that don't dispatch to ``pyarrow.compute`` methods (:issue:`42613`)
- Added ``numeric_only`` argument to :meth:`Resampler.sum`, :meth:`Resampler.prod`, :meth:`Resampler.min`, :meth:`Resampler.max`, :meth:`Resampler.first`, and :meth:`Resampler.last` (:issue:`46442`)
- ``times`` argument in :class:`.ExponentialMovingWindow` now accepts ``np.timedelta64`` (:issue:`47003`)
- :class:`.DataError`, :class:`.SpecificationError`, :class:`.SettingWithCopyError`, :class:`.SettingWithCopyWarning`, :class:`.NumExprClobberingError`, :class:`.UndefinedVariableError`, :class:`.IndexingError`, :class:`.PyperclipException`, :class:`.PyperclipWindowsException`, :class:`.CSSWarning`, :class:`.PossibleDataLossError`, :class:`.ClosedFileError`, :class:`.IncompatibilityWarning`, :class:`.AttributeConflictWarning`, :class:`.DatabaseError`, :class:`.PossiblePrecisionLoss`, :class:`.ValueLabelTypeMismatch`, :class:`.InvalidColumnName`, and :class:`.CategoricalConversionWarning` are now exposed in ``pandas.errors`` (:issue:`27656`)
- :class:`.DataError`, :class:`.SpecificationError`, :class:`.SettingWithCopyError`, :class:`.SettingWithCopyWarning`, :class:`.NumExprClobberingError`, :class:`.UndefinedVariableError`, :class:`.IndexingError`, :class:`.PyperclipException`, :class:`.PyperclipWindowsException`, :class:`.CSSWarning`, :class:`.PossibleDataLossError`, :class:`.ClosedFileError`, :class:`.IncompatibilityWarning`, :class:`.AttributeConflictWarning`, :class:`.DatabaseError`, :class:`.PossiblePrecisionLoss`, :class:`.ValueLabelTypeMismatch`, :class:`.InvalidColumnName`, :class:`.CategoricalConversionWarning`, :class:`.InvalidComparison`, :class:`.InvalidVersion`, :class:`.LossySetitemError`, :class:`.NoBufferPresent`, and :class:`.NotThisMethod` are now exposed in ``pandas.errors`` (:issue:`27656`)
- Added ``check_like`` argument to :func:`testing.assert_series_equal` (:issue:`47247`)
- Add support for :meth:`.GroupBy.ohlc` for extension array dtypes (:issue:`37493`)
- Allow reading compressed SAS files with :func:`read_sas` (e.g., ``.sas7bdat.gz`` files)
Expand Down
10 changes: 1 addition & 9 deletions pandas/core/arrays/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
from pandas.compat.numpy import function as nv
from pandas.errors import (
AbstractMethodError,
InvalidComparison,
NullFrequencyError,
PerformanceWarning,
)
Expand Down Expand Up @@ -152,15 +153,6 @@
DatetimeLikeArrayT = TypeVar("DatetimeLikeArrayT", bound="DatetimeLikeArrayMixin")


class InvalidComparison(Exception):
"""
Raised by _validate_comparison_value to indicate to caller it should
return invalid_comparison.
"""

pass


class DatetimeLikeArrayMixin(OpsMixin, NDArrayBackedExtensionArray):
"""
Shared Base/Mixin class for DatetimeArray, TimedeltaArray, PeriodArray
Expand Down
13 changes: 4 additions & 9 deletions pandas/core/dtypes/cast.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@
DtypeObj,
Scalar,
)
from pandas.errors import IntCastingNaNError
from pandas.errors import (
IntCastingNaNError,
LossySetitemError,
)
from pandas.util._exceptions import find_stack_level
from pandas.util._validators import validate_bool_kwarg

Expand Down Expand Up @@ -2093,11 +2096,3 @@ def _dtype_can_hold_range(rng: range, dtype: np.dtype) -> bool:
if not len(rng):
return True
return np.can_cast(rng[0], dtype) and np.can_cast(rng[-1], dtype)


class LossySetitemError(Exception):
"""
Raised when trying to do a __setitem__ on an np.ndarray that is not lossless.
"""

pass
2 changes: 1 addition & 1 deletion pandas/core/interchange/column.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from pandas._libs.lib import infer_dtype
from pandas._libs.tslibs import iNaT
from pandas.errors import NoBufferPresent
from pandas.util._decorators import cache_readonly

import pandas as pd
Expand All @@ -23,7 +24,6 @@
from pandas.core.interchange.utils import (
ArrowCTypes,
Endianness,
NoBufferPresent,
dtype_to_arrow_c_fmt,
)

Expand Down
4 changes: 0 additions & 4 deletions pandas/core/interchange/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,3 @@ def dtype_to_arrow_c_fmt(dtype: DtypeObj) -> str:
raise NotImplementedError(
f"Conversion of {dtype} to Arrow C format string is not implemented."
)


class NoBufferPresent(Exception):
"""Exception to signal that there is no requested buffer."""
26 changes: 26 additions & 0 deletions pandas/errors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
OutOfBoundsTimedelta,
)

from pandas._version import NotThisMethod
from pandas.util.version import InvalidVersion


class IntCastingNaNError(ValueError):
"""
Expand Down Expand Up @@ -535,6 +538,24 @@ class CategoricalConversionWarning(Warning):
"""


class LossySetitemError(Exception):
"""
Raised when trying to do a __setitem__ on an np.ndarray that is not lossless.
"""


class NoBufferPresent(Exception):
"""
Exception is raised in _get_data_buffer to signal that there is no requested buffer.
"""


class InvalidComparison(Exception):
"""
Exception is raised by _validate_comparison_value to indicate an invalid comparison.
"""


__all__ = [
"AbstractMethodError",
"AccessorRegistrationWarning",
Expand All @@ -550,9 +571,14 @@ class CategoricalConversionWarning(Warning):
"IncompatibilityWarning",
"IntCastingNaNError",
"InvalidColumnName",
"InvalidComparison",
"InvalidIndexError",
"InvalidVersion",
"IndexingError",
"LossySetitemError",
"MergeError",
"NoBufferPresent",
"NotThisMethod",
"NullFrequencyError",
"NumbaUtilError",
"NumExprClobberingError",
Expand Down
47 changes: 26 additions & 21 deletions pandas/tests/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,38 @@
@pytest.mark.parametrize(
"exc",
[
"UnsupportedFunctionCall",
"UnsortedIndexError",
"OutOfBoundsDatetime",
"ParserError",
"PerformanceWarning",
"AttributeConflictWarning",
"CSSWarning",
"CategoricalConversionWarning",
"ClosedFileError",
"DataError",
"DatabaseError",
"DtypeWarning",
"EmptyDataError",
"ParserWarning",
"IncompatibilityWarning",
"IndexingError",
"InvalidColumnName",
"InvalidComparison",
"InvalidVersion",
"LossySetitemError",
"MergeError",
"OptionError",
"NumbaUtilError",
"DataError",
"SpecificationError",
"SettingWithCopyError",
"SettingWithCopyWarning",
"NoBufferPresent",
"NotThisMethod",
"NumExprClobberingError",
"IndexingError",
"PyperclipException",
"CSSWarning",
"ClosedFileError",
"NumbaUtilError",
"OptionError",
"OutOfBoundsDatetime",
"ParserError",
"ParserWarning",
"PerformanceWarning",
"PossibleDataLossError",
"IncompatibilityWarning",
"AttributeConflictWarning",
"DatabaseError",
"PossiblePrecisionLoss",
"CategoricalConversionWarning",
"InvalidColumnName",
"PyperclipException",
"SettingWithCopyError",
"SettingWithCopyWarning",
"SpecificationError",
"UnsortedIndexError",
"UnsupportedFunctionCall",
"ValueLabelTypeMismatch",
],
)
Expand Down
2 changes: 1 addition & 1 deletion scripts/pandas_errors_documented.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Check that doc/source/reference/general_utility_functions.rst documents
Check that doc/source/reference/testing.rst documents
all exceptions and warnings in pandas/errors/__init__.py.

This is meant to be run as a pre-commit hook - to run it manually, you can do:
Expand Down
64 changes: 64 additions & 0 deletions scripts/tests/test_validate_exception_location.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import pytest

from scripts.validate_exception_location import validate_exception_and_warning_placement

PATH = "t.py"

# ERRORS_IN_TESTING_RST is the set returned when parsing testing.rst for all the
# exceptions and warnings.
CUSTOM_EXCEPTION_NOT_IN_TESTING_RST = "MyException"
CUSTOM_EXCEPTION__IN_TESTING_RST = "MyOldException"
ERRORS_IN_TESTING_RST = {CUSTOM_EXCEPTION__IN_TESTING_RST}

TEST_CODE = """
import numpy as np
import sys
def my_func():
pass
class {custom_name}({error_type}):
pass
"""

# Test with various python-defined exceptions to ensure they are all flagged.
testdata = [
"Exception",
"ValueError",
"Warning",
"UserWarning",
]


@pytest.mark.parametrize("error_type", testdata)
def test_class_that_inherits_an_exception_and_is_not_in_the_testing_rst_is_flagged(
capsys, error_type
):
content = TEST_CODE.format(
custom_name=CUSTOM_EXCEPTION_NOT_IN_TESTING_RST, error_type=error_type
)
result_msg = (
"t.py:8:0: {exception_name}: Please don't place exceptions or "
"warnings outside of pandas/errors/__init__.py or "
"pandas/_libs\n".format(exception_name=CUSTOM_EXCEPTION_NOT_IN_TESTING_RST)
)
with pytest.raises(SystemExit, match=None):
validate_exception_and_warning_placement(PATH, content, ERRORS_IN_TESTING_RST)
expected_msg, _ = capsys.readouterr()
assert result_msg == expected_msg


@pytest.mark.parametrize("error_type", testdata)
def test_class_that_inherits_an_exception_but_is_in_the_testing_rst_is_not_flagged(
capsys, error_type
):
content = TEST_CODE.format(
custom_name=CUSTOM_EXCEPTION__IN_TESTING_RST, error_type=error_type
)
validate_exception_and_warning_placement(PATH, content, ERRORS_IN_TESTING_RST)


def test_class_that_does_not_inherit_an_exception_is_not_flagged(capsys):
content = "class MyClass(NonExceptionClass): pass"
validate_exception_and_warning_placement(PATH, content, ERRORS_IN_TESTING_RST)
Loading