Barry A. Warsaw added the comment: 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! ---------- _______________________________________ Python tracker <rep...@bugs.python.org> <http://bugs.python.org/issue26632> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com