Skip to content

Commit 8f07dc9

Browse files
authored
Merge pull request #3538 from emma58/params-complain-more
Type check all `exception` arguments in component `__call__` implementations
2 parents 11fa674 + 70cc92f commit 8f07dc9

22 files changed

+120
-39
lines changed

pyomo/core/base/boolean_var.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from pyomo.core.staleflag import StaleFlagManager
2020
from pyomo.core.expr.boolean_value import BooleanValue
2121
from pyomo.core.expr import GetItemExpression
22+
from pyomo.core.expr.expr_common import _type_check_exception_arg
2223
from pyomo.core.expr.numvalue import value
2324
from pyomo.core.base.component import ComponentData, ModelComponentFactory
2425
from pyomo.core.base.global_set import UnindexedComponent_index
@@ -155,8 +156,9 @@ def set_value(self, val, skip_validation=False):
155156
def clear(self):
156157
self.value = None
157158

158-
def __call__(self, exception=True):
159+
def __call__(self, exception=NOTSET):
159160
"""Compute the value of this variable."""
161+
exception = _type_check_exception_arg(self, exception)
160162
return self.value
161163

162164
@property

pyomo/core/base/constraint.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
InequalityExpression,
3939
RangedExpression,
4040
)
41+
from pyomo.core.expr.expr_common import _type_check_exception_arg
4142
from pyomo.core.expr.template_expr import templatize_constraint
4243
from pyomo.core.base.component import ActiveComponentData, ModelComponentFactory
4344
from pyomo.core.base.global_set import UnindexedComponent_index
@@ -168,8 +169,9 @@ def __init__(self, expr=None, component=None):
168169
if expr is not None:
169170
self.set_value(expr)
170171

171-
def __call__(self, exception=True):
172+
def __call__(self, exception=NOTSET):
172173
"""Compute the value of the body of this constraint."""
174+
exception = _type_check_exception_arg(self, exception)
173175
body = self.to_bounded_expression()[1]
174176
if body.__class__ not in native_numeric_types:
175177
body = value(self.body, exception=exception)

pyomo/core/base/expression.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
)
2727

2828
import pyomo.core.expr as EXPR
29+
from pyomo.core.expr.expr_common import _type_check_exception_arg
2930
import pyomo.core.expr.numeric_expr as numeric_expr
3031
from pyomo.core.base.component import ComponentData, ModelComponentFactory
3132
from pyomo.core.base.global_set import UnindexedComponent_index
@@ -50,8 +51,9 @@ class NamedExpressionData(numeric_expr.NumericValue):
5051
PRECEDENCE = 0
5152
ASSOCIATIVITY = EXPR.OperatorAssociativity.NON_ASSOCIATIVE
5253

53-
def __call__(self, exception=True):
54+
def __call__(self, exception=NOTSET):
5455
"""Compute the value of this expression."""
56+
exception = _type_check_exception_arg(self, exception)
5557
(arg,) = self.args
5658
if arg.__class__ in native_types:
5759
# Note: native_types includes NoneType
@@ -396,8 +398,9 @@ def __init__(self, *args, **kwds):
396398
# construction
397399
#
398400

