def METHOD_NAME(exercise_fail):
  exercise_func_wrapper_sub_directories()
  from libtbx import easy_mp
  mp_problem = easy_mp.detect_problem()
  if (mp_problem is not None):
    print("Skipping tst_easy_mp.py: %s" % mp_problem)
    return
  check_if_stacktrace_is_propagated_properly(method='threading', nproc=2)
  check_if_stacktrace_is_propagated_properly(method='multiprocessing', nproc=2)
  check_if_stacktrace_is_propagated_properly(method='threading', nproc=1)
  check_if_stacktrace_is_propagated_properly(method='multiprocessing', nproc=1)
  data = potentially_large(size=1000)
  eval_parallel(data)
  assert len(easy_mp.fixed_func_registry) == 0
  eval_parallel(data, func_wrapper=None, index_args=False)
  assert len(easy_mp.fixed_func_registry) == 1
  eval_parallel(data, func_wrapper=None, index_args=False, log=sys.stdout)
  assert len(easy_mp.fixed_func_registry) == 2
  sio = StringIO()
  eval_parallel(data, func_wrapper=None, index_args=False, log=sio)
  assert len(easy_mp.fixed_func_registry) == 3
  lines = sio.getvalue().splitlines()
  assert len(lines) == 2
  assert lines[0].startswith("multiprocessing pool size: ")
  assert lines[1].startswith("wall clock time: ")
  eval_parallel(data, exercise_out_of_range=True)
  if (exercise_fail):
    eval_parallel(data, exercise_fail=True)
    raise Exception_expected
  results = easy_mp.pool_map(fixed_func=data, args=list(range(1000)), processes=Auto)
  del data
  assert len(easy_mp.fixed_func_registry) == 0
  #
  from libtbx.clear_paths import \
    remove_or_rename_files_and_directories_if_possible as clear
  import os
  op = os.path
  def fixed_func(arg):
    print("hello world", arg)
    return 10*arg
  def go():
    return easy_mp.pool_map(
      fixed_func=fixed_func,
      func_wrapper="sub_directories",
      args=[1,2])
  clear(paths=["mp000", "mp001"])
  results = go()
  assert results == [(None, 10), (None, 20)]
  for i in [1,2]:
    with open("mp%03d/log" % (i-1), 'r') as fh:
      assert fh.read().splitlines() == ["hello world %d" % i]
  results = go()
  assert results == [
    ('sub-directory exists already: "mp000"', None),
    ('sub-directory exists already: "mp001"', None)]
  clear(paths=["mp001"])
  results = go()
  assert results == [
    ('sub-directory exists already: "mp000"', None),
    (None, 20)]
  clear(paths=["mp000", "mp001"])
  results = easy_mp.pool_map(
    fixed_func=fixed_func,
    func_wrapper=easy_mp.func_wrapper_sub_directories(makedirs_mode=0),
    args=[1,2])
  assert results == [
    ('cannot chdir to sub-directory: "mp000"', None),
    ('cannot chdir to sub-directory: "mp001"', None)]
  clear(paths=["mp000", "mp001"])
  clear(paths=["bf000", "bf001"])
  def bad_func(arg):
    raise RuntimeError(str(arg))
  results = easy_mp.pool_map(
    fixed_func=bad_func,
    func_wrapper="sub_directories:bf",
    args=[1,2])
  assert results == [
    ('CAUGHT EXCEPTION: "bf000/log"', None),
    ('CAUGHT EXCEPTION: "bf001/log"', None)]
  for i in [1,2]:
    with open("bf%03d/log" % (i-1)) as f:
      assert f.read().splitlines()[-1] == "RuntimeError: %d" % i
  out = StringIO()
  def simple_func(arg):
    from math import log
    x = float(arg)
    y = 0
    for i in range(2, 1000):
      y += log(x) / log(float(i))
    return y
  def cb(result):
    out.write("%.3f\n" % result)
  result = easy_mp.pool_map(
    func=simple_func,
    args=[1,2,3,4,5,6,7,8],
    call_back_for_serial_run=cb,
    processes=1)
  assert (out.getvalue() == """\