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: At the metric node, intersect candidates from the parents and pass them to the children.
  
  This node can represent a metric that prevents querying by some types of group-by items. To model that
  restriction, this filters the appropriate candidates.
code: def visit_metric_node(
      self, node: MetricGroupByItemResolutionNode
  ) -> PushDownResult:
      """At the metric node, intersect candidates from the parents and pass them to the children.
  
      This node can represent a metric that prevents querying by some types of group-by items. To model that
      restriction, this filters the appropriate candidates.
      """
      with self._path_from_start_node_tracker.track_node_visit(
          node
      ) as current_traversal_path:
          merged_result_from_parents = (
              self._merge_push_down_results_from_parents(
                  push_down_results_from_parents={
                      parent_node: parent_node.accept(self)
                      for parent_node in node.parent_nodes
                  },
                  current_traversal_path=current_traversal_path,
              )
          )
          logger.info(f"Handling {node.ui_description}")
          logger.debug(
              "candidates from parents:\n"
              + indent(
                  mf_pformat(
                      merged_result_from_parents.candidate_set.specs
                  )
              )
          )
          if merged_result_from_parents.candidate_set.is_empty:
              return merged_result_from_parents
  
          metric = (
              self._semantic_manifest_lookup.metric_lookup.get_metric(
                  node.metric_reference
              )
          )
  
          # For metrics with offset_to_grain, don't allow date_part group-by-items
          patterns_to_apply: Sequence[SpecPattern] = ()
          if (
              metric.type is MetricType.SIMPLE
              or metric.type is MetricType.CUMULATIVE
              or metric.type is MetricType.CONVERSION
          ):
              pass
          elif (
              metric.type is MetricType.RATIO
              or metric.type is MetricType.DERIVED
          ):
              for input_metric in metric.input_metrics:
                  if input_metric.offset_to_grain:
                      # From comment in previous query parser:
                      # "Cannot extract date part for metrics with offset_to_grain."
                      patterns_to_apply = (NoneDatePartPattern(),)
                      break
          else:
              assert_values_exhausted(metric.type)
  
          candidate_specs: Sequence[
              LinkableInstanceSpec
          ] = merged_result_from_parents.candidate_set.specs
          issue_sets_to_merge = [merged_result_from_parents.issue_set]
  
          matched_specs = candidate_specs
          for pattern_to_apply in patterns_to_apply:
              matched_specs = InstanceSpecSet.from_specs(
                  pattern_to_apply.match(matched_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, outputs are:\n"
                  + indent(mf_pformat(matched_specs))
              )
          )
  
          # There were candidates that were common from the ones passed from parents, but after applying the filters,
          # none of the candidates were valid.
          if len(matched_specs) == 0:
              issue_sets_to_merge.append(
                  MetricFlowQueryResolutionIssueSet.from_issue(
                      MetricExcludesDatePartIssue.from_parameters(
                          query_resolution_path=current_traversal_path,
                          candidate_specs=candidate_specs,
                          parent_issues=(),
                      )
                  )
              )
  
          return PushDownResult(
              candidate_set=GroupByItemCandidateSet(
                  specs=tuple(matched_specs),
                  measure_paths=merged_result_from_parents.candidate_set.measure_paths
                  if len(matched_specs) > 0
                  else (),
                  path_from_leaf_node=current_traversal_path,
              ),
              issue_set=MetricFlowQueryResolutionIssueSet.merge_iterable(
                  issue_sets_to_merge
              ),
          )
  
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 4 for calling the _PushDownGroupByItemCandidatesVisitor obj's underlying member method code with example usage: obj.visit_metric_node(node) and returns: PushDownResult, merged_result_from_parents