blob: c19029a1ef0a2b3ba5135cbfc57dd9b12b0d08cb [file] [log] [blame]
[email protected]15f08dd2012-01-27 07:29:481# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5class Code(object):
6 """A convenience object for constructing code.
7
8 Logically each object should be a block of code. All methods except |Render|
9 and |IsEmpty| return self.
10 """
11 def __init__(self, indent_size=2, comment_length=80):
12 self._code = []
13 self._indent_level = 0
14 self._indent_size = indent_size
15 self._comment_length = comment_length
16
17 def Append(self, line=''):
18 """Appends a line of code at the current indent level or just a newline if
19 line is not specified. Trailing whitespace is stripped.
20 """
21 self._code.append(((' ' * self._indent_level) + line).rstrip())
22 return self
23
24 def IsEmpty(self):
25 """Returns True if the Code object is empty.
26 """
27 return not bool(self._code)
28
29 def Concat(self, obj):
30 """Concatenate another Code object onto this one. Trailing whitespace is
31 stripped.
32
33 Appends the code at the current indent level. Will fail if there are any
34 un-interpolated format specifiers eg %s, %(something)s which helps
35 isolate any strings that haven't been substituted.
36 """
37 if not isinstance(obj, Code):
[email protected]cfe484902012-02-15 14:52:3238 raise TypeError(type(obj))
[email protected]15f08dd2012-01-27 07:29:4839 assert self is not obj
40 for line in obj._code:
[email protected]cfe484902012-02-15 14:52:3241 try:
42 # line % () will fail if any substitution tokens are left in line
43 self._code.append(((' ' * self._indent_level) + line % ()).rstrip())
44 except TypeError:
45 raise TypeError('Unsubstituted value when concatting\n' + line)
[email protected]15f08dd2012-01-27 07:29:4846
47 return self
48
49 def Sblock(self, line=''):
50 """Starts a code block.
51
52 Appends a line of code and then increases the indent level.
53 """
54 self.Append(line)
55 self._indent_level += self._indent_size
56 return self
57
58 def Eblock(self, line=''):
59 """Ends a code block by decreasing and then appending a line (or a blank
60 line if not given).
61 """
62 # TODO(calamity): Decide if type checking is necessary
63 #if not isinstance(line, basestring):
64 # raise TypeError
65 self._indent_level -= self._indent_size
66 self.Append(line)
67 return self
68
69 # TODO(calamity): Make comment its own class or something and Render at
70 # self.Render() time
71 def Comment(self, comment):
72 """Adds the given string as a comment.
73
74 Will split the comment if it's too long. Use mainly for variable length
75 comments. Otherwise just use code.Append('// ...') for comments.
76 """
77 comment_symbol = '// '
78 max_len = self._comment_length - self._indent_level - len(comment_symbol)
79 while len(comment) >= max_len:
80 line = comment[0:max_len]
81 last_space = line.rfind(' ')
82 if last_space != -1:
83 line = line[0:last_space]
84 comment = comment[last_space + 1:]
85 else:
86 comment = comment[max_len:]
87 self.Append(comment_symbol + line)
88 self.Append(comment_symbol + comment)
89 return self
90
91 def Substitute(self, d):
92 """Goes through each line and interpolates using the given dict.
93
94 Raises type error if passed something that isn't a dict
95
96 Use for long pieces of code using interpolation with the same variables
97 repeatedly. This will reduce code and allow for named placeholders which
98 are more clear.
99 """
100 if not isinstance(d, dict):
101 raise TypeError('Passed argument is not a dictionary: ' + d)
102 for i, line in enumerate(self._code):
103 # Only need to check %s because arg is a dict and python will allow
104 # '%s %(named)s' but just about nothing else
105 if '%s' in self._code[i] or '%r' in self._code[i]:
106 raise TypeError('"%s" or "%r" found in substitution. '
107 'Named arguments only. Use "%" to escape')
108 self._code[i] = line % d
109 return self
110
111 def Render(self):
112 """Renders Code as a string.
113 """
114 return '\n'.join(self._code)
115