Improved settings.parser API
This commit is contained in:
parent
c8d694299b
commit
a1f8d32ac7
5
TODO.md
5
TODO.md
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
18
orchestra/contrib/settings/README.md
Normal file
18
orchestra/contrib/settings/README.md
Normal 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()})
|
||||
...
|
||||
```
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue