diff --git a/src/Bridges/bridge.jl b/src/Bridges/bridge.jl index 47c951b13e..0afb895468 100644 --- a/src/Bridges/bridge.jl +++ b/src/Bridges/bridge.jl @@ -119,6 +119,17 @@ function MOI.supports( return false end +function _attribute_error_message(attr, ::Type{B}, action) where {B} + return "Bridge of type `$(nameof(B))` does not support " * + "$action the attribute `$attr`. If you encountered this error " * + "unexpectedly, it probably means your model has been " * + "reformulated using the bridge, and you are attempting to query " * + "an attribute that we haven't implemented yet for this bridge. " * + "Please open an issue at https://github.com/jump-dev/MathOptInterface.jl/issues/new " * + "and provide a reproducible example explaining what you were " * + "trying to do." +end + """ function MOI.get( model::MOI.ModelLike, @@ -134,18 +145,8 @@ function MOI.get( attr::MOI.AbstractConstraintAttribute, bridge::AbstractBridge, ) - return throw( - ArgumentError( - "Bridge of type `$(typeof(bridge))` does not support accessing " * - "the attribute `$attr`. If you encountered this error " * - "unexpectedly, it probably means your model has been " * - "reformulated using the bridge, and you are attempting to query " * - "an attribute that we haven't implemented yet for this bridge. " * - "Please open an issue at https://github.com/jump-dev/MathOptInterface.jl/issues/new " * - "and provide a reproducible example explaining what you were " * - "trying to do.", - ), - ) + message = _attribute_error_message(attr, typeof(bridge), "accessing") + return throw(ArgumentError(message)) end function MOI.get( @@ -173,12 +174,14 @@ function MOI.set( model::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute, bridge::AbstractBridge, - value, + ::Any, ) + message = + _attribute_error_message(attr, typeof(bridge), "setting a value for") if MOI.is_copyable(attr) && !MOI.supports(model, attr, typeof(bridge)) - throw(MOI.UnsupportedAttribute(attr)) + return throw(MOI.UnsupportedAttribute(attr, message)) else - throw(MOI.SetAttributeNotAllowed(attr)) + return throw(MOI.SetAttributeNotAllowed(attr, message)) end end diff --git a/test/Bridges/Constraint/bridge.jl b/test/Bridges/Constraint/bridge.jl index 8cd2bf9f89..b3ae367b7a 100644 --- a/test/Bridges/Constraint/bridge.jl +++ b/test/Bridges/Constraint/bridge.jl @@ -26,17 +26,26 @@ struct DummyBridge <: MOI.Bridges.Constraint.AbstractBridge end function test_AbstractBridge() model = MOI.Utilities.Model{Float64}() bridge = DummyBridge() - attr = MOI.ConstraintPrimalStart() - @test !MOI.supports(model, attr, typeof(bridge)) - @test_throws MOI.UnsupportedAttribute(attr) MOI.set( - model, - attr, - bridge, - 1.0, + @test !MOI.supports(model, MOI.ConstraintPrimalStart(), typeof(bridge)) + message = MOI.Bridges._attribute_error_message( + MOI.ConstraintPrimalStart(), + DummyBridge, + "setting a value for", ) - attr = MOI.ConstraintFunction() - err = MOI.SetAttributeNotAllowed(attr) - @test_throws err MOI.set(model, attr, bridge, MOI.EqualTo(1.0)) + @test_throws( + MOI.UnsupportedAttribute(MOI.ConstraintPrimalStart(), message), + MOI.set(model, MOI.ConstraintPrimalStart(), bridge, 1.0), + ) + message = MOI.Bridges._attribute_error_message( + MOI.ConstraintSet(), + DummyBridge, + "setting a value for", + ) + @test_throws( + MOI.SetAttributeNotAllowed(MOI.ConstraintSet(), message), + MOI.set(model, MOI.ConstraintSet(), bridge, MOI.EqualTo(1.0)), + ) + return end end # module diff --git a/test/Bridges/bridge_optimizer.jl b/test/Bridges/bridge_optimizer.jl index 27ce198de4..2647bf947d 100644 --- a/test/Bridges/bridge_optimizer.jl +++ b/test/Bridges/bridge_optimizer.jl @@ -218,7 +218,17 @@ MOI.Utilities.@model( (MOI.VectorAffineFunction, MOI.VectorQuadraticFunction) ) -function unsupported_constraint_attribute() +struct AttributeNotAllowed <: MOI.AbstractConstraintAttribute end + +function MOI.supports( + ::MOI.ModelLike, + ::AttributeNotAllowed, + ::Type{<:MOI.Bridges.Constraint.SplitIntervalBridge}, +) + return true +end + +function test_unsupported_constraint_attribute() mock = MOI.Utilities.MockOptimizer(NoIntervalModel{Float64}()) bridged_mock = MOI.Bridges.Constraint.LessToGreater{Float64}( MOI.Bridges.Constraint.SplitInterval{Float64}(mock), @@ -231,22 +241,19 @@ function unsupported_constraint_attribute() MOI.LessThan{Float64}, } attr = MOI.Test.UnknownConstraintAttribute() - err = ArgumentError( - "Bridge of type `$(bridge)` does not support accessing " * - "the attribute `$attr`. If you encountered this error " * - "unexpectedly, it probably means your model has been " * - "reformulated using the bridge, and you are attempting to query " * - "an attribute that we haven't implemented yet for this bridge. " * - "Please open an issue at https://github.com/jump-dev/MathOptInterface.jl/issues/new " * - "and provide a reproducible example explaining what you were " * - "trying to do.", - ) + message(action) = MOI.Bridges._attribute_error_message(attr, bridge, action) x = MOI.add_variable(bridged_mock) ci = MOI.add_constraint(bridged_mock, x, MOI.Interval(0.0, 1.0)) @test !MOI.Bridges.is_bridged(bridged_mock, ci) @test MOI.Bridges.is_bridged(bridged_mock.model, ci) @test !MOI.supports(bridged_mock, attr, typeof(ci)) + err = ArgumentError(message("accessing")) @test_throws err MOI.get(bridged_mock, attr, ci) + err = MOI.UnsupportedAttribute(attr, message("setting a value for")) + @test_throws err MOI.set(bridged_mock, attr, ci, 1) + attr = AttributeNotAllowed() + err = MOI.SetAttributeNotAllowed(attr, message("setting a value for")) + @test_throws err MOI.set(bridged_mock, attr, ci, 1) return end