diff --git a/easybuild/base/generaloption.py b/easybuild/base/generaloption.py index 655056c482..3e9788ac44 100644 --- a/easybuild/base/generaloption.py +++ b/easybuild/base/generaloption.py @@ -48,7 +48,7 @@ from easybuild.base.fancylogger import getLogger, setroot, setLogLevel, getDetailsLogLevels from easybuild.base.optcomplete import autocomplete, CompleterOption -from easybuild.tools.asyncprocess import subprocess_popen_text +from easybuild.tools.run import subprocess_popen_text from easybuild.tools.utilities import mk_md_table, mk_rst_table, nub, shell_quote try: diff --git a/easybuild/tools/asyncprocess.py b/easybuild/tools/asyncprocess.py index 468b1783e2..b3a7d330ff 100644 --- a/easybuild/tools/asyncprocess.py +++ b/easybuild/tools/asyncprocess.py @@ -152,24 +152,6 @@ def _recv(self, which, maxsize): message = "Other end disconnected!" -def subprocess_popen_text(cmd, **kwargs): - """Call subprocess.Popen in text mode with specified named arguments.""" - # open stdout/stderr in text mode in Popen when using Python 3 - kwargs.setdefault('stderr', subprocess.PIPE) - return subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True, **kwargs) - - -def subprocess_terminate(proc, timeout): - """Terminate the subprocess if it hasn't finished after the given timeout""" - try: - proc.communicate(timeout=timeout) - except subprocess.TimeoutExpired: - for pipe in (proc.stdout, proc.stderr, proc.stdin): - if pipe: - pipe.close() - proc.terminate() - - def recv_some(p, t=.2, e=1, tr=5, stderr=0): if tr < 1: tr = 1 diff --git a/easybuild/tools/modules.py b/easybuild/tools/modules.py index 658d2bc9a7..9037be6ea8 100644 --- a/easybuild/tools/modules.py +++ b/easybuild/tools/modules.py @@ -43,7 +43,6 @@ from easybuild.base import fancylogger from easybuild.tools import StrictVersion -from easybuild.tools.asyncprocess import subprocess_popen_text from easybuild.tools.build_log import EasyBuildError, print_warning from easybuild.tools.config import ERROR, IGNORE, PURGE, UNLOAD, UNSET from easybuild.tools.config import EBROOT_ENV_VAR_ACTIONS, LOADED_MODULES_ACTIONS @@ -51,7 +50,7 @@ from easybuild.tools.environment import ORIG_OS_ENVIRON, restore_env, setvar, unset_env_vars from easybuild.tools.filetools import convert_name, mkdir, normalize_path, path_matches, read_file, which, write_file from easybuild.tools.module_naming_scheme.mns import DEVEL_MODULE_SUFFIX -from easybuild.tools.run import run_cmd +from easybuild.tools.run import run_cmd, subprocess_popen_text from easybuild.tools.utilities import get_subclasses, nub # software root/version environment variable name prefixes diff --git a/easybuild/tools/py2vs3/__init__.py b/easybuild/tools/py2vs3/__init__.py index 41ce21c3f6..4bd0e47191 100644 --- a/easybuild/tools/py2vs3/__init__.py +++ b/easybuild/tools/py2vs3/__init__.py @@ -22,9 +22,12 @@ # You should have received a copy of the GNU General Public License # along with EasyBuild. If not, see . # +import sys from easybuild.base import fancylogger +from easybuild.base.wrapper import create_base_metaclass # noqa + # all functionality provided by the py3 modules is made available via the easybuild.tools.py2vs3 namespace from easybuild.tools.py2vs3.py3 import * # noqa @@ -33,8 +36,24 @@ _log.deprecated("Using py2vs3 is deprecated, since EasyBuild no longer runs on Python 2.", '6.0') -# based on six's 'with_metaclass' function -# see also https://stackoverflow.com/questions/18513821/python-metaclass-understanding-the-with-metaclass -def create_base_metaclass(base_class_name, metaclass, *bases): - """Create new class with specified metaclass based on specified base class(es).""" - return metaclass(base_class_name, bases, {}) +def python2_is_deprecated(): + """ + Print warning when using Python 2, since the support for running EasyBuild with it is deprecated. + """ + if sys.version_info[0] == 2: + full_py_ver = '.'.join(str(x) for x in sys.version_info[:3]) + warning_lines = [ + "Running EasyBuild with Python v2.x is deprecated, found Python v%s." % full_py_ver, + "Support for running EasyBuild with Python v2.x will be removed in EasyBuild v5.0.", + '', + "It is strongly recommended to start using Python v3.x for running EasyBuild,", + "see https://docs.easybuild.io/en/latest/Python-2-3-compatibility.html for more information.", + ] + max_len = max(len(x) for x in warning_lines) + for i in range(len(warning_lines)): + line_len = len(warning_lines[i]) + warning_lines[i] = '!!! ' + warning_lines[i] + ' ' * (max_len - line_len) + ' !!!' + max_len = max(len(x) for x in warning_lines) + warning_lines.insert(0, '!' * max_len) + warning_lines.append('!' * max_len) + sys.stderr.write('\n\n' + '\n'.join(warning_lines) + '\n\n\n') diff --git a/easybuild/tools/py2vs3/py3.py b/easybuild/tools/py2vs3/py3.py index 91a5342075..b81e549013 100644 --- a/easybuild/tools/py2vs3/py3.py +++ b/easybuild/tools/py2vs3/py3.py @@ -34,7 +34,6 @@ # these are not used here, but imported from here in other places import configparser # noqa import json -import subprocess import sys import urllib.request as std_urllib # noqa from collections import OrderedDict # noqa @@ -59,6 +58,9 @@ except ImportError: HAVE_DISTUTILS = False +from easybuild.base.wrapper import mk_wrapper_baseclass # noqa +from easybuild.tools.run import subprocess_popen_text, subprocess_terminate # noqa + # string type that can be used in 'isinstance' calls string_type = str @@ -75,24 +77,6 @@ def json_loads(body): return json.loads(body) -def subprocess_popen_text(cmd, **kwargs): - """Call subprocess.Popen in text mode with specified named arguments.""" - # open stdout/stderr in text mode in Popen when using Python 3 - kwargs.setdefault('stderr', subprocess.PIPE) - return subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True, **kwargs) - - -def subprocess_terminate(proc, timeout): - """Terminate the subprocess if it hasn't finished after the given timeout""" - try: - proc.communicate(timeout=timeout) - except subprocess.TimeoutExpired: - for pipe in (proc.stdout, proc.stderr, proc.stdin): - if pipe: - pipe.close() - proc.terminate() - - def raise_with_traceback(exception_class, message, traceback): """Raise exception of specified class with given message and traceback.""" raise exception_class(message).with_traceback(traceback) @@ -103,17 +87,6 @@ def extract_method_name(method_func): return '_'.join(method_func.__code__.co_names) -def mk_wrapper_baseclass(metaclass): - - class WrapperBase(object, metaclass=metaclass): - """ - Wrapper class that provides proxy access to an instance of some internal instance. - """ - __wraps__ = None - - return WrapperBase - - def safe_cmp_looseversions(v1, v2): """Safe comparison function for two (values containing) LooseVersion instances.""" diff --git a/easybuild/tools/run.py b/easybuild/tools/run.py index 6540fece2d..7ce358ca89 100644 --- a/easybuild/tools/run.py +++ b/easybuild/tools/run.py @@ -769,3 +769,21 @@ def check_log_for_errors(log_txt, reg_exps): if errors: raise EasyBuildError("Found %s error(s) in command output (output: %s)", len(errors), "\n\t".join(errors)) + + +def subprocess_popen_text(cmd, **kwargs): + """Call subprocess.Popen in text mode with specified named arguments.""" + # open stdout/stderr in text mode in Popen when using Python 3 + kwargs.setdefault('stderr', subprocess.PIPE) + return subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True, **kwargs) + + +def subprocess_terminate(proc, timeout): + """Terminate the subprocess if it hasn't finished after the given timeout""" + try: + proc.communicate(timeout=timeout) + except subprocess.TimeoutExpired: + for pipe in (proc.stdout, proc.stderr, proc.stdin): + if pipe: + pipe.close() + proc.terminate() diff --git a/easybuild/tools/systemtools.py b/easybuild/tools/systemtools.py index 4db28015b2..2d58ae1f55 100644 --- a/easybuild/tools/systemtools.py +++ b/easybuild/tools/systemtools.py @@ -45,7 +45,7 @@ from collections import OrderedDict from ctypes.util import find_library from socket import gethostname -from easybuild.tools.asyncprocess import subprocess_popen_text +from easybuild.tools.run import subprocess_popen_text # pkg_resources is provided by the setuptools Python package, # which we really want to keep as an *optional* dependency diff --git a/test/framework/asyncprocess.py b/test/framework/asyncprocess.py index 5ec6acfef9..56666f3f50 100644 --- a/test/framework/asyncprocess.py +++ b/test/framework/asyncprocess.py @@ -34,7 +34,8 @@ from unittest import TextTestRunner import easybuild.tools.asyncprocess as p -from easybuild.tools.asyncprocess import Popen, subprocess_terminate +from easybuild.tools.asyncprocess import Popen +from easybuild.tools.run import subprocess_terminate class AsyncProcessTest(EnhancedTestCase): diff --git a/test/framework/run.py b/test/framework/run.py index 29a67188c1..f1b6f1973f 100644 --- a/test/framework/run.py +++ b/test/framework/run.py @@ -46,11 +46,10 @@ import easybuild.tools.asyncprocess as asyncprocess import easybuild.tools.utilities -from easybuild.tools.asyncprocess import subprocess_terminate from easybuild.tools.build_log import EasyBuildError, init_logging, stop_logging from easybuild.tools.filetools import adjust_permissions, read_file, write_file from easybuild.tools.run import check_async_cmd, check_log_for_errors, complete_cmd, get_output_from_process -from easybuild.tools.run import parse_log_for_error, run_cmd, run_cmd_qa +from easybuild.tools.run import parse_log_for_error, run_cmd, run_cmd_qa, subprocess_terminate from easybuild.tools.config import ERROR, IGNORE, WARN