I've seen the "schema.One()" syntax around a few times since PyCon, and there's always been something that bothers me slightly about it. I think what it comes down to is that you're almost always defining "one" of something - and the rest of the time is divided between lists and dicts.
Actually, the choice of names is intended to subtly encourage the use of 'Many' (set) in preference to 'Sequence' (list).
What about turning this syntax on its head and saying class ....: body = Types.Lob(displayName = "body", doc="whee", cardinality=schema.One)
(where of course, cardinality would default to One)
I think you're forgetting about kinds here; calling a kind class creates an item of that kind, so there's no way to distinguish that from this. (i.e., replace 'Lob' with 'Contact' and see what you get.)
Also, this approach has no way to make a forward reference to a kind that hasn't been defined yet. And, if I understand correctly, even for value types (i.e. non-kinds) it would be quite difficult and perhaps horrendous to abuse the Types.* classes to turn them into descriptor classes or factories. And of course the 'cardinality=' part adds extra typing compared to my proposal, while burying the cardinality information.
One of the benefits of my proposed format for readbility is that it puts the attribute name, cardinality, and type in close proximity: name = cardinality(type). All other aspects occur later, so you can skim just the first part of each to get a basic grasp of the schema.
Arguably, you could try to change this to any of six sequences:
1. name = cardinality(type) 2. cardinality(name,type) 3. cardinality(type,name) 4. name = type(cardinality) 5. type(name,cardinality) 6. type(cardinality,name)
Alternatives 4, 5, and 6 all have ambiguity and forward reference problems, because you can't refer to a type that doesn't exist yet, and because calling a type normally creates an instance of that type. Alternatives 2 and 3 require the name to be quoted by the user, and have to have some kind of "magic" occurring to get the attribute to be bound to that name in the enclosing class. By "magic", I mean that it is not obvious that something is being created and bound to that attribute (versus options 1 and 4 where the name is clearly being bound by normal Python rules).
So, I think to improve any further on the syntactical arrangement of the API, you would need a more outside-the-box innovation than just rearranging the symbols in use. I don't know what such an innovation would look like, though, or else I'd have done it. :)
On a related note, having reasonable defaults for things is really important.. for instance, it would be really nice if the displayName actually defaulted to the name, in case you didn't define one.
Sure, this would be pretty easy. In Spike, I thought about actually having it parse CamelCase names and split them into words to make the displayName, such that the displayName of an attribute called "displayName" would be "Display Name" if not otherwise specified. It just wasn't a high priority for Spike's goals, but it has a higher priority in this context, so I'll add it to my list.
Spike also takes the doc, display name, and other attributes to create a fancier documentation string that displays when you use Python's help() function or pydoc utility, and I plan to port that feature too.
It just seems like this simplifies simple single-cardinality instances to something really easy:
body = Types.Lob()
Well, the only difference in character count here is in the length of "schema.One", so nothing stops you from doing:
from schema import One, Item
from repository.schema.Types import Lob class Whatever(Item):
body = One(Lob)if you would like to do less typing. I just have a personal bias towards using a single level of "." qualification when using lots of names from the same module or package.
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
Open Source Applications Foundation "Dev" mailing list http://lists.osafoundation.org/mailman/listinfo/dev
