Constant values defined in interfaces
Hello, I was wondering if the Zope collective had given any consideration to allowing constants to be defined in interfaces. To be clear, these are constant values that make up the protocol defined by the interface. Just to have a concrete example, let's say we're modeling an http response: class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ status = Attribute("HTTP status code for this response.") It might be useful to include in our interface spec what some proper values for status code might be and make them available to applications as static constants on the interface class. A naive implementer might do something like this: class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ HTTP_OK = "200 Ok" HTTP_NOT_FOUND = "404 Not Found" status = Attribute("HTTP status code for this response.") As you can see, the HTTP_OK and HTTP_NOT_FOUND constants are conceptually part of the interface--they help define our contract for http responses. We might expect to then be able to do something like this in application code: response.status = IHttpResponse.HTTP_OK Of course, if you try this currently, Interface will complain: InvalidInterface: Concrete attribute, HTTP_OK I did do some poking around in the source code and in the documentation, such as it is, and didn't see anything like constants in interfaces, but it's possible this use case has already been addressed somehow and I am just ignorant of how, in which case, my apologies, and thanks for telling me the already accepted way to do this. If this hasn't been done yet, I can envision doing something like: from zope.interface import Constant class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ HTTP_OK = Constant("200 Ok", "An HTTP Ok response.") HTTP_NOT_FOUND = Constant("404 Not Found", "An HTTP Not Found response") status = Attribute("HTTP status code for this response.") Using descriptors, the results could be both static and immutable. Does this seem useful to anyone besides me? Anyone who's done much Java programming will recognize that I did not have an original idea here. If there is a general buy in, I would be happy to attempt an implementation and submit a patch. Thanks, Chris PS I did find a hack that will let you accomplish this, although it is a hack rather than a supported feature of the component architecture: class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ status = Attribute("HTTP status code for this response.") IHttpResponse.HTTP_OK = "200 Ok" IHttpResponse.HTTP_NOT_FOUND = "404 Not Found" Because the interface class is decorated after the type is instantiated, the InvalidInterface check is avoided.
On Apr 3, 2009, at 12:35 PM, Chris Rossi wrote:
Hello,
Hi Chris.
I was wondering if the Zope collective had given any consideration to allowing constants to be defined in interfaces. To be clear, these are constant values that make up the protocol defined by the interface.
... FWIW, interfaces.py is often regarded as a contract itself. A current best practice is to define constants and exceptions in that file, and import them from the module.
If this hasn't been done yet, I can envision doing something like:
from zope.interface import Constant
class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ HTTP_OK = Constant("200 Ok", "An HTTP Ok response.") HTTP_NOT_FOUND = Constant("404 Not Found", "An HTTP Not Found response")
status = Attribute("HTTP status code for this response.")
How would that be a win for you (or anybody else) over just putting the constant in the interfaces module? If it is in the interface, that implies we need to "implement" it someplace--the constant will be defined in the interface and in the implementation? Or we would offer automation to copy the values over from the interface to objects that implement the interface?
Using descriptors, the results could be both static and immutable.
Mostly static and immutable, anyway. :-) In Python, there's almost always a way around absolutes like that unless you are working with something in which the constraint has been coded in C. I don't find enforcing a constant's immutability in any way other than relying on a programmer's good sense to be particularly valuable. I have sympathy for "enterprise-y" concerns, in which you have some belt- and-suspenders--we use security proxies for that kind of thing, and appreciate them despite their added pain--but I don't see how a programmer might "forget" to not overwrite an ALL_CAPS constant. And finally, this "Constant" constructor would control the interface, not the implementation, so you'd have to do the static/immutable things elsewhere. So, I'm -1 on "Constant" unless someone gives a convincing reason why it is preferable to putting constants in the module, in which case I will suddenly have a polar shift to +0. :-) Gary
On Fri, Apr 3, 2009 at 1:55 PM, Gary Poster <gary.poster@gmail.com> wrote:
How would that be a win for you (or anybody else) over just putting the constant in the interfaces module?
Standard practice seems to favor putting several interfaces in a single "interfaces" module, so just putting the constants in the module doesn't make it explicit that these constants go with a particular interface. Putting them on the interface itself makes the relationship plain.
If it is in the interface, that implies we need to "implement" it someplace--the constant will be defined in the interface and in the implementation? Or we would offer automation to copy the values over from the interface to objects that implement the interface?
Thanks for bringing that up. I hadn't considered that. In languages with built-in interface support, classes generally inherent constants from the interface, so automatically copying the values over would seem to make sense. Although it's also considered best practice in those languages to refer to the constants by their interface anyway, so one could argue that such a step could be omitted. Zope's implementation of interfaces is unique among implementations I know about, in allowing programmers to mark live objects with an interface, which would introduce an interesting edge case with regards to copying constants over. I'd say if the implementation gets too polluted with edge cases, it's probably not worth it.
Using descriptors, the results could be both static and immutable.
Mostly static and immutable, anyway. :-) In Python, there's almost always a way around absolutes like that unless you are working with something in which the constraint has been coded in C.
Right, but since we have descriptors, why not use them? Maybe I should have used the Colbertian "immutablish". I don't have my heart set on this. I found a use case today and was just thinking about ways to satisfy it. Thanks, Chris
On Apr 3, 2009, at 2:14 PM, Chris Rossi wrote:
On Fri, Apr 3, 2009 at 1:55 PM, Gary Poster <gary.poster@gmail.com> wrote:
How would that be a win for you (or anybody else) over just putting the constant in the interfaces module?
Standard practice seems to favor putting several interfaces in a single "interfaces" module, so just putting the constants in the module doesn't make it explicit that these constants go with a particular interface. Putting them on the interface itself makes the relationship plain.
True. FWIW, I usually do that in the text of the interface if I need to, but I absolutely see where you are coming from.
If it is in the interface, that implies we need to "implement" it someplace--the constant will be defined in the interface and in the implementation? Or we would offer automation to copy the values over from the interface to objects that implement the interface?
Thanks for bringing that up. I hadn't considered that. In languages with built-in interface support, classes generally inherent constants from the interface, so automatically copying the values over would seem to make sense. Although it's also considered best practice in those languages to refer to the constants by their interface anyway, so one could argue that such a step could be omitted. Zope's implementation of interfaces is unique among implementations I know about, in allowing programmers to mark live objects with an interface, which would introduce an interesting edge case with regards to copying constants over. I'd say if the implementation gets too polluted with edge cases, it's probably not worth it.
Agree.
Using descriptors, the results could be both static and immutable.
Mostly static and immutable, anyway. :-) In Python, there's almost always a way around absolutes like that unless you are working with something in which the constraint has been coded in C.
Right, but since we have descriptors, why not use them? Maybe I should have used the Colbertian "immutablish".
:-) Yeah, ok.
I don't have my heart set on this. I found a use case today and was just thinking about ways to satisfy it.
If there's a reasonable story for the connection between interfaces and the objects providing them (that is, what we talked about above), then I wouldn't object. I don't know what that reasonable story might be, though. Gary
Previously Chris Rossi wrote:
I was wondering if the Zope collective had given any consideration to allowing constants to be defined in interfaces. To be clear, these are constant values that make up the protocol defined by the interface. Just to have a concrete example, let's say we're modeling an http response:
class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ status = Attribute("HTTP status code for this response.")
It might be useful to include in our interface spec what some proper values for status code might be and make them available to applications as static constants on the interface class. A naive implementer might do something like this:
class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ HTTP_OK = "200 Ok" HTTP_NOT_FOUND = "404 Not Found"
status = Attribute("HTTP status code for this response.")
This looks like a poor man's enum. I'ld prefer to have a proper enum like thing. Wichert. -- Wichert Akkerman <wichert@wiggy.net> It is simple to make things. http://www.wiggy.net/ It is hard to make things simple.
On Apr 3, 2009, at 7:21 PM, Wichert Akkerman wrote:
This looks like a poor man's enum. I'ld prefer to have a proper enum like thing.
Seems a little different to me. For what it is worth, though, if you do want an enum in zope.schema, Canonical has lazr.enum: http://pypi.python.org/pypi/lazr.enum . Gary
On Fri, Apr 3, 2009 at 7:21 PM, Wichert Akkerman <wichert@wiggy.net> wrote:
Previously Chris Rossi wrote:
I was wondering if the Zope collective had given any consideration to allowing constants to be defined in interfaces. To be clear, these are constant values that make up the protocol defined by the interface. Just to have a concrete example, let's say we're modeling an http response:
class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ status = Attribute("HTTP status code for this response.")
It might be useful to include in our interface spec what some proper values for status code might be and make them available to applications as static constants on the interface class. A naive implementer might do something like this:
class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ HTTP_OK = "200 Ok" HTTP_NOT_FOUND = "404 Not Found"
status = Attribute("HTTP status code for this response.")
This looks like a poor man's enum. I'ld prefer to have a proper enum like thing.
I'm sure you can think of a lot of uses of constant values where an enum wouldn't be appropriate. I don't know that I'd even use an enum in this made up example. Some use cases might be covered by an enum, sure, but that's not really the point here. Thanks, Chris
Using an interface class for a constant "container" would often be handy but it might be an inappropriate use of interface classes. FTR, I do often put constants in an "interfaces.py" module at module scope (if there are more than one related, sometimes in a dictionary or within a non-interface class statement) in order to not feel I need to create some "constants.py" module. Maybe we could just agree that doing so isn't some sort of violation of intent? Chris Rossi wrote:
On Fri, Apr 3, 2009 at 7:21 PM, Wichert Akkerman <wichert@wiggy.net> wrote:
Previously Chris Rossi wrote:
I was wondering if the Zope collective had given any consideration to allowing constants to be defined in interfaces. To be clear, these are constant values that make up the protocol defined by the interface. Just to have a concrete example, let's say we're modeling an http response:
class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ status = Attribute("HTTP status code for this response.")
It might be useful to include in our interface spec what some proper values for status code might be and make them available to applications as static constants on the interface class. A naive implementer might do something like this:
class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ HTTP_OK = "200 Ok" HTTP_NOT_FOUND = "404 Not Found"
status = Attribute("HTTP status code for this response.") This looks like a poor man's enum. I'ld prefer to have a proper enum like thing.
I'm sure you can think of a lot of uses of constant values where an enum wouldn't be appropriate. I don't know that I'd even use an enum in this made up example. Some use cases might be covered by an enum, sure, but that's not really the point here.
Thanks, Chris
------------------------------------------------------------------------
_______________________________________________ Zope-Dev maillist - Zope-Dev@zope.org http://mail.zope.org/mailman/listinfo/zope-dev ** No cross posts or HTML encoding! ** (Related lists - http://mail.zope.org/mailman/listinfo/zope-announce http://mail.zope.org/mailman/listinfo/zope )
On Sat, Apr 4, 2009 at 6:04 PM, Chris McDonough <chrism@plope.com> wrote:
Using an interface class for a constant "container" would often be handy but it might be an inappropriate use of interface classes.
I would argue that it is appropriate, but that's probably related to my experience with languages other than Python.
FTR, I do often put constants in an "interfaces.py" module at module scope (if there are more than one related, sometimes in a dictionary or within a non-interface class statement) in order to not feel I need to create some "constants.py" module. Maybe we could just agree that doing so isn't some sort of violation of intent?
That works, too. Seeing as I how I don't see much interest, I'll consider this something not worth pursuing further. Thanks, Chris
Chris McDonough wrote:
FTR, I do often put constants in an "interfaces.py" module at module scope (if there are more than one related, sometimes in a dictionary or within a non-interface class statement) in order to not feel I need to create some "constants.py" module. Maybe we could just agree that doing so isn't some sort of violation of intent?
+1 Shane
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Chris McDonough wrote:
Using an interface class for a constant "container" would often be handy but it might be an inappropriate use of interface classes. FTR, I do often put constants in an "interfaces.py" module at module scope (if there are more than one related, sometimes in a dictionary or within a non-interface class statement) in order to not feel I need to create some "constants.py" module. Maybe we could just agree that doing so isn't some sort of violation of intent?
If constants are part of the spec for an interface, then placing them at module scope as peers of the interface seems fine to me. In that case, the docstrings of one or methods would presumably refer to them, e.g. describing allowed valeus for an argument. Using a "real" class as a container for them seems over-fancy for my taste. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFJ2M14+gerLs4ltQ4RAlFtAJoC1a429B5HLcsdoMAlQ3ovjuBUjgCgyTlt GiAEVR//SkkiMO60xn+fHvE= =Mzb/ -----END PGP SIGNATURE-----
Tres Seaver wrote:
Chris McDonough wrote:
Using an interface class for a constant "container" would often be handy but it might be an inappropriate use of interface classes. FTR, I do often put constants in an "interfaces.py" module at module scope (if there are more than one related, sometimes in a dictionary or within a non-interface class statement) in order to not feel I need to create some "constants.py" module. Maybe we could just agree that doing so isn't some sort of violation of intent?
If constants are part of the spec for an interface, then placing them at module scope as peers of the interface seems fine to me. In that case, the docstrings of one or methods would presumably refer to them, e.g. describing allowed valeus for an argument.
These are usually not part of any spec for any interface. They are just plain constants, e.g.: BROWSER_NAMESPACE = 'http://namespaces.zope.org/browser' I put items like this at module scope in interfaces.py. Sometimes (if I get really wild), I'll put a dict like the below in "interfaces.py": NAMESPACES = { 'BROWSER':'http://namespaces.zope.org/browser', 'ZOPE':'http://namespaces.zope.org/zope' } And sometimes I'll do the thing you think is too-clever in interfaces.py: class NAMESPACES: BROWSER = 'http://namespaces.zope.org/browser' ZOPE = 'http://namespaces.zope.org/zope' But that's neither here nor there; in every case, the data structure gets defined at module scope in interfaces.py, even though the data structure has nothing to do with the other interfaces in the file. Putting these things in interfaces.py is just taking advantage of the fact that it's fine that other modules depend on it, because it typically depends on nothing except zope.interface itself. I think this pattern is fine; the alternative is creating another module that contains constants which should also not depend on anything else. - C
Chris Rossi wrote:
from zope.interface import Constant
class IHttpResponse(Interface): """Models an HTTP 1.1 response. """ HTTP_OK = Constant("200 Ok", "An HTTP Ok response.") HTTP_NOT_FOUND = Constant("404 Not Found", "An HTTP Not Found response")
status = Attribute("HTTP status code for this response.")
Using descriptors, the results could be both static and immutable.
Does this seem useful to anyone besides me? Anyone who's done much Java programming will recognize that I did not have an original idea here.
-1 I think having a more robust way to spell and namespace constants may be interesting, but what we see above, with a mixed interface/class, is actually not going to work given zope.interface's semantics. You'd end up with an interface that promised an attribute that didn't exist. In Java, you can do: interface IFoo { public static String FOO = "foo"; } class Foo implements IFoo {} f = Foo(); System.out.println(f.FOO); In Python with zope.interface, implements() doesn't mean you inherit attributes. Therefore, you'd need to do: class IFoo(Interface): FOO = Constant("foo") class Foo(object): implements(IFoo) FOO = "foo" # or FOO = IFoo['FOO'].get() or something f = Foo() print f.FOO Without repeating the definition of the FOO constant in each class implementing IFoo, the interface would be promising an attribute that didn't exist. validateInterface() would complain too. In general, I don't have a problem with doing constants that are not, strictly speaking, immutable. I tend to put them in interfaces.py if they are part of the "contract" of my package. Codifying that as good practice is probably a good idea. We certainly could come up with some other way of spelling this, e.g. class FooConstants(Constants): FOO = Contant("Foo") or whatever... maybe some way to mix in zope.schema to to describe the fields in more details. But it feels like a fair amount of situps when you can just do FOO = "Foo" at module level and be done with it. Python is a dynamic language. We don't have privates or final methods or lots of other things that you can spell in a strictly typed, compiled language like Java. Martin -- Author of `Professional Plone Development`, a book for developers who want to work with Plone. See http://martinaspeli.net/plone-book
participants (7)
-
Chris McDonough -
Chris Rossi -
Gary Poster -
Martin Aspeli -
Shane Hathaway -
Tres Seaver -
Wichert Akkerman