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