[Python-3000] Fwd: Conventions for annotation consumers
rrr at ronadam.com
Sat Aug 19 12:29:31 CEST 2006
Nick Coghlan wrote:
[Clipped other good points.]
> 3. The question has been raised as to whether or not there is a practical way
> for a developer to use annotations that make sense to a *static* analysis tool
> that doesn't actually execute the Python code
> If someone figures out a way to handle the last point *without* compromising
> the ease of use for annotations designed to handle point 1, all well and good.
> Otherwise, I'd call YAGNI. OK, annotations wouldn't be useful for tools like
> pychecker in that case. So be it - to be really useful for a tool like
> pychecker they'd have to be ubiquitous, and that's really not Python any more.
Something I've been looking for is an alternate way to generate function
signatures that are closer to those used in the documents.
Where help(str.find) gives:
S.find(sub [,start [,end]]) -> int
Return the lowest index in S where substring sub is found,
such that sub is contained within s[start,end]. Optional
arguments start and end are interpreted as in slice notation.
Return -1 on failure.
But I am wondering if the annotations could help with both pydoc and
pychecker. Then maybe function specifications could be generated and
look more like ...
str.find(sub:IsString [,start:IsInt [,end:IsInt]]) -> IsInt
instead of just...
[See below where I'm going with this.]
> All that said, I'm still not entirely convinced that function annotations are
> a good idea in the first place - I'm inclined to believe that signature
> objects providing a "bind" method that returns a dictionary mapping the method
> call's arguments to the function's named parameters will prove far more
> useful. With this approach, the 'annotations' would continue to be supplied as
> arguments to decorator factories instead of as expressions directly in the
> function header. IOW, I've yet to see any use case that is significantly
> easier to write with function annotations instead of decorator arguments, and
> several cases where function annotations are significantly worse.
> For one thing, function annotations are useless for decorating a function that
> was defined elsewhere, whereas it doesn't matter where the function came from
> when using decorator arguments. The latter also has a major benefit in
> unambiguously associating each annotation with the decorator that is the
> intended consumer.
I've been thinking about this also. It seems maybe there is an effort
to separate the "meta-data" and the "use of meta-data" a bit too finely.
So What you then get is lock and key effect where the decorators that
use the meta-data and the meta-data itself are separate, but at the same
time, strongly associated by location (module) and developer. This may
be a bit overstated in order to describe it, but I do think it's a
concern as well. But this is also probably more of a style of use issue
than an issue with annotations them selves.
The meta-data can also *be* the validator. So instead of just using
Float, Int, Long, etc... and writing a smart validator to read and check
each of those, You can just call the meta-data directly with each
related argument to validate/modify/or do whatever to it.
So this ...
> def foo(a: [doc("frobination count"),
> b: [type(Number),
> # This can be only 4, 8 or 12
> constrain_values([4,8,12])]) -> type(Number):
could be reduced to ... (removing redundant checks as well)
from metalib import *
def foo( a: [ SetDoc("frobination count"), InRange(3,9) ],
b: InSet([4,8,12]) )
Which isn't too bad. Or even as positional decorator arguments...
from metalib import *
@callmeta( [SetDoc("frobination count"), InRange(3,9)],
def foo(a, b):
Both of these are very similar. The callmeta decorator would be
impemented different, but by using the validators as the meta-data, it
makes both versions easier to read and use. IMHO of course.
The metalib routines could be something (roughly) like...
return type(arg) in (float, int, long)
return type(arg) in (str, unicode)
return arg in list_
def InRange(start, stop):
return start <= arg <= stop
(Or it might be better for them to be objects.)
Anyway it's vary late and I'm probably overlooking something, and I
haven't actually tried any of these so your mileage may vary. ;-)
More information about the Python-3000