Improved settings.parser API

This commit is contained in:
Marc Aymerich 2015-10-04 19:57:00 +00:00
parent c8d694299b
commit a1f8d32ac7
9 changed files with 76 additions and 27 deletions

View File

@ -237,7 +237,6 @@ https://code.djangoproject.com/ticket/24576
# TASK_BEAT_BACKEND = ('cron', 'celerybeat', 'uwsgi')
# Ship orchestra production-ready (no DEBUG etc)
# Settings.parser.changes: if setting.value == default. remove
# reload generic admin view ?redirect=http...
# inspecting django db connection for asserting db readines? or performing a query
* wake up django mailer on send_mail
@ -379,8 +378,6 @@ Case
# Mailer: mark as sent
# Mailer: download attachments
# Deprecate orchestra start/stop/restart services management commands?
# Enable/disable ignore period orders list filter
@ -414,8 +411,6 @@ http://makandracards.com/makandra/24933-chrome-34+-firefox-38+-ie11+-ignore-auto
mkhomedir_helper or create ssh homes with bash.rc and such
# validate saas setting allow_custom_url check that websites have a related declared directive
# warnings if some plugins are disabled, like make routes red
# replace show emails by https://docs.python.org/3/library/email.contentmanager.html#module-email.contentmanager
# tzinfo=datetime.timezone.utc

View File

@ -149,8 +149,12 @@ def admin_date(*args, **kwargs):
natural = humanize.naturaldatetime(date)
else:
natural = humanize.naturaldate(date)
local = timezone.localtime(date).strftime("%Y-%m-%d %H:%M:%S %Z")
return '<span title="{0}">{1}</span>'.format(local, escape(natural))
if hasattr(date, 'hour'):
date = timezone.localtime(date)
date = date.strftime("%Y-%m-%d %H:%M:%S %Z")
else:
date = date.strftime("%Y-%m-%d")
return '<span title="{0}">{1}</span>'.format(date, escape(natural))
def get_object_from_url(modeladmin, request):

View File

