diff --git a/t/python/spooler.ini b/t/python/spooler_decorators/spooler_decorator_test.ini similarity index 71% rename from t/python/spooler.ini rename to t/python/spooler_decorators/spooler_decorator_test.ini index 3b95ba8e..352f1aee 100644 --- a/t/python/spooler.ini +++ b/t/python/spooler_decorators/spooler_decorator_test.ini @@ -2,9 +2,10 @@ socket = /tmp/temporary_socket ; Spooler! -spooler-import = %d/spooler_handler.py + +spooler-import = %d/spooler_handlers.py ; Specify the spooler -spooler = %d/temporary_spooler +spooler = $(SPOOLER_DIR) ; And the number of processes spooler-processes = 1 ; Spooler ordered scanning (only works with "numbered" dirs) @@ -12,6 +13,6 @@ spooler-ordered = 1 ; Spooler frequency spooler-frequency = 1 -pyrun = %d/spooler.py +pyrun = %d/spooler_decorator_tests.py master = 1 diff --git a/t/python/spooler_decorators/spooler_decorator_tests.py b/t/python/spooler_decorators/spooler_decorator_tests.py new file mode 100644 index 00000000..ec72e399 --- /dev/null +++ b/t/python/spooler_decorators/spooler_decorator_tests.py @@ -0,0 +1,31 @@ +# run it with: +# export SPOOLER_DIR=t/python/spooler_priority/temporary_spooler; # or your spooler dir +# ./uwsgi t/python/spooler_decorators/spooler_decorator_test.ini + +import unittest +import uwsgi +import spooler_handlers +from os import remove, path + + +class BitmapTest(unittest.TestCase): + + def setUp(self): + try: + remove(spooler_handlers.ghostpath) + except OSError: # file does not exist + pass + + spooler_handlers.controlled_task.spool(arg='alive', ghost='world') + spooler_handlers.controlled_task.spool(arg='barbis') + spooler_handlers.controlled_raw_task.spool(arg='alive', ghost='world') + spooler_handlers.controlled_raw_task.spool(arg='barbis') + + for i in range(4): + uwsgi.signal_wait(20) + print("Signal received!") + + def test_spooler(self): + self.assertFalse(path.exists(spooler_handlers.ghostpath)) + +unittest.main() diff --git a/t/python/spooler_decorators/spooler_handlers.py b/t/python/spooler_decorators/spooler_handlers.py new file mode 100644 index 00000000..5298f91c --- /dev/null +++ b/t/python/spooler_decorators/spooler_handlers.py @@ -0,0 +1,23 @@ +# See spooler_decorator_tests + +from uwsgidecorators import * +import uwsgi + +ghostpath = "/tmp/ghost" + + +@spool +def controlled_task(arguments): + if arguments['arg'] != 'alive' and 'ghost' in arguments: + print("We have a problem!") + open(ghostpath, 'w').close() + uwsgi.signal(20) + + +@spoolraw +def controlled_raw_task(arguments): + if arguments['arg'] != 'alive' and 'ghost' in arguments: + print("We have a problem!") + open(ghostpath, 'w').close() + uwsgi.signal(20) + return uwsgi.SPOOL_OK diff --git a/t/python/constants.py b/t/python/spooler_priority/spooler_priority_constants.py similarity index 100% rename from t/python/constants.py rename to t/python/spooler_priority/spooler_priority_constants.py diff --git a/t/python/spooler_priority/spooler_priority_handler.py b/t/python/spooler_priority/spooler_priority_handler.py new file mode 100644 index 00000000..f0e151e4 --- /dev/null +++ b/t/python/spooler_priority/spooler_priority_handler.py @@ -0,0 +1,33 @@ +#! /usr/bin/env python +# coding = utf-8 + +from __future__ import print_function +from constants import tasks, LOGFILE +from os import remove +import uwsgi + +counter = 0 + + +def spoolerHandler(env): + global counter + # Spooler is handling a task + with open(LOGFILE, "a") as log: + print("%s" % (env['name']), file=log) + + counter += 1 + + if counter == len(tasks): + # Each task has been processed. + uwsgi.signal(17) + + # Spooler has done handling the task + return uwsgi.SPOOL_OK + +uwsgi.spooler = spoolerHandler + +# Clear the logfile +try: + remove(LOGFILE) +except OSError, e: # log does not exist + print(e) diff --git a/t/python/spooler_priority/spooler_priority_test.ini b/t/python/spooler_priority/spooler_priority_test.ini new file mode 100644 index 00000000..0076fe6d --- /dev/null +++ b/t/python/spooler_priority/spooler_priority_test.ini @@ -0,0 +1,17 @@ +[uwsgi] +socket = /tmp/temporary-socket + +; Specify the spooler +spooler = %d/temporary_spooler +; Spooler handler +spooler-import = %d/spooler_priority_handler.py +; And the number of processes +spooler-processes = 1 +; Spooler ordered scanning (only works with "numbered" dirs) +spooler-ordered = 1 +; Spooler scans folder each second +spooler-frequency = 1 + +pyrun = %d/spooler_priority_test.py +master = 1 + diff --git a/t/python/spooler.py b/t/python/spooler_priority/spooler_priority_test.py similarity index 85% rename from t/python/spooler.py rename to t/python/spooler_priority/spooler_priority_test.py index 04faaba3..5b255b26 100644 --- a/t/python/spooler.py +++ b/t/python/spooler_priority/spooler_priority_test.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python2 +#! /usr/bin/env python # coding = utf-8 import uwsgi @@ -7,8 +7,7 @@ import os import fcntl from shutil import rmtree import time -import constants -from signal import * +import spooler_priority_constants def spoolersTaskList(): @@ -62,8 +61,7 @@ def cleanTasks(): class BitmapTest(unittest.TestCase): def setUp(self): - self.addCleanup(cleanTasks) - for priority, name in constants.tasks: + for priority, name in spooler_priority_constants.tasks: task = {'name': name, 'at': int(time.time() + 10)} if priority is not None: task['priority'] = str(priority) @@ -73,10 +71,10 @@ class BitmapTest(unittest.TestCase): uwsgi.signal_wait(17) print("Signal received.") - with open(constants.LOGFILE, "r") as log: + with open(spooler_priority_constants.LOGFILE, "r") as log: # Check logging ordering. loglines = [line.rstrip() for line in log] - self.assertEqual(loglines, constants.ordered_tasks) + self.assertEqual(loglines, spooler_priority_constants.ordered_tasks) signal(SIGINT, cleanTasks) unittest.main() diff --git a/uwsgidecorators.py b/uwsgidecorators.py index ec24edc5..290ee666 100644 --- a/uwsgidecorators.py +++ b/uwsgidecorators.py @@ -18,6 +18,29 @@ mule_functions = {} postfork_chain = [] +# Python3 compatibility +def _encode1(val): + if sys.version_info >= (3, 0) and isinstance(val, str): + return val.encode('utf-8') + else: + return val + + +def _decode1(val): + if sys.version_info >= (3, 0) and isinstance(val, bytes): + return val.decode('utf-8') + else: + return val + + +def _encode_to_spooler(vars): + return dict((_encode1(K), _encode1(V)) for (K, V) in vars.items()) + + +def _decode_from_spooler(vars): + return dict((_decode1(K), _decode1(V)) for (K, V) in vars.items()) + + def get_free_signal(): for signum in range(0, 256): if not uwsgi.signal_registered(signum): @@ -27,6 +50,7 @@ def get_free_signal(): def manage_spool_request(vars): + vars = _decode_from_spooler(vars) f = spooler_functions[vars['ud_spool_func']] if 'args' in vars: args = pickle.loads(vars.pop('args')) @@ -48,6 +72,7 @@ uwsgi.post_fork_hook = postfork_chain_hook class postfork(object): + def __init__(self, f): if callable(f): self.wid = 0 @@ -56,6 +81,7 @@ class postfork(object): self.f = None self.wid = f postfork_chain.append(self) + def __call__(self, *args, **kwargs): if self.f: if self.wid > 0 and self.wid != uwsgi.worker_id(): @@ -67,7 +93,7 @@ class postfork(object): class _spoolraw(object): def __call__(self, *args, **kwargs): - arguments = self.base_dict + arguments = self.base_dict.copy() if not self.pass_arguments: if len(args) > 0: arguments.update(args[0]) @@ -79,8 +105,9 @@ class _spoolraw(object): if key in kwargs: spooler_args.update({key: kwargs.pop(key)}) arguments.update(spooler_args) - arguments.update({'args': pickle.dumps(args), 'kwargs': pickle.dumps(kwargs)}) - return uwsgi.spool(arguments) + arguments.update( + {'args': pickle.dumps(args), 'kwargs': pickle.dumps(kwargs)}) + return uwsgi.spool(_encode_to_spooler(arguments)) # For backward compatibility (uWSGI < 1.9.13) def spool(self, *args, **kwargs): @@ -234,6 +261,7 @@ class mule_brainloop(mule_brain): class mule(object): + def __init__(self, num): self.num = num @@ -242,6 +270,7 @@ class mule(object): class muleloop(mule): + def __call__(self, f): postfork_chain.append(mule_brainloop(f, self.num)) @@ -261,6 +290,7 @@ class mulemsg_loop(object): class mulemsg(object): + def __init__(self, num): self.num = num @@ -306,7 +336,7 @@ class cron(object): def __call__(self, f): uwsgi.register_signal(self.num, self.target, f) uwsgi.add_cron(self.num, self.minute, self.hour, - self.day, self.month, self.dayweek) + self.day, self.month, self.dayweek) return f @@ -347,6 +377,7 @@ class erlang(object): class lock(object): + def __init__(self, f): self.f = f