formatting.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. """
  2. Utility functions to return a formatted name and description for a given view.
  3. """
  4. import re
  5. from django.utils.encoding import force_str
  6. from django.utils.html import escape
  7. from django.utils.safestring import mark_safe
  8. from rest_framework.compat import apply_markdown
  9. def remove_trailing_string(content, trailing):
  10. """
  11. Strip trailing component `trailing` from `content` if it exists.
  12. Used when generating names from view classes.
  13. """
  14. if content.endswith(trailing) and content != trailing:
  15. return content[:-len(trailing)]
  16. return content
  17. def dedent(content):
  18. """
  19. Remove leading indent from a block of text.
  20. Used when generating descriptions from docstrings.
  21. Note that python's `textwrap.dedent` doesn't quite cut it,
  22. as it fails to dedent multiline docstrings that include
  23. unindented text on the initial line.
  24. """
  25. content = force_str(content)
  26. lines = [line for line in content.splitlines()[1:] if line.lstrip()]
  27. # unindent the content if needed
  28. if lines:
  29. whitespace_counts = min([len(line) - len(line.lstrip(' ')) for line in lines])
  30. tab_counts = min([len(line) - len(line.lstrip('\t')) for line in lines])
  31. if whitespace_counts:
  32. whitespace_pattern = '^' + (' ' * whitespace_counts)
  33. content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content)
  34. elif tab_counts:
  35. whitespace_pattern = '^' + ('\t' * tab_counts)
  36. content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content)
  37. return content.strip()
  38. def camelcase_to_spaces(content):
  39. """
  40. Translate 'CamelCaseNames' to 'Camel Case Names'.
  41. Used when generating names from view classes.
  42. """
  43. camelcase_boundary = '(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))'
  44. content = re.sub(camelcase_boundary, ' \\1', content).strip()
  45. return ' '.join(content.split('_')).title()
  46. def markup_description(description):
  47. """
  48. Apply HTML markup to the given description.
  49. """
  50. if apply_markdown:
  51. description = apply_markdown(description)
  52. else:
  53. description = escape(description).replace('\n', '<br />')
  54. description = '<p>' + description + '</p>'
  55. return mark_safe(description)
  56. class lazy_format:
  57. """
  58. Delay formatting until it's actually needed.
  59. Useful when the format string or one of the arguments is lazy.
  60. Not using Django's lazy because it is too slow.
  61. """
  62. __slots__ = ('format_string', 'args', 'kwargs', 'result')
  63. def __init__(self, format_string, *args, **kwargs):
  64. self.result = None
  65. self.format_string = format_string
  66. self.args = args
  67. self.kwargs = kwargs
  68. def __str__(self):
  69. if self.result is None:
  70. self.result = self.format_string.format(*self.args, **self.kwargs)
  71. self.format_string, self.args, self.kwargs = None, None, None
  72. return self.result
  73. def __mod__(self, value):
  74. return str(self) % value