Skip to content

Commit

Permalink
Merge pull request #2823 from mmerickel/remove-custom-settings-object
Browse files Browse the repository at this point in the history
make settings only accessible using dictionary lookup
  • Loading branch information
mmerickel authored Nov 20, 2016
2 parents 14fcc74 + 7b58c0f commit 3c5db58
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 142 deletions.
13 changes: 13 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ Backward Incompatibilities
encoding via ``Accept-Encoding`` request headers.
See https://github.com/Pylons/pyramid/pull/2810

- Settings are no longer accessible as attributes on the settings object
(e.g. ``request.registry.settings.foo``). This was deprecated in Pyramid 1.2.
See https://github.com/Pylons/pyramid/pull/2823

Features
--------

Expand Down Expand Up @@ -80,6 +84,15 @@ Features
as soon as possible before importing the rest of pyramid.
See https://github.com/Pylons/pyramid/pull/2797

- Pyramid no longer copies the settings object passed to the
``pyramid.config.Configurator(settings=)``. The original ``dict`` is kept.
See https://github.com/Pylons/pyramid/pull/2823

- The csrf trusted origins setting may now be a whitespace-separated list of
domains. Previously only a python list was allowed. Also, it can now be set
using the ``PYRAMID_CSRF_TRUSTED_ORIGINS`` environment variable similar to
other settings. See https://github.com/Pylons/pyramid/pull/2823

Bug Fixes
---------

Expand Down
4 changes: 0 additions & 4 deletions TODO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ Nice-to-Have
Future
------

- 1.6: turn ``pyramid.settings.Settings`` into a function that returns the
original dict (after ``__getattr__`` deprecation period, it was deprecated
in 1.2).

- 1.6: Remove IContextURL and TraversalContextURL.

- 1.9: Remove set_request_property.
Expand Down
171 changes: 53 additions & 118 deletions pyramid/config/settings.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import os
import warnings

from zope.interface import implementer

from pyramid.interfaces import ISettings

from pyramid.settings import asbool
from pyramid.settings import asbool, aslist

class SettingsConfiguratorMixin(object):
def _set_settings(self, mapping):
if not mapping:
if mapping is None:
mapping = {}
settings = Settings(mapping)
self.registry.settings = settings
Expand Down Expand Up @@ -54,118 +49,58 @@ def get_settings(self):
return self.registry.settings


@implementer(ISettings)
class Settings(dict):
def Settings(d=None, _environ_=os.environ, **kw):
""" Deployment settings. Update application settings (usually
from PasteDeploy keywords) with framework-specific key/value pairs
(e.g. find ``PYRAMID_DEBUG_AUTHORIZATION`` in os.environ and jam into
keyword args)."""
# _environ_ is dep inj for testing
def __init__(self, d=None, _environ_=os.environ, **kw):
if d is None:
d = {}
dict.__init__(self, d, **kw)
eget = _environ_.get
config_debug_all = self.get('debug_all', '')
config_debug_all = self.get('pyramid.debug_all', config_debug_all)
eff_debug_all = asbool(eget('PYRAMID_DEBUG_ALL', config_debug_all))
config_reload_all = self.get('reload_all', '')
config_reload_all = self.get('pyramid.reload_all', config_reload_all)
eff_reload_all = asbool(eget('PYRAMID_RELOAD_ALL', config_reload_all))
config_debug_auth = self.get('debug_authorization', '')
config_debug_auth = self.get('pyramid.debug_authorization',
config_debug_auth)
eff_debug_auth = asbool(eget('PYRAMID_DEBUG_AUTHORIZATION',
config_debug_auth))
config_debug_notfound = self.get('debug_notfound', '')
config_debug_notfound = self.get('pyramid.debug_notfound',
config_debug_notfound)
eff_debug_notfound = asbool(eget('PYRAMID_DEBUG_NOTFOUND',
config_debug_notfound))
config_debug_routematch = self.get('debug_routematch', '')
config_debug_routematch = self.get('pyramid.debug_routematch',
config_debug_routematch)
eff_debug_routematch = asbool(eget('PYRAMID_DEBUG_ROUTEMATCH',
config_debug_routematch))
config_debug_templates = self.get('debug_templates', '')
config_debug_templates = self.get('pyramid.debug_templates',
config_debug_templates)
eff_debug_templates = asbool(eget('PYRAMID_DEBUG_TEMPLATES',
config_debug_templates))
config_reload_templates = self.get('reload_templates', '')
config_reload_templates = self.get('pyramid.reload_templates',
config_reload_templates)
eff_reload_templates = asbool(eget('PYRAMID_RELOAD_TEMPLATES',
config_reload_templates))
config_reload_assets = self.get('reload_assets', '')
config_reload_assets = self.get('pyramid.reload_assets',
config_reload_assets)
reload_assets = asbool(eget('PYRAMID_RELOAD_ASSETS',
config_reload_assets))
config_reload_resources = self.get('reload_resources', '')
config_reload_resources = self.get('pyramid.reload_resources',
config_reload_resources)
reload_resources = asbool(eget('PYRAMID_RELOAD_RESOURCES',
config_reload_resources))
# reload_resources is an older alias for reload_assets
eff_reload_assets = reload_assets or reload_resources
locale_name = self.get('default_locale_name', 'en')
locale_name = self.get('pyramid.default_locale_name', locale_name)
eff_locale_name = eget('PYRAMID_DEFAULT_LOCALE_NAME', locale_name)
config_prevent_http_cache = self.get('prevent_http_cache', '')
config_prevent_http_cache = self.get('pyramid.prevent_http_cache',
config_prevent_http_cache)
eff_prevent_http_cache = asbool(eget('PYRAMID_PREVENT_HTTP_CACHE',
config_prevent_http_cache))
config_prevent_cachebust = self.get('prevent_cachebust', '')
config_prevent_cachebust = self.get('pyramid.prevent_cachebust',
config_prevent_cachebust)
eff_prevent_cachebust = asbool(eget('PYRAMID_PREVENT_CACHEBUST',
config_prevent_cachebust))
csrf_trusted_origins = self.get("pyramid.csrf_trusted_origins", [])
eff_csrf_trusted_origins = csrf_trusted_origins

