On Sat, 03 May 2008 13:51:15 +0200, Martijn Faassen wrote:

Besides giving the detailed reply below, I also try to summarize in the
beginning how I see the situation and what principal questions I can
identify. I hope this makes my point of view more clear. I suggest to
continue along these summary points, by extending them if needed. The
reason for this is that the posts are getting too long and have
overlapping issues.

I believe I now understand the arguments of both sides. My main motivation
to extend the original kss.base commandset API proposal is that while I
agree upon the need to use commandsets more pythonically, there is also a
need from the kss side to provide clean concepts and be mostly server
agnostic. These seem to be contradicting requirements at first, but still
I believe there is a way that satisfies both the kss and the python
perspectives.

But I agree with your intentions, that the actual python implementation
should be easy to use, and the usage should be natural and intuitive for
the programmers - apparently this is not the case now so this needs to be
changed. It should be also beyond argument that no such implementation
will be chosen that python programmers find non-fitting.

Based on the discussion so far, I believe the following corner questions
need to be decided:

1. Do we want to keep the "commandset" concept in kss,
   or drop it completely?

2. In the python-based server implementations, does kss want to support
   "commandsets" in a unified and server independent way?

3. Are commandsets accessed from python by their kss, or python
   namespace?

I propose to examine the answer to these points, and only after this we
should decide about the implementation issues, like the actual import and
calling of commandsets from python or from a zope view.


Let me explain the relevance of these points in details:

-------------------------------------------------------------
1. Do we want to
keep the "commandset" concept in kss, or drop it completely?
-------------------------------------------------------------

I believe the answer to the question should be yes. Commandsets turned out
to be a useful concept for two reasons:

- by making an equivalent "command" for each client action, server
  side can emit these commands easily and in a pythonic way.

- complex commands that have no client action, can break down a
  complex page manipulation task into smaller parts without the need to
  program on the client. This code is distributed together with the kss
  plugin, and is usable everywhere in the same way, where the plugin is
  installed. - Occasionally these commands have the need to access the
  server side content and utilities.

  The advantage of this last point may not be obvious if we only think of
  the usage of the "core" commandset but it's somewhat understood if we
  look at the commandsets in the "plone" plugin.

If we answer "no" to this question, then there is no need to think about
the following questions. If the answer is "yes" then it is worth thinking
on how make the commandsets more accessible, so that programmers can find
the best and most balanced way of using them.


----------------------------------------------------------------------- 
2. In the python-based server implementations, does kss want to support
"commandsets" in a unified and server independent way?
-----------------------------------------------------------------------

By "unified" I mean that although commandsets are provided by plugins, the

- usage and definition of commandsets is independent from
  the plugin they are defined from: the commandsets from the core plugin
  and from other plugins should not be different to define or use. We do
  not give the freedom to plugins to come up with their own format.

By "server independent way" I mean that while servers will need different
commandset implementations, for example Zope needs to use them from a
view, other environments will come up with their individual needs, but:

- usage of the commandsets (and the readibility of code that calls them)
  should be the same in the different server implementations (python,
  zope, django) and their

- definition should also be "as similar as possible".

I'd find it very undesirable, if for example, a Zope view needs to call up
a core command and a command from a Zope commandset (that accesses the
view) in two consequtive lines, and it needs to do these two calls with a
different syntax.

In my definition, if we leave to decide the way of implementation
completely up to the plugins and/or server side implementations, that
means the answer "no" to this question. This means leave to python to
decide its own "best way", let Zope add its own type of commandsets, and
django, etc. do the same, then actually kss is not specifying the API, but
python, Zope, django does.

Which is also a possible direction, and this means that the answer to this
question needs to be considered seriously. In case of "no", there is also
no need to think about point 3.

While I agree that this unified support must not impose restriction on the
server side code (ie. should be very pythonic) kss should do (in my
opinion) more then just giving suggestions and guidelines for server side
implementations: it should provide a pythonic API but also provide a
generic scheme that can be implemented by all server sides. We should also
demonstrate how this works in python, zope and django. We can never be
sure if a new server side brings additonal requirements, but we can
already draw conclusions from the cases we have already at hand.


