Hi Phillip,<br><br>Are there any GUI examples? wxPython examples would be ideal. :)<br><br>Thank you.<br><br>Peter.<br><br><br><div class="gmail_quote">On Tue, Mar 11, 2008 at 3:05 AM, Phillip J. Eby &lt;<a href="mailto:pje@telecommunity.com">pje@telecommunity.com</a>&gt; wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">There are two types of connections to non-trellis systems I&#39;d like to<br>
talk about for a bit: connections to callback-based systems, and<br>
connections to &quot;pure imperative&quot; systems. &nbsp;For example, connecting to<br>
a socket using Twisted or receiving wxPython GUI events might be an<br>
example of the first kind of system, and connecting to a database via<br>
SQLAlchemy or controlling Ecco via DDE would be examples of the second.<br>
<br>
The catch with the first kind of system is that in the Trellis, we<br>
don&#39;t like callbacks. &nbsp;:) &nbsp;More specifically, we don&#39;t like having to<br>
explicitly register them. &nbsp;What we really want is to just do<br>
something like &#39;is_readable(socket)&#39; in a rule, and have the<br>
callbacks automatically get set up -- and automatically disconnected<br>
when they&#39;re not needed.<br>
<br>
One crude way of doing this is already implemented in Trellis &quot;Time&quot;<br>
rules. &nbsp;There&#39;s a weak dictionary that discards the timed event cells<br>
if they are no longer being referenced. &nbsp;This is fine for Trellis<br>
time events, as they don&#39;t have any reference cycles and the Trellis<br>
ignores discarded events.<br>
<br>
But, for more complex applications, there can be undesirable<br>
consequences for leaving subscriptions in effect when they are not<br>
being used. &nbsp;For example, consider the consequences of say, capturing<br>
the mouse in a particular window, and not releasing it! &nbsp;(i.e.,<br>
imagine you could have a cell like &#39;captured_mouse_position&#39;, and<br>
capture the mouse as a side effect of depending on its value).<br>
<br>
(Also, when interacting with an external system, unless special<br>
precautions are taken, the callback from that system is going to have<br>
a reference to the target cell... &nbsp;meaning it won&#39;t be able to be<br>
garbage collected, and thus won&#39;t be able to rely on getting<br>
automatically cleaned up.)<br>
<br>
So, we really need an easy way to make or break callback arrangements<br>
when a cell gains -- or loses -- subscribers. &nbsp;Perhaps a specialized<br>
cell type that&#39;s easily customized (either per-instance or<br>
per-subclass) to do the right sort of callback-making and callback-breaking.<br>
<br>
The second kind of interfacing is a bit different. &nbsp;Instead of<br>
wanting to receive input via callbacks from the outside world, we<br>
want to simply *wrap* an externally-supplied data value, passing both<br>
reads and writes through to the underlying system (maybe with some<br>
caching), but notifying other cells when the value is changed.<br>
<br>
Generally, these external systems may be costly to retrieve data<br>
from, so we probably want them to be &quot;optional&quot; attributes, i.e., not<br>
initialized until/unless you read their values. &nbsp;Costly retrieval<br>
also implies that we may want to cache the read value, rather than<br>
passing through every read.<br>
<br>
In fact, even if retrieval isn&#39;t costly, we *still* need to do<br>
caching, because within a given trellis recalculation, a cell&#39;s value<br>
is supposed to be stable. &nbsp;It can&#39;t just go changing on its own.<br>
<br>
If we&#39;re connecting to a system where values *can* change on the fly,<br>
but does not offer notification callbacks, then we may need some way<br>
to use a &quot;time to live&quot; (TTL) before the value is automatically<br>
refreshed. &nbsp;And we can provide an explicit refresh operation as a @modifier.<br>
<br>
Having the cached value is also handy for writes; we can compare the<br>
cached value to the written value in order to decide whether to pass<br>
on the write to the underlying system.<br>
<br>
It seems like it might be possible to create a single cell type that<br>
handles all of these use cases. &nbsp;There could be methods like:<br>
<br>
_poll() -&gt; return the current value from the outside system<br>
_send(value) -&gt; send the value to the outside system<br>
_receive(value) -&gt; receive a value from a callback<br>
<br>
_subscribe() -&gt; arrange for _receive() to be called on changes<br>
_unsubscribe() -&gt; stop calling _receive()<br>
<br>
refresh() -&gt; self._receive(self._poll())<br>
<br>
With the exception of _receive(), and refresh(), these methods would<br>
be specific to the type of thing being interfaced with. &nbsp;When the<br>
cell is uninitialized -- or if its TTL has expired -- reading its<br>
value would do a refresh() first, before reading the cached value.<br>
<br>
Whenever the cell is written with a changed value, it would pass the<br>
value through to _send(), as soon as the current recalculation was<br>
completed. &nbsp;And when listeners are added or removed from the cell, it<br>
would make sure to arrange for the appropriate _subscribe() or<br>
_unsubscribe() operation to occur when the current recalculation commits.<br>
<br>
In practice, it will probably be best to have two cell types: one for<br>
a read-only connections, and one for read-write connections. &nbsp;For<br>
SQLAlchemy integration, we&#39;ll take the read-write version and set it<br>
up to talk through a delegated descriptor. &nbsp;(Which will be an<br>
interesting sub-project in itself, I suspect.)<br>
<br>
TTL is also an interesting subproject. &nbsp;If reading the cell value<br>
checks the TTL using the standard Time service, then any rule that<br>
reads the cell will also implicitly depend on the TTL and be<br>
refreshed when the TTL expires. &nbsp;In some respects this is reasonable<br>
and perhaps even desirable, except that it will cause rules to be<br>
re-run even when the value hasn&#39;t changed. &nbsp;Perhaps it would be<br>
better to make the TTL system a part of the subscribe/unsubscribe<br>
mechanism, such that refresh() is called when the TTL expires, if and<br>
only if there are listeners still looking for the value.<br>
<br>
Yes, that seems to make more sense. &nbsp;In fact, if this cell type is a<br>
variation of the standard rule+value cell type, then it&#39;s even<br>
easier. &nbsp;The rule will simply amount to something like:<br>
<br>
 &nbsp; &nbsp; if ttl_has_expired:<br>
 &nbsp; &nbsp; &nbsp; &nbsp; reset the ttl<br>
 &nbsp; &nbsp; &nbsp; &nbsp; return _poll()<br>
<br>
So that could work pretty decently, I think. &nbsp;What&#39;s rather<br>
interesting about this is that it would make it *really* easy to<br>
interface to systems that have to be polled, such as inter-thread<br>
queues, filesystem directory contents, and so forth. &nbsp;We could even<br>
have an API function like &quot;sense(interval, func, *args)&quot; so you could<br>
do something like::<br>
<br>
 &nbsp; &nbsp; if trellis.sense(10, os.path.isfile, some_file):<br>
 &nbsp; &nbsp; &nbsp; &nbsp;...<br>
<br>
in a rule, so as to automatically detect when some_file is created,<br>
checking every 10 seconds. &nbsp;(The function would similar to the Time<br>
service, i.e., by referring to a service that holds cells in a weak<br>
value dictionary, keyed by the interval, function, and function<br>
arguments. &nbsp;Thus, the same cell would be reused by any/every rule<br>
that&#39;s polling for the same thing.)<br>
<br>
So, I&#39;m thinking that the two cell types we need could be called a<br>
Sensor (read-only) and an Effector (read-write). &nbsp;The only difference<br>
between the two would be that an Effector would be writable, and<br>
would need a _send() method. &nbsp;(Well, and Effector would probably mix<br>
in different classes to get write behavior, but that&#39;s an<br>
implementation detail.)<br>
<br>
[insert **long** delay while I sketch lots of code for hours on end...]<br>
<br>
So, I think I&#39;ve got this figured out now. &nbsp;Sensor and Effector will<br>
be generic cell types, and the standard Cell() constructor will be<br>
enhanced to automatically create them if needed. &nbsp;There will be a<br>
&#39;writer&#39; keyword you can use to supply a &#39;write(value)&#39; function, and<br>
if specified it will make your cell an Effector.<br>
<br>
To implement subscription-based rules, you&#39;ll be able to subclass a<br>
base called AbstractConnector, implementing read(), subscribe(cell)<br>
and unsubscribe(cell, key) methods. &nbsp;Using such a rule will make your<br>
cell a Sensor (unless you also specify a writer, in which case you&#39;ll<br>
get an Effector). &nbsp;You&#39;ll also be able to use<br>
Connector(read,sub,unsub) to create a connector from three functions,<br>
without needing to make a subclass.<br>
<br>
The net result, once I do the implementation and testing, should be<br>
that connecting to read-only outside data sources will require only<br>
making appropriate connectors, or using a polling factory to wrap the<br>
rule. &nbsp;Connecting a writer to a data source will require only that<br>
the write function be known.<br>
<br>
There may also need to be some changes to the high-level API to allow<br>
specifying this sort of thing for rules in a class body. &nbsp;More<br>
likely, however, connectors will get managed via services or explicit<br>
cell creation and manipulation.<br>
<br>
For example, it&#39;s likely that testing a socket&#39;s readability or<br>
writability will occur through an API, rather than by having a cell<br>
attribute tied directly to this. &nbsp;These APIs will simply create the<br>
appropriate cell and read its value, caching the cell according to<br>
its creation parameters (e.g., by using an add-on, or a weak<br>
dictionary). &nbsp;For example, a socket management service would probably<br>
cache such cells by fileno(), while for a wx event listener, there<br>
would probably be a cache of cells by event ID attached as an add-on<br>
to the target window or other object. &nbsp;Querying for these events is<br>
then reduced to a dictionary lookup followed by a .value access in<br>
the common case.<br>
<br>
Whew! &nbsp;I think that&#39;s more than enough for today. &nbsp;:)<br>
<br>
_______________________________________________<br>
PEAK mailing list<br>
<a href="mailto:PEAK@eby-sarna.com">PEAK@eby-sarna.com</a><br>
<a href="http://www.eby-sarna.com/mailman/listinfo/peak" target="_blank">http://www.eby-sarna.com/mailman/listinfo/peak</a><br>
</blockquote></div><br><br clear="all"><br>-- <br>There is NO FATE, we are the creators.