class_docstr: A visitor that implements the logic for group-by-item resolution using the resolution DAG.
  
  Please see the behavior at each node for more details. Overall, this visitor pushes candidates that match an
  ambiguous group-by-item pattern from the root nodes (representing measures / the specs available for those
  measures) to the leaf node (the query for metrics / valid group-by items for querying). For nodes with multiple
  parents, the candidates from each parent are intersected and passed to the child node.
class_docstr_tok: ["A","visitor","that","implements","the","logic","for","group-by-item","resolution","using","the","resolution","DAG",".","Please","see","the","behavior","at","each","node","for","more","details",".","Overall",",","this","visitor","pushes","candidates","that","match","an","ambiguous","group-by-item","pattern","from","the","root","nodes","(","representing","measures","\/","the","specs","available","for","those","measures",")","to","the","leaf","node","(","the","query","for","metrics","\/","valid","group-by","items","for","querying",")",".","For","nodes","with","multiple","parents",",","the","candidates","from","each","parent","are","intersected","and","passed","to","the","child","node","."]
docstr: Push the group-by-item specs that are available to the measure and match the source patterns to the child.
code: def visit_measure_node(
      self, node: MeasureGroupByItemSourceNode
  ) -> PushDownResult:
      """Push the group-by-item specs that are available to the measure and match the source patterns to the child."""
      with self._path_from_start_node_tracker.track_node_visit(
          node
      ) as current_traversal_path:
          logger.info(f"Handling {node.ui_description}")
          specs_available_for_measure: Sequence[
              LinkableInstanceSpec
          ] = tuple(
              self._semantic_manifest_lookup.metric_lookup.group_by_item_specs_for_measure(
                  measure_reference=node.measure_reference,
                  with_any_of=self._with_any_property,
                  without_any_of=self._without_any_property,
              )
          )
  
          # The following is needed to handle limitation of cumulative metrics. Filtering could be done at the measure
          # node, but doing it here makes it a little easier to generate the error message.
          metric = (
              self._semantic_manifest_lookup.metric_lookup.get_metric(
                  node.child_metric_reference
              )
          )
  
          patterns_to_apply: Tuple[SpecPattern, ...] = ()
          if (
              metric.type is MetricType.SIMPLE
              or metric.type is MetricType.CONVERSION
          ):
              pass
          elif (
              metric.type is MetricType.RATIO
              or metric.type is MetricType.DERIVED
          ):
              assert (
                  False
              ), f"A measure should have a simple or cumulative metric as a child, but got {metric.type}"
          elif metric.type is MetricType.CUMULATIVE:
              # To handle the restriction that cumulative metrics can only be queried at the base grain, it's
              # easiest to handle that by applying the pattern to remove non-base grain time dimension specs at the
              # measure node and generate the issue here if there's nothing that matches. Generating the issue here
              # allows for creation of a more specific issue (i.e. include the measure) vs. generating the issue
              # at a higher level. This can be more cleanly handled once we add additional context to the
              # LinkableInstanceSpec.
              patterns_to_apply = (
                  # From comment in ValidLinkableSpecResolver:
                  #   It's possible to aggregate measures to coarser time granularities
                  #   (except with cumulative metrics).
                  BaseTimeGrainPattern(only_apply_for_metric_time=True),
                  # From comment in previous query parser:
                  #   Cannot extract date part for cumulative metrics.
                  NoneDatePartPattern(),
              )
          else:
              assert_values_exhausted(metric.type)
  
          specs_available_for_measure_given_child_metric = (
              specs_available_for_measure
          )
  
          for pattern_to_apply in patterns_to_apply:
              specs_available_for_measure_given_child_metric = (
                  InstanceSpecSet.from_specs(
                      pattern_to_apply.match(
                          specs_available_for_measure_given_child_metric
                      )
                  ).linkable_specs
              )
  
          matching_specs = (
              specs_available_for_measure_given_child_metric
          )
  
          for source_spec_pattern in self._source_spec_patterns:
              matching_specs = InstanceSpecSet.from_specs(
                  source_spec_pattern.match(matching_specs)
              ).linkable_specs
  
          logger.debug(
              f"For {node.ui_description}:\n"
              + indent(
                  "After applying patterns:\n"
                  + indent(mf_pformat(patterns_to_apply))
                  + "\n"
                  + "to inputs, matches are:\n"
                  + indent(mf_pformat(matching_specs))
              )
          )
  
          # The specified patterns don't match to any of the available group-by-items that can be queried for the
          # measure.
          if len(matching_specs) == 0:
              return PushDownResult(
                  candidate_set=GroupByItemCandidateSet.empty_instance(),
                  issue_set=MetricFlowQueryResolutionIssueSet.from_issue(
                      NoMatchingItemsForMeasure.from_parameters(
                          parent_issues=(),
                          query_resolution_path=current_traversal_path,
                          input_suggestions=tuple(
                              self._suggestion_generator.input_suggestions(
                                  specs_available_for_measure_given_child_metric
                              )
                          )
                          if self._suggestion_generator is not None
                          else (),
                      )
                  ),
              )
  
          return PushDownResult(
              candidate_set=GroupByItemCandidateSet(
                  measure_paths=(current_traversal_path,),
                  specs=tuple(matching_specs),
                  path_from_leaf_node=current_traversal_path,
              ),
              issue_set=MetricFlowQueryResolutionIssueSet(),
          )
  
