Beispiel #1
0
    def simplify_with_example_cloning(self, random, x):
        assert isinstance(x, tuple)
        if len(x) <= 1:
            return

        for _ in hrange(20):
            result = list(x)
            pivot = random.choice(x)
            for _ in hrange(3):
                alt_pivot = random.choice(x)
                if self.element_strategy.strictly_simpler(alt_pivot, pivot):
                    pivot = alt_pivot
            indices = [j for j in hrange(len(x)) if self.element_strategy.strictly_simpler(pivot, x[j])]
            if not indices:
                continue
            random.shuffle(indices)
            # For slightly silly reasons we first try cloning to all but one
            # index, then to all of them. This is because empirically in some
            # artificial examples this is more likely to hit, and if we're in
            # the warmup phase we only get one.
            # It's unlikely this is actually of much benefit in practical cases
            # but it makes the tests pass. Sorry.
            for j in indices[:-1]:
                result[j] = deepcopy(pivot)
            yield tuple(result)
            result[indices[-1]] = deepcopy(pivot)
            yield tuple(result)
            for i in indices[:-2]:
                result[i] = x[i]
                yield tuple(result)
Beispiel #2
0
    def simplify_towards_2000(self, random, value):
        s = set((value,))
        s.add(value.replace(microsecond=0))
        s.add(value.replace(second=0))
        s.add(value.replace(minute=0))
        s.add(value.replace(hour=0))
        s.add(value.replace(day=1))
        s.add(value.replace(month=1))
        if self.year_in_bounds(2000):
            s.add(value.replace(year=2000))
        s.remove(value)
        for t in s:
            yield t

        for h in hrange(value.hour - 1, 0, -1):
            yield value.replace(hour=h)

        year = value.year
        if year == 2000:
            return
        # We swallow a bunch of value errors here.
        # These can happen if the original value was february 29 on a
        # leap year and the current year is not a leap year.
        # Note that 2000 was a leap year which is why we didn't need one above.
        mid = (year + 2000) // 2
        if mid != 2000 and mid != year and self.year_in_bounds(mid):
            yield value.replace(year=mid)
        direction = -1 if year > 2000 else 1
        years = hrange(year + direction, 2000, direction)
        for year in years:
            if year == mid:
                continue
            if self.year_in_bounds(year):
                yield value.replace(year=year)
