validate_templates.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. # -*- coding: utf-8 -*-
  2. import os
  3. import fnmatch
  4. from django.apps import apps
  5. from django.conf import settings
  6. from django.core.management.base import BaseCommand, CommandError
  7. from django.core.management.color import color_style
  8. from django.template.loader import get_template
  9. from django_extensions.compat import get_template_setting
  10. from django_extensions.management.utils import signalcommand
  11. #
  12. # TODO: Render the template with fake request object ?
  13. #
  14. class Command(BaseCommand):
  15. args = ''
  16. help = "Validate templates on syntax and compile errors"
  17. ignores = set([
  18. ".DS_Store",
  19. "*.swp",
  20. "*~",
  21. ])
  22. def add_arguments(self, parser):
  23. super().add_arguments(parser)
  24. parser.add_argument(
  25. '--no-apps', action='store_true', dest='no_apps',
  26. default=False, help="Do not automatically include apps.")
  27. parser.add_argument(
  28. '--break', '-b', action='store_true', dest='break',
  29. default=False, help="Break on first error.")
  30. parser.add_argument(
  31. '--include', '-i', action='append', dest='includes',
  32. default=[], help="Append these paths to TEMPLATE DIRS")
  33. parser.add_argument(
  34. '--ignore-app', action='append', dest='ignore_apps',
  35. default=[], help="Ignore these apps")
  36. def ignore_filename(self, filename):
  37. filename = os.path.basename(filename)
  38. for ignore_pattern in self.ignores:
  39. if fnmatch.fnmatch(filename, ignore_pattern):
  40. return True
  41. return False
  42. @signalcommand
  43. def handle(self, *args, **options):
  44. if hasattr(settings, 'VALIDATE_TEMPLATES_IGNORES'):
  45. self.ignores = getattr(settings, 'VALIDATE_TEMPLATES_IGNORES')
  46. style = color_style()
  47. template_dirs = set(get_template_setting('DIRS', []))
  48. template_dirs |= set(options['includes'])
  49. template_dirs |= set(getattr(settings, 'VALIDATE_TEMPLATES_EXTRA_TEMPLATE_DIRS', []))
  50. if not options['no_apps']:
  51. ignore_apps = options['ignore_apps']
  52. if not ignore_apps and hasattr(settings, 'VALIDATE_TEMPLATES_IGNORE_APPS'):
  53. ignore_apps = getattr(settings, 'VALIDATE_TEMPLATES_IGNORE_APPS')
  54. for app in apps.get_app_configs():
  55. if app.name in ignore_apps:
  56. continue
  57. app_template_dir = os.path.join(app.path, 'templates')
  58. if os.path.isdir(app_template_dir):
  59. template_dirs.add(app_template_dir)
  60. settings.TEMPLATES[0]['DIRS'] = list(template_dirs)
  61. settings.TEMPLATE_DEBUG = True
  62. verbosity = options["verbosity"]
  63. errors = 0
  64. for template_dir in template_dirs:
  65. for root, dirs, filenames in os.walk(template_dir):
  66. for filename in filenames:
  67. if self.ignore_filename(filename):
  68. continue
  69. filepath = os.path.join(root, filename)
  70. if verbosity > 1:
  71. self.stdout.write(filepath)
  72. try:
  73. get_template(filepath)
  74. except Exception as e:
  75. errors += 1
  76. self.stdout.write("%s: %s" % (filepath, style.ERROR("%s %s" % (e.__class__.__name__, str(e)))))
  77. if errors and options['break']:
  78. raise CommandError("Errors found")
  79. if errors:
  80. raise CommandError("%s errors found" % errors)
  81. self.stdout.write("%s errors found" % errors)