[Web-SIG] Any practical reason type(environ) must be dict (not subclass)?

Jason Madden jason.madden at nextthought.com
Thu Mar 24 11:18:04 EDT 2016


Hi all,


Is there any practical reason that the type of the `environ` object must be exactly `dict`, as specified in PEP3333?  

I'm asking because it was recently pointed out that gevent's WSGI server can sometimes print `environ` (on certain error cases), but that can lead to sensitive information being kept in the server's logs (e.g., HTTP_AUTHORIZATION, HTTP_COOKIE, maybe other things). The simplest and most flexible way to prevent this from happening, not just inadvertently within gevent itself but also for client applications, I thought, was to have `environ` be a subclass of `dict` with a customized `__repr__` (much like WebOb does for MultiDict, and repoze.who does for Identity, both for similar reasons).

Unfortunately, when I implemented that in [0], I discovered that `wsgiref.validator` asserts that type(environ) is dict. I looked up the PEP, and sure enough, PEP 3333 states that environ "must be a builtin Python dictionary (not a subclass, UserDict or other dictionary emulation)." [1]

Background/History
==================

That seemed overly restrictive to me, so I tried to backtrack the history of that language in hopes of discovering the rationale. 

- It was present in the predecessor of PEP 3333, PEP 0333, in the first version committed to the repository in August 2004. [2] 
- Prior to that, it was in both drafts of what would become PEP 0333 posted to this mailing list, again from August 2004: [3], [4].
- The ancestor of those drafts, the "Python Web Container Interface v1.0" was posted in December of 2003 with somewhat less restrictive language: "the environ object *must* be a Python dictionary....The rationale for requiring a dictionary is to maximize portability
between containers" [5].

Now, the discussion on that earliest draft in [5] specifically brought up using other types that implement all the methods of a dictionary, like UserDict.DictMixin [6]. The last post on the subject in that thread seemed to be leaning towards accepting non-dict objects, at least if they were good enough [7].

By the time the draft became recognizable as the precursor to PEP 0333 in [3], the very strict language we have now was in place. That draft, however, specifically stated that it was intended to be compatible with Python 1.5.2. In Python 1.5.2, it wasn't possible to subclass the builtin dict, so imitations, like UserDict.DictMixin, were necessarily imprecise. This was later changed to the much-maligned Python 2.2.2 release [8]; Python 2.2 added the ability to subclass dict, but the language wasn't changed.

Today
=====

Given that today, we can subclass dict with full fidelity, is there still any practical reason not to be able to do so? I'm probably OK with gevent violating the letter of the spec in this regard, so long as there are no practical consequences. I was able to think of two possible objections, but both can be solved:

- Pickling the custom `environ` type and then loading it in another process might not work if the class is not available. I can imagine this coming up with Celery, for example. This is easily fixed by adding an appropriate `__reduce_ex__` implementation.

- Code somewhere relies on `if type(some_object) is dict:` (where `environ` became `some_object`, presumably through several levels of calls), instead of `isinstance(some_object, dict)` or `isinstance(some_object, collections.MutableMapping)`. The solution here is simply to not do that :) Pylint, among other linters, produces warnings if you do.

Can anyone think of any other practical reasons I've overlooked? Is this just a horrible idea for other reasons?

I appreciate any discussion!

Thanks,
Jason

[0] https://github.com/gevent/gevent/compare/secure-environ
[1] https://www.python.org/dev/peps/pep-3333/#specification-details
[2] https://github.com/python/peps/commit/d5864f018f58a35fa787492e6763e382f98b923c#diff-ff370d50af3db062b015d1ef85935779
[3] https://mail.python.org/pipermail/web-sig/2004-August/000518.html
[4] https://mail.python.org/pipermail/web-sig/2004-August/000562.html
[5] https://mail.python.org/pipermail/web-sig/2003-December/000394.html
[7] https://mail.python.org/pipermail/web-sig/2003-December/000401.html
[8] https://mail.python.org/pipermail/web-sig/2004-August/000565.html



More information about the Web-SIG mailing list