# Builtin "export" decorator/function

**URL:** <https://discuss.python.org/t/builtin-export-decorator-function/11757>\
**Category:** Ideas\
**Created:** [November 5, 2021, 8:40pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757 "2021-11-05T20:40:25Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![friedkeenan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/friedkeenan/32/5409_2.png) [@friedkeenan](https://discuss.python.org/u/friedkeenan)\
**Post date:** [November 5, 2021, 8:40pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/1 "2021-11-05T20:40:25Z")

</div>

It would be nice to not have to reiterate names when using ` __all__ `, and instead do something like

```auto
@export
def my_function():
    pass

@export
class MyClass:
    pass

```

which would append `"my_function"` and `"MyClass"` to ` __all__ `, creating it if not already present. This avoids reiterating exported names, and keeps the exportation near objects’ definitions. This is already possible in pure python, but having to import something to export something (cleanly) is strange and clunky. Additionally, it may be desirable to have something like

```auto
my_variable = 1
export(my_variable)

```

which would require reiterating `my_variable` but would keep its exportation near its definition. It might be possible to do this (and keep the decorator functionality) without making it an intrinsic function by calling various `inspect` functions and parsing the relative source to get the variable name and whether `export` is being used as a decorator or not, but that seems _very_ overkill when the interpreter should be able to get all that information much more simply.

---

<div class="post-metadata">

**Author:** ![smontanaro](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/smontanaro/32/1389_2.png) [@smontanaro](https://discuss.python.org/u/smontanaro)\
**Post date:** [November 5, 2021, 10:18pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/2 "2021-11-05T22:18:43Z")

</div>

That’s an interesting idea. Of course, it (I think) would only work for classes and functions (stuff you can decorate). Something like this:

```auto
% python
Python 3.9.1 (default, Dec 11 2020, 14:32:07) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from export_example import export
>>> __all__
autoloading __all__
Error in sys.excepthook:
Traceback (most recent call last):
  File "/home/skip/misc/python/python3/autoload.py", line 35, in _exec
    exec(import_stmt, f_locals, f_globals)
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named ' __all__'

Original exception was:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name ' __all__' is not defined
>>> @export
... def f(a):
... return a
... 
>>> __all__
['f']
>>> 
>>> @export
... class X:
... def __init__ (self):
... pass
... 
>>> __all__
['f', 'X']

```

Here’s the barebones `export_example` module. I’m sure the `export` decorator doesn’t adhere to recommended practice and doesn’t trap any user mistakes, but it demonstrates that it can do what you requested.

```auto
import inspect

def export(func_or_class):
    name = func_or_class. __name__
    caller = inspect.stack()[1]
    globals = caller.frame.f_globals
    if " __all__" not in globals:
        globals[" __all__"] = []
    if name not in globals[" __all__"]:
        globals[" __all__"].append(name)
    return func_or_class

```

---

<div class="post-metadata">

**Author:** ![friedkeenan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/friedkeenan/32/5409_2.png) [@friedkeenan](https://discuss.python.org/u/friedkeenan)\
**Post date:** [November 5, 2021, 10:25pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/3 "2021-11-05T22:25:36Z")

</div>

if all you care about is decorators, I believe it should be as simple as

```auto
import sys

def export(cls_or_func):
    module = sys.modules[cls_or_func. __module__]

    if not hasattr(module, " __all__"):
        module. __all__ = []

    module. __all__.append(cls_or_func. __name__ )

    return cls_or_func

```

I do think it would be nice to be able to do `export(my_variable)` as well, but even just getting the decorator version as a builtin would be very nice.

---

<div class="post-metadata">

**Author:** ![brettcannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/brettcannon/32/34895_2.png) [@brettcannon](https://discuss.python.org/u/brettcannon)\
**Post date:** [November 5, 2021, 10:31pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/4 "2021-11-05T22:31:40Z")

</div>

I think you’re looking for something like [atpublic · PyPI](https://pypi.org/project/atpublic/) .

---

<div class="post-metadata">

**Author:** ![friedkeenan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/friedkeenan/32/5409_2.png) [@friedkeenan](https://discuss.python.org/u/friedkeenan)\
**Post date:** [November 5, 2021, 10:41pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/5 "2021-11-05T22:41:02Z")

</div>

Ah, that is actually really clever using keyword arguments to mimic assignments. I’d still be interested in getting something like this builtin though, as having to import something to neatly export something is cumbersome, not to mention needing a dependency for what I feel should be a pretty basic thing for the language.

---

<div class="post-metadata">

**Author:** ![smontanaro](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/smontanaro/32/1389_2.png) [@smontanaro](https://discuss.python.org/u/smontanaro)\
**Post date:** [November 5, 2021, 11:05pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/6 "2021-11-05T23:05:42Z")

</div>

Even better. Just toss out my little hack.

Which brings up a corollary question. How are people supposed to find out what’s “good” in PyPI? There are so many packages…

---

<div class="post-metadata">

**Author:** ![friedkeenan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/friedkeenan/32/5409_2.png) [@friedkeenan](https://discuss.python.org/u/friedkeenan)\
**Post date:** [January 22, 2023, 9:32pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/7 "2023-01-22T21:32:44Z")

</div>

Bumping this because I still think it would be good to have an `export` facility builtin. I am currently writing code which discovers subclasses of a parent class through the ` __subclasses__ ` method, and so if I create a subclass of the parent class, that class automatically gets “proper” functionality without me needing to export it, which makes it much harder to remember to add it to the ` __all__ ` variable, because everything except actually naming the class works fine without doing so.

And I still very much maintain that

```python
@export
class MyClass:
    pass

```

and

```python
export(MY_VARIABLE=1)

```

are not only much easier to write, but also much easier to read.

---

<div class="post-metadata">

**Author:** ![smontanaro](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/smontanaro/32/1389_2.png) [@smontanaro](https://discuss.python.org/u/smontanaro)\
**Post date:** [January 22, 2023, 10:26pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/8 "2023-01-22T22:26:50Z")

</div>

Have you checked out the `atpublic` package? I realize it’s technically not built-in, but if anything, you’d need to start with a PEP and/or PyPI package to demonstrate its usefulness and iron out any wrinkles. That package offers an `install` function you can use to stuff the decorators/functions into the builtin module saving the effort of requiring import everywhere.

Also, @barry opened a ticket for inclusion years ago which was “closed – won’t fix”.

> <https://github.com/python/cpython/issues/70819>
>
> BPO | \[26632\](https://bugs.python.org/issue26632)
> \--- | :---
> Nosy | @warsaw, @rh…ettinger, @ncoghlan, @bitdancer, @ethanfurman, @berkerpeksag, @vadmium, @zware, @eryksun, @leewz, @jayvdb
> Files | \<li\>\[26632-in-c.diff\](https://bugs.python.org/file42757/26632-in-c.diff "Uploaded as text/plain at 2016-05-06.15:06:44 by @warsaw")\</li\>\<li\>\[26632-in-c-2.diff\](https://bugs.python.org/file42758/26632-in-c-2.diff "Uploaded as text/plain at 2016-05-06.15:26:34 by @warsaw")\</li\>\<li\>\[26632-in-c-3.diff\](https://bugs.python.org/file42760/26632-in-c-3.diff "Uploaded as text/plain at 2016-05-06.17:04:07 by @warsaw")\</li\>\<li\>\[26632-in-c-4.diff\](https://bugs.python.org/file42784/26632-in-c-4.diff "Uploaded as text/plain at 2016-05-08.20:27:21 by @warsaw")\</li\>\<li\>\[26632-in-c-5.diff\](https://bugs.python.org/file42833/26632-in-c-5.diff "Uploaded as text/plain at 2016-05-12.21:27:16 by @warsaw")\</li\>
> 
> \<sup\>\*Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.\*\</sup\>
> 
> \<details\>\<summary\>Show more details\</summary\>\<p\>
> 
> GitHub fields:
> \`\`\`python
> assignee = None
> closed\_at = \<Date 2016-05-31.16:10:36.631\>
> created\_at = \<Date 2016-03-24.02:33:17.124\>
> labels = \['interpreter-core', 'type-feature'\]
> title = '@public - an \_\_all\_\_ decorator'
> updated\_at = \<Date 2017-01-10.06:20:18.743\>
> user = 'https://github.com/warsaw'
> \`\`\`
> 
> bugs.python.org fields:
> \`\`\`python
> activity = \<Date 2017-01-10.06:20:18.743\>
> actor = 'ncoghlan'
> assignee = 'none'
> closed = True
> closed\_date = \<Date 2016-05-31.16:10:36.631\>
> closer = 'barry'
> components = \['Interpreter Core'\]
> creation = \<Date 2016-03-24.02:33:17.124\>
> creator = 'barry'
> dependencies = \[\]
> files = \['42757', '42758', '42760', '42784', '42833'\]
> hgrepos = \[\]
> issue\_num = 26632
> keywords = \['patch'\]
> message\_count = 42.0
> messages = \['262319', '262320', '262321', '262346', '262380', '262383', '262389', '262390', '262396', '262562', '262564', '262568', '262575', '262617', '262697', '262878', '264977', '264980', '264987', '264997', '265001', '265165', '265198', '265207', '265211', '265428', '265910', '265911', '266107', '266108', '266117', '266120', '266152', '266159', '266319', '266403', '266411', '266746', '266756', '267305', '267734', '285092'\]
> nosy\_count = 11.0
> nosy\_names = \['barry', 'rhettinger', 'ncoghlan', 'r.david.murray', 'ethan.furman', 'berker.peksag', 'martin.panter', 'zach.ware', 'eryksun', 'leewz', 'jayvdb'\]
> pr\_nums = \[\]
> priority = 'normal'
> resolution = 'wont fix'
> stage = 'patch review'
> status = 'closed'
> superseder = None
> type = 'enhancement'
> url = 'https://bugs.python.org/issue26632'
> versions = \['Python 3.6'\]
> \`\`\`
> 
> \</p\>\</details\>

At minimum, you’d have to understand and address the issues which kept the concept out of Python at that time.

---

<div class="post-metadata">

**Author:** ![barry](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/barry/32/42_2.png) [@barry](https://discuss.python.org/u/barry)\
**Post date:** [January 23, 2023, 4:40pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/9 "2023-01-23T16:40:45Z")

</div>

> [@friedkeenan](#):
>
> are not only much easier to write, but also much easier to read

Well, as author of [atpublic](https://pypi.org/project/atpublic/) I agree! 😉 It even [works](https://public.readthedocs.io/en/stable/using.html#the-solution) exactly the way you want and can even be [injected in builtins](https://public.readthedocs.io/en/stable/using.html#making-public-and-private-built-ins).

I opened that issue years ago and talked to a bunch of core developers at the time about adding it to the stdlib, but it didn’t get much of a warm welcome, so I dropped it. It’s just as easy to install it from PyPI. The library and its API has been incredibly stable, so it would make a perfect candidate for the stdlib. Just sayin’ 😄

---

<div class="post-metadata">

**Author:** ![friedkeenan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/friedkeenan/32/5409_2.png) [@friedkeenan](https://discuss.python.org/u/friedkeenan)\
**Post date:** [January 23, 2023, 5:02pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/10 "2023-01-23T17:02:00Z")

</div>

Yeah, for now I’ll use atpublic, thank you. I investigated injecting it into the builtins but since my project is a library I would be concerned about it leaking into users’ builtins as well. But yes, the library is workable, though I don’t think it’s at the ideal state, which would be making it a standard builtin.

I think a better name would be `export` (especially when it comes to putting variable names in ` __all__ `, a verb makes more conceptual sense I think), but that’s semantics at that point. In the github issue linked I didn’t see too many reasons to oppose it that seemed substantive to me, perhaps it just needed time to ferment? I’d be interested in whatever help I could give to making it a standard feature, though I haven’t really dabbled in PEPs at all. Maybe people nowadays would be more open to it.

And again, thank you for your work on this.

---

<div class="post-metadata">

**Author:** ![barry](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/barry/32/42_2.png) [@barry](https://discuss.python.org/u/barry)\
**Post date:** [January 23, 2023, 9:26pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/11 "2023-01-23T21:26:45Z")

</div>

> [@friedkeenan](#):
>
> I investigated injecting it into the builtins but since my project is a library I would be concerned about it leaking into users’ builtins as well.

I should probably add that to the documentation as a recommendation against installing in built-ins for a general purpose library.

> [@friedkeenan](#):
>
> I think a better name would be `export`

It’s bikeshedding of course, but to me, `@public` works well when you add the “opposite” `@private` \[1\].

* * *

1. although the use case for even having `@private` isn’t that strong, IMHO

---

<div class="post-metadata">

**Author:** ![smontanaro](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/smontanaro/32/1389_2.png) [@smontanaro](https://discuss.python.org/u/smontanaro)\
**Post date:** [January 23, 2023, 9:55pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/12 "2023-01-23T21:55:21Z")

</div>

> [@barry](#):
>
> > [@friedkeenan](#):
> >
> > I investigated injecting it into the builtins but since my project is a library I would be concerned about it leaking into users’ builtins as well.
> 
> I should probably add that to the documentation as a recommendation against installing in built-ins for a general purpose library.

Hmmm… Maybe an install/remove pair? Your ` __init__.py` could look like (spitballing, not bikeshedding):

```python
from atpublic import install, remove
install()
blah blah blah
remove()

```

OR (now maybe I’m bikeshedding):

```python
from atpublic import public_manager

with public_manager:
    blah blah blah

```

`public_manager` would take care of properly calling install and remove.

---

<div class="post-metadata">

**Author:** ![barry](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/barry/32/42_2.png) [@barry](https://discuss.python.org/u/barry)\
**Post date:** [January 23, 2023, 10:49pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/13 "2023-01-23T22:49:06Z")

</div>

The [only open issue](https://gitlab.com/warsaw/public/-/issues/12) on the project tracker is about providing a context manager, although with different semantics as what you’re suggesting. OTOH, I really wonder how useful the inject-into-builtins feature really is.

---

<div class="post-metadata">

**Author:** ![smontanaro](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/smontanaro/32/1389_2.png) [@smontanaro](https://discuss.python.org/u/smontanaro)\
**Post date:** [January 23, 2023, 11:01pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/14 "2023-01-23T23:01:51Z")

</div>

> [@barry](#):
>
> I really wonder how useful the inject-into-builtins feature really is.

I think only for library authors to make it easier to get the benefit without having to import it in every module of a (potentially large) package.

---

<div class="post-metadata">

**Author:** ![friedkeenan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/friedkeenan/32/5409_2.png) [@friedkeenan](https://discuss.python.org/u/friedkeenan)\
**Post date:** [January 24, 2023, 1:24am UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/15 "2023-01-24T01:24:26Z")

</div>

That issue actually brings up a decent argument for standardization too. If there were a standard `public` then linters would be able to see `public(a=1)` and correctly assume that `a` is now a variable that could be named. Not something that a non-standard library can really offer I think.

Though I do find myself quite fond of that

```python
with public:
    a = 1

```

syntax, though am unsure how it would work implementation-wise.

---

<div class="post-metadata">

**Author:** ![barry](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/barry/32/42_2.png) [@barry](https://discuss.python.org/u/barry)\
**Post date:** [January 24, 2023, 4:37am UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/16 "2023-01-24T04:37:31Z")

</div>

I would suggest engaging in the `atpublic` issue if you want to discuss further.

---

<div class="post-metadata">

**Author:** ![malemburg](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/malemburg/32/50_2.png) [@malemburg](https://discuss.python.org/u/malemburg)\
**Post date:** [January 24, 2023, 9:02am UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/17 "2023-01-24T09:02:18Z")

</div>

> [@barry](#):
>
> OTOH, I really wonder how useful the inject-into-builtins feature really is.

I’d advise against this. I have been doing builtin injections with mxTools back in the days with many new builtins and esp. in larger projects this became a maintenance pain: figuring out whether a module used one of the many new builtins was getting increasingly difficult. It’s better to be explicit and import whatever you need at the top of the module.

---

<div class="post-metadata">

**Author:** ![jsbueno](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jsbueno/32/1210_2.png) [@jsbueno](https://discuss.python.org/u/jsbueno)\
**Post date:** [January 24, 2023, 2:57pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/18 "2023-01-24T14:57:04Z")

</div>

As a late arriver to the thread - the o.p. asking for an “export” just looks like some, with no ofense needed, a “javascriptcism”.  
Python does not _need_ it because it has module namespaces - while Javascript, without such mechanisms, would have all declared functions, classes, etc…in all modules of a project being in a common global namespace.

That said, it is normal to try ways to avoid “WET” stuff and having to repeat things in ` __all__ ` - but ` __all__ ` itself is not that a useful language feature. As a package author, ok, I can do "from subpackage import \*` into a package root, but it is about it - (and personally I avoid that too).

Sorry, just my 2 cents - as for the atpublic that does builtin injection - I’d also say it “smells better” not to be a standard language feature - one missing ZEN is that Python _invites_ for good coding practices. So whlle atpubic is great, and I might even end using it in some projects, having this functionality by default might deteriorate coding practices long term.  
(for one, it would be very hard for a developer, unaided by a specialized IDE, i.e. with just a common code editor, to find out where did a name came from if it was not explicitly imported)

---

<div class="post-metadata">

**Author:** ![barry](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/barry/32/42_2.png) [@barry](https://discuss.python.org/u/barry)\
**Post date:** [January 24, 2023, 4:20pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/19 "2023-01-24T16:20:37Z")

</div>

If you think ` __all__ ` is useful, then `atpublic` can help you keep it in sync. More than that, it is good documentation when reading the code that a symbol is intended to be publicly exported. It doesn’t bother me at all if you find no value in `@public`.

As for `builtins` injection, it’s [pretty trivial](https://gitlab.com/warsaw/public/-/blob/main/src/public/ __init__.py#L8) to add yourself if you really want it. So I’ve opened a [ticket for its removal](https://gitlab.com/warsaw/public/-/issues/14).

---

<div class="post-metadata">

**Author:** ![zware](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/zware/32/58_2.png) [@zware](https://discuss.python.org/u/zware)\
**Post date:** [January 24, 2023, 4:34pm UTC](https://discuss.python.org/t/builtin-export-decorator-function/11757/20 "2023-01-24T16:34:40Z")

</div>

> [@jsbueno](#):
>
> as for the atpublic that does builtin injection

Just of itself, not of what it decorates.

Edit: It seemed like the rest of the paragraph was aimed at the assumption that `@public` could inject the things it decorates into `builtins`, but another reading makes me question that interpretation. So, basically, ignore this post 🙂

[Next page](https://discuss.python.org/t/builtin-export-decorator-function/11757.md?page=2)
