Contextual-fork upcoming changes

9 views
Skip to first unread message

Sergey Schetinin

unread,
Sep 27, 2009, 9:28:47 AM9/27/09
to better...@googlegroups.com
I think I've mentioned before that Reaction (and PasteOb) use
Contextual-fork which is (obviously) a fork of Contextual by PJE. The
fork isn't that different -- it's functionally equivalent and strips a
bunch of stuff that seems to be partially realized refactoring of some
peak.config stuff. Also the State object is reimplemented with normal
methods and stuff (the original is simply unintelligible*). Also
duplicate APIs are removed, so instead it's 'with State()" and 'with
State.child()', not 'with context.empty()' and 'with context.new()'.
@setting was changed into Setting w/ a different API (which I find
much clearer). But the package still uses most of the Contextual tests
and behaves very much the same.

* Just have a look at _let_there_be_state() in
http://svn.eby-sarna.com/Contextual/peak/context.py?view=markup


If anyone were using Contextual and Trellis and then switched from
Trellis to Reaction, I guess they would switch to Contextual-fork as
well, so I'm planning to do some more changes the to the
Contextual-fork and wonder if anyone is using it at all and if they
are using the features I want to remove or change.

1) The feature I want to remove is context rule dependency -- I'm not
sure it has any documentation other than a couple of tests, but
Contextual actually tracks if rule execution touches any items in the
state, and if it does, then if those keys are overridden in a child
state, then even if the rule is the same the value in that substate
will be different. Let me demonstrate

from mext.context import *

a = Setting(0)
b = Setting(a.get, lambda f: f())

assert b.get() == 0

with State.child():
a.set(100)
assert b.get() == 100

See, the b rule is still the same, but the b value is different
because it depends on a value. The same thing works for services

class Svc(Service):
a = None
def __init__(self):
self.a = a.get()

assert Svc.a == 0
with State.child():
a.set(100)
assert Svc.a == 100


There are at least two problems w/ that: first is that services'
__init__ gets called once per state the service is being acquired --
the new service instances are discarded if their dependencies didn't
change compared the parent state where the service was already
queried. Second is that this behavior is simply unwanted some of the
time. Anyway, it seems like a nice way to solve some problems in big
systems, but from my experience it brings some problems with it which
kind of balances the benefits out. And given the non-trivial nature of
this feature I think it should go. Something similar would be nice,
but it will have to be way more explicit than that.

2) I want to remove the <<= operator overload, just do the
State[Service] = OverrideService

3) State layers. I don't like the fact that when you create a child
state to override some service, all the other state keys become
overridable in this new state. Every key can only have one rule and
value per state, but given it's a new one, the rules can be overridden
which often negates that immutability of state. My idea of a solution
is to add a concept of a layer, that is: keys belong to layers, for
example a group of related services and settings would be belong to
one "layer" and when you create a child state you specify what layer
do you intend to make writable in it -- that way the state mutations
are much better controlled and one could enable instrumenting existing
state with pre-created layers. For example one would create a layer
defining network settings and services (think connection pools,
proxied HTTP connectors etc) and then add that layer to state when
needed. For example it could be an app that would use different bunch
of settings for the same operation when carried out on behalf of
different users -- they each would have a layer configured for them
swapped in an out as necessary, orthogonally to the rest of
application state. An alternative name for "layers" could be "slice".

4) Global state. Some of the services should not be thread-local,
stuff like connection pools and other. This could be a special case of
"layer" described above. I already have an implementation of this one,
but I'm not sure if I want to keep it or redo it, it's a bit of a hack
on top of existing system.

5) Explicit laziness. Now everything is lazy by default, I'm
considering changing that, so that state would generally operate on
values, not rules and the rules would be defined more explicitly,
something like State[foo] = rule(lambda: ..), = lazy(lambda:..),
State.rules[..] = .. or something else of that kind. Suggestions are
welcome.

Anyway, if you have any objections or thoughts on this please don't
take too long with voicing them, I think I'll start implementing the
changes rather soon (except for layers, I think they'll take more
time, so I'll save them for later).

--
Best Regards,
Sergey Schetinin

http://s3bk.com/ -- S3 Backup
http://word-to-html.com/ -- Word to HTML Converter

Sergey Schetinin

unread,
Oct 4, 2009, 11:35:22 AM10/4/09
to Better Python
Contextual-fork 0.2 is out: http://pypi.python.org/pypi/Contextual-fork/0.2.r1175

It implements points 1, 2 and 5. Except that the only trace of
laziness is left in __default__. Now service overrides have to be set
with instances, not factories, e.g. State[Time] = PausedTime().

Also, in Contextual the rule of "write until read" was applied, but
it's "only write once" now. So, if you set some State key, you can't
change it in that state anymore, even if it wasn't read yet.

Setting has lost its "normalize" argument, Scope, Action and Resource
are disabled for now (they will probably return in a different form
when the layers will be implemented.

Also, to create extending / overriding service, use __service__ class
attribute (was done via replaces() and more recently via get = ..).

To understand what kind of changes this requires from your code, see
http://code.google.com/p/trellis-fork/source/detail?r=281

On Sep 27, 4:28 pm, Sergey Schetinin <mal...@gmail.com> wrote:
> I think I've mentioned before that Reaction (and PasteOb) use
> Contextual-fork which is (obviously) a fork of Contextual by PJE. The
> fork isn't that different -- it's functionally equivalent and strips a
> bunch of stuff that seems to be partially realized refactoring of some
> peak.config stuff. Also the State object is reimplemented with normal
> methods and stuff (the original is simply unintelligible*). Also
> duplicate APIs are removed, so instead it's 'with State()" and 'with
> State.child()', not 'with context.empty()' and 'with context.new()'.
> @setting was changed into Setting w/ a different API (which I find
> much clearer). But the package still uses most of the Contextual tests
> and behaves very much the same.
>
> * Just have a look at _let_there_be_state() inhttp://svn.eby-sarna.com/Contextual/peak/context.py?view=markup

Sergey Schetinin

unread,
Feb 24, 2010, 12:48:46 PM2/24/10
to Better Python
Currently when State is used as a context manager (__enter__ /
__exit__) it destroys state when exited, tracks the order of enters /
exits, children etc. I think we can just remove all that and let the
__enter__ / __exit__ essentially be self.prev_state = self.swap()
self.prev_state.swap(). The more advanced usage of states (Resources /
Scopes / Actions from Contextual) are disabled at the moment anyway
and I think they are better served by a clearly separate API, maybe
the StateLayer API I mentioned earlier.

Any objections?

> --~--~---------~--~----~------------~-------~--~----~
> Mailing list: http://groups.google.com/group/better-python
> Unsubscribe: better-pytho...@googlegroups.com
> -~----------~----~----~----~------~----~------~--~---
>
>

--
Best Regards,
Sergey Schetinin

http://s3bk.com/ -- S3 Backup

Reply all
Reply to author
Forward
0 new messages