399-
def __call__(self, exception=True):
401+
def __call__(self, exception=NOTSET):
400402
"""Return expression on this expression."""
403+
exception = _type_check_exception_arg(self, exception)
401404
if self._constructed:
402405
return super().__call__(exception)
403406
raise ValueError(

pyomo/core/base/logical_constraint.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from pyomo.common.modeling import NOTSET
2121
from pyomo.common.timing import ConstructionTimer
2222

23+
from pyomo.core.expr.expr_common import _type_check_exception_arg
2324
from pyomo.core.expr.boolean_value import as_boolean, BooleanConstant
2425
from pyomo.core.expr.numvalue import native_types, native_logical_types
2526
from pyomo.core.base.component import ActiveComponentData, ModelComponentFactory
@@ -84,8 +85,9 @@ def __init__(self, expr=None, component=None):
8485
if expr is not None:
8586
self.set_value(expr)
8687

87-
def __call__(self, exception=True):
88+
def __call__(self, exception=NOTSET):
8889
"""Compute the value of the body of this logical constraint."""
90+
exception = _type_check_exception_arg(self, exception)
8991
if self.body is None:
9092
return None
9193
return self.body(exception=exception)

pyomo/core/base/matrix_constraint.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515

1616
from pyomo.common.gc_manager import PauseGC
1717
from pyomo.common.log import is_debug_set
18+
from pyomo.common.modeling import NOTSET
1819
from pyomo.core.base.set_types import Any
20+
from pyomo.core.expr.expr_common import _type_check_exception_arg
1921
from pyomo.core.expr.numvalue import value
2022
from pyomo.core.expr.numeric_expr import LinearExpression
2123
from pyomo.core.base.component import ModelComponentFactory
@@ -130,8 +132,9 @@ def __getstate__(self):
130132
# possible
131133
#
132134

133-
def __call__(self, exception=True):
135+
def __call__(self, exception=NOTSET):
134136
"""Compute the value of the body of this constraint."""
137+
exception = _type_check_exception_arg(self, exception)
135138
comp = self.parent_component()
136139
index = self._index
137140
data = comp._A_data

pyomo/core/base/objective.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from pyomo.common.formatting import tabular_writer
2323
from pyomo.common.timing import ConstructionTimer
2424

25+
from pyomo.core.expr.expr_common import _type_check_exception_arg
2526
from pyomo.core.expr.numvalue import value
2627
from pyomo.core.expr.template_expr import templatize_rule
2728
from pyomo.core.base.component import ActiveComponentData, ModelComponentFactory
@@ -418,7 +419,8 @@ def __init__(self, *args, **kwd):
418419
# construction
419420
#
420421

421-
def __call__(self, exception=True):
422+
def __call__(self, exception=NOTSET):
423+
exception = _type_check_exception_arg(self, exception)
422424
if self._constructed:
423425
if len(self._data) == 0:
424426
raise ValueError(

pyomo/core/base/param.py

+7-9
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from pyomo.common.modeling import NOTSET
2424
from pyomo.common.numeric_types import native_types, value as expr_value
2525
from pyomo.common.timing import ConstructionTimer
26+
from pyomo.core.expr.expr_common import _type_check_exception_arg
2627
from pyomo.core.expr.numvalue import NumericValue
2728
from pyomo.core.base.component import ComponentData, ModelComponentFactory
2829
from pyomo.core.base.global_set import UnindexedComponent_index
@@ -201,10 +202,12 @@ def set_value(self, value, idx=NOTSET):
201202
self._value = old_value
202203
raise
203204

204-
def __call__(self, exception=True):
205+
def __call__(self, exception=NOTSET):
205206
"""
206207
Return the value of this object.
207208
"""
209+
exception = _type_check_exception_arg(self, exception)
210+
208211
if self._value is Param.NoValue:
209212
if exception:
210213
raise ValueError(
@@ -929,10 +932,12 @@ def __init__(self, *args, **kwds):
929932
# up both the Component and Data base classes.
930933
#
931934

932-
def __call__(self, exception=True):
935+
def __call__(self, exception=NOTSET):
933936
"""
934937
Return the value of this parameter.
935938
"""
939+
exception = _type_check_exception_arg(self, exception)
940+
936941
if self._constructed:
937942
if not self._data:
938943
if self._mutable:
@@ -976,13 +981,6 @@ class SimpleParam(metaclass=RenamedClass):
976981

977982

978983
class IndexedParam(Param):
979-
def __call__(self, exception=True):
980-
"""Compute the value of the parameter"""
981-
if exception:
982-
raise TypeError(
983-
'Cannot compute the value of an indexed Param (%s)' % (self.name,)
984-
)
985-
986984
# Because IndexedParam can use a non-standard data store (i.e., the
987985
# values in the _data dict may not be ComponentData objects), we
988986
# need to override the normal scheme for pre-allocating

pyomo/core/base/units_container.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113

114114
from pyomo.common.dependencies import pint as pint_module, pint_available
115115
from pyomo.common.modeling import NOTSET
116+
from pyomo.core.expr.expr_common import _type_check_exception_arg
116117
from pyomo.core.expr.numvalue import (
117118
NumericValue,
118119
nonpyomo_leaf_types,
@@ -384,14 +385,15 @@ def to_string(self, verbose=None, labeler=None, smap=None, compute_values=False)
384385
else:
385386
return _str
386387

387-
def __call__(self, exception=True):
388+
def __call__(self, exception=NOTSET):
388389
"""Unit is treated as a constant value, and this method always returns 1.0
389390
390391
Returns
391392
-------
392393
: float
393394
Returns 1.0
394395
"""
396+
_type_check_exception_arg(self, exception)
395397
return 1.0
396398

397399
@property

pyomo/core/base/var.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from pyomo.core.staleflag import StaleFlagManager
2525
from pyomo.core.expr import GetItemExpression
2626
from pyomo.core.expr.numeric_expr import NPV_MaxExpression, NPV_MinExpression
27+
from pyomo.core.expr.expr_common import _type_check_exception_arg
2728
from pyomo.core.expr.numvalue import (
2829
NumericValue,
2930
value,
@@ -197,8 +198,9 @@ def value(self):
197198
def value(self, val):
198199
self.set_value(val)
199200

200-
def __call__(self, exception=True):
201+
def __call__(self, exception=NOTSET):
201202
"""Compute the value of this variable."""
203+
exception = _type_check_exception_arg(self, exception)
202204
return self._value
203205

204206
@property

pyomo/core/expr/base.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313

1414
from pyomo.common.dependencies import attempt_import
1515
from pyomo.common.numeric_types import native_types
16+
from pyomo.common.modeling import NOTSET
1617
from pyomo.core.pyomoobject import PyomoObject
17-
from pyomo.core.expr.expr_common import OperatorAssociativity
18+
from pyomo.core.expr.expr_common import OperatorAssociativity, _type_check_exception_arg
1819

1920
visitor, _ = attempt_import('pyomo.core.expr.visitor')
2021

@@ -100,7 +101,7 @@ def args(self):
100101
f"Derived expression ({self.__class__}) failed to implement args()"
101102
)
102103

103-
def __call__(self, exception=True):
104+
def __call__(self, exception=NOTSET):
104105
"""Evaluate the value of the expression tree.
105106
106107
Parameters
@@ -115,6 +116,7 @@ def __call__(self, exception=True):
115116
The value of the expression or :const:`None`.
116117
117118
"""
119+
exception = _type_check_exception_arg(self, exception)
118120
return visitor.evaluate_expression(self, exception)
119121

120122
def __str__(self):

pyomo/core/expr/boolean_value.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import logging
1414

1515
from pyomo.common.deprecation import deprecated
16+
from pyomo.common.modeling import NOTSET
17+
from pyomo.core.expr.expr_common import _type_check_exception_arg
1618
from pyomo.core.expr.numvalue import native_types, native_logical_types
1719
from pyomo.core.expr.expr_common import _and, _or, _equiv, _inv, _xor, _impl
1820
from pyomo.core.pyomoobject import PyomoObject
@@ -296,8 +298,9 @@ def __nonzero__(self):
296298
def __bool__(self):
297299
return self.value
298300

299-
def __call__(self, exception=True):
301+
def __call__(self, exception=NOTSET):
300302
"""Return the constant value"""
303+
exception = _type_check_exception_arg(self, exception)
301304
return self.value
302305

303306
def pprint(self, ostream=None, verbose=False):

pyomo/core/expr/expr_common.py

+13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from pyomo.common import enums
1515
from pyomo.common.deprecation import deprecated
16+
from pyomo.common.modeling import NOTSET
1617

1718
TO_STRING_VERBOSE = False
1819

@@ -108,3 +109,15 @@ def __init__(self):
108109
def count(self):
109110
"""A property that returns the clone count value."""
110111
return clone_counter._count
112+
113+
114+
def _type_check_exception_arg(cls, exception):
115+
if exception is NOTSET:
116+
return True
117+
elif type(exception) is not bool:
118+
raise ValueError(
119+
f"{cls.ctype.__name__} '{cls.name}' was called with a non-bool "
120+
f"argument for 'exception': {exception}"
121+
)
122+
else:
123+
return exception

pyomo/core/expr/numvalue.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
deprecation_warning,
1818
relocated_module_attribute,
1919
)
20-
from pyomo.core.expr.expr_common import ExpressionType
20+
from pyomo.common.modeling import NOTSET
21+
from pyomo.core.expr.expr_common import ExpressionType, _type_check_exception_arg
2122
from pyomo.core.expr.numeric_expr import NumericValue
2223

2324
# TODO: update Pyomo to import these objects from common.numeric_types
@@ -113,7 +114,8 @@ def __str__(self):
113114
def __repr__(self):
114115
return repr(self.value)
115116

116-
def __call__(self, exception=None):
117+
def __call__(self, exception=NOTSET):
118+
exception = _type_check_exception_arg(self, exception)
117119
return self.value
118120

119121
def is_constant(self):
@@ -422,8 +424,9 @@ def _compute_polynomial_degree(self, result):
422424
def __str__(self):
423425
return str(self.value)
424426

425-
def __call__(self, exception=True):
427+
def __call__(self, exception=NOTSET):
426428
"""Return the constant value"""
429+
exception = _type_check_exception_arg(self, exception)
427430
return self.value
428431

429432
def pprint(self, ostream=None, verbose=False):

pyomo/core/expr/template_expr.py

+5
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ def __call__(self, *args, **kwargs):
229229
#
230230
# TODO: deprecate (then remove) evaluating expressions by
231231
# "calling" them.
232+
#
233+
# [ESJ 3/25/25]: Note that since this always calls the ExpressionBase
234+
# implementation of __call__ if 'exception' is specified, we need not
235+
# check the type of the exception arg here--it will get checked in the
236+
# base class.
232237
try:
233238
if not args:
234239
if not kwargs:

pyomo/core/kernel/conic.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
# ___________________________________________________________________________
1111
"""Various conic constraint implementations."""
1212

13+
from pyomo.common.modeling import NOTSET
1314
from pyomo.core.expr.numvalue import is_numeric_data
1415
from pyomo.core.expr import value, exp
16+
from pyomo.core.expr.expr_common import _type_check_exception_arg
1517
from pyomo.core.kernel.block import block
1618
from pyomo.core.kernel.variable import IVariable, variable, variable_tuple
1719
from pyomo.core.kernel.constraint import (
@@ -133,7 +135,8 @@ def equality(self):
133135
# to avoid building the body expression, if possible
134136
#
135137

136-
def __call__(self, exception=True):
138+
def __call__(self, exception=NOTSET):
139+
exception = _type_check_exception_arg(self, exception)
137140
try:
138141
# we wrap the result with value(...) as the
139142
# alpha term used by some of the constraints

pyomo/core/kernel/constraint.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99
# This software is distributed under the 3-clause BSD License.
1010
# ___________________________________________________________________________
1111

12+
from pyomo.common.modeling import NOTSET
1213
from pyomo.core.expr.numvalue import (
1314
ZeroConstant,
1415
as_numeric,
1516
is_potentially_variable,
1617
is_numeric_data,
1718
value,
1819
)
19-
from pyomo.core.expr.expr_common import ExpressionType
20+
from pyomo.core.expr.expr_common import ExpressionType, _type_check_exception_arg
2021
from pyomo.core.expr.relational_expr import (
2122
EqualityExpression,
2223
RangedExpression,
@@ -74,8 +75,9 @@ class IConstraint(ICategorizedObject):
7475
# Interface
7576
#
7677

77-
def __call__(self, exception=True):
78+
def __call__(self, exception=NOTSET):
7879
"""Compute the value of the body of this constraint."""
80+
exception = _type_check_exception_arg(self, exception)
7981
if exception and (self.body is None):
8082
raise ValueError("constraint body is None")
8183
elif self.body is None:
@@ -798,7 +800,8 @@ def terms(self, terms):
798800
# to avoid building the body expression
799801
#
800802

801-
def __call__(self, exception=True):
803+
def __call__(self, exception=NOTSET):
804+
exception = _type_check_exception_arg(self, exception)
802805
try:
803806
return sum(
804807
value(c, exception=exception) * v(exception=exception)

0 commit comments

Comments
 (0)