Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make settings only accessible using dictionary lookup #2823

Merged
merged 2 commits into from
Nov 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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