# Buggy code
def extract_lambda_source(f):
    """Extracts a single lambda expression from the string source. Returns a
    string indicating an unknown body if it gets confused in any way.

    This is not a good function and I am sorry for it. Forgive me my
    sins, oh lord
    """
    argspec = getfullargspec(f)
    arg_strings = []
    # In Python 2 you can have destructuring arguments to functions. This
    # results in an argspec with non-string values. I'm not very interested in
    # handling these properly, but it's important to not crash on them.
    bad_lambda = False
    for a in argspec.args:
        if isinstance(a, (tuple, list)):  # pragma: no cover
            arg_strings.append("(%s)" % (", ".join(a),))
            bad_lambda = True
        else:
            assert isinstance(a, str)
            arg_strings.append(a)
    if argspec.varargs:
        arg_strings.append("*" + argspec.varargs)
    elif argspec.kwonlyargs:
        arg_strings.append("*")
    for a in argspec.kwonlyargs or []:
        default = (argspec.kwonlydefaults or {}).get(a)
        if default:
            arg_strings.append("{}={}".format(a, default))
        else:
            arg_strings.append(a)

    if_confused = "lambda %s: <unknown>" % (", ".join(arg_strings),)
    if bad_lambda:  # pragma: no cover
        return if_confused
    try:
        source = inspect.getsource(f)
    except IOError:
        return if_confused

    source = LINE_CONTINUATION.sub(" ", source)
    source = WHITESPACE.sub(" ", source)
    source = source.strip()
    assert "lambda" in source

    tree = None

    try:
        tree = ast.parse(source)
    except SyntaxError:
        for i in hrange(len(source) - 1, len("lambda"), -1):
            prefix = source[:i]
            if "lambda" not in prefix:
                break
            try:
                tree = ast.parse(prefix)
                source = prefix
                break
            except SyntaxError:
                continue
    if tree is None:
        if source.startswith("@"):
            # This will always eventually find a valid expression because
            # the decorator must be a valid Python function call, so will
            # eventually be syntactically valid and break out of the loop. Thus
            # this loop can never terminate normally, so a no branch pragma is
            # appropriate.
            for i in hrange(len(source) + 1):  # pragma: no branch
                p = source[1:i]
                if "lambda" in p:
                    try:
                        tree = ast.parse(p)
                        source = p
                        break
                    except SyntaxError:
                        pass

    if tree is None:
        return if_confused

    all_lambdas = extract_all_lambdas(tree)
    aligned_lambdas = [l for l in all_lambdas if args_for_lambda_ast(l) == argspec.args]
    if len(aligned_lambdas) != 1:
        return if_confused
    lambda_ast = aligned_lambdas[0]
    assert lambda_ast.lineno == 1
    source = source[lambda_ast.col_offset :].strip()

    source = source[source.index("lambda") :]
    for i in hrange(len(source), len("lambda"), -1):  # pragma: no branch
        try:
            parsed = ast.parse(source[:i])
            assert len(parsed.body) == 1
            assert parsed.body
            if isinstance(parsed.body[0].value, ast.Lambda):
                source = source[:i]
                break
        except SyntaxError:
            pass
    lines = source.split("\n")
    lines = [PROBABLY_A_COMMENT.sub("", l) for l in lines]
    source = "\n".join(lines)

    source = WHITESPACE.sub(" ", source)
    source = SPACE_FOLLOWS_OPEN_BRACKET.sub("(", source)
    source = SPACE_PRECEDES_CLOSE_BRACKET.sub(")", source)
    source = source.strip()
    return source


# Traceback (ValueError)
$ python3.6 -m pytest test2.py --tb=short
pyenv-implicit: found multiple python3.6 in pyenv. Use version 3.6.
================================================================ test session starts ================================================================
platform darwin -- Python 3.6.5, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/Users/lwiman/personal/nerdsniped/sine_testing/.hypothesis/examples')
rootdir: /Users/lwiman/personal/nerdsniped/sine_testing, inifile:
plugins: hypothesis-3.83.2
collected 1 item

test2.py F                                                                                                                                    [100%]

