Browse Source

Adds `celery upgrade settings [filename]` command to upgrade to new setting names

Ask Solem 9 years ago
parent
commit
babf124156
1 changed files with 73 additions and 2 deletions
  1. 73 2
      celery/bin/celery.py

+ 73 - 2
celery/bin/celery.py

@@ -6,8 +6,9 @@ The :program:`celery` umbrella command.
 .. program:: celery
 
 """
-from __future__ import absolute_import, unicode_literals
+from __future__ import absolute_import, unicode_literals, print_function
 
+import codecs
 import numbers
 import os
 import sys
@@ -17,10 +18,12 @@ from importlib import import_module
 
 from kombu.utils import json
 
+from celery.app import defaults
 from celery.five import string_t, values
 from celery.platforms import EX_OK, EX_FAILURE, EX_UNAVAILABLE, EX_USAGE
 from celery.utils import term
 from celery.utils import text
+from celery.utils.functional import pass1
 from celery.utils.timeutils import maybe_iso8601
 
 # Cannot use relative imports here due to a Windows issue (#1111).
@@ -55,7 +58,9 @@ DEBUG = os.environ.get('C_DEBUG', False)
 command_classes = [
     ('Main', ['worker', 'events', 'beat', 'shell', 'multi', 'amqp'], 'green'),
     ('Remote Control', ['status', 'inspect', 'control'], 'blue'),
-    ('Utils', ['purge', 'list', 'migrate', 'call', 'result', 'report'], None),
+    ('Utils',
+     ['purge', 'list', 'migrate', 'call', 'result', 'report', 'upgrade'],
+     None),
 ]
 if DEBUG:  # pragma: no cover
     command_classes.append(
@@ -658,6 +663,71 @@ class shell(Command):  # pragma: no cover
         bpython.embed(self.locals)
 
 
+class upgrade(Command):
+    """Perform upgrade between versions."""
+    option_list = Command.option_list + (
+        Option('--django', action='store_true',
+               help='Upgrade Django project'),
+        Option('--compat', action='store_true',
+               help='Maintain backwards compatibility'),
+        Option('--no-backup', action='store_true',
+               help='Dont backup original files'),
+    )
+    choices = {'settings'}
+
+    def usage(self, command):
+        return "%prog <command> settings [filename] [options]"
+
+    def run(self, *args, **kwargs):
+        try:
+            command = args[0]
+        except IndexError:
+            raise self.UsageError('missing upgrade type')
+        if command not in self.choices:
+            raise self.UsageError('unknown upgrade type: {0}'.format(command))
+        return getattr(self, command)(*args, **kwargs)
+
+    def settings(self, command, filename,
+                 no_backup=False, django=False, compat=False, **kwargs):
+        lines = self._slurp(filename) if no_backup else self._backup(filename)
+        keyfilter = self._compat_key if django or compat else pass1
+        print('processing {0}...'.format(filename), file=self.stderr)
+        with codecs.open(filename, 'w', 'utf-8') as write_fh:
+            for line in lines:
+                write_fh.write(self._to_new_key(line, keyfilter))
+
+    def _slurp(self, filename):
+        with codecs.open(filename, 'r', 'utf-8') as read_fh:
+            return [line for line in read_fh]
+
+    def _backup(self, filename, suffix='.orig'):
+        lines = []
+        backup_filename = ''.join([filename, suffix])
+        print('writing backup to {0}...'.format(backup_filename),
+              file=self.stderr)
+        with codecs.open(filename, 'r', 'utf-8') as read_fh:
+            with codecs.open(backup_filename, 'w', 'utf-8') as backup_fh:
+                for line in read_fh:
+                    backup_fh.write(line)
+                    lines.append(line)
+        return lines
+
+    def _to_new_key(self, line, keyfilter=pass1, source=defaults._TO_NEW_KEY):
+        # sort by length to avoid e.g. broker_transport overriding
+        # broker_transport_options.
+        for old_key in reversed(sorted(source, key=lambda x: len(x))):
+            new_line = line.replace(old_key, keyfilter(source[old_key]))
+            if line != new_line:
+                return new_line  # only one match per line.
+        return line
+
+    def _compat_key(self, key, namespace='CELERY'):
+        key = key.upper()
+        if not key.startswith(namespace):
+            key = '_'.join([namespace, key])
+        return key
+
+
 class help(Command):
     """Show help screen and exit."""
 
@@ -702,6 +772,7 @@ class CeleryCommand(Command):
         'result': result,
         'shell': shell,
         'status': status,
+        'upgrade': upgrade,
         'worker': worker,
 
     }