------------------------------------------------------------------- 
3. Are commandsets accessed from python by 
their kss, or python namespace?
-------------------------------------------------------------------

I could also ask it in this way: Are kss _commands_ accessed from python
by their kss, or python namespace?

Fact is that in kss we have a namespace where all events, actions, etc.,
including commands, are named with a dashed namespaced name, like
"pluginname-commandname". We will soon allow multi-level namespaces as
well: ie. you could soon use a "jquery-dnd-drag" action or
"plone-forms-submit' action and command. Note: the kss namespace is not a
python namespace and it's unhandy to think of it as such.

There are two basic issues, which namespace the commandsets are accessed
by, and how do we aid calling the commandsets from view-like constructs
(in Zope, from kss views or from other commandsets).

The namespace issue is principal and I see the following possible
directions (there could be more):


A. Access commandsets by the kss namespace
..........................................

This is how currently, in Zope, the commandsets are looked up by the
getCommandSet call that accepts the kss name of the commandset as a
parameter: this would be the namespaced name, but since we have no
multilevel, now it's just the "name" of the commandset.

The disadvantage is that the actual python code of the commandset is
difficult to find: there is no direct import, so similarly how you need to
look up the CA to find the actual import location of a Zope browser view,
you also need to call the kss plugin registry to look up the commandset.

I understand why this is a serious con as well.


B. Access commandsets by the python namespace
.............................................

This is the suggested way if we follow the original proposal and extend it
to other plugins. However intuitive it may be that in this case the
commandset's code is exactly where you import it from, but it is
contraintuitive from the point of view that for example the
"plone-issuePortalMessage' command would be needed to import from
"plone.app.kss.plugin.commandsets.PloneCommandSet", however specifying the
command by its kss namespace seems to be more important here then knowing
its python namespace.

I also understand that the direct import is handy in case of the
python-only commandsets (that use no context view), and why it's important
to have it, but I don't imagine easily how this direct import would be
applied benefitially to the current Zope usage, where I guess we will need
some helper method on the view to lookup the commandset anyway.


C. Unify the two namespaces
---------------------------

One feasible solution that would allow direct import from python would be
to _force_ the implementation of kss plugin "X" to be in the python module
kss.plugin.X. In this case plugins would loose import location
independence, which is however not the only consequence.

In this case all code that adds a kss plugin, will need to create a
separate egg in kss.plugin. For example plone.app.kss will need to come
with a kss.plugin.plone and if we are consequent kss.base should have a
kss.plugin.core. Similarly, megrok.kss would need to add a
kss.plugin.megrok egg if it has plugins, and so on. Since we have
setuptools, this may be easy to satisfy in some cases but can also be a
burden in other cases.

There is another specific use case when I create a kss plugin for a
specific application, or application component, in this case my code is in
for example z3c.mylib.mywidget but I would immediately need to make a
kss.plugin.mywidget egg, while I would rather just put the plugin in a
subdirectory of my component itself (as possible currently, as well).

Altogether, while I see the apparent merit of forcing all plugins to
kss.plugin, I believe it adds too much constraint to developers to require
this in each case. I would find it better to allow the current placeless
definition and registration of plugins, with a _convention_ to say that
they should be eggified to kss.plugin. This way all general purpose
plugins would move there while application components lile kss.base,
plone.app.kss, or megrok.kss, as well as custom application code, could
choose to keep their plugin code embedded.

This would make code easily enough locatable but indeed not in every case,
additional information is needed like to know that the plone plugin is in
plone.app.kss.

But indeed if we strictly enforce this placement rule, then we can import
the commandset (class?) in python always from its real location, and
without a registry, since it will have a 1-to-1 mapping to the kss plugin
namespace.

Even in this case we have to work out where the commandsets themselves are
in the kss.plugin.X module, so probably more convention or rule is needed.




In the following, I attempt to react to Martijn's original letter, which
will be a repetition or elaboration of the above summary.

On Sat, 03 May 2008 13:51:15 +0200, Martijn Faassen wrote:

> Balazs Ree wrote:
> [snip]
>> The way Jeroen implemented the commandsets in kss.base first, mainly
>> considers the actual commandsets that are in the core, and simplify
>> these cases. However we have more complex usage needs in zope and plone
>> that the previous version supported. With the way the kss.base handles
>> the commandsets,
>> - we cannot support our current needs with Zope and Plone, - but we
>> also don't want different type of commandsets in python, Zope, and
>> server X,
> 
> I disagree with the second requirement. Why *do* you want the same type
> of commandset for all these servers? What's the point? I can't reuse the
> commandset that I write for a Zope app in server X anyway, right? Any
> resuable bits that do exist I can factor out into independent libraries.

This is party true. I believe however that the followings are targetable
(and feasible) goals, as outlined in the point 2.:

1. Commandsets should be acquired in always the same way, independently
from the application type.

2. The commandset code written for any server can be read and understand
by anyone who is familiar with kss, even if he does not know that
particular server side well. This includes our tutorials become more
useful as well (we cannot expect every tutorial ported to every single
server side implementation).

I note that I also believe that in the present application code we are not
using commandsets efficiently enough. Lot of the things we are doing in
the browser view in a server action method, could be factored to
commandsets and let the view call them (examples are in plone.app.kss and
archetypes.kss). But again this is my personal impression and I am sure
that some programmers will think in a different flavour, therefore this
(what you factor out into a commandset) should be a personal decision of
the programmer, but to enable this decision, we have to give good and
coherent support from kss.

> I think that anything that relies on kss.base only should only have to
> deal with commandsets that are as close to straightforward Python
> possible, which means a module with functions in it. Or a module with an
> instance of a class in it (where the methods are the commands), if you
> need to be able to parameterize commandsets for some reason (in specific
> cases). The great advantage of this is that when people follow the
> import trail (something all Python programmers can do), they can find
> the implementation. Any registry-based approach breaks this.
> 
> Some commandsets may implement the same abstract interface. You can then
> look up the one relevant in your particular application. I'm somewhat
> skeptical of this requirement myself, as I've only heard the 'different
> effects libraries' examples so far, which doesn't convince me. How many
> of these protocols are there right now in the real world? Do they
> actually have multiple implementations?
> 
> Anyway, putting aside this skepticism, let's imagine we have an effects
> commandset and there's a reimplementation of this (the example I keep
> hearing about). I want to use this other implementation in my
> application. So I change the import. This is straightforward and
> something that Python programmers understand. It's not like I don't need
> to test my application with this new effects library anyway.
> 
> The word 'commandset' may be able to go away. Instead people can use the
> familiar concept of a "python module".
> 
> Now you could envisage a general framework that helps you get an
> implementation of a commandset interface dynamically, based on some
> configuration setting and pulling it from a registry. Zope 3 has such a
> story for registration (the component architecture). So when *I* need
> this, I'll use zope.component. kss.zope could offer some approach that
> uses this. Other systems have different stories for this, or will grow
> ad-hoc stories for this.

> I want the Zope 3 story to look like the Zope 3 story, and the other
> frameworks will have to be on their own. Perhaps eventually something
> like kss.registry might grow if some of these frameworks want to reuse
> the same registry after they gain some experience with this and it turns
> out to be an important requirement. I doubt a Zope 3 application will
> want to use this, as one of Zope 3's strongest points is its powerful
> configuration story.

There is two reasons why it is good if kss commands follow the kss
namespace and not the python namespace.

First, let's consider a command 'plone-followLink'. Now this should
actually be in its own namespace and depend on python only but let's
ignore this now.

The 'plone-followLink' action is in the 'plone' kss plugin. The command
that emits this action from the server is also called 'plone-followLink'.
The command is bound to the action, it actually provides the access
interface for it on the python side.

Now in an advantagous solution, this would be accessable from python (from
a view-like construct that Zope uses) like this:

    >>> # ...we are in a view or commandset
    >>> self.commandsets.plone.followLink(href)

(for reference, currently this works like:

    >>> # ...we are in a view or commandset
    >>> self.getCommandset('plone').followLink(href)
)

Let me apply the same pattern you suggest to this case:

    >>> # ...we are in a view or commandset

    >>> # Import the commandset class directly from
    >>> plone.app.kss.plugins.plone import PloneCommandSet

    >>> # Instantiate the commandset
    >>> commandset = CommandSet(self, self.request, self.response)
    >>> self.commandsets.plone.followLink(href)

Ignore the last two lines for a moment since that is the instantiation
only, and look at the import path. Why is it intuitive to import the
plugin from "plone.app.kss" (the module that provides the implementation),
instead of accessing it with its kss plugin name? Especially since we
_are_ already in a kss view's or another commandset's code.

It is easy to understand that we also don't want to call the kss action
itself "plone-app-kss-plugins-plone-followLink", because there is no point
in following python namespaces from kss stylesheets. But note, the "name
of the commandset" still does not match the python import path of the
code.

Now, you can argue "but what about the commands that call up other
commands and have no corresponding action on the client." For example,
plone-issuePortalMessage is like this. However I still prefer to call it
plone-issuePortalMessage and not
plone.app.kss.plugins.plone.issuePortalMessage. It belongs to the "plone" 
kss plugin, its availability depends if the plugin is enabled. (although 
it's true that the commandset can only be used if plone.app.kss is 
present.)


Second thing that happens often is that I want to move a plugin definition
from one module to the another. "plone-followLink" will be still
plone.followLink, even if I move it from plone.app.kss.plugins.plone to
somewhere else. So now you'd have to change all your python imports in
every code that uses the plugin. But I agree that the current
placeless-ness of commandsets, however handy, is not an absolute
requirement in itself.


>> 1. KSS plugins have no global import, is Good
> 
> This is not good in the lowest layer. The lowest layer should be simple
> to understand for a Python programmer. If you want this kind of stuff,
> please use the Zope 3 component architecture, which was born to do this
> kind of thing and is at least something you don't need to reinvent.

We cannot use the CA for implementation. We had that and we have decided
to move away from it, precisely because we want to support plain python.
The implementation of the kss plugin registry in kss.zope uses the CA, but
the definition and usage of commandsets must be zope-independent.

This does not mean the programmer cannot use the CA for the implementation
of its own code. Only the code that is specific to KSS, should go to
commandsets, and this code does not need the CA for its definition.

But otherwise this is turning to touch my initial point 3.
 
>> I would like to add that (unlike Martijn said) the commandset's name is
>> not "just a string". It is a fully dashed global identifier of the
>> commandset (Example plone-issuePortalMessage, but we will support
>> multilevel namespaces my-fancy-app-command any moment):
> 
> (I'm not sure why I am said to have said this)
> 
> Why invent your own namespace construct when Python already has one?

KSS has a namespace that is based on KSS's conceptial model and not that
of python. Following the python namespace from kss was never raised as a
requirement: this is ultimately a client library and the closest language
it depends on is actually javascript. I also believe it would be
inconvenient and pointless to require python import paths to appear in the
plugin namespaces and kss files.

But I also think that this is not what is being argued. The maximum thing
that can be considered is saying that the commandsets (and command names)
should not follow the namespace of the kss plugin they belong to, or find
another solution (as described in point 3.)


>>  The slight separation from python land also
>> helps to understand to the reader of the code that executes is a
>> registered part of a kss plugin - much more important then to see which
>> python module it's importing from.
> 
> I assume that if you import KSS commands from a module, you already know
> you're dealing with KSS here. Use a global 'kssplugin' namespace that
> peopple can use their KSS plugins under if you're concerned with this,
> just like Zope uses 'zope'. Don't enforce that (Zope doesn't).