===================================================================== FAILURES ======================================================================
_____________________________________________________________________ test_foo ______________________________________________________________________
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/engine.py:174: in test_function
self._test_function(data)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/core.py:625: in evaluate_test_data
escalate_hypothesis_internal_error()
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/core.py:605: in evaluate_test_data
result = self.execute(data)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/core.py:574: in execute
result = self.test_runner(data, run)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/executors.py:56: in default_new_style_executor
return function(data)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/core.py:551: in run
args, kwargs = data.draw(self.search_strategy)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:224: in draw
return self.__draw(strategy, label=label)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:239: in __draw
return strategy.do_draw(self)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/searchstrategy/collections.py:55: in do_draw
return tuple(data.draw(e) for e in self.element_strategies)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/searchstrategy/collections.py:55: in <genexpr>
return tuple(data.draw(e) for e in self.element_strategies)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:224: in draw
return self.__draw(strategy, label=label)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:233: in __draw
return strategy.do_draw(self)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/searchstrategy/strategies.py:576: in do_draw
result = self.pack(data.draw(self.mapped_strategy))
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:224: in draw
return self.__draw(strategy, label=label)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:233: in __draw
return strategy.do_draw(self)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/searchstrategy/lazy.py:156: in do_draw
return data.draw(self.wrapped_strategy)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:224: in draw
return self.__draw(strategy, label=label)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:233: in __draw
return strategy.do_draw(self)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/searchstrategy/strategies.py:576: in do_draw
result = self.pack(data.draw(self.mapped_strategy))
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:224: in draw
return self.__draw(strategy, label=label)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:233: in __draw
return strategy.do_draw(self)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/searchstrategy/collections.py:55: in do_draw
return tuple(data.draw(e) for e in self.element_strategies)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/searchstrategy/collections.py:55: in <genexpr>
return tuple(data.draw(e) for e in self.element_strategies)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:224: in draw
return self.__draw(strategy, label=label)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/conjecture/data.py:233: in __draw
return strategy.do_draw(self)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/searchstrategy/strategies.py:633: in do_draw
data.note_event("Aborted test because unable to satisfy %r" % (self,))
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/searchstrategy/strategies.py:610: in __repr__
get_pretty_function_description(self.condition),
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/reflection.py:375: in get_pretty_function_description
result = extract_lambda_source(f)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/reflection.py:348: in extract_lambda_source
source = source[source.index("lambda") :]
E   ValueError: substring not found

During handling of the above exception, another exception occurred:
test2.py:7: in test_foo
???
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/lazyformat.py:29: in __str__
return self.__format_string % self.__args
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/searchstrategy/strategies.py:610: in __repr__
get_pretty_function_description(self.condition),
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/reflection.py:375: in get_pretty_function_description
result = extract_lambda_source(f)
../../../.pyenv/versions/3.6/lib/python3.6/site-packages/hypothesis/internal/reflection.py:348: in extract_lambda_source
source = source[source.index("lambda") :]
E   ValueError: substring not found
-------------------------------------------------------------------- Hypothesis ---------------------------------------------------------------------
You can add @seed(306900588268218898824024975885321058451) to this test or run pytest with --hypothesis-seed=306900588268218898824024975885321058451 to reproduce this failure.
============================================================= 1 failed in 0.32 seconds ==============================================================

