|
53 |
""" |
53 |
""" |
54 |
|
54 |
|
55 |
import inspect |
55 |
import inspect |
|
|
56 |
from threading import Thread |
56 |
|
57 |
|
57 |
from univention.lib.i18n import Translation |
58 |
from univention.lib.i18n import Translation |
58 |
_ = Translation( 'univention.management.console' ).translate |
59 |
_ = Translation( 'univention.management.console' ).translate |
|
258 |
if function is None: |
259 |
if function is None: |
259 |
return lambda f: simple_response(f, with_flavor) |
260 |
return lambda f: simple_response(f, with_flavor) |
260 |
|
261 |
|
|
|
262 |
# does it |
263 |
# return value ? |
264 |
# => just send the value to the frontend |
265 |
# or |
266 |
# yield value ? |
267 |
# => send the value to the frontend and |
268 |
# finish the function in a thread |
269 |
function_yields = inspect.isgeneratorfunction(function) |
270 |
|
261 |
# fake a generator function that yields whatever the original |
271 |
# fake a generator function that yields whatever the original |
262 |
# function returned |
272 |
# function returned |
263 |
def _fake_func(self, iterator, *args): |
273 |
def _fake_func(self, iterator, *args): |
264 |
for args in iterator: |
274 |
for args in iterator: |
265 |
break |
275 |
break |
266 |
yield function(self, *args) |
276 |
if function_yields: |
|
|
277 |
for res in function(self, *args): |
278 |
yield res |
279 |
else: |
280 |
res = function(self, *args) |
281 |
yield res |
267 |
copy_function_meta_data(function, _fake_func, copy_arg_inspect=True) |
282 |
copy_function_meta_data(function, _fake_func, copy_arg_inspect=True) |
268 |
# fake another variable name |
283 |
# fake another variable name |
269 |
# the name is not important as it is removed from the list while |
284 |
# the name is not important as it is removed from the list while |
|
279 |
|
294 |
|
280 |
# fake a multi_request |
295 |
# fake a multi_request |
281 |
request.options = [request.options] |
296 |
request.options = [request.options] |
282 |
result = _multi_response(self, request) |
297 |
gen = _multi_response(self, request) |
283 |
self.finished(request.id, result[0]) |
298 |
result = gen.next() |
|
|
299 |
self.finished(request.id, result) |
300 |
if function_yields: |
301 |
def _run_to_end(local_gen): |
302 |
for result in local_gen: |
303 |
pass |
304 |
thread = Thread(target=_run_to_end, args=[gen]) |
305 |
thread.start() |
284 |
|
306 |
|
285 |
copy_function_meta_data(function, _response) |
307 |
copy_function_meta_data(function, _response) |
286 |
return _response |
308 |
return _response |
|
329 |
return lambda f: multi_response(f, with_flavor, single_values) |
351 |
return lambda f: multi_response(f, with_flavor, single_values) |
330 |
response_func = _eval_simple_decorated_function(function, with_flavor, single_values) |
352 |
response_func = _eval_simple_decorated_function(function, with_flavor, single_values) |
331 |
def _response(self, request): |
353 |
def _response(self, request): |
332 |
result = response_func(self, request) |
354 |
result = list(response_func(self, request)) |
333 |
self.finished(request.id, result) |
355 |
self.finished(request.id, result) |
334 |
copy_function_meta_data(function, _response) |
356 |
copy_function_meta_data(function, _response) |
335 |
return _response |
357 |
return _response |
|
375 |
# check for required arguments (those without default) |
397 |
# check for required arguments (those without default) |
376 |
raise UMC_OptionMissing(arg) |
398 |
raise UMC_OptionMissing(arg) |
377 |
|
399 |
|
378 |
|
|
|
379 |
# checked for required arguments, set default... now run! |
400 |
# checked for required arguments, set default... now run! |
380 |
result = [] |
|
|
381 |
|
382 |
iterator = RequestOptionsIterator(request.options, arguments, single_values) |
401 |
iterator = RequestOptionsIterator(request.options, arguments, single_values) |
383 |
nones = [None] * len(arguments) |
402 |
nones = [None] * len(arguments) |
384 |
for res in function(self, iterator, *nones): |
403 |
return function(self, iterator, *nones) |
385 |
result.append(res) |
|
|
386 |
|
387 |
return result |
388 |
return _response |
404 |
return _response |
389 |
|
405 |
|
390 |
class RequestOptionsIterator(object): |
406 |
class RequestOptionsIterator(object): |
|
439 |
# copy __module__, otherwise it would be "univention.management.console.modules.decorators" |
455 |
# copy __module__, otherwise it would be "univention.management.console.modules.decorators" |
440 |
new_function.__module__ = original_function.__module__ |
456 |
new_function.__module__ = original_function.__module__ |
441 |
|
457 |
|
442 |
def log(function=None, sensitives=None, customs=None, single_values=False): |
458 |
def log(function=None, sensitives=None, customs=None, single_values=False, logger=None, simple_threaded=False): |
443 |
'''Log decorator to be used with |
459 |
'''Log decorator to be used with :func:`simple_response`. |
444 |
:func:`simple_response`:: |
|
|
445 |
|
460 |
|
|
|
461 |
You can specify any function to handle the log information. By default |
462 |
it is handled by MODULE.info, but you can pass MODULE.process (already |
463 |
logs when *umc/module/debug/level* is set to 2) as well as any other |
464 |
function that can handle strings:: |
465 |
|
446 |
@simple_response |
466 |
@simple_response |
447 |
@log |
467 |
@log |
448 |
def my_func(self, var1, var2): |
468 |
def my_func(self, var1, var2): |
449 |
return "%s__%s" % (var1, var2) |
469 |
return "%s__%s" % (var1, var2) |
450 |
|
470 |
|
451 |
The above example will write two lines into the logfile for the |
471 |
The above example will write two lines into the logfile for the module |
452 |
module (given that the the UCR variable *umc/module/debug/level* |
472 |
(given that the the UCR variable *umc/module/debug/level* is set to at |
453 |
is set to at least 3):: |
473 |
least 3):: |
454 |
|
474 |
|
455 |
<date> MODULE ( INFO ) : my_func got: var1='value1', var2='value2' |
475 |
<date> MODULE ( INFO ) : my_func got: var1='value1', var2='value2' |
456 |
<date> MODULE ( INFO ) : my_func returned: 'value1__value2' |
476 |
<date> MODULE ( INFO ) : my_func returned: 'value1__value2' |
457 |
|
477 |
|
458 |
The variable names are ordered by appearance and hold the values that |
478 |
The variable names are ordered by appearance and hold the values that |
459 |
are actually going to be passed to the function (i.e. after they were |
479 |
are actually going to be passed to the function (i.e. after they were |
460 |
:func:`sanitize` 'd or set to their default value). |
480 |
:func:`sanitize` 'd or set to their default value). You may specify the |
461 |
You may specify the names of sensitive arguments that should not |
481 |
names of sensitive arguments that should not show up in log files and |
462 |
show up in log files and custom functions that can alter the |
482 |
custom functions that can alter the representation of a certain |
463 |
representation of a certain variable's values (useful for non-standard |
483 |
variable's values (useful for non-standard datatypes like regular |
464 |
datatypes like regular expressions - you may have used a |
484 |
expressions - you may have used a |
465 |
:class:`~univention.management.console.modules.sanitizers.PatternSanitizer` |
485 |
:class:`~univention.management.console.modules.sanitizers.PatternSanitizer`):: |
466 |
):: |
|
|
467 |
|
486 |
|
468 |
@sanitize(pattern=PatternSanitizer()) |
487 |
@sanitize(pattern=PatternSanitizer()) |
469 |
@simple_reponse |
488 |
@simple_reponse |
|
479 |
The decorator also works with :func:`multi_response`:: |
498 |
The decorator also works with :func:`multi_response`:: |
480 |
|
499 |
|
481 |
@multi_response |
500 |
@multi_response |
482 |
@log |
501 |
@log(logger=MODULE.process) # logs to PROCESS, not INFO |
483 |
def multi_my_func(self, var1, var2): |
502 |
def multi_my_func(self, var1, var2): |
484 |
return "%s__%s" % (var1, var2) |
503 |
return "%s__%s" % (var1, var2) |
485 |
|
504 |
|
486 |
This results in something like:: |
505 |
This results in something like:: |
487 |
|
506 |
|
488 |
<date> MODULE ( INFO ) : multi_my_func got: [var1='value1', var2='value2'], [var1='value3', var2='value4'] |
507 |
<date> MODULE ( PROCESS ) : multi_my_func got: [var1='value1', var2='value2'], [var1='value3', var2='value4'] |
489 |
<date> MODULE ( INFO ) : multi_my_func returned: ['value1__value2', 'value3__value4'] |
508 |
<date> MODULE ( PROCESS ) : multi_my_func returned: ['value1__value2', 'value3__value4'] |
490 |
''' |
509 |
''' |
491 |
if function is None: |
510 |
if function is None: |
492 |
return lambda f: log(f, sensitives, customs, single_values) |
511 |
return lambda f: log(f, sensitives, customs, single_values, logger, simple_threaded) |
493 |
if customs is None: |
512 |
if customs is None: |
494 |
customs = {} |
513 |
customs = {} |
495 |
if sensitives is None: |
514 |
if sensitives is None: |
496 |
sensitives = [] |
515 |
sensitives = [] |
497 |
for sensitive in sensitives: |
516 |
for sensitive in sensitives: |
498 |
customs[sensitive] = lambda x: '********' |
517 |
customs[sensitive] = lambda x: '********' |
|
|
518 |
if logger is None: |
519 |
logger = MODULE.info |
499 |
|
520 |
|
500 |
def _log(names, args): |
521 |
def _log(names, args): |
501 |
if single_values: |
522 |
if single_values: |
|
507 |
name = function.__name__ |
528 |
name = function.__name__ |
508 |
# multi_response yields i.e. is generator function |
529 |
# multi_response yields i.e. is generator function |
509 |
if inspect.isgeneratorfunction(function): |
530 |
if inspect.isgeneratorfunction(function): |
510 |
# remove self, iterator |
531 |
# problem: simple_response can also yield |
511 |
names = names[2:] |
532 |
if simple_threaded: |
512 |
def _response(self, iterator, *args): |
533 |
# remove self |
513 |
arg_reprs = [] |
534 |
names = names[1:] |
514 |
for element in iterator: |
535 |
def _response_simple_threaded(self, *args): |
515 |
arg_repr = _log(names, element) |
536 |
arg_repr = _log(names, args) |
516 |
if arg_repr: |
537 |
if arg_repr: |
517 |
arg_reprs.append(arg_repr) |
538 |
logger('%s got: %s' % (name, ', '.join(arg_repr))) |
518 |
if arg_reprs: |
539 |
result = function(self, *args) |
519 |
MODULE.info('%s got: [%s]' % (name, '], ['.join(', '.join(arg_repr) for arg_repr in arg_reprs))) |
540 |
res = result.next() |
520 |
result = [] |
541 |
logger('%s returned: %r' % (name, res)) |
521 |
for res in function(self, iterator, *args): |
|
|
522 |
result.append(res) |
523 |
yield res |
542 |
yield res |
524 |
MODULE.info('%s returned: %r' % (name, result)) |
543 |
for res in result: |
|
|
544 |
yield res |
545 |
_response = _response_simple_threaded |
546 |
else: |
547 |
# remove self, iterator |
548 |
names = names[2:] |
549 |
def _response_multiple(self, iterator, *args): |
550 |
arg_reprs = [] |
551 |
for element in iterator: |
552 |
arg_repr = _log(names, element) |
553 |
if arg_repr: |
554 |
arg_reprs.append(arg_repr) |
555 |
if arg_reprs: |
556 |
logger('%s got: [%s]' % (name, '], ['.join(', '.join(arg_repr) for arg_repr in arg_reprs))) |
557 |
result = [] |
558 |
for res in function(self, iterator, *args): |
559 |
result.append(res) |
560 |
yield res |
561 |
logger('%s returned: %r' % (name, result)) |
562 |
_response = _response_multiple |
525 |
else: |
563 |
else: |
526 |
# remove self |
564 |
# remove self |
527 |
names = names[1:] |
565 |
names = names[1:] |
528 |
def _response(self, *args): |
566 |
def _response_simple(self, *args): |
529 |
arg_repr = _log(names, args) |
567 |
arg_repr = _log(names, args) |
530 |
if arg_repr: |
568 |
if arg_repr: |
531 |
MODULE.info('%s got: %s' % (name, ', '.join(arg_repr))) |
569 |
logger('%s got: %s' % (name, ', '.join(arg_repr))) |
532 |
result = function(self, *args) |
570 |
result = function(self, *args) |
533 |
MODULE.info('%s returned: %r' % (name, result)) |
571 |
logger('%s returned: %r' % (name, result)) |
534 |
return result |
572 |
return result |
|
|
573 |
_response = _response_simple |
535 |
copy_function_meta_data(function, _response, copy_arg_inspect=True) |
574 |
copy_function_meta_data(function, _response, copy_arg_inspect=True) |
536 |
return _response |
575 |
return _response |
537 |
|
576 |
|