update = {
'debug_authorization': eff_debug_all or eff_debug_auth,
'debug_notfound': eff_debug_all or eff_debug_notfound,
'debug_routematch': eff_debug_all or eff_debug_routematch,
'debug_templates': eff_debug_all or eff_debug_templates,
'reload_templates': eff_reload_all or eff_reload_templates,
'reload_resources':eff_reload_all or eff_reload_assets,
'reload_assets':eff_reload_all or eff_reload_assets,
'default_locale_name':eff_locale_name,
'prevent_http_cache':eff_prevent_http_cache,
'prevent_cachebust':eff_prevent_cachebust,
'csrf_trusted_origins':eff_csrf_trusted_origins,

'pyramid.debug_authorization': eff_debug_all or eff_debug_auth,
'pyramid.debug_notfound': eff_debug_all or eff_debug_notfound,
'pyramid.debug_routematch': eff_debug_all or eff_debug_routematch,
'pyramid.debug_templates': eff_debug_all or eff_debug_templates,
'pyramid.reload_templates': eff_reload_all or eff_reload_templates,
'pyramid.reload_resources':eff_reload_all or eff_reload_assets,
'pyramid.reload_assets':eff_reload_all or eff_reload_assets,
'pyramid.default_locale_name':eff_locale_name,
'pyramid.prevent_http_cache':eff_prevent_http_cache,
'pyramid.prevent_cachebust':eff_prevent_cachebust,
'pyramid.csrf_trusted_origins':eff_csrf_trusted_origins,
}

self.update(update)

def __getattr__(self, name):
try:
val = self[name]
# only deprecate on success; a probing getattr/hasattr should not
# print this warning
warnings.warn(
'Obtaining settings via attributes of the settings dictionary '
'is deprecated as of Pyramid 1.2; use settings["foo"] instead '
'of settings.foo',
DeprecationWarning,
2
)
return val
except KeyError:
raise AttributeError(name)

if d is None:
d = {}
d.update(**kw)

eget = _environ_.get
def expand_key(key):
keys = [key]
if not key.startswith('pyramid.'):
keys.append('pyramid.' + key)
return keys
def S(settings_key, env_key=None, type_=str, default=False):
value = default
keys = expand_key(settings_key)
for key in keys:
value = d.get(key, value)
if env_key:
value = eget(env_key, value)
value = type_(value)
d.update({k: value for k in keys})
def O(settings_key, override_key):
for key in expand_key(settings_key):
d[key] = d[key] or d[override_key]

