Message266107
On May 20, 2016, at 07:21 AM, Franklin? Lee wrote:
>I am iffy about using ``public`` to define other values. That part might be
>considered unpythonic.
It's a bit of a stretch. I like it for the convenience, and the
implementation is simple, but if e.g. Guido disliked this part of it, I'd be
okay dropping it. I think the use on non-__name__'d things is rare enough
that a little inconvenience wouldn't be a fatal flaw.
> - ``__module__`` is not reliable. ``functools.wraps`` changes it. (Why
> - does it do that, though?)
I don't know, but what practical effect will this have? I.e. under what
conditions would you @public wrap a @functools.wraps function and want it to
show up in __all__? Do you have a specific use case?
Also note that this is a subtle difference between the C and Python
implementations. I actually expect that if this were adopted for Python 3.6,
we'd pretty much only use the C version. In the standalone package, I'm
including the Python versions mostly just for convenience in environments
without a compiler (though maybe a built wheel for some platforms would be
useful).
> - If `__all__` isn't a list, you'd have to make it a list before you mess
> - with it. (Is this possible?)
It would be possible. I'd just do the equivalent of::
__all__ = list(__all__)
But I actually think that best practice would be not to set __all__ explicitly
if you're going to use @public. If you really want it to be immutable, you'd
put the following at the bottom of your module:
__all__ = tuple(__all__)
For now I've added some usage caveats that describe these points.
>> > On the down side, you know somebody is going to @public a class' method --
>> > how do we check for that?
>>
>> Do we need to? Consenting adults and __all__.
>
>It's a silent error waiting to happen. If you never use ``import *`` on it
>(e.g. because it's your main file), you won't get the error message. Things
>will work "as expected" (your methods are class-public!) until you give a
>method the same name as a builtin or something you imported or defined
>earlier. When that happens, the error message will have nothing to do with
>the problem.
>
>It might be detectable using ``thing.__qualname__ != thing.__name__``, but
>this disallows functions decorated without updating __qualname__, and
>static/class methods exposed in a module's interface.
>
>It might be detectable by checking, on the callstack, whether you're in a
>module load or a class definition.
Sure, we could probably add some heuristics, but I still don't see the need
for the extra complexity. The error will be far from the declaration, but the
exception should make it relatively obvious what's going on. I also really
don't think folks would naturally be inclined to put @public on anything but a
top-level definition. You wouldn't ever put such a thing in your __all__ so
why would you decorate it with @public?
In any case, I've added a usage caveat for this case too.
>How many public module values aren't enum-type constants?
These days I bet they are quite a bit more common than enum-types, although I
agree that enums are great and we should use more of them! Just historically
speaking I don't know how many packages have converted all their constants
over to enums.
Also, I know that I have several cases where constants are actually
instances. They could be marker objects like::
MARKER = object()
or system globals::
configuration = Configuration()
I'd want both of those in __all__.
>It could be useful to be able to dump an enum into a module's space. I mean,
>a canonical way. With that, maybe maintaining module-level constants in
>__all__ isn't that big a deal.
>
> # Rather than:
> globals().update(MyEnum.__members__)
> __all__.extend(MyEnum.__members__)
> # Perhaps allow:
> enum.dump_namespace(MyEnum, globals())
It's an interesting thought.
>About the cost paid at every load:
> - Should tools update __all__ for you, and comment out the ``@public``s?
> - If so, how would they deal with public non-callable values?
> - When compiling to .pyc, should the compiler remove ``@public`` calls
> and explicitly add the values to __all__?
Why? Aren't those one-time costs only borne when the module is originally
imported?
>API:
> - Alternative syntax for constants, requiring less frame hackery:
> public(globals(), x=1, y=2, z=3)
Possibly. Since this is really only relevant for the pure-Python
implementation, I'm not as keen on the extra cruft.
> - Naming: Is it really "public"? Some names might be public but not in
> - __all__.
What does it mean for a name to be "public but not in __all__"?
I'll also note that since the basic API has been independently invented at
least three times, and all of them use @public, it seems like the obvious
choice. ;)
>P.S. Typo in the ReadTheDocs. ``py_install`` should be a function call, right?
>
> >>> from public import py_install
> >>> py_install
Fixed, thanks!
>P.S.: Would OrderedSet (which doesn't exist) be the ideal type for __all__? I
>mean, if you had to use someone else's __all__, not if you had to maintain
>it.
It's an interesting thought, but I don't know if it's enough of a use case to
add collections.OrderedSet. Traditionally __all__ has been a list, with some
relatively recent moves to making it a tuple.
Thanks for the interesting and useful feedback! |
|
Date |
User |
Action |
Args |
2016-05-22 20:59:23 | barry | set | recipients:
+ barry, rhettinger, r.david.murray, ethan.furman, berker.peksag, martin.panter, eryksun, leewz, jayvdb |
2016-05-22 20:59:23 | barry | link | issue26632 messages |
2016-05-22 20:59:22 | barry | create | |
|