Source code for nv_dfm_core.api._bool_expressions

# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from abc import ABC
from typing import Any, Literal

from pydantic import BaseModel, Field, JsonValue, field_validator

from ._node_param import NodeParam

json_without_dicts = list[JsonValue] | str | bool | int | float | None
# we cannot allow dicts as an atom because then serialization is ambiguous
# and the other Bool models will be de-serialized as simple dicts
Atom = NodeParam | list[JsonValue] | json_without_dicts


[docs] class And(BaseModel): """Represents a logical AND operation.""" operator: Literal["and"] = Field(default="and") exp: list["BooleanExpression"] = Field(min_length=2) @field_validator("exp", mode="before") # noqa: F821 @classmethod def rewrite_expression_object_to_noderef(cls, v: Any) -> Any: from ._expression import Expression # pylint: disable=import-outside-toplevel return [e.dfm_node_id.to_ref() if isinstance(e, Expression) else e for e in v] def accept(self, visitor: Any) -> None: visitor.visit_and(self)
[docs] class Or(BaseModel): """Represents a logical OR operation.""" operator: Literal["or"] = Field(default="or") exp: list["BooleanExpression"] = Field(min_length=2) @field_validator("exp", mode="before") # noqa: F821 @classmethod def rewrite_expression_object_to_noderef(cls, v: Any) -> Any: from ._expression import Expression # pylint: disable=import-outside-toplevel return [e.dfm_node_id.to_ref() if isinstance(e, Expression) else e for e in v] def accept(self, visitor: Any) -> None: visitor.visit_or(self)
[docs] class Not(BaseModel): """Represents a logical NOT operation.""" operator: Literal["not"] = Field(default="not") exp: "BooleanExpression" @field_validator("exp", mode="before") # noqa: F821 @classmethod def rewrite_expression_object_to_noderef(cls, v: Any) -> Any: from ._expression import Expression # pylint: disable=import-outside-toplevel return v.dfm_node_id.to_ref() if isinstance(v, Expression) else v def accept(self, visitor: Any) -> None: visitor.visit_not(self)
class BinaryOp(BaseModel, ABC): # pyright: ignore[reportUnsafeMultipleInheritance] """Base class for binary boolean operations (e.g., comparisons).""" left: "BooleanExpression" right: "BooleanExpression" @field_validator("left", mode="before") # noqa: F821 @classmethod def rewrite_left_object_to_noderef(cls, v: Any) -> Any: from ._expression import Expression # pylint: disable=import-outside-toplevel return v.dfm_node_id.to_ref() if isinstance(v, Expression) else v @field_validator("right", mode="before") # noqa: F821 @classmethod def rewrite_right_object_to_noderef(cls, v: Any) -> Any: from ._expression import Expression # pylint: disable=import-outside-toplevel return v.dfm_node_id.to_ref() if isinstance(v, Expression) else v
[docs] class Equal(BinaryOp): """Represents an equality comparison (==).""" operator: Literal["=="] = "==" def accept(self, visitor: Any) -> None: visitor.visit_equal(self)
[docs] class NotEqual(BinaryOp): """Represents a not equal comparison (!=).""" operator: Literal["!="] = "!=" def accept(self, visitor: Any) -> None: visitor.visit_not_equal(self)
[docs] class GreaterThan(BinaryOp): """Represents a greater than comparison (>).""" operator: Literal[">"] = ">" def accept(self, visitor: Any) -> None: visitor.visit_greater_than(self)
[docs] class GreaterThanOrEqual(BinaryOp): """Represents a greater than or equal comparison (>=).""" operator: Literal[">="] = ">=" def accept(self, visitor: Any) -> None: visitor.visit_greater_than_or_equal(self)
[docs] class LessThan(BinaryOp): """Represents a less than comparison (<).""" operator: Literal["<"] = "<" def accept(self, visitor: Any) -> None: visitor.visit_less_than(self)
[docs] class LessThanOrEqual(BinaryOp): """Represents a less than or equal comparison (<=).""" operator: Literal["<="] = "<=" def accept(self, visitor: Any) -> None: visitor.visit_less_than_or_equal(self)
# A comparison expression can be any of the specific comparison types # We use a discriminator on the 'operator' field. ComparisonExpression = ( Equal | NotEqual | GreaterThan | GreaterThanOrEqual | LessThan | LessThanOrEqual ) # The main BooleanExpression type: # This is where the magic of the discriminator truly shines for nested unions. # Pydantic will first look for an 'operator' field. # If 'operator' is present, it will try to match one of the operator-based models (And, Or, Not, ComparisonExpression). # If 'operator' is not present, it will fall back to trying to match Atom. BooleanExpression = Atom | And | Or | Not | ComparisonExpression