Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

core.py 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. from operator import attrgetter
  2. from django.template import Node
  3. from classytags.blocks import BlockDefinition
  4. from classytags.compat import compat_basestring
  5. from classytags.parser import Parser
  6. from classytags.utils import StructuredOptions
  7. from classytags.utils import get_default_name
  8. class Options(object):
  9. """
  10. Option class holding the arguments of a tag.
  11. """
  12. def __init__(self, *options, **kwargs):
  13. self._options = options
  14. self._kwargs = kwargs
  15. self.options = {}
  16. self.raw_options = options
  17. self.breakpoints = []
  18. self.combined_breakpoints = {}
  19. current_breakpoint = None
  20. last = None
  21. self.options[current_breakpoint] = []
  22. self.all_argument_names = []
  23. for value in options:
  24. if isinstance(value, compat_basestring):
  25. if isinstance(last, compat_basestring):
  26. self.combined_breakpoints[last] = value
  27. self.breakpoints.append(value)
  28. current_breakpoint = value
  29. self.options[current_breakpoint] = []
  30. else:
  31. self.options[current_breakpoint].append(value)
  32. self.all_argument_names.append(value.name)
  33. last = value
  34. self.blocks = []
  35. for block in kwargs.get('blocks', []):
  36. if isinstance(block, BlockDefinition):
  37. block_definition = block
  38. elif isinstance(block, compat_basestring):
  39. block_definition = BlockDefinition(block, block)
  40. else:
  41. block_definition = BlockDefinition(block[1], block[0])
  42. block_definition.validate(self)
  43. self.blocks.append(block_definition)
  44. if 'parser_class' in kwargs:
  45. self.parser_class = kwargs['parser_class']
  46. else:
  47. self.parser_class = Parser
  48. def __repr__(self):
  49. bits = list(map(repr, self.options[None]))
  50. for breakpoint in self.breakpoints:
  51. bits.append(breakpoint)
  52. for option in self.options[breakpoint]:
  53. bits.append(repr(option))
  54. options = ','.join(bits)
  55. if self.blocks:
  56. blocks = ';%s' % ','.join(map(attrgetter('alias'), self.blocks))
  57. else: # pragma: no cover
  58. blocks = ''
  59. return '<Options:%s%s>' % (options, blocks)
  60. def __add__(self, other):
  61. if not isinstance(other, Options):
  62. raise TypeError("Cannot add Options to non-Options object")
  63. if self.blocks and other.blocks:
  64. raise ValueError(
  65. "Cannot add two Options objects if both objects define blocks"
  66. )
  67. if self.parser_class is not other.parser_class:
  68. raise ValueError(
  69. "Cannot add two Options objects with different parser classes"
  70. )
  71. full_options = self._options + other._options
  72. full_kwargs = {
  73. 'parser_class': self.parser_class
  74. }
  75. if self._kwargs.get('blocks', False):
  76. full_kwargs['blocks'] = self._kwargs['blocks']
  77. elif other._kwargs.get('blocks', False):
  78. full_kwargs['blocks'] = other._kwargs['blocks']
  79. return Options(*full_options, **full_kwargs)
  80. def get_parser_class(self):
  81. return self.parser_class
  82. def bootstrap(self):
  83. """
  84. Bootstrap this options
  85. """
  86. return StructuredOptions(
  87. self.options,
  88. self.breakpoints,
  89. self.blocks,
  90. self.combined_breakpoints
  91. )
  92. def parse(self, parser, tokens):
  93. """
  94. Parse template tokens into a dictionary
  95. """
  96. argument_parser_class = self.get_parser_class()
  97. argument_parser = argument_parser_class(self)
  98. return argument_parser.parse(parser, tokens)
  99. class TagMeta(type):
  100. """
  101. Metaclass for the Tag class that set's the name attribute onto the class
  102. and a _decorated_function pseudo-function which is used by Django's
  103. template system to get the tag name.
  104. """
  105. def __new__(cls, name, bases, attrs):
  106. parents = [base for base in bases if isinstance(base, TagMeta)]
  107. if not parents:
  108. return super(TagMeta, cls).__new__(cls, name, bases, attrs)
  109. tag_name = str(attrs.get('name', get_default_name(name)))
  110. def fake_func():
  111. pass # pragma: no cover
  112. fake_func.__name__ = tag_name
  113. attrs['_decorated_function'] = fake_func
  114. attrs['name'] = str(tag_name)
  115. return super(TagMeta, cls).__new__(cls, name, bases, attrs)
  116. class Tag(TagMeta('TagMeta', (Node,), {})):
  117. """
  118. Main Tag class.
  119. """
  120. options = Options()
  121. name = None
  122. def __init__(self, parser, tokens):
  123. self.kwargs, self.blocks = self.options.parse(parser, tokens)
  124. self.child_nodelists = []
  125. for key, value in self.blocks.items():
  126. setattr(self, key, value)
  127. self.child_nodelists.append(key)
  128. def render(self, context):
  129. """
  130. INTERNAL method to prepare rendering
  131. Usually you should not override this method, but rather use render_tag.
  132. """
  133. items = self.kwargs.items()
  134. kwargs = dict([(key, value.resolve(context)) for key, value in items])
  135. kwargs.update(self.blocks)
  136. return self.render_tag(context, **kwargs)
  137. def render_tag(self, context, **kwargs):
  138. """
  139. The method you should override in your custom tags
  140. """
  141. raise NotImplementedError
  142. def __repr__(self):
  143. return '<Tag: %s>' % self.name