def test_already_canceled(): parent, pcancel = context.with_cancel(bg) assertCtx(parent, Z) pcancel() assertCtx(parent, Z, err=C, done=Y) ctxC, _ = context.with_cancel(parent) assert ctxC.done() != parent.done() assertCtx(parent, Z, err=C, done=Y) # no ctxC in children assertCtx(ctxC, Z, err=C, done=Y) ctxT, _ = context.with_timeout(parent, 10 * dt) d = ctxT.deadline() assert ctxT.done() != parent.done() assertCtx(parent, Z, err=C, done=Y) # no ctxT in children assertCtx(ctxT, Z, deadline=d, err=C, done=Y) d = time.now() + 10 * dt ctxD, _ = context.with_deadline(parent, d) assert ctxD.done() != parent.done() assertCtx(parent, Z, err=C, done=Y) # no ctxD in children assertCtx(ctxD, Z, deadline=d, err=C, done=Y) ctxM, _ = context.merge(parent, bg) assert ctxM.done() != parent.done() assertCtx(parent, Z, err=C, done=Y) # no ctxM in children assertCtx(ctxM, Z, err=C, done=Y) ctxV = context.with_value(parent, kHello, "world") assert ctxV.done() == parent.done() assert ctxV.value(kHello) == "world" assertCtx(parent, Z, err=C, done=Y) # no ctxV in children assertCtx(ctxV, Z, err=C, done=Y)
def test_workgroup(): ctx, cancel = context.with_cancel(context.background()) mu = sync.Mutex() # t1=ok, t2=ok wg = sync.WorkGroup(ctx) l = [0, 0] for i in range(2): def _(ctx, i): with mu: l[i] = i + 1 wg.go(_, i) wg.wait() assert l == [1, 2] # WorkGroup must catch/propagate all exception classes. # Python2 allows to raise old-style classes not derived from BaseException. # Python3 allows to raise only BaseException derivatives. if six.PY2: class MyError: def __init__(self, *args): self.args = args else: class MyError(BaseException): pass # t1=fail, t2=ok, does not look at ctx wg = sync.WorkGroup(ctx) l = [0, 0] for i in range(2): def _(ctx, i): Iam__ = 0 with mu: l[i] = i + 1 if i == 0: raise MyError('aaa') def f(ctx, i): Iam_f = 0 _(ctx, i) wg.go(f, i) with raises(MyError) as exc: wg.wait() assert exc.type is MyError assert exc.value.args == ('aaa', ) if PyErr_Restore_traceback_ok: assert 'Iam__' in exc.traceback[-1].locals assert 'Iam_f' in exc.traceback[-2].locals assert l == [1, 2] # t1=fail, t2=wait cancel, fail wg = sync.WorkGroup(ctx) l = [0, 0] for i in range(2): def _(ctx, i): Iam__ = 0 with mu: l[i] = i + 1 if i == 0: raise MyError('bbb') if i == 1: ctx.done().recv() raise ValueError('ccc') # != MyError def f(ctx, i): Iam_f = 0 _(ctx, i) wg.go(f, i) with raises(MyError) as exc: wg.wait() assert exc.type is MyError assert exc.value.args == ('bbb', ) if PyErr_Restore_traceback_ok: assert 'Iam__' in exc.traceback[-1].locals assert 'Iam_f' in exc.traceback[-2].locals assert l == [1, 2] # t1=ok,wait cancel t2=ok,wait cancel # cancel parent wg = sync.WorkGroup(ctx) l = [0, 0] for i in range(2): def _(ctx, i): with mu: l[i] = i + 1 ctx.done().recv() wg.go(_, i) cancel() # parent cancel - must be propagated into workgroup wg.wait() assert l == [1, 2]
def test_context(): assert bg.err() is None assert bg.done() == nilchan assert bg.deadline() is None assert not ready(bg.done()) assert bg.value(kHello) is None ctx1, cancel1 = context.with_cancel(bg) assert ctx1.done() != bg.done() assertCtx(ctx1, Z) ctx11, cancel11 = context.with_cancel(ctx1) assert ctx11.done() != ctx1.done() assertCtx(ctx1, {ctx11}) assertCtx(ctx11, Z) vAlpha = Key("alpha") # value object; Key is just reused as placeholder ctx111 = context.with_value(ctx11, kHello, vAlpha) assert ctx111.done() == ctx11.done() assert ctx111.value(kHello) is vAlpha # original value object is returned assert ctx111.value( kHello2) is None # _not_ vAlpha: keys are compared by identity assert ctx111.value(kAbc) is None assertCtx(ctx1, {ctx11}) assertCtx(ctx11, {ctx111}) assertCtx(ctx111, Z) ctx1111 = context.with_value(ctx111, kBeta, "gamma") assert ctx1111.done() == ctx11.done() assert ctx1111.value(kHello) is vAlpha assert ctx1111.value(kBeta) == "gamma" assert ctx1111.value(kAbc) is None assertCtx(ctx1, {ctx11}) assertCtx(ctx11, {ctx111}) assertCtx(ctx111, {ctx1111}) assertCtx(ctx1111, Z) ctx12 = context.with_value(ctx1, kHello, "world") assert ctx12.done() == ctx1.done() assert ctx12.value(kHello) == "world" assert ctx12.value(kAbc) is None assertCtx(ctx1, {ctx11, ctx12}) assertCtx(ctx11, {ctx111}) assertCtx(ctx111, {ctx1111}) assertCtx(ctx1111, Z) assertCtx(ctx12, Z) ctx121, cancel121 = context.with_cancel(ctx12) assert ctx121.done() != ctx12.done() assert ctx121.value(kHello) == "world" assert ctx121.value(kAbc) is None assertCtx(ctx1, {ctx11, ctx12}) assertCtx(ctx11, {ctx111}) assertCtx(ctx111, {ctx1111}) assertCtx(ctx1111, Z) assertCtx(ctx12, {ctx121}) assertCtx(ctx121, Z) ctx1211 = context.with_value(ctx121, kMir, "май") assert ctx1211.done() == ctx121.done() assert ctx1211.value(kHello) == "world" assert ctx1211.value(kMir) == "май" assert ctx1211.value(kAbc) is None assertCtx(ctx1, {ctx11, ctx12}) assertCtx(ctx11, {ctx111}) assertCtx(ctx111, {ctx1111}) assertCtx(ctx1111, Z) assertCtx(ctx12, {ctx121}) assertCtx(ctx121, {ctx1211}) assertCtx(ctx1211, Z) ctxM, cancelM = context.merge(ctx1111, ctx1211) assert ctxM.done() != ctx1111.done() assert ctxM.done() != ctx1211.done() assert ctxM.value(kHello) is vAlpha assert ctxM.value(kMir) == "май" assert ctxM.value(kBeta) == "gamma" assert ctxM.value(kAbc) is None assertCtx(ctx1, {ctx11, ctx12}) assertCtx(ctx11, {ctx111}) assertCtx(ctx111, {ctx1111}) assertCtx(ctx1111, {ctxM}) assertCtx(ctx12, {ctx121}) assertCtx(ctx121, {ctx1211}) assertCtx(ctx1211, {ctxM}) assertCtx(ctxM, Z) for _ in range(2): cancel11() assertCtx(ctx1, {ctx12}) assertCtx(ctx11, Z, err=C, done=Y) assertCtx(ctx111, Z, err=C, done=Y) assertCtx(ctx1111, Z, err=C, done=Y) assertCtx(ctx12, {ctx121}) assertCtx(ctx121, {ctx1211}) assertCtx(ctx1211, Z) assertCtx(ctxM, Z, err=C, done=Y) for _ in range(2): cancel1() assertCtx(ctx1, Z, err=C, done=Y) assertCtx(ctx11, Z, err=C, done=Y) assertCtx(ctx111, Z, err=C, done=Y) assertCtx(ctx1111, Z, err=C, done=Y) assertCtx(ctx12, Z, err=C, done=Y) assertCtx(ctx121, Z, err=C, done=Y) assertCtx(ctx1211, Z, err=C, done=Y) assertCtx(ctxM, Z, err=C, done=Y)
def test_deadline(): t0 = time.now() d1 = t0 + 10 * dt d2 = t0 + 20 * dt d3 = t0 + 30 * dt ctx1, cancel1 = context.with_deadline(bg, d2) assert ctx1.done() != bg.done() assertCtx(ctx1, Z, deadline=d2) ctx11 = context.with_value(ctx1, kA, "b") assert ctx11.done() == ctx1.done() assert ctx11.value(kA) == "b" assertCtx(ctx1, {ctx11}, deadline=d2) assertCtx(ctx11, Z, deadline=d2) ctx111, cancel111 = context.with_cancel(ctx11) assert ctx111.done() != ctx11.done assertCtx(ctx1, {ctx11}, deadline=d2) assertCtx(ctx11, {ctx111}, deadline=d2) assertCtx(ctx111, Z, deadline=d2) ctx1111, cancel1111 = context.with_deadline(ctx111, d3) # NOTE deadline > parent assert ctx1111.done() != ctx111.done() assertCtx(ctx1, {ctx11}, deadline=d2) assertCtx(ctx11, {ctx111}, deadline=d2) assertCtx(ctx111, {ctx1111}, deadline=d2) assertCtx(ctx1111, Z, deadline=d2) # NOTE not d3 ctx12, cancel12 = context.with_deadline(ctx1, d1) assert ctx12.done() != ctx1.done() assertCtx(ctx1, {ctx11, ctx12}, deadline=d2) assertCtx(ctx11, {ctx111}, deadline=d2) assertCtx(ctx111, {ctx1111}, deadline=d2) assertCtx(ctx1111, Z, deadline=d2) assertCtx(ctx12, Z, deadline=d1) ctxM, cancelM = context.merge(ctx1111, ctx12) assert ctxM.done() != ctx1111.done() assert ctxM.done() != ctx12.done() assert ctxM.value(kA) == "b" assertCtx(ctx1, {ctx11, ctx12}, deadline=d2) assertCtx(ctx11, {ctx111}, deadline=d2) assertCtx(ctx111, {ctx1111}, deadline=d2) assertCtx(ctx1111, {ctxM}, deadline=d2) assertCtx(ctx12, {ctxM}, deadline=d1) assertCtx(ctxM, Z, deadline=d1) time.sleep(11 * dt) assertCtx(ctx1, {ctx11}, deadline=d2) assertCtx(ctx11, {ctx111}, deadline=d2) assertCtx(ctx111, {ctx1111}, deadline=d2) assertCtx(ctx1111, Z, deadline=d2) assertCtx(ctx12, Z, deadline=d1, err=D, done=Y) assertCtx(ctxM, Z, deadline=d1, err=D, done=Y) # explicit cancel first -> err=canceled instead of deadlineExceeded for i in range(2): cancel1() assertCtx(ctx1, Z, deadline=d2, err=C, done=Y) assertCtx(ctx11, Z, deadline=d2, err=C, done=Y) assertCtx(ctx111, Z, deadline=d2, err=C, done=Y) assertCtx(ctx1111, Z, deadline=d2, err=C, done=Y) assertCtx(ctx12, Z, deadline=d1, err=D, done=Y) assertCtx(ctxM, Z, deadline=d1, err=D, done=Y) # with_timeout ctx, cancel = context.with_timeout(bg, 10 * dt) assert ctx.done() != bg.done() d = ctx.deadline() assert abs(d - (time.now() + 10 * dt)) < 1 * dt assertCtx(ctx, Z, deadline=d) time.sleep(11 * dt) assertCtx(ctx, Z, deadline=d, err=D, done=Y)
def test_workgroup_with(): # verify with support for sync.WorkGroup ctx, cancel = context.with_cancel(context.background()) defer(cancel) mu = sync.Mutex() # t1=ok, t2=ok l = [0, 0] with sync.WorkGroup(ctx) as wg: for i in range(2): def _(ctx, i): with mu: l[i] = i+1 wg.go(_, i) assert l == [1, 2] # t1=fail, t2=wait cancel, fail with raises(MyError) as exci: with sync.WorkGroup(ctx) as wg: def _(ctx): Iam_t1 = 0 raise MyError('hello (fail)') wg.go(_) def _(ctx): ctx.done().recv() raise MyError('world (after zzz)') wg.go(_) e = exci.value assert e.__class__ is MyError assert e.args == ('hello (fail)',) assert e.__cause__ is None assert e.__context__ is None assert e.__suppress_context__ == False if PyErr_Restore_traceback_ok: assert 'Iam_t1' in exci.traceback[-1].locals # t=ok, but code from under with raises l = [0] with raises(MyError) as exci: with sync.WorkGroup(ctx) as wg: def _(ctx): l[0] = 1 wg.go(_) def bad(): raise MyError('wow') bad() e = exci.value assert e.__class__ is MyError assert e.args == ('wow',) assert e.__cause__ is None assert e.__context__ is None assert e.__suppress_context__ == False assert exci.traceback[-1].name == 'bad' assert l[0] == 1 # t=fail, code from under with also raises with raises(MyError) as exci: with sync.WorkGroup(ctx) as wg: def f(ctx): raise MyError('fail from go') wg.go(f) def g(): raise MyError('just raise') g() e = exci.value assert e.__class__ is MyError assert e.args == ('fail from go',) assert e.__cause__ is None assert e.__context__ is not None assert e.__suppress_context__ == False assert exci.traceback[-1].name == 'f' e2 = e.__context__ assert e2.__class__ is MyError assert e2.args == ('just raise',) assert e2.__cause__ is None assert e2.__context__ is None assert e2.__suppress_context__ == False assert e2.__traceback__ is not None t2 = Traceback(e2.__traceback__) assert t2[-1].name == 'g'
def test_workgroup(): ctx, cancel = context.with_cancel(context.background()) mu = sync.Mutex() # t1=ok, t2=ok wg = sync.WorkGroup(ctx) l = [0, 0] for i in range(2): def _(ctx, i): with mu: l[i] = i+1 wg.go(_, i) wg.wait() assert l == [1, 2] # t1=fail, t2=ok, does not look at ctx wg = sync.WorkGroup(ctx) l = [0, 0] for i in range(2): def _(ctx, i): Iam__ = 0 with mu: l[i] = i+1 if i == 0: raise MyError('aaa') def f(ctx, i): Iam_f = 0 _(ctx, i) wg.go(f, i) with raises(MyError) as exc: wg.wait() assert exc.type is MyError assert exc.value.args == ('aaa',) if PyErr_Restore_traceback_ok: assert 'Iam__' in exc.traceback[-1].locals assert 'Iam_f' in exc.traceback[-2].locals assert l == [1, 2] # t1=fail, t2=wait cancel, fail wg = sync.WorkGroup(ctx) l = [0, 0] for i in range(2): def _(ctx, i): Iam__ = 0 with mu: l[i] = i+1 if i == 0: raise MyError('bbb') if i == 1: ctx.done().recv() raise ValueError('ccc') # != MyError def f(ctx, i): Iam_f = 0 _(ctx, i) wg.go(f, i) with raises(MyError) as exc: wg.wait() assert exc.type is MyError assert exc.value.args == ('bbb',) if PyErr_Restore_traceback_ok: assert 'Iam__' in exc.traceback[-1].locals assert 'Iam_f' in exc.traceback[-2].locals assert l == [1, 2] # t1=ok,wait cancel t2=ok,wait cancel # cancel parent wg = sync.WorkGroup(ctx) l = [0, 0] for i in range(2): def _(ctx, i): with mu: l[i] = i+1 ctx.done().recv() wg.go(_, i) cancel() # parent cancel - must be propagated into workgroup wg.wait() assert l == [1, 2]