[Python-ideas] Adding an "export" decorator in (e.g.) functools

Bill Winslow bunslow at gmail.com
Fri May 9 10:03:02 CEST 2014


On Fri, May 9, 2014 at 2:38 AM, Chris Angelico <rosuav at gmail.com> wrote:

> (By the way: You responded to two different posts in yours, and didn't
> cite either of them. Please keep the original-poster line, such as
> you'll see on the next non-blank line.)


Sorry -- I think this is correct? I'm new to mailing lists :P


> I don't mind the concept of one-line directives to specify things. In
> the same way that you would put "import socket" at the top if you use
> sockets, you put "__all__ = export.get_all()" at the bottom to capture
> all the __all__ entries. It still deduplicates and brings the
> information right to where the function's defined, so there is some
> value in it.


Fair enough, but I'd still like to avoid such statements if possible. I
think we can do better.


> > I'll try and think up an alternative implementation that would work as
> > advertised when imported from another module. (Note that if the code
> itself
> > were copy and pasted, the function would work fine, yet importing it
> fails
> > -- something I have not yet encountered in Python. This also suggests one
> > trivial solution -- import a function that instead exec()'s the
> definition
> > above.) I suspect I might have to learn something about import internals
> to
> > come up with a (better-than-the-trivial) solution. (Copy and pasting
> code is
> > of course unacceptable as well.)
>
> For the exec method to work, it would have to be passed a reference to
> globals() for the calling module, so you can simplify it. I don't know
> how useful it would be, but this ought to work:
>
> # my_tools.py
> def make_all(globls, listname="__all__"):
>     globls[listname] = []
>     def grabber(obj):
>         globls[listname].append(obj)
>         return obj
>     return grabber
>
> # your module
> import my_tools
> export = make_all(globals())
>
> def _private_func_1(): pass
>
> def _private_func_2(): pass
>
> @export
> def public_func_1(): pass
>
> @export
> def public_func_2(): pass
>
>
> This is still a bit magical, in that you assign to the name "export"
> and it actually is for setting __all__, but it's better than exec :)


That's what I had basically just got working, except with exec instead of
just straight up modifying the globals... and if you're going to do that,
you may as well directly assign globls['export'] = grabber :P   (still
better than an exec of course :D).




On the other hand, while testing my version of the above (with exec), I ran
into another issue (or at least I perceive it as such): the import stuff
only respects __all__ *if* we are importing * from the module. If instead
we do "import module as m", the *entire* namespace of the module is made
available in m, even those that start with an underscore.

Can someone please explain the rationale behind that? I would consider this
surprising. Why should "import module" give different results than "from
module import *"? If the latter can be author-limited, why not the former
as well? Even a pointer to relevant documentation would be helpful.

(Basically, I had this idea because I thought that __all__ was way more
important than it apparently is.)

-Bill
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20140509/cf078fc7/attachment.html>


More information about the Python-ideas mailing list