class IntListRules(RuleBasedStateMachine): @initialize(ls=st.lists(INTEGERS)) def starting_lists(self, ls): self.model = list(ls) self.target = IntList(ls) @invariant() def lists_are_equivalent(self): if hasattr(self, "model"): assert isinstance(self.model, list) assert isinstance(self.target, IntList) assert len(self.model) == len(self.target) assert list(self.target) == self.model @rule(n=INTEGERS) def append(self, n): self.model.append(n) self.target.append(n) @rule(i=valid_index() | valid_slice()) def delete(self, i): del self.model[i] del self.target[i] @rule(sl=valid_slice()) def slice(self, sl): self.model = self.model[sl] self.target = self.target[sl] @rule(i=valid_index()) def agree_on_values(self, i): assert self.model[i] == self.target[i]
def test_int_list_equality(): ls = [1, 2, 3] x = IntList(ls) y = IntList(ls) assert ls != x assert x != ls assert not (x == ls) assert x == x assert x == y
class ExampleRecord(object): """Records the series of ``start_example``, ``stop_example``, and ``draw_bits`` calls so that these may be stored in ``Examples`` and replayed when we need to know about the structure of individual ``Example`` objects. Note that there is significant similarity between this class and ``DataObserver``, and the plan is to eventually unify them, but they currently have slightly different functions and implementations. """ def __init__(self): self.labels = [DRAW_BYTES_LABEL] self.__index_of_labels = {DRAW_BYTES_LABEL: 0} self.trail = IntList() def freeze(self): self.__index_of_labels = None def start_example(self, label): try: i = self.__index_of_labels[label] except KeyError: i = self.__index_of_labels.setdefault(label, len(self.labels)) self.labels.append(label) self.trail.append(START_EXAMPLE_RECORD + i) def stop_example(self, discard): if discard: self.trail.append(STOP_EXAMPLE_DISCARD_RECORD) else: self.trail.append(STOP_EXAMPLE_NO_DISCARD_RECORD) def draw_bits(self, n, forced): self.trail.append(DRAW_BITS_RECORD)
def children(self): if self.__children is None: self.__children = [IntList() for _ in hrange(len(self))] for i, p in enumerate(self.parentage): if i > 0: self.__children[p].append(i) # Replace empty children lists with a tuple to reduce # memory usage. for i, c in enumerate(self.__children): if not c: self.__children[i] = () return self.__children
def begin(self): self.result = IntList.of_length(len(self.examples))
def starting_lists(self, ls): self.model = list(ls) self.target = IntList(ls)
def begin(self): self.starts = IntList.of_length(len(self.examples)) self.ends = IntList.of_length(len(self.examples))
def begin(self): self.nontrivial = IntList.of_length(len(self.examples)) self.result = set()
def __init__(self): self.labels = [DRAW_BYTES_LABEL] self.__index_of_labels = {DRAW_BYTES_LABEL: 0} self.trail = IntList()
def test_int_list_of_length(): assert list(IntList.of_length(10)) == [0] * 10
def begin(self): """Called at the beginning of the run to initialise any relevant state.""" self.result = IntList.of_length(len(self.examples))
class Blocks(object): """A lazily calculated list of blocks for a particular ``ConjectureResult`` or ``ConjectureData`` object. Pretends to be a list containing ``Block`` objects but actually only contains their endpoints right up until the point where you want to access the actual block, at which point it is constructed. This is designed to be as space efficient as possible, so will at various points silently transform its representation into one that is better suited for the current access pattern. In addition, it has a number of convenience methods for accessing properties of the block object at index ``i`` that should generally be preferred to using the Block objects directly, as it will not have to allocate the actual object.""" __slots__ = ("endpoints", "owner", "__blocks", "__count", "__sparse") def __init__(self, owner): self.owner = owner self.endpoints = IntList() self.__blocks = {} self.__count = 0 self.__sparse = True def add_endpoint(self, n): """Add n to the list of endpoints.""" assert isinstance(self.owner, ConjectureData) self.endpoints.append(n) def transfer_ownership(self, new_owner): """Used to move ``Blocks`` over to a ``ConjectureResult`` object when that is read to be used and we no longer want to keep the whole ``ConjectureData`` around.""" assert isinstance(new_owner, ConjectureResult) self.owner = new_owner self.__check_completion() def start(self, i): """Equivalent to self[i].start.""" i = self._check_index(i) if i == 0: return 0 else: return self.end(i - 1) def end(self, i): """Equivalent to self[i].end.""" return self.endpoints[i] def bounds(self, i): """Equivalent to self[i].bounds.""" return (self.start(i), self.end(i)) def all_bounds(self): """Equivalent to [(b.start, b.end) for b in self].""" prev = 0 for e in self.endpoints: yield (prev, e) prev = e @property def last_block_length(self): return self.end(-1) - self.start(-1) def __len__(self): return len(self.endpoints) def __known_block(self, i): try: return self.__blocks[i] except (KeyError, IndexError): return None def trivial(self, i): """Equivalent to self.blocks[i].trivial.""" if self.owner is not None: return self.start(i) in self.owner.forced_indices or not any( self.owner.buffer[self.start(i):self.end(i)]) else: return self[i].trivial def _check_index(self, i): n = len(self) if i < -n or i >= n: raise IndexError("Index %d out of range [-%d, %d)" % (i, n, n)) if i < 0: i += n return i def __getitem__(self, i): i = self._check_index(i) assert i >= 0 result = self.__known_block(i) if result is not None: return result # We store the blocks as a sparse dict mapping indices to the # actual result, but this isn't the best representation once we # stop being sparse and want to use most of the blocks. Switch # over to a list at that point. if self.__sparse and len(self.__blocks) * 2 >= len(self): new_blocks = [None] * len(self) for k, v in self.__blocks.items(): new_blocks[k] = v self.__sparse = False self.__blocks = new_blocks assert self.__blocks[i] is None start = self.start(i) end = self.end(i) # We keep track of the number of blocks that have actually been # instantiated so that when every block that could be instantiated # has been we know that the list is complete and can throw away # some data that we no longer need. self.__count += 1 # Integrity check: We can't have allocated more blocks than we have # positions for blocks. assert self.__count <= len(self) result = Block( start=start, end=end, index=i, forced=start in self.owner.forced_indices, all_zero=not any(self.owner.buffer[start:end]), ) try: self.__blocks[i] = result except IndexError: assert isinstance(self.__blocks, list) assert len(self.__blocks) < len(self) self.__blocks.extend([None] * (len(self) - len(self.__blocks))) self.__blocks[i] = result self.__check_completion() return result def __check_completion(self): """The list of blocks is complete if we have created every ``Block`` object that we currently good and know that no more will be created. If this happens then we don't need to keep the reference to the owner around, and delete it so that there is no circular reference. The main benefit of this is that the gc doesn't need to run to collect this because normal reference counting is enough. """ if self.__count == len(self) and isinstance(self.owner, ConjectureResult): self.owner = None def __iter__(self): for i in hrange(len(self)): yield self[i] def __repr__(self): parts = [] for i in hrange(len(self)): b = self.__known_block(i) if b is None: parts.append("...") else: parts.append(repr(b)) return "Block([%s])" % (", ".join(parts), )
def test_int_list_cannot_contain_negative(): with pytest.raises(ValueError): IntList([-1])
def __init__(self, owner): self.owner = owner self.endpoints = IntList() self.__blocks = {} self.__count = 0 self.__sparse = True
class Blocks(object): """A lazily calculated list of blocks for a particular ``ConjectureResult`` or ``ConjectureData`` object. Pretends to be a list containing ``Block`` objects but actually only contains their endpoints right up until the point where you want to access the actual block, at which point it is constructed. This is designed to be as space efficient as possible, so will at various points silently transform its representation into one that is better suited for the current access pattern. In addition, it has a number of convenience methods for accessing properties of the block object at index ``i`` that should generally be preferred to using the Block objects directly, as it will not have to allocate the actual object.""" __slots__ = ("endpoints", "owner", "__blocks", "__count", "__sparse") def __init__(self, owner): self.owner = owner self.endpoints = IntList() self.__blocks = {} self.__count = 0 self.__sparse = True def add_endpoint(self, n): """Add n to the list of endpoints.""" assert isinstance(self.owner, ConjectureData) self.endpoints.append(n) def transfer_ownership(self, new_owner): """Used to move ``Blocks`` over to a ``ConjectureResult`` object when that is read to be used and we no longer want to keep the whole ``ConjectureData`` around.""" assert isinstance(new_owner, ConjectureResult) self.owner = new_owner self.__check_completion() def start(self, i): """Equivalent to self[i].start.""" i = self._check_index(i) if i == 0: return 0 else: return self.end(i - 1) def end(self, i): """Equivalent to self[i].end.""" return self.endpoints[i] def bounds(self, i): """Equivalent to self[i].bounds.""" return (self.start(i), self.end(i)) def all_bounds(self): """Equivalent to [(b.start, b.end) for b in self].""" prev = 0 for e in self.endpoints: yield (prev, e) prev = e @property def last_block_length(self): return self.end(-1) - self.start(-1) def __len__(self): return len(self.endpoints) def __known_block(self, i): try: return self.__blocks[i] except (KeyError, IndexError): return None def trivial(self, i): """Equivalent to self.blocks[i].trivial.""" if self.owner is not None: return self.start(i) in self.owner.forced_indices or not any( self.owner.buffer[self.start(i) : self.end(i)] ) else: return self[i].trivial def _check_index(self, i): n = len(self) if i < -n or i >= n: raise IndexError("Index %d out of range [-%d, %d)" % (i, n, n)) if i < 0: i += n return i def __getitem__(self, i): i = self._check_index(i) assert i >= 0 result = self.__known_block(i) if result is not None: return result # We store the blocks as a sparse dict mapping indices to the # actual result, but this isn't the best representation once we # stop being sparse and want to use most of the blocks. Switch # over to a list at that point. if self.__sparse and len(self.__blocks) * 2 >= len(self): new_blocks = [None] * len(self) for k, v in self.__blocks.items(): new_blocks[k] = v self.__sparse = False self.__blocks = new_blocks assert self.__blocks[i] is None start = self.start(i) end = self.end(i) # We keep track of the number of blocks that have actually been # instantiated so that when every block that could be instantiated # has been we know that the list is complete and can throw away # some data that we no longer need. self.__count += 1 # Integrity check: We can't have allocated more blocks than we have # positions for blocks. assert self.__count <= len(self) result = Block( start=start, end=end, index=i, forced=start in self.owner.forced_indices, all_zero=not any(self.owner.buffer[start:end]), ) try: self.__blocks[i] = result except IndexError: assert isinstance(self.__blocks, list) assert len(self.__blocks) < len(self) self.__blocks.extend([None] * (len(self) - len(self.__blocks))) self.__blocks[i] = result self.__check_completion() return result def __check_completion(self): """The list of blocks is complete if we have created every ``Block`` object that we currently good and know that no more will be created. If this happens then we don't need to keep the reference to the owner around, and delete it so that there is no circular reference. The main benefit of this is that the gc doesn't need to run to collect this because normal reference counting is enough. """ if self.__count == len(self) and isinstance(self.owner, ConjectureResult): self.owner = None def __iter__(self): for i in hrange(len(self)): yield self[i] def __repr__(self): parts = [] for i in hrange(len(self)): b = self.__known_block(i) if b is None: parts.append("...") else: parts.append(repr(b)) return "Block([%s])" % (", ".join(parts),)
def __init__(self): # We store canonical values as a sorted list of integers # with each value being treated as equivalent to the largest # integer in the list that is below it. self.__values = IntList([0])
def test_int_list_can_contain_arbitrary_size(): n = 2**65 assert list(IntList([n])) == [n]
def __copy__(self): result = IntegerNormalizer() result.__values = IntList(self.__values) return result
def test_extend_by_too_large(): x = IntList() ls = [1, 10 ** 6] x.extend(ls) assert list(x) == ls