code: def test_get_many(inject_failure_rate, s3root, prefixes, expected):
      def iter_objs(urls, objs):
          for url in urls:
              obj = objs[url]
              for r, expected in obj.items():
                  if r is None:
                      yield url, None, None
                  else:
                      yield url, expected.range.req_offset, expected.range.req_size
  
      with S3(inject_failure_rate=inject_failure_rate) as s3:
          # 1) test the non-missing case
  
          # to test result ordering, make sure we are requesting
          # keys in a non-lexicographic order
          not_missing_urls = [
              k for k, v in expected.items() if v[None].size is not None
          ]
          urls_in_order = list(sorted(not_missing_urls, reverse=True))
          ranges_in_order = []
          for url in urls_in_order:
              ranges_in_order.extend(
                  v.range for v in expected[url].values()
              )
  
          objs_in_order = list(
              starmap(S3GetObject, iter_objs(urls_in_order, expected))
          )
          s3objs = s3.get_many(list(objs_in_order), return_info=True)
  
          fetched_urls = []
          for url in urls_in_order:
              fetched_urls.extend([url] * len(expected[url]))
          # results should come out in the order of keys requested
          assert fetched_urls == [e.url for e in s3objs]
          assert_results(
              s3objs, expected, ranges_fetched=ranges_in_order
          )
  
          # 2) test with missing items, default case
          if not_missing_urls != list(expected.keys()):
              urls_in_order = list(
                  sorted(expected.keys(), reverse=True)
              )
              ranges_in_order = []
              for url in urls_in_order:
                  ranges_in_order.extend(
                      v.range for v in expected[url].values()
                  )
              objs_in_order = list(
                  starmap(
                      S3GetObject, iter_objs(urls_in_order, expected)
                  )
              )
              fetched_urls = []
              for url in urls_in_order:
                  fetched_urls.extend([url] * len(expected[url]))
              with pytest.raises(MetaflowS3NotFound):
                  s3objs = s3.get_many(
                      list(objs_in_order), return_info=True
                  )
  
          # 3) test with missing items, return_missing=True
  
          # to test result ordering, make sure we are requesting
          # keys in a non-lexicographic order. Missing files should
          # be returned in order too
          # Here we can use urls_in_order, ranges_in_order and objs_in_order because they
          # always correspond to the full set
          s3objs = s3.get_many(
              list(objs_in_order), return_missing=True, return_info=True
          )
          assert fetched_urls == [e.url for e in s3objs]
          assert_results(
              s3objs, expected, ranges_fetched=ranges_in_order
          )
  
imports: import os
  from re import I
  import shutil
  from hashlib import sha1
  from tempfile import mkdtemp
  from itertools import groupby, starmap
  import random
  from uuid import uuid4
  from metaflow.plugins.datatools import s3
  import pytest
  from metaflow import current, namespace, Run
  from metaflow.plugins.datatools.s3 import S3, MetaflowS3AccessDenied, MetaflowS3NotFound, MetaflowS3URLException, MetaflowS3InvalidObject, S3PutObject, S3GetObject
  from metaflow.util import to_bytes, unicode_type
  from .None import s3_data
  from ..None import FakeFlow, DO_TEST_RUN
label_desc: Use image node_id 20 for calling a global function with example usage: test_get_many(inject_failure_rate, s3root, prefixes, expected) without return types