text: 
  import pytest
  
  from boltons.dictutils import OMD
  from boltons.iterutils import (first,
                                 remap,
                                 research,
                                 default_enter,
                                 default_exit,
                                 get_path)
  from boltons.namedutils import namedtuple
  
  
  isbool = lambda x: isinstance(x, bool)
  isint = lambda x: isinstance(x, int)
  odd = lambda x: isint(x) and x % 2 != 0
  even = lambda x: isint(x) and x % 2 == 0
  is_meaning_of_life = lambda x: x == 42
  
  
  class TestFirst(object):
      def test_empty_iterables(self):
          """
          Empty iterables return None.
          """
          s = set()
          l = []
          assert first(s) is None
          assert first(l) is None
  
      def test_default_value(self):
          """
          Empty iterables + a default value return the default value.
          """
          s = set()
          l = []
          assert first(s, default=42) == 42
          assert first(l, default=3.14) == 3.14
  
          l = [0, False, []]
          assert first(l, default=3.14) == 3.14
  
      def test_selection(self):
          """
          Success cases with and without a key function.
          """
          l = [(), 0, False, 3, []]
  
          assert first(l, default=42) == 3
          assert first(l, key=isint) == 0
          assert first(l, key=isbool) is False
          assert first(l, key=odd) == 3
          assert first(l, key=even) == 0
          assert first(l, key=is_meaning_of_life) is None
  
  
  class TestRemap(object):
      # TODO: test namedtuples and other immutable containers
  
      def test_basic_clone(self):
          orig = {"a": "b", "c": [1, 2]}
          assert orig == remap(orig)
  
          orig2 = [{1: 2}, {"a": "b", "c": [1, 2, {"cat": "dog"}]}]
          assert orig2 == remap(orig2)
  
      def test_empty(self):
          assert [] == remap([])
          assert {} == remap({})
          assert set() == remap(set())
  
      def test_unremappable(self):
          obj = object()
          with pytest.raises(TypeError):
              remap(obj)
  
      def test_basic_upper(self):
          orig = {'a': 1, 'b': object(), 'c': {'d': set()}}
          remapped = remap(orig, lambda p, k, v: (k.upper(), v))
          assert orig['a'] == remapped['A']
          assert orig['b'] == remapped['B']
          assert orig['c']['d'] == remapped['C']['D']
  
      def test_item_drop(self):
          orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
          even_items = remap(orig, lambda p, k, v: not (v % 2))
          assert even_items == [0, 2, 4, 6, 8]
  
      def test_noncallables(self):
          with pytest.raises(TypeError):
              remap([], visit='test')
          with pytest.raises(TypeError):
              remap([], enter='test')
          with pytest.raises(TypeError):
              remap([], exit='test')
  
      def test_sub_selfref(self):
          coll = [0, 1, 2, 3]
          sub = []
          sub.append(sub)
          coll.append(sub)
          with pytest.raises(RuntimeError):
              # if equal, should recurse infinitely
              assert coll == remap(coll)
  
      def test_root_selfref(self):
          selfref = [0, 1, 2, 3]
          selfref.append(selfref)
          with pytest.raises(RuntimeError):
              assert selfref == remap(selfref)
  
          selfref2 = {}
          selfref2['self'] = selfref2
          with pytest.raises(RuntimeError):
              assert selfref2 == remap(selfref2)
  
      def test_duperef(self):
          val = ['hello']
          duperef = [val, val]
          remapped = remap(duperef)
          assert remapped[0] is remapped[1]
          assert remapped[0] is not duperef[0]
  
      def test_namedtuple(self):
          """TODO: this fails right now because namedtuples' __new__ is
          overridden to accept arguments. remap's default_enter tries
          to create an empty namedtuple and gets a TypeError.
  
          Could make it so that immutable types actually don't create a
          blank new parent and instead use the old_parent as a
          placeholder, creating a new one at exit-time from the value's
          __class__ (how default_exit works now). But even then it would
          have to *args in the values, as namedtuple constructors don't
          take an iterable.
          """
  
          Point = namedtuple('Point', 'x y')
          point_map = {'origin': [Point(0, 0)]}
  
          with pytest.raises(TypeError):
              remapped = remap(point_map)
              assert isinstance(remapped['origin'][0], Point)
  
      def test_path(self):
          path_map = {}
  
          # test visit's path
          target_str = 'test'
          orig = [[[target_str]]]
          ref_path = (0, 0, 0)
  
          def visit(path, key, value):
              if value is target_str:
                  path_map['target_str'] = path + (key,)
              return key, value
  
          remapped = remap(orig, visit=visit)
  
          assert remapped == orig
          assert path_map['target_str'] == ref_path
  
          # test enter's path
          target_obj = object()
          orig = {'a': {'b': {'c': {'d': ['e', target_obj, 'f']}}}}
          ref_path = ('a', 'b', 'c', 'd', 1)
  
          def enter(path, key, value):
              if value is target_obj:
                  path_map['target_obj'] = path + (key,)
              return default_enter(path, key, value)
  
          remapped = remap(orig, enter=enter)
  
          assert remapped == orig
          assert path_map['target_obj'] == ref_path
  
          # test exit's path
          target_set = frozenset([1, 7, 3, 8])
          orig = [0, 1, 2, [3, 4, [5, target_set]]]
          ref_path = (3, 2, 1)
  
          def exit(path, key, old_parent, new_parent, new_items):
              if old_parent is target_set:
                  path_map['target_set'] = path + (key,)
              return default_exit(path, key, old_parent, new_parent, new_items)
  
          remapped = remap(orig, exit=exit)
  
          assert remapped == orig
          assert path_map['target_set'] == ref_path
  
      def test_reraise_visit(self):
          root = {'A': 'b', 1: 2}
          key_to_lower = lambda p, k, v: (k.lower(), v)
          with pytest.raises(AttributeError):
              remap(root, key_to_lower)
  
          remapped = remap(root, key_to_lower, reraise_visit=False)
          assert remapped['a'] == 'b'
          assert remapped[1] == 2
  
      def test_drop_nones(self):
          orig = {'a': 1, 'b': None, 'c': [3, None, 4, None]}
          ref = {'a': 1, 'c': [3, 4]}
          drop_none = lambda p, k, v: v is not None
          remapped = remap(orig, visit=drop_none)
          assert remapped == ref
  
          orig = [None] * 100
          remapped = remap(orig, drop_none)
          assert not remapped
  
      def test_dict_to_omd(self):
          def enter(path, key, value):
              if isinstance(value, dict):
                  return OMD(), sorted(value.items())
              return default_enter(path, key, value)
  
          orig = [{'title': 'Wild Palms',
                   'ratings': {1: 1, 2: 3, 3: 5, 4: 6, 5: 3}},
                  {'title': 'Twin Peaks',
                   'ratings': {1: 3, 2: 2, 3: 8, 4: 12, 5: 15}}]
          remapped = remap(orig, enter=enter)
          assert remapped == orig
  
          assert isinstance(remapped[0], OMD)
          assert isinstance(remapped[0]['ratings'], OMD)
          assert isinstance(remapped[1], OMD)
          assert isinstance(remapped[1]['ratings'], OMD)
  
      def test_sort_all_lists(self):
          def exit(path, key, old_parent, new_parent, new_items):
              # NB: in this case, I'd normally use *a, **kw
              ret = default_exit(path, key, old_parent, new_parent, new_items)
              if isinstance(ret, list):
                  ret.sort()
              return ret
  
          # NB: Airplane model numbers (Boeing and Airbus)
          orig = [[[7, 0, 7],
                   [7, 2, 7],
                   [7, 7, 7],
                   [7, 3, 7]],
                  [[3, 8, 0],
                   [3, 2, 0],
                   [3, 1, 9],
                   [3, 5, 0]]]
          ref = [[[0, 2, 3],
                  [0, 3, 5],
                  [0, 3, 8],
                  [1, 3, 9]],
                 [[0, 7, 7],
                  [2, 7, 7],
                  [3, 7, 7],
                  [7, 7, 7]]]
  
          remapped = remap(orig, exit=exit)
          assert remapped == ref
  
      def test_collector_pattern(self):
          all_interests = set()
  
          def enter(path, key, value):
              try:
                  all_interests.update(value['interests'])
              except:
                  pass
              return default_enter(path, key, value)
  
          orig = [{'name': 'Kate',
                   'interests': ['theater', 'manga'],
                   'dads': [{'name': 'Chris',
                             'interests': ['biking', 'python']}]},
                  {'name': 'Avery',
                   'interests': ['museums', 'pears'],
                   'dads': [{'name': 'Kurt',
                             'interests': ['python', 'recursion']}]}]
  
          ref = set(['python', 'recursion', 'biking', 'museums',
                     'pears', 'theater', 'manga'])
  
          remap(orig, enter=enter)
          assert all_interests == ref
  
      def test_add_length(self):
          def exit(path, key, old_parent, new_parent, new_items):
              ret = default_exit(path, key, old_parent, new_parent, new_items)
              try:
                  ret['review_length'] = len(ret['review'])
              except:
                  pass
              return ret
  
          orig = {'Star Trek':
                  {'TNG': {'stars': 10,
                           'review': "Episodic AND deep. <3 Data."},
                   'DS9': {'stars': 8.5,
                           'review': "Like TNG, but with a story and no Data."},
                   'ENT': {'stars': None,
                           'review': "Can't review what you can't watch."}},
                  'Babylon 5': {'stars': 6,
                                'review': "Sophomoric, like a bitter laugh."},
                  'Dr. Who': {'stars': None,
                              'review': "800 episodes is too many to review."}}
          remapped = remap(orig, exit=exit)
          assert (remapped['Star Trek']['TNG']['review_length']
                  < remapped['Star Trek']['DS9']['review_length'])
  
      def test_prepop(self):
          """Demonstrating normalization and ID addition through prepopulating
          the objects wth an enter callback.
          """
          base_obj = {'name': None,
                      'rank': None,
                      'id': 1}
  
          def enter(path, key, value):
              new_parent, new_items = default_enter(path, key, value)
              try:
                  new_parent.update(base_obj)
                  base_obj['id'] += 1
              except:
                  pass
              return new_parent, new_items
  
          orig = [{'name': 'Firefox', 'rank': 1},
                  {'name': 'Chrome', 'rank': 2},
                  {'name': 'IE'}]
          ref = [{'name': 'Firefox', 'rank': 1, 'id': 1},
                 {'name': 'Chrome', 'rank': 2, 'id': 2},
                 {'name': 'IE', 'rank': None, 'id': 3}]
          remapped = remap(orig, enter=enter)
          assert remapped == ref
  
      def test_remap_set(self):
          # explicit test for sets to make sure #84 is covered
          s = set([1, 2, 3])
          assert remap(s) == s
  
          fs = frozenset([1, 2, 3])
          assert remap(fs) == fs
  
  
  class TestGetPath(object):
      def test_depth_one(self):
          root = ['test']
          assert get_path(root, (0,)) == 'test'
          assert get_path(root, '0') == 'test'
  
          root = {'key': 'value'}
          assert get_path(root, ('key',)) == 'value'
          assert get_path(root, 'key') == 'value'
  
      def test_depth_two(self):
          root = {'key': ['test']}
          assert get_path(root, ('key', 0)) == 'test'
          assert get_path(root, 'key.0') == 'test'
  
  
  def test_research():
      root = {}
  
      with pytest.raises(TypeError):
          research(root, query=None)
  
      root = {'a': 'a'}
      res = research(root, query=lambda p, k, v: v == 'a')
      assert len(res) == 1
      assert res[0] == (('a',), 'a')
  
      def broken_query(p, k, v):
          raise RuntimeError()
  
      with pytest.raises(RuntimeError):
          research(root, broken_query, reraise=True)
  
      # empty results with default, reraise=False
      assert research(root, broken_query) == []
  
  
  def test_backoff_basic():
      from boltons.iterutils import backoff
  
      assert backoff(1, 16) == [1.0, 2.0, 4.0, 8.0, 16.0]
      assert backoff(1, 1) == [1.0]
      assert backoff(2, 15) == [2.0, 4.0, 8.0, 15.0]
  
  
  def test_backoff_repeat():
      from boltons.iterutils import backoff_iter
  
      fives = []
      for val in backoff_iter(5, 5, count='repeat'):
          fives.append(val)
          if len(fives) >= 1000:
              break
      assert fives == [5] * 1000
  
  
  def test_backoff_zero_start():
      from boltons.iterutils import backoff
  
      assert backoff(0, 16) == [0.0, 1.0, 2.0, 4.0, 8.0, 16.0]
      assert backoff(0, 15) == [0.0, 1.0, 2.0, 4.0, 8.0, 15.0]
  
      slow_backoff = [round(x, 2) for x in backoff(0, 2.9, factor=1.2)]
      assert slow_backoff == [0.0, 1.0, 1.2, 1.44, 1.73, 2.07, 2.49, 2.9]
  
  
  def test_backoff_validation():
      from boltons.iterutils import backoff
  
      with pytest.raises(ValueError):
          backoff(8, 2)
      with pytest.raises(ValueError):
          backoff(1, 0)
      with pytest.raises(ValueError):
          backoff(-1, 10)
      with pytest.raises(ValueError):
          backoff(2, 8, factor=0)
      with pytest.raises(ValueError):
          backoff(2, 8, jitter=20)
  
  
  def test_backoff_jitter():
      from boltons.iterutils import backoff
  
      start, stop = 1, 256
  
      unjittered = backoff(start, stop)
      jittered = backoff(start, stop, jitter=True)
  
      assert len(unjittered) == len(jittered)
      assert [u >= j for u, j in zip(unjittered, jittered)]
  
      neg_jittered = backoff(start, stop, jitter=-0.01)
  
      assert len(unjittered) == len(neg_jittered)
      assert [u <= j for u, j in zip(unjittered, neg_jittered)]
  
      o_jittered = backoff(start, stop, jitter=-0.0)
      assert len(unjittered) == len(o_jittered)
      assert [u == j for u, j in zip(unjittered, o_jittered)]
  
      nonconst_jittered = backoff(stop, stop, count=5, jitter=True)
      assert len(nonconst_jittered) == 5
      # no two should be equal realistically
      assert len(set(nonconst_jittered)) == 5
  
  
  def test_guiderator():
      import string
      from boltons.iterutils import GUIDerator
  
      guid_iter = GUIDerator()
  
      guid = next(guid_iter)
      assert guid
      assert len(guid) == guid_iter.size
      assert all([c in string.hexdigits for c in guid])
  
      guid2 = next(guid_iter)
  
      assert guid != guid2
  
      # custom size
      guid_iter = GUIDerator(size=26)
      assert len(next(guid_iter)) == 26
  
  
  def test_seqguiderator():
      import string
      from boltons.iterutils import SequentialGUIDerator as GUIDerator
  
      guid_iter = GUIDerator()
  
      guid = next(guid_iter)
      assert guid
      assert len(guid) == guid_iter.size
      assert all([c in string.hexdigits for c in guid])
  
      guid2 = next(guid_iter)
  
      assert guid != guid2
  
      # custom size
      for x in range(10000):
          guid_iter = GUIDerator(size=26)
          assert len(next(guid_iter)) == 26
  