code: def numeric_bounds_from_ast(
      tree: ast.AST, argname: str, fallback: ConstructivePredicate
  ) -> ConstructivePredicate:
      """Take an AST; return a ConstructivePredicate.
  
      >>> lambda x: x >= 0
      {"min_value": 0}, None
      >>> lambda x: x < 10
      {"max_value": 10, "exclude_max": True}, None
      >>> lambda x: x >= y
      {}, lambda x: x >= y
  
      See also https://greentreesnakes.readthedocs.io/en/latest/
      """
      if isinstance(tree, ast.Compare):
          ops = tree.ops
          vals = tree.comparators
          comparisons = [(tree.left, ops[0], vals[0])]
          for i, (op, val) in enumerate(
              zip(ops[1:], vals[1:]), start=1
          ):
              comparisons.append((vals[i - 1], op, val))
          bounds = []
          for comp in comparisons:
              try:
                  kwargs = comp_to_kwargs(*comp, argname=argname)
                  bounds.append(ConstructivePredicate(kwargs, None))
              except ValueError:
                  bounds.append(fallback)
          return merge_preds(*bounds)
  
      if isinstance(tree, ast.BoolOp) and isinstance(tree.op, ast.And):
          return merge_preds(
              *(
                  numeric_bounds_from_ast(node, argname, fallback)
                  for node in tree.values
              )
          )
  
      return fallback
  
imports: import ast
  import inspect
  import math
  import operator
  from decimal import Decimal
  from fractions import Fraction
  from functools import partial
  from typing import Any, Callable, Collection, Dict, NamedTuple, Optional, TypeVar
  from hypothesis.internal.compat import ceil, floor
  from hypothesis.internal.floats import next_down, next_up
  from hypothesis.internal.reflection import extract_lambda_source
label_desc: Use image node_id 4 for calling a global function with example usage: numeric_bounds_from_ast(tree, argname, fallback) and returns: fallback, merge_preds, merge_preds