diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index 04cd5e4c2c918..c7f8bb70e3461 100644 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -195,6 +195,12 @@ ExtensionArray - - + +Other +^^^^^ +- Trying to set the ``display.precision``, ``display.max_rows`` or ``display.max_columns`` using :meth:`set_option` to anything but a ``None`` or a positive int will raise a ``ValueError`` (:issue:`23348`) + + .. _whatsnew_1000.contributors: Contributors diff --git a/pandas/_config/config.py b/pandas/_config/config.py index 4f0720abd1445..890db5b41907e 100644 --- a/pandas/_config/config.py +++ b/pandas/_config/config.py @@ -787,6 +787,7 @@ def is_instance_factory(_type): ValueError if x is not an instance of `_type` """ + if isinstance(_type, (tuple, list)): _type = tuple(_type) type_repr = "|".join(map(str, _type)) @@ -820,6 +821,32 @@ def inner(x): return inner +def is_nonnegative_int(value): + """ + Verify that value is None or a positive int. + + Parameters + ---------- + value : None or int + The `value` to be checked. + + Raises + ------ + ValueError + When the value is not None or is a negative integer + """ + + if value is None: + return + + elif isinstance(value, int): + if value >= 0: + return + + msg = "Value must be a nonnegative integer or None" + raise ValueError(msg) + + # common type validators, for convenience # usage: register_option(... , validator = is_int) is_int = is_type_factory(int) diff --git a/pandas/core/config_init.py b/pandas/core/config_init.py index be6086dd360f2..08dce6aca6e6d 100644 --- a/pandas/core/config_init.py +++ b/pandas/core/config_init.py @@ -17,6 +17,7 @@ is_callable, is_instance_factory, is_int, + is_nonnegative_int, is_one_of_factory, is_text, ) @@ -319,7 +320,7 @@ def is_terminal(): with cf.config_prefix("display"): - cf.register_option("precision", 6, pc_precision_doc, validator=is_int) + cf.register_option("precision", 6, pc_precision_doc, validator=is_nonnegative_int) cf.register_option( "float_format", None, @@ -333,12 +334,7 @@ def is_terminal(): pc_max_info_rows_doc, validator=is_instance_factory((int, type(None))), ) - cf.register_option( - "max_rows", - 60, - pc_max_rows_doc, - validator=is_instance_factory([type(None), int]), - ) + cf.register_option("max_rows", 60, pc_max_rows_doc, validator=is_nonnegative_int) cf.register_option( "min_rows", 10, @@ -352,10 +348,7 @@ def is_terminal(): else: max_cols = 20 # cannot determine optimal number of columns cf.register_option( - "max_columns", - max_cols, - pc_max_cols_doc, - validator=is_instance_factory([type(None), int]), + "max_columns", max_cols, pc_max_cols_doc, validator=is_nonnegative_int ) cf.register_option( "large_repr", diff --git a/pandas/tests/config/test_config.py b/pandas/tests/config/test_config.py index 3f12d1d7a292d..efaeb7b1471ec 100644 --- a/pandas/tests/config/test_config.py +++ b/pandas/tests/config/test_config.py @@ -208,13 +208,16 @@ def test_set_option_multiple(self): def test_validation(self): self.cf.register_option("a", 1, "doc", validator=self.cf.is_int) + self.cf.register_option("d", 1, "doc", validator=self.cf.is_nonnegative_int) self.cf.register_option("b.c", "hullo", "doc2", validator=self.cf.is_text) + msg = "Value must have type ''" with pytest.raises(ValueError, match=msg): self.cf.register_option("a.b.c.d2", "NO", "doc", validator=self.cf.is_int) self.cf.set_option("a", 2) # int is_int self.cf.set_option("b.c", "wurld") # str is_str + self.cf.set_option("d", 2) # None not is_int with pytest.raises(ValueError, match=msg): @@ -222,6 +225,16 @@ def test_validation(self): with pytest.raises(ValueError, match=msg): self.cf.set_option("a", "ab") + msg = "Value must be a nonnegative integer or None" + with pytest.raises(ValueError, match=msg): + self.cf.register_option( + "a.b.c.d3", "NO", "doc", validator=self.cf.is_nonnegative_int + ) + with pytest.raises(ValueError, match=msg): + self.cf.register_option( + "a.b.c.d3", -2, "doc", validator=self.cf.is_nonnegative_int + ) + msg = r"Value must be an instance of \|" with pytest.raises(ValueError, match=msg): self.cf.set_option("b.c", 1)