Merge lp:~openerp-dev/openerp-command/trunk-entry-points-cto into lp:openerp-command
- trunk-entry-points-cto
- Merge into trunk
Proposed by
Samus CTO (OpenERP)
Status: | Work in progress |
---|---|
Proposed branch: | lp:~openerp-dev/openerp-command/trunk-entry-points-cto |
Merge into: | lp:openerp-command |
Diff against target: |
1406 lines (+518/-472) 30 files modified
README (+1/-1) openerpcommand/__init__.py (+95/-52) openerpcommand/addons/bench_sale_mrp/data.yml (+1/-1) openerpcommand/benchmark/__init__.py (+111/-0) openerpcommand/benchmark/dummy.py (+18/-0) openerpcommand/benchmark/fields_view_get.py (+20/-0) openerpcommand/benchmark/login.py (+13/-0) openerpcommand/benchmark/read.py (+20/-0) openerpcommand/benchmark/sale_mrp.py (+14/-18) openerpcommand/benchmarks.py (+0/-166) openerpcommand/call.py (+11/-20) openerpcommand/client.py (+0/-137) openerpcommand/common.py (+49/-14) openerpcommand/conf.py (+5/-10) openerpcommand/consume_cpu.py (+16/-0) openerpcommand/consume_memory.py (+16/-0) openerpcommand/consume_nothing.py (+11/-0) openerpcommand/drop.py (+2/-6) openerpcommand/initialize.py (+10/-8) openerpcommand/leak_memory.py (+11/-0) openerpcommand/model.py (+2/-5) openerpcommand/module.py (+4/-5) openerpcommand/open.py (+29/-0) openerpcommand/read.py (+1/-4) openerpcommand/run_tests.py (+4/-6) openerpcommand/scaffold.py (+1/-5) openerpcommand/show.py (+23/-0) openerpcommand/uninstall.py (+4/-7) openerpcommand/update.py (+1/-3) setup.py (+25/-4) |
To merge this branch: | bzr merge lp:~openerp-dev/openerp-command/trunk-entry-points-cto |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenERP Core Team | Pending | ||
Review via email: mp+135720@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 187. By Samus CTO (OpenERP)
-
[IMP] Missing calls to expand_path for addons paths
Unmerged revisions
- 187. By Samus CTO (OpenERP)
-
[IMP] Missing calls to expand_path for addons paths
- 186. By Samus CTO (OpenERP)
-
[IMP] Modules now use entry points and OE populate automatically
- 185. By Samus CTO (OpenERP)
-
[FIX] Agrolait's id has changed in default server demo data
- 184. By Samus CTO (OpenERP)
-
Updated TODO
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'README' |
2 | --- README 2012-10-31 10:52:02 +0000 |
3 | +++ README 2012-11-27 15:42:19 +0000 |
4 | @@ -4,6 +4,6 @@ |
5 | ---- |
6 | |
7 | - Make sure to have proper return code for success/failure. |
8 | -- `oe --help` is a mess (it seems argparse doesn't try to do something sensible). |
9 | - Have a flag for `run-tests` to drop in the debugger (to be combined with |
10 | unittest2 fail-fast mode?). |
11 | +- Make sure oe can do everything openerp-server can do |
12 | |
13 | === modified file 'openerpcommand/__init__.py' |
14 | --- openerpcommand/__init__.py 2012-11-05 11:27:01 +0000 |
15 | +++ openerpcommand/__init__.py 2012-11-27 15:42:19 +0000 |
16 | @@ -1,61 +1,104 @@ |
17 | +""" |
18 | +OpenERP Command provides a set of command-line tools around the OpenERP |
19 | +framework: openobject-server. All the tools are sub-commands of a single |
20 | +oe executable. |
21 | +""" |
22 | +import pkg_resources |
23 | +import sys |
24 | +import os |
25 | import argparse |
26 | -import textwrap |
27 | - |
28 | -from .call import Call |
29 | -from .client import Open, Show, ConsumeNothing, ConsumeMemory, LeakMemory, ConsumeCPU |
30 | -from .benchmarks import Bench, BenchRead, BenchFieldsViewGet, BenchDummy, BenchLogin |
31 | -from .bench_sale_mrp import BenchSaleMrp |
32 | -from . import common |
33 | - |
34 | -from . import conf # Not really server-side (in the `for` below). |
35 | -from . import drop |
36 | -from . import initialize |
37 | -from . import model |
38 | -from . import module |
39 | -from . import read |
40 | -from . import run_tests |
41 | -from . import scaffold |
42 | -from . import uninstall |
43 | -from . import update |
44 | - |
45 | -command_list_server = (conf, drop, initialize, model, module, read, run_tests, |
46 | - scaffold, uninstall, update, ) |
47 | - |
48 | -command_list_client = (Call, Open, Show, ConsumeNothing, ConsumeMemory, |
49 | - LeakMemory, ConsumeCPU, Bench, BenchRead, |
50 | - BenchFieldsViewGet, BenchDummy, BenchLogin, |
51 | - BenchSaleMrp, ) |
52 | + |
53 | +command_list = [] |
54 | +base_group = '%s.subcommands' % __name__ |
55 | +toplevel_mark = '==toplevel==' |
56 | + |
57 | +for group, entry_points in pkg_resources.get_entry_map(pkg_resources.get_distribution('openerp-command')).items(): |
58 | + if group.startswith(base_group): |
59 | + container = group[len(base_group)+1:] or None |
60 | + for name, entry_point in entry_points.items(): |
61 | + try: |
62 | + init_parser = entry_point.load() |
63 | + pkg = sys.modules[init_parser.__module__.rsplit('.', 1)[0]] |
64 | + description = unicode(sys.modules[init_parser.__module__].__doc__).strip() |
65 | + command_list.append( (name.replace('_', '-'), container, description, init_parser, pkg) ) |
66 | + except ImportError, e: |
67 | + sys.stderr.write("Invalid entry point: %s" % e.message + os.linesep) |
68 | |
69 | def main_parser(): |
70 | + |
71 | + def format_command(name, description): |
72 | + text = [name] |
73 | + if description: |
74 | + text.extend([ |
75 | + ":", os.linesep, |
76 | + " " + description.strip().replace("\n", "\n "), |
77 | + ]) |
78 | + return "".join(text) |
79 | + |
80 | parser = argparse.ArgumentParser( |
81 | usage=argparse.SUPPRESS, |
82 | - description=textwrap.fill(textwrap.dedent("""\ |
83 | - OpenERP Command provides a set of command-line tools around |
84 | - the OpenERP framework: openobject-server. All the tools are |
85 | - sub-commands of a single oe executable.""")), |
86 | + description=__doc__, |
87 | epilog="""Use <command> --help to get information about the command.""", |
88 | formatter_class=argparse.RawDescriptionHelpFormatter, |
89 | ) |
90 | - description = [] |
91 | - for x in command_list_server: |
92 | - description.append(x.__name__[len(__package__)+1:]) |
93 | - if x.__doc__: |
94 | - description.extend([ |
95 | - ":\n", |
96 | - textwrap.fill(str(x.__doc__).strip(), |
97 | - subsequent_indent=' ', |
98 | - initial_indent=' '), |
99 | - ]) |
100 | - description.append("\n\n") |
101 | - subparsers = parser.add_subparsers( |
102 | - title="Available commands", |
103 | - help=argparse.SUPPRESS, |
104 | - description="".join(description[:-1]), |
105 | - ) |
106 | - # Server-side commands. |
107 | - for x in command_list_server: |
108 | - x.add_parser(subparsers) |
109 | - # Client-side commands. TODO one per .py file. |
110 | - for x in command_list_client: |
111 | - x(subparsers) |
112 | + command_list.sort(key=lambda x:(x[1], x[0])) |
113 | + container_help = {} |
114 | + for name, container, description, _, pkg in command_list: |
115 | + # Update parent subparser help_text |
116 | + container = container or toplevel_mark |
117 | + if container in container_help or container is toplevel_mark: |
118 | + help_text = container_help.setdefault(container, []) |
119 | + else: |
120 | + parent_container = container.rsplit('.', 1)[0] if '.' in container else toplevel_mark |
121 | + container_name = container.rsplit('.', 1)[1] if '.' in container else container |
122 | + parent_text = container_help.setdefault(parent_container, []) |
123 | + parent_text.extend([format_command(container_name, pkg.__doc__), os.linesep * 2]) |
124 | + # Make subcommand help |
125 | + help_text = container_help.setdefault(container or toplevel_mark, []) |
126 | + help_text.extend([format_command(name, description), os.linesep * 2]) |
127 | + subparsers = {toplevel_mark : parser.add_subparsers( |
128 | + title="Available commands", |
129 | + help=argparse.SUPPRESS, |
130 | + description="".join(container_help[toplevel_mark][:-1])), |
131 | + } |
132 | + for _, container, _, _, pkg in command_list: |
133 | + if container is not None and container not in subparsers: |
134 | + path, name = container.rsplit('.', 1) if '.' in container else (toplevel_mark, container) |
135 | + parent_subparser = subparsers[path] |
136 | + parent_parser = parent_subparser.add_parser(name, description=pkg.__doc__, |
137 | + formatter_class=argparse.RawDescriptionHelpFormatter) |
138 | + subparsers[container] = parent_parser.add_subparsers( |
139 | + title="Available commands", |
140 | + help=argparse.SUPPRESS, |
141 | + description="".join(container_help[container][:-1]), |
142 | + ) |
143 | + for name, container, description, init_parser, _ in command_list: |
144 | + subparser = subparsers[container or toplevel_mark] |
145 | + command_parser = subparser.add_parser(name, description=description) |
146 | + init_parser(command_parser) |
147 | return parser |
148 | + |
149 | +def make_entry_points(path='.'): |
150 | + import pkgutil |
151 | + import pprint |
152 | + res = {} |
153 | + for importer, fullname, is_pkg in pkgutil.walk_packages(path): |
154 | + try: |
155 | + module_path = fullname.split('.') |
156 | + prefix, name = module_path[0], module_path[-1] |
157 | + if not prefix == __name__: |
158 | + continue |
159 | + module_container = ".".join(['openerpcommand','subcommands']+module_path[1:-1]) |
160 | + if is_pkg: |
161 | + # Needed to display importation errors |
162 | + __import__(fullname, globals(), locals(), [], -1) |
163 | + else: |
164 | + module = __import__(fullname, globals(), locals(), [name.split('.')[-1]], -1) |
165 | + entry_point_string = "%s = %s:init_parser" % (name, fullname) |
166 | + if not hasattr(module, 'init_parser'): |
167 | + print "#", entry_point_string |
168 | + else: |
169 | + res.setdefault(module_container, []).append( entry_point_string ) |
170 | + except ValueError: |
171 | + pass |
172 | + pprint.pprint(res, indent=4) |
173 | |
174 | === modified file 'openerpcommand/addons/bench_sale_mrp/data.yml' |
175 | --- openerpcommand/addons/bench_sale_mrp/data.yml 2012-02-06 14:54:54 +0000 |
176 | +++ openerpcommand/addons/bench_sale_mrp/data.yml 2012-11-27 15:42:19 +0000 |
177 | @@ -18,7 +18,7 @@ |
178 | seller_delay: '1' |
179 | seller_ids: |
180 | - delay: 1 |
181 | - name: base.res_partner_agrolait |
182 | + name: base.res_partner_2 |
183 | min_qty: 2.0 |
184 | qty: 5.0 |
185 | standard_price: 189.0 |
186 | |
187 | === added directory 'openerpcommand/benchmark' |
188 | === added file 'openerpcommand/benchmark/__init__.py' |
189 | --- openerpcommand/benchmark/__init__.py 1970-01-01 00:00:00 +0000 |
190 | +++ openerpcommand/benchmark/__init__.py 2012-11-27 15:42:19 +0000 |
191 | @@ -0,0 +1,111 @@ |
192 | +""" |
193 | +Base class for benchmark |
194 | +""" |
195 | +import sys |
196 | +import hashlib |
197 | +import multiprocessing |
198 | +import time |
199 | + |
200 | +from openerpcommand.common import Client |
201 | + |
202 | +class Benchmark(Client): |
203 | + """ |
204 | + Base class for concurrent benchmarks. The measure_once() method must be |
205 | + overriden. |
206 | + |
207 | + Each sub-benchmark will be run in its own process then a report is done |
208 | + with all the results (shared with the main process using a |
209 | + `multiprocessing.Array`). |
210 | + """ |
211 | + |
212 | + def __init__(self, parser): |
213 | + super(Benchmark, self).__init__(parser) |
214 | + self.parser.add_argument('-n', '--samples', metavar='SAMPLES', |
215 | + default=100, help='number of measurements to take.') |
216 | + # TODO if -n <samples>s is given (instead of -n <samples>), run the |
217 | + # benchmark for <samples> seconds and return the number of iterations. |
218 | + self.parser.add_argument('-o', '--output', metavar='FILE', |
219 | + required=True, help='path to save the generated report.') |
220 | + self.parser.add_argument('--append', action='store_true', |
221 | + default=False, help='append the report to an existing file.') |
222 | + self.parser.add_argument('-j', '--jobs', metavar='JOBS', |
223 | + default=1, help='number of concurrent workers') |
224 | + self.parser.add_argument('--seed', metavar='SEED', |
225 | + default=0, help='a value to ensure different runs can create unique data.') |
226 | + self.worker = -1 |
227 | + |
228 | + def work(self, iarr=None): |
229 | + if iarr: |
230 | + # If an array is given, it means we are a worker process... |
231 | + self.work_slave(iarr) |
232 | + else: |
233 | + # ... else we are the main process and we will spawn workers, |
234 | + # passing them an array. |
235 | + self.work_master() |
236 | + |
237 | + def work_master(self): |
238 | + N = int(self.args.samples) |
239 | + self.arrs = [(i, multiprocessing.Array('f', range(N))) |
240 | + for i in xrange(int(self.args.jobs))] |
241 | + ps = [multiprocessing.Process(target=self.run, args=(arr,)) |
242 | + for arr in self.arrs] |
243 | + [p.start() for p in ps] |
244 | + [p.join() for p in ps] |
245 | + |
246 | + self.report_html() |
247 | + |
248 | + def work_slave(self, iarr): |
249 | + j, arr = iarr |
250 | + self.worker = j |
251 | + N = int(self.args.samples) |
252 | + total_t0 = time.time() |
253 | + for i in xrange(N): |
254 | + t0 = time.time() |
255 | + self.measure_once(i) |
256 | + t1 = time.time() |
257 | + arr[i] = t1 - t0 |
258 | + print >> sys.stdout, '\r%s' % ('|' * (i * 60 / N)), |
259 | + print >> sys.stdout, '%s %s%%' % \ |
260 | + (' ' * (60 - (i * 60 / N)), int(float(i+1)/N*100)), |
261 | + sys.stdout.flush() |
262 | + total_t1 = time.time() |
263 | + print '\nDone in %ss.' % (total_t1 - total_t0) |
264 | + |
265 | + def report_html(self): |
266 | + series = [] |
267 | + for arr in self.arrs: |
268 | + serie = """{ |
269 | + data: %s, |
270 | + points: { show: true } |
271 | + }""" % ([[x, i] for i, x in enumerate(arr)],) |
272 | + series.append(serie) |
273 | + chart_id = hashlib.md5(" ".join(sys.argv)).hexdigest() |
274 | + HEADER = """<!doctype html> |
275 | +<title>Benchmarks</title> |
276 | +<meta charset=utf-8> |
277 | +<script type="text/javascript" src="js/jquery.min.js"></script> |
278 | +<script type="text/javascript" src="js/jquery.flot.js"></script> |
279 | +""" |
280 | + |
281 | + CONTENT = """<h1>%s</h1> |
282 | +%s |
283 | +<div id='chart_%s' style='width:400px;height:300px;'>...</div> |
284 | +<script type="text/javascript"> |
285 | +$.plot($("#chart_%s"), [%s], |
286 | + {yaxis: { ticks: false }}); |
287 | +</script>""" % (self.bench_name, ' '.join(sys.argv), chart_id, chart_id, |
288 | + ','.join(series)) |
289 | + if self.args.append: |
290 | + with open(self.args.output, 'a') as f: |
291 | + f.write(CONTENT,) |
292 | + else: |
293 | + with open(self.args.output, 'w') as f: |
294 | + f.write(HEADER + CONTENT,) |
295 | + |
296 | + def measure_once(self, i): |
297 | + """ |
298 | + The `measure_once` method is called --jobs times. A `i` argument is |
299 | + supplied to allow to create unique values for each execution (e.g. to |
300 | + supply fresh identifiers to a `create` method. |
301 | + """ |
302 | + pass |
303 | |
304 | === added file 'openerpcommand/benchmark/dummy.py' |
305 | --- openerpcommand/benchmark/dummy.py 1970-01-01 00:00:00 +0000 |
306 | +++ openerpcommand/benchmark/dummy.py 2012-11-27 15:42:19 +0000 |
307 | @@ -0,0 +1,18 @@ |
308 | +""" |
309 | +Dummy (call test.limits.model.consume_nothing()). |
310 | +""" |
311 | +from openerpcommand.benchmark import Benchmark |
312 | + |
313 | +class Dummy(Benchmark): |
314 | + bench_name = 'test.limits.model.consume_nothing()' |
315 | + |
316 | + def __init__(self, parser): |
317 | + super(Dummy, self).__init__(parser) |
318 | + self.parser.add_argument('-a', '--args', metavar='ARGS', |
319 | + default='', help='some arguments to serialize') |
320 | + |
321 | + def measure_once(self, i): |
322 | + self.execute('test.limits.model', 'consume_nothing') |
323 | + |
324 | +def init_parser(parser): |
325 | + Dummy(parser) |
326 | |
327 | === added file 'openerpcommand/benchmark/fields_view_get.py' |
328 | --- openerpcommand/benchmark/fields_view_get.py 1970-01-01 00:00:00 +0000 |
329 | +++ openerpcommand/benchmark/fields_view_get.py 2012-11-27 15:42:19 +0000 |
330 | @@ -0,0 +1,20 @@ |
331 | +""" |
332 | +Read a record's fields and view architecture repeatedly. |
333 | +""" |
334 | +from openerpcommand.benchmark import Benchmark |
335 | + |
336 | +class FieldsViewGet(Benchmark): |
337 | + bench_name = 'res.users.fields_view_get(1)' |
338 | + |
339 | + def __init__(self, parser): |
340 | + super(FieldsViewGet, self).__init__(parser) |
341 | + self.parser.add_argument('-m', '--model', metavar='MODEL', |
342 | + required=True, help='the model') |
343 | + self.parser.add_argument('-i', '--id', metavar='RECORDID', |
344 | + required=True, help='the record id') |
345 | + |
346 | + def measure_once(self, i): |
347 | + self.execute(self.args.model, 'fields_view_get', self.args.id) |
348 | + |
349 | +def init_parser(parser): |
350 | + FieldsViewGet(parser) |
351 | |
352 | === added file 'openerpcommand/benchmark/login.py' |
353 | --- openerpcommand/benchmark/login.py 1970-01-01 00:00:00 +0000 |
354 | +++ openerpcommand/benchmark/login.py 2012-11-27 15:42:19 +0000 |
355 | @@ -0,0 +1,13 @@ |
356 | +""" |
357 | +Login (update res_users.date). |
358 | +""" |
359 | +from openerpcommand.benchmark import Benchmark |
360 | + |
361 | +class Login(Benchmark): |
362 | + bench_name = 'res.users.login(1)' |
363 | + |
364 | + def measure_once(self, i): |
365 | + self.common_proxy.login(self.database, self.user, self.password) |
366 | + |
367 | +def init_parser(parser): |
368 | + Login(parser) |
369 | |
370 | === added file 'openerpcommand/benchmark/read.py' |
371 | --- openerpcommand/benchmark/read.py 1970-01-01 00:00:00 +0000 |
372 | +++ openerpcommand/benchmark/read.py 2012-11-27 15:42:19 +0000 |
373 | @@ -0,0 +1,20 @@ |
374 | +""" |
375 | +Define a base class for client-side benchmarking. |
376 | +""" |
377 | +from openerpcommand.benchmark import Benchmark |
378 | + |
379 | +class Read(Benchmark): |
380 | + bench_name = 'res.users.read(1)' |
381 | + |
382 | + def __init__(self, parser): |
383 | + super(Read, self).__init__(parser) |
384 | + self.parser.add_argument('-m', '--model', metavar='MODEL', |
385 | + required=True, help='the model') |
386 | + self.parser.add_argument('-i', '--id', metavar='RECORDID', |
387 | + required=True, help='the record id') |
388 | + |
389 | + def measure_once(self, i): |
390 | + self.execute(self.args.model, 'read', [self.args.id], []) |
391 | + |
392 | +def init_parser(parser): |
393 | + Read(parser) |
394 | |
395 | === renamed file 'openerpcommand/bench_sale_mrp.py' => 'openerpcommand/benchmark/sale_mrp.py' |
396 | --- openerpcommand/bench_sale_mrp.py 2012-02-06 14:54:54 +0000 |
397 | +++ openerpcommand/benchmark/sale_mrp.py 2012-11-27 15:42:19 +0000 |
398 | @@ -1,23 +1,17 @@ |
399 | -""" |
400 | -Benchmark based on the `sale_mrp` addons (in `sale_mrp/test/sale_mrp.yml`). |
401 | -""" |
402 | +"""\ |
403 | +Similar to `sale_mrp/test/sale_mrp.yml`. |
404 | |
405 | +This benchmarks the OpenERP server `sale_mrp` module by creating and |
406 | +confirming a sale order. As it creates data in the server, it is necessary |
407 | +to ensure unique names for the newly created data. You can use the --seed |
408 | +argument to give a lower bound to those names. (The number of generated |
409 | +names is JOBS * SAMPLES.) |
410 | +""" |
411 | import time |
412 | |
413 | -from .benchmarks import Bench |
414 | - |
415 | -class BenchSaleMrp(Bench): |
416 | - """\ |
417 | - Similar to `sale_mrp/test/sale_mrp.yml`. |
418 | - |
419 | - This benchmarks the OpenERP server `sale_mrp` module by creating and |
420 | - confirming a sale order. As it creates data in the server, it is necessary |
421 | - to ensure unique names for the newly created data. You can use the --seed |
422 | - argument to give a lower bound to those names. (The number of generated |
423 | - names is --jobs * --samples.) |
424 | - """ |
425 | - |
426 | - command_name = 'bench-sale-mrp' |
427 | +from openerpcommand.benchmark import Benchmark |
428 | + |
429 | +class SaleMrp(Benchmark): |
430 | bench_name = '`sale_mrp/test/sale_mrp.yml`' |
431 | |
432 | def measure_once(self, i): |
433 | @@ -33,7 +27,7 @@ |
434 | res_partner_4 = self.execute('ir.model.data', 'get_object_reference', 'base', 'res_partner_4')[1] |
435 | res_partner_address_7 = self.execute('ir.model.data', 'get_object_reference', 'base', 'res_partner_address_7')[1] |
436 | list0 = self.execute('ir.model.data', 'get_object_reference', 'product', 'list0')[1] |
437 | - shop = self.execute('ir.model.data', 'get_object_reference', 'sale', 'shop')[1] |
438 | + shop = self.execute('ir.model.data', 'get_object_reference', 'sale', 'sale_shop_1')[1] |
439 | |
440 | # Create a sale order for the product `Slider Mobile`. |
441 | data = { |
442 | @@ -66,3 +60,5 @@ |
443 | # Confirm the sale order. |
444 | self.object_proxy.exec_workflow(self.database, self.uid, self.password, 'sale.order', 'order_confirm', sale_order_id, {}) |
445 | |
446 | +def init_parser(parser): |
447 | + SaleMrp(parser) |
448 | |
449 | === removed file 'openerpcommand/benchmarks.py' |
450 | --- openerpcommand/benchmarks.py 2012-07-23 09:51:09 +0000 |
451 | +++ openerpcommand/benchmarks.py 1970-01-01 00:00:00 +0000 |
452 | @@ -1,166 +0,0 @@ |
453 | -""" |
454 | -Define a base class for client-side benchmarking. |
455 | -""" |
456 | -import hashlib |
457 | -import multiprocessing |
458 | -import sys |
459 | -import time |
460 | - |
461 | -from .client import Client |
462 | - |
463 | -class Bench(Client): |
464 | - """ |
465 | - Base class for concurrent benchmarks. The measure_once() method must be |
466 | - overriden. |
467 | - |
468 | - Each sub-benchmark will be run in its own process then a report is done |
469 | - with all the results (shared with the main process using a |
470 | - `multiprocessing.Array`). |
471 | - """ |
472 | - |
473 | - def __init__(self, subparsers=None): |
474 | - super(Bench, self).__init__(subparsers) |
475 | - self.parser.add_argument('-n', '--samples', metavar='INT', |
476 | - default=100, help='number of measurements to take') |
477 | - # TODO if -n <int>s is given (instead of -n <int>), run the |
478 | - # benchmark for <int> seconds and return the number of iterations. |
479 | - self.parser.add_argument('-o', '--output', metavar='PATH', |
480 | - required=True, help='path to save the generated report') |
481 | - self.parser.add_argument('--append', action='store_true', |
482 | - default=False, help='append the report to an existing file') |
483 | - self.parser.add_argument('-j', '--jobs', metavar='JOBS', |
484 | - default=1, help='number of concurrent workers') |
485 | - self.parser.add_argument('--seed', metavar='SEED', |
486 | - default=0, help='a value to ensure different runs can create unique data') |
487 | - self.worker = -1 |
488 | - |
489 | - def work(self, iarr=None): |
490 | - if iarr: |
491 | - # If an array is given, it means we are a worker process... |
492 | - self.work_slave(iarr) |
493 | - else: |
494 | - # ... else we are the main process and we will spawn workers, |
495 | - # passing them an array. |
496 | - self.work_master() |
497 | - |
498 | - def work_master(self): |
499 | - N = int(self.args.samples) |
500 | - self.arrs = [(i, multiprocessing.Array('f', range(N))) |
501 | - for i in xrange(int(self.args.jobs))] |
502 | - ps = [multiprocessing.Process(target=self.run, args=(arr,)) |
503 | - for arr in self.arrs] |
504 | - [p.start() for p in ps] |
505 | - [p.join() for p in ps] |
506 | - |
507 | - self.report_html() |
508 | - |
509 | - def work_slave(self, iarr): |
510 | - j, arr = iarr |
511 | - self.worker = j |
512 | - N = int(self.args.samples) |
513 | - total_t0 = time.time() |
514 | - for i in xrange(N): |
515 | - t0 = time.time() |
516 | - self.measure_once(i) |
517 | - t1 = time.time() |
518 | - arr[i] = t1 - t0 |
519 | - print >> sys.stdout, '\r%s' % ('|' * (i * 60 / N)), |
520 | - print >> sys.stdout, '%s %s%%' % \ |
521 | - (' ' * (60 - (i * 60 / N)), int(float(i+1)/N*100)), |
522 | - sys.stdout.flush() |
523 | - total_t1 = time.time() |
524 | - print '\nDone in %ss.' % (total_t1 - total_t0) |
525 | - |
526 | - def report_html(self): |
527 | - series = [] |
528 | - for arr in self.arrs: |
529 | - serie = """{ |
530 | - data: %s, |
531 | - points: { show: true } |
532 | - }""" % ([[x, i] for i, x in enumerate(arr)],) |
533 | - series.append(serie) |
534 | - chart_id = hashlib.md5(" ".join(sys.argv)).hexdigest() |
535 | - HEADER = """<!doctype html> |
536 | -<title>Benchmarks</title> |
537 | -<meta charset=utf-8> |
538 | -<script type="text/javascript" src="js/jquery.min.js"></script> |
539 | -<script type="text/javascript" src="js/jquery.flot.js"></script> |
540 | -""" |
541 | - |
542 | - CONTENT = """<h1>%s</h1> |
543 | -%s |
544 | -<div id='chart_%s' style='width:400px;height:300px;'>...</div> |
545 | -<script type="text/javascript"> |
546 | -$.plot($("#chart_%s"), [%s], |
547 | - {yaxis: { ticks: false }}); |
548 | -</script>""" % (self.bench_name, ' '.join(sys.argv), chart_id, chart_id, |
549 | - ','.join(series)) |
550 | - if self.args.append: |
551 | - with open(self.args.output, 'a') as f: |
552 | - f.write(CONTENT,) |
553 | - else: |
554 | - with open(self.args.output, 'w') as f: |
555 | - f.write(HEADER + CONTENT,) |
556 | - |
557 | - def measure_once(self, i): |
558 | - """ |
559 | - The `measure_once` method is called --jobs times. A `i` argument is |
560 | - supplied to allow to create unique values for each execution (e.g. to |
561 | - supply fresh identifiers to a `create` method. |
562 | - """ |
563 | - pass |
564 | - |
565 | -class BenchRead(Bench): |
566 | - """Read a record repeatedly.""" |
567 | - |
568 | - command_name = 'bench-read' |
569 | - bench_name = 'res.users.read(1)' |
570 | - |
571 | - def __init__(self, subparsers=None): |
572 | - super(BenchRead, self).__init__(subparsers) |
573 | - self.parser.add_argument('-m', '--model', metavar='MODEL', |
574 | - required=True, help='the model') |
575 | - self.parser.add_argument('-i', '--id', metavar='RECORDID', |
576 | - required=True, help='the record id') |
577 | - |
578 | - def measure_once(self, i): |
579 | - self.execute(self.args.model, 'read', [self.args.id], []) |
580 | - |
581 | -class BenchFieldsViewGet(Bench): |
582 | - """Read a record's fields and view architecture repeatedly.""" |
583 | - |
584 | - command_name = 'bench-view' |
585 | - bench_name = 'res.users.fields_view_get(1)' |
586 | - |
587 | - def __init__(self, subparsers=None): |
588 | - super(BenchFieldsViewGet, self).__init__(subparsers) |
589 | - self.parser.add_argument('-m', '--model', metavar='MODEL', |
590 | - required=True, help='the model') |
591 | - self.parser.add_argument('-i', '--id', metavar='RECORDID', |
592 | - required=True, help='the record id') |
593 | - |
594 | - def measure_once(self, i): |
595 | - self.execute(self.args.model, 'fields_view_get', self.args.id) |
596 | - |
597 | -class BenchDummy(Bench): |
598 | - """Dummy (call test.limits.model.consume_nothing()).""" |
599 | - |
600 | - command_name = 'bench-dummy' |
601 | - bench_name = 'test.limits.model.consume_nothing()' |
602 | - |
603 | - def __init__(self, subparsers=None): |
604 | - super(BenchDummy, self).__init__(subparsers) |
605 | - self.parser.add_argument('-a', '--args', metavar='ARGS', |
606 | - default='', help='some arguments to serialize') |
607 | - |
608 | - def measure_once(self, i): |
609 | - self.execute('test.limits.model', 'consume_nothing') |
610 | - |
611 | -class BenchLogin(Bench): |
612 | - """Login (update res_users.date).""" |
613 | - |
614 | - command_name = 'bench-login' |
615 | - bench_name = 'res.users.login(1)' |
616 | - |
617 | - def measure_once(self, i): |
618 | - self.common_proxy.login(self.database, self.user, self.password) |
619 | |
620 | === modified file 'openerpcommand/call.py' |
621 | --- openerpcommand/call.py 2012-02-10 16:19:48 +0000 |
622 | +++ openerpcommand/call.py 2012-11-27 15:42:19 +0000 |
623 | @@ -1,29 +1,18 @@ |
624 | """ |
625 | Call an arbitrary model's method. |
626 | + |
627 | +Example: |
628 | + > oe call res.users.read '[1, 3]' '[]' -u 1 -p admin |
629 | """ |
630 | import ast |
631 | -import os |
632 | import pprint |
633 | import sys |
634 | -import time |
635 | -import xmlrpclib |
636 | - |
637 | -import client |
638 | - |
639 | -class Call(client.Client): |
640 | - """\ |
641 | - Call an arbitrary model's method. |
642 | - |
643 | - Example: |
644 | - > oe call res.users.read '[1, 3]' '[]' -u 1 -p admin |
645 | - """ |
646 | - # TODO The above docstring is completely borked in the |
647 | - # --help message. |
648 | - |
649 | - command_name = 'call' |
650 | - |
651 | - def __init__(self, subparsers=None): |
652 | - super(Call, self).__init__(subparsers) |
653 | + |
654 | +from openerpcommand.common import Client |
655 | + |
656 | +class Call(Client): |
657 | + def __init__(self, parser): |
658 | + super(Call, self).__init__(parser) |
659 | self.parser.add_argument('call', metavar='MODEL.METHOD', |
660 | help='the model and the method to call, using the ' |
661 | '<model>.<method> format.') |
662 | @@ -42,3 +31,5 @@ |
663 | x = self.execute(model, method, *args) |
664 | pprint.pprint(x, indent=4) |
665 | |
666 | +def init_parser(parser): |
667 | + Call(parser) |
668 | |
669 | === removed file 'openerpcommand/client.py' |
670 | --- openerpcommand/client.py 2012-01-20 15:41:17 +0000 |
671 | +++ openerpcommand/client.py 1970-01-01 00:00:00 +0000 |
672 | @@ -1,137 +0,0 @@ |
673 | -""" |
674 | -Define a few common arguments for client-side command-line tools. |
675 | -""" |
676 | -import os |
677 | -import sys |
678 | -import time |
679 | -import xmlrpclib |
680 | - |
681 | -import common |
682 | - |
683 | -class Client(common.Command): |
684 | - """ |
685 | - Base class for XML-RPC command-line clients. It must be inherited and the |
686 | - work() method overriden. |
687 | - """ |
688 | - |
689 | - def __init__(self, subparsers=None): |
690 | - super(Client, self).__init__(subparsers) |
691 | - required_or_default = common.required_or_default |
692 | - self.parser.add_argument('-H', '--host', metavar='HOST', |
693 | - **required_or_default('HOST', 'the server host')) |
694 | - self.parser.add_argument('-P', '--port', metavar='PORT', |
695 | - **required_or_default('PORT', 'the server port')) |
696 | - |
697 | - def execute(self, *args): |
698 | - return self.object_proxy.execute(self.database, self.uid, self.password, *args) |
699 | - |
700 | - def initialize(self): |
701 | - self.host = self.args.host |
702 | - self.port = int(self.args.port) |
703 | - self.database = self.args.database |
704 | - self.user = self.args.user |
705 | - self.password = self.args.password |
706 | - |
707 | - self.url = 'http://%s:%d/xmlrpc/' % (self.host, self.port) |
708 | - self.common_proxy = xmlrpclib.ServerProxy(self.url + 'common') |
709 | - self.object_proxy = xmlrpclib.ServerProxy(self.url + 'object') |
710 | - |
711 | - try: |
712 | - self.uid = int(self.user) |
713 | - except ValueError, e: |
714 | - self.uid = self.common_proxy.login(self.database, self.user, self.password) |
715 | - |
716 | - def run(self, *args): |
717 | - self.initialize() |
718 | - self.work(*args) |
719 | - |
720 | - def work(self, *args): |
721 | - pass |
722 | - |
723 | -class Open(Client): |
724 | - """Get the web client's URL to view a specific model.""" |
725 | - |
726 | - command_name = 'open' |
727 | - |
728 | - def __init__(self, subparsers=None): |
729 | - super(Open, self).__init__(subparsers) |
730 | - self.parser.add_argument('-m', '--model', metavar='MODEL', |
731 | - required=True, help='the view type') |
732 | - self.parser.add_argument('-v', '--view-mode', metavar='VIEWMODE', |
733 | - default='tree', help='the view mode') |
734 | - |
735 | - def work(self): |
736 | - ids = self.execute('ir.actions.act_window', 'search', [ |
737 | - ('res_model', '=', self.args.model), |
738 | - ('view_mode', 'like', self.args.view_mode), |
739 | - ]) |
740 | - xs = self.execute('ir.actions.act_window', 'read', ids, []) |
741 | - for x in xs: |
742 | - print x['id'], x['name'] |
743 | - d = {} |
744 | - d['host'] = self.host |
745 | - d['port'] = self.port |
746 | - d['action_id'] = x['id'] |
747 | - print " http://%(host)s:%(port)s/web/webclient/home#action_id=%(action_id)s" % d |
748 | - |
749 | -class Show(Client): |
750 | - """Display a record.""" |
751 | - |
752 | - command_name = 'show' |
753 | - |
754 | - def __init__(self, subparsers=None): |
755 | - super(Show, self).__init__(subparsers) |
756 | - self.parser.add_argument('-m', '--model', metavar='MODEL', |
757 | - required=True, help='the model') |
758 | - self.parser.add_argument('-i', '--id', metavar='RECORDID', |
759 | - required=True, help='the record id') |
760 | - |
761 | - def work(self): |
762 | - xs = self.execute(self.args.model, 'read', [self.args.id], []) |
763 | - if xs: |
764 | - x = xs[0] |
765 | - print x['name'] |
766 | - else: |
767 | - print "Record not found." |
768 | - |
769 | -class ConsumeNothing(Client): |
770 | - """Call test.limits.model.consume_nothing().""" |
771 | - |
772 | - command_name = 'consume-nothing' |
773 | - |
774 | - def work(self): |
775 | - xs = self.execute('test.limits.model', 'consume_nothing') |
776 | - |
777 | -class ConsumeMemory(Client): |
778 | - """Call test.limits.model.consume_memory().""" |
779 | - |
780 | - command_name = 'consume-memory' |
781 | - |
782 | - def __init__(self, subparsers=None): |
783 | - super(ConsumeMemory, self).__init__(subparsers) |
784 | - self.parser.add_argument('--size', metavar='SIZE', |
785 | - required=True, help='size of the list to allocate') |
786 | - |
787 | - def work(self): |
788 | - xs = self.execute('test.limits.model', 'consume_memory', int(self.args.size)) |
789 | - |
790 | -class LeakMemory(ConsumeMemory): |
791 | - """Call test.limits.model.leak_memory().""" |
792 | - |
793 | - command_name = 'leak-memory' |
794 | - |
795 | - def work(self): |
796 | - xs = self.execute('test.limits.model', 'leak_memory', int(self.args.size)) |
797 | - |
798 | -class ConsumeCPU(Client): |
799 | - """Call test.limits.model.consume_cpu_time().""" |
800 | - |
801 | - command_name = 'consume-cpu' |
802 | - |
803 | - def __init__(self, subparsers=None): |
804 | - super(ConsumeCPU, self).__init__(subparsers) |
805 | - self.parser.add_argument('--seconds', metavar='INT', |
806 | - required=True, help='how much CPU time to consume') |
807 | - |
808 | - def work(self): |
809 | - xs = self.execute('test.limits.model', 'consume_cpu_time', int(self.args.seconds)) |
810 | |
811 | === modified file 'openerpcommand/common.py' |
812 | --- openerpcommand/common.py 2012-10-30 15:11:48 +0000 |
813 | +++ openerpcommand/common.py 2012-11-27 15:42:19 +0000 |
814 | @@ -1,9 +1,9 @@ |
815 | """ |
816 | -Define a few common arguments for server-side command-line tools. |
817 | +Define a few common arguments for server-side (Command) and client-side (Client) command-line tools. |
818 | """ |
819 | -import argparse |
820 | import os |
821 | import sys |
822 | +import xmlrpclib |
823 | |
824 | def add_addons_argument(parser): |
825 | """ |
826 | @@ -37,28 +37,24 @@ |
827 | a mandatory argument. |
828 | """ |
829 | if os.environ.get('OPENERP_' + name.upper()): |
830 | - d = {'default': os.environ['OPENERP_' + name.upper()]} |
831 | + d = {'default': os.environ['OPENERP_' + name.upper()]} |
832 | else: |
833 | - d = {'required': True} |
834 | + d = {'required': True} |
835 | d['help'] = h + '. The environment variable OPENERP_' + \ |
836 | - name.upper() + ' can be used instead.' |
837 | + name.upper() + ' can be used instead.' |
838 | return d |
839 | |
840 | +def expand_path(path): |
841 | + return os.path.expanduser(os.path.expandvars(path)) |
842 | + |
843 | class Command(object): |
844 | """ |
845 | Base class to create command-line tools. It must be inherited and the |
846 | run() method overriden. |
847 | """ |
848 | |
849 | - command_name = 'stand-alone' |
850 | - |
851 | - def __init__(self, subparsers=None): |
852 | - if subparsers: |
853 | - self.parser = parser = subparsers.add_parser(self.command_name, |
854 | - description=self.__class__.__doc__) |
855 | - else: |
856 | - self.parser = parser = argparse.ArgumentParser( |
857 | - description=self.__class__.__doc__) |
858 | + def __init__(self, parser): |
859 | + self.parser = parser |
860 | |
861 | parser.add_argument('-d', '--database', metavar='DATABASE', |
862 | **required_or_default('DATABASE', 'the database to connect to')) |
863 | @@ -86,3 +82,42 @@ |
864 | command = cls() |
865 | args = command.parser.parse_args() |
866 | args.run(args) |
867 | + |
868 | +class Client(Command): |
869 | + """ |
870 | + Base class for XML-RPC command-line clients. It must be inherited and the |
871 | + work() method overriden. |
872 | + """ |
873 | + |
874 | + def __init__(self, parser): |
875 | + super(Client, self).__init__(parser) |
876 | + self.parser.add_argument('-H', '--host', metavar='HOST', |
877 | + **required_or_default('HOST', 'the server host')) |
878 | + self.parser.add_argument('-P', '--port', metavar='PORT', |
879 | + **required_or_default('PORT', 'the server port')) |
880 | + |
881 | + def execute(self, *args): |
882 | + return self.object_proxy.execute(self.database, self.uid, self.password, *args) |
883 | + |
884 | + def initialize(self): |
885 | + self.host = self.args.host |
886 | + self.port = int(self.args.port) |
887 | + self.database = self.args.database |
888 | + self.user = self.args.user |
889 | + self.password = self.args.password |
890 | + |
891 | + self.url = 'http://%s:%d/xmlrpc/' % (self.host, self.port) |
892 | + self.common_proxy = xmlrpclib.ServerProxy(self.url + 'common') |
893 | + self.object_proxy = xmlrpclib.ServerProxy(self.url + 'object') |
894 | + |
895 | + try: |
896 | + self.uid = int(self.user) |
897 | + except ValueError, e: |
898 | + self.uid = self.common_proxy.login(self.database, self.user, self.password) |
899 | + |
900 | + def run(self, *args): |
901 | + self.initialize() |
902 | + self.work(*args) |
903 | + |
904 | + def work(self, *args): |
905 | + pass |
906 | |
907 | === modified file 'openerpcommand/conf.py' |
908 | --- openerpcommand/conf.py 2012-10-31 11:26:36 +0000 |
909 | +++ openerpcommand/conf.py 2012-11-27 15:42:19 +0000 |
910 | @@ -1,13 +1,11 @@ |
911 | """ |
912 | Display the currently used configuration. The configuration for any |
913 | -sub-command is normally given by options. But some options can be specified |
914 | -using environment variables. This sub-command shows those variables. |
915 | -A `set` sub-command should be provided when the configuration is in a real |
916 | -configuration file instead of environment variables. |
917 | +sub-command is normally given by options. But some options can be |
918 | +specified using environment variables. This sub-command shows those |
919 | +variables. A `set` sub-command should be provided when the configuration |
920 | +is in a real configuration file instead of environment variables. |
921 | """ |
922 | import os |
923 | -import sys |
924 | -import textwrap |
925 | |
926 | def run(args): |
927 | for x in ('database', 'addons', 'host', 'port'): |
928 | @@ -18,8 +16,5 @@ |
929 | print '%s: <not set>' % (x, ) |
930 | os.environ['OPENERP_DATABASE'] = 'yeah' |
931 | |
932 | -def add_parser(subparsers): |
933 | - parser = subparsers.add_parser('conf', |
934 | - description='Display the currently used configuration.') |
935 | - |
936 | +def init_parser(parser): |
937 | parser.set_defaults(run=run) |
938 | |
939 | === added file 'openerpcommand/consume_cpu.py' |
940 | --- openerpcommand/consume_cpu.py 1970-01-01 00:00:00 +0000 |
941 | +++ openerpcommand/consume_cpu.py 2012-11-27 15:42:19 +0000 |
942 | @@ -0,0 +1,16 @@ |
943 | +""" |
944 | +Call test.limits.model.consume_cpu_time(). |
945 | +""" |
946 | +from openerpcommand.common import Client |
947 | + |
948 | +class ConsumeCPU(Client): |
949 | + def __init__(self, parser): |
950 | + super(ConsumeCPU, self).__init__(parser) |
951 | + self.parser.add_argument('--seconds', metavar='INT', |
952 | + required=True, help='how much CPU time to consume') |
953 | + |
954 | + def work(self): |
955 | + xs = self.execute('test.limits.model', 'consume_cpu_time', int(self.args.seconds)) |
956 | + |
957 | +def init_parser(parser): |
958 | + ConsumeCPU(parser) |
959 | |
960 | === added file 'openerpcommand/consume_memory.py' |
961 | --- openerpcommand/consume_memory.py 1970-01-01 00:00:00 +0000 |
962 | +++ openerpcommand/consume_memory.py 2012-11-27 15:42:19 +0000 |
963 | @@ -0,0 +1,16 @@ |
964 | +""" |
965 | +Call test.limits.model.consume_memory(). |
966 | +""" |
967 | +from openerpcommand.common import Client |
968 | + |
969 | +class ConsumeMemory(Client): |
970 | + def __init__(self, parser): |
971 | + super(ConsumeMemory, self).__init__(parser) |
972 | + self.parser.add_argument('--size', metavar='SIZE', |
973 | + required=True, help='size of the list to allocate') |
974 | + |
975 | + def work(self): |
976 | + xs = self.execute('test.limits.model', 'consume_memory', int(self.args.size)) |
977 | + |
978 | +def init_parser(parser): |
979 | + ConsumeMemory(parser) |
980 | |
981 | === added file 'openerpcommand/consume_nothing.py' |
982 | --- openerpcommand/consume_nothing.py 1970-01-01 00:00:00 +0000 |
983 | +++ openerpcommand/consume_nothing.py 2012-11-27 15:42:19 +0000 |
984 | @@ -0,0 +1,11 @@ |
985 | +""" |
986 | +Call test.limits.model.consume_nothing(). |
987 | +""" |
988 | +from openerpcommand.common import Client |
989 | + |
990 | +class ConsumeNothing(Client): |
991 | + def work(self): |
992 | + xs = self.execute('test.limits.model', 'consume_nothing') |
993 | + |
994 | +def init_parser(parser): |
995 | + ConsumeNothing(parser) |
996 | |
997 | === modified file 'openerpcommand/drop.py' |
998 | --- openerpcommand/drop.py 2012-08-23 09:25:05 +0000 |
999 | +++ openerpcommand/drop.py 2012-11-27 15:42:19 +0000 |
1000 | @@ -1,8 +1,7 @@ |
1001 | """ |
1002 | Drop a database. |
1003 | """ |
1004 | - |
1005 | -import common |
1006 | +from openerpcommand import common |
1007 | |
1008 | # TODO turn template1 in a parameter |
1009 | # This should be exposed from openerp (currently in |
1010 | @@ -36,10 +35,7 @@ |
1011 | assert args.database |
1012 | drop_database(args.database) |
1013 | |
1014 | -def add_parser(subparsers): |
1015 | - parser = subparsers.add_parser('drop', |
1016 | - description='Drop a database.') |
1017 | +def init_parser(parser): |
1018 | parser.add_argument('-d', '--database', metavar='DATABASE', |
1019 | **common.required_or_default('DATABASE', 'the database to create')) |
1020 | - |
1021 | parser.set_defaults(run=run) |
1022 | |
1023 | === modified file 'openerpcommand/initialize.py' |
1024 | --- openerpcommand/initialize.py 2012-11-05 17:34:39 +0000 |
1025 | +++ openerpcommand/initialize.py 2012-11-27 15:42:19 +0000 |
1026 | @@ -1,10 +1,9 @@ |
1027 | """ |
1028 | Install OpenERP on a new (by default) database. |
1029 | +Create and initialize a new OpenERP database. |
1030 | """ |
1031 | -import os |
1032 | import sys |
1033 | - |
1034 | -import common |
1035 | +from openerpcommand import common |
1036 | |
1037 | def install_openerp(database_name, create_database_flag, module_names): |
1038 | import openerp |
1039 | @@ -47,6 +46,10 @@ |
1040 | |
1041 | config = openerp.tools.config |
1042 | |
1043 | + if args.drop: |
1044 | + import drop |
1045 | + drop.drop_database(args.database) |
1046 | + |
1047 | if args.tests: |
1048 | config['log_handler'] = [':TEST'] |
1049 | config['test_enable'] = True |
1050 | @@ -57,7 +60,7 @@ |
1051 | config['without_demo'] = True |
1052 | |
1053 | if args.addons: |
1054 | - args.addons = args.addons.split(':') |
1055 | + args.addons = map(common.expand_path, args.addons.split(':')) |
1056 | else: |
1057 | args.addons = [] |
1058 | config['addons_path'] = ','.join(args.addons) |
1059 | @@ -77,11 +80,11 @@ |
1060 | if hasattr(registry, '_assertion_report'): |
1061 | sys.exit(1 if registry._assertion_report.failures else 0) |
1062 | |
1063 | -def add_parser(subparsers): |
1064 | - parser = subparsers.add_parser('initialize', |
1065 | - description='Create and initialize a new OpenERP database.') |
1066 | +def init_parser(parser): |
1067 | parser.add_argument('-d', '--database', metavar='DATABASE', |
1068 | **common.required_or_default('DATABASE', 'the database to create')) |
1069 | + parser.add_argument('--drop', action='store_true', |
1070 | + default=False, help="Drop the database if exists") |
1071 | common.add_addons_argument(parser) |
1072 | parser.add_argument('--module', metavar='MODULE', action='append', |
1073 | help='specify a module to install' |
1074 | @@ -98,5 +101,4 @@ |
1075 | ' (use the `run-tests` command to choose specific' |
1076 | ' tests to run against an existing database).' |
1077 | ' Demo data are installed.') |
1078 | - |
1079 | parser.set_defaults(run=run) |
1080 | |
1081 | === added file 'openerpcommand/leak_memory.py' |
1082 | --- openerpcommand/leak_memory.py 1970-01-01 00:00:00 +0000 |
1083 | +++ openerpcommand/leak_memory.py 2012-11-27 15:42:19 +0000 |
1084 | @@ -0,0 +1,11 @@ |
1085 | +""" |
1086 | +Call test.limits.model.leak_memory(). |
1087 | +""" |
1088 | +from .consume_memory import ConsumeMemory |
1089 | + |
1090 | +class LeakMemory(ConsumeMemory): |
1091 | + def work(self): |
1092 | + xs = self.execute('test.limits.model', 'leak_memory', int(self.args.size)) |
1093 | + |
1094 | +def init_parser(parser): |
1095 | + LeakMemory(parser) |
1096 | |
1097 | === modified file 'openerpcommand/model.py' |
1098 | --- openerpcommand/model.py 2012-02-14 15:05:03 +0000 |
1099 | +++ openerpcommand/model.py 2012-11-27 15:42:19 +0000 |
1100 | @@ -1,7 +1,6 @@ |
1101 | """ |
1102 | -Display information about a given model. |
1103 | +Display information about a given model for an existing database. |
1104 | """ |
1105 | -import os |
1106 | import sys |
1107 | import textwrap |
1108 | |
1109 | @@ -46,9 +45,7 @@ |
1110 | if args.verbose and v.help: |
1111 | print textwrap.fill(v.help, initial_indent=' ', subsequent_indent=' ') |
1112 | |
1113 | -def add_parser(subparsers): |
1114 | - parser = subparsers.add_parser('model', |
1115 | - description='Display information about a given model for an existing database.') |
1116 | +def init_parser(parser): |
1117 | parser.add_argument('-d', '--database', metavar='DATABASE', required=True, |
1118 | help='the database to connect to') |
1119 | parser.add_argument('-m', '--model', metavar='MODEL', required=True, |
1120 | |
1121 | === modified file 'openerpcommand/module.py' |
1122 | --- openerpcommand/module.py 2012-08-23 09:25:05 +0000 |
1123 | +++ openerpcommand/module.py 2012-11-27 15:42:19 +0000 |
1124 | @@ -1,11 +1,12 @@ |
1125 | """ |
1126 | Show module information for a given database or from the file-system. |
1127 | +Display modules known from a given database or on file-system. |
1128 | """ |
1129 | import os |
1130 | import sys |
1131 | import textwrap |
1132 | |
1133 | -from . import common |
1134 | +from openerpcommand import common |
1135 | |
1136 | # TODO provide a --rpc flag to use XML-RPC (with a specific username) instead |
1137 | # of server-side library. |
1138 | @@ -16,7 +17,7 @@ |
1139 | config = openerp.tools.config |
1140 | config['log_handler'] = [':CRITICAL'] |
1141 | if args.addons: |
1142 | - args.addons = args.addons.split(':') |
1143 | + args.addons = map(common.expand_path, args.addons.split(':')) |
1144 | else: |
1145 | args.addons = [] |
1146 | config['addons_path'] = ','.join(args.addons) |
1147 | @@ -50,9 +51,7 @@ |
1148 | else: |
1149 | print "No module found (database `%s`)." % (args.database,) |
1150 | |
1151 | -def add_parser(subparsers): |
1152 | - parser = subparsers.add_parser('module', |
1153 | - description='Display modules known from a given database or on file-system.') |
1154 | +def init_parser(parser): |
1155 | parser.add_argument('-d', '--database', metavar='DATABASE', |
1156 | **common.required_or_default('DATABASE', 'the database to modify')) |
1157 | common.add_addons_argument(parser) |
1158 | |
1159 | === added file 'openerpcommand/open.py' |
1160 | --- openerpcommand/open.py 1970-01-01 00:00:00 +0000 |
1161 | +++ openerpcommand/open.py 2012-11-27 15:42:19 +0000 |
1162 | @@ -0,0 +1,29 @@ |
1163 | +""" |
1164 | +Get the web client's URL to view a specific model. |
1165 | +""" |
1166 | +from openerpcommand.common import Client |
1167 | + |
1168 | +class Open(Client): |
1169 | + def __init__(self, parser): |
1170 | + super(Open, self).__init__(parser) |
1171 | + self.parser.add_argument('-m', '--model', metavar='MODEL', |
1172 | + required=True, help='the view type') |
1173 | + self.parser.add_argument('-v', '--view-mode', metavar='VIEWMODE', |
1174 | + default='tree', help='the view mode') |
1175 | + |
1176 | + def work(self): |
1177 | + ids = self.execute('ir.actions.act_window', 'search', [ |
1178 | + ('res_model', '=', self.args.model), |
1179 | + ('view_mode', 'like', self.args.view_mode), |
1180 | + ]) |
1181 | + xs = self.execute('ir.actions.act_window', 'read', ids, []) |
1182 | + for x in xs: |
1183 | + print x['id'], x['name'] |
1184 | + d = {} |
1185 | + d['host'] = self.host |
1186 | + d['port'] = self.port |
1187 | + d['action_id'] = x['id'] |
1188 | + print " http://%(host)s:%(port)s/web/webclient/home#action_id=%(action_id)s" % d |
1189 | + |
1190 | +def init_parser(parser): |
1191 | + Open(parser) |
1192 | |
1193 | === modified file 'openerpcommand/read.py' |
1194 | --- openerpcommand/read.py 2012-11-05 17:34:39 +0000 |
1195 | +++ openerpcommand/read.py 2012-11-27 15:42:19 +0000 |
1196 | @@ -41,9 +41,7 @@ |
1197 | else: |
1198 | print "Record not found." |
1199 | |
1200 | -def add_parser(subparsers): |
1201 | - parser = subparsers.add_parser('read', |
1202 | - description='Display a record.') |
1203 | +def init_parser(parser): |
1204 | parser.add_argument('-d', '--database', metavar='DATABASE', required=True, |
1205 | help='the database to connect to') |
1206 | parser.add_argument('-m', '--model', metavar='MODEL', required=True, |
1207 | @@ -56,5 +54,4 @@ |
1208 | help='display less information') |
1209 | parser.add_argument('-f', '--field', metavar='FIELD', |
1210 | help='display information only for this particular field') |
1211 | - |
1212 | parser.set_defaults(run=run) |
1213 | |
1214 | === modified file 'openerpcommand/run_tests.py' |
1215 | --- openerpcommand/run_tests.py 2012-11-19 13:23:37 +0000 |
1216 | +++ openerpcommand/run_tests.py 2012-11-27 15:42:19 +0000 |
1217 | @@ -1,12 +1,13 @@ |
1218 | """ |
1219 | Execute the unittest2 tests available in OpenERP addons. |
1220 | +Run the OpenERP server and/or addons tests. |
1221 | """ |
1222 | |
1223 | import os |
1224 | import sys |
1225 | import types |
1226 | |
1227 | -import common |
1228 | +from openerpcommand import common |
1229 | |
1230 | def get_test_modules(module, submodule, explode): |
1231 | """ |
1232 | @@ -102,7 +103,7 @@ |
1233 | config['db_name'] = args.database |
1234 | config['addons_path'] = args.addons.replace(':',',') |
1235 | if args.addons: |
1236 | - args.addons = args.addons.split(':') |
1237 | + args.addons = map(common.expand_path, args.addons.split(':')) |
1238 | else: |
1239 | args.addons = [] |
1240 | if args.sanity_checks and args.fast_suite: |
1241 | @@ -169,9 +170,7 @@ |
1242 | for test_module in test_modules: |
1243 | print ' ', test_module.__name__ |
1244 | |
1245 | -def add_parser(subparsers): |
1246 | - parser = subparsers.add_parser('run-tests', |
1247 | - description='Run the OpenERP server and/or addons tests.') |
1248 | +def init_parser(parser): |
1249 | parser.add_argument('-d', '--database', metavar='DATABASE', required=True, |
1250 | help='the database to test. Depending on the test suites, the ' |
1251 | 'database must already exist or not.') |
1252 | @@ -192,5 +191,4 @@ |
1253 | help='run only the sanity check tests') |
1254 | parser.add_argument('--dry-run', action='store_true', |
1255 | help='do not run the tests') |
1256 | - |
1257 | parser.set_defaults(run=run) |
1258 | |
1259 | === modified file 'openerpcommand/scaffold.py' |
1260 | --- openerpcommand/scaffold.py 2012-02-01 22:38:52 +0000 |
1261 | +++ openerpcommand/scaffold.py 2012-11-27 15:42:19 +0000 |
1262 | @@ -1,7 +1,6 @@ |
1263 | """ |
1264 | Generate an OpenERP module skeleton. |
1265 | """ |
1266 | - |
1267 | import os |
1268 | import sys |
1269 | |
1270 | @@ -22,12 +21,9 @@ |
1271 | with open(os.path.join(module, 'models', '__init__.py'), 'w') as h: |
1272 | h.write(MODELS_PY % (module,)) |
1273 | |
1274 | -def add_parser(subparsers): |
1275 | - parser = subparsers.add_parser('scaffold', |
1276 | - description='Generate an OpenERP module skeleton.') |
1277 | +def init_parser(parser): |
1278 | parser.add_argument('module', metavar='MODULE', |
1279 | help='the name of the generated module') |
1280 | - |
1281 | parser.set_defaults(run=run) |
1282 | |
1283 | MANIFEST = """\ |
1284 | |
1285 | === added file 'openerpcommand/show.py' |
1286 | --- openerpcommand/show.py 1970-01-01 00:00:00 +0000 |
1287 | +++ openerpcommand/show.py 2012-11-27 15:42:19 +0000 |
1288 | @@ -0,0 +1,23 @@ |
1289 | +""" |
1290 | +Display a record. |
1291 | +""" |
1292 | +from openerpcommand.common import Client |
1293 | + |
1294 | +class Show(Client): |
1295 | + def __init__(self, parser): |
1296 | + super(Show, self).__init__(parser) |
1297 | + self.parser.add_argument('-m', '--model', metavar='MODEL', |
1298 | + required=True, help='the model') |
1299 | + self.parser.add_argument('-i', '--id', metavar='RECORDID', |
1300 | + required=True, help='the record id') |
1301 | + |
1302 | + def work(self): |
1303 | + xs = self.execute(self.args.model, 'read', [self.args.id], []) |
1304 | + if xs: |
1305 | + x = xs[0] |
1306 | + print x['name'] |
1307 | + else: |
1308 | + print "Record not found." |
1309 | + |
1310 | +def init_parser(parser): |
1311 | + Show(parser) |
1312 | |
1313 | === modified file 'openerpcommand/uninstall.py' |
1314 | --- openerpcommand/uninstall.py 2012-08-22 17:20:15 +0000 |
1315 | +++ openerpcommand/uninstall.py 2012-11-27 15:42:19 +0000 |
1316 | @@ -1,10 +1,10 @@ |
1317 | """ |
1318 | -Install OpenERP on a new (by default) database. |
1319 | +Uninstall some modules from an OpenERP database. |
1320 | """ |
1321 | import os |
1322 | import sys |
1323 | |
1324 | -import common |
1325 | +from openerpcommand import common |
1326 | |
1327 | # TODO turn template1 in a parameter |
1328 | # This should be exposed from openerp (currently in |
1329 | @@ -30,7 +30,7 @@ |
1330 | config = openerp.tools.config |
1331 | config['log_handler'] = [':CRITICAL'] |
1332 | if args.addons: |
1333 | - args.addons = args.addons.split(':') |
1334 | + args.addons = map(common.expand_path, args.addons.split(':')) |
1335 | else: |
1336 | args.addons = [] |
1337 | config['addons_path'] = ','.join(args.addons) |
1338 | @@ -54,14 +54,11 @@ |
1339 | finally: |
1340 | cr.close() |
1341 | |
1342 | -def add_parser(subparsers): |
1343 | - parser = subparsers.add_parser('uninstall', |
1344 | - description='Uninstall some modules from an OpenERP database.') |
1345 | +def init_parser(parser): |
1346 | parser.add_argument('-d', '--database', metavar='DATABASE', |
1347 | **common.required_or_default('DATABASE', 'the database to modify')) |
1348 | common.add_addons_argument(parser) |
1349 | parser.add_argument('--module', metavar='MODULE', action='append', |
1350 | help='specify a module to uninstall' |
1351 | ' (this option can be repeated)') |
1352 | - |
1353 | parser.set_defaults(run=run) |
1354 | |
1355 | === modified file 'openerpcommand/update.py' |
1356 | --- openerpcommand/update.py 2012-02-14 15:05:03 +0000 |
1357 | +++ openerpcommand/update.py 2012-11-27 15:42:19 +0000 |
1358 | @@ -10,9 +10,7 @@ |
1359 | openerp.modules.registry.RegistryManager.get( |
1360 | args.database, update_module=True, pooljobs=False) |
1361 | |
1362 | -def add_parser(subparsers): |
1363 | - parser = subparsers.add_parser('update', |
1364 | - description='Update an existing OpenERP database.') |
1365 | +def init_parser(parser): |
1366 | parser.add_argument('-d', '--database', metavar='DATABASE', required=True, |
1367 | help='the database to update') |
1368 | |
1369 | |
1370 | === modified file 'setup.py' (properties changed: -x to +x) |
1371 | --- setup.py 2012-01-31 17:55:23 +0000 |
1372 | +++ setup.py 2012-11-27 15:42:19 +0000 |
1373 | @@ -31,8 +31,29 @@ |
1374 | zip_safe = False, |
1375 | packages = find_packages(), |
1376 | include_package_data = True, |
1377 | - entry_points=""" |
1378 | - [console_scripts] |
1379 | - oe=openerpcommand.main:run |
1380 | - """, |
1381 | + entry_points={ |
1382 | + 'console_scripts' : ['oe = openerpcommand.main:run'], |
1383 | + 'openerpcommand.subcommands': [ 'call = openerpcommand.call:init_parser', |
1384 | + 'conf = openerpcommand.conf:init_parser', |
1385 | + 'consume_cpu = openerpcommand.consume_cpu:init_parser', |
1386 | + 'consume_memory = openerpcommand.consume_memory:init_parser', |
1387 | + 'consume_nothing = openerpcommand.consume_nothing:init_parser', |
1388 | + 'drop = openerpcommand.drop:init_parser', |
1389 | + 'initialize = openerpcommand.initialize:init_parser', |
1390 | + 'leak_memory = openerpcommand.leak_memory:init_parser', |
1391 | + 'model = openerpcommand.model:init_parser', |
1392 | + 'module = openerpcommand.module:init_parser', |
1393 | + 'open = openerpcommand.open:init_parser', |
1394 | + 'read = openerpcommand.read:init_parser', |
1395 | + 'run_tests = openerpcommand.run_tests:init_parser', |
1396 | + 'scaffold = openerpcommand.scaffold:init_parser', |
1397 | + 'show = openerpcommand.show:init_parser', |
1398 | + 'uninstall = openerpcommand.uninstall:init_parser', |
1399 | + 'update = openerpcommand.update:init_parser'], |
1400 | + 'openerpcommand.subcommands.benchmark': [ 'dummy = openerpcommand.benchmark.dummy:init_parser', |
1401 | + 'fields_view_get = openerpcommand.benchmark.fields_view_get:init_parser', |
1402 | + 'login = openerpcommand.benchmark.login:init_parser', |
1403 | + 'read = openerpcommand.benchmark.read:init_parser', |
1404 | + 'sale_mrp = openerpcommand.benchmark.sale_mrp:init_parser'], |
1405 | + }, |
1406 | ) |