@ -127,7 +127,11 @@ DATABASES = {
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
try:
TIME_ZONE = open('/etc/timezone', 'r').read().strip()
except IOError:
TIME_ZONE = 'UTC'
USE_I18N = True

View File

@ -0,0 +1,18 @@
```python
>>> from orchestra.contrib.settings import Setting, parser
>>> Setting.settings['TASKS_BACKEND'].value
'thread'
>>> Setting.settings['TASKS_BACKEND'].default
'thread'
>>> Setting.settings['TASKS_BACKEND'].validate_value('rata')
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/orchestra/django-orchestra/orchestra/contrib/settings/__init__.py", line 99, in validate_value
raise ValidationError("'%s' not in '%s'" % (value, ', '.join(choices)))
django.core.exceptions.ValidationError: ["'rata' not in 'thread, process, celery'"]
>>> parser.apply({'TASKS_BACKEND': 'process'})
...
>>> parser.apply({'TASKS_BACKEND': parser.Remove()})
...
```

View File

@ -82,18 +82,32 @@ class Setting(object):
raise ValidationError(errors)
return validate_string_format
def validate(self):
if self.value:
validators.all_valid(self.value, self.validators)
def validate_value(self, value):
if value:
validators.all_valid(value, self.validators)
valid_types = list(self.types)
if self.choices:
choices = self.choices
if callable(choices):
choices = choices()
choices = [n for n,v in choices]
values = value
if not isinstance(values, (list, tuple)):
values = [value]
for cvalue in values:
if cvalue not in choices:
raise ValidationError("'%s' not in '%s'" % (value, ', '.join(choices)))
if isinstance(self.default, (list, tuple)):
valid_types.extend([list, tuple])
valid_types.append(type(self.default))
if not isinstance(self.value, tuple(valid_types)):
if not isinstance(value, tuple(valid_types)):
raise ValidationError("%s is not a valid type (%s)." %
(type(self.value).__name__, ', '.join(t.__name__ for t in valid_types))
(type(value).__name__, ', '.join(t.__name__ for t in valid_types))
)
def validate(self):
self.validate_value(self.value)
@classmethod
def get_value(cls, name, default):
return getattr(cls.conf_settings, name, default)

View File

@ -53,10 +53,15 @@ class SettingView(generic.edit.FormView):
setting = settings[data['name']]
if not isinstance(data['value'], parser.NotSupported) and setting.editable:
if setting.value != data['value']:
# Ignore differences between lists and tuples
if (type(setting.value) != type(data['value']) and
isinstance(data['value'], list) and
tuple(data['value']) == setting.value):
continue
if setting.default == data['value']:
changes[setting.name] = parser.Remove()
else:
changes[setting.name] = parser.serialize(data['value'])
changes[setting.name] = data['value']
if changes:
# Display confirmation
if not self.request.POST.get('confirmation'):
@ -66,6 +71,8 @@ class SettingView(generic.edit.FormView):
diff = sys.run(cmd, valid_codes=(1, 0)).stdout
context = self.get_context_data(form=form)
context['diff'] = diff
if not diff:
messages.warning(self.request, _("Changes detected but no diff %s.") % changes)
return self.render_to_response(context)
n = len(changes)
# Save changes

View File

@ -113,9 +113,9 @@ class SettingForm(ReadOnlyFormMixin, forms.Form):
except Exception as exc:
raise ValidationError(format_exception(exc))
self.setting.validate_value(value)
if not isinstance(value, self.setting_type):
if self.setting_type in (tuple, list) and isinstance(value, (tuple, list)):
value = self.setting_type(value)
# if not isinstance(value, self.setting_type):
# if self.setting_type in (tuple, list) and isinstance(value, (tuple, list)):
# value = self.setting_type(value)
return value

View File

@ -9,6 +9,8 @@ from django.utils.functional import Promise
from orchestra.utils.paths import get_project_dir
from . import Setting
class Remove(object):
""" used to signal a setting remove """
@ -92,7 +94,6 @@ def serialize(obj, init=True):
def _format_setting(name, value):
if isinstance(value, Remove):
return ""
value = eval(value, get_eval_context())
try:
value = json.dumps(value, indent=4)
except TypeError:
@ -100,8 +101,20 @@ def _format_setting(name, value):
return "{name} = {value}".format(name=name, value=value)
def validate_changes(changes):
for name, value in changes.items():
if not isinstance(value, Remove):
try:
setting = Setting.settings[name]
except KeyError:
pass
else:
setting.validate_value(value)
def apply(changes, settings_file=get_settings_file()):
""" returns settings_file content with applied changes """
validate_changes(changes)
updates = _find_updates(changes, settings_file)
content = []
_changes = copy.copy(changes)

View File

@ -110,32 +110,26 @@ class Command(BaseCommand):
if Setting.settings['TASKS_BACKEND'].value != 'celery':
changes['TASKS_BACKEND'] = 'celery'
if Setting.settings['ORCHESTRA_START_SERVICES'].value == Setting.settings['ORCHESTRA_START_SERVICES'].default:
changes['ORCHESTRA_START_SERVICES'] = settings_parser.serialize(
(
changes['ORCHESTRA_START_SERVICES'] = (
'postgresql',
'celeryevcam',
'celeryd',
'celerybeat',
('uwsgi', 'nginx'),
)
)
if Setting.settings['ORCHESTRA_RESTART_SERVICES'].value == Setting.settings['ORCHESTRA_RESTART_SERVICES'].default:
changes['ORCHESTRA_RESTART_SERVICES'] = settings_parser.serialize(
(
changes['ORCHESTRA_RESTART_SERVICES'] = (
'celeryd',
'celerybeat',
'uwsgi',
)
)
if Setting.settings['ORCHESTRA_STOP_SERVICES'].value == Setting.settings['ORCHESTRA_STOP_SERVICES'].default:
changes['ORCHESTRA_STOP_SERVICES'] = settings_parser.serialize(
(
changes['ORCHESTRA_STOP_SERVICES'] = (
('uwsgi', 'nginx'),
'celerybeat',
'celeryd',
'celeryevcam',
'postgresql'
)
)
if changes:
settings_parser.apply(changes)