# PEP 726: Module \_\_setattr\_\_ and \_\_delattr\_\_

**URL:** <https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640>\
**Category:** PEPs\
**Created:** [August 31, 2023, 3:34am UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640 "2023-08-31T03:34:40Z")\
**Posts on this page:** 16\
**Page:** 2

<div class="post-metadata">

**Author:** ![nas](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nas/32/536_2.png) [@nas](https://discuss.python.org/u/nas)\
**Post date:** [September 6, 2023, 6:50pm UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/21 "2023-09-06T18:50:48Z")

</div>

I haven’t yet read this PEP but it sounds like my idea for using the [module object as the global namespace](https://discuss.python.org/t/use-module-as-global-namespace-object/2430) would give the desired behaviour. I had prototype that was passing nearly all tests so I think the backwards compatibility issues are not too horrible. A high level summary of my idea is that global lookups do `getattr()` on a module object, rather than ` __getitem__ ()` on a dictionary-like object. The import machinery would pass around module objects rather than the module globals dict, like it currently does. Maybe my idea is not very good or not explained very well. As Guido suggested, I should write a PEP for it.

---

<div class="post-metadata">

**Author:** ![vstinner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/vstinner/32/15130_2.png) [@vstinner](https://discuss.python.org/u/vstinner)\
**Post date:** [September 7, 2023, 7:41am UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/22 "2023-09-07T07:41:28Z")

</div>

The `sys` module API is designed around the idea of setting its attribute, but there are other stdlib modules with some attributes which can be overriden (and it’s explained in their doc, it’s the way to go).

Some more examples:

- [threading.excepthook](https://docs.python.org/dev/library/threading.html#threading.excepthook) doc: _can be overridden_ to control how uncaught exceptions raised by Thread.run() are handled.

- `warnings`

There are some constants which may be interesting to _implement_ as constants: either deny modifying them, or at least disallow _removing_ them. Examples:

- [os.sep](https://docs.python.org/dev/library/os.html#os.sep) and curdir, pardir, altsep, extsep, defpath, linesep, …
- [os.environ](https://docs.python.org/dev/library/os.html#os.environ): is it a feature to be able to override the attribute? or a bug? 🙂
- [math.pi](https://docs.python.org/dev/library/math.html#math.pi) and e, tau, inf, nan
- [http.client.HTTP\_PORT](https://docs.python.org/dev/library/http.client.html#http.client.HTTP_PORT) and HTTPS\_PORT

I’m not declaring a war to monkey patching (even if I not really like that). If the PEP is approved, each of these attributes should be discussed on a case by case basis.

On the other side, it seems like [io.DEFAULT\_BUFFER\_SIZE](https://docs.python.org/dev/library/io.html#io.DEFAULT_BUFFER_SIZE) can be modified on purpose. Just don’t assign it to a string, right? 😉

---

<div class="post-metadata">

**Author:** ![vstinner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/vstinner/32/15130_2.png) [@vstinner](https://discuss.python.org/u/vstinner)\
**Post date:** [October 7, 2023, 4:48pm UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/23 "2023-10-07T16:48:08Z")

</div>

@skirpichev @AA-Turner: Would you mind to update the PEP to mention that the API is limited **on purpose** and list limitations? In short, summarize what has been discussed here. You might also add a few more examples in the PEP, look at examples that I gave.

---

<div class="post-metadata">

**Author:** ![skirpichev](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skirpichev/32/10996_2.png) [@skirpichev](https://discuss.python.org/u/skirpichev)\
**Post date:** [October 8, 2023, 5:06am UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/24 "2023-10-08T05:06:32Z")

</div>

> [@vstinner](#):
>
> update the PEP to mention that the API is limited **on purpose** and list limitations?

I was hoping the major limitation is mentioned in the [Specification](https://peps.python.org/pep-0726/#specification) section:

> [@](#):
>
> Defining module ` __setattr__ ` or ` __delattr__ ` only affects lookups made using the attribute access syntax — directly accessing the module globals (whether by `globals()` within the module, or via a reference to the module’s globals dictionary) is unaffected.

Do you suggest to rephrase this or to extend with an explanation for this limitation (which is very clear, I believe)?

> [@vstinner](#):
>
> You might also add a few more examples in the PEP

Maybe it’s better to replace an artificial example (inspired by the mpmath) in the [Motivation](https://peps.python.org/pep-0726/#motivation) section? It was here just to illustrate all use cases for the proposed interface (read-only attributes, input validation, monitoring attribute change) in one shot.

Or we can add a new section (Discussion?) and add real-world examples from the discussion thread. BTW maybe this is a suitable place also to mention reasons for limitations of the API.

---

<div class="post-metadata">

**Author:** ![vstinner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/vstinner/32/15130_2.png) [@vstinner](https://discuss.python.org/u/vstinner)\
**Post date:** [October 8, 2023, 10:10am UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/25 "2023-10-08T10:10:21Z")

</div>

> [@skirpichev](#):
>
> I was hoping the major limitation is mentioned in the [Specification](https://peps.python.org/pep-0726/#specification) section

No, I think that it should be elaborated. See previous messages.

---

<div class="post-metadata">

**Author:** ![skirpichev](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skirpichev/32/10996_2.png) [@skirpichev](https://discuss.python.org/u/skirpichev)\
**Post date:** [October 31, 2023, 2:54am UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/26 "2023-10-31T02:54:07Z")

</div>

FYI: PEP was updated to address intentional limitations of the API and to include few more examples from the discussion thread.

---

<div class="post-metadata">

**Author:** ![vstinner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/vstinner/32/15130_2.png) [@vstinner](https://discuss.python.org/u/vstinner)\
**Post date:** [November 8, 2023, 1:48pm UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/27 "2023-11-08T13:48:46Z")

</div>

So, what’s the next step? Ask the Steering Council to take a decision on that PEP?

Or are there still open questions or points to discuss?

---

<div class="post-metadata">

**Author:** ![skirpichev](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skirpichev/32/10996_2.png) [@skirpichev](https://discuss.python.org/u/skirpichev)\
**Post date:** [November 8, 2023, 3:40pm UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/28 "2023-11-08T15:40:44Z")

</div>

> So, what’s the next step? Ask the Steering Council to take a decision on that PEP?

I think it’s ready. I would appreciate @AA-Turner opinion on the PEP state, so lets wait few days. Then I’ll open an issue.

Edit:  
JFR, the issue thread has [additional example](https://github.com/python/cpython/issues/106016#issuecomment-1806943020) of using module’s ` __setattr__ ` to handle deprecation warning (actually, something like this was mentioned before in the discussion).

> <https://github.com/python/steering-council/issues/216>
>
> \<!--
> Submitting to the SC is one of the last steps listed in PEP 1: https://pe…ps.python.org/pep-0001/
> Congratulations for making it this far!
> Please fill in the blanks below, and double-check you haven't forgotten something. (Feel free to edit the checklist -- each PEP is different!)
> \--\>
> 
> 
> Please consider PEP 726 -- \`\`Module \_\_setattr\_\_ and \_\_delattr\_\_\`\`
> https://peps.python.org/pep-0726/
> 
> \* \[x\] The PEP has been discussed in threads listed in its \`Post-History\` header
>   
> \* \[x\] The PEP was announced on \[Discuss\](https://discuss.python.org/c/peps/19) (link in \`Post-History\`)
>   
> \* \[x\] The PEP includes all relevant \[Suggested Sections\](https://peps.python.org/pep-0012/#suggested-sections)
> \* \[x\] The PEP includes endorsements from the projects/groups/people it helps
>   
> \* \[x\] The PEP has a CODEOWNERS entry

---

<div class="post-metadata">

**Author:** ![joaoe](https://avatars.discourse-cdn.com/v4/letter/j/f475e1/32.png) [@joaoe](https://discuss.python.org/u/joaoe)\
**Post date:** [November 13, 2023, 10:09pm UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/29 "2023-11-13T22:09:53Z")

</div>

Hi.  
I’m following up on the comment by Sergey.

There is an open issue regarding the warnings module not being thread safe, namely the bit regarding `catch_warnings()` and `monkeypatching` of the `showwarning` function. I have commented on that issue and came up with a very rought draft branch. [1]

This is unfortunatelly an anti-pattern with some bits of the Python stdlib, where specific behavior is controlled by monkeypatching global symbols. This is unsafe.

Compare for instance `sys.excepthook` vs `atexit.register(callback)`. The latter is a good example of how to setup hooks or callbacks. The first one not so much.

Back to `warnings`, fixing that module would require forbidding all monkeypatching (and many other implementation details with a stack of `catch_warnings` objects, not relevant for this discussion).

However, that module is as you well know quite a central part of the python stdlib, and any change in its behavior needs 1. to go through a deprecation cycle, plus, such changes should not introduce any 2. performance penalties.

Therefore, with these concerns, 1. requires a ` __setattr__ ` hook to warn that monkeypatching that function will no longer be supported, then in a latter version, raise an Exception. The performance part 2. kind of negates the choice of subclassing the `warnings` module with a class with a setter, because of the perormance penalty when accessing attributes.

To conclude, the proposal of a ` __setattr__ ` in modules is something that would not have that wide use, but there are at least some niche use cases, including these problems with `warnings` that I mentioned which do not have any other good options. Hopefully, people should not be designing APIs that require patching global functions, but that ship has sailed long ago.  
Moreover, I personally like when APIs are complete, symmetric and consistent, so it’s only natural that if a module object has a ` __getattr__ ` that it would too have a ` __setattr__ `.

Thank you.

PS: I think that there should be a PEP about removing all monkeypatching from stdlib and change those APIs to use register callback functions. It would not be much work implementation wise, but it would be a bit bureocratic.

[1] [warnings.catch\_warnings is async-unsafe · Issue #91505 · python/cpython · GitHub](https://github.com/python/cpython/issues/91505)

---

<div class="post-metadata">

**Author:** ![beauxq](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/beauxq/32/15042_2.png) [@beauxq](https://discuss.python.org/u/beauxq)\
**Post date:** [February 6, 2024, 2:12am UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/30 "2024-02-06T02:12:46Z")

</div>

First, a minor question about this comment referenced in the PEP:

> [@vstinner](#):
>
> [to ensure] that `sys.modules` type is always a `list`

That should be “`dict`” right?

```python
>>> type(sys.modules)
<class 'dict'>

```

If so, that should be fixed in the PEP.

But my bigger concern with that statement in the PEP:

> [@skirpichev](#):
>
> directly accessing the module globals ([…] via a reference to the module’s globals dictionary) is unaffected

Reading more of the discussion, it looks like I could do this?

```python
import sys

sys.modules['sys']. __dict__ ['modules'] = 123

```

If I can bypass the ` __setattr__ `, doesn’t that mean we still can’t “make sure that `sys.modules` is always a `list`” (or `dict`)?

That would make the motivation for this a lot weaker.  
When I read this:

> [@vstinner](#):
>
> If we can make sure that `sys.modules` always exist and its type is always `list`, the C code using `sys.modules` can avoid some error checking and use more efficient code.

That sounded good to me, to get the performance improvement from the C code.  
But if there’s a way to bypass it, then we can’t avoid the error checking in the C code.

And it seems kind of misleading for the PEP to say that we can “ensure” when we can’t ensure it.

It makes accidental change less likely, but it doesn’t ensure it.

---

<div class="post-metadata">

**Author:** ![skirpichev](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skirpichev/32/10996_2.png) [@skirpichev](https://discuss.python.org/u/skirpichev)\
**Post date:** [February 6, 2024, 3:52am UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/31 "2024-02-06T03:52:40Z")

</div>

> [@beauxq](#):
>
> That should be “`dict`” right?

Yep, good catch. Thanks!

> [@beauxq](#):
>
> Reading more of the discussion, it looks like I could do this?
> 
> ```python
> import sys
> sys.modules['sys']. __dict__ ['modules'] = 123
> 
> ```

Yes, this is correct.

> [@beauxq](#):
>
> If I can bypass the ` __setattr__ `, doesn’t that mean we still can’t “make sure that `sys.modules` is always a `list`” (or `dict`)?

True. But we can’t catch all cases when someone want to shot own leg.

> **For example (maybe not a better one, but), how can we be sure that all instances of some class have its methods?**
>
> ```pycon
> >>> class Foo:
> ... def spam(self):
> ... pass
> ... 
> >>> del Foo.spam
> >>> bar = Foo()
> >>> # del Foo.spam # or here...
> >>> isinstance(bar, Foo)
> True
> >>> bar.spam()
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> AttributeError: 'Foo' object has no attribute 'spam'
> 
> ```

> **In a similar way we could easily break other invariants that might be assumed by some code, e.g. in the stdlib...**
>
> ```pycon
> >>> import fractions
> >>> from fractions import Fraction as F
> >>> half = F(1, 2)
> >>> half._denominator = 'spam'
> >>> half + half
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "/usr/local/lib/python3.12/fractions.py", line 615, in forward
> return monomorphic_operator(a, b)
> ^^^^^^^^^^^^^^^^^^^^^^^^^^
> File "/usr/local/lib/python3.12/fractions.py", line 714, in _add
> g = math.gcd(da, db)
> ^^^^^^^^^^^^^^^^
> TypeError: 'str' object cannot be interpreted as an integer
> 
> ```

> [@beauxq](#):
>
> It makes accidental change less likely, but it doesn’t ensure it.

Maybe correct wording is: “It makes accidental change impossible.”?

---

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [February 28, 2024, 5:21pm UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/32 "2024-02-28T17:21:44Z")

</div>

The Python Steering Council has discussed [PEP-726 – Module ` __setattr__ ` and ` __delattr__ `](https://peps.python.org/pep-0726/) and decided to Reject it.

We did not see clear consensus in the community about use cases such as read-only module attributes, and we are concerned that going further down the route of modules behaving semi-class-like may lead to future performance optimization challenges and confusing semantics. We do not find there to be a strong need for having read-only module attributes, and even if there is one, this is not the ideal solution to that problem. Preventing one possible mis-use of modules did not seem compelling enough.

There are already workarounds that people can use to achieve similar results. That some people consider those workarounds such as replacing a module with another object to be gross is not considered a problem as it is an uncommon need.

Thanks for the PEP and thorough discussion,  
– The 2024 PSC

---

<div class="post-metadata">

**Author:** ![skirpichev](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skirpichev/32/10996_2.png) [@skirpichev](https://discuss.python.org/u/skirpichev)\
**Post date:** [February 28, 2024, 10:43pm UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/33 "2024-02-28T22:43:34Z")

</div>

Thanks to all for discussions, including previous one, and help. Especially to @oscarbenjamin and to @vstinner for a lot of advocacy (and pr review). Sorry that my efforts led to the death of the idea.

> [@gpshead](#):
>
> behaving semi-class-like may lead to future performance optimization challenges

Very well, instead we will live with existing performance penalties. Another attempt to fix this (issue #103951) seems to be fallen as well.

---

<div class="post-metadata">

**Author:** ![vstinner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/vstinner/32/15130_2.png) [@vstinner](https://discuss.python.org/u/vstinner)\
**Post date:** [February 29, 2024, 5:30pm UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/34 "2024-02-29T17:30:36Z")

</div>

> [@gpshead](#):
>
> The Python Steering Council has discussed [PEP-726 – Module ` __setattr__ ` and ` __delattr__ `](https://peps.python.org/pep-0726/) and decided to Reject it.

As a supporter of the idea, it makes me sad, but it’s great to have a full PEP which writes down the rationale and limitations of this design. Thanks a lot Sergey for your great work on this topic!

---

<div class="post-metadata">

**Author:** ![joaoe](https://avatars.discourse-cdn.com/v4/letter/j/f475e1/32.png) [@joaoe](https://discuss.python.org/u/joaoe)\
**Post date:** [March 6, 2024, 8:08pm UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/35 "2024-03-06T20:08:28Z")

</div>

> [@gpshead](#):
>
> The Python Steering Council has discussed [PEP-726 – Module ` __setattr__ ` and ` __delattr__ `](https://peps.python.org/pep-0726/) and decided to Reject it.

Oh, too bad. Hopefully, the performance hit when using a custom class for a module will be fixed then.

---

<div class="post-metadata">

**Author:** ![davidism](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/davidism/32/3295_2.png) [@davidism](https://discuss.python.org/u/davidism)\
**Post date:** [March 6, 2024, 9:22pm UTC](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640/36 "2024-03-06T21:22:53Z")

</div>

Best way to ensure it’s fixed is to research it and submit a patch 😉

[Previous page](https://discuss.python.org/t/pep-726-module-setattr-and-delattr/32640.md?page=1)
