11 |
11 |
12 from logilab.common import flatten |
12 from logilab.common import flatten |
13 from logilab.common.cli import CLIHelper |
13 from logilab.common.cli import CLIHelper |
14 from logilab.common.clcommands import BadCommandUsage, pop_arg, register_commands |
14 from logilab.common.clcommands import BadCommandUsage, pop_arg, register_commands |
15 from cubicweb.toolsutils import CONNECT_OPTIONS, Command |
15 from cubicweb.toolsutils import CONNECT_OPTIONS, Command |
16 |
16 |
17 # result formatter ############################################################ |
17 # result formatter ############################################################ |
18 |
18 |
19 PAGER = os.environ.get('PAGER', 'less') |
19 PAGER = os.environ.get('PAGER', 'less') |
20 |
20 |
21 def pager_format_results(writer, layout): |
21 def pager_format_results(writer, layout): |
22 """pipe results to a pager like more or less""" |
22 """pipe results to a pager like more or less""" |
23 (r, w) = os.pipe() |
23 (r, w) = os.pipe() |
24 pid = os.fork() |
24 pid = os.fork() |
25 if pid == 0: |
25 if pid == 0: |
40 os.waitpid(pid, 0) |
40 os.waitpid(pid, 0) |
41 |
41 |
42 def izip2(list1, list2): |
42 def izip2(list1, list2): |
43 for i in xrange(len(list1)): |
43 for i in xrange(len(list1)): |
44 yield list1[i] + tuple(list2[i]) |
44 yield list1[i] + tuple(list2[i]) |
45 |
45 |
46 def format_results(writer, layout, stream=sys.stdout): |
46 def format_results(writer, layout, stream=sys.stdout): |
47 """format result as text into the given file like object""" |
47 """format result as text into the given file like object""" |
48 writer.format(layout, stream) |
48 writer.format(layout, stream) |
49 |
49 |
50 |
50 |
51 try: |
51 try: |
58 if isinstance(value, unicode): |
58 if isinstance(value, unicode): |
59 return value.encode(encoding, 'replace') |
59 return value.encode(encoding, 'replace') |
60 return str(value) |
60 return str(value) |
61 |
61 |
62 # command line querier ######################################################## |
62 # command line querier ######################################################## |
63 |
63 |
64 class RQLCli(CLIHelper): |
64 class RQLCli(CLIHelper): |
65 """Interactive command line client for CubicWeb, allowing user to execute |
65 """Interactive command line client for CubicWeb, allowing user to execute |
66 arbitrary RQL queries and to fetch schema information |
66 arbitrary RQL queries and to fetch schema information |
67 """ |
67 """ |
68 # commands are prefixed by ":" |
68 # commands are prefixed by ":" |
72 'connect' : "CubicWeb", |
72 'connect' : "CubicWeb", |
73 'schema' : "CubicWeb", |
73 'schema' : "CubicWeb", |
74 'description' : "CubicWeb", |
74 'description' : "CubicWeb", |
75 'commit' : "CubicWeb", |
75 'commit' : "CubicWeb", |
76 'rollback' : "CubicWeb", |
76 'rollback' : "CubicWeb", |
77 'autocommit' : "Others", |
77 'autocommit' : "Others", |
78 'debug' : "Others", |
78 'debug' : "Others", |
79 }) |
79 }) |
80 |
80 |
81 def __init__(self, application=None, user=None, password=None, |
81 def __init__(self, application=None, user=None, password=None, |
82 host=None, debug=0): |
82 host=None, debug=0): |
83 CLIHelper.__init__(self, os.path.join(os.environ["HOME"], ".erqlhist")) |
83 CLIHelper.__init__(self, os.path.join(os.environ["HOME"], ".erqlhist")) |
84 self.cnx = None |
84 self.cnx = None |
85 self.cursor = None |
85 self.cursor = None |
92 self._last_result = None |
92 self._last_result = None |
93 self._previous_lines = [] |
93 self._previous_lines = [] |
94 if application is not None: |
94 if application is not None: |
95 self.do_connect(application, user, password, host) |
95 self.do_connect(application, user, password, host) |
96 self.do_debug(debug) |
96 self.do_debug(debug) |
97 |
97 |
98 def do_connect(self, application, user=None, password=None, host=None): |
98 def do_connect(self, application, user=None, password=None, host=None): |
99 """connect to an cubicweb application""" |
99 """connect to an cubicweb application""" |
100 from cubicweb.dbapi import connect |
100 from cubicweb.dbapi import connect |
101 if user is None: |
101 if user is None: |
102 user = raw_input('login: ') |
102 user = raw_input('login: ') |
111 self.cursor = self.cnx.cursor() |
111 self.cursor = self.cnx.cursor() |
112 # add entities types to the completion commands |
112 # add entities types to the completion commands |
113 self._completer.list = (self.commands.keys() + |
113 self._completer.list = (self.commands.keys() + |
114 self.schema.entities() + ['Any']) |
114 self.schema.entities() + ['Any']) |
115 print _('You are now connected to %s') % application |
115 print _('You are now connected to %s') % application |
116 |
116 |
117 |
117 |
118 help_do_connect = ('connect', "connect <application> [<user> [<password> [<host>]]]", |
118 help_do_connect = ('connect', "connect <application> [<user> [<password> [<host>]]]", |
119 _(do_connect.__doc__)) |
119 _(do_connect.__doc__)) |
120 |
120 |
121 def do_debug(self, debug=1): |
121 def do_debug(self, debug=1): |
125 self._format = format_results |
125 self._format = format_results |
126 else: |
126 else: |
127 self._format = pager_format_results |
127 self._format = pager_format_results |
128 if self._debug: |
128 if self._debug: |
129 print _('Debug level set to %s'%debug) |
129 print _('Debug level set to %s'%debug) |
130 |
130 |
131 help_do_debug = ('debug', "debug [debug_level]", _(do_debug.__doc__)) |
131 help_do_debug = ('debug', "debug [debug_level]", _(do_debug.__doc__)) |
132 |
132 |
133 def do_description(self): |
133 def do_description(self): |
134 """display the description of the latest result""" |
134 """display the description of the latest result""" |
135 if self.rset.description is None: |
135 if self.rset.description is None: |
136 print _('No query has been executed') |
136 print _('No query has been executed') |
137 else: |
137 else: |
138 print '\n'.join([', '.join(line_desc) |
138 print '\n'.join([', '.join(line_desc) |
139 for line_desc in self.rset.description]) |
139 for line_desc in self.rset.description]) |
140 |
140 |
141 help_do_description = ('description', "description", _(do_description.__doc__)) |
141 help_do_description = ('description', "description", _(do_description.__doc__)) |
142 |
142 |
143 def do_schema(self, name=None): |
143 def do_schema(self, name=None): |
144 """display information about the application schema """ |
144 """display information about the application schema """ |
145 if self.cnx is None: |
145 if self.cnx is None: |
146 print _('You are not connected to an application !') |
146 print _('You are not connected to an application !') |
147 return |
147 return |
157 if self.schema.has_relation(name): |
157 if self.schema.has_relation(name): |
158 self.display_schema(self.schema.rschema(name)) |
158 self.display_schema(self.schema.rschema(name)) |
159 done = 1 |
159 done = 1 |
160 if done is None: |
160 if done is None: |
161 print _('Unable to find anything named "%s" in the schema !') % name |
161 print _('Unable to find anything named "%s" in the schema !') % name |
162 |
162 |
163 help_do_schema = ('schema', "schema [keyword]", _(do_schema.__doc__)) |
163 help_do_schema = ('schema', "schema [keyword]", _(do_schema.__doc__)) |
164 |
164 |
165 |
165 |
166 def do_commit(self): |
166 def do_commit(self): |
167 """commit the current transaction""" |
167 """commit the current transaction""" |
168 self.cnx.commit() |
168 self.cnx.commit() |
169 |
169 |
170 help_do_commit = ('commit', "commit", _(do_commit.__doc__)) |
170 help_do_commit = ('commit', "commit", _(do_commit.__doc__)) |
171 |
171 |
172 def do_rollback(self): |
172 def do_rollback(self): |
173 """rollback the current transaction""" |
173 """rollback the current transaction""" |
174 self.cnx.rollback() |
174 self.cnx.rollback() |
175 |
175 |
176 help_do_rollback = ('rollback', "rollback", _(do_rollback.__doc__)) |
176 help_do_rollback = ('rollback', "rollback", _(do_rollback.__doc__)) |
177 |
177 |
178 def do_autocommit(self): |
178 def do_autocommit(self): |
179 """toggle autocommit mode""" |
179 """toggle autocommit mode""" |
180 self.autocommit = not self.autocommit |
180 self.autocommit = not self.autocommit |
181 |
181 |
182 help_do_autocommit = ('autocommit', "autocommit", _(do_autocommit.__doc__)) |
182 help_do_autocommit = ('autocommit', "autocommit", _(do_autocommit.__doc__)) |
183 |
183 |
184 |
184 |
185 def handle_line(self, stripped_line): |
185 def handle_line(self, stripped_line): |
186 """handle non command line : |
186 """handle non command line : |
187 if the query is complete, executes it and displays results (if any) |
187 if the query is complete, executes it and displays results (if any) |
188 else, stores the query line and waits for the suite |
188 else, stores the query line and waits for the suite |
230 |
230 |
231 class CubicWebClientCommand(Command): |
231 class CubicWebClientCommand(Command): |
232 """A command line querier for CubicWeb, using the Relation Query Language. |
232 """A command line querier for CubicWeb, using the Relation Query Language. |
233 |
233 |
234 <application> |
234 <application> |
235 identifier of the application to connect to |
235 identifier of the application to connect to |
236 """ |
236 """ |
237 name = 'client' |
237 name = 'client' |
238 arguments = '<application>' |
238 arguments = '<application>' |
239 options = CONNECT_OPTIONS + ( |
239 options = CONNECT_OPTIONS + ( |
240 ("verbose", |
240 ("verbose", |
245 ("batch", |
245 ("batch", |
246 {'short': 'b', 'type' : 'string', 'metavar': '<file>', |
246 {'short': 'b', 'type' : 'string', 'metavar': '<file>', |
247 'help': 'file containing a batch of RQL statements to execute.', |
247 'help': 'file containing a batch of RQL statements to execute.', |
248 }), |
248 }), |
249 ) |
249 ) |
250 |
250 |
251 def run(self, args): |
251 def run(self, args): |
252 """run the command with its specific arguments""" |
252 """run the command with its specific arguments""" |
253 appid = pop_arg(args, expected_size_after=None) |
253 appid = pop_arg(args, expected_size_after=None) |
254 batch_stream = None |
254 batch_stream = None |
255 if args: |
255 if args: |