S('debug_all', 'PYRAMID_DEBUG_ALL', asbool)
S('debug_authorization', 'PYRAMID_DEBUG_AUTHORIZATION', asbool)
O('debug_authorization', 'debug_all')
S('debug_notfound', 'PYRAMID_DEBUG_NOTFOUND', asbool)
O('debug_notfound', 'debug_all')
S('debug_routematch', 'PYRAMID_DEBUG_ROUTEMATCH', asbool)
O('debug_routematch', 'debug_all')
S('debug_templates', 'PYRAMID_DEBUG_TEMPLATES', asbool)
O('debug_templates', 'debug_all')

S('reload_all', 'PYRAMID_RELOAD_ALL', asbool)
S('reload_templates', 'PYRAMID_RELOAD_TEMPLATES', asbool)
O('reload_templates', 'reload_all')
S('reload_assets', 'PYRAMID_RELOAD_ASSETS', asbool)
O('reload_assets', 'reload_all')
S('reload_resources', 'PYRAMID_RELOAD_RESOURCES', asbool)
O('reload_resources', 'reload_all')
# reload_resources is an older alias for reload_assets
for k in expand_key('reload_assets') + expand_key('reload_resources'):
d[k] = d['reload_assets'] or d['reload_resources']

S('default_locale_name', 'PYRAMID_DEFAULT_LOCALE_NAME', str, 'en')
S('prevent_http_cache', 'PYRAMID_PREVENT_HTTP_CACHE', asbool)
S('prevent_cachebust', 'PYRAMID_PREVENT_CACHEBUST', asbool)
S('csrf_trusted_origins', 'PYRAMID_CSRF_TRUSTED_ORIGINS', aslist, [])

return d
39 changes: 19 additions & 20 deletions pyramid/tests/test_config/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ def test__set_settings_as_None(self):
settings = config._set_settings(None)
self.assertTrue(settings)

def test__set_settings_uses_original_dict(self):
config = self._makeOne()
dummy = {}
result = config._set_settings(dummy)
self.assertTrue(dummy is result)
self.assertEqual(dummy['pyramid.debug_all'], False)

def test__set_settings_as_dictwithvalues(self):
config = self._makeOne()
settings = config._set_settings({'a':'1'})
Expand Down Expand Up @@ -68,26 +75,6 @@ def _makeOne(self, d=None, environ=None):
klass = self._getTargetClass()
return klass(d, _environ_=environ)

def test_getattr_success(self):
import warnings
with warnings.catch_warnings(record=True) as w:
warnings.filterwarnings('always')
settings = self._makeOne({'reload_templates':False})
self.assertEqual(settings.reload_templates, False)
self.assertEqual(len(w), 1)

def test_getattr_fail(self):
import warnings
with warnings.catch_warnings(record=True) as w:
warnings.filterwarnings('always')
settings = self._makeOne({})
self.assertRaises(AttributeError, settings.__getattr__, 'wontexist')
self.assertEqual(len(w), 0)

def test_getattr_raises_attribute_error(self):
settings = self._makeOne()
self.assertRaises(AttributeError, settings.__getattr__, 'mykey')

def test_noargs(self):
settings = self._makeOne()
self.assertEqual(settings['debug_authorization'], False)
Expand Down Expand Up @@ -557,6 +544,18 @@ def test_default_locale_name(self):
self.assertEqual(result['default_locale_name'], 'abc')
self.assertEqual(result['pyramid.default_locale_name'], 'abc')

def test_csrf_trusted_origins(self):
result = self._makeOne({})
self.assertEqual(result['pyramid.csrf_trusted_origins'], [])
result = self._makeOne({'pyramid.csrf_trusted_origins': 'example.com'})
self.assertEqual(result['pyramid.csrf_trusted_origins'], ['example.com'])
result = self._makeOne({'pyramid.csrf_trusted_origins': ['example.com']})
self.assertEqual(result['pyramid.csrf_trusted_origins'], ['example.com'])
result = self._makeOne({'pyramid.csrf_trusted_origins': (
'example.com foo.example.com\nasdf.example.com')})
self.assertEqual(result['pyramid.csrf_trusted_origins'], [
'example.com', 'foo.example.com', 'asdf.example.com'])

def test_originals_kept(self):
result = self._makeOne({'a':'i am so a'})
self.assertEqual(result['a'], 'i am so a')
Expand Down

0 comments on commit 3c5db58

Please sign in to comment.