You are suggesting to enforce plugins into the kss.plugin python namespace
to solve the "kss name != python import" problem, I elaborated on this in
my initial summary.
 
 
> [snip]
>> We may think, however, other ways to make the syntax usage sweeter, if
>> the view.getCommandSet() is the biggest annoyance in the story. The
>> view.getCommandSet can be removed and instead we can provide a kss
>> import namespace like
> 
> To me, it's not just the syntax. It's mostly the semantics. It that it's
> an unnecessary abstraction at this level, that hinders the ability of a
> Python programmer to find things. If you used modules, a developer can
> find a module and then have an immediate idea of how you can use it (you
> don't need to know how it's registered, you can import it). If you see a
> module in use, you can go and find the module as you know its dotted
> name.

In python, if you see a method called on an object, you don't necessarily
find the module the method is defined in at once. As a programmer you
always need to learn to find things, such is the case for zope where you
need to search zcml files, or kss where you currently need to find the
plugin's define location to read its code. So "it needs an effort to find
a code" is not a disaster in itself, if it's easy enough.

But it is indeed important that beginners should not need to learn
everything before they can use a commandset. (To write a commandset: well,
for that we can already assume some more knowledge, but it should be easy
as well.) It's also an important concept not to invent something if there
is a straightforward solution.


> [snip]
>> 2. Zope commandsets do need to access the view
> 
> I'll rephrase this: a Zope specific command will need access to the Zope
> view.
> 
> You can pass in the view as a parameter, or you could implement your
> Zope-specific set of commands as a class that takes the view as the
> argument. That's up to the developer. I disagree with tying this to the
> kss.base discussion, as I don't agree with the basic requirement that
> requires this.

The basic reason why the discussion must be made now, is that kss.base
will not only support commandsets from the core plugin. It will support
all commandsets, coming from different plugins. And kss.base will replace
kss.core, so all current kss application will be based on it. This will
affect the development model of kss in a global way.

It is also the first time that we support a plain python stack, which
means carefully reconsidering the kss model and adjusting it as needed, so
it stays generic and does not only fit one or the other server side
implementation.

Naturally if the final decision concerning  my initial point 2. in turns
out to be negative, then this reason becomes invalidated, but still the
basic requirement to discuss point 2. stays.


> Commands are much like functions or methods. I think you shouldn't
> require people to have to make a choice between implementing their own
> commands as functions, or methods, in the core of your framework.

Not forcing them to implement in a specific way means that instead of the
support we give, we basically say "no support, do it your own way". (Re:
Point 2 in my initial summary.) So the message is: forget about
commandsets ant their registry, and try to implement it with the full
power of python.

The scenario that this will lead to, is, in my opinion:

Besides that everyone will implement commandsets a bit differently,
everyone will also use commandsets in different ways. Zope will apperantly
stick to its current implementation or similar, to be able to support
transparent changes triggered by events, for which views are inevitable.

Occasinally people will build their own libraries and convenience methods,
for calling up a commandset from a view or from another command set. Both
the definition and the way of calling of a commandset will be different
everywhere. And indeed as you mention, commandsets will cease to exist
because it will not be a central concept in kss any more.

As a result, commandset code and code that uses them will not only be
significantly different between say plain python, zope and django, but 
even within a specific server implementation there will occur to be 
differences between two plugins.

Instead of this, I suggest the keep the current notion of "commandsets" as
it has an important role in the kss model. And try to finalize a python
level implementation and access for it that is both pythonic, easy to use,
and that supports the needs of different server side environments. This
would not force programmers to move all code to commandsets though: we
should let them find the balance between "code that is in the app" that
uses free python, or "code in the commandset" that complies some rules and
is closely attached to client page manipulation and reusable on the level
of kss.


> [snip]
>> For example, the plone-issuePortalMessage command needs to access not
>> only to commands, but also to the content and the request. This could
>> not be handled sanely in the plain-python way, unless an arbitrary
>> number of parameters were passed to the actual command implementations
>> which would be insane.
> 
> I probably don't understand something here, but why can't the *Zope
> specific commands* get these extra parameters, while the underlying core
> commands on top of which you're building them don't? Is this again a
> result of the requirement that commandsets look the same for all Python
> frameworks?
> 
>> However with the "commandset as adapter" pattern, this is easy since
>> the implementation-dependent parameters are all hidden. (In addition
>> every other system may also want to implement its own
>> issuePortalMessage.)
> 
> If you want to use adapters, please use zope.component. I don't want to
> use a reimplementation of zope.component. I also don't think Python
> programmers in general are looking for this.

When I said "adaptation" I was not talking about the registration or
lookup of adapters in the Component Architecture, I was trying to explain
my point by using the adaptation pattern as a generic OOP principle. This
characterized a possible way how the commandsets could be defined, but
this knowledge would not be needed for just using commandsets. (Very
roughly speaking, the views that use commandsets would "inherit by
adaptation" from all plugged in commandsets, similarly to the currently
applied pattern.) This is however just one possibility for implementation
to consider, and could be revisited after the ponts 1., 2., 3. will have
been answered.

We can and we do use the CA within the implementation of kss.zope itself,
but we cannot require programmers to use the CA from code where they want
to define or access a commandset: this way it would be entirely zope
specific - we are moving away from this.

Apart from this, there is no intention of reimplementing any CA in kss:
kss.base has a simple registry for kss plugins that works in plain python
with setuptools, and it has no similarity whatsoever to the CA.


> [snip]
>> If the proposal in the thread becomes the supported way of API for
>> commandsets, first we loose import location independence,
> 
> Location independence doesn't belong in the core. Python has location
> dependence, so there's no requirement to get rid of this dependence in a
> general Python library.
> 
>> then we also
>> won't be able to support existing commandsets code this way.
> 
> That's only because of the requirement you want commands to look like
> each other in different frameworks, while still supporting the different
> requirements of each framework. You're essentially looking to abstract
> over different frameworks, and that's a very risky business. Commands
> already look like each other as they look like Python functions, and
> sets of commands are like modules or classes. The universal way to plug
> into any framework is by writing an actual library.

This is true, and this does not differ principally from the goals I
outlined.  However we do want to support zope commandset similarly to
python, django, etc. commandsets, if the answer to point "2" in the
beginning is Yes.


>> As a result
>> all the code that cannot be supported and inevitably will be
>> implemented out of scope from kss (= differently from the supported
>> way), which means loosing the advantage of platform independence and
>> portability.
> 
> Platform independence for Zope specific code dealing with Zope views,
> contexts and requests?

The breaking down the commandset to smaller commandsets will be different,
but the pattern will be the same on each platform. As a result, if I read
the code of a "django" commandset, I may find it easy to port it to zope,
because I will just need to replace the parts that access the (view)
context, but the code part that calls up other commandsets, will not need
to be changed.

Obviously we need the "writing an actual library" as you refer above, ie.
implement the zope needs to add support to instantiate its own type of
commandsets, and so does django, but the usage will be the same and
definition will be also very similar.


>> At the same time I feel that the "commandset adaptation" pattern would
>> allow to keep best of all worlds, and it would not limit the
>> implementations. It may not be the most perfect option either so it's
>> essential to understand for me to get your reactions on the issue.
> 
> 
> I think it is limiting implementations and makes KSS harder to
> understand for Python programmers. I myself am familiar with adaptation
> but I already am committed to the use of zope.component.

Finally, I come to the conclusion that we are arguing here about "should
commandsets go from kss" as referred in point 1. of my initial summary.

I just would like to add that writing code like

   >>> self.commandsets.core.replaceHTML

in a commandset, does not require the deep understanding of the adaptation
pattern, in case it is used. Neither does inheriting from "KSSCommandSet".
For all the rest, kss users need not we aware of the adaptation, it only
concerns the implementation of the base commandsets and there will be only
two of these implementations at the moment (python, zope). Again, the
implementation question needs to be revisited after answering the initial
points.



So these were my detailed reactions but I think that my initial summary
offers an easier way for the issue to be discussed further.



-- 
Balazs Ree

_______________________________________________
Kss-devel mailing list
Kss-devel@codespeak.net
http://codespeak.net/mailman/listinfo/kss-devel

Reply via email to