# Fixed code
def extract_lambda_source(f):
    """Extracts a single lambda expression from the string source. Returns a
    string indicating an unknown body if it gets confused in any way.

    This is not a good function and I am sorry for it. Forgive me my
    sins, oh lord
    """
    argspec = getfullargspec(f)
    arg_strings = []
    # In Python 2 you can have destructuring arguments to functions. This
    # results in an argspec with non-string values. I'm not very interested in
    # handling these properly, but it's important to not crash on them.
    bad_lambda = False
    for a in argspec.args:
        if isinstance(a, (tuple, list)):  # pragma: no cover
            arg_strings.append("(%s)" % (", ".join(a),))
            bad_lambda = True
        else:
            assert isinstance(a, str)
            arg_strings.append(a)
    if argspec.varargs:
        arg_strings.append("*" + argspec.varargs)
    elif argspec.kwonlyargs:
        arg_strings.append("*")
    for a in argspec.kwonlyargs or []:
        default = (argspec.kwonlydefaults or {}).get(a)
        if default:
            arg_strings.append("{}={}".format(a, default))
        else:
            arg_strings.append(a)

    if_confused = "lambda %s: <unknown>" % (", ".join(arg_strings),)
    if bad_lambda:  # pragma: no cover
        return if_confused
    try:
        source = inspect.getsource(f)
    except IOError:
        return if_confused

    source = LINE_CONTINUATION.sub(" ", source)
    source = WHITESPACE.sub(" ", source)
    source = source.strip()
    assert "lambda" in source

    tree = None

    try:
        tree = ast.parse(source)
    except SyntaxError:
        for i in hrange(len(source) - 1, len("lambda"), -1):
            prefix = source[:i]
            if "lambda" not in prefix:
                break
            try:
                tree = ast.parse(prefix)
                source = prefix
                break
            except SyntaxError:
                continue
    if tree is None:
        if source.startswith("@"):
            # This will always eventually find a valid expression because
            # the decorator must be a valid Python function call, so will
            # eventually be syntactically valid and break out of the loop. Thus
            # this loop can never terminate normally, so a no branch pragma is
            # appropriate.
            for i in hrange(len(source) + 1):  # pragma: no branch
                p = source[1:i]
                if "lambda" in p:
                    try:
                        tree = ast.parse(p)
                        source = p
                        break
                    except SyntaxError:
                        pass

    if tree is None:
        return if_confused

    all_lambdas = extract_all_lambdas(tree)
    aligned_lambdas = [l for l in all_lambdas if args_for_lambda_ast(l) == argspec.args]
    if len(aligned_lambdas) != 1:
        return if_confused
    lambda_ast = aligned_lambdas[0]
    assert lambda_ast.lineno == 1

    # If the source code contains Unicode characters, the bytes of the original
    # file don't line up with the string indexes, and `col_offset` doesn't match
    # the string we're using.  We need to convert the source code into bytes
    # before slicing.
    #
    # Under the hood, the inspect module is using `tokenize.detect_encoding` to
    # detect the encoding of the original source file.  We'll use the same
    # approach to get the source code as bytes.
    #
    # See https://github.com/HypothesisWorks/hypothesis/issues/1700 for an
    # example of what happens if you don't correct for this.
    #
    # Note: if the code doesn't come from a file (but, for example, a doctest),
    # `getsourcefile` will return `None` and the `open()` call will fail with
    # an OSError.  Or if `f` is a built-in function, in which case we get a
    # TypeError.  In both cases, fall back to splitting the Unicode string.
    # It's not perfect, but it's the best we can do.
    #
    # Note 2: You can only detect the encoding with `tokenize.detect_encoding`
    # in Python 3.2 or later.  But that's okay, because the only version that
    # affects for us is Python 2.7, and 2.7 doesn't support non-ASCII identifiers:
    # https://www.python.org/dev/peps/pep-3131/. In this case we'll get an
    # AttributeError.
    #
    try:
        encoding, _ = tokenize.detect_encoding(
            open(inspect.getsourcefile(f), "rb").readline
        )
        source_bytes = source.encode(encoding)
        source_bytes = source_bytes[lambda_ast.col_offset :].strip()
        source = source_bytes.decode(encoding)
    except (AttributeError, OSError, TypeError):
        source = source[lambda_ast.col_offset :].strip()

    # This ValueError can be thrown in Python 3 if:
    #
    #  - There's a Unicode character in the line before the Lambda, and
    #  - For some reason we can't detect the source encoding of the file
    #
    # because slicing on `lambda_ast.col_offset` will account for bytes, but
    # the slice will be on Unicode characters.
    #
    # In practice this seems relatively rare, so we just give up rather than
    # trying to recover.
    try:
        source = source[source.index("lambda") :]
    except ValueError:
        return if_confused

    for i in hrange(len(source), len("lambda"), -1):  # pragma: no branch
        try:
            parsed = ast.parse(source[:i])
            assert len(parsed.body) == 1
            assert parsed.body
            if isinstance(parsed.body[0].value, ast.Lambda):
                source = source[:i]
                break
        except SyntaxError:
            pass
    lines = source.split("\n")
    lines = [PROBABLY_A_COMMENT.sub("", l) for l in lines]
    source = "\n".join(lines)

    source = WHITESPACE.sub(" ", source)
    source = SPACE_FOLLOWS_OPEN_BRACKET.sub("(", source)
    source = SPACE_PRECEDES_CLOSE_BRACKET.sub(")", source)
    source = source.strip()
    return source