示例#1
0
def test_add_with_field():
    data = {}
    session = Session(data)
    ex1 = Example(pk=1, name="Paul")
    session.add(ex1)
    session.flush()

    assert data == {"examples/1": {"name": "Paul"}}
示例#2
0
def test_non_nullable_fields_cannot_be_saved_with_null_values():
    instance = Example(pk=1)
    instance.defaulted_field = None
    session = Session({})
    session.add(instance)

    with pytest.raises(ValueError):
        session.flush()
示例#3
0
def test_add():
    data = {}
    session = Session(data)
    ex1 = Example(pk=1)
    session.add(ex1)
    session.flush()

    assert data == {"examples/1": {}}
示例#4
0
def test_remove_is_idempotent():
    data = {"examples/1": {"name": "Paula"}}
    session = Session(data)

    ex1 = session.get(Example, 1)
    session.remove(ex1)
    session.remove(ex1)
    session.flush()
示例#5
0
def test_get_then_remove_entity():
    data = {"examples/1": {"name": "Paula"}}
    session = Session(data)

    ex1 = session.get(Example, 1)
    session.remove(ex1)
    session.flush()

    assert data == {}
示例#6
0
def test_get_then_edit_entity():
    data = {"examples/1": {"name": "Paula"}}
    session = Session(data)

    ex1 = session.get(Example, 1)
    ex1.name = "Paul"
    session.flush()

    assert data == {"examples/1": {"name": "Paul"}}
示例#7
0
def test_sessions_are_not_global():
    instance_session1 = Example(pk=1)
    session = Session({})
    session.add(instance_session1)
    session.flush()

    session2 = Session({})
    with pytest.raises(KeyError):
        instance_session2 = session2.get(Example, 1)
示例#8
0
def test_add_then_remove_entity_has_no_effect():
    data = {}
    session = Session(data)

    ex1 = Example(pk=1, name="Horse")
    session.add(ex1)
    session.remove(ex1)
    session.flush()

    assert data == {}
示例#9
0
def release(store, name, constraints, branches):
    """
    Release a given configuration.

    This is the main utility for launching experiments. `store` is the storage
    transaction mapping. `name` is a free-form name (generally an experiment
    ID), `constraints` is the constraints covering this release, and `branches`
    is an iterable of (branch ID, num buckets, settings) triples.

    The utility will select buckets which are not already covering the given
    settings, which allows for partial rollout before running a test.
    """
    session = Session(store)

    # Branches is a list of (name, n_buckets, settings) tuples
    all_buckets = [
        session.get(Bucket, x, default=CREATE) for x in range(NUM_BUCKETS)
    ]

    edited_settings = set.union(*[set(x[2].keys()) for x in branches])

    conflicting_experiments = set()
    valid_bucket_indices = []

    for idx, bucket in enumerate(all_buckets):
        if is_valid_bucket(bucket, edited_settings, constraints):
            valid_bucket_indices.append(idx)
        else:
            for entry in bucket.entries:
                # Determine if this entry is a potential conflict
                if set(entry.settings.keys()).isdisjoint(edited_settings):
                    continue
                conflicting_experiment_id, _ = entry.key
                conflicting_experiments.add(conflicting_experiment_id)

    random.shuffle(valid_bucket_indices)

    for branch_name, n_buckets, settings in branches:
        key = [name, branch_name]
        bucket_indices = valid_bucket_indices[:n_buckets]

        if len(bucket_indices) < n_buckets:
            raise NotEnoughBucketsException(conflicts=conflicting_experiments)

        valid_bucket_indices = valid_bucket_indices[n_buckets:]

        for bucket_idx in bucket_indices:
            bucket = all_buckets[bucket_idx]

            bucket.add(key, settings, constraints)

    session.flush()
示例#10
0
def close(store, name, constraints, branches):
    """
    Close a given configuration.

    This is the main utility for ending experiments. `store` is the storage
    transaction mapping. `name` is a free-form name (generally an experiment
    ID), `constraints` is the constraints covering this release, and `branches`
    is an iterable of (branch ID, num buckets, settings) triples.

    Deliberately looks like `release` and works to counteract its effects.
    """
    session = Session(store)

    keys = [[name, x[0]] for x in branches]

    for idx in range(NUM_BUCKETS):
        bucket = session.get(Bucket, idx, default=None)
        if bucket is not None:
            for key in keys:
                bucket.remove(key)

    session.flush()