Beispiel #3
0
 def simplify(self, x):
     ix = int(x)
     if type(ix) != type(x):  # pragma: no cover
         yield ix
     if x < 0:
         yield -x
         for y in self.simplify(-x):
             yield -y
     elif x > 0:
         yield 0
         if x == 1:
             return
         yield x // 2
         if x == 2:
             return
         max_iters = 100
         if x <= max_iters:
             for i in hrange(x - 1, 0, -1):
                 if i != x // 2:
                     yield i
         else:
             random = Random(x)
             seen = {0, x // 2}
             for _ in hrange(max_iters):
                 i = random.randint(0, x - 1)
                 if i not in seen:
                     yield i
                 seen.add(i)
def test_large_hrange():
    n = 1 << 1024
    assert list(hrange(n, n + 5, 2)) == [n, n + 2, n + 4]
    assert list(hrange(n, n)) == []

    with pytest.raises(ValueError):
        hrange(n, n, 0)
    def run(self):
        if not any(self.current):
            return
        if self.incorporate(hbytes(self.size)):
            return
        for c in hrange(max(self.current)):
            if self.incorporate(
                hbytes(min(b, c) for b in self.current)
            ):
                break

        change_counter = -1
        while self.current and change_counter < self.changes:
            change_counter = self.changes
            for i in hrange(self.size):
                t = self.current[i]
                if t > 0:
                    ss = small_shrinks[self.current[i]]
                    for c in ss:
                        if self._shrink_index(i, c):
                            for c in hrange(self.current[i]):
                                if c in ss:
                                    continue
                                if self._shrink_index(i, c):
                                    break
                            break
def test_compact_blocks_during_generation():
    d = ConjectureData.for_buffer([1] * 10)
    for _ in hrange(5):
        d.draw_bits(1)
    assert len(list(d.blocks)) == 5
    for _ in hrange(5):
        d.draw_bits(1)
    assert len(list(d.blocks)) == 10
Beispiel #7
0
 def simplify(self, x):
     if x == self.start:
         return
     mid = (self.start + self.end) // 2
     for t in hrange(self.start, min(x, mid)):
         yield t
     if x > mid:
         yield self.start + (self.end - x)
         for t in hrange(self.end, x, -1):
             yield t
def test_strategy_for_integer_range_produces_only_integers_in_that_range():
    just_one_integer = strategy(specifiers.IntegerRange(1, 1))
    for _ in hrange(100):
        pv = just_one_integer.draw_parameter(random)
        x = just_one_integer.produce_template(BuildContext(random), pv)
        assert x == 1
    some_integers = strategy(specifiers.IntegerRange(1, 10))
    for _ in hrange(100):
        pv = some_integers.produce_parameter(random)
        x = some_integers.produce_template(BuildContext(random), pv)
        assert 1 <= x <= 10
Beispiel #9
0
 def basic_simplify(self, random, x):
     if x in self.ascii_characters:
         for i in hrange(0, self.ascii_characters.index(x)):
             yield self.ascii_characters[i]
     else:
         o = ord(x)
         for c in self.ascii_characters:
             yield text_type(c)
         yield hunichr(o // 2)
         for t in hrange(o - 1, max(o - 10, -1), -1):
             yield hunichr(t)
def rare_value_strategy(n, target):
    def forbid(s, forbidden):
        """Helper function to avoid Python variable scoping issues."""
        return s.filter(lambda x: x != forbidden)

    s = st.sampled_from(hrange(n))
    for i in hrange(n):
        if i != target:
            s = forbid(s, i)

    return s
def test_strategy_for_integer_range_produces_only_integers_in_that_range():
    just_one_integer = integers(1, 1)
    for _ in hrange(100):
        pv = just_one_integer.draw_parameter(random)
        t = just_one_integer.draw_template(random, pv)
        x = just_one_integer.reify(t)
        assert x == 1
    some_integers = integers(1, 10)
    for _ in hrange(100):
        pv = some_integers.draw_parameter(random)
        x = some_integers.draw_template(random, pv)
        assert 1 <= x <= 10
Beispiel #12
0
 def shift(self):
     """Attempt to shift individual byte values right as far as they can
     go."""
     prev = -1
     while prev != self.changes:
         prev = self.changes
         for i in hrange(self.size):
             block = bytearray(self.current)
             c = block[i]
             for k in hrange(c.bit_length(), 0, -1):
                 block[i] = c >> k
                 if self.incorporate(hbytes(block)):
                     break
Beispiel #13
0
    def run(self, state_machine, print_steps=None):
        if print_steps is None:
            print_steps = current_verbosity() >= Verbosity.debug

        try:
            for i in hrange(self.n_steps):
                strategy = state_machine.steps()

                template_set = False
                if i < len(self.record):
                    if self.record[i] is TOMBSTONE:
                        continue
                    _, data = self.record[i]
                    data = list(data)
                    for data_index in hrange(len(data) - 1, -1, -1):
                        try:
                            template = strategy.from_basic(data[data_index])
                            template_set = True
                            break
                        except BadData:
                            pass
                    if template_set:
                        data[data_index], data[-1] = (
                            data[-1], data[data_index]
                        )
                else:
                    data = []
                if not template_set:
                    parameter = strategy.draw_parameter(Random(
                        self.parameter_seed
                    ))
                    template = strategy.draw_template(
                        Random(self.templates[i]), parameter)
                    data.append(strategy.to_basic(template))

                new_record = (
                    strategy, data,
                )
                if i < len(self.record):
                    self.record[i] = new_record
                else:
                    self.record.append(new_record)

                strategy.from_basic(self.record[i][1][-1])
                value = strategy.reify(template)

                if print_steps:
                    state_machine.print_step(value)
                state_machine.execute_step(value)
        finally:
            state_machine.teardown()
def test_trivial_before_force_agrees_with_trivial_after():
    d = ConjectureData.for_buffer([0, 1, 1])
    d.draw_bits(1)
    d.draw_bits(1, forced=1)
    d.draw_bits(1)

    t1 = [d.blocks.trivial(i) for i in hrange(3)]
    d.freeze()
    r = d.as_result()
    t2 = [b.trivial for b in r.blocks]
    assert d.blocks.owner is None
    t3 = [r.blocks.trivial(i) for i in hrange(3)]

    assert t1 == t2 == t3
def minimal_basic():
    global __minimal_basic
    if __minimal_basic is None:
        random = Random('__minimal_templates_as_basic_data')
        __minimal_basic = []
        for typ in standard_types:
            strat = strategy(typ, Settings(average_list_length=2))
            for m in minimal_elements(strat, random):
                __minimal_basic.append(strat.to_basic(m))
        for i in hrange(10):
            __minimal_basic.append(list(hrange(i)))
            __minimal_basic.append([None] * i)
        __minimal_basic.append(None)
    return __minimal_basic
def minimal_basic():
    global __minimal_basic
    if __minimal_basic is None:
        random = Random(u'__minimal_templates_as_basic_data')
        __minimal_basic = []
        for typ in standard_types:
            strat = typ
            for m in minimal_elements(strat, random):
                __minimal_basic.append(strat.to_basic(m))
        for i in hrange(10):
            __minimal_basic.append(list(hrange(i)))
            __minimal_basic.append([None] * i)
        __minimal_basic.append(None)
    return __minimal_basic
Beispiel #17
0
    def run(self):
        if not any(self.current):
            return
        if self.incorporate(hbytes(self.size)):
            return
        change_counter = -1
        while self.current and change_counter < self.changes:
            change_counter = self.changes
            for c in hrange(max(self.current)):
                if self.incorporate(
                    hbytes(min(b, c) for b in self.current)
                ):
                    break

            for c in sorted(set(self.current), reverse=True):
                for d in hrange(c):
                    if self.incorporate(
                        hbytes(d if b == c else b for b in self.current)
                    ):
                        break

            for c in hrange(max(self.current)):
                k = len(self.current) // 2
                while k > 0:
                    i = 0
                    while i + k <= len(self.current):
                        self.incorporate(
                            self.current[:i] +
                            hbytes(min(b, c) for b in self.current[i:i + k]) +
                            self.current[i + k:]
                        )
                        i += k
                    k //= 2

            if change_counter != self.changes or self.cautious:
                continue

            for i in hrange(self.size):
                t = self.current[i]
                if t > 0:
                    ss = small_shrinks[self.current[i]]
                    for c in ss:
                        if self._shrink_index(i, c):
                            for c in hrange(self.current[i]):
                                if c in ss:
                                    continue
                                if self._shrink_index(i, c):
                                    break
                            break
def _test_matching_pattern(pattern, isvalidchar, is_unicode=False):
    r = unicode_regex(pattern) if is_unicode else ascii_regex(pattern)

    codepoints = hrange(0, sys.maxunicode + 1) if is_unicode else hrange(1, 128)
    for c in [hunichr(x) for x in codepoints]:
        if isvalidchar(c):
            assert r.search(c), (
                '"%s" supposed to match "%s" (%r, category "%s"), '
                "but it doesn't" % (pattern, c, c, unicodedata.category(c))
            )
        else:
            assert not r.search(c), (
                '"%s" supposed not to match "%s" (%r, category "%s"), '
                "but it does" % (pattern, c, c, unicodedata.category(c))
            )
def length_of_longest_ordered_sequence(xs):
    if not xs:
        return 0
    # FIXME: Needlessly O(n^2) algorithm, but it's a test so eh.
    lengths = [-1] * len(xs)
    lengths[-1] = 1
    for i in hrange(len(xs) - 2, -1, -1):
        assert lengths[i] == -1
        for j in hrange(i + 1, len(xs)):
            assert lengths[j] >= 1
            if xs[j] > xs[i]:
                lengths[i] = max(lengths[i], lengths[j] + 1)
        if lengths[i] < 0:
            lengths[i] = 1
    assert all(t >= 1 for t in lengths)
    return max(lengths)
Beispiel #20
0
 def basic_simplify(self, random, value):
     if value == self.lower_bound:
         return
     lb = self.lower_bound
     for _ in hrange(32):
         yield lb
         lb = (lb + value) * 0.5
def test_exhaustive_enumeration(prefix, bits, seed):
    seen = set()

    def f(data):
        if prefix:
            data.write(hbytes(prefix))
            assert len(data.buffer) == len(prefix)
        k = data.draw_bits(bits)
        assert k not in seen
        seen.add(k)

    size = 2 ** bits

    seen_prefixes = set()

    runner = ConjectureRunner(
        f, settings=settings(database=None, max_examples=size),
        random=Random(seed),
    )
    with pytest.raises(RunIsComplete):
        runner.cached_test_function(b'')
        for _ in hrange(size):
            p = runner.generate_novel_prefix()
            assert p not in seen_prefixes
            seen_prefixes.add(p)
            data = ConjectureData.for_buffer(
                hbytes(p + hbytes(2 + len(prefix))))
            runner.test_function(data)
            assert data.status == Status.VALID
            node = 0
            for b in data.buffer:
                node = runner.tree[node][b]
            assert node in runner.dead
    assert len(seen) == size
Beispiel #22
0
def defines_strategy(strategy_definition):
    from hypothesis.internal.reflection import proxies, arg_string, \
        convert_positional_arguments
    argspec = getargspec(strategy_definition)
    defaults = {}
    if argspec.defaults is not None:
        for k in hrange(1, len(argspec.defaults) + 1):
            defaults[argspec.args[-k]] = argspec.defaults[-k]

    @proxies(strategy_definition)
    def accept(*args, **kwargs):
        result = strategy_definition(*args, **kwargs)

        def calc_repr():
            _args = args
            _kwargs = kwargs
            _args, _kwargs = convert_positional_arguments(
                strategy_definition, _args, _kwargs)
            kwargs_for_repr = dict(_kwargs)
            for k, v in defaults.items():
                if k in kwargs_for_repr and kwargs_for_repr[k] is defaults[k]:
                    del kwargs_for_repr[k]
            return u'%s(%s)' % (
                strategy_definition.__name__,
                arg_string(strategy_definition, _args, kwargs_for_repr)
            )
        return ReprWrapperStrategy(result, calc_repr)
    return accept
def test_clears_out_its_database_on_shrinking(
    initial_attempt, skip_target, monkeypatch
):
    def generate_new_examples(self):
        self.test_function(
            ConjectureData.for_buffer(hbytes([initial_attempt])))

    monkeypatch.setattr(
        ConjectureRunner, 'generate_new_examples', generate_new_examples)

    key = b'key'
    db = InMemoryExampleDatabase()

    def f(data):
        if data.draw_bits(8) >= 127:
            data.mark_interesting()

    runner = ConjectureRunner(
        f, settings=settings(database=db, max_examples=256), database_key=key,
        random=Random(0),
    )

    for n in hrange(256):
        if n != 127 or not skip_target:
            db.save(runner.secondary_key, hbytes([n]))
    runner.run()
    assert len(runner.interesting_examples) == 1
    for b in db.fetch(runner.secondary_key):
        assert b[0] >= 127
    assert len(list(db.fetch(runner.database_key))) == 1
Beispiel #24
0
 def draw_parameter(self, random):
     locales = dist.non_empty_subset(random, self.locales)
     n = 1 + geometric(random, 0.1)
     return [
         self.gen_example(random, locales)
         for _ in hrange(n)
     ]
Beispiel #25
0
    def simplifiers(self, random, template):
        i, value = template
        for j in hrange(i):
            yield self.redraw_simplifier(j)

        for simplify in self.element_strategies[i].simplifiers(random, value):
            yield self.element_simplifier(i, simplify)
Beispiel #26
0
 def __repr__(self):
     if self.__representation is None:
         _args = self.__args
         _kwargs = self.__kwargs
         argspec = getargspec(self.__function)
         defaults = {}
         if argspec.defaults is not None:
             for k in hrange(1, len(argspec.defaults) + 1):
                 defaults[argspec.args[-k]] = argspec.defaults[-k]
         if len(argspec.args) > 1 or argspec.defaults:
             _args, _kwargs = convert_positional_arguments(
                 self.__function, _args, _kwargs)
         else:
             _args, _kwargs = convert_keyword_arguments(
                 self.__function, _args, _kwargs)
         kwargs_for_repr = dict(_kwargs)
         for k, v in defaults.items():
             if k in kwargs_for_repr and kwargs_for_repr[k] is defaults[k]:
                 del kwargs_for_repr[k]
         self.__representation = '%s(%s)' % (
             self.__function.__name__,
             arg_string(
                 self.__function, _args, kwargs_for_repr, reorder=False),
         )
     return self.__representation
 def simplifiers(self, template):
     if not template:
         return
     for i in hrange(len(self.element_strategies)):
         strat = self.element_strategies[i]
         for simplifier in strat.simplifiers(template[i]):
             yield self.simplifier_for_index(i, simplifier)
Beispiel #28
0
 def cut_steps(self, random, template):
     if len(template.record) < template.n_steps:
         yield StateMachineRunner(
             parameter_seed=template.parameter_seed,
             template_seed=template.template_seed,
             templates=template.templates,
             n_steps=len(template.record),
             record=template.record,
         )
     mid = 0
     while True:
         next_mid = (template.n_steps + mid) // 2
         if next_mid == mid:
             break
         mid = next_mid
         yield StateMachineRunner(
             parameter_seed=template.parameter_seed,
             template_seed=template.template_seed,
             templates=template.templates,
             n_steps=mid,
             record=template.record,
         )
         new_record = list(template.record)
         for i in hrange(min(mid, len(new_record))):
             new_record[i] = TOMBSTONE
         yield StateMachineRunner(
             parameter_seed=template.parameter_seed,
             template_seed=template.template_seed,
             templates=template.templates,
             n_steps=template.n_steps,
             record=new_record,
         )
Beispiel #29
0
    def example(self):
        """Provide an example of the sort of value that this strategy
        generates. This is biased to be slightly simpler than is typical for
        values from this strategy, for clarity purposes.

        This method shouldn't be taken too seriously. It's here for interactive
        exploration of the API, not for any sort of real testing.

        This method is part of the public API.

        """
        random = Random()

        parts = []

        for _ in hrange(20):
            if len(parts) >= 3:
                break
            try:
                template = self.draw_and_produce(random)
                reified = self.reify(template)
                parts.append((template, reified))
            except UnsatisfiedAssumption:
                pass
        if not parts:
            raise NoExamples("Could not find any valid examples in 20 tries")

        return min(parts, key=lambda tr: self.__template_size(tr[0]))[1]
Beispiel #30
0
def test_streaming_flatmap_past_point_of_read():
    s = find(
        streaming(integers().flatmap(lambda n: integers(min_value=n))),
        lambda x: x[0])
    assert s[0] == 1
    for i in hrange(100):
        s[i]
 def x(data):
     s = [data.draw_bits(8) for _ in hrange(6)]
     if any(s):
         data.mark_interesting()
Beispiel #32
0
 def simplifiers(self, random, template):
     for i in hrange(len(template.stream.fetched)):
         for s in self.source_strategy.simplifiers(random,
                                                   template.stream[i]):
             yield self.simplifier_for_index(s, i)
Beispiel #33
0
    def _new_mutator(self):
        target_data = [None]

        def draw_new(data, n):
            return uniform(self.random, n)

        def draw_existing(data, n):
            return target_data[0].buffer[data.index:data.index + n]

        def draw_smaller(data, n):
            existing = target_data[0].buffer[data.index:data.index + n]
            r = uniform(self.random, n)
            if r <= existing:
                return r
            return _draw_predecessor(self.random, existing)

        def draw_larger(data, n):
            existing = target_data[0].buffer[data.index:data.index + n]
            r = uniform(self.random, n)
            if r >= existing:
                return r
            return _draw_successor(self.random, existing)

        def reuse_existing(data, n):
            choices = data.block_starts.get(n, [])
            if choices:
                i = self.random.choice(choices)
                assert i + n <= len(data.buffer)
                return hbytes(data.buffer[i:i + n])
            else:
                result = uniform(self.random, n)
                assert isinstance(result, hbytes)
                return result

        def flip_bit(data, n):
            buf = bytearray(target_data[0].buffer[data.index:data.index + n])
            i = self.random.randint(0, n - 1)
            k = self.random.randint(0, 7)
            buf[i] ^= 1 << k
            return hbytes(buf)

        def draw_zero(data, n):
            return hbytes(b"\0" * n)

        def draw_max(data, n):
            return hbytes([255]) * n

        def draw_constant(data, n):
            return hbytes([self.random.randint(0, 255)]) * n

        def redraw_last(data, n):
            u = target_data[0].blocks[-1].start
            if data.index + n <= u:
                return target_data[0].buffer[data.index:data.index + n]
            else:
                return uniform(self.random, n)

        options = [
            draw_new,
            redraw_last,
            redraw_last,
            reuse_existing,
            reuse_existing,
            draw_existing,
            draw_smaller,
            draw_larger,
            flip_bit,
            draw_zero,
            draw_max,
            draw_zero,
            draw_max,
            draw_constant,
        ]

        bits = [self.random.choice(options) for _ in hrange(3)]

        prefix = [None]

        def mutate_from(origin):
            target_data[0] = origin
            prefix[0] = self.generate_novel_prefix()
            return draw_mutated

        def draw_mutated(data, n):
            if data.index + n > len(target_data[0].buffer):
                result = uniform(self.random, n)
            else:
                draw = self.random.choice(bits)
                result = draw(data, n)
            p = prefix[0]
            if data.index < len(p):
                start = p[data.index:data.index + n]
                result = start + result[len(start):]
            assert len(result) == n
            return self.__zero_bound(data, result)

        return mutate_from
Beispiel #34
0
    def __init__(self, weights):

        n = len(weights)

        self.table = [[i, None, None] for i in hrange(n)]

        total = sum(weights)

        num_type = type(total)

        zero = num_type(0)
        one = num_type(1)

        small = []
        large = []

        probabilities = [w / total for w in weights]
        scaled_probabilities = []

        for i, p in enumerate(probabilities):
            scaled = p * n
            scaled_probabilities.append(scaled)
            if scaled == 1:
                self.table[i][2] = zero
            elif scaled < 1:
                small.append(i)
            else:
                large.append(i)
        heapq.heapify(small)
        heapq.heapify(large)

        while small and large:
            lo = heapq.heappop(small)
            hi = heapq.heappop(large)

            assert lo != hi
            assert scaled_probabilities[hi] > one
            assert self.table[lo][1] is None
            self.table[lo][1] = hi
            self.table[lo][2] = one - scaled_probabilities[lo]
            scaled_probabilities[hi] = (
                scaled_probabilities[hi] + scaled_probabilities[lo]
            ) - one

            if scaled_probabilities[hi] < 1:
                heapq.heappush(small, hi)
            elif scaled_probabilities[hi] == 1:
                self.table[hi][2] = zero
            else:
                heapq.heappush(large, hi)
        while large:
            self.table[large.pop()][2] = zero
        while small:
            self.table[small.pop()][2] = zero

        for entry in self.table:
            assert entry[2] is not None
            if entry[1] is None:
                entry[1] = entry[0]
            elif entry[1] < entry[0]:
                entry[0], entry[1] = entry[1], entry[0]
                entry[2] = one - entry[2]
        self.table.sort()
Beispiel #35
0
    def do_draw(self, data):
        if 0 in self.shape:
            return np.zeros(dtype=self.dtype, shape=self.shape)

        # Reset this flag for each test case to emit warnings from set_element
        self._report_overflow = True

        # This could legitimately be a np.empty, but the performance gains for
        # that would be so marginal that there's really not much point risking
        # undefined behaviour shenanigans.
        result = np.zeros(shape=self.array_size, dtype=self.dtype)

        if self.fill.is_empty:
            # We have no fill value (either because the user explicitly
            # disabled it or because the default behaviour was used and our
            # elements strategy does not produce reusable values), so we must
            # generate a fully dense array with a freshly drawn value for each
            # entry.
            if self.unique:
                seen = set()
                elements = cu.many(data,
                                   min_size=self.array_size,
                                   max_size=self.array_size,
                                   average_size=self.array_size)
                i = 0
                while elements.more():
                    # We assign first because this means we check for
                    # uniqueness after numpy has converted it to the relevant
                    # type for us. Because we don't increment the counter on
                    # a duplicate we will overwrite it on the next draw.
                    self.set_element(data, result, i)
                    if result[i] not in seen:
                        seen.add(result[i])
                        i += 1
                    else:
                        elements.reject()
            else:
                for i in hrange(len(result)):
                    self.set_element(data, result, i)
        else:
            # We draw numpy arrays as "sparse with an offset". We draw a
            # collection of index assignments within the array and assign
            # fresh values from our elements strategy to those indices. If at
            # the end we have not assigned every element then we draw a single
            # value from our fill strategy and use that to populate the
            # remaining positions with that strategy.

            elements = cu.many(
                data,
                min_size=0,
                max_size=self.array_size,
                # sqrt isn't chosen for any particularly principled reason. It
                # just grows reasonably quickly but sublinearly, and for small
                # arrays it represents a decent fraction of the array size.
                average_size=math.sqrt(self.array_size),
            )

            needs_fill = np.full(self.array_size, True)
            seen = set()

            while elements.more():
                i = cu.integer_range(data, 0, self.array_size - 1)
                if not needs_fill[i]:
                    elements.reject()
                    continue
                self.set_element(data, result, i)
                if self.unique:
                    if result[i] in seen:
                        elements.reject()
                        continue
                    else:
                        seen.add(result[i])
                needs_fill[i] = False
            if needs_fill.any():
                # We didn't fill all of the indices in the early loop, so we
                # put a fill value into the rest.

                # We have to do this hilarious little song and dance to work
                # around numpy's special handling of iterable values. If the
                # value here were e.g. a tuple then neither array creation
                # nor putmask would do the right thing. But by creating an
                # array of size one and then assigning the fill value as a
                # single element, we both get an array with the right value in
                # it and putmask will do the right thing by repeating the
                # values of the array across the mask.
                one_element = np.zeros(shape=1, dtype=self.dtype)
                self.set_element(data, one_element, 0, self.fill)
                fill_value = one_element[0]
                if self.unique:
                    try:
                        is_nan = np.isnan(fill_value)
                    except TypeError:
                        is_nan = False

                    if not is_nan:
                        raise InvalidArgument(
                            'Cannot fill unique array with non-NaN '
                            'value %r' % (fill_value, ))

                np.putmask(result, needs_fill, one_element)

        return result.reshape(self.shape)
Beispiel #36
0
    extra_information = attr.ib()
    has_discards = attr.ib()
    target_observations = attr.ib()
    forced_indices = attr.ib(repr=False)
    examples = attr.ib(repr=False)

    index = attr.ib(init=False)

    def __attrs_post_init__(self):
        self.index = len(self.buffer)
        self.forced_indices = frozenset(self.forced_indices)


# Masks for masking off the first byte of an n-bit buffer.
# The appropriate mask is stored at position n % 8.
BYTE_MASKS = [(1 << n) - 1 for n in hrange(8)]
BYTE_MASKS[0] = 255


class ConjectureData(object):
    @classmethod
    def for_buffer(self, buffer, observer=None):
        buffer = hbytes(buffer)
        return ConjectureData(
            max_length=len(buffer),
            draw_bytes=lambda data, n: hbytes(buffer[data.index:data.index + n]
                                              ),
            observer=observer,
        )

    def __init__(self, max_length, draw_bytes, observer=None):
Beispiel #37
0
def test_can_reduce_poison_from_any_subtree(size, seed):
    """This test validates that we can minimize to any leaf node of a binary
    tree, regardless of where in the tree the leaf is."""
    random = Random(seed)

    # Initially we create the minimal tree of size n, regardless of whether it
    # is poisoned (which it won't be - the poison event essentially never
    # happens when drawing uniformly at random).

    # Choose p so that the expected size of the tree is equal to the desired
    # size.
    p = 1.0 / (2.0 - 1.0 / size)
    strat = PoisonedTree(p)

    def test_function(data):
        v = data.draw(strat)
        if len(v) >= size:
            data.mark_interesting()

    runner = ConjectureRunner(
        test_function, random=random, settings=TEST_SETTINGS
    )

    while not runner.interesting_examples:
        runner.test_function(ConjectureData(
            draw_bytes=lambda data, n: uniform(random, n),
            max_length=LOTS))

    runner.shrink_interesting_examples()

    data, = runner.interesting_examples.values()

    assert len(ConjectureData.for_buffer(data.buffer).draw(strat)) == size

    starts = data.block_starts[2]
    assert len(starts) % 2 == 0

    for i in hrange(0, len(starts), 2):
        # Now for each leaf position in the tree we try inserting a poison
        # value artificially. Additionally, we add a marker to the end that
        # must be preserved. The marker means that we are not allow to rely on
        # discarding the end of the buffer to get the desired shrink.
        u = starts[i]
        marker = hbytes([1, 2, 3, 4])

        poisoned_data = ConjectureData.for_buffer(
            data.buffer[:u] + hbytes([255]) * 4 + data.buffer[u + 4:] +
            marker
        )

        def test_function(data):
            v = data.draw(strat)
            m = data.draw_bytes(len(marker))
            if POISON in v and m == marker:
                data.mark_interesting()
        runner = ConjectureRunner(
            test_function, random=random, settings=TEST_SETTINGS)

        runner.test_function(poisoned_data)
        assert runner.interesting_examples
        runner.shrink_interesting_examples()

        shrunk, = runner.interesting_examples.values()

        assert ConjectureData.for_buffer(
            shrunk.buffer).draw(strat) == (POISON,)
Beispiel #38
0
 def __iter__(self):
     for i in hrange(len(self)):
         yield self[i]
Beispiel #39
0
    def generate_new_examples(self):
        if Phase.generate not in self.settings.phases:
            return

        zero_data = self.cached_test_function(hbytes(
            self.settings.buffer_size))
        if zero_data.status > Status.OVERRUN:
            self.__data_cache.pin(zero_data.buffer)

        if zero_data.status == Status.OVERRUN or (
                zero_data.status == Status.VALID
                and len(zero_data.buffer) * 2 > self.settings.buffer_size):
            fail_health_check(
                self.settings,
                "The smallest natural example for your test is extremely "
                "large. This makes it difficult for Hypothesis to generate "
                "good examples, especially when trying to reduce failing ones "
                "at the end. Consider reducing the size of your data if it is "
                "of a fixed size. You could also fix this by improving how "
                "your data shrinks (see https://hypothesis.readthedocs.io/en/"
                "latest/data.html#shrinking for details), or by introducing "
                "default values inside your strategy. e.g. could you replace "
                "some arguments with their defaults by using "
                "one_of(none(), some_complex_strategy)?",
                HealthCheck.large_base_example,
            )

        if zero_data is not Overrun:
            # If the language starts with writes of length >= cap then there is
            # only one string in it: Everything after cap is forced to be zero (or
            # to be whatever value is written there). That means that once we've
            # tried the zero value, there's nothing left for us to do, so we
            # exit early here.
            for i in hrange(self.cap):
                if i not in zero_data.forced_indices:
                    break
            else:
                self.exit_with(ExitReason.finished)

        self.health_check_state = HealthCheckState()

        count = 0
        while not self.interesting_examples and (
                count < 10 or self.health_check_state is not None):
            prefix = self.generate_novel_prefix()

            def draw_bytes(data, n):
                if data.index < len(prefix):
                    result = prefix[data.index:data.index + n]
                    if len(result) < n:
                        result += uniform(self.random, n - len(result))
                else:
                    result = uniform(self.random, n)
                return self.__zero_bound(data, result)

            targets_found = len(self.covering_examples)

            last_data = ConjectureData(max_length=self.settings.buffer_size,
                                       draw_bytes=draw_bytes)
            self.test_function(last_data)
            last_data.freeze()

            count += 1

        mutations = 0
        mutator = self._new_mutator()

        zero_bound_queue = []

        while not self.interesting_examples:
            if zero_bound_queue:
                # Whenever we generated an example and it hits a bound
                # which forces zero blocks into it, this creates a weird
                # distortion effect by making certain parts of the data
                # stream (especially ones to the right) much more likely
                # to be zero. We fix this by redistributing the generated
                # data by shuffling it randomly. This results in the
                # zero data being spread evenly throughout the buffer.
                # Hopefully the shrinking this causes will cause us to
                # naturally fail to hit the bound.
                # If it doesn't then we will queue the new version up again
                # (now with more zeros) and try again.
                overdrawn = zero_bound_queue.pop()
                buffer = bytearray(overdrawn.buffer)

                # These will have values written to them that are different
                # from what's in them anyway, so the value there doesn't
                # really "count" for distributional purposes, and if we
                # leave them in then they can cause the fraction of non
                # zero bytes to increase on redraw instead of decrease.
                for i in overdrawn.forced_indices:
                    buffer[i] = 0

                self.random.shuffle(buffer)
                buffer = hbytes(buffer)

                def draw_bytes(data, n):
                    result = buffer[data.index:data.index + n]
                    if len(result) < n:
                        result += hbytes(n - len(result))
                    return self.__rewrite(data, result)

                data = ConjectureData(draw_bytes=draw_bytes,
                                      max_length=self.settings.buffer_size)
                self.test_function(data)
                data.freeze()
            else:
                origin = self.target_selector.select()
                mutations += 1
                targets_found = len(self.covering_examples)
                data = ConjectureData(draw_bytes=mutator(origin),
                                      max_length=self.settings.buffer_size)
                self.test_function(data)
                data.freeze()
                if (data.status > origin.status
                        or len(self.covering_examples) > targets_found):
                    mutations = 0
                elif data.status < origin.status or mutations >= 10:
                    # Cap the variations of a single example and move on to
                    # an entirely fresh start.  Ten is an entirely arbitrary
                    # constant, but it's been working well for years.
                    mutations = 0
                    mutator = self._new_mutator()
            if getattr(data, "hit_zero_bound", False):
                zero_bound_queue.append(data)
            mutations += 1
def test_minimize_one_of():
    for _ in hrange(100):
        assert minimal(integers() | text() | booleans()) in (0, u'', False)
 def x(data):
     for _ in hrange(10):
         data.draw_bytes(1)
     data.mark_interesting()
 def data(data):
     n = data.draw_bits(4)
     b = [data.draw_bits(8) for _ in hrange(n)]
     if sum(b) >= target:
         data.mark_interesting()
 def shrinker(data):
     n = data.draw_bits(4)
     b = [data.draw_bits(8) for _ in hrange(n)]
     if any(b):
         data.mark_interesting()
Beispiel #44
0
 def draw_parameter(self, random):
     n = 1 + dist.geometric(random, 0.01)
     results = []
     for _ in hrange(n):
         results.append(random.randint(self.start, self.end))
     return results
 def shrinker(data):
     for _ in hrange(10):
         data.draw_bits(1)
         data.draw_bits(16)
     if data.draw_bits(1):
         data.mark_interesting()
Beispiel #46
0
MAX_POSITIVE_EXPONENT = MAX_EXPONENT - 1 - BIAS

DRAW_FLOAT_LABEL = calc_label_from_name("drawing a float")


def exponent_key(e):
    if e == MAX_EXPONENT:
        return float("inf")
    unbiased = e - BIAS
    if unbiased < 0:
        return 10000 - unbiased
    else:
        return unbiased


ENCODING_TABLE = array("H", sorted(hrange(MAX_EXPONENT + 1), key=exponent_key))
DECODING_TABLE = array("H", [0]) * len(ENCODING_TABLE)

for i, b in enumerate(ENCODING_TABLE):
    DECODING_TABLE[b] = i

del i, b


def decode_exponent(e):
    """Take draw_bits(11) and turn it into a suitable floating point exponent
    such that lexicographically simpler leads to simpler floats."""
    assert 0 <= e <= MAX_EXPONENT
    return ENCODING_TABLE[e]

Beispiel #47
0
    def _new_mutator(self):
        def draw_new(data, n, distribution):
            return distribution(self.random, n)

        def draw_existing(data, n, distribution):
            return self.last_data.buffer[data.index:data.index + n]

        def draw_smaller(data, n, distribution):
            existing = self.last_data.buffer[data.index:data.index + n]
            r = distribution(self.random, n)
            if r <= existing:
                return r
            return _draw_predecessor(self.random, existing)

        def draw_larger(data, n, distribution):
            existing = self.last_data.buffer[data.index:data.index + n]
            r = distribution(self.random, n)
            if r >= existing:
                return r
            return _draw_successor(self.random, existing)

        def reuse_existing(data, n, distribution):
            choices = data.block_starts.get(n, []) or \
                self.last_data.block_starts.get(n, [])
            if choices:
                i = self.random.choice(choices)
                return self.last_data.buffer[i:i + n]
            else:
                return distribution(self.random, n)

        def flip_bit(data, n, distribution):
            buf = bytearray(self.last_data.buffer[data.index:data.index + n])
            i = self.random.randint(0, n - 1)
            k = self.random.randint(0, 7)
            buf[i] ^= (1 << k)
            return hbytes(buf)

        def draw_zero(data, n, distribution):
            return b'\0' * n

        def draw_constant(data, n, distribution):
            return bytes_from_list([self.random.randint(0, 255)] * n)

        options = [
            draw_new,
            reuse_existing,
            reuse_existing,
            draw_existing,
            draw_smaller,
            draw_larger,
            flip_bit,
            draw_zero,
            draw_constant,
        ]

        bits = [self.random.choice(options) for _ in hrange(3)]

        def draw_mutated(data, n, distribution):
            if (data.index + n > len(self.last_data.buffer)):
                return distribution(self.random, n)
            return self.random.choice(bits)(data, n, distribution)

        return draw_mutated
Beispiel #48
0

def abc(x, y, z):
    return builds(ABC, x, y, z)

with Settings(average_list_length=10.0):
    standard_types = [
        basic(Bitfields),
        EvalledIntStream,
        lists(max_size=0), tuples(), sets(max_size=0), frozensets(max_size=0),
        fixed_dictionaries({}),
        n_ary_tree(booleans(), booleans(), booleans()),
        n_ary_tree(integers(), integers(), integers()),
        abc(booleans(), booleans(), booleans()),
        abc(booleans(), booleans(), integers()),
        templates_for(one_of(*map(just, hrange(10)))),
        fixed_dictionaries({'a': integers(), 'b': booleans()}),
        dictionaries(booleans(), integers()),
        dictionaries(text(), booleans()),
        one_of(integers(), tuples(booleans())),
        sampled_from(range(10)),
        one_of(just('a'), just('b'), just('c')),
        sampled_from(('a', 'b', 'c')),
        integers(),
        integers(min_value=3),
        integers(min_value=(-2 ** 32), max_value=(2 ** 64)),
        floats(), floats(min_value=-2.0, max_value=3.0),
        floats(min_value=3.14, max_value=3.14),
        text(), binary(),
        booleans(),
        tuples(booleans(), booleans()),
def test_can_mutate_large_int():
    r = Random(0)
    for _ in hrange(20):
        mutate_basic(1 << 1024, r)
Beispiel #50
0
        def assign_rows(draw):
            index = draw(index_strategy)

            result = pandas.DataFrame(
                OrderedDict(
                    (
                        c.name,
                        pandas.Series(
                            np.zeros(dtype=c.dtype, shape=len(index)), dtype=c.dtype
                        ),
                    )
                    for c in rewritten_columns
                ),
                index=index,
            )

            fills = {}

            any_unique = any(c.unique for c in rewritten_columns)

            if any_unique:
                all_seen = [set() if c.unique else None for c in rewritten_columns]
                while all_seen[-1] is None:
                    all_seen.pop()

            for row_index in hrange(len(index)):
                for _ in hrange(5):
                    original_row = draw(rows)
                    row = original_row
                    if isinstance(row, dict):
                        as_list = [None] * len(rewritten_columns)
                        for i, c in enumerate(rewritten_columns):
                            try:
                                as_list[i] = row[c.name]
                            except KeyError:
                                try:
                                    as_list[i] = fills[i]
                                except KeyError:
                                    fills[i] = draw(c.fill)
                                    as_list[i] = fills[i]
                        for k in row:
                            if k not in column_names:
                                raise InvalidArgument(
                                    "Row %r contains column %r not in columns %r)"
                                    % (row, k, [c.name for c in rewritten_columns])
                                )
                        row = as_list
                    if any_unique:
                        has_duplicate = False
                        for seen, value in zip(all_seen, row):
                            if seen is None:
                                continue
                            if value in seen:
                                has_duplicate = True
                                break
                            seen.add(value)
                        if has_duplicate:
                            continue
                    row = list(try_convert(tuple, row, "draw(rows)"))

                    if len(row) > len(rewritten_columns):
                        raise InvalidArgument(
                            (
                                "Row %r contains too many entries. Has %d but "
                                "expected at most %d"
                            )
                            % (original_row, len(row), len(rewritten_columns))
                        )
                    while len(row) < len(rewritten_columns):
                        row.append(draw(rewritten_columns[len(row)].fill))
                    result.iloc[row_index] = row
                    break
                else:
                    reject()
            return result
Beispiel #51
0
def test_one_of_strategy_goes_infinite():
    x = integers(0, 2**32 - 2)
    assert not math.isinf(x.template_upper_bound)
    for _ in hrange(10):
        x |= x
    assert math.isinf(x.template_upper_bound)
def test_can_find_each_month():
    for month in hrange(1, 13):
        dates().filter(lambda x: x.month == month).example()
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
Beispiel #54
0
import base64

import pytest

from hypothesis import given, settings
from hypothesis.database import ExampleDatabase, SQLiteExampleDatabase, \
    InMemoryExampleDatabase, DirectoryBasedExampleDatabase
from hypothesis.strategies import lists, binary, tuples
from hypothesis.internal.compat import PY26, hrange

small_settings = settings(max_examples=100, timeout=4)

if PY26:
    # Workaround for bug with embedded null characters in a text string under
    # python 2.6
    alphabet = [chr(i) for i in hrange(1, 128)]
else:
    alphabet = None


@given(lists(tuples(binary(), binary())))
@small_settings
def test_backend_returns_what_you_put_in(xs):
    backend = SQLiteExampleDatabase(':memory:')
    mapping = {}
    for key, value in xs:
        mapping.setdefault(key, set()).add(value)
        backend.save(key, value)
    for key, values in mapping.items():
        backend_contents = list(backend.fetch(key))
        distinct_backend_contents = set(backend_contents)
Beispiel #55
0
    def run_step(self):
        # Try to delete as many elements as possible from the sequence, trying
        # each element no more than once.

        # We convert the sequence to a set of indices. This allows us to more
        # easily do book-keeping around which elements we've tried removing.
        initial = self.current

        indices = list(hrange(len(self.current)))

        # The set of indices that we have not yet removed (either because
        # we have not yet tried to remove them or because we tried and
        # failed).
        current_subset = set(indices)

        # The set of indices in current_subset that we have not yet tried
        # to remove.
        candidates_for_removal = set(current_subset)

        def consider_set(keep):
            """Try replacing current_subset with current_subset & keep."""

            keep = keep & current_subset
            to_remove = current_subset - keep

            # Once we've tried and failed to delete an element we never
            # attempt to delete it again in the current pass. This can cause
            # us to skip shrinks that would work, but that doesn't matter -
            # if this pass succeeded then it will run again at some point,
            # so those will be picked up later.
            if not to_remove.issubset(candidates_for_removal):
                return False
            if self.consider([v for i, v in enumerate(initial) if i in keep]):
                current_subset.intersection_update(keep)
                return True
            return False

        # We iterate over the indices in random order. This is because deletions
        # towards the end are more likely to work, while deletions from the
        # beginning are more likely to have higher impact. In addition there
        # tend to be large "dead" regions where nothing can be deleted, and
        # by proceeding in random order we don't have long gaps in those where
        # we make no progress.
        #
        # Note that this may be strictly more expensive than iterating from
        # left to right or right to left. The cost of find_integer, say f, is
        # convex. When deleting n elements starting from the left we pay f(n)
        # invocations, but when starting from the middle we pay 2 f(n / 2)
        # > f(n) invocations. In this case we are prioritising making progress
        # over a possibly strictly lower cost for two reasons: Firstly, when
        # n is small we just do linear scans anyway so this doesn't actually
        # matter, and secondly because successfuly deletions will tend to
        # speed up the test function and thus even when we make more test
        # function calls we may still win on time.
        #
        # It's also very frustrating watching the shrinker repeatedly fail
        # to delete, so there's a psychological benefit to prioritising
        # progress over cost.
        self.random.shuffle(indices)
        for i in indices:
            candidates_for_removal &= current_subset
            if not candidates_for_removal:
                break

            # We have already processed this index, either because it was bulk
            # removed or is the end point of a set that was.
            if i not in candidates_for_removal:
                continue

            # Note that we do not update candidates_for_removal until we've
            # actually tried removing them. This is because our consider_set
            # predicate checks whether we've previously tried deleting them,
            # so removing them here would result in wrong checks!

            # We now try to delete a region around i. We start by trying to
            # delete a region starting with i, i.e. [i, j) for some j > i.
            to_right = find_integer(
                lambda n: i + n <= len(initial)
                and consider_set(current_subset - set(hrange(i, i + n)))
            )

            # If that succeeded we're in a deletable region. It's unlikely that
            # we happened to pick the starting index of that region, so we try
            # to extend it to the left too.
            if to_right > 0:
                to_left = find_integer(
                    lambda n: i - n >= 0
                    and consider_set(current_subset - set(hrange(i - n, i)))
                )

                # find_integer always tries at least n + 1 when it returns n.
                # This means that we've tried deleting i - (to_left + 1) and
                # failed to do so, so we can remove it from our candidates for
                # deletion.
                candidates_for_removal.discard(i - to_left - 1)

            # We've now tried deleting i so remove it.
            candidates_for_removal.discard(i)

            # As per comment above we've also tried deleting one past the end
            # of the region so we remove that from the candidate set too.
            candidates_for_removal.discard(i + to_right)
Beispiel #56
0
 def basic_simplify(self, random, template):
     for i in hrange(0, template):
         yield i
 def pack(self, length):
     return [
         self.new_element() for _ in hrange(length)
     ]
Beispiel #58
0
def test_small_hrange():
    assert list(hrange(5)) == [0, 1, 2, 3, 4]
    assert list(hrange(3, 5)) == [3, 4]
    assert list(hrange(1, 10, 2)) == [1, 3, 5, 7, 9]
Beispiel #59
0
 def short_circuit(self):
     for i in hrange(3):
         if self.consider(i):
             return True
     return False
Beispiel #60
0
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 arg_strings:
        if_confused = "lambda %s: <unknown>" % (", ".join(arg_strings),)
    else:
        if_confused = "lambda: <unknown>"
    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
    # TypeError again because we set detect_encoding to None above.
    #
    try:
        with open(inspect.getsourcefile(f), "rb") as src_f:
            encoding, _ = detect_encoding(src_f.readline)

        source_bytes = source.encode(encoding)
        source_bytes = source_bytes[lambda_ast.col_offset :].strip()
        source = source_bytes.decode(encoding)
    except (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