Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~vlewis/boots/vdev |
Merge into: | lp:boots |
Diff against target: |
311 lines (+214/-3) (has conflicts) 5 files modified
src/boots/app/client_config.py (+14/-2) src/boots/lib/console.py (+31/-1) src/boots/lib/ui/components/helptopics.py (+137/-0) src/boots/lib/ui/generic.py (+2/-0) src/boots/lib/ui/plain.py (+30/-0) Text conflict in src/boots/lib/console.py Text conflict in src/boots/lib/ui/plain.py |
To merge this branch: | bzr merge lp:~vlewis/boots/vdev |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Max Goodhart | Approve | ||
David Rosenbaum | Needs Fixing | ||
Review via email: mp+18176@code.launchpad.net |
Commit message
Description of the change
Victoria Lewis (vlewis) wrote : | # |
- 23. By Victoria Lewis <email address hidden>
-
Add sort to helptopics.py
Max Goodhart (chromakode) wrote : | # |
> Here is my code. I need to add a sort.
The code sorted(lines) does not sort the lines list in place. Instead, it will return an iterator that traverses the sorted list. Instead, use lines.sort().
The files help.py and helpinfo.py are no longer needed in this merge. Use "bzr rm --keep <file>" to remove them.
Finally, instead of:
leng = len(max(
You can find the topic with the longest name using:
max_name_len = max((subtopic for subtopic in self.subtopics.
-M
Victoria Lewis (vlewis) wrote : | # |
Thanks.
Don't I still need to add some space to the end of the longest name?
Victoria
>>> Chromakode 01/28/10 8:10 PM >>>
Review: Needs Fixing
> Here is my code. I need to add a sort.
The code sorted(lines) does not sort the lines list in place. Instead, it will return an iterator that traverses the sorted list. Instead, use lines.sort().
The files help.py and helpinfo.py are no longer needed in this merge. Use "bzr rm --keep " to remove them.
Finally, instead of:
leng = len(max(
You can find the topic with the longest name using:
max_name_len = max((subtopic for subtopic in self.subtopics.
-M
--
https:/
You are the owner of lp:~vlewis/boots/vdev.
David Rosenbaum (davidjrosenbaum) wrote : | # |
In addition to the above comments (which I agree with) there are a couple of other issues:
First, the local function maxword that you added in the issubseq function I wrote is dead code and should be removed. Another problem is that the code does not use proper indentation which will cause the interpreter to crash. Lines 51-53 mix tabs and spaces and use indentation of width 8. This is incorrect in Python and the indentation width must be 4. Please use only spaces for indentation.
Max Goodhart (chromakode) wrote : | # |
Merged.
A couple comments:
* The default config options disabled this feature! I've modified them so the user gets 100 lines of persistent history by default, saved at ~/.boots_history.
* Whenever possible, try to keep UI-specific code out of the Console. I moved the save/load calls to inside the Plain UI class, and created a general "unload" method which, when called by the quitting Console, calls save_history.
Preview Diff
1 | === modified file 'src/boots/app/client_config.py' | |||
2 | --- src/boots/app/client_config.py 2010-01-22 05:58:25 +0000 | |||
3 | +++ src/boots/app/client_config.py 2010-02-04 01:10:27 +0000 | |||
4 | @@ -51,7 +51,9 @@ | |||
5 | 51 | "port" : 9306, | 51 | "port" : 9306, |
6 | 52 | "username" : os.getenv("USER"), | 52 | "username" : os.getenv("USER"), |
7 | 53 | "password" : False, | 53 | "password" : False, |
9 | 54 | "prompt" : "> "} | 54 | "prompt" : "> ", |
10 | 55 | "history_length": 0, | ||
11 | 56 | "history_file": None} | ||
12 | 55 | self._dict = self._defaults.copy() | 57 | self._dict = self._defaults.copy() |
13 | 56 | 58 | ||
14 | 57 | # Use default rc location in info if no argument is passed in. | 59 | # Use default rc location in info if no argument is passed in. |
15 | @@ -111,7 +113,17 @@ | |||
16 | 111 | type = "string", | 113 | type = "string", |
17 | 112 | dest = "password", | 114 | dest = "password", |
18 | 113 | help = "Connect using password. If none is given, query for password") | 115 | help = "Connect using password. If none is given, query for password") |
20 | 114 | 116 | self._cli_parser.add_option("-F", "--historyfile", | |
21 | 117 | action = "store", | ||
22 | 118 | type = "string", | ||
23 | 119 | dest = "history_file", | ||
24 | 120 | help = "Specify file to save history to") | ||
25 | 121 | self._cli_parser.add_option("-L", "--historylength", | ||
26 | 122 | action = "store", | ||
27 | 123 | type = "int", | ||
28 | 124 | dest = "history_length", | ||
29 | 125 | help = "Specify max history file length") | ||
30 | 126 | |||
31 | 115 | def __getitem__(self, key): | 127 | def __getitem__(self, key): |
32 | 116 | return self._dict[key] | 128 | return self._dict[key] |
33 | 117 | 129 | ||
34 | 118 | 130 | ||
35 | === modified file 'src/boots/lib/console.py' | |||
36 | --- src/boots/lib/console.py 2010-02-01 20:46:55 +0000 | |||
37 | +++ src/boots/lib/console.py 2010-02-04 01:10:27 +0000 | |||
38 | @@ -30,6 +30,7 @@ | |||
39 | 30 | 30 | ||
40 | 31 | from boots.api import api | 31 | from boots.api import api |
41 | 32 | from boots.lib.ui.plain import PlainUI | 32 | from boots.lib.ui.plain import PlainUI |
42 | 33 | <<<<<<< TREE | ||
43 | 33 | from boots.lib.metacommands import metacommands | 34 | from boots.lib.metacommands import metacommands |
44 | 34 | from boots.lib.lingos import lingo | 35 | from boots.lib.lingos import lingo |
45 | 35 | from boots.lib.lingos.lisp import lisp | 36 | from boots.lib.lingos.lisp import lisp |
46 | @@ -44,10 +45,15 @@ | |||
47 | 44 | return True | 45 | return True |
48 | 45 | except TypeError: | 46 | except TypeError: |
49 | 46 | return False | 47 | return False |
50 | 48 | ======= | ||
51 | 49 | import os | ||
52 | 50 | import atexit | ||
53 | 51 | >>>>>>> MERGE-SOURCE | ||
54 | 47 | 52 | ||
55 | 48 | class Console(object): | 53 | class Console(object): |
56 | 49 | def __init__(self, config): | 54 | def __init__(self, config): |
57 | 50 | self.config = config | 55 | self.config = config |
58 | 56 | <<<<<<< TREE | ||
59 | 51 | # FIXME: Once metacommands are supported it should be possible to change the lingo at runtime using a metacommand. | 57 | # FIXME: Once metacommands are supported it should be possible to change the lingo at runtime using a metacommand. |
60 | 52 | self.lingo = self.config['lingo'] | 58 | self.lingo = self.config['lingo'] |
61 | 53 | self.servers = [api.Server(self.config["host"], self.config["port"], | 59 | self.servers = [api.Server(self.config["host"], self.config["port"], |
62 | @@ -102,7 +108,15 @@ | |||
63 | 102 | lingo.register('lisp', lisp.LispInterpreter(self)) | 108 | lingo.register('lisp', lisp.LispInterpreter(self)) |
64 | 103 | lingo.register('python', python.PythonInterpreter(self)) | 109 | lingo.register('python', python.PythonInterpreter(self)) |
65 | 104 | lingo.register('sql', sql.SQLInterpreter(self)) | 110 | lingo.register('sql', sql.SQLInterpreter(self)) |
66 | 111 | ======= | ||
67 | 112 | self.server = api.Server(self.config["host"], self.config["port"], | ||
68 | 113 | {"database": self.config["database"]}) | ||
69 | 114 | self.ui = PlainUI(self.config["prompt"], self.config["history_length"], | ||
70 | 115 | self.config["history_file"]) | ||
71 | 116 | atexit.register(self.ui.save_history) | ||
72 | 117 | >>>>>>> MERGE-SOURCE | ||
73 | 105 | 118 | ||
74 | 119 | <<<<<<< TREE | ||
75 | 106 | for command in self.ui.get_input(input_complete = lingo.get(self.lingo).input_complete): | 120 | for command in self.ui.get_input(input_complete = lingo.get(self.lingo).input_complete): |
76 | 107 | if not self.metacommands.execute(metacommands.parse(command)): | 121 | if not self.metacommands.execute(metacommands.parse(command)): |
77 | 108 | result = self.run(command, self.lingo) | 122 | result = self.run(command, self.lingo) |
78 | @@ -114,7 +128,23 @@ | |||
79 | 114 | # Don't print None; a return value of None means there was no result. | 128 | # Don't print None; a return value of None means there was no result. |
80 | 115 | elif result != None: | 129 | elif result != None: |
81 | 116 | self.ui.present(result) | 130 | self.ui.present(result) |
83 | 117 | 131 | ======= | |
84 | 132 | |||
85 | 133 | def run(self, command, language): | ||
86 | 134 | result = self.server.execute(command) | ||
87 | 135 | return result | ||
88 | 136 | >>>>>>> MERGE-SOURCE | ||
89 | 137 | |||
90 | 138 | <<<<<<< TREE | ||
91 | 118 | # Print a trailing newline. | 139 | # Print a trailing newline. |
92 | 119 | self.ui.present('') | 140 | self.ui.present('') |
93 | 120 | self.disconnect() | 141 | self.disconnect() |
94 | 142 | ======= | ||
95 | 143 | def main(self): | ||
96 | 144 | self.ui.read_history() | ||
97 | 145 | self.server.connect() | ||
98 | 146 | for command in self.ui.get_input(): | ||
99 | 147 | for row in self.run(command, "sql"): | ||
100 | 148 | self.ui.present(row) | ||
101 | 149 | self.server.disconnect() | ||
102 | 150 | >>>>>>> MERGE-SOURCE | ||
103 | 121 | 151 | ||
104 | === removed file 'src/boots/lib/ui/components/help.py' | |||
105 | === added file 'src/boots/lib/ui/components/helptopics.py' | |||
106 | --- src/boots/lib/ui/components/helptopics.py 1970-01-01 00:00:00 +0000 | |||
107 | +++ src/boots/lib/ui/components/helptopics.py 2010-02-04 01:10:27 +0000 | |||
108 | @@ -0,0 +1,137 @@ | |||
109 | 1 | # Utility function | ||
110 | 2 | def issubseq(seq, seq2): | ||
111 | 3 | """Determines if the elements the sequence seq form a subsequence of the sequence seq2.""" | ||
112 | 4 | if len(seq) == 0: | ||
113 | 5 | return True | ||
114 | 6 | else: | ||
115 | 7 | i = 0 | ||
116 | 8 | |||
117 | 9 | while i < len(seq2) and seq[0] != seq2[i]: | ||
118 | 10 | i += 1 | ||
119 | 11 | |||
120 | 12 | if i == len(seq2): | ||
121 | 13 | return False | ||
122 | 14 | |||
123 | 15 | return list(seq[1:]) == list(seq2[:i + 1:len(seq)]) | ||
124 | 16 | |||
125 | 17 | |||
126 | 18 | class Topic(object): | ||
127 | 19 | """A topic in the help system which may contain other topics.""" | ||
128 | 20 | def __init__(self, name, title = None, description = None, short_description = None, see_also = None): | ||
129 | 21 | """Creates a new a help topic. The fields are as described above.""" | ||
130 | 22 | self.subtopics = dict() | ||
131 | 23 | self.title = title | ||
132 | 24 | self.name = name | ||
133 | 25 | self.description = description | ||
134 | 26 | self.short_description = short_description | ||
135 | 27 | self.see_also = see_also | ||
136 | 28 | |||
137 | 29 | def format(self): | ||
138 | 30 | """Returns a string that represents this topic in human readable form as | ||
139 | 31 | described in detail above.""" | ||
140 | 32 | text = "{0.title}\n\n{0.description}\n\nSee also: {0.see_also}".format(self) | ||
141 | 33 | return text | ||
142 | 34 | |||
143 | 35 | def format_index(self): | ||
144 | 36 | """Returns a string that lists the subtopics of this topic in human | ||
145 | 37 | readable form as described above.""" | ||
146 | 38 | lines = list() | ||
147 | 39 | |||
148 | 40 | for name, topic in self.subtopics.iteritems(): | ||
149 | 41 | max_name_len = max(len(subtopic.name) for subtopic in self.subtopics.itervalues()) | ||
150 | 42 | subtext = "{0:{2}}--{1:30}".format(topic.name, topic.short_description, max_name_len +3) | ||
151 | 43 | lines.append(subtext) | ||
152 | 44 | lines.sort() | ||
153 | 45 | return "\n".join(lines) | ||
154 | 46 | |||
155 | 47 | # Queries: | ||
156 | 48 | # | ||
157 | 49 | # A query is used to refer to a topic in the help system. A query is either | ||
158 | 50 | # a string or a non-empty tuple of strings. Using a string is equivalent to | ||
159 | 51 | # using a tuple of length one containing that string. As an example, the | ||
160 | 52 | # tuple ('python', 'syntax', 'csv') refers to the csv subtopic in the syntax | ||
161 | 53 | # topic which is itself a subtopic of the python topic. | ||
162 | 54 | |||
163 | 55 | def _translate_query(self, query): | ||
164 | 56 | """Converts a query into a tuple. If query is not a valid query an | ||
165 | 57 | exception is raised.""" | ||
166 | 58 | if isinstance(query, str): | ||
167 | 59 | return (query,) | ||
168 | 60 | elif isinstance(query, (tuple, list)): | ||
169 | 61 | assert len(query) != 0 | ||
170 | 62 | for item in query: | ||
171 | 63 | assert isinstance(item, str) | ||
172 | 64 | return query | ||
173 | 65 | else: | ||
174 | 66 | raise TypeError('query object {0} is not a string or tuple of strings'.format(query)) | ||
175 | 67 | |||
176 | 68 | def _ensure_key(self, key): | ||
177 | 69 | """Creates a new subtopic that key maps to if none exists.""" | ||
178 | 70 | if key not in self.subtopics: | ||
179 | 71 | self.subtopics[key] = Topic(key) | ||
180 | 72 | |||
181 | 73 | def _walk(self, path = ()): | ||
182 | 74 | """Returns an iterable of all absolute paths.""" | ||
183 | 75 | if len(self.subtopics) == 0: | ||
184 | 76 | yield path | ||
185 | 77 | else: | ||
186 | 78 | for subtopic in self.subtopics: | ||
187 | 79 | for path2 in subtopic._walk(path + self.name): | ||
188 | 80 | yield path2 | ||
189 | 81 | |||
190 | 82 | # The intention is for these methods to be implemented recursively | ||
191 | 83 | # by calling these methods on topics contained in the current | ||
192 | 84 | # topic (ie. this Help object). Queries are represented as a | ||
193 | 85 | # tuple denoting the topics. For example, "python syntax csv" | ||
194 | 86 | # would be represented as ('python', 'syntax', 'csv'). | ||
195 | 87 | |||
196 | 88 | def __contains__(self, query): | ||
197 | 89 | """Return True if query refers to a help topic. OTherwise, | ||
198 | 90 | False is returned.""" | ||
199 | 91 | t = self._translate_query(query) | ||
200 | 92 | |||
201 | 93 | if len(t) == 1: | ||
202 | 94 | return t[0] in self.subtopics | ||
203 | 95 | else: | ||
204 | 96 | return t[0] in self.subtopics and t[1:] in self.subtopics[t[0]] | ||
205 | 97 | |||
206 | 98 | def __getitem__(self, query): | ||
207 | 99 | """Returns the help topic denoted by query; topics in query | ||
208 | 100 | that do not exist should be created automatically. query may | ||
209 | 101 | refer to topic with subtopics.""" | ||
210 | 102 | t = self._translate_query(query) | ||
211 | 103 | self._ensure_key(t[0]) | ||
212 | 104 | |||
213 | 105 | if len(t) == 1: | ||
214 | 106 | return self.subtopics[t[0]] | ||
215 | 107 | else: | ||
216 | 108 | return self.subtopics[t[0]][t[1:]] | ||
217 | 109 | |||
218 | 110 | def __setitem__(self, query, value): | ||
219 | 111 | """Sets the help topic denoted by query to value. topics in | ||
220 | 112 | query that do not exist should be created automatically. | ||
221 | 113 | query may refer to topic with subtopics.""" | ||
222 | 114 | t = self._translate_query(query) | ||
223 | 115 | |||
224 | 116 | if len(t) == 1: | ||
225 | 117 | self.subtopics[t[0]] = value | ||
226 | 118 | else: | ||
227 | 119 | self._ensure_key(t[0]) | ||
228 | 120 | self.subtopics[t[0]][t[1:]] = value | ||
229 | 121 | |||
230 | 122 | def __delitem__(self, query): | ||
231 | 123 | """Deletes the reference to help topic denoted by query. | ||
232 | 124 | query may refer to topic with subtopics.""" | ||
233 | 125 | t = self._translate_query(query) | ||
234 | 126 | if len(t) == 1: | ||
235 | 127 | del self.subtopics[t[0]] | ||
236 | 128 | else: | ||
237 | 129 | del self.subtopics[t[0]][t[1:]] | ||
238 | 130 | |||
239 | 131 | def search(self, query): | ||
240 | 132 | """Returns an iterable of the absolute paths to topics that | ||
241 | 133 | partially match query.""" | ||
242 | 134 | t = self._translate_query(query) | ||
243 | 135 | return (path for path in self._walk() if issubseq(t, path)) | ||
244 | 136 | |||
245 | 137 | |||
246 | 0 | 138 | ||
247 | === modified file 'src/boots/lib/ui/generic.py' | |||
248 | --- src/boots/lib/ui/generic.py 2010-01-31 00:00:49 +0000 | |||
249 | +++ src/boots/lib/ui/generic.py 2010-02-04 01:10:27 +0000 | |||
250 | @@ -51,3 +51,5 @@ | |||
251 | 51 | """Prints row information in a "raw" format""" | 51 | """Prints row information in a "raw" format""" |
252 | 52 | def present(self, row): | 52 | def present(self, row): |
253 | 53 | print(row) | 53 | print(row) |
254 | 54 | |||
255 | 55 | |||
256 | 54 | 56 | ||
257 | === modified file 'src/boots/lib/ui/plain.py' | |||
258 | --- src/boots/lib/ui/plain.py 2010-02-02 02:48:04 +0000 | |||
259 | +++ src/boots/lib/ui/plain.py 2010-02-04 01:10:27 +0000 | |||
260 | @@ -34,17 +34,44 @@ | |||
261 | 34 | from boots.lib.ui.generic import StdinDriver, StdoutPresenter | 34 | from boots.lib.ui.generic import StdinDriver, StdoutPresenter |
262 | 35 | 35 | ||
263 | 36 | class PlainUI(StdinDriver, StdoutPresenter): | 36 | class PlainUI(StdinDriver, StdoutPresenter): |
264 | 37 | <<<<<<< TREE | ||
265 | 37 | def __init__(self, prompt): | 38 | def __init__(self, prompt): |
266 | 38 | self.metacommands = metacommands.MetaCommands({}, self) | 39 | self.metacommands = metacommands.MetaCommands({}, self) |
267 | 40 | ======= | ||
268 | 41 | def __init__(self, prompt, hist_length, hist_file): | ||
269 | 42 | readline.set_history_length(hist_length) | ||
270 | 43 | self.hist_file = hist_file | ||
271 | 44 | >>>>>>> MERGE-SOURCE | ||
272 | 39 | self.prompt = prompt | 45 | self.prompt = prompt |
273 | 46 | <<<<<<< TREE | ||
274 | 40 | self.current_widths = None | 47 | self.current_widths = None |
275 | 41 | self.buffer = [] | 48 | self.buffer = [] |
276 | 42 | 49 | ||
277 | 43 | def get_input(self, input_complete = lambda command: True): | 50 | def get_input(self, input_complete = lambda command: True): |
278 | 44 | return super(PlainUI, self).get_input(self.prompt, input_complete) | 51 | return super(PlainUI, self).get_input(self.prompt, input_complete) |
279 | 52 | ======= | ||
280 | 53 | |||
281 | 54 | def read_history(self): | ||
282 | 55 | try: | ||
283 | 56 | readline.read_history_file(self.hist_file) | ||
284 | 57 | except IOError: | ||
285 | 58 | pass | ||
286 | 59 | |||
287 | 60 | def save_history(self): | ||
288 | 61 | readline.write_history_file(self.hist_file) | ||
289 | 62 | |||
290 | 63 | def get_input(self): | ||
291 | 64 | while True: | ||
292 | 65 | var = raw_input(self.prompt) | ||
293 | 66 | yield var | ||
294 | 67 | |||
295 | 68 | def present(self, row): | ||
296 | 69 | print(row) | ||
297 | 70 | >>>>>>> MERGE-SOURCE | ||
298 | 45 | 71 | ||
299 | 46 | def set_prompt(self, prompt): | 72 | def set_prompt(self, prompt): |
300 | 47 | self.prompt = prompt | 73 | self.prompt = prompt |
301 | 74 | <<<<<<< TREE | ||
302 | 48 | 75 | ||
303 | 49 | def present(self, result): | 76 | def present(self, result): |
304 | 50 | def get_dashes(x): | 77 | def get_dashes(x): |
305 | @@ -79,3 +106,6 @@ | |||
306 | 79 | except: | 106 | except: |
307 | 80 | # Reset values for next result set. | 107 | # Reset values for next result set. |
308 | 81 | self.current_widths = None | 108 | self.current_widths = None |
309 | 109 | ======= | ||
310 | 110 | |||
311 | 111 | >>>>>>> MERGE-SOURCE |
Here is my code. I need to add a sort.