This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author barry
Recipients barry, berker.peksag, eryksun, ethan.furman, jayvdb, leewz, martin.panter, r.david.murray, rhettinger
Date 2016-05-22.20:59:22
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <20160522165919.21a72cea@subdivisions.wooz.org>
In-reply-to <1463728902.04.0.1418816847.issue26632@psf.upfronthosting.co.za>
Content
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!
History
Date User Action Args
2016-05-22 20:59:23barrysetrecipients: + barry, rhettinger, r.david.murray, ethan.furman, berker.peksag, martin.panter, eryksun, leewz, jayvdb
2016-05-22 20:59:23barrylinkissue26632 messages
2016-05-22 20:59:22barrycreate