imports: from __future__ import annotations
  import logging
  from contextlib import contextmanager
  from dataclasses import dataclass
  from typing import Dict, Iterator, List, Optional, Sequence, Set, Tuple
  from dbt_semantic_interfaces.enum_extension import assert_values_exhausted
  from dbt_semantic_interfaces.type_enums import MetricType
  from typing_extensions import override
  from metricflow.mf_logging.formatting import indent
  from metricflow.mf_logging.pretty_print import mf_pformat, mf_pformat_many
  from metricflow.model.semantic_manifest_lookup import SemanticManifestLookup
  from metricflow.model.semantics.linkable_element_properties import LinkableElementProperties
  from metricflow.query.group_by_item.candidate_push_down.group_by_item_candidate import GroupByItemCandidateSet
  from metricflow.query.group_by_item.resolution_dag.resolution_nodes.base_node import GroupByItemResolutionNode, GroupByItemResolutionNodeVisitor
  from metricflow.query.group_by_item.resolution_dag.resolution_nodes.measure_source_node import MeasureGroupByItemSourceNode
  from metricflow.query.group_by_item.resolution_dag.resolution_nodes.metric_resolution_node import MetricGroupByItemResolutionNode
  from metricflow.query.group_by_item.resolution_dag.resolution_nodes.no_metrics_query_source_node import NoMetricsGroupByItemSourceNode
  from metricflow.query.group_by_item.resolution_dag.resolution_nodes.query_resolution_node import QueryGroupByItemResolutionNode
  from metricflow.query.group_by_item.resolution_path import MetricFlowQueryResolutionPath
  from metricflow.query.issues.group_by_item_resolver.invalid_use_of_date_part import MetricExcludesDatePartIssue
  from metricflow.query.issues.group_by_item_resolver.no_common_items import NoCommonItemsInParents
  from metricflow.query.issues.group_by_item_resolver.no_matching_items_for_measure import NoMatchingItemsForMeasure
  from metricflow.query.issues.group_by_item_resolver.no_matching_items_for_no_metrics_query import NoMatchingItemsForNoMetricsQuery
  from metricflow.query.issues.issues_base import MetricFlowQueryResolutionIssueSet
  from metricflow.query.suggestion_generator import QueryItemSuggestionGenerator
  from metricflow.specs.patterns.base_time_grain import BaseTimeGrainPattern
  from metricflow.specs.patterns.none_date_part import NoneDatePartPattern
  from metricflow.specs.patterns.spec_pattern import SpecPattern
  from metricflow.specs.specs import InstanceSpecSet, LinkableInstanceSpec
label_desc: Use image node_id 2 for calling the _PushDownGroupByItemCandidatesVisitor obj's underlying member method code with example usage: obj.visit_measure_node(node) and returns: PushDownResult, PushDownResult