Merge lp:~chromakode/boots/localization into lp:~ken-brotherton/boots/local2
- localization
- Merge into local2
Proposed by
Max Goodhart
Status: | Merged |
---|---|
Merge reported by: | Max Goodhart |
Merged at revision: | not available |
Proposed branch: | lp:~chromakode/boots/localization |
Merge into: | lp:~ken-brotherton/boots/local2 |
Diff against target: |
2946 lines (+1110/-813) 32 files modified
boots/api/__init__.py (+1/-0) boots/api/api.py (+131/-29) boots/api/constructors.py (+106/-15) boots/api/errors.py (+34/-10) boots/api/nodes/node.py (+146/-64) boots/app/__init__.py (+1/-1) boots/app/client_config.py (+44/-6) boots/lib/__init__.py (+4/-0) boots/lib/console.py (+93/-44) boots/lib/lingos/bash_external.py (+2/-2) boots/lib/lingos/external.py (+2/-2) boots/lib/lingos/lingo.py (+6/-6) boots/lib/lingos/lisp/builtins.py (+3/-3) boots/lib/lingos/lisp/lexer.py (+2/-1) boots/lib/lingos/lisp/lisp.py (+9/-9) boots/lib/lingos/lisp/objects.py (+5/-5) boots/lib/lingos/lisp/parser.py (+4/-3) boots/lib/lingos/piped_sql.py (+17/-8) boots/lib/lingos/python.py (+9/-3) boots/lib/lingos/sql.py (+2/-1) boots/lib/ui/components/help.py (+3/-3) boots/lib/ui/components/metacommands.py (+39/-8) boots/lib/ui/generic.py (+6/-0) boots/lib/ui/plain.py (+99/-42) boots/util.py (+0/-27) localizations/potfiles.txt (+0/-12) po/README.txt (+12/-18) po/boots.pot (+316/-221) po/createpots.sh (+1/-1) tests/boots/api/api_tests.py (+10/-10) tests/boots/lib/lingos/cand_correct.txt (+0/-256) tests/boots/lib/lingos/lingo_tests.py (+3/-3) |
To merge this branch: | bzr merge lp:~chromakode/boots/localization |
Related bugs: | |
Related blueprints: |
Boots Localization
(High)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Boots Developers | Pending | ||
Review via email: mp+20843@code.launchpad.net |
Commit message
Description of the change
Merged with the latest boots trunk. Localization install code forthcoming.
To post a comment you must log in.
- 117. By Max Goodhart
-
Fix a couple translatable strings in client_config.py.
- 118. By Max Goodhart
-
Fix translatable strings again.
- 119. By Max Goodhart
-
Localize boots.api.
- 120. By Max Goodhart
-
Merge and localize latest updates to trunk.
- 121. By Max Goodhart
-
Update potfile.
- 122. By Max Goodhart
-
Update localization README.
- 123. By Max Goodhart
-
Fix another translatable string that slipped through.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'boots/api/__init__.py' |
2 | --- boots/api/__init__.py 2009-12-22 00:12:18 +0000 |
3 | +++ boots/api/__init__.py 2010-03-07 09:23:23 +0000 |
4 | @@ -0,0 +1,1 @@ |
5 | +from boots.lib import _, n_ |
6 | \ No newline at end of file |
7 | |
8 | === modified file 'boots/api/api.py' |
9 | --- boots/api/api.py 2010-02-27 05:40:10 +0000 |
10 | +++ boots/api/api.py 2010-03-07 09:23:23 +0000 |
11 | @@ -30,37 +30,63 @@ |
12 | |
13 | import time |
14 | from boots.api import errors |
15 | -from drizzle import db |
16 | +from boots.api.nodes.node import Status |
17 | +from boots.api import _, n_ |
18 | + |
19 | +_server_classes = [] |
20 | + |
21 | +def extract_field_indexes(cursor_description): |
22 | + """Returns a dictionary which maps field names to their indexes.""" |
23 | + return dict(zip((field[0] for field in cursor_description), range(0, len(cursor_description)))) |
24 | + |
25 | +class Rows(list): |
26 | + """Represents a chunk of rows.""" |
27 | + def __init__(self, sequence, field_indexes): |
28 | + super(Rows, self).__init__(sequence) |
29 | + self.field_indexes = field_indexes |
30 | + |
31 | +class ResultInfo(Status): |
32 | + pass |
33 | |
34 | class Server(object): |
35 | """Represents a connection to a database server. |
36 | |
37 | This object is used to execute commands on a remote server.""" |
38 | |
39 | - def __init__(self, hostname, port, auth): |
40 | + def __init__(self, dbapi_module, hostname, port, auth): |
41 | """Initialize a Server object. |
42 | |
43 | hostname should be a string. |
44 | port should be a positive integer value. |
45 | auth should be a dictionary possibly containing keys: |
46 | 'username', 'password', and 'database' mapping to their |
47 | - appropriate string values.""" |
48 | + appropriate string values.""" |
49 | + self.db = dbapi_module |
50 | self.hostname = hostname |
51 | self.port = port |
52 | self.auth = auth |
53 | self._connection = None |
54 | |
55 | + def __str__(self): |
56 | + return "{0}:{1}".format(self.hostname, self.port) |
57 | + |
58 | + def _connect(self): |
59 | + return self.db.connect( |
60 | + host = self.hostname, |
61 | + port = self.port, |
62 | + username = self.auth.get("username", ""), |
63 | + password = self.auth.get("password", ""), |
64 | + database = self.auth.get("database", "")) |
65 | + |
66 | def connect(self): |
67 | """Establishes database connection. |
68 | |
69 | Connect to the database server whose details were provided through |
70 | the __init__ method.""" |
71 | - self._connection = db.connect( |
72 | - host = self.hostname, |
73 | - port = self.port, |
74 | - username = self.auth.get('username', ""), |
75 | - password = self.auth.get('password', ""), |
76 | - database = self.auth.get('database', "")) |
77 | + try: |
78 | + self._connection = self._connect() |
79 | + except self.db.Error: |
80 | + raise errors.ConnectionError(_("Could not connect to {0}").format(self)) |
81 | |
82 | @property |
83 | def is_connected(self): |
84 | @@ -72,38 +98,60 @@ |
85 | def _check_connected(self): |
86 | """Raise a ProgrammingError if the Connection has been closed.""" |
87 | if not self.is_connected: |
88 | - raise errors.ConnectionError("Connection closed") |
89 | + raise errors.ConnectionError(_("Connection closed")) |
90 | |
91 | def disconnect(self): |
92 | self._check_connected() |
93 | """Disconnect from the connected database server.""" |
94 | self._connection.close() |
95 | self._connection = None |
96 | + |
97 | + def _execute(self, query): |
98 | + cursor = self._connection.cursor() |
99 | + cursor.execute(query) |
100 | + return cursor |
101 | |
102 | def execute(self, query): |
103 | - #FIXME: I don't like how this is phrased, but cannot right now think of a better way to do it. |
104 | - #FIXME: Gotta come back to that, I think. |
105 | - """Executes a query, returning an iterator of resulting rows. |
106 | - |
107 | - The resulting rows are packaged in a 'packet' which is a dict: |
108 | - {"__server_execute_sql_query": query, |
109 | - "info": meta data, |
110 | - "result": chunk of rows (list of tuples)}""" |
111 | - |
112 | + """Executes a query, returning an iterator of resulting rows.""" |
113 | self._check_connected() |
114 | - cursor = self._connection.cursor(convert=False) |
115 | begin_time = time.time() |
116 | - cursor.execute(query) |
117 | + |
118 | + try: |
119 | + cursor = self._execute(query) |
120 | + except errors.LostConnectionError as lost_e: |
121 | + # FIXME: this is NOT thread safe |
122 | + # Attempt to reconnect and retry |
123 | + yield Status(_("Lost connection to {0}. Reconnecting...").format(self)) |
124 | + |
125 | + if self.is_connected: |
126 | + self.disconnect() |
127 | + |
128 | + try: |
129 | + self.connect() |
130 | + cursor = self._execute(query) |
131 | + except Exception as e: |
132 | + lost_e.reconnect_exception = e |
133 | + raise lost_e |
134 | + else: |
135 | + yield Status(_("Reconnected.")) |
136 | + |
137 | if cursor.description: |
138 | + field_indexes = extract_field_indexes(cursor.description) |
139 | + |
140 | while True: |
141 | chunk = cursor.fetchmany(256) |
142 | - packet = {"__server_execute_sql_query": query, |
143 | - "info": cursor.description, |
144 | + # FIXME: Eventually it would be good to implement chunk packeting |
145 | + # as a generic nodes feature. |
146 | + if chunk: |
147 | + yield Rows(chunk, field_indexes) |
148 | + else: |
149 | + break |
150 | + |
151 | + yield ResultInfo({"description": cursor.description, |
152 | + "row_count": cursor.rowcount, |
153 | "begin_time": begin_time, |
154 | - "result": chunk} |
155 | - yield packet |
156 | - if not chunk: |
157 | - return |
158 | + "end_time": time.time()}) |
159 | + return |
160 | |
161 | @property |
162 | def server_version(self): |
163 | @@ -115,7 +163,61 @@ |
164 | self._check_connected() |
165 | return self._connection.protocol_version |
166 | |
167 | +try: |
168 | + import drizzle.db |
169 | + class DrizzleServer(Server): |
170 | + def __init__(self, hostname, port, auth): |
171 | + Server.__init__(self, drizzle.db, hostname, port, auth) |
172 | + |
173 | + def _execute(self, query): |
174 | + try: |
175 | + cursor = self._connection.cursor(convert=False) |
176 | + cursor.execute(query) |
177 | + except drizzle.errors.LostConnectionError: |
178 | + raise errors.LostConnectionError(server=self) |
179 | + else: |
180 | + return cursor |
181 | + |
182 | +except ImportError: |
183 | + pass |
184 | +else: |
185 | + _server_classes.append(DrizzleServer) |
186 | + |
187 | +try: |
188 | + import MySQLdb |
189 | + import MySQLdb.converters |
190 | + class MySQLServer(Server): |
191 | + def __init__(self, hostname, port, auth): |
192 | + Server.__init__(self, MySQLdb, hostname, port, auth) |
193 | + |
194 | + def _connect(self): |
195 | + """Establishes database connection. |
196 | + |
197 | + Connect to the database server whose details were provided through |
198 | + the __init__ method.""" |
199 | + |
200 | + converter = self.db.converters.conversions.copy() |
201 | + for x in range(0,256): |
202 | + converter.pop(x, None) |
203 | + if self.auth["database"] == None: |
204 | + self.auth["database"] = "" |
205 | + return self.db.connect( |
206 | + host = self.hostname, |
207 | + port = self.port, |
208 | + user = self.auth.get("username", ""), |
209 | + passwd = self.auth.get("password", ""), |
210 | + db = self.auth.get("database", ""), |
211 | + conv = converter) |
212 | + |
213 | + @property |
214 | + def server_version(self): |
215 | + self._check_connected() |
216 | + return self._connection.get_server_info() |
217 | +except ImportError: |
218 | + pass |
219 | +else: |
220 | + _server_classes.append(MySQLServer) |
221 | + |
222 | class Query(str): |
223 | def substitute(self, rows): pass |
224 | - |
225 | -class Row(dict): pass |
226 | + |
227 | |
228 | === modified file 'boots/api/constructors.py' |
229 | --- boots/api/constructors.py 2010-02-27 05:40:10 +0000 |
230 | +++ boots/api/constructors.py 2010-03-07 09:23:23 +0000 |
231 | @@ -21,7 +21,11 @@ |
232 | # |
233 | # ##### END LICENSE BLOCK ##### |
234 | |
235 | +import re |
236 | + |
237 | +from boots.api.api import Rows, ResultInfo |
238 | from boots.api.nodes.node import NodeGraph, SyncNode, IteratorNode |
239 | +from boots.api import _, n_ |
240 | |
241 | _constructors = {} |
242 | |
243 | @@ -29,7 +33,7 @@ |
244 | """Allow the function constructor to be used from PipedSQL under name. constructor is a |
245 | function that takes any number of strings as arguments and returns a node.""" |
246 | if name in _constructors: |
247 | - raise KeyError('A constructor with name ' + name + ' already exists') |
248 | + raise KeyError(_('A constructor with name {0} already exists').format(name)) |
249 | |
250 | _constructors[name] = constructor |
251 | |
252 | @@ -40,7 +44,7 @@ |
253 | def construct(name, *arguments): |
254 | """Contructs a node using the given name and arguments.""" |
255 | if name not in _constructors: |
256 | - raise KeyError('There is no constructor with the name' + ' {0}.'.format(name)) |
257 | + raise KeyError(_('There is no constructor with the name {0}.').format(name)) |
258 | |
259 | return _constructors[name](*arguments) |
260 | |
261 | @@ -52,18 +56,105 @@ |
262 | |
263 | return register_constructor_with_name |
264 | |
265 | +@register('csv_in') |
266 | +def csv_input_file_node(path): |
267 | + """Constructs a node that reads from a csv file and then sends the word to its outputs.""" |
268 | + input_file = open(path) |
269 | + |
270 | + def read_iterator(): |
271 | + for line in input_file: |
272 | + for word in line.split(','): |
273 | + yield word |
274 | + |
275 | + return IteratorNode(read_iterator()) |
276 | + |
277 | @register('csv_out') |
278 | def csv_output_file_node(path): |
279 | - """Constructs a node that writes each object it is passed to a |
280 | - file and then sends the object to its outputs.""" |
281 | - output_file = open(path, 'w') |
282 | - |
283 | - def write(result): |
284 | - if type(result) is dict and "__server_execute_sql_query" in result: |
285 | - output_file.write('\n'.join(','.join(row) for row in result['result'])) |
286 | - else: |
287 | - output_file.write(str(result) + '\n') |
288 | - |
289 | - return StopIteration |
290 | - |
291 | - return SyncNode(write) |
292 | + """Constructs a node that writes each object it is passed to a file and then sends the object to |
293 | + its outputs.""" |
294 | + class CSVOutput(object): |
295 | + def __init__(self): |
296 | + self.write_comma = False |
297 | + self.output_file = open(path, 'w') |
298 | + |
299 | + def write(self, result): |
300 | + if type(result) is Rows: |
301 | + self.output_file.write('\n'.join(','.join(row) for row in result)) |
302 | + else: |
303 | + if self.write_comma: |
304 | + self.output_file.write(',') |
305 | + |
306 | + self.output_file.write(str(result)) |
307 | + |
308 | + if not isinstance(result, str) or not result.endswith('\n'): |
309 | + self.write_comma = True |
310 | + else: |
311 | + self.write_comma = False |
312 | + |
313 | + self.output_file.flush() |
314 | + |
315 | + csv_output = CSVOutput() |
316 | + return SyncNode(csv_output.write) |
317 | + |
318 | +@register('sink') |
319 | +def sink_node(): |
320 | + """Constructs a node that ignores all input.""" |
321 | + return SyncNode(lambda x: None) |
322 | + |
323 | +@register('sub') |
324 | +def substitute_node(pattern, replacement, field = None): |
325 | + """Constructs a node that replaces any text matching pattern with replacement as if by re.sub. |
326 | + field can be used to specify the name of the field to perform substitution against. The default |
327 | + is to substitute against all fields.""" |
328 | + |
329 | + regexp = re.compile(pattern) |
330 | + |
331 | + def sub(result): |
332 | + if type(result) is Rows: |
333 | + field_indexes = result.field_indexes |
334 | + |
335 | + if field: |
336 | + index = field_indexes[field] |
337 | + |
338 | + def sub_field(row, replacement): |
339 | + row = list(row) |
340 | + row[index] = replacement |
341 | + return tuple(row) |
342 | + |
343 | + return Rows((sub_field(row, regexp.sub(replacement, row[index])) for row in result), field_indexes) |
344 | + else: |
345 | + return Rows((tuple(regexp.sub(replacement, field) for field in row) for row in result), field_indexes) |
346 | + |
347 | + elif isinstance(result, str): |
348 | + return regexp.sub(replacement, result) |
349 | + else: |
350 | + return result |
351 | + |
352 | + return SyncNode(sub) |
353 | + |
354 | +@register('filter') |
355 | +def filter_node(pattern, field = None): |
356 | + """Constructs a node that allows text to pass through only if it matches pattern. A row is |
357 | + allowed to pass through if at least one of its fields matches pattern. field can be used to |
358 | + specify the name of the field to perform substitution against. The default is to substitute |
359 | + against all fields.""" |
360 | + |
361 | + regexp = re.compile(pattern) |
362 | + |
363 | + def filter(result): |
364 | + if type(result) is Rows: |
365 | + field_indexes = result.field_indexes |
366 | + |
367 | + if field: |
368 | + index = field_indexes[field] |
369 | + return Rows((row for row in result if regexp.match(row[index])), field_indexes) |
370 | + else: |
371 | + return Rows((row for row in result if any(map(regexp.match, row))), field_indexes) |
372 | + |
373 | + elif isinstance(result, str): |
374 | + if regexp.match(result): |
375 | + return result |
376 | + else: |
377 | + return result |
378 | + |
379 | + return SyncNode(filter) |
380 | |
381 | === modified file 'boots/api/errors.py' |
382 | --- boots/api/errors.py 2010-02-27 05:40:10 +0000 |
383 | +++ boots/api/errors.py 2010-03-07 09:23:23 +0000 |
384 | @@ -22,15 +22,39 @@ |
385 | # ##### END LICENSE BLOCK ##### |
386 | |
387 | import exceptions |
388 | +import traceback |
389 | + |
390 | +from boots.api import _, n_ |
391 | |
392 | class BootsError(exceptions.StandardError): |
393 | - """Base class of boots error exceptions""" |
394 | - pass |
395 | - |
396 | -class ConsoleError(BootsError): |
397 | - """Errors to be displayed to be displayed on the console""" |
398 | - pass |
399 | - |
400 | -class ConnectionError(ConsoleError): |
401 | - """Errors related to server connections""" |
402 | - pass |
403 | + """Base class of boots error exceptions.""" |
404 | + pass |
405 | + |
406 | +class BootsWarning(exceptions.Warning): |
407 | + """Base class of boots warnings.""" |
408 | + pass |
409 | + |
410 | +class ConnectionError(BootsError): |
411 | + """Errors related to server connections.""" |
412 | + pass |
413 | + |
414 | +class LostConnectionError(ConnectionError): |
415 | + """Raised when a connection is lost.""" |
416 | + def __init__(self, msg=None, server=None, reconnect_exception=None): |
417 | + self.msg = msg |
418 | + self.server = server |
419 | + self.reconnect_exception = reconnect_exception |
420 | + |
421 | + def __str__(self): |
422 | + msg = [] |
423 | + if self.msg: |
424 | + msg.append(self.msg) |
425 | + else: |
426 | + msg.append(_("Lost connection to {0}").format(self.server)) |
427 | + |
428 | + if self.reconnect_exception: |
429 | + exc_lines = traceback.format_exception_only(type(self.reconnect_exception), self.reconnect_exception) |
430 | + exc_text = ". ".join(line.strip() for line in exc_lines) |
431 | + msg.append(_("(Exception upon reconnect: {0})").format(exc_text)) |
432 | + |
433 | + return " ".join(msg) |
434 | |
435 | === modified file 'boots/api/nodes/node.py' |
436 | --- boots/api/nodes/node.py 2010-03-02 00:45:59 +0000 |
437 | +++ boots/api/nodes/node.py 2010-03-07 09:23:23 +0000 |
438 | @@ -21,12 +21,13 @@ |
439 | # |
440 | # ##### END LICENSE BLOCK ##### |
441 | |
442 | +from __future__ import print_function |
443 | + |
444 | +import sys |
445 | from Queue import Queue |
446 | -from itertools import chain, tee, repeat |
447 | +from itertools import chain, tee, repeat, ifilter |
448 | from threading import Thread, Event, Lock |
449 | from types import GeneratorType |
450 | -import gettext |
451 | -gettext.install("boots",localedir=None,unicode=1) |
452 | |
453 | def catch_exceptions(iterable): |
454 | try: |
455 | @@ -34,6 +35,11 @@ |
456 | yield x |
457 | except Exception as e: |
458 | yield e |
459 | + |
460 | +def trace(msg, iterable): |
461 | + for x in iterable: |
462 | + print(msg, repr(x), file=sys.stderr) |
463 | + yield x |
464 | |
465 | def alternate_iterators(*iterables): |
466 | """Iterate over the given iterables, taking turns. |
467 | @@ -62,7 +68,7 @@ |
468 | yield x |
469 | |
470 | def imap_flatten(func, iterable): |
471 | - """Map a function over the iterables, flattening the result.""" |
472 | + """Map a function over the iterable, flattening the result.""" |
473 | for x in iterable: |
474 | result = func(x) |
475 | for item in flatten(result): |
476 | @@ -70,27 +76,27 @@ |
477 | |
478 | # FIXME: need a thread safe tee here, perhapse "safetee"? |
479 | |
480 | -class BaseNode(object): |
481 | - """Base class for the node system.""" |
482 | - |
483 | - def __init__(self, name=None, inputs=None, outputs=None): |
484 | +class Status(object): |
485 | + def __init__(self, value): |
486 | + self.value = value |
487 | + |
488 | + def __str__(self): |
489 | + return str(self.value) |
490 | + |
491 | +def filter_default(value): |
492 | + return not isinstance(value, Status) and not isinstance(value, Exception) |
493 | + |
494 | +def filter_none(value): |
495 | + return True |
496 | + |
497 | +class PrimitiveNode(object): |
498 | + def __init__(self, name=None): |
499 | """Constructor of the base node object. |
500 | |
501 | Initializes the node object. |
502 | Takes the node name, inputs and outputs as parameters. |
503 | """ |
504 | self.name = name |
505 | - self._finalized = False |
506 | - |
507 | - if inputs: |
508 | - self.inputs = set(inputs) |
509 | - else: |
510 | - self.inputs = set() |
511 | - |
512 | - if outputs: |
513 | - self.outputs = set(outputs) |
514 | - else: |
515 | - self.outputs = set() |
516 | |
517 | @property |
518 | def name(self): |
519 | @@ -117,6 +123,28 @@ |
520 | For the base node object, returns NONE.""" |
521 | return None |
522 | |
523 | +class BaseNode(PrimitiveNode): |
524 | + """Base class for the node system.""" |
525 | + |
526 | + def __init__(self, name=None, inputs=None, outputs=None): |
527 | + """Constructor of the base node object. |
528 | + |
529 | + Initializes the node object. |
530 | + Takes the node name, inputs and outputs as parameters. |
531 | + """ |
532 | + PrimitiveNode.__init__(self, name) |
533 | + self._finalized = False |
534 | + |
535 | + if inputs: |
536 | + self.inputs = set(inputs) |
537 | + else: |
538 | + self.inputs = set() |
539 | + |
540 | + if outputs: |
541 | + self.outputs = set(outputs) |
542 | + else: |
543 | + self.outputs = set() |
544 | + |
545 | def finalize(self): |
546 | """Finalizes node setup. |
547 | |
548 | @@ -240,33 +268,40 @@ |
549 | assert input_edges == output_edges, "Graph integrity check failed." |
550 | return output_edges |
551 | |
552 | - def add(self, *nodes): |
553 | + def add(self, *nodes, **flags): |
554 | """Add individual nodes to the collection. |
555 | |
556 | Accepts a list of nodes to be added to the collection.""" |
557 | - return self._add(False, nodes) |
558 | + self._add(False, nodes) |
559 | + if flags.get("input", False): |
560 | + for node in nodes: |
561 | + self.input_link.connect_output(node) |
562 | + elif flags.get("output", False): |
563 | + for node in nodes: |
564 | + node.connect_output(self.output_link) |
565 | + |
566 | + return self |
567 | |
568 | def add_input(self, *nodes): |
569 | """Adds nodes to the input link. |
570 | |
571 | Accepts a list of nodes and adds them to the input_link of this |
572 | graph.""" |
573 | - self.add(*nodes) |
574 | - for node in nodes: |
575 | - self.input_link.connect_output(node) |
576 | + self.add(*nodes, input=True) |
577 | + return self |
578 | |
579 | def add_output(self, *nodes): |
580 | """Adds nodes to the output link. |
581 | |
582 | Accepts a list of nodes and adds them to the output_link of this |
583 | graph.""" |
584 | - self.add(*nodes) |
585 | - for node in nodes: |
586 | - node.connect_output(self.output_link) |
587 | + self.add(*nodes, output=True) |
588 | + return self |
589 | |
590 | def add_connected(self, *nodes): |
591 | """Add all connected nodes to the collection.""" |
592 | - return self._add(True, nodes) |
593 | + self._add(True, nodes) |
594 | + return self |
595 | |
596 | def _add(self, traverse, nodes): |
597 | if traverse: |
598 | @@ -278,8 +313,6 @@ |
599 | self.nodes.add(node) |
600 | if node.name: |
601 | self.names[node.name] = node |
602 | - |
603 | - return nodes |
604 | |
605 | def link_output(self, output_graph): |
606 | """Link output link of this node graph to input of another.""" |
607 | @@ -346,6 +379,9 @@ |
608 | # Notify all nodes that their connections are final. |
609 | for node in self.nodes: |
610 | node.finalize() |
611 | + |
612 | + self.input_link.finalize() |
613 | + self.output_link.finalize() |
614 | |
615 | self._finalized = True |
616 | |
617 | @@ -370,14 +406,21 @@ |
618 | self.output_link.put(data) |
619 | |
620 | class ActionNode(BaseNode): |
621 | - def __init__(self, action, name=None, inputs=None, outputs=None, include_stop=False): |
622 | + def __init__(self, action, name=None, inputs=None, outputs=None, filter=None): |
623 | BaseNode.__init__(self, name, inputs, outputs) |
624 | + self.filter = filter or filter_default |
625 | self.action = action |
626 | - |
627 | + |
628 | + def _do_action(self, data): |
629 | + if self.filter(data): |
630 | + return self.action(data) |
631 | + else: |
632 | + return data |
633 | + |
634 | def _run(self): |
635 | for data in self._iter_inputs(): |
636 | try: |
637 | - for result in flatten(self.action(data)): |
638 | + for result in flatten(self._do_action(data)): |
639 | self._put_outputs(result) |
640 | except Exception as e: |
641 | self.put_outputs(e) |
642 | @@ -392,7 +435,7 @@ |
643 | class QueueNode(BaseNode): |
644 | """Thread-safe queue that buffers pushed data for pull access.""" |
645 | |
646 | - def __init__(self, name=None, inputs=None, outputs=None, include_stop=False): |
647 | + def __init__(self, name=None, inputs=None, outputs=None): |
648 | BaseNode.__init__(self, name, inputs, outputs) |
649 | self._queue = Queue() |
650 | self._unfinished_inputs = None |
651 | @@ -413,31 +456,39 @@ |
652 | else: |
653 | yield data |
654 | |
655 | -class LinkNode(BaseNode): |
656 | - """Link node to connect to nodes in a dynamic, thread-safe manner.""" |
657 | - |
658 | - def __init__(self, name=None, inputs=None, outputs=None, mortal=False): |
659 | +class BaseLinkNode(object): |
660 | + def __init__(self): |
661 | """Initializes link node.""" |
662 | - BaseNode.__init__(self, name, inputs, outputs) |
663 | self.input_links = set() |
664 | + self.output_links = set() |
665 | self.input_lock = Lock() |
666 | - self.output_links = set() |
667 | self.output_lock = Lock() |
668 | - self.mortal = mortal |
669 | - |
670 | + |
671 | def link_output(self, output_link): |
672 | """Links own output to another unit's input link.""" |
673 | - with self.input_lock: |
674 | - with output_link.output_lock: |
675 | + with self.output_lock: |
676 | + with output_link.input_lock: |
677 | self.output_links.add(output_link) |
678 | output_link.input_links.add(self) |
679 | |
680 | def unlink_output(self, output_link): |
681 | """Unlinks own output from another unit's input link.""" |
682 | - with self.input_lock: |
683 | - with output_link.output_lock: |
684 | + with self.output_lock: |
685 | + with output_link.input_lock: |
686 | self.output_links.remove(output_link) |
687 | output_link.input_links.remove(self) |
688 | + |
689 | +class LinkNode(BaseNode, BaseLinkNode): |
690 | + """Link node to connect to nodes in a dynamic, thread-safe manner.""" |
691 | + |
692 | + def __init__(self, name=None, inputs=None, outputs=None, mortal=False): |
693 | + """Initializes link node.""" |
694 | + BaseNode.__init__(self, name, inputs, outputs) |
695 | + BaseLinkNode.__init__(self) |
696 | + self.mortal = mortal |
697 | + |
698 | + def _finalize(self): |
699 | + self._input_iter = self._iter_inputs() |
700 | |
701 | def put(self, data): |
702 | """Push data through the link.""" |
703 | @@ -471,23 +522,57 @@ |
704 | yield |
705 | |
706 | def __iter__(self): |
707 | - return alternate_iterators(self._iter_inputs(), self._iter_links()) |
708 | - |
709 | + return alternate_iterators(self._input_iter, self._iter_links()) |
710 | + |
711 | +class LinkStackNode(PrimitiveNode, BaseLinkNode): |
712 | + """Link node to read input from a stack of nodes, popping off the stack |
713 | + when a node finishes.""" |
714 | + |
715 | + def __init__(self, name=None): |
716 | + """Initializes link node.""" |
717 | + PrimitiveNode.__init__(self, name) |
718 | + BaseLinkNode.__init__(self) |
719 | + self.input_stack = list() |
720 | + |
721 | + def push_input(self, input_link): |
722 | + with self.input_lock: |
723 | + with input_link.output_lock: |
724 | + self.input_stack.append(input_link) |
725 | + input_link.output_links.add(self) |
726 | + |
727 | + def pop_input(self): |
728 | + with self.input_lock: |
729 | + input_link = self.input_stack.pop() |
730 | + with input_link.output_lock: |
731 | + input_link.output_links.remove(self) |
732 | + |
733 | + def __iter__(self): |
734 | + while True: |
735 | + with self.input_lock: |
736 | + top_input = self.input_stack[-1] if self.input_stack else None |
737 | + |
738 | + if top_input: |
739 | + try: |
740 | + yield iter(top_input).next() |
741 | + except StopIteration: |
742 | + self.pop_input() |
743 | + else: |
744 | + return |
745 | + |
746 | class SyncNode(ActionNode): |
747 | """Synchronous lazy pull-based node.""" |
748 | def _finalize(self): |
749 | if self.inputs: |
750 | - self._input_iter = self._iter_inputs() |
751 | + self._result_iter = imap_flatten(self._do_action, self._iter_inputs()) |
752 | else: |
753 | - self._input_iter = repeat(None) |
754 | - self._result_iter = catch_exceptions(imap_flatten(self.action, self._input_iter)) |
755 | - self._tees = list(tee(self._result_iter, len(self.outputs))) |
756 | + self._result_iter = self.action |
757 | + self._tees = list(tee(catch_exceptions(self._result_iter), len(self.outputs))) |
758 | |
759 | def put(self, data): |
760 | if data is StopIteration: |
761 | result = StopIteration |
762 | else: |
763 | - result = self.action(data) |
764 | + result = self._do_action(data) |
765 | |
766 | self._put_outputs(result) |
767 | |
768 | @@ -501,19 +586,16 @@ |
769 | self._run() |
770 | |
771 | class IteratorNode(SyncNode): |
772 | - def __init__(self, value, name=None, inputs=None, outputs=None, include_stop=False): |
773 | + def __init__(self, value, name=None, inputs=None, outputs=None, filter=None): |
774 | self.iterator = iter(value) |
775 | - SyncNode.__init__(self, self.advance, name, inputs, outputs, include_stop) |
776 | - |
777 | - def advance(self, data): |
778 | - return self.iterator.next() |
779 | + SyncNode.__init__(self, self.iterator, name, inputs, outputs, filter) |
780 | |
781 | class AsyncNode(ActionNode, Thread): |
782 | """Asynchronous push-based node. Runs in a thread.""" |
783 | |
784 | - def __init__(self, action=None, name=None, inputs=None, outputs=None, include_stop=False, profile=False): |
785 | + def __init__(self, action=None, name=None, inputs=None, outputs=None, filter=None, profile=False): |
786 | Thread.__init__(self) |
787 | - ActionNode.__init__(self, action, name, inputs, outputs, include_stop) |
788 | + ActionNode.__init__(self, action, name, inputs, outputs, filter) |
789 | if action: |
790 | self.action = action |
791 | else: |
792 | @@ -544,12 +626,12 @@ |
793 | def test(): |
794 | def wait(x): |
795 | import time |
796 | - print "waiting: {0}".format(x) |
797 | + print("waiting: {0}".format(x)) |
798 | time.sleep(1) |
799 | return x |
800 | |
801 | def print_it(foo): |
802 | - print "printing: {0}".format(foo) |
803 | + print("printing: {0}".format(foo)) |
804 | return foo |
805 | |
806 | nodes = NodeGraph.from_sequence([SyncNode(lambda x:xrange(10), name="count"), |
807 | @@ -557,9 +639,9 @@ |
808 | SyncNode(print_it, name="print"), |
809 | QueueNode(name="out")]) |
810 | nodes.start() |
811 | - print nodes.edges() |
812 | + print(nodes.edges()) |
813 | for result in nodes["out"]: |
814 | - print result |
815 | + print(result) |
816 | |
817 | if __name__ == "__main__": |
818 | test() |
819 | |
820 | === modified file 'boots/app/__init__.py' |
821 | --- boots/app/__init__.py 2009-12-23 20:58:28 +0000 |
822 | +++ boots/app/__init__.py 2010-03-07 09:23:23 +0000 |
823 | @@ -1,1 +1,1 @@ |
824 | -__all__ = ["boots", "config", "info"] |
825 | +from boots.lib import _, n_ |
826 | \ No newline at end of file |
827 | |
828 | === modified file 'boots/app/client_config.py' |
829 | --- boots/app/client_config.py 2010-03-02 00:45:59 +0000 |
830 | +++ boots/app/client_config.py 2010-03-07 09:23:23 +0000 |
831 | @@ -26,9 +26,32 @@ |
832 | options/arguments.""" |
833 | |
834 | import optparse |
835 | -import os |
836 | +import os, os.path |
837 | import info |
838 | -from boots.util import set_gettext |
839 | + |
840 | +from boots.app import _, n_ |
841 | + |
842 | +def find_executable(name): |
843 | + # Search algorithm adapted from os._execvpe |
844 | + head, tail = os.path.split(name) |
845 | + if head and os.path.exists(name): |
846 | + return name |
847 | + else: |
848 | + if 'PATH' in os.environ: |
849 | + envpath = os.environ['PATH'] |
850 | + else: |
851 | + envpath = os.defpath |
852 | + PATH = envpath.split(os.pathsep) |
853 | + for dir in PATH: |
854 | + fullname = os.path.join(dir, name) |
855 | + if os.path.exists(fullname): |
856 | + return fullname |
857 | + |
858 | +def get_auto_pager(): |
859 | + if find_executable("less"): |
860 | + return "less -XF" |
861 | + elif find_executable("more"): |
862 | + return "more" |
863 | |
864 | class ClientConfig(object): |
865 | """Class used to read client configuration from files and the cli.""" |
866 | @@ -42,7 +65,7 @@ |
867 | self._defaults = {"command": None, |
868 | "database": None, |
869 | "script": None, |
870 | - 'lingo': 'sql', |
871 | + "lingo": "sql", |
872 | "rcfile": info.RCFILE, |
873 | "host": "localhost", |
874 | "port": 9306, |
875 | @@ -50,6 +73,8 @@ |
876 | "password": False, |
877 | "prompt1": "> ", |
878 | "prompt2": "+ ", |
879 | + "pager": None, |
880 | + "pager_command": None, |
881 | "terminating_char": ";", |
882 | "history_length": 100, |
883 | "history_file": os.path.expanduser("~/.boots_history")} |
884 | @@ -110,6 +135,11 @@ |
885 | type = "string", |
886 | dest = "password", |
887 | help = _("Connect using password. If none is given, query for password")) |
888 | + self._cli_parser.add_option("--pager", |
889 | + action = "store", |
890 | + type = "string", |
891 | + dest = "pager", |
892 | + help = _("Pipe query results to the specified pager.")) |
893 | self._cli_parser.add_option("-t", "--terminatingchar", |
894 | action = "store", |
895 | type = "string", |
896 | @@ -155,6 +185,14 @@ |
897 | |
898 | self._dict.update(from_file) |
899 | self._dict.update(from_cli) |
900 | + self._interpret_options() |
901 | + |
902 | + def _interpret_options(self): |
903 | + # Interpret options |
904 | + if self["pager"] == "auto": |
905 | + self["pager_command"] = get_auto_pager() |
906 | + elif self["pager"]: |
907 | + self["pager_command"] = self["pager"] |
908 | |
909 | def get_file_conf(self, filepath): |
910 | """Read a configuration from the specified file. Return a dict.""" |
911 | @@ -164,11 +202,11 @@ |
912 | except IOError: |
913 | if filepath != info.RCFILE: |
914 | # Only print an error if a non-default rc file was specified. |
915 | - print("rcfile {0} "+_("not found").format(filepath)) |
916 | + print(_("rcfile {0} not found").format(filepath)) |
917 | except SyntaxError: |
918 | - print("rcfile {0} "+_("contains a syntax error").format(filepath)) |
919 | + print(_("rcfile {0} contains a syntax error").format(filepath)) |
920 | except Exception as e: |
921 | - print("rcfile {0} "+_("contains an error:")+" {1}".format(filepath, e)) |
922 | + print(_("rcfile {0} contains an error: {1}").format(filepath, e)) |
923 | return file_dict |
924 | |
925 | def get_cli_conf(self): |
926 | |
927 | === modified file 'boots/lib/__init__.py' |
928 | --- boots/lib/__init__.py 2009-12-22 00:12:18 +0000 |
929 | +++ boots/lib/__init__.py 2010-03-07 09:23:23 +0000 |
930 | @@ -0,0 +1,4 @@ |
931 | +import gettext |
932 | +translation = gettext.translation("boots", fallback=True) |
933 | +_ = translation.lgettext |
934 | +n_ = translation.lngettext |
935 | \ No newline at end of file |
936 | |
937 | === modified file 'boots/lib/console.py' |
938 | --- boots/lib/console.py 2010-03-02 00:45:59 +0000 |
939 | +++ boots/lib/console.py 2010-03-07 09:23:23 +0000 |
940 | @@ -24,9 +24,11 @@ |
941 | """This module provides the boots Console class. This class is the core |
942 | driver of any boots client.""" |
943 | |
944 | +import sys |
945 | + |
946 | from boots.api import api |
947 | -from boots.api.errors import ConsoleError |
948 | -from boots.api.nodes import node |
949 | +from boots.api.errors import BootsError, BootsWarning, ConnectionError |
950 | +from boots.api.nodes.node import NodeGraph, IteratorNode, LinkStackNode |
951 | from boots.lib.ui.plain import PlainUI |
952 | from boots.lib.ui.generic import ScriptDriver, StringDriver, StdoutPresenter |
953 | from boots.lib.ui.components.help import HelpTopic, QueryIndexTopic |
954 | @@ -34,14 +36,17 @@ |
955 | from boots.lib.hook import Hooks |
956 | from boots.lib.lingos.lingo import lingo_registry |
957 | from boots.lib.lingos import bash_external |
958 | -from boots.lib.lingos.lisp import lisp |
959 | from boots.lib.lingos import python |
960 | from boots.lib.lingos import piped_sql |
961 | from boots.lib.lingos import sql |
962 | -from boots.util import set_gettext |
963 | +from boots.lib import _, n_ |
964 | +try: |
965 | + from boots.lib.lingos.lisp import lisp |
966 | +except ImportError: |
967 | + # Allow the lisp lingo import to fail if Ply is not installed. |
968 | + pass |
969 | import sys |
970 | |
971 | - |
972 | # Utility function. |
973 | def iterable(obj): |
974 | """Returns True if obj is iterable; otherwise, False is returned.""" |
975 | @@ -50,6 +55,18 @@ |
976 | return True |
977 | except TypeError: |
978 | return False |
979 | + |
980 | +class ConsoleError(BootsError): |
981 | + """Errors to be displayed on the console.""" |
982 | + pass |
983 | + |
984 | +class InternalError(BootsError): |
985 | + """Errors to be displayed on the console.""" |
986 | + def __init__(self, exception): |
987 | + self.exception = exception |
988 | + |
989 | + def __str__(self): |
990 | + return _("Boots encountered an internal error: {0}").format(self.exception) |
991 | |
992 | class Console(object): |
993 | def __init__(self, config, welcome_msg=None): |
994 | @@ -67,7 +84,9 @@ |
995 | '\quit': self.quit, |
996 | '\help': self.show_help, |
997 | '\h': self.show_help, |
998 | - 'help': self.show_help}) |
999 | + 'help': self.show_help, |
1000 | + 'source': self.meta_source, |
1001 | + '\.': self.meta_source,}) |
1002 | self.metacommandmanager.add(self.metacommands) |
1003 | |
1004 | self.servers = [] |
1005 | @@ -86,27 +105,31 @@ |
1006 | self.driver = self.ui |
1007 | |
1008 | if hasattr(self.ui, "metacommands"): |
1009 | - self.metacommandmanager.add(self.ui.metacommands) |
1010 | + result = self.metacommandmanager.add(self.ui.metacommands) |
1011 | + if isinstance(result, BootsWarning): |
1012 | + self._display_error(result) |
1013 | if hasattr(self.ui, "help"): |
1014 | self.help["ui"] = self.ui.help |
1015 | + |
1016 | + self.driver_stack = LinkStackNode() |
1017 | |
1018 | self.hooks["load"].call() |
1019 | |
1020 | def _init_help(self): |
1021 | - self.help = HelpTopic("boots", "Boots Help", "Help", _(""" |
1022 | - Boots is a flexible, extensible, and multilingual shell for working with databases. To read more about a subtopic, use `help <topic>`.""")) |
1023 | + self.help = HelpTopic("boots", _("Boots Help"), _("Help"), |
1024 | + description = _("Boots is a flexible, extensible, and multilingual shell for working with databases. To read more about a subtopic, use `help <topic>`.")) |
1025 | |
1026 | self.help["commands"] = QueryIndexTopic("commands", None, |
1027 | _("List of Boots commands."), |
1028 | query=("commands",)) |
1029 | |
1030 | - self.help["commands"].add("\quit", "quit", _("Quit boots.")) |
1031 | - self.help["commands"].add("\help", "help", _("Show help for a topic."), _(""" |
1032 | - help <topic> |
1033 | - Show help for \"topic\".""")) |
1034 | + self.help["commands"].add("\quit", _("quit"), _("Quit boots.")) |
1035 | + self.help["commands"].add("\help", _("help"), _("Show help for a topic."), |
1036 | + description = _("help <topic>\n" |
1037 | + "Show help for \"topic\".")) |
1038 | |
1039 | - self.help.add("lingos", "Lingos", _("Documentation for loaded lingos."), _(""" |
1040 | - A lingo is a command language usable within Boots.""")) |
1041 | + self.help.add("lingos", _("Lingos"), _("Documentation for loaded lingos."), |
1042 | + description = _("A lingo is a command language usable within Boots.")) |
1043 | |
1044 | def input_complete(self, command, lingo): |
1045 | if self.metacommands.get_metacommand(command) is not None: |
1046 | @@ -117,24 +140,37 @@ |
1047 | return command |
1048 | |
1049 | def run(self, command, lingo="sql"): |
1050 | - if lingo in self.lingos: |
1051 | - return self.lingos[lingo].execute(command) |
1052 | + if not lingo in self.lingos: |
1053 | + self._display_error(ConsoleError(_("Invalid lingo \"{0}\" specified.").format(lingo))) |
1054 | + return |
1055 | + |
1056 | + result_data = self.lingos[lingo].execute(command) |
1057 | + result = self.lingos[lingo].present(result_data) |
1058 | + if isinstance(result, NodeGraph): |
1059 | + result_graph = result |
1060 | else: |
1061 | - return ConsoleError(_("Invalid lingo")+" \"{0}\" "+_("specified.").format(lingo)) |
1062 | + result_graph = NodeGraph() |
1063 | + if isinstance(result, (str, Exception)) or not iterable(result): |
1064 | + result = iter([result]) |
1065 | + result_graph.add_output(IteratorNode(result)) |
1066 | + |
1067 | + result_graph.link_output(self.presenter.presenter_graph) |
1068 | + result_graph.start() |
1069 | + result_graph.run() |
1070 | + return result_data |
1071 | |
1072 | def connect(self, host, port, database): |
1073 | try: |
1074 | - server = api.Server(host, port, {"database": database}) |
1075 | + server = api.DrizzleServer(host, port, {"database": database}) |
1076 | server.connect() |
1077 | - except: |
1078 | + except ConnectionError: |
1079 | server = None |
1080 | |
1081 | if server and server.is_connected: |
1082 | self.servers.append(server) |
1083 | return True |
1084 | else: |
1085 | - error = ConsoleError(_("Could not connect to")+" {0}:{1}".format(host, port)) |
1086 | - self.presenter.presenter_graph.put(error) |
1087 | + self._display_error(ConsoleError(_("Could not connect to {0}:{1}").format(host, port))) |
1088 | return False |
1089 | |
1090 | def disconnect(self, host, port): |
1091 | @@ -153,38 +189,40 @@ |
1092 | if self.welcome_msg and self.driver.is_interactive: |
1093 | server_lines = [] |
1094 | for server in self.servers: |
1095 | - line_format = "{0.hostname}:{0.port} ("+_("server")+" v{0.server_version})" |
1096 | + line_format = _("{0.hostname}:{0.port} (server v{0.server_version})") |
1097 | server_lines.append(line_format.format(server)) |
1098 | server_status = "\n".join(server_lines) |
1099 | |
1100 | welcome = self.welcome_msg.format(server_status=server_status) |
1101 | self.presenter.presenter_graph.put(welcome) |
1102 | |
1103 | + def _display_error(self, error): |
1104 | + if not isinstance(error, (BootsError, BootsWarning)): |
1105 | + error = InternalError(error) |
1106 | + self.presenter.presenter_graph.put(error) |
1107 | + |
1108 | def main(self): |
1109 | self.presenter.presenter_graph.start() |
1110 | + self.driver_stack.push_input(self.driver.driver_graph.output_link) |
1111 | + self.driver.driver_graph.start() |
1112 | + |
1113 | self._load_servers() |
1114 | self._welcome_message() |
1115 | - |
1116 | - for command, lingo in self.driver.get_input(): |
1117 | - if not self.metacommands.execute(command): |
1118 | - result = self.run(command, lingo) |
1119 | - |
1120 | - if isinstance(result, node.NodeGraph): |
1121 | - result_graph = result |
1122 | - |
1123 | - else: |
1124 | - result_graph = node.NodeGraph() |
1125 | - if isinstance(result, (str, Exception)) or not iterable(result): |
1126 | - result = iter([result]) |
1127 | - result_graph.add_output(node.IteratorNode(result)) |
1128 | - |
1129 | - result_graph.link_output(self.presenter.presenter_graph) |
1130 | - result_graph.start() |
1131 | - result_graph.run() |
1132 | - |
1133 | + |
1134 | + for data in self.driver_stack: |
1135 | + if isinstance(data, Exception): |
1136 | + self._display_error(data) |
1137 | + else: |
1138 | + command, lingo = data |
1139 | + try: |
1140 | + if not self.metacommands.execute(command): |
1141 | + self.run(command, lingo) |
1142 | + except Exception as e: |
1143 | + self._display_error(e) |
1144 | + |
1145 | self.quit() |
1146 | |
1147 | - def quit(self, exitcode = 0): |
1148 | + def quit(self, exitcode=0): |
1149 | if self.driver.is_interactive: |
1150 | self.presenter.presenter_graph.put(_("Boots quit.")) |
1151 | |
1152 | @@ -204,9 +242,9 @@ |
1153 | self.presenter.present(topic.format(include_index=True)) |
1154 | else: |
1155 | paths = ", ".join("\"{0}\"".format(" ".join(path)) for path in results) |
1156 | - self.presenter.present(_("Did you mean:")+" {0}?".format(paths)) |
1157 | + self.presenter.present(_("Did you mean: {0}?").format(paths)) |
1158 | else: |
1159 | - self.presenter.present(_("No help found for")+" \"{0}\".".format(" ".join(query))) |
1160 | + self.presenter.present(_("No help found for \"{0}\".").format(" ".join(query))) |
1161 | |
1162 | def meta_connect(self, host=None, port=None, database=None): |
1163 | if host is None: |
1164 | @@ -229,3 +267,14 @@ |
1165 | port = int(port) |
1166 | |
1167 | self.disconnect(host, port) |
1168 | + |
1169 | + def meta_source(self, *file_list): |
1170 | + for filepath in file_list: |
1171 | + try: |
1172 | + script_driver = ScriptDriver(self, filepath, self.config["lingo"]) |
1173 | + except IOError as e: |
1174 | + self._display_error(ConsoleError(_("Could not open file {0}").format(filepath))) |
1175 | + return |
1176 | + |
1177 | + self.driver_stack.push_input(script_driver.driver_graph.output_link) |
1178 | + script_driver.driver_graph.start() |
1179 | |
1180 | === modified file 'boots/lib/lingos/bash_external.py' |
1181 | --- boots/lib/lingos/bash_external.py 2010-02-27 05:54:13 +0000 |
1182 | +++ boots/lib/lingos/bash_external.py 2010-03-07 09:23:23 +0000 |
1183 | @@ -25,12 +25,12 @@ |
1184 | from boots.lib.lingos import external |
1185 | from boots.lib.lingos import lingo |
1186 | from boots.lib.ui.components.help import HelpTopic |
1187 | - |
1188 | +from boots.lib import _, n_ |
1189 | |
1190 | class ExternalBashInterpreter(external.ExternalInterpreter): |
1191 | """Provides an interface to an external bash shell.""" |
1192 | |
1193 | - help = HelpTopic("bash", "Bash Lingo", "External bash shell.") |
1194 | + help = HelpTopic("bash", _("Bash Lingo"), _("External bash shell.")) |
1195 | |
1196 | def __init__(self, *args, **kwargs): |
1197 | super(ExternalBashInterpreter, self).__init__('bash', [], *args, **kwargs) |
1198 | |
1199 | === modified file 'boots/lib/lingos/external.py' |
1200 | --- boots/lib/lingos/external.py 2010-02-27 05:40:10 +0000 |
1201 | +++ boots/lib/lingos/external.py 2010-03-07 09:23:23 +0000 |
1202 | @@ -47,12 +47,12 @@ |
1203 | """Evaluate code using the external interpreter. All return values are strings.""" |
1204 | if self.interpreter == None: |
1205 | self.interpreter = subprocess.Popen(self.executable, *self.executable_args, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT) |
1206 | + self.output_lines = [] |
1207 | + self.lock = threading.RLock() |
1208 | # Since self.interpreter.stdout.readline could block, a thread is used to read the output. |
1209 | self.read_thread = threading.Thread(target = self._readlines) |
1210 | self.read_thread.daemon = True |
1211 | self.read_thread.start() |
1212 | - self.output_lines = [] |
1213 | - self.lock = threading.RLock() |
1214 | # The maximum amount of time to wait for output. |
1215 | # FIXME: This is a kludge and should be fixed if possible. |
1216 | |
1217 | |
1218 | === modified file 'boots/lib/lingos/lingo.py' |
1219 | --- boots/lib/lingos/lingo.py 2010-03-02 00:45:59 +0000 |
1220 | +++ boots/lib/lingos/lingo.py 2010-03-07 09:23:23 +0000 |
1221 | @@ -23,7 +23,7 @@ |
1222 | |
1223 | import sys |
1224 | |
1225 | -from boots.util import set_gettext |
1226 | +from boots.lib import _, n_ |
1227 | |
1228 | class LingoRegistry(object): |
1229 | def __init__(self): |
1230 | @@ -33,7 +33,7 @@ |
1231 | """Registers a lingo with the given name and interpreter. Raises a KeyError if a lingo with |
1232 | name is already registered.""" |
1233 | if name in self._lingos: |
1234 | - raise KeyError(_('A lingo with name')+' {0} '+_('already exists').format(name)) |
1235 | + raise KeyError(_('A lingo with name {0} already exists').format(name)) |
1236 | |
1237 | self._lingos[name] = interpreter |
1238 | |
1239 | @@ -92,10 +92,10 @@ |
1240 | code should be an object returned by read.""" |
1241 | pass |
1242 | |
1243 | - def string(self, obj): |
1244 | - """Convert obj to a string. obj is an object in the object system for this lingo.""" |
1245 | - return str(obj) |
1246 | - |
1247 | + def present(self, obj): |
1248 | + """Convert obj to a presentable object. obj is an object in the object system for this lingo.""" |
1249 | + return obj |
1250 | + |
1251 | def python(self, obj): |
1252 | """Convert obj to the appropriate python type. obj is an object in the object system for |
1253 | this lingo.""" |
1254 | |
1255 | === modified file 'boots/lib/lingos/lisp/builtins.py' |
1256 | --- boots/lib/lingos/lisp/builtins.py 2010-03-02 00:45:59 +0000 |
1257 | +++ boots/lib/lingos/lisp/builtins.py 2010-03-07 09:23:23 +0000 |
1258 | @@ -21,10 +21,10 @@ |
1259 | # |
1260 | # ##### END LICENSE BLOCK ##### |
1261 | |
1262 | +import sys |
1263 | + |
1264 | from boots.lib.lingos.lisp import objects |
1265 | -from boots.util import set_gettext |
1266 | -import sys |
1267 | - |
1268 | +from boots.lib import _, n_ |
1269 | |
1270 | nil = objects.Null() |
1271 | t = objects.T() |
1272 | |
1273 | === modified file 'boots/lib/lingos/lisp/lexer.py' |
1274 | --- boots/lib/lingos/lisp/lexer.py 2010-03-02 00:45:59 +0000 |
1275 | +++ boots/lib/lingos/lisp/lexer.py 2010-03-07 09:23:23 +0000 |
1276 | @@ -22,7 +22,8 @@ |
1277 | # ##### END LICENSE BLOCK ##### |
1278 | |
1279 | import ply.lex as lex |
1280 | -from boots.util import set_gettext |
1281 | + |
1282 | +from boots.lib import _, n_ |
1283 | |
1284 | tokens = ('INTEGER', 'STRING', 'SYMBOL', 'QUOTE', 'LEFT_PAREN', 'RIGHT_PAREN') |
1285 | |
1286 | |
1287 | === modified file 'boots/lib/lingos/lisp/lisp.py' |
1288 | --- boots/lib/lingos/lisp/lisp.py 2010-03-02 00:45:59 +0000 |
1289 | +++ boots/lib/lingos/lisp/lisp.py 2010-03-07 09:23:23 +0000 |
1290 | @@ -30,23 +30,20 @@ |
1291 | # let |
1292 | # do |
1293 | |
1294 | -# Optimize the code: |
1295 | -# The current implementation is simple but very inefficient (especially environements, this can be fixed by using a |
1296 | -# dictionary of lists instead of a list of dictionaries). |
1297 | - |
1298 | import sys |
1299 | |
1300 | if __name__ == '__main__': |
1301 | sys.path.append('../../../../') |
1302 | |
1303 | +import re |
1304 | +import readline |
1305 | + |
1306 | from boots.lib.lingos import lingo |
1307 | from boots.lib.lingos.lisp import builtins |
1308 | from boots.lib.lingos.lisp import lexer |
1309 | from boots.lib.lingos.lisp import objects |
1310 | from boots.lib.lingos.lisp import parser |
1311 | -from boots.util import set_gettext |
1312 | -import re |
1313 | -import readline |
1314 | +from boots.lib import _, n_ |
1315 | |
1316 | |
1317 | def input_complete(string): |
1318 | @@ -77,7 +74,7 @@ |
1319 | if isinstance(stream, file): |
1320 | stream = stream.readline |
1321 | elif not callable(stream): |
1322 | - raise TypeError('{0} '+_('is not a stream or a callable').format(stream)) |
1323 | + raise TypeError(_('{0} is not a stream or a callable').format(stream)) |
1324 | # Read lines until all open lisp expressions have been closed. |
1325 | while True: |
1326 | line = stream() |
1327 | @@ -118,7 +115,10 @@ |
1328 | builtins.initialize(global_environment) |
1329 | |
1330 | while True: |
1331 | - print(evaluate(read(lambda: raw_input('> ')), global_environment)) |
1332 | + try: |
1333 | + print(evaluate(read(lambda: raw_input('> ')), global_environment)) |
1334 | + except SyntaxError as e: |
1335 | + print(e) |
1336 | |
1337 | except EOFError: |
1338 | pass |
1339 | |
1340 | === modified file 'boots/lib/lingos/lisp/objects.py' |
1341 | --- boots/lib/lingos/lisp/objects.py 2010-03-02 00:45:59 +0000 |
1342 | +++ boots/lib/lingos/lisp/objects.py 2010-03-07 09:23:23 +0000 |
1343 | @@ -25,7 +25,7 @@ |
1344 | import copy |
1345 | import sys |
1346 | |
1347 | -from boots.util import set_gettext |
1348 | +from boots.lib import _, n_ |
1349 | |
1350 | _gensym_count = 1 |
1351 | _symbols = {} |
1352 | @@ -98,7 +98,7 @@ |
1353 | else_expression = car(cdr(cdr(cdr(self)))) |
1354 | |
1355 | if then_expression is Null() or cdr(cdr(cdr(cdr(self)))) is not Null(): |
1356 | - raise SyntaxError(_('wrong number of arguments for if special form')+' {0}'.format(self)) |
1357 | + raise SyntaxError(_('Wrong number of arguments for if special form {0}').format(self)) |
1358 | |
1359 | if condition_expression.evaluate(environment) != Null(): |
1360 | return then_expression.evaluate(environment) |
1361 | @@ -172,10 +172,10 @@ |
1362 | try: |
1363 | return self._environment[symbol][-1] |
1364 | except KeyError: |
1365 | - raise KeyError(_('No binding for')+' {0} '+_('exists in the current environment').format(symbol)) |
1366 | + raise KeyError(_('No binding for {0} exists in the current environment').format(symbol)) |
1367 | |
1368 | def __setitem__(self, symbol, value): |
1369 | - """Sets symbol to value in the environment. If no binding for symbol exists, a binding is created at the innermost |
1370 | + """Sets symbol to value in the environment. If no binding for symbol exists, a binding is created at the innermost |
1371 | scope.""" |
1372 | if not isinstance(symbol, Symbol): |
1373 | raise TypeError(_('Expected a symbol')) |
1374 | @@ -204,7 +204,7 @@ |
1375 | # return False |
1376 | |
1377 | def push(self, symbol_values): |
1378 | - """Adds a new innermost lexical scope as determined by symbol_values. symbol_values is a dictionary mapping Symbols |
1379 | + """Adds a new innermost lexical scope as determined by symbol_values. symbol_values is a dictionary mapping Symbols |
1380 | to Objects and describes bindings in the current scope.""" |
1381 | self._scopes.append(set(symbol_values.keys())) |
1382 | |
1383 | |
1384 | === modified file 'boots/lib/lingos/lisp/parser.py' |
1385 | --- boots/lib/lingos/lisp/parser.py 2010-03-02 00:45:59 +0000 |
1386 | +++ boots/lib/lingos/lisp/parser.py 2010-03-07 09:23:23 +0000 |
1387 | @@ -22,10 +22,11 @@ |
1388 | # ##### END LICENSE BLOCK ##### |
1389 | |
1390 | from lexer import tokens |
1391 | +import ply.yacc as yacc |
1392 | + |
1393 | from boots.lib.lingos.lisp import builtins |
1394 | from boots.lib.lingos.lisp import objects |
1395 | -from boots.util import set_gettext |
1396 | -import ply.yacc as yacc |
1397 | +from boots.lib import _, n_ |
1398 | |
1399 | |
1400 | def p_expression_integer(p): |
1401 | @@ -61,7 +62,7 @@ |
1402 | p[0] = objects.Cons(p[1], p[2]) |
1403 | |
1404 | def p_error(p): |
1405 | - print(_('syntax error')) |
1406 | + raise SyntaxError(_('syntax error')) |
1407 | |
1408 | # Disable debugging output and caching of parse tables. |
1409 | parser = yacc.yacc(debug = 0, write_tables = 0) |
1410 | |
1411 | === modified file 'boots/lib/lingos/piped_sql.py' |
1412 | --- boots/lib/lingos/piped_sql.py 2010-03-02 00:45:59 +0000 |
1413 | +++ boots/lib/lingos/piped_sql.py 2010-03-07 09:23:23 +0000 |
1414 | @@ -21,14 +21,14 @@ |
1415 | # |
1416 | # ##### END LICENSE BLOCK ##### |
1417 | |
1418 | +import re |
1419 | + |
1420 | from boots.api.nodes.node import NodeGraph, SyncNode, IteratorNode |
1421 | -from boots.api.constructors import construct, csv_output_file_node |
1422 | +from boots.api.constructors import construct, csv_output_file_node, sink_node |
1423 | from boots.lib.ui.components.help import HelpTopic |
1424 | from boots.lib.lingos import lingo |
1425 | from boots.lib.lingos import sql |
1426 | -from boots.util import set_gettext |
1427 | -import re |
1428 | - |
1429 | +from boots.lib import _, n_ |
1430 | |
1431 | # Set this to another node constructor to change the default constructor used for output files. |
1432 | output_file_node = csv_output_file_node |
1433 | @@ -40,9 +40,11 @@ |
1434 | # This regexp matches a function call syntax. Use the name group to access the name of the function |
1435 | # and the arguments group to access the list of arguments (as a string). |
1436 | constructor_call_re = re.compile('^(?P<name>[a-zA-Z_]+)\((?P<arguments>([^,\(\)]+,)*[^,\(\)]+)?\)$') |
1437 | +# Matches a string of the form 'abc' or "abc". |
1438 | +string_re = re.compile('^(?P<text>(\'[^\']*\')|("[^"]*"))') |
1439 | |
1440 | class PipedSQLInterpreter(sql.SQLInterpreter): |
1441 | - help = HelpTopic("pipedsql", "Piped SQL Lingo", _("Enhanced SQL query language supporting pipes.")) |
1442 | + help = HelpTopic("pipedsql", _("Piped SQL Lingo"), _("Enhanced SQL query language supporting pipes.")) |
1443 | |
1444 | def __init__(self, *args, **kwargs): |
1445 | super(PipedSQLInterpreter, self).__init__(*args, **kwargs) |
1446 | @@ -54,7 +56,7 @@ |
1447 | match = clause_re.match(clause_string) |
1448 | |
1449 | if not match: |
1450 | - raise SyntaxError('{0} '+_('is not in the correct function call syntax').format(clause_string)) |
1451 | + raise SyntaxError(_('{0} is not in the correct function call syntax').format(clause_string)) |
1452 | |
1453 | constructor_call = match.group('constructor_call').strip() |
1454 | output_file = match.group('output_file') |
1455 | @@ -65,13 +67,13 @@ |
1456 | match = constructor_call_re.match(constructor_string) |
1457 | |
1458 | if not match: |
1459 | - raise SyntaxError('{0} '+_('is not in the correct function call syntax').format(constructor_string)) |
1460 | + raise SyntaxError(_('{0} is not in the correct function call syntax').format(constructor_string)) |
1461 | |
1462 | name = match.group('name') |
1463 | arguments = match.group('arguments') |
1464 | |
1465 | if arguments: |
1466 | - arguments = [argument.strip('\'"') for argument in arguments.split(',')] |
1467 | + arguments = [string_re.match(argument.strip()).group('text')[1:-1] for argument in arguments.split(',')] |
1468 | else: |
1469 | arguments = [] |
1470 | |
1471 | @@ -137,6 +139,13 @@ |
1472 | last_node = node |
1473 | first_node = False |
1474 | |
1475 | + # Suppress output from the pipeline if the last clause redirected output. |
1476 | + if output_file: |
1477 | + sink = sink_node() |
1478 | + nodes.add(sink) |
1479 | + node.connect_output(sink) |
1480 | + node = sink |
1481 | + |
1482 | nodes.add_output(node) |
1483 | return nodes |
1484 | |
1485 | |
1486 | === modified file 'boots/lib/lingos/python.py' |
1487 | --- boots/lib/lingos/python.py 2010-02-27 05:54:13 +0000 |
1488 | +++ boots/lib/lingos/python.py 2010-03-07 09:23:23 +0000 |
1489 | @@ -27,12 +27,13 @@ |
1490 | from boots.api import errors |
1491 | from boots.lib.lingos import lingo |
1492 | from boots.lib.ui.components.help import HelpTopic |
1493 | +from boots.lib import _, n_ |
1494 | |
1495 | class PythonInterpreter(lingo.Interpreter): |
1496 | """Implements a python interpreter that will send commands to the python interpreter that runs |
1497 | boots.""" |
1498 | |
1499 | - help = HelpTopic("python", "Python Lingo", "Integrated Python interpreter using the Boots API.") |
1500 | + help = HelpTopic("python", _("Python Lingo"), _("Integrated Python interpreter using the Boots API.")) |
1501 | |
1502 | def __init__(self, *args, **kwargs): |
1503 | super(PythonInterpreter, self).__init__(*args, **kwargs) |
1504 | @@ -56,13 +57,18 @@ |
1505 | def evaluate(self, code): |
1506 | if code.strip(): |
1507 | try: |
1508 | - # Wrap each python object in a list so that the Console will not iterate over it if it is an iterable. |
1509 | return eval(code, self._globals, self._locals) |
1510 | # If eval results in an error, it might be because code is a statement rather than an expression. |
1511 | except: |
1512 | try: |
1513 | exec code in self._globals, self._locals |
1514 | except Exception: |
1515 | - return errors.ConsoleError(traceback.format_exc()) |
1516 | + return errors.BootsError(traceback.format_exc()) |
1517 | + |
1518 | + def present(self, obj): |
1519 | + if not isinstance(obj, Exception) and obj is not None: |
1520 | + return repr(obj) |
1521 | + else: |
1522 | + return obj |
1523 | |
1524 | lingo.register('python', PythonInterpreter) |
1525 | |
1526 | === modified file 'boots/lib/lingos/sql.py' |
1527 | --- boots/lib/lingos/sql.py 2010-02-27 05:54:13 +0000 |
1528 | +++ boots/lib/lingos/sql.py 2010-03-07 09:23:23 +0000 |
1529 | @@ -24,9 +24,10 @@ |
1530 | from boots.api.nodes.node import NodeGraph, SyncNode, IteratorNode |
1531 | from boots.lib.ui.components.help import HelpTopic |
1532 | from boots.lib.lingos import lingo |
1533 | +from boots.lib import _, n_ |
1534 | |
1535 | class SQLInterpreter(lingo.Interpreter): |
1536 | - help = HelpTopic("sql", "SQL Lingo", "Raw SQL commands sent to a server.") |
1537 | + help = HelpTopic("sql", _("SQL Lingo"), _("Raw SQL commands sent to a server.")) |
1538 | |
1539 | def __init__(self, *args, **kwargs): |
1540 | super(SQLInterpreter, self).__init__(*args, **kwargs) |
1541 | |
1542 | === modified file 'boots/lib/ui/components/help.py' |
1543 | --- boots/lib/ui/components/help.py 2010-03-02 00:45:59 +0000 |
1544 | +++ boots/lib/ui/components/help.py 2010-03-07 09:23:23 +0000 |
1545 | @@ -21,7 +21,7 @@ |
1546 | # |
1547 | # ##### END LICENSE BLOCK ##### |
1548 | |
1549 | -from boots.util import set_gettext |
1550 | +from boots.lib import _, n_ |
1551 | |
1552 | def issubseq(subseq, seq): |
1553 | """Determines if the elements the sequence subseq form a subsequence of the sequence seq.""" |
1554 | @@ -65,7 +65,7 @@ |
1555 | template += "{0.description}" |
1556 | |
1557 | if self.see_also: |
1558 | - template += "\n\n"+_("See also:")+" {0.see_also}" |
1559 | + template += "\n\n"+_("See also: {0.see_also}") |
1560 | |
1561 | output = template.format(self) |
1562 | if include_index and self._subtopics: |
1563 | @@ -103,7 +103,7 @@ |
1564 | assert isinstance(item, str) |
1565 | return query |
1566 | else: |
1567 | - raise TypeError(_('query object')+' {0} '+_('is not a string or tuple of strings').format(query)) |
1568 | + raise TypeError(_('query object {0} is not a string or tuple of strings').format(query)) |
1569 | |
1570 | def _ensure_key(self, key): |
1571 | """Creates a new subtopic that key maps to if none exists.""" |
1572 | |
1573 | === modified file 'boots/lib/ui/components/metacommands.py' |
1574 | --- boots/lib/ui/components/metacommands.py 2010-03-02 00:45:59 +0000 |
1575 | +++ boots/lib/ui/components/metacommands.py 2010-03-07 09:23:23 +0000 |
1576 | @@ -22,7 +22,32 @@ |
1577 | # ##### END LICENSE BLOCK ##### |
1578 | |
1579 | import warnings |
1580 | -from boots.util import set_gettext |
1581 | +import inspect |
1582 | +import types |
1583 | + |
1584 | +from boots.api.errors import BootsError, BootsWarning |
1585 | +from boots.lib import _, n_ |
1586 | + |
1587 | +class MetaCommandError(BootsError): |
1588 | + """Errors related to metacommands.""" |
1589 | + |
1590 | +class MetaCommandConflictWarning(BootsWarning): |
1591 | + """Raised when conflicting metacommand definitions are encountered.""" |
1592 | + pass |
1593 | + |
1594 | +class InvalidArgumentError(MetaCommandError): |
1595 | + """Raised when a metacommand has an invalid argument.""" |
1596 | + |
1597 | +def argument_count(func): |
1598 | + # Note: this can be fooled by things like *args |
1599 | + argspec = inspect.getargspec(func) |
1600 | + if argspec.varargs is not None: |
1601 | + return None |
1602 | + else: |
1603 | + argcount = len(argspec.args) |
1604 | + if type(func) is types.MethodType and func.im_self is not None: |
1605 | + argcount -= 1 |
1606 | + return argcount |
1607 | |
1608 | def parse_metacommand(commandstring): |
1609 | """This function wraps str.split() for metacommands |
1610 | @@ -31,8 +56,8 @@ |
1611 | to return a list of substrings, using whitespace as separator. The metacommand |
1612 | object uses this list to call a metacommand function and pass parameters. |
1613 | |
1614 | - """ |
1615 | - return commandstring.split() |
1616 | + """ |
1617 | + return commandstring.rstrip(";\n").split() |
1618 | |
1619 | class MetaCommandManager(object): |
1620 | def __init__(self): |
1621 | @@ -44,7 +69,7 @@ |
1622 | # FIXME: Issue warning through the UI using ui.print |
1623 | # FIXME: Determine the offending command(s) (set.intersect?) |
1624 | # FIXME: Print offending commands |
1625 | - warnings.warn(_("Conflicts between metacommands detected.")) |
1626 | + return MetaCommandConflictWarning(_("Conflicts between metacommands detected.")) |
1627 | self.names |= newnames |
1628 | |
1629 | class MetaCommands(object): |
1630 | @@ -66,7 +91,7 @@ |
1631 | def get_metacommand(self, command): |
1632 | commandlist = parse_metacommand(command) |
1633 | if len(commandlist) != 0 and commandlist[0] in self.commands: |
1634 | - return self.commands[commandlist[0]], commandlist[1:] |
1635 | + return commandlist[0], self.commands[commandlist[0]], commandlist[1:] |
1636 | |
1637 | def execute(self, command): |
1638 | """Parse and execute a metacommand string. |
1639 | @@ -78,8 +103,14 @@ |
1640 | """ |
1641 | metacommand = self.get_metacommand(command) |
1642 | if metacommand: |
1643 | - metacommand[0](*metacommand[1]) |
1644 | - # FIXME: Do we need to pass metacommand return values back? |
1645 | - return True |
1646 | + name, func, args = metacommand |
1647 | + expected_arg_count = argument_count(func) |
1648 | + if expected_arg_count is not None and len(args) != expected_arg_count: |
1649 | + raise InvalidArgumentError(_("{0} takes {1} arguments ({2} given)").format( |
1650 | + name, expected_arg_count, len(args))) |
1651 | + else: |
1652 | + func(*args) |
1653 | + # FIXME: Do we need to pass metacommand return values back? |
1654 | + return True |
1655 | else: |
1656 | return False |
1657 | |
1658 | === modified file 'boots/lib/ui/generic.py' |
1659 | --- boots/lib/ui/generic.py 2010-02-27 05:40:10 +0000 |
1660 | +++ boots/lib/ui/generic.py 2010-03-07 09:23:23 +0000 |
1661 | @@ -28,6 +28,8 @@ |
1662 | import os |
1663 | import StringIO |
1664 | |
1665 | +from boots.api.nodes.node import NodeGraph, SyncNode |
1666 | + |
1667 | class StreamDriver(object): |
1668 | """StreamDrivers receive stream inputs to boots, stdin for example.""" |
1669 | def __init__(self, console, stream, lingo): |
1670 | @@ -36,6 +38,8 @@ |
1671 | self.console = console |
1672 | self.stream = stream |
1673 | self.lingo = lingo |
1674 | + self.driver_graph = NodeGraph("streamdriver") |
1675 | + self.driver_graph.add(SyncNode(self.get_input()), output=True) |
1676 | |
1677 | def get_input(self): |
1678 | """Gets user input from a stream.""" |
1679 | @@ -64,6 +68,8 @@ |
1680 | def __init__(self, stream): |
1681 | """Initialize a StreamPresenter giving it the stream to write to.""" |
1682 | self.stream = stream |
1683 | + self.presenter_graph = NodeGraph("streampresenter") |
1684 | + self.presenter_graph.add(SyncNode(self.present), input=True, output=True) |
1685 | |
1686 | def present(self, row): |
1687 | """Prints row information in a "raw" format""" |
1688 | |
1689 | === modified file 'boots/lib/ui/plain.py' |
1690 | --- boots/lib/ui/plain.py 2010-03-02 00:45:59 +0000 |
1691 | +++ boots/lib/ui/plain.py 2010-03-07 09:23:23 +0000 |
1692 | @@ -28,13 +28,15 @@ |
1693 | import os |
1694 | import time |
1695 | import readline |
1696 | +import subprocess |
1697 | +import threading |
1698 | |
1699 | - |
1700 | -from boots.api.nodes.node import NodeGraph, SyncNode |
1701 | +from boots.api.api import Rows, ResultInfo |
1702 | +from boots.api.nodes.node import Status, NodeGraph, SyncNode, filter_none |
1703 | from boots.lib.ui.components.help import HelpTopic |
1704 | -from boots.lib.ui.components.metacommands import MetaCommands, parse_metacommand |
1705 | +from boots.lib.ui.components.metacommands import MetaCommandError, MetaCommands, parse_metacommand |
1706 | from boots.lib.ui.generic import StdinDriver, StdoutPresenter |
1707 | -from boots.util import set_gettext |
1708 | +from boots.lib import _, n_ |
1709 | |
1710 | class PlainUI(StdinDriver, StdoutPresenter): |
1711 | """Class that provides a 'plain' UI for boots. Input is taken on stdin, |
1712 | @@ -42,11 +44,11 @@ |
1713 | results are printed to stdout. These results are printed as tables when |
1714 | they are Server packets and as string interpretations otherwise.""" |
1715 | |
1716 | - help = HelpTopic("plain", "Plain UI", _("Plain UI documentation."), _(""" |
1717 | - A simple, minimal user interface for Boots.""")) |
1718 | + help = HelpTopic("plain", _("Plain UI"), _("Plain UI documentation."), |
1719 | + _("A simple, minimal user interface for Boots.")) |
1720 | |
1721 | help.add("commands", None, _("List of Plain UI commands.")) |
1722 | - help["commands"].add("\use", "use", _("Switch to a different lingo.")) |
1723 | + help["commands"].add("\use", _("use"), _("Switch to a different lingo.")) |
1724 | |
1725 | def __init__(self, console): |
1726 | """Initialize a UI giving it its parent console, primary prompt, |
1727 | @@ -61,16 +63,15 @@ |
1728 | self.prompt2 = console.config["prompt2"] |
1729 | self.hist_file = console.config["history_file"] |
1730 | self.lingo = console.config["lingo"] |
1731 | + self.pager_command = console.config["pager_command"] |
1732 | self.last_desc = None |
1733 | self.buffer = [] |
1734 | |
1735 | self.driver_graph = NodeGraph("plaindriver") |
1736 | - self.driver_graph.add_output(SyncNode(self.get_input)) |
1737 | + self.driver_graph.add(SyncNode(self.get_input()), output=True) |
1738 | |
1739 | self.presenter_graph = NodeGraph("plainpresenter") |
1740 | - present_node = SyncNode(self.present) |
1741 | - self.presenter_graph.add_input(present_node) |
1742 | - self.presenter_graph.add_output(present_node) |
1743 | + self.presenter_graph.add(SyncNode(self.present, filter=filter_none), input=True, output=True) |
1744 | |
1745 | readline.set_history_length(console.config["history_length"]) |
1746 | self.read_history() |
1747 | @@ -112,6 +113,8 @@ |
1748 | |
1749 | except EOFError: |
1750 | return |
1751 | + except MetaCommandError as e: |
1752 | + yield e |
1753 | |
1754 | def set_prompt(self, prompt1 = None, prompt2 = None): |
1755 | """Change the prompts during execution of a PlainUI.""" |
1756 | @@ -125,6 +128,25 @@ |
1757 | self.prompt1 = prompt1 |
1758 | self.prompt2 = prompt2 |
1759 | |
1760 | + def print_with_pager(self, text): |
1761 | + try: |
1762 | + pager = subprocess.Popen(self.pager_command.split(), |
1763 | + shell=False, |
1764 | + stdin=subprocess.PIPE) |
1765 | + except: |
1766 | + sys.stdout.write(_("Unable to run pager command \"{0}\". Pager disabled.\n") |
1767 | + .format(self.pager_command)) |
1768 | + self.pager_command = None |
1769 | + return False |
1770 | + |
1771 | + try: |
1772 | + pager.communicate(text) |
1773 | + except IOError: |
1774 | + # IOError is raised sometimes with large outputs |
1775 | + pass |
1776 | + |
1777 | + return True |
1778 | + |
1779 | def present(self, result): |
1780 | """Print the result provided as an argument. |
1781 | |
1782 | @@ -135,42 +157,77 @@ |
1783 | def padded(fields, widths): |
1784 | """Utility function used to convert rows from tuples to table rows. |
1785 | Results are returned as strings.""" |
1786 | - return "| {0} |\n".format(" | ".join(map(str.ljust, fields, widths))) |
1787 | + return "| {0} |".format(" | ".join(map(str.ljust, fields, widths))) |
1788 | |
1789 | def show_NULL(value): |
1790 | """There is a 'bug' in the dbapi that does not convert NULL objects |
1791 | to the string 'NULL'. This utility function performs that |
1792 | conversion.""" |
1793 | return value if value is not None else "NULL" |
1794 | - |
1795 | - if type(result) is dict and "__server_execute_sql_query" in result: |
1796 | - self.last_desc = result["info"] |
1797 | - if result["result"]: |
1798 | - self.buffer.extend(result["result"]) |
1799 | - else: |
1800 | - if self.buffer: |
1801 | - max_widths = map(max, [(len(column[0]), column[2]) for column in self.last_desc]) |
1802 | - dashes = map(lambda x: "-"*(x+2), max_widths) |
1803 | - sep_line = "+" + "+".join(dashes) + "+\n" |
1804 | - current_time = time.time() |
1805 | - info_line = "{0} "+_("rows in set")+" ({1:.2f} "+_("seconds")+").\n".format(len(self.buffer), |
1806 | - current_time - result["begin_time"]) |
1807 | - names = (column[0] for column in self.last_desc) |
1808 | - sys.stdout.write(sep_line) |
1809 | - sys.stdout.write(padded(names, max_widths)) |
1810 | - sys.stdout.write(sep_line) |
1811 | - for row in self.buffer: |
1812 | - sys.stdout.write(padded(map(show_NULL, row), max_widths)) |
1813 | - sys.stdout.write(sep_line) |
1814 | - sys.stdout.write(info_line) |
1815 | - # Reset values for next result set. |
1816 | - self.buffer = [] |
1817 | - else: |
1818 | - if isinstance(result, Exception): |
1819 | - sys.stdout.write(_("ERROR ")) |
1820 | - sys.stdout.write(" :: ".join(map(str, result.args))) |
1821 | - else: |
1822 | - sys.stdout.write(str(result)) |
1823 | + |
1824 | + def _gen_table(info): |
1825 | + if self.buffer: |
1826 | + max_widths = map(max, [(len(column[0]), column[2]) for column in info["description"]]) |
1827 | + dashes = map(lambda x: "-"*(x+2), max_widths) |
1828 | + sep_line = "+" + "+".join(dashes) + "+" |
1829 | + names = (column[0] for column in info["description"]) |
1830 | + yield sep_line |
1831 | + yield padded(names, max_widths) |
1832 | + yield sep_line |
1833 | + for row in self.buffer: |
1834 | + yield padded(map(show_NULL, row), max_widths) |
1835 | + |
1836 | + yield sep_line |
1837 | + |
1838 | + if self.console.driver.is_interactive: |
1839 | + elapsed = info["end_time"] - info["begin_time"] |
1840 | + noun = "row" if info["row_count"] == 1 else "rows" |
1841 | + if info["description"] is not None: |
1842 | + info_line = n_("{count} row in set ({elapsed:.2f} seconds)", |
1843 | + "{count} rows in set ({elapsed:.2f} seconds)", |
1844 | + info["row_count"]) |
1845 | + else: |
1846 | + info_line = n_("{count} row affected ({elapsed:.2f} seconds)", |
1847 | + "{count} rows affected ({elapsed:.2f} seconds)", |
1848 | + info["row_count"]) |
1849 | + |
1850 | + yield info_line.format(count=info["row_count"], |
1851 | + noun=noun, |
1852 | + elapsed=elapsed) |
1853 | + |
1854 | + if type(result) is Rows: |
1855 | + self.buffer.extend(result) |
1856 | + elif isinstance(result, ResultInfo): |
1857 | + printed = False |
1858 | + output = _gen_table(result.value) |
1859 | + if self.pager_command and self.console.driver.is_interactive: |
1860 | + printed = self.print_with_pager("\n".join(output)) |
1861 | + |
1862 | + # Fallback if no pager set, or paging fails. |
1863 | + if not printed: |
1864 | + for line in output: |
1865 | + sys.stdout.write(line) |
1866 | + sys.stdout.write("\n") |
1867 | + |
1868 | + # Reset values for next result set. |
1869 | + self.buffer = [] |
1870 | + elif isinstance(result, Status): |
1871 | + sys.stdout.write(_("Status")) |
1872 | + sys.stdout.write(" :: ") |
1873 | + sys.stdout.write(str(result)) |
1874 | + sys.stdout.write("\n") |
1875 | + elif isinstance(result, Warning): |
1876 | + sys.stderr.write(_("WARNING")) |
1877 | + sys.stderr.write(" :: ") |
1878 | + sys.stderr.write(str(result)) |
1879 | + sys.stderr.write("\n") |
1880 | + elif isinstance(result, Exception): |
1881 | + sys.stderr.write(_("ERROR")) |
1882 | + sys.stderr.write(" :: ") |
1883 | + sys.stderr.write(str(result)) |
1884 | + sys.stderr.write("\n") |
1885 | + elif result is not None: |
1886 | + sys.stdout.write(str(result)) |
1887 | sys.stdout.write("\n") |
1888 | |
1889 | @property |
1890 | @@ -186,4 +243,4 @@ |
1891 | if lingo in self.console.lingos: |
1892 | self.lingo = lingo |
1893 | else: |
1894 | - self.present(_("The specified lingo")+" \"{0}\" "+_("does not exist.").format(lingo)) |
1895 | + self.present(_("The specified lingo \"{0}\" does not exist.").format(lingo)) |
1896 | |
1897 | === removed file 'boots/util.py' |
1898 | --- boots/util.py 2010-03-02 01:04:23 +0000 |
1899 | +++ boots/util.py 1970-01-01 00:00:00 +0000 |
1900 | @@ -1,27 +0,0 @@ |
1901 | -# Boots Client Project |
1902 | -# www.launchpad.net/boots |
1903 | -# |
1904 | -# ##### BEGIN LICENSE BLOCK ##### |
1905 | -# |
1906 | -# Copyright (C) 2009-2010 Clark Boylan, Ken Brotherton, Max Goodman, |
1907 | -# Victoria Lewis, David Rosenbaum, and Andreas Turriff |
1908 | -# |
1909 | -# This program is free software: you can redistribute it and/or modify |
1910 | -# it under the terms of the GNU General Public License as published by |
1911 | -# the Free Software Foundation, either version 3 of the License, or |
1912 | -# (at your option) any later version. |
1913 | -# |
1914 | -# This program is distributed in the hope that it will be useful, |
1915 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1916 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1917 | -# GNU General Public License for more details. |
1918 | -# |
1919 | -# You should have received a copy of the GNU General Public License |
1920 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1921 | -# |
1922 | -# ##### END LICENSE BLOCK ##### |
1923 | - |
1924 | -import locale, gettext |
1925 | -def set_gettext(mo_location='/usr/share/locale'): |
1926 | - locale.setlocale(locale.LC_ALL) |
1927 | - gettext.install('boots', mo_location) |
1928 | |
1929 | === removed file 'localizations/potfiles.txt' |
1930 | --- localizations/potfiles.txt 2010-03-02 01:04:23 +0000 |
1931 | +++ localizations/potfiles.txt 1970-01-01 00:00:00 +0000 |
1932 | @@ -1,12 +0,0 @@ |
1933 | -../boots/lib/console.py |
1934 | -../boots/lib/ui/plain.py |
1935 | -../boots/lib/ui/components/metacommands.py |
1936 | -../boots/lib/ui/components/help.py |
1937 | -../boots/lib/lingos/lingo.py |
1938 | -../boots/lib/lingos/piped_sql.py |
1939 | -../boots/lib/lingos/lisp/builtins.py |
1940 | -../boots/lib/lingos/lisp/lexer.py |
1941 | -../boots/lib/lingos/lisp/lisp.py |
1942 | -../boots/lib/lingos/lisp/objects.py |
1943 | -../boots/lib/lingos/lisp/parser.py |
1944 | -../boots/app/client_config.py |
1945 | |
1946 | === renamed directory 'localizations' => 'po' |
1947 | === modified file 'po/README.txt' |
1948 | --- localizations/README.txt 2010-03-02 01:04:23 +0000 |
1949 | +++ po/README.txt 2010-03-07 09:23:23 +0000 |
1950 | @@ -1,22 +1,16 @@ |
1951 | -========When adding code that outputs a string======== |
1952 | -1) Add (if not present already) to the import section of the .py file |
1953 | -#========= |
1954 | -import gettext |
1955 | -gettext.bindtextdomain("fslint", "/usr/share/locale") #sys default used if localedir=None |
1956 | -gettext.textdomain("fslint") |
1957 | -_ = gettext.gettext |
1958 | -#========= |
1959 | -And then wrap all output strings with _("..."). |
1960 | -You should do this so that if you are using dynamicly created content, that created content is not part of the wrapped string. |
1961 | -Ex: |
1962 | +====== When adding code that outputs a string ====== |
1963 | +1) Add (if not present already) to the import section of the .py file: |
1964 | + |
1965 | +Depending on the package the file is in: |
1966 | +from boots.app import _, n_ |
1967 | +from boots.api import _, n_ |
1968 | +from boots.lib import _, n_ |
1969 | + |
1970 | +Then, wrap all output strings with _("..."). |
1971 | + |
1972 | +Example: |
1973 | help = "Execute commands from file and exit") |
1974 | should be wrapped as such: |
1975 | help = _("Execute commands from file and exit")) |
1976 | -Ex: |
1977 | -print("rcfile {0} contains a syntax error".format(filepath)) |
1978 | -should be wrapped as such: |
1979 | -print(_("rcfile ")+"{0} "+_("contains a syntax error").format(filepath)) |
1980 | |
1981 | -2) Add your file path to the /boots/localizations/potfiles.txt file. |
1982 | -3) Generate the updated .pot file by running the /boots/localization/createpots.sh script. This will overwrite the existing pot.cms file. Any prevously generated .po .mo files can then be updated from this new file. |
1983 | -4) Upload new pot file to the launchpad localization section for creation or updating of .po files. (more on this later --ken) |
1984 | +2) Generate the updated .pot file by running the /boots/po/createpots.sh script. This will overwrite the existing pot.cms file. Any prevously generated .po .mo files can then be updated from this new file. |
1985 | \ No newline at end of file |
1986 | |
1987 | === modified file 'po/boots.pot' |
1988 | --- localizations/boots.pot 2010-03-02 01:04:23 +0000 |
1989 | +++ po/boots.pot 2010-03-07 09:23:23 +0000 |
1990 | @@ -6,160 +6,115 @@ |
1991 | #, fuzzy |
1992 | msgid "" |
1993 | msgstr "" |
1994 | -"Project-Id-Version: PACKAGE VERSION\n" |
1995 | -"Report-Msgid-Bugs-To: \n" |
1996 | -"POT-Creation-Date: 2010-03-01 16:40-0800\n" |
1997 | +"Project-Id-Version: boots\n" |
1998 | +"Report-Msgid-Bugs-To: http://translations.launchpad.net/boots\n" |
1999 | +"POT-Creation-Date: 2010-03-07 01:23-0800\n" |
2000 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
2001 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
2002 | "Language-Team: LANGUAGE <LL@li.org>\n" |
2003 | "MIME-Version: 1.0\n" |
2004 | "Content-Type: text/plain; charset=CHARSET\n" |
2005 | "Content-Transfer-Encoding: 8bit\n" |
2006 | - |
2007 | -#: ../boots/lib/console.py:96 |
2008 | -msgid "" |
2009 | -"\n" |
2010 | -" Boots is a flexible, extensible, and multilingual shell for " |
2011 | -"working with databases. To read more about a subtopic, use `help <topic>`." |
2012 | -msgstr "" |
2013 | - |
2014 | -#: ../boots/lib/console.py:100 |
2015 | -msgid "List of Boots commands." |
2016 | -msgstr "" |
2017 | - |
2018 | -#: ../boots/lib/console.py:103 |
2019 | -msgid "Quit boots." |
2020 | -msgstr "" |
2021 | - |
2022 | -#: ../boots/lib/console.py:104 |
2023 | -msgid "Show help for a topic." |
2024 | -msgstr "" |
2025 | - |
2026 | -#: ../boots/lib/console.py:104 |
2027 | -msgid "" |
2028 | -"\n" |
2029 | -" help <topic>\n" |
2030 | -" Show help for \"topic\"." |
2031 | -msgstr "" |
2032 | - |
2033 | -#: ../boots/lib/console.py:108 |
2034 | -msgid "Documentation for loaded lingos." |
2035 | -msgstr "" |
2036 | - |
2037 | -#: ../boots/lib/console.py:108 |
2038 | -msgid "" |
2039 | -"\n" |
2040 | -" A lingo is a command language usable within Boots." |
2041 | -msgstr "" |
2042 | - |
2043 | -#: ../boots/lib/console.py:123 |
2044 | -msgid "Invalid lingo" |
2045 | -msgstr "" |
2046 | - |
2047 | -#: ../boots/lib/console.py:123 |
2048 | -msgid "specified." |
2049 | -msgstr "" |
2050 | - |
2051 | -#: ../boots/lib/console.py:136 |
2052 | -msgid "Could not connect to" |
2053 | -msgstr "" |
2054 | - |
2055 | -#: ../boots/lib/console.py:156 |
2056 | -msgid "server" |
2057 | -msgstr "" |
2058 | - |
2059 | -#: ../boots/lib/console.py:189 |
2060 | -msgid "Boots quit." |
2061 | -msgstr "" |
2062 | - |
2063 | -#: ../boots/lib/console.py:207 |
2064 | -msgid "Did you mean:" |
2065 | -msgstr "" |
2066 | - |
2067 | -#: ../boots/lib/console.py:209 |
2068 | -msgid "No help found for" |
2069 | -msgstr "" |
2070 | - |
2071 | -#: ../boots/lib/ui/plain.py:45 |
2072 | -msgid "Plain UI documentation." |
2073 | -msgstr "" |
2074 | - |
2075 | -#: ../boots/lib/ui/plain.py:45 |
2076 | -msgid "" |
2077 | -"\n" |
2078 | -" A simple, minimal user interface for Boots." |
2079 | -msgstr "" |
2080 | - |
2081 | -#: ../boots/lib/ui/plain.py:48 |
2082 | -msgid "List of Plain UI commands." |
2083 | -msgstr "" |
2084 | - |
2085 | -#: ../boots/lib/ui/plain.py:49 |
2086 | -msgid "Switch to a different lingo." |
2087 | -msgstr "" |
2088 | - |
2089 | -#: ../boots/lib/ui/plain.py:156 |
2090 | -msgid "rows in set" |
2091 | -msgstr "" |
2092 | - |
2093 | -#: ../boots/lib/ui/plain.py:156 |
2094 | -msgid "seconds" |
2095 | -msgstr "" |
2096 | - |
2097 | -#: ../boots/lib/ui/plain.py:170 |
2098 | -msgid "ERROR " |
2099 | -msgstr "" |
2100 | - |
2101 | -#: ../boots/lib/ui/plain.py:189 |
2102 | -msgid "The specified lingo" |
2103 | -msgstr "" |
2104 | - |
2105 | -#: ../boots/lib/ui/plain.py:189 |
2106 | -msgid "does not exist." |
2107 | -msgstr "" |
2108 | - |
2109 | -#: ../boots/lib/ui/components/metacommands.py:47 |
2110 | -msgid "Conflicts between metacommands detected." |
2111 | -msgstr "" |
2112 | - |
2113 | -#: ../boots/lib/ui/components/help.py:68 |
2114 | -msgid "See also:" |
2115 | -msgstr "" |
2116 | - |
2117 | -#: ../boots/lib/ui/components/help.py:72 |
2118 | -msgid "Subtopics" |
2119 | -msgstr "" |
2120 | - |
2121 | -#: ../boots/lib/ui/components/help.py:106 |
2122 | -msgid "query object" |
2123 | -msgstr "" |
2124 | - |
2125 | -#: ../boots/lib/ui/components/help.py:106 |
2126 | -msgid "is not a string or tuple of strings" |
2127 | -msgstr "" |
2128 | - |
2129 | -#: ../boots/lib/lingos/lingo.py:36 |
2130 | -msgid "A lingo with name" |
2131 | -msgstr "" |
2132 | - |
2133 | -#: ../boots/lib/lingos/lingo.py:36 |
2134 | -msgid "already exists" |
2135 | -msgstr "" |
2136 | - |
2137 | -#: ../boots/lib/lingos/piped_sql.py:45 |
2138 | -msgid "Enhanced SQL query language supporting pipes." |
2139 | -msgstr "" |
2140 | - |
2141 | -#: ../boots/lib/lingos/piped_sql.py:57 ../boots/lib/lingos/piped_sql.py:68 |
2142 | -msgid "is not in the correct function call syntax" |
2143 | -msgstr "" |
2144 | - |
2145 | -#: ../boots/lib/lingos/piped_sql.py:95 |
2146 | -msgid "error1" |
2147 | -msgstr "" |
2148 | - |
2149 | -#: ../boots/lib/lingos/piped_sql.py:107 |
2150 | -msgid "SQL is only permitted for the first clause" |
2151 | +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" |
2152 | + |
2153 | +#: ../boots/api/api.py:89 |
2154 | +msgid "Could not connect to {0}" |
2155 | +msgstr "" |
2156 | + |
2157 | +#: ../boots/api/api.py:101 |
2158 | +msgid "Connection closed" |
2159 | +msgstr "" |
2160 | + |
2161 | +#: ../boots/api/api.py:124 |
2162 | +msgid "Lost connection to {0}. Reconnecting..." |
2163 | +msgstr "" |
2164 | + |
2165 | +#: ../boots/api/api.py:136 |
2166 | +msgid "Reconnected." |
2167 | +msgstr "" |
2168 | + |
2169 | +#: ../boots/api/constructors.py:36 |
2170 | +msgid "A constructor with name {0} already exists" |
2171 | +msgstr "" |
2172 | + |
2173 | +#: ../boots/api/constructors.py:47 |
2174 | +msgid "There is no constructor with the name {0}." |
2175 | +msgstr "" |
2176 | + |
2177 | +#: ../boots/api/errors.py:53 |
2178 | +msgid "Lost connection to {0}" |
2179 | +msgstr "" |
2180 | + |
2181 | +#: ../boots/api/errors.py:58 |
2182 | +msgid "(Exception upon reconnect: {0})" |
2183 | +msgstr "" |
2184 | + |
2185 | +#: ../boots/app/client_config.py:93 |
2186 | +msgid "Execute command and exit" |
2187 | +msgstr "" |
2188 | + |
2189 | +#: ../boots/app/client_config.py:98 |
2190 | +msgid "Use database" |
2191 | +msgstr "" |
2192 | + |
2193 | +#: ../boots/app/client_config.py:103 |
2194 | +msgid "Execute commands from file and exit" |
2195 | +msgstr "" |
2196 | + |
2197 | +#: ../boots/app/client_config.py:108 |
2198 | +msgid "Set the lingo to use" |
2199 | +msgstr "" |
2200 | + |
2201 | +#: ../boots/app/client_config.py:112 |
2202 | +msgid "Do not read user configuration file" |
2203 | +msgstr "" |
2204 | + |
2205 | +#: ../boots/app/client_config.py:117 |
2206 | +msgid "Filename of user configuration file" |
2207 | +msgstr "" |
2208 | + |
2209 | +#: ../boots/app/client_config.py:122 |
2210 | +msgid "Connect to host" |
2211 | +msgstr "" |
2212 | + |
2213 | +#: ../boots/app/client_config.py:127 |
2214 | +msgid "Use port number" |
2215 | +msgstr "" |
2216 | + |
2217 | +#: ../boots/app/client_config.py:132 |
2218 | +msgid "Login with username" |
2219 | +msgstr "" |
2220 | + |
2221 | +#: ../boots/app/client_config.py:137 |
2222 | +msgid "Connect using password. If none is given, query for password" |
2223 | +msgstr "" |
2224 | + |
2225 | +#: ../boots/app/client_config.py:142 |
2226 | +msgid "Pipe query results to the specified pager." |
2227 | +msgstr "" |
2228 | + |
2229 | +#: ../boots/app/client_config.py:147 |
2230 | +msgid "Specify the SQL statement terminating character. Default is ';'." |
2231 | +msgstr "" |
2232 | + |
2233 | +#: ../boots/app/client_config.py:152 |
2234 | +msgid "Specify file to save history to" |
2235 | +msgstr "" |
2236 | + |
2237 | +#: ../boots/app/client_config.py:157 |
2238 | +msgid "Specify max history file length" |
2239 | +msgstr "" |
2240 | + |
2241 | +#: ../boots/app/client_config.py:205 |
2242 | +msgid "rcfile {0} not found" |
2243 | +msgstr "" |
2244 | + |
2245 | +#: ../boots/app/client_config.py:207 |
2246 | +msgid "rcfile {0} contains a syntax error" |
2247 | +msgstr "" |
2248 | + |
2249 | +#: ../boots/app/client_config.py:209 |
2250 | +msgid "rcfile {0} contains an error: {1}" |
2251 | msgstr "" |
2252 | |
2253 | #: ../boots/lib/lingos/lisp/builtins.py:62 |
2254 | @@ -170,20 +125,20 @@ |
2255 | msgid "Expected a cons or string" |
2256 | msgstr "" |
2257 | |
2258 | -#: ../boots/lib/lingos/lisp/lexer.py:38 |
2259 | +#: ../boots/lib/lingos/lisp/lexer.py:39 |
2260 | msgid "illegal character {0} found" |
2261 | msgstr "" |
2262 | |
2263 | -#: ../boots/lib/lingos/lisp/lisp.py:61 |
2264 | +#: ../boots/lib/lingos/lisp/lisp.py:58 |
2265 | msgid "unmatched closing \")\"" |
2266 | msgstr "" |
2267 | |
2268 | -#: ../boots/lib/lingos/lisp/lisp.py:80 |
2269 | -msgid "is not a stream or a callable" |
2270 | +#: ../boots/lib/lingos/lisp/lisp.py:77 |
2271 | +msgid "{0} is not a stream or a callable" |
2272 | msgstr "" |
2273 | |
2274 | #: ../boots/lib/lingos/lisp/objects.py:101 |
2275 | -msgid "wrong number of arguments for if special form" |
2276 | +msgid "Wrong number of arguments for if special form {0}" |
2277 | msgstr "" |
2278 | |
2279 | #: ../boots/lib/lingos/lisp/objects.py:120 |
2280 | @@ -201,81 +156,221 @@ |
2281 | msgstr "" |
2282 | |
2283 | #: ../boots/lib/lingos/lisp/objects.py:175 |
2284 | -msgid "No binding for" |
2285 | -msgstr "" |
2286 | - |
2287 | -#: ../boots/lib/lingos/lisp/objects.py:175 |
2288 | -msgid "exists in the current environment" |
2289 | +msgid "No binding for {0} exists in the current environment" |
2290 | msgstr "" |
2291 | |
2292 | #: ../boots/lib/lingos/lisp/objects.py:220 |
2293 | msgid "Cannot pop the innermost scope because then no scopes would remain" |
2294 | msgstr "" |
2295 | |
2296 | -#: ../boots/lib/lingos/lisp/parser.py:64 |
2297 | +#: ../boots/lib/lingos/lisp/parser.py:65 |
2298 | msgid "syntax error" |
2299 | msgstr "" |
2300 | |
2301 | -#: ../boots/app/client_config.py:68 |
2302 | -msgid "Execute command and exit" |
2303 | -msgstr "" |
2304 | - |
2305 | -#: ../boots/app/client_config.py:73 |
2306 | -msgid "Use database" |
2307 | -msgstr "" |
2308 | - |
2309 | -#: ../boots/app/client_config.py:78 |
2310 | -msgid "Execute commands from file and exit" |
2311 | -msgstr "" |
2312 | - |
2313 | -#: ../boots/app/client_config.py:83 |
2314 | -msgid "Set the lingo to use" |
2315 | -msgstr "" |
2316 | - |
2317 | -#: ../boots/app/client_config.py:87 |
2318 | -msgid "Do not read user configuration file" |
2319 | -msgstr "" |
2320 | - |
2321 | -#: ../boots/app/client_config.py:92 |
2322 | -msgid "Filename of user configuration file" |
2323 | -msgstr "" |
2324 | - |
2325 | -#: ../boots/app/client_config.py:97 |
2326 | -msgid "Connect to host" |
2327 | -msgstr "" |
2328 | - |
2329 | -#: ../boots/app/client_config.py:102 |
2330 | -msgid "Use port number" |
2331 | -msgstr "" |
2332 | - |
2333 | -#: ../boots/app/client_config.py:107 |
2334 | -msgid "Login with username" |
2335 | -msgstr "" |
2336 | - |
2337 | -#: ../boots/app/client_config.py:112 |
2338 | -msgid "Connect using password. If none is given, query for password" |
2339 | -msgstr "" |
2340 | - |
2341 | -#: ../boots/app/client_config.py:117 |
2342 | -msgid "Specify the SQL statement terminating character. Default is ';'." |
2343 | -msgstr "" |
2344 | - |
2345 | -#: ../boots/app/client_config.py:122 |
2346 | -msgid "Specify file to save history to" |
2347 | -msgstr "" |
2348 | - |
2349 | -#: ../boots/app/client_config.py:127 |
2350 | -msgid "Specify max history file length" |
2351 | -msgstr "" |
2352 | - |
2353 | -#: ../boots/app/client_config.py:167 |
2354 | -msgid "not found" |
2355 | -msgstr "" |
2356 | - |
2357 | -#: ../boots/app/client_config.py:169 |
2358 | -msgid "contains a syntax error" |
2359 | -msgstr "" |
2360 | - |
2361 | -#: ../boots/app/client_config.py:171 |
2362 | -msgid "contains an error:" |
2363 | +#: ../boots/lib/lingos/bash_external.py:33 |
2364 | +msgid "Bash Lingo" |
2365 | +msgstr "" |
2366 | + |
2367 | +#: ../boots/lib/lingos/bash_external.py:33 |
2368 | +msgid "External bash shell." |
2369 | +msgstr "" |
2370 | + |
2371 | +#: ../boots/lib/lingos/lingo.py:36 |
2372 | +msgid "A lingo with name {0} already exists" |
2373 | +msgstr "" |
2374 | + |
2375 | +#: ../boots/lib/lingos/sql.py:30 |
2376 | +msgid "SQL Lingo" |
2377 | +msgstr "" |
2378 | + |
2379 | +#: ../boots/lib/lingos/sql.py:30 |
2380 | +msgid "Raw SQL commands sent to a server." |
2381 | +msgstr "" |
2382 | + |
2383 | +#: ../boots/lib/lingos/python.py:36 |
2384 | +msgid "Python Lingo" |
2385 | +msgstr "" |
2386 | + |
2387 | +#: ../boots/lib/lingos/python.py:36 |
2388 | +msgid "Integrated Python interpreter using the Boots API." |
2389 | +msgstr "" |
2390 | + |
2391 | +#: ../boots/lib/lingos/piped_sql.py:47 |
2392 | +msgid "Piped SQL Lingo" |
2393 | +msgstr "" |
2394 | + |
2395 | +#: ../boots/lib/lingos/piped_sql.py:47 |
2396 | +msgid "Enhanced SQL query language supporting pipes." |
2397 | +msgstr "" |
2398 | + |
2399 | +#: ../boots/lib/lingos/piped_sql.py:59 ../boots/lib/lingos/piped_sql.py:70 |
2400 | +msgid "{0} is not in the correct function call syntax" |
2401 | +msgstr "" |
2402 | + |
2403 | +#: ../boots/lib/lingos/piped_sql.py:97 |
2404 | +msgid "error1" |
2405 | +msgstr "" |
2406 | + |
2407 | +#: ../boots/lib/lingos/piped_sql.py:109 |
2408 | +msgid "SQL is only permitted for the first clause" |
2409 | +msgstr "" |
2410 | + |
2411 | +#: ../boots/lib/ui/components/help.py:68 |
2412 | +msgid "See also: {0.see_also}" |
2413 | +msgstr "" |
2414 | + |
2415 | +#: ../boots/lib/ui/components/help.py:72 |
2416 | +msgid "Subtopics" |
2417 | +msgstr "" |
2418 | + |
2419 | +#: ../boots/lib/ui/components/help.py:106 |
2420 | +msgid "query object {0} is not a string or tuple of strings" |
2421 | +msgstr "" |
2422 | + |
2423 | +#: ../boots/lib/ui/components/metacommands.py:72 |
2424 | +msgid "Conflicts between metacommands detected." |
2425 | +msgstr "" |
2426 | + |
2427 | +#: ../boots/lib/ui/components/metacommands.py:109 |
2428 | +msgid "{0} takes {1} arguments ({2} given)" |
2429 | +msgstr "" |
2430 | + |
2431 | +#: ../boots/lib/ui/plain.py:47 |
2432 | +msgid "Plain UI" |
2433 | +msgstr "" |
2434 | + |
2435 | +#: ../boots/lib/ui/plain.py:47 |
2436 | +msgid "Plain UI documentation." |
2437 | +msgstr "" |
2438 | + |
2439 | +#: ../boots/lib/ui/plain.py:48 |
2440 | +msgid "A simple, minimal user interface for Boots." |
2441 | +msgstr "" |
2442 | + |
2443 | +#: ../boots/lib/ui/plain.py:50 |
2444 | +msgid "List of Plain UI commands." |
2445 | +msgstr "" |
2446 | + |
2447 | +#: ../boots/lib/ui/plain.py:51 |
2448 | +msgid "use" |
2449 | +msgstr "" |
2450 | + |
2451 | +#: ../boots/lib/ui/plain.py:51 |
2452 | +msgid "Switch to a different lingo." |
2453 | +msgstr "" |
2454 | + |
2455 | +#: ../boots/lib/ui/plain.py:137 |
2456 | +msgid "Unable to run pager command \"{0}\". Pager disabled.\n" |
2457 | +msgstr "" |
2458 | + |
2459 | +#: ../boots/lib/ui/plain.py:186 |
2460 | +msgid "{count} row in set ({elapsed:.2f} seconds)" |
2461 | +msgid_plural "{count} rows in set ({elapsed:.2f} seconds)" |
2462 | +msgstr[0] "" |
2463 | +msgstr[1] "" |
2464 | + |
2465 | +#: ../boots/lib/ui/plain.py:190 |
2466 | +msgid "{count} row affected ({elapsed:.2f} seconds)" |
2467 | +msgid_plural "{count} rows affected ({elapsed:.2f} seconds)" |
2468 | +msgstr[0] "" |
2469 | +msgstr[1] "" |
2470 | + |
2471 | +#: ../boots/lib/ui/plain.py:215 |
2472 | +msgid "Status" |
2473 | +msgstr "" |
2474 | + |
2475 | +#: ../boots/lib/ui/plain.py:220 |
2476 | +msgid "WARNING" |
2477 | +msgstr "" |
2478 | + |
2479 | +#: ../boots/lib/ui/plain.py:225 |
2480 | +msgid "ERROR" |
2481 | +msgstr "" |
2482 | + |
2483 | +#: ../boots/lib/ui/plain.py:246 |
2484 | +msgid "The specified lingo \"{0}\" does not exist." |
2485 | +msgstr "" |
2486 | + |
2487 | +#: ../boots/lib/console.py:69 |
2488 | +msgid "Boots encountered an internal error: {0}" |
2489 | +msgstr "" |
2490 | + |
2491 | +#: ../boots/lib/console.py:119 |
2492 | +msgid "Boots Help" |
2493 | +msgstr "" |
2494 | + |
2495 | +#: ../boots/lib/console.py:119 |
2496 | +msgid "Help" |
2497 | +msgstr "" |
2498 | + |
2499 | +#: ../boots/lib/console.py:120 |
2500 | +msgid "" |
2501 | +"Boots is a flexible, extensible, and multilingual shell for working with " |
2502 | +"databases. To read more about a subtopic, use `help <topic>`." |
2503 | +msgstr "" |
2504 | + |
2505 | +#: ../boots/lib/console.py:123 |
2506 | +msgid "List of Boots commands." |
2507 | +msgstr "" |
2508 | + |
2509 | +#: ../boots/lib/console.py:126 |
2510 | +msgid "quit" |
2511 | +msgstr "" |
2512 | + |
2513 | +#: ../boots/lib/console.py:126 |
2514 | +msgid "Quit boots." |
2515 | +msgstr "" |
2516 | + |
2517 | +#: ../boots/lib/console.py:127 |
2518 | +msgid "help" |
2519 | +msgstr "" |
2520 | + |
2521 | +#: ../boots/lib/console.py:127 |
2522 | +msgid "Show help for a topic." |
2523 | +msgstr "" |
2524 | + |
2525 | +#: ../boots/lib/console.py:128 |
2526 | +msgid "" |
2527 | +"help <topic>\n" |
2528 | +"Show help for \"topic\"." |
2529 | +msgstr "" |
2530 | + |
2531 | +#: ../boots/lib/console.py:131 |
2532 | +msgid "Lingos" |
2533 | +msgstr "" |
2534 | + |
2535 | +#: ../boots/lib/console.py:131 |
2536 | +msgid "Documentation for loaded lingos." |
2537 | +msgstr "" |
2538 | + |
2539 | +#: ../boots/lib/console.py:132 |
2540 | +msgid "A lingo is a command language usable within Boots." |
2541 | +msgstr "" |
2542 | + |
2543 | +#: ../boots/lib/console.py:144 |
2544 | +msgid "Invalid lingo \"{0}\" specified." |
2545 | +msgstr "" |
2546 | + |
2547 | +#: ../boots/lib/console.py:173 |
2548 | +msgid "Could not connect to {0}:{1}" |
2549 | +msgstr "" |
2550 | + |
2551 | +#: ../boots/lib/console.py:192 |
2552 | +msgid "{0.hostname}:{0.port} (server v{0.server_version})" |
2553 | +msgstr "" |
2554 | + |
2555 | +#: ../boots/lib/console.py:227 |
2556 | +msgid "Boots quit." |
2557 | +msgstr "" |
2558 | + |
2559 | +#: ../boots/lib/console.py:245 |
2560 | +msgid "Did you mean: {0}?" |
2561 | +msgstr "" |
2562 | + |
2563 | +#: ../boots/lib/console.py:247 |
2564 | +msgid "No help found for \"{0}\"." |
2565 | +msgstr "" |
2566 | + |
2567 | +#: ../boots/lib/console.py:276 |
2568 | +msgid "Could not open file {0}" |
2569 | msgstr "" |
2570 | |
2571 | === modified file 'po/createpots.sh' |
2572 | --- localizations/createpots.sh 2010-03-02 01:04:23 +0000 |
2573 | +++ po/createpots.sh 2010-03-07 09:23:23 +0000 |
2574 | @@ -1,3 +1,3 @@ |
2575 | #!/bin/bash |
2576 | -xgettext --from-code=utf-8 --keyword=tr --default-domain=cms --output=boots.pot --files-from=potfiles.txt |
2577 | +find ../boots/ -name *.py | xargs xgettext --language=Python --keyword="n_:1,2" --output=boots.pot --package-name="boots" --msgid-bugs-address="http://translations.launchpad.net/boots" |
2578 | |
2579 | |
2580 | === modified file 'tests/boots/api/api_tests.py' |
2581 | --- tests/boots/api/api_tests.py 2010-02-27 05:40:10 +0000 |
2582 | +++ tests/boots/api/api_tests.py 2010-03-07 09:23:23 +0000 |
2583 | @@ -28,7 +28,7 @@ |
2584 | import boots_unit_test |
2585 | |
2586 | class TestServerConnections(boots_unit_test.BootsQueryTester): |
2587 | - """Class that provides unit tests that test the api.Server class' ability |
2588 | + """Class that provides unit tests that test the api.DrizzleServer class' ability |
2589 | to connect to servers as well as provide proper errors when connections |
2590 | should fail.""" |
2591 | #FIXME should use the configuration provided. |
2592 | @@ -38,54 +38,54 @@ |
2593 | pass |
2594 | |
2595 | def test_valid_connect_fqdn(self): |
2596 | - server = api.Server("capstonedd.cs.pdx.edu", 9306, {}) |
2597 | + server = api.DrizzleServer("capstonedd.cs.pdx.edu", 9306, {}) |
2598 | server.connect() |
2599 | self.assert_(server.is_connected) |
2600 | server.disconnect() |
2601 | |
2602 | def test_valid_connect_partial(self): |
2603 | - server = api.Server("capstonedd", 9306, {}) |
2604 | + server = api.DrizzleServer("capstonedd", 9306, {}) |
2605 | server.connect() |
2606 | self.assert_(server.is_connected) |
2607 | server.disconnect() |
2608 | |
2609 | def test_valid_connect_ip(self): |
2610 | - server = api.Server("131.252.214.178", 9306, {}) |
2611 | + server = api.DrizzleServer("131.252.214.178", 9306, {}) |
2612 | server.connect() |
2613 | self.assert_(server.is_connected) |
2614 | server.disconnect() |
2615 | |
2616 | def test_valid_connect_fqdn_with_db(self): |
2617 | boots_unit_test.BootsQueryTester.setUp(self) |
2618 | - server = api.Server("capstonedd.cs.pdx.edu", 9306, {"database": "fec"}) |
2619 | + server = api.DrizzleServer("capstonedd.cs.pdx.edu", 9306, {"database": "fec"}) |
2620 | server.connect() |
2621 | self.assert_(server.is_connected) |
2622 | server.disconnect() |
2623 | |
2624 | def test_invalid_connect(self): |
2625 | - server = api.Server("kororaa.cs.pdx.edu", 9306, {}) |
2626 | + server = api.DrizzleServer("kororaa.cs.pdx.edu", 9306, {}) |
2627 | self.assertRaises(errors.InterfaceError, server.connect) |
2628 | self.assert_(not server.is_connected) |
2629 | |
2630 | def test_valid_disconnect(self): |
2631 | - server = api.Server("capstonedd.cs.pdx.edu", 9306, {}) |
2632 | + server = api.DrizzleServer("capstonedd.cs.pdx.edu", 9306, {}) |
2633 | server.connect() |
2634 | server.disconnect() |
2635 | self.assert_(not server.is_connected) |
2636 | |
2637 | def test_invalid_disconnect(self): |
2638 | - server = api.Server("kororaa.cs.pdx.edu", 9306, {}) |
2639 | + server = api.DrizzleServer("kororaa.cs.pdx.edu", 9306, {}) |
2640 | self.assertRaises(errors.InterfaceError, server.connect) |
2641 | self.assertRaises(AttributeError, server.disconnect) |
2642 | self.assert_(not server.is_connected) |
2643 | |
2644 | class TestServerExecute(boots_unit_test.BootsQueryTester): |
2645 | - """Class that provides unit tests that tests api.Server class' ability |
2646 | + """Class that provides unit tests that tests api.DrizzleServer class' ability |
2647 | to execute SQL statements on a server.""" |
2648 | #FIXME needs to test more than just SELECT statements. |
2649 | def setUp(self): |
2650 | boots_unit_test.BootsQueryTester.setUp(self) |
2651 | - self.server = api.Server(self.config["host"], self.config["port"], {"database": self.config["database"]}) |
2652 | + self.server = api.DrizzleServer(self.config["host"], self.config["port"], {"database": self.config["database"]}) |
2653 | self.server.connect() |
2654 | |
2655 | def tearDown(self): |
2656 | |
2657 | === removed file 'tests/boots/lib/lingos/cand_correct.txt' |
2658 | --- tests/boots/lib/lingos/cand_correct.txt 2010-02-21 21:24:59 +0000 |
2659 | +++ tests/boots/lib/lingos/cand_correct.txt 1970-01-01 00:00:00 +0000 |
2660 | @@ -1,256 +0,0 @@ |
2661 | -H0AK00089,CRAWFORD, HARRY T JR,H,AK,DEM,4350 BUTTE CIR,ANCHORAGE,AK,99504 |
2662 | -H0AL00016,BOZEMAN, MARTHA RENEE,H,AL,UNK,PO BOX 2512,BIRMINGHAM,AL,35201 |
2663 | -H0AL01030,GOUNARES, PETER HUNTER,H,AL,REP,4881 CYPRESS VILLAGE BLVD UNIT E,ORANGE BEACH,AL,36561 |
2664 | -H0AL02095,JOHN, ROBERT E JR,H,AL,IND,1465 W OVERBROOK ROAD,MILLBROOK,AL,36054 |
2665 | -H0AL02103,BARBER, RICK JOE,H,AL,REP,211 ARROWHEAD DRIVE,MONTGOMERY,AL,36117 |
2666 | -H0AL05163,BROOKS, MO,H,AL,REP,7610 FOXFIRE DRIVE,HUNTSVILLE,AL,35802 |
2667 | -H0AL06088,COOKE, STANLEY KYLE,H,AL,REP,723 CHERRY BROOK ROAD,KIMBERLY,AL,35091 |
2668 | -H0AL06096,LAMBERT, PAUL ANTHONY,H,AL,REP,POST OFFICE BOX 244,MAYLENE,AL,35114 |
2669 | -H0AL07086,SEWELL, TERRYCINA ANDREA,H,AL,DEM,PO BOX 1964,BIRMINGHAM,AL,35201 |
2670 | -H0AL07094,HILLIARD, EARL FREDERICK JR,H,AL,DEM,PO BOX 12804,BIRMINGHAM,AL,35202 |
2671 | -H0AL07102,SMOOT, SHEILA,H,AL,DEM,PO BOX 1354,BIRMINGHAM,AL,35201 |
2672 | -H0AL07128,MOKOLO, PATRICIA EVANS,H,AL,DEM,15 EL DORADO EAST,TUSCALOOSA,AL,35405 |
2673 | -H0AL07136,LANKSTER, FRANK,H,AL,DEM,1000 COATS AVE EAST,LINDEN,AL,367481608 |
2674 | -H0AL07144,WALLER, MICHELE,H,AL,REP,PO BOX 10711,BIRMINGHAM,AL,35202 |
2675 | -H0AR01083,CRAWFORD, ERIC ALAN RICK,H,AR,REP,34 CR 455,JONESBORO,AR,72404 |
2676 | -H0AR01091,GREGORY, JAMES CHRISTOPHER,H,AR,DEM,510 S LILLY ST,BLYTHEVILLE,AR,72315 |
2677 | -H0AR02099,MEEKS, DAVID MARK EMERSON,H,AR,REP,3130 DAVE WARD DR,CONWAY,AR,72034 |
2678 | -H0AR02107,GRIFFIN, JOHN TIMOTHY,H,AR,REP,1819 NORTH TYLER STREET,LITTLE ROCK,AR,72217 |
2679 | -H0AR02115,WALLACE, SCOTT,H,AR,REP,PO BOX 242600,LITTLE ROCK,AR,72223 |
2680 | -H0AR03022,SKOCH, BERNARD KURT BERNIE'',H,AR,REP,21142 KIRKSEY ROAD,ELKINS,AR,72727 |
2681 | -H0AR03030,WHITAKER, DAVID JEFFREY,H,AR,DEM,PO BOX 957,FAYETTEVILLE,AR,727020957 |
2682 | -H0AR04038,ROSS, MICHAEL AVERY,H,AR,DEM,PO BOX 360,PRESCOTT,AR,71857 |
2683 | -H0AS00018,FALEOMAVAEGA, ENI,H,AS,DEM,PO BOX 44669,WASHINGTON,DC,20026 |
2684 | -H0AZ01184,FLAKE, JEFF MR.,H,AZ,REP,4222 E. MCLELLAN, NO. 19,MESA,AZ,85205 |
2685 | -H0AZ01259,GOSAR, PAUL ANTHONY,H,AZ,REP,7485 RAIN VALLEY RD,FLAGSTAFF,AZ,86004 |
2686 | -H0AZ01267,BEAUCHAMP, BRADLEY DON,H,AZ,REP,PO BOX 222,GLOBE ,AZ,85502 |
2687 | -H0AZ01275,BOWERS, RUSSELL WESLEY,H,AZ,REP,PO BOX 336,SUPERIOR,AZ,85173 |
2688 | -H0AZ03305,HULBURD, JON,H,AZ,DEM,4340 E INDIAN SCHOOL RD #21-467,PHOENIX,AZ,85018 |
2689 | -H0AZ04485,HILL, BRIAN,H,AZ,IND,PO BOX 6013,PHOENIX,AZ,85005 |
2690 | -H0AZ05078,WARD, JIM,H,AZ,REP,9342 EAST SANDS DRIVE,SCOTTSDALE,AZ,85255 |
2691 | -H0AZ05086,WNUCK, ERIC,H,AZ,REP,8630 E WINDROSE,SCOTTSDALE,AZ,85260 |
2692 | -H0AZ05094,SMITH, JEFFREY W,H,AZ,REP,3713 E ENCINAS AVE,GILBERT,AZ,85234 |
2693 | -H0AZ05102,SALVINO, CHRIS,H,AZ,REP,7694 E ROSE GARDEN LANE,SCOTTSDALE,AZ,85255 |
2694 | -H0AZ06043,KELSEY, EASTON CLINT,H,AZ,REP,3927 S RIM RD,GILBERT,AZ,85297 |
2695 | -H0AZ07017,MCCLUNG, RUTH CRAWFORD,H,AZ,REP,3963 W PROSPERITY MINE PL,TUCSON,AZ,85745 |
2696 | -H0AZ08015,KELLY, JESSE,H,AZ,REP,7481 W PHOBOS DR,TUCSON,AZ,85743 |
2697 | -H0AZ08023,GOSS, VINCENT ANDREW (ANDY),H,AZ,REP,4617 TERRITORIAL LP,SIERRA VISTA,AZ,85635 |
2698 | -H0AZ08031,MILLER, BRIAN ALLAN,H,AZ,REP,PO BOX 15023,TUCSON,AZ,857085023 |
2699 | -H0AZ08049,CARLSON, THOMAS A JR,H,AZ,REP,9598 N ELAN LANE,TUCSON,AZ,85742 |
2700 | -H0CA00066,FINK, MARI HAMLIN,H,CA,REP,3410 BANGOR PLACE,SAN DIEGO,CA,92106 |
2701 | -H0CA02120,STIGLICH, PETER VINCENT,H,CA,REP,14684 SINGING TREES LANE,COTTONWOOD,CA,96022 |
2702 | -H0CA03078,BERA, AMERIASH,H,CA,DEM,61707 PIRATE POINT CT,ELK GROVE,CA,95758 |
2703 | -H0CA03086,DAVIS, GARY,H,CA,DEM,510 BERCUT DRIVE SUITE S,SACRAMENTO,CA,95811 |
2704 | -H0CA03094,SLATON, BILL,H,CA,DEM,555 CAPITOL MALL SUITE 1425,SACRAMENTO,CA,95814 |
2705 | -H0CA06089,ROMANOWSKY, PETER CHRISTIAN,H,CA,REP,300 NAPA ST #40,SAUSACITO,CA,94965 |
2706 | -H0CA08069,DENNIS, JOHN,H,CA,REP,1592 UNION STREET,SAN FRANCISCO,CA,941234531 |
2707 | -H0CA08077,SHIELDS, SUMMER J,H,CA,DEM,PO BOX 882472,SAN FRANCISCO,CA,94188 |
2708 | -H0CA10073,DESAULNIER, MARK,H,CA,DEM,POST OFFICE BOX 6066,CONCORD,CA,94524 |
2709 | -H0CA10081,HAMPTON, ADRIEL,H,CA,DEM,5425 DE MARCUS BLVD., APT 104,DUBLIN,CA,94568 |
2710 | -H0CA10099,BUCHANAN, JOAN,H,CA,DEM,555 CAPITOL MALL, SUITE 1425,SACRAMENTO,CA,95814 |
2711 | -H0CA10107,WOODS, ANTHONY MR,H,CA,DEM,PO BOX 28,FAIRFIELD,CA,94533 |
2712 | -H0CA10115,VANGUNDY, GINO W,H,CA,IND,1500 OLIVER ROAD #K259,FAIRFIELD,CA,94533 |
2713 | -H0CA10123,HARMER, DAVID JEFFREY,H,CA,REP,9321 SILVERBEND LANE,ELK GROVE,CA,95624 |
2714 | -H0CA10131,ATTWOOD, TIFFANY,H,CA,DEM,939 RICHARD LANE,DANVILLE,CA,94526 |
2715 | -H0CA10149,GARAMENDI, JOHN,H,CA,DEM,C/O CALIFORNIA POLITICAL LAW, INC.,LONG BEACH,CA,90807 |
2716 | -H0CA10156,BUNCH, CHRISTOPHER N,H,CA,REP,1652 W TEXAS ST SUITE 258,FAIRFIELD,CA,94533 |
2717 | -H0CA10164,CLOWARD, JEREMY,H,CA,GRE,1713 MARY DRIVE,PLEASANT HILL,CA,94523 |
2718 | -H0CA10180,CLIFT, GARY WILLIAM,H,CA,REP,PO BOX 841,DIXON,CA,95620 |
2719 | -H0CA10198,LOOS, MARK ALAN,H,CA,REP,5631 CARNEGIE WAY,LIVERMORE,CA,94550 |
2720 | -H0CA10206,TOTH, JOHN RICHARD,H,CA,REP,2270 BACON STREET,CONCORD,CA,94520 |
2721 | -H0CA11295,DEL ARROZ, JONATHAN MR.,H,CA,REP,656 TUNBRIDGE ROAD,DANVILLE,CA,945263651 |
2722 | -H0CA11303,GOEHRING, BRAD,H,CA,REP,21500 CLEMENTS ROAD,ACAMPO,CA,95220 |
2723 | -H0CA11329,BEADLES, ROBERT,H,CA,REP,1040 W KETTLEMAN LANE #388,LODI,CA,95240 |
2724 | -H0CA11337,AMADOR, ANTONIO C,H,CA,REP,30151 TOMAS,RCHO STA MARGARITA,CA,92688 |
2725 | -H0CA11345,TAKADA, JEFFREY DAVID,H,CA,REP,1081 BRENDA LEE DRIVE,MANTECA,CA,95337 |
2726 | -H0CA11352,EMKEN, ELIZABETH,H,CA,REP,243 MORRIS RANCH COURT,DANVILLE,CA,94526 |
2727 | -H0CA15148,HONDA, MIKE,H,CA,DEM,P.O. BOX 8180,SAN JOSE,CA,95155 |
2728 | -H0CA15171,SIMITIAN, S. JOSEPH,H,CA,DEM,2059 CAMDEN AVENUE #281,SAN JOSE,CA,95124 |
2729 | -H0CA15189,BARICH, DON,H,CA,REP,1071 WILMINGTON AVE,SAN JOSE,CA,95129 |
2730 | -H0CA16088,HOWELL, ROBERT PAUL,H,CA,REP,1145 ARDSLEY CT,SAN JOSE,CA,951201783 |
2731 | -H0CA18050,BERRYHILL, MICHAEL CLARE SR,H,CA,REP,1842 EAST TAYLOR ROAD,CERES,CA,95307 |
2732 | -H0CA19124,MARSDEN, LES,H,CA,DEM,7145 SNYDER CREEK ROAD,MARIPOSA,CA,953389641 |
2733 | -H0CA19132,GOODWIN, LORAINE,H,CA,DEM,1009 W FOURTH STREET,MADERA,CA,93637 |
2734 | -H0CA19173,DENHAM, JEFF,H,CA,REP,941 E MONTE VISTA,TURLOCK,CA,95381 |
2735 | -H0CA20080,MILLER, JOSHUA THOMAS COULSTON,H,CA,REP,PO BOX 711,HANFORD,CA,93277 |
2736 | -H0CA20098,VIDAK, JAMES ANDREW,H,CA,REP,13775 LACY BLVD,VISALIA,CA,93230 |
2737 | -H0CA20114,LAKE, RICHARD DAVID GEORGE,H,CA,REP,7005 N MAPLE AVE SUITE 101,FRESNO,CA,93720 |
2738 | -H0CA23043,KALEMKARIAN, TIMOTHY CHARLES,H,CA,REP,PO BOX 3272,WESTLAKE VILLAGE,CA,91359 |
2739 | -H0CA23092,STOCKDALE, DAVID R,H,CA,REP,2151 S COLLEGE DR SUITE 101,SANTA MARIA,CA,93455 |
2740 | -H0CA23100,DAVIDSON, JOHN,H,CA,REP,1710 N MOORPARK ROAD SUITE 18,THOUSAND OAKS,CA,91360 |
2741 | -H0CA24108,STERN, SHAWN,H,CA,DEM,1212 S VICTORY BLVD,BURBANK,CA,91502 |
2742 | -H0CA24116,ALLISON, TIMOTHY JAMES,H,CA,DEM,PO BOX 4304,SANTA BARBARA,CA,93140 |
2743 | -H0CA27085,SCHIFF, ADAM,H,CA,DEM,777 S. FIGUEROA ST., STE. 4050,LOS ANGELES,CA,90017 |
2744 | -H0CA28091,FROYD, ERIK MERLIN,H,CA,REP,PO BOX 6455,BURBANK,CA,91510 |
2745 | -H0CA29081,LARGE, MORTON R,H,CA,REP,PO BOX 5003,BURBANK,CA,91504 |
2746 | -H0CA30063,DAVID, ARI,H,CA,REP,PO BOX 163,MALIBU,CA,90265 |
2747 | -H0CA30071,FLUTIE, ROBERT A,H,CA,REP,888 S FIGUEROA #860,LOS ANGELES,CA,90017 |
2748 | -H0CA30089,BENNING, DAVID,H,CA,REP,5931 WOODLAND VIEW DRIVE,WOODLAND HILLS,CA,913671075 |
2749 | -H0CA32101,CHU, JUDY,H,CA,DEM,777 S. FIGUEROA ST., STE.4050,LOS ANGELES,CA,90017 |
2750 | -H0CA32119,ROMERO, GLORIA,H,CA,,PO BOX 32398,LOS ANGELES,CA,90032 |
2751 | -H0CA32127,CEDILLO, GILBERT,H,CA,DEM,1212 S VICTORY BLVD,BURBANK,CA,91502 |
2752 | -H0CA32135,HERNANDEZ, TERESA,H,CA,REP,2001 SANTA ANITA AVENUE, #103,SOUTH EL MONTE,CA,91733 |
2753 | -H0CA32176,ALONSO, FRANCISCO,H,CA,DEM,415-A N SIERR VISTA ST,MONTEREY PARK,CA,91755 |
2754 | -H0CA32192,ARIF, MOHAMMAD,H,CA,PAF,11520 JEFFERSON BOULEVARD, #201,CULVER CITY,CA,90230 |
2755 | -H0CA32200,BLAKE, M. WAYNE,H,CA,REP,506 EAST 9THSTREET, #2,AZUSA,CA,91702 |
2756 | -H0CA32218,CHU, BETTY,H,CA,REP,645 BARNUM WAY,MONTEREY PARK,CA,91754 |
2757 | -H0CA32226,DURAN, BENITA,H,CA,DEM,313 NORTH RECORD AVENUE,EAST LOS ANGELES,CA,90063 |
2758 | -H0CA32234,ESTRADA, ANDRES,H,CA,DEM,3700 EAGLE STREET,LOS ANGELES,CA,90063 |
2759 | -H0CA32242,LYSENKO, STEFAN (CONTRERAS),H,CA,DEM,P.O. BOX 58024,VAN NUYS,CA,91413 |
2760 | -H0CA32259,MORRISON, WILLIAM (RODRIGUEZ),H,CA,REP,2728 CINCINNATI STREET,LOS ANGELES,CA,90033 |
2761 | -H0CA32267,MOSTERT, NICK JUAN,H,CA,DEM,P.O. BOX 428,MONTEREY PARK,CA,91754 |
2762 | -H0CA32291,SANTANA, SALVADOR,H,CA,REP,1928 WEST KENOAK DRIVE,WEST COVINA,CA,91790 |
2763 | -H0CA32309,WONG, MARGARITA MARIE,H,CA,DEM,P.O. BOX 416,SAN GABRIEL,CA,91778 |
2764 | -H0CA32317,SCARBOROUGH, LARRY DEAN,H,CA,REP,POST OFFICE BOX 341174,LOS ANGELES,CA,900349174 |
2765 | -H0CA36128,KESTERSON, PETE,H,CA,REP,800 S PACIFIC COAST HWY #8-305,REDONDO BEACH,CA,90277 |
2766 | -H0CA36136,FEIN, MATTIE,H,CA,REP,PO BOX 2006,REDONDO BEACH,CA,90278 |
2767 | -H0CA39080,ANDRE, LARRY STEVEN,H,CA,REP,7033 MCMANUS STREET,LAKEWOOD,CA,90713 |
2768 | -H0CA42167,MCGROARTY, LEE,H,CA,REP,15338 CENTRAL AVENUE,CHINO,CA,947107658 |
2769 | -H0CA42175,LIBERATORE, PHILIP LAURENCE,H,CA,REP,14702 ROMERO DRIVE,WHITTIER,CA,90605 |
2770 | -H0CA45061,THIBODEAU, CLAYTON DEL,H,CA,REP,3800 W DEVONSHIRE AVE #D-128,HEMET,CA,92545 |
2771 | -H0CA47067,TRAN, VAN,H,CA,REP,8856 CITRUS AVE,WESTMINSTER,CA,92683 |
2772 | -H0CA47083,IGLESIAS, CECILIA PATRICIA,H,CA,IND,1322 S ARAPAHO DR,SANTA ANA,CA,92704 |
2773 | -H0CA48024,ISSA, DARRELL,H,CA,REP,PO BOX 760,VISTA,CA,92085 |
2774 | -H0CA48131,KROM, BETH,H,CA,DEM,1212 S VICTORY BLVD,BURBANK,CA,91502 |
2775 | -H0CA49055,DAVIS, SUSAN A,H,CA,DEM,5241 CANTERBURY DR.,SAN DIEGO,CA,92116 |
2776 | -H0CA49089,KATZ, HOWARD LOUIS,H,CA,DEM,35125 CALLE NOPAL,TEMECULA,CA,92592 |
2777 | -H0CA50061,EMBLEM, TRACY,H,CA,DEM,205 W. 5TH AVENUE SUITE 105,ESCONDIDO,CA,92025 |
2778 | -H0CA51028,MCLEROY, WILLIAM DAVID,H,CA,REP,3862 GATTY ST,SAN DIEGO,CA,92154 |
2779 | -H0CA53016,FRIEDMAN, MATT,H,CA,REP,302 WASHINGTON #942,SAN DIEGO,CA,92103 |
2780 | -H0CA53024,ARRINGTON, RANDALL STEVEN PHD,H,CA,REP,5206 MERRICK DRIVE,PEACHTREE CITY,GA,30269 |
2781 | -H0CA53032,WEAVER, CLARENCE MASON,H,CA,REP,PO BOX 33451,SAN DIEGO,CA,92163 |
2782 | -H0CA53040,MERRIMAN, CHARLES MILLER II,H,CA,REP,1740 ROOSEVELT AVE #M,SAN DIEGO,CA,92109 |
2783 | -H0CO00013,BROWN, DIGGS,H,CO,UNK,125 S HOWES STREET,FORT COLLINS,CO,80521 |
2784 | -H0CO02134,BAILEY, STEPHEN,H,CO,REP,6664 CHEROKEE COURT,HIWOT,CO,80503 |
2785 | -H0CO02142,BRANCATO, BOB,H,CO,REP,534 HART SSTREET,FIRESTONE,CO,80520 |
2786 | -H0CO03074,BEESON, MARTIN C,H,CO,REP,1533 DOGWOOD DRIVE,RIFLE,CO,81650 |
2787 | -H0CO03082,THOMPSON, DOUGLAS BOMAR,H,CO,REP,2694 DEL MAR DRIVE,GRAND JUNCTION,CO,81506 |
2788 | -H0CO03090,MCCONNELL, ROBERT M,H,CO,REP,PO 883133,STEAMBOAT SPRINGS,CO,80488 |
2789 | -H0CO04114,LUCERO, THOMAS JOSEPH,H,CO,REP,PO BOX 921,JOHNSTOWN,CO,80534 |
2790 | -H0CO04122,GARDNER, CORY,H,CO,REP,PO BOX 2408,LOVELAND,CO,80539 |
2791 | -H0CO04130,MADERE, DEAN MATTHEW,H,CO,REP,4092 GOLF VISTA DR,LOVELAND,CO,80537 |
2792 | -H0CO06069,CANTER, DAVID,H,CO,DEM,303 WEST WINTERTHUR WAY,HIGHLANDS RANCH,CO,80139 |
2793 | -H0CO06077,FLERLAGE, JOHN,H,CO,DEM,PO BOX 1279,LITTLETON,CO,80160 |
2794 | -H0CO07018,CAMPBELL, BRIAN T SR,H,CO,REP,6164 CARR STREET,ARVADA,CO,80004 |
2795 | -H0CO07026,FRAZIER, RYAN L,H,CO,REP,PO BOX 140182,EDGEWATER,CO,802140182 |
2796 | -H0CO07034,DEMING, MICHAEL,H,CO,REP,1171 S SABLE BLVD UNIT F,AURORA,CO,800124900 |
2797 | -H0CO07042,SHEELY, MICHEL L,H,CO,REP,48065 EAST 38TH AVENUE,BENNETT,CO,80102 |
2798 | -H0CO07059,LAKEY, JIMMY,H,CO,REP,PO BOX 402,ARVADA,CO,800010402 |
2799 | -H0CO07067,SIAS, LANGHORNE C,H,CO,REP,15400 WEST 64TH AVENUE SUITE 9F,ARVADA,CO,80007 |
2800 | -H0CT02132,DALY, MATTHEW MOULTON,H,CT,REP,14 PASTURE LANE,SOUTH GLASTONBURY,CT,06073 |
2801 | -H0CT03072,DELAURO, ROSA,H,CT,DEM,49 HUNTINGTON STREET,NEW HAVEN,CT,06511 |
2802 | -H0CT03106,LABRIOLA, JERRY JR,H,CT,REP,8 AUTUM LEAVES ROAD,WALLINGFORD,CT,06492 |
2803 | -H0CT04120,GREGORY, WILL,H,CT,REP,PO BOX 972,STAMFORD,CT,06904 |
2804 | -H0CT04138,MERKLE, ROBERT,H,CT,REP,PO BOX 155,WILTON,CT,06897 |
2805 | -H0CT04146,DEBICELLA, DAN,H,CT,REP,1 LAZYBROOK ROAD,SHELTON,CT,06484 |
2806 | -H0CT04153,RUSSO, ROBERT,H,CT,REP,PO BOX 3493,BRIDGEPORT,CT,06055 |
2807 | -H0CT04161,TORRES, ENRIQUE RAUL,H,CT,REP,108 MIDLAND STREET,BRIDGEPORT,CT,06605 |
2808 | -H0CT05119,BERNIER, JUSTIN,H,CT,REP,PO BOX 207,PLAINVILLE,CT,06062 |
2809 | -H0CT05135,CARTER, DANIEL E,H,CT,REP,PO BOX 11331,WATERBURY,CT,06703 |
2810 | -H0CT05143,WESTBY, KIE,H,CT,REP,45 HOMESTEAD ROAD,SOUTHBURY,CT,06488 |
2811 | -H0CT05150,GREENBERG, MARK DANIEL,H,CT,REP,184 FERN AVENUE,LITCHFIELD,CT,06759 |
2812 | -H0CT05168,EVANS, WILLIAM J JR,H,CT,REP,325 CELIA DRIVE,WOLCOTT,CT,06705 |
2813 | -H0CT05176,CALIGIURI, SAM,H,CT,REP,PO BOX 11252,WATERBURY,CT,06703 |
2814 | -H0DE00084,SPENCER, SCOTT RICHARD,H,DE,DEM,PO BOX 484,WILMINGTON,DE,19899 |
2815 | -H0DE00092,CULLIS, FREDERICK R,H,DE,REP,601 ORIOLE PLACE,HOCKESSIN,DE,19707 |
2816 | -H0DE00100,LAFLANC, EARL R,H,DE,CON,,WYOMING,DE,19934 |
2817 | -H0DE00118,CAMPBELL, DOUGLAS ALLEN JR,H,DE,CON,5979 SUMMIT BRIDGE RD,TOWNSEND,DE,19734 |
2818 | -H0DE01017,CARNEY, JOHN CHARLES JR,H,DE,DEM,506 WEST 19TH STREET,WILMINGTON,DE,19802 |
2819 | -H0DE01025,IZZO, ROSE,H,DE,REP,2115 COVENTRY DRIVE,WILMINGTON,DE,198102851 |
2820 | -H0FL00023,KRAUSE, JOHN E,H,FL,REP,2350 TEATE AVE,PENSACOLA,FL,32504 |
2821 | -H0FL01096,KELLEY, HENRY ARTHUR JR,H,FL,UNK,548 MARY ESTHER CUTOFF UNIT 18-133,FORT WALTON BEACH,FL,32548 |
2822 | -H0FL02086,LAWSON, ALFRED AL' JR',H,FL,DEM,400 NORTH ADAMS ST,TALLAHASSEE,FL,32301 |
2823 | -H0FL02094,MCKAIN, PAUL CRANDALL,H,FL,OTH,PO BOX 71,LLOYD,FL,32337 |
2824 | -H0FL02102,MEECE, CARL EDWARD JR,H,FL,REP,20253 153RD PL,O'BRIEN,FL,32071 |
2825 | -H0FL02110,SOUTHERLAND, WILLIAM STEVE II,H,FL,REP,PO BOX 1692,LYNN HAVEN,FL,32444 |
2826 | -H0FL03068,GILMAN, JAMES NICHOLAS,H,FL,REP,550 WATER ST STE 1325,JACKSONVILLE,FL,32202 |
2827 | -H0FL03076,MARTIN-BACK, TERRY L,H,FL,NPA,10930 NW 9TH PL,GAINESVILLE,FL,32606 |
2828 | -H0FL03084,ANNARUMMA, JOHN CARL,H,FL,OTH,PO BOX 971,ALACHUA,FL,32616 |
2829 | -H0FL03092,MACNAUGHTON, GEORGE SCOTT,H,FL,LIB,10336 DEPAUL DR,JACKSONVILLE,FL,32218 |
2830 | -H0FL04066,CRENSHAW, ANDER HON,H,FL,REP,2358 RIVERSIDE AVENUE,JACKSONVILLE,FL,322044641 |
2831 | -H0FL04108,STANLEY, TROY DWAYNE,H,FL,REP,12436 HICKORY FOREST RD,JACKSONVILLE,FL,32226 |
2832 | -H0FL05105,DOOLAN, THOMAS JOSEPH,H,FL,DEM,2114 MEDINA HILLS LN,MASCOTTE,FL,34753 |
2833 | -H0FL05121,SAGER, JASON PATRICK,H,FL,REP,915 HAMMOCK RD,BROOKSVILLE,FL,34601 |
2834 | -H0FL06061,YOST, MICHAEL F 'MIKE',H,FL,REP,902 HALSEMA RD S,JACKSONVILLE,FL,321101020 |
2835 | -H0FL07051,BACON, STEPHEN,H,FL,NNE,257 BAYOU CIRCLE,DEBARY,FL,32713 |
2836 | -H0FL07069,SILVA, PETER,H,FL,DEM,891 E RED HOUSE BRANCH RD,ST AUGUSTINE,FL,32084 |
2837 | -H0FL07077,BEAVEN, HEATHER MAURINE,H,FL,DEM,203 LONDON DRIVE,PALM COAST,FL,321379709 |
2838 | -H0FL07085,MORRIS, RALPH LEROY,H,FL,UNK,8300 MORRISON ROAD,HASTINGS,FL,321453907 |
2839 | -H0FL08091,FANELLI, DANIEL ROY,H,FL,REP,2958 MARQUESAS CT,WINDERMERE,FL,347867825 |
2840 | -H0FL08109,SULLIVAN, PATRICIA ANNE,H,FL,REP,901 HASELTON ST,EUSTIS,FL,32726 |
2841 | -H0FL08117,DUNMIRE, PEG,H,FL,REP,13352 PALOMA DR,ORLANDO,FL,32837 |
2842 | -H0FL08125,GUTIERREZ, ARMANDO JR,H,FL,REP,2640A MITCHAM DRIVE,TALLAHASSEE,FL,32308 |
2843 | -H0FL08133,BUTLER, KEVIN MICHAEL,H,FL,REP,4453 WINDERWOOD CIRCLE,ORLANDO,FL,32835 |
2844 | -H0FL08141,BROWN, PRINCE,H,FL,REP,PO BOX 607812,ORLANDO,FL,32860 |
2845 | -H0FL08158,KELLY, KURT,H,FL,REP,PO BOX 533021,ORLANDO,FL,328533021 |
2846 | -H0FL10055,JUSTICE, CHARLIE,H,FL,DEM,3101 60TH STREET NORTH,ST PETERSBURG,FL,33170 |
2847 | -H0FL10063,FORCADE, ERIC LEE,H,FL,REP,PO BOX 128,PALM HARBOR,FL,346820128 |
2848 | -H0FL11137,KELLER, SCOTT THOMAS,H,FL,REP,8754 HANDEL LOOP,LAND O LAKES,FL,34637 |
2849 | -H0FL11145,STANFORD, STEVEN CRAIG,H,FL,REP,3106 W FAIR OAKS AVE,TAMPA,FL,33611 |
2850 | -H0FL11152,BUNTYN, TONY,H,FL,REP,4934 W SAN RAFAEL ST,TAMPA,FL,33629 |
2851 | -H0FL12101,ROSS, DENNIS ALAN,H,FL,REP,607 LAKE MIRIAM DRIVE,LAKELAND,FL,33813 |
2852 | -H0FL12119,EDWARDS, LORI,H,FL,DEM,4066 LAKE MARIANNA DRIVE,WINTER HAVEN,FL,33881 |
2853 | -H0FL12127,SNIDER, THOMAS K,H,FL,LIB,810 WALSINGHAM WAY,VALRICO,FL,33594 |
2854 | -H0FL12135,WILKINSON, RANDY,H,FL,REP,100 E HOOKER STREET,BARTOW,FL,33830 |
2855 | -H0FL12143,EDWARDS, RANDOLPH,H,FL,DEM,1609 GRAND HERITAGE BLVD,VALRICO,FL,33594 |
2856 | -H0FL12150,LINDSEY, JOHN W JR,H,FL,REP,385 STERLING DRIVE,WINTER HAVEN,FL,33884 |
2857 | -H0FL13109,GOLDEN, JAMES THEOPOLIS,H,FL,DEM,4815 11TH AVE CIR E,BRADENTON,FL,34208 |
2858 | -H0FL16045,CRAFT, CHRISTOPHER LEE,H,FL,DEM,PO BOX 1328,FT PIERCE,FL,34954 |
2859 | -H0FL17035,WILLIAMS, ANDRE LEWIS,H,FL,DEM,1850 NW 170TH STREET,MIAMI GARDENS,FL,33056 |
2860 | -H0FL17050,BASTIEN, MARLEINE M,H,FL,DEM,PO BOX 381255,MIAMI,FL,33238 |
2861 | -H0FL17068,WILSON, FREDERICA S,H,FL,DEM,19821 NW 2ND AVENUE,MIAMI GARDENS,FL,33169 |
2862 | -H0FL17076,BRUTUS, PHILLIP J,H,FL,DEM,16801 NE 6 AVENUE,N MIAMI BEACH,FL,33162 |
2863 | -H0FL17084,GIBSON, SHIRLEY,H,FL,DEM,PO BOX 694313,MIAMI GARDENS,FL,33269 |
2864 | -H0FL17092,VEREEN, RODERICK D,H,FL,DEM,14630 S RIVER DRIVE,MIAMI,FL,33167 |
2865 | -H0FL17100,ADAM, LEROY,H,FL,DEM,888 NE 89 ST APT D,MIAMI,FL,33138 |
2866 | -H0FL17118,MOISE, RUDOLPH,H,FL,DEM,PO BOX 680417,NORTH MIA,FL,331689998 |
2867 | -H0FL18025,ROS-LEHTINEN, ILEANA,H,FL,REP,POST OFFICE BOX 52-2784,MIAMI,FL,33152 |
2868 | -H0FL19049,RUIZ, JOSE M,H,FL,DEM,6875 HOULTON CIR,LAKE WORTH,FL,334678742 |
2869 | -H0FL19056,LAROSE, JOSUE,H,FL,DEM,929 SW 15TH STREET,DEERFIELD BEACH,FL,33441 |
2870 | -H0FL19072,BUDD, JOSEPH E,H,FL,UNK,10396 SUNSTREAM LANE,BOCA RATON,FL,33428 |
2871 | -H0FL19080,DEUTCH, THEODORE ELIOT,H,FL,DEM,12373 CASCADES POINTE DRIVE,BOCA RATON,FL,33428 |
2872 | -H0FL19098,MCCORMICK, JIM,H,FL,NPA,22465 TIKI DR,BOCA RATON,FL,33428 |
2873 | -H0FL20039,LOWRY, ROBERT PAUL,H,FL,REP,940 N NORTHLAKE DR,HOLLYWOOD,FL,33020 |
2874 | -H0FL20054,SCHOCK, CLAYTON JAMES,H,FL,OTH,641 WOODGATE LN,SUNRISE,FL,33326 |
2875 | -H0FL21029,SANCHEZ, WILLAIM,H,FL,DEM,9725 SW 140TH ST,MIAMI,FL,33176 |
2876 | -H0FL24015,MILLER, KENNETH JOHN,H,FL,REP,4098 SCARLET IRIS PL,WINTER PARK,FL,32792 |
2877 | -H0FL24023,DIEBEL, KAREN,H,FL,REP,941 GEORGIA AVE,WINTER PARK,FL,32789 |
2878 | -H0FL24031,HUKILL, DOROTHY L,H,FL,REP,5832 WALES AVENUE,PORT ORANGE,FL,32127 |
2879 | -H0FL24049,ADAMS, SANDY,H,FL,REP,PO BOX 1566,ORLANDO,FL,32802 |
2880 | -H0FL24056,CAMPBELL, SEAN FIELD,H,FL,REP,PO BOX 542846,MERRITT ISLAND,FL,32954 |
2881 | -H0FL24064,LONG, OMETRIAS DEON,H,FL,REP,POST OFFICE BOX 3119,WINTER PARK,FL,327903119 |
2882 | -H0FL24072,HEINZELMAN, JAMES P SR,H,FL,REP,2957 W ST RD 434 STE 200,LONGWOOD,FL,32779 |
2883 | -H0FL24080,MACINNES, CHAD,H,FL,REP,13638 CRYSTAL RIVER DRIVE,ORLANDO,FL,32828 |
2884 | -H0FL24098,SINCLAIR, LARRY,H,FL,UNK,9 SPRING DRIVE,PORT ORANGE,FL,32129 |
2885 | -H0FL24122,FOSTER, JAMES NORMAN,H,FL,REP,2116 ELMCREST PL,OVIEDO,FL,32765 |
2886 | -H0FL24130,GARCIA, GEORGE THOMAS,H,FL,REP,PO BOX 5037,TITUSVILLE,FL,32783 |
2887 | -H0GA02217,KEOWN, MICHAEL HUEL (MIKE),H,GA,REP,1086 W VIOLET AVE,COOLIDGE,GA,31738 |
2888 | -H0GA04023,LINDER, JOHN,H,GA,REP,PO BOX 4026,DULUTH,GA,30096 |
2889 | -H0GA04064,GAUSE, LAWRENCE FRANKLIN,H,GA,REP,PO BOX 225,TUCKER,GA,300850225 |
2890 | -H0GA04072,CARTER, LISBETH LIZ'',H,GA,REP,2639 BRIARLAKE RD NE,ATLANTA,GA,30345 |
2891 | -H0GA04080,JONES, VERNON A,H,GA,DEM,PO BOX 190496,ATLANTA,GA,31119 |
2892 | -H0GA08032,MARSHALL, JIM,H,GA,DEM,586 ORANGE STREET,MACON,GA,31201 |
2893 | -H0GA08040,DELOACH, KENNETH RAY JR,H,GA,REP,314 CHEYENNE DRIVE,WARNER ROBINS,GA,31093 |
2894 | -H0GA08065,HICKS, ANGELA,H,GA,REP,5962 ZEBULON ROAD BOX 306,MACON,GA,31210 |
2895 | -H0GA09022,EVANS, MIKE ALLEN,H,GA,REP,212 DAHLONEGA STREET,CUMMING,GA,30040 |
2896 | -H0GA09030,GRAVES, JOHN THOMAS JR,H,GA,REP,475 CRAIG ROAD,RANGER,GA,30734 |
2897 | -H0GA09055,STEPHENS, WILLIAM V,H,GA,REP,2300 BETHELVIEW ROAD,CUMMINGS,GA,30040 |
2898 | -H0GA09063,JONES, JEREMY EDWARD,H,GA,REP,190 PLAYHOUSE DRIVE,RINGGOLD,GA,30736 |
2899 | -H0GA09071,MOON, EDWARD 'EUGENE',H,GA,IND,3407 LAKE RIDGE PLACE,GAINESVILLE,GA,30506 |
2900 | -H0GA09089,HAWKINS, B LEE,H,GA,REP,4710 JIM HOOD ROAD,GAINESVILLE,GA,30506 |
2901 | -H0GA09097,TARVIN, THOMAS STEPHEN,H,GA,REP,PO BOX 365,CHICKAMAUGA,GA,307070365 |
2902 | -H0GA09105,FREEMAN, MIKE,H,GA,DEM,5194 GLENSTONE CT,GAINSVILLE,GA,30504 |
2903 | -H0GA09113,BENTON, CLYDE DANIEL,H,GA,REP,114 WASHINGTON ST,GAINESVILLE,GA,30501 |
2904 | -H0GA09121,LOFTMAN, BERTIL ARMIN,H,GA,REP,11342 BIG CANOE,JASPER,GA,30143 |
2905 | -H0GA09139,DOOLEY, THOMAS EUGENE,H,GA,REP,129 MORRIS DRIVE,RINGGOLD,GA,30736 |
2906 | -H0GA10228,CATES, CHRISTOPHER U MD,H,GA,REP,11 TIMBER GRACE COURT,BLAIRSVILLE,GA,30512 |
2907 | -H0GA12018,MOSLEY, EMMETT WAYNE JR,H,GA,REP,2107 ADAMS STREET,VIDALIA,GA,30474 |
2908 | -H0GA12026,SMITH, LAWTON CARLOS JR,H,GA,REP,3210 GILREATH DR,THUNDERBOLT,GA,31404 |
2909 | -H0GA12034,SEAVER, JEAN MARIE,H,GA,REP,63 RIVER BLUFF DRIVE,SAVANNAH,GA,31406 |
2910 | -H0GA13016,SLEEPER, SONJA,H,GA,REP,201 PORTER LN,JONESBORO,GA,30236 |
2911 | -H0GA13024,FRISBEE, MICHAEL,H,GA,IND,PO BOX 5,WINSTON,GA,30187 |
2912 | -H0HI01157,DJOU, CHARLES KONG,H,HI,REP,P O BOX 23580,HONOLULU,HI,96813 |
2913 | -H0HI01165,HANABUSA, COLLEEN WAKAKO,H,HI,DEM,92-1019 J KOIO DRIVE,KAPOLEI,HI,96707 |
2914 | -H0HI02106,WILLOUGHBY, JOHN WILLIAM,H,HI,REP,3054 ALA POHA PLACE APT 1811,HONOLULU,HI,968181678 |
2915 | -H0IA01109,BUDDE, JAMES R,H,IA,REP,23741 415TH AVE,BELLEVUE,IA,52031 |
2916 | -H0IA02107,SICARD, GARY JOSEPH,H,IA,LIB,585 BROUGHAM RD,ROBINS,IA,52328 |
2917 | \ No newline at end of file |
2918 | |
2919 | === modified file 'tests/boots/lib/lingos/lingo_tests.py' |
2920 | --- tests/boots/lib/lingos/lingo_tests.py 2010-02-27 05:40:10 +0000 |
2921 | +++ tests/boots/lib/lingos/lingo_tests.py 2010-03-07 09:23:23 +0000 |
2922 | @@ -27,7 +27,7 @@ |
2923 | from boots.app import client_config |
2924 | from boots.lib import console |
2925 | from boots.lib.lingos import lingo, bash_external, lisp, piped_sql, python, sql |
2926 | -import filecmp |
2927 | +import os |
2928 | import tempfile |
2929 | |
2930 | def node_graph_values(graph): |
2931 | @@ -105,13 +105,13 @@ |
2932 | super(TestPipedSQLInterpreter, self).setUp() |
2933 | self.sql = piped_sql.PipedSQLInterpreter(self.console) |
2934 | |
2935 | - def test_input_redirect(self): |
2936 | + def test_redirect(self): |
2937 | temp = tempfile.mkstemp(prefix = 'boots-test-file')[1] |
2938 | piped_sql_query = 'select * from cand; > {0}'.format(temp) |
2939 | graph = self.sql.execute(piped_sql_query) |
2940 | graph.start() |
2941 | graph.run() |
2942 | - self.assertTrue(filecmp.cmp(temp, 'lib/lingos/cand_correct.txt', False)) |
2943 | + self.assertEqual(len(open(temp).readlines()), 1458) |
2944 | |
2945 | class TestPythonInterpreter(LingoBaseTester): |
2946 | def setUp(self): |