On Sun, Apr 19, 2015 at 11:19 AM, Larry Hastings <la...@hastings.org> wrote: > > > On 08/07/2014 09:41 PM, Larry Hastings wrote: > > Well! It's rare that the core dev community is so consistent in its > opinion. I still think "nullable" is totally appropriate, but I'll change > it to "allow_none". > > > (reviving eight-month-old thread) > > In case anybody here is still interested in arguing about this: the Clinic > API may be shifting a bit here. What follows is a quick refresher course on > Argument Clinic, followed by a discussion of the proposed new API. > > Here's an Argument Clinic declaration of a parameter: > s: str() > The parameter is called "s", and it's specifying a converter function called > "str" which handles converting string parameters. The str() converter > itself accepts parameters; since the parameters all have default values, > they're all optional. By default, str() maps directly to the "s" format > unit for PyArg_ParseTuple(), as it does here. > > Currently str() (and a couple other converter functions) accepts a parameter > called "types". "types" is specified as a string, and contains an unordered > set of whitespace-separated strings representing the Python types of the > values this (Clinic) parameter should accept. The default value of "types" > for str() is "str"; the following declaration is equivalent to the > declaration above: > s: str(types="str") > Other legal values for the "types" parameter for the str converter include > "bytes bytearray str" and "robuffer str". Internally the types parameter is > converted into a set of strings; passing it in as a string is a nicety for > the caller's benefit. (It also means that the strings "robuffer str" and > "str robuffer" are considered equivalent.) > > There's a second parameter, currently called "nullable", but I was supposed > to rename it "allow_none", so I'll use that name here. If you pass in > "allow_none=True" to a converter, it means "this (Clinic) parameter should > accept the Python value None". So, to map to the format unit "z", you would > specify: > s: str(allow_none=True) > > And to map to the format unit "z#", you would specify: > s: str(types="robuffer str", allow_none=True, length=True) > > > In hindsight this is all a bit silly. I propose what I think is a much > better API below. > > We should rename "types" to "accept". "accept" should takes a set of types; > these types specify the types of Python objects the Clinic parameter should > accept. For the funny pseudo-types needed in some Clinic declarations > ("buffer", "robuffer", and "rwbuffer"), Clinic provides empty class > declarations so these behave like types too. > > accept={str} is the default for the str() converter. If you want to map to > format unit "z", you would write this: > s: str(accept={str, NoneType}) > (In case you haven't seen it before: NoneType = type(None). I don't think > the name is registered anywhere officially in the standard library... but > that's the name.) > > The upside of this approach: > > Way, way more obvious to the casual reader. "types" was always meant as an > unordered collection of types, but I felt specifying it with strings was > unwieldy and made for poor reading ({'str', 'robuffer'}). Passing it in as > a single string which I internally split and put in a set() was a bad > compromise. But the semantics of this whitespace-delimited string were a > bit unclear, even to the experienced Clinic hacker. This set-of-types > version maps exactly to what the parameter was always meant to accept in the > first place. As with any other code, people will read Clinic declarations > far, far more often than they will write them, so optimizing for clarity is > paramount. > Zen: "There should be one (and preferably only one) obvious way to do it." > We have a way of specifying the types this parameter should accept; > "allow_none" adds a second. > Zen: "Special cases aren't special enough to break the rules". "allow_none" > was really just a special case of one possible type for "types". > > > The downside of this approach: > > You have to know what the default accept= set is for each converter. > Luckily this is not onerous; there are only four converters that need an > "accept" parameter, and their default values are all simple: > > int(accept={int}) > str(accept={str}) > Py_UNICODE(accept={str}) > Py_buffer(accept={buffer}) > > I suggest this is only a (minor) problem when writing a Clinic > declaration. It doesn't affect later readability, which is much more > important. > > It means repeating yourself a little. If you just want to say "I want to > accept None too", you have to redundantly specify the default type(s) > accepted by the converter function. In practice, it's really only redundant > for four or five format units, and they're not the frequently-used ones. > Right now I only see three uses of nullable for the built-in format units > (there are two more for my path_converter) and they're all for the str > converter. > > Yes, we could create a set containing the default types accepted by each > converter function, and just let the caller specify that and incrementally > add +{NoneType}. But this would be far longer than simply redundantly > respecifying the default (e.g. "accept=str_converter.accept_default + > {NoneType}"). > > Sometimes the best thing is just to bite the bullet and accept a little > redundancy. > > > Does "accept" sound good, including accepting "NoneType"?
"Accept" sounds great to me. Allowing a parameter to be None by specifying NoneType is logical and so makes a certain amount of sense. It seems to me that this approach will make it very common to have parameter declarations of the form "int(accept={int, NoneType})" in the stdlib. I'm conflicted whether this is better or worse than "int(allow_none=True)". The former makes it clear that the parameter can accept more than a single type, necessitating additional processing to check what the actual type of the passed value is, which I like. So I'm +0.5 for this at the moment. As for the default set of accepted types for various convertors, if we could choose any syntax we liked, something like "accept=+{NoneType}" would be much better IMO. I'm definitely against repeating the default set of accepted types, since this would require very wide changes whenever the default set of types for a convertor is changed, as well as breaking compatibility for 3rd party libraries using AC. - Tal _______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com