[Ed.: I'm sending it on behalf of Chris since his mail server is down.
I have not edited the mail, and I will reply to it seperately]
Alright, I discussed this with moshe for a while on IRC, and here's my summary
of the discussion.
On Fri, May 11, 2001 at 04:53:15PM +0300, Moshe Zadka wrote:
> OK, let me first state some axioms:
>> 1. Everything should be configurable the same way, from within twisted.
>> Glyph has mentioned that he's not familiar with what's in bin/ anymore:
> he just mktelnetserver, and configures from within the telnet server
> everything. This is what should happen, except not limited to the telnet
> server.
Yes, this is definitely good, and planned this way.
> 2. The configuration interface must not be ui specific.
>> This is obvious, right? Command line and web based aren't the only option.
> No reason why we can't implement a special purpose client/server for configuring
> twisted.
Again, this was planned. web UIs, reality UIs (you enter the magic box, there
is a web server floating here. You configure the web server.), GTK UIs are
all distant goals.
> 3. It should be easy to optimize the configuration for a ui
>> This is like the last one -- since we might deal with very good UIs, we
> need to give them enough information for using all their abilities to
> help us.
>> 4. It should be very easy to make an object configurable.
>> This is very important -- the harder it is, the less it will be easy
> to add *good* code to twisted. This is what may be the single most idiotic
> thing in Zope (and DC is aware of it! and thinking of how to fix it!). Let's
> learn from their mistakes: the less methods, the better. The more the
> learning curve is gradual (not needing to learn a class/method/interface
> before you need the functionality), the better.
I think this is also very important to end-user acceptance of TPy. If it's
got an easy-to-use and robust configurator, people will use it.
> OK, so what do we need to do about it?
> Here's a rough proposition:
>> the configurable *interface*, which will be a class, but not a class
> people should inherit from, will contain the following methods:
>> .getQuestions() --> return a dictionary: name of question, Question object
name change: getParamaters().
> .getAnswer(name, answer) --> notify the application that an answer has been
> given to a particular question
> It can throw an InvalidAnswer exception with
> a string for a reason. This is for
> application-level verification, and is discouraged.
name change: setParamaters()
Also, throwing the exception is what's discouraged, not the actual use of the
method. :)
> .endAnswer() --> the "application" promises that no methods of the object
> will be called between a series of .getAnswer()s and
> .endAnswer(). So, this means that if the UI got a bunch
> of answers, it will call .getAnswer() several times, and
> then .endAnswer(). The UI will *check* for this method's
> existance, and will not call it if it doesn't exist.
name change: i'm not sure what, but endAnswer doesn't sound right.
What this is for is deferred calculation of answers. the not-calling-methods
thing needs to be thought about, so we don't have some weird bugs where TPy
goes completely haywire while configuring. (For instance, what if we're
configuring a web server through the web interface?)
> Question objects are meant to be open ended.
> They can contain a default.
>> Here is the general interface of the Question, that all objects conform
> too:
>> .hasAnswer() --> boolean, whether the Question already contains an
> answer/default
> .getValue() --> will only work if .hasAnswer() is true, returns the answer
> .setValue(val) --> make .hasAnswer() true
>
name change: hasValue()?
> Objects which can be created by the UI should have an __init__ which
> can be called without arguments. If there is any initialization which
> requires arguments, it should be done in endAnswer(). The UI also promises
> not to call .endAnswer() for an object if there any questions which have
> not been answered and do not have a default (.hasAnswer is false.)
Ok, instead of __init__s that don't take arguments, we have klass.getInitArgs()
that is another set of questions that the user answers before the UI
instantiates the object (well, it'll have to be klass.__dict__['getInitArgs'](),
as glyph pointed out to me). Also, when we instantiate the object, we call
newObject.setParent(parent), as moshe talks about in the open questions at
the bottom of his mail.
> Well, my proposal would not be complete if I didn't say what questions are
> available. Keep in mind, though, that the set of questions is *open ended*.
> That does not violate the light-and-lean guidelines, since a specific
> Question class will only be used if the functionality is needed.
>> Without further ado:
name change for all of the following: *Parameter
>> class BooleanQuestion:
> class IntQuestion: (can have .min and .max)
> class FloatQuestion:
> class StringQuestion: (can have .maxlength)
> class LongStringQuestion: (same as above -- it's a hint to the ui)
> class InterfaceQuestion: (specify interface, valid answers are objects)
> class ArrayQuestion: (an array of the same kind of question)
moshe told me this was a thinko, so forget about array of questions :)
> class DictQuestion: (a dictionary mapping strings -> same kind of question)
I don't really get this one.
> example:
>> class Server: # note -- not inheriting from anything
>> name = port = None
>> def getQuestions(self):
> name = StringQuestion()
> if self.name is not None:
> name.setValue(self.name)
> port = IntQuestion()
> if self.port is not None:
> port.setValue(self.port)
> return {'port': port, 'name': name}
>> def getAnswer(self, name, answer):
> # note: no need to use int(answer)
> # for port: an IntQuestion has an integer as a .getValue()
> setattr(self, name, answer)
>> Open questions:
> * How do we connect classes to interfaces?
> Suggestion: each class has an attribute __implements__ containig a list
> of interfaces. Alternatively, an interface is really a list of
> (callable, Questions) tuples, which are answered and passed to the callable.
> Modules with relevant classes register with the correct interface.
I seem to remember some standardization of this that was proposed in a PEP.
Maybe we should use that standard for now, but of course just use our own
implementation of the implementation-checking stuff. (tongue twister, eh?)
> * How do we let objects created inside another object who their parent are?
> Suggestion: if an object from on Interface question has a method .setParent,
> call it with the parent as argument. This should be done by the Question
> object, so it knows how to call it for each object in a ListQuestion
After thinking about this for a while, I decided it's a good idea. At first,
it seemed like an arbitrary fix to a problem, but when moshe brought up
how useful it would be when "moving" objects around.
> * How do we let objects "title" a question?
> Suggestion: a question has a string argument on __init__ titling it.
that's simple enough.
> * How do we allow the object to signify groupings of questions?
> Suggestion: this means the design is bad -- break down the object
> into smaller objects
I'm not sure of my stance on this. I'll try to think of some examples and
discuss.
--
Chris Armstrong carmstro at twistedmatrix.comhttp://twistedmatrix.com/~carmstrocarmstro at dynup.net