From 1263cb5e806890283b152455f50d6986526e8bcd Mon Sep 17 00:00:00 2001 From: Alexander Turenko Date: Mon, 5 Apr 2021 04:58:32 +0300 Subject: [PATCH] Fix slowdown on Python 3 Don't parse command line arguments each time `Options()` is called. The metaclass syntax was changed in Python 3 (see [1]) and it is tricky to add a metaclass in a Python version agnostic way. Still possible, but the code would be hard to understand. So I get rid of the Singleton metaclass and use __new__() to implement this logic. Aside of the Option class, we had another singleton: Colorer. However it is instanciated once (this instance is exposed as color_stdout) and it does not matter, whether it is a singleton or not. A brief testing on tarantool's test suite (with --long --force) gives me ~8 minutes after the fix instead of ~11 minutes before the fix. However it worth to note that we have a lot of tests, which are marked as fragile and so run without parallelization. Otherwise the difference would be lower, I guess. [1]: https://www.python.org/dev/peps/pep-3115/ Fixes #279 --- lib/colorer.py | 2 -- lib/options.py | 21 ++++++++++++++++++--- lib/singleton.py | 8 -------- 3 files changed, 18 insertions(+), 13 deletions(-) delete mode 100644 lib/singleton.py diff --git a/lib/colorer.py b/lib/colorer.py index 8c867c1b..48ab5607 100644 --- a/lib/colorer.py +++ b/lib/colorer.py @@ -1,7 +1,6 @@ import os import re import sys -from lib.singleton import Singleton # Use it to print messages on the screen and to the worker's log. @@ -128,7 +127,6 @@ class Colorer(object): 1. ftp://ftp.cs.utk.edu/pub/shuford/terminal/dec_vt220_codes.txt 2. http://invisible-island.net/xterm/ctlseqs/ctlseqs.html """ - __metaclass__ = Singleton fgcolor = { "black": '0;30', "red": '0;31', diff --git a/lib/options.py b/lib/options.py index 94824174..c4703ac4 100644 --- a/lib/options.py +++ b/lib/options.py @@ -2,7 +2,6 @@ import sys import argparse from itertools import product -from lib.singleton import Singleton from lib.colorer import color_stdout @@ -23,14 +22,28 @@ def env_list(name, default): return value_list or default -class Options: +class Options(object): """Handle options of test-runner""" - __metaclass__ = Singleton + _instance = None + _initialized = False + + def __new__(cls, *args, **kwargs): + """Make the class singleton.""" + if cls._instance: + return cls._instance + cls._instance = super(Options, cls).__new__(cls, *args, **kwargs) + return cls._instance def __init__(self): """Add all program options, with their defaults.""" + # The __init__() method is called always, even when we + # return already initialized Options instance from + # __new__(). + if Options._initialized: + return + parser = argparse.ArgumentParser( description="Tarantool regression test suite front-end.") @@ -264,6 +277,8 @@ def __init__(self): self.args = parser.parse_args() self.check() + Options._initialized = True + def check(self): """Check the arguments for correctness.""" check_error = False diff --git a/lib/singleton.py b/lib/singleton.py deleted file mode 100644 index 571a1741..00000000 --- a/lib/singleton.py +++ /dev/null @@ -1,8 +0,0 @@ -class Singleton(type): - _instances = {} - - def __call__(cls, *args, **kwargs): - if cls not in cls._instances: - cls._instances[cls] = super(Singleton, cls).__call__( - *args, **kwargs) - return cls._instances[cls]