Repository: falconry/falcon

Issue:
Make it easier to add custom attributes to slotted classes 
Users of the framework sometimes want to assign additional variables to the `Request`, `Response`, or other "slotted" classes. This can be allowed without hurting efficiency by adding a `'__dict__'` string to the `__slots__` declarations.

From the Python [data model docs](https://docs.python.org/2/reference/datamodel.html#slots): 

> Without a __dict__ variable, instances cannot be assigned new variables not listed in the __slots__ definition. Attempts to assign to an unlisted variable name raises AttributeError. If dynamic assignment of new variables is desired, then add '__dict__' to the sequence of strings in the __slots__ declaration.

I did a quick test and adding '__dict__' does not decrease performance for accessing the other slotted variables in 2.7 and 3.5. 

Question: Does this enable an anti-pattern? Would we rather that apps use `req.context`? Would it be better to have a context type that supports attribute-based access? (e.g., `req.context.foo` == `req.context['foo']`)?

Discussion:
codecov-io: ## [Current coverage](https://codecov.io/gh/falconry/falcon/pull/868?src=pr) is 100% (diff: 100%)
> Merging [#868](https://codecov.io/gh/falconry/falcon/pull/868?src=pr) into [master](https://codecov.io/gh/falconry/falcon/branch/master?src=pr) will not change coverage

```diff
@@           master   #868   diff @@
====================================
  Files          30     30          
  Lines        1864   1864          
  Methods         0      0          
  Messages        0      0          
  Branches      305    305          
====================================
  Hits         1864   1864          
  Misses          0      0          
  Partials        0      0          
```

> Powered by [Codecov](https://codecov.io?src=pr). Last update [fd5a0ba...dbf09ee](https://codecov.io/gh/falconry/falcon/compare/fd5a0ba5874c68e2a61470180455c8e05c3d3d66...dbf09ee6361a2abaa9ceaae5e4fedc77fefad291?src=pr)

Patch:
diff --git a/falcon/request.py b/falcon/request.py
index 30a2bd3..dd49159 100644
--- a/falcon/request.py
+++ b/falcon/request.py
@@ -243,6 +243,7 @@ class Request(object):
         'options',
         '_cookies',
         '_cached_access_route',
+        '__dict__',
     )
 
     # Child classes may override this
diff --git a/falcon/response.py b/falcon/response.py
index 44d3d5d..fd25479 100644
--- a/falcon/response.py
+++ b/falcon/response.py
@@ -116,6 +116,7 @@ class Response(object):
         'stream',
         'stream_len',
         'context',
+        '__dict__',
     )
 
     # Child classes may override this


Test patch:
diff --git a/tests/test_slots.py b/tests/test_slots.py
new file mode 100644
index 0000000..665ea32
--- /dev/null
+++ b/tests/test_slots.py
@@ -0,0 +1,22 @@
+from falcon import Request, Response
+import falcon.testing as testing
+
+
+class TestSlots(testing.TestBase):
+
+    def test_slots_request(self):
+        env = testing.create_environ()
+        req = Request(env)
+
+        try:
+            req.doesnt = 'exist'
+        except AttributeError:
+            self.fail('Unable to add additional variables dynamically')
+
+    def test_slots_response(self):
+        resp = Response()
+
+        try:
+            resp.doesnt = 'exist'
+        except AttributeError:
+            self.fail('Unable to add additional variables dynamically')