From 005c97764c7f83b2fabf427664e46e6a8bf7fe9d Mon Sep 17 00:00:00 2001 From: Jonathan Haigh Date: Wed, 18 Sep 2024 12:06:47 +0100 Subject: [PATCH 1/4] remove code intended for python 2 --- .../PythonInterface/mantid/api/_adsimports.py | 3 +- .../Exports/SpectrumInfoPythonIterator.cpp | 12 +---- .../algorithms/BASISCrystalDiffraction.py | 1 - .../plugins/algorithms/BASISDiffraction.py | 1 - .../algorithms/BASISPowderDiffraction.py | 3 -- .../plugins/algorithms/BASISReduction.py | 3 -- .../algorithms/Examples/ExampleSaveAscii.py | 1 - .../plugins/algorithms/Examples/Squares.py | 2 +- .../plugins/algorithms/LoadEXED.py | 9 +--- .../algorithms/SaveGEMMAUDParamFile.py | 5 +- .../python/mantid/api/MDHistoWorkspaceTest.py | 13 ++--- .../framework/ISIS_WISHPowderReductionTest.py | 51 +++++-------------- .../framework/WeightedLeastSquaresTest.py | 29 +++++------ .../CMake/Packaging/AddPythonPath.py.in | 47 ++++++----------- dev-docs/source/DebuggingUnitTests.rst | 2 +- dev-docs/source/Standards/RenameAlgorithm.rst | 2 +- .../widgets/codeeditor/interpreter.py | 9 +--- .../Muon/GUI/Common/muon_load_data.py | 3 -- .../GUI/Common/utilities/muon_test_helpers.py | 3 -- .../test/Muon/LoadWidgetPresenter_test.py | 7 +-- .../loadfile_presenter_multiple_file_test.py | 2 - .../test/TOFTOF/TOFTOFScriptElementTest.py | 14 ++--- .../Inelastic/Direct/PropertiesDescriptors.py | 4 -- scripts/SANS/SANSBatchMode.py | 6 +-- scripts/SANS/isis_instrument.py | 4 +- scripts/SANS/isis_reduction_steps.py | 28 ++-------- scripts/SANS/sans/gui_logic/gui_common.py | 5 +- scripts/abins/input/crystalloader.py | 5 +- scripts/abins/io.py | 14 ----- .../instruments/sans/sans_reduction_steps.py | 20 ++------ scripts/reduction_gui/reduction/scripter.py | 47 ++++++++--------- scripts/test/DirectPropertyManagerTest.py | 6 +-- scripts/test/ReductionWrapperTest.py | 2 +- 33 files changed, 98 insertions(+), 265 deletions(-) diff --git a/Framework/PythonInterface/mantid/api/_adsimports.py b/Framework/PythonInterface/mantid/api/_adsimports.py index b08ca01c2755..7d9b61ee09e4 100644 --- a/Framework/PythonInterface/mantid/api/_adsimports.py +++ b/Framework/PythonInterface/mantid/api/_adsimports.py @@ -85,7 +85,8 @@ def is_valid_identifier(name): """ if _keyword.iskeyword(name): return False - # If the regex matches it is a valid identifier in Python 2.x + # If the regex matches it is a valid identifier + # Rules can be found here https://www.w3schools.com/python/gloss_python_variable_names.asp return IDENT_REGEX.match(name) is not None diff --git a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoPythonIterator.cpp b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoPythonIterator.cpp index b2e7b4a55fce..ad91229ec8b4 100644 --- a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoPythonIterator.cpp +++ b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoPythonIterator.cpp @@ -7,7 +7,6 @@ #include "MantidPythonInterface/api/SpectrumInfoPythonIterator.h" #include -#include #include #include @@ -20,14 +19,5 @@ void export_SpectrumInfoPythonIterator() { // Export to Python class_("SpectrumInfoPythonIterator", no_init) .def("__iter__", objects::identity_function()) - .def("__next__", &SpectrumInfoPythonIterator::next, return_value_policy()); - /* - Return value policy for next is to copy the const reference. Copy by value is - essential for python 2.0 compatibility because items (SpectrumInfoItem) will - outlive their iterators if declared as part of for loops. There is no good - way to deal with this other than to force a copy so that internals of the - item are not also corrupted. Future developers may wish to choose a separte - policy for python 3.0 where this is not a concern, and const ref returns - would be faster. - */ + .def("__next__", &SpectrumInfoPythonIterator::next, return_value_policy()); } diff --git a/Framework/PythonInterface/plugins/algorithms/BASISCrystalDiffraction.py b/Framework/PythonInterface/plugins/algorithms/BASISCrystalDiffraction.py index 83890c7941b1..7150714e2e9f 100644 --- a/Framework/PythonInterface/plugins/algorithms/BASISCrystalDiffraction.py +++ b/Framework/PythonInterface/plugins/algorithms/BASISCrystalDiffraction.py @@ -274,7 +274,6 @@ def PyExec(self): # Facility and database configuration config_new_options = {"default.facility": "SNS", "default.instrument": "BASIS", "datasearch.searcharchive": "On"} - # implement with ContextDecorator after python2 is deprecated) with pyexec_setup(config_new_options) as self._temps: # Load the mask to a temporary workspace self._t_mask = LoadMask(Instrument="BASIS", InputFile=self.getProperty("MaskFile").value, OutputWorkspace="_t_mask") diff --git a/Framework/PythonInterface/plugins/algorithms/BASISDiffraction.py b/Framework/PythonInterface/plugins/algorithms/BASISDiffraction.py index 89c8b6edc189..3ae6bf4cdb3c 100644 --- a/Framework/PythonInterface/plugins/algorithms/BASISDiffraction.py +++ b/Framework/PythonInterface/plugins/algorithms/BASISDiffraction.py @@ -237,7 +237,6 @@ def PyExec(self): self._lambda_range = np.array(self.getProperty("LambdaRange").value) self._momentum_range = np.sort(2 * np.pi / self._lambda_range) - # implement with ContextDecorator after python2 is deprecated) with pyexec_setup(config_new_options) as self._temps: # Load the mask to a workspace self._t_mask = LoadMask(Instrument="BASIS", InputFile=self.getProperty("MaskFile").value, OutputWorkspace="_t_mask") diff --git a/Framework/PythonInterface/plugins/algorithms/BASISPowderDiffraction.py b/Framework/PythonInterface/plugins/algorithms/BASISPowderDiffraction.py index b858b1432c2b..aa1359f1b618 100644 --- a/Framework/PythonInterface/plugins/algorithms/BASISPowderDiffraction.py +++ b/Framework/PythonInterface/plugins/algorithms/BASISPowderDiffraction.py @@ -314,9 +314,6 @@ def PyExec(self): # Find desired Q-binning # self._qbins = np.array(self.getProperty("MomentumTransferBins").value) - # - # implement with ContextDecorator after python2 is deprecated) - # remove_temp = self.getProperty("RemoveTemporaryWorkspaces").value with pyexec_setup(remove_temp, config_new_options) as self._temps: # diff --git a/Framework/PythonInterface/plugins/algorithms/BASISReduction.py b/Framework/PythonInterface/plugins/algorithms/BASISReduction.py index 86f9d1ae5b8c..c461ef71c3a1 100644 --- a/Framework/PythonInterface/plugins/algorithms/BASISReduction.py +++ b/Framework/PythonInterface/plugins/algorithms/BASISReduction.py @@ -354,9 +354,6 @@ def PyInit(self): def PyExec(self): # Facility and database configuration config_new_options = {"default.facility": "SNS", "default.instrument": "BASIS", "datasearch.searcharchive": "On"} - # - # implement with ContextDecorator after python2 is deprecated) - # remove_temp = self.getProperty("RemoveTemporaryWorkspaces").value with pyexec_setup(remove_temp, config_new_options) as self._temps: self._PyExec() diff --git a/Framework/PythonInterface/plugins/algorithms/Examples/ExampleSaveAscii.py b/Framework/PythonInterface/plugins/algorithms/Examples/ExampleSaveAscii.py index f1c37795da35..445ceb8aa082 100644 --- a/Framework/PythonInterface/plugins/algorithms/Examples/ExampleSaveAscii.py +++ b/Framework/PythonInterface/plugins/algorithms/Examples/ExampleSaveAscii.py @@ -11,7 +11,6 @@ Note that the SaveAscii algorithm should be used instead in most cases. """ -# This __future__ import is for Python 2/3 compatibility from mantid.kernel import * from mantid.api import * diff --git a/Framework/PythonInterface/plugins/algorithms/Examples/Squares.py b/Framework/PythonInterface/plugins/algorithms/Examples/Squares.py index c13efab7f297..0208e65a16fa 100644 --- a/Framework/PythonInterface/plugins/algorithms/Examples/Squares.py +++ b/Framework/PythonInterface/plugins/algorithms/Examples/Squares.py @@ -5,7 +5,7 @@ # Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS # SPDX - License - Identifier: GPL - 3.0 + # pylint: disable=no-init -# This __future__ import is for Python 2/3 compatibility + from mantid.api import * from mantid.kernel import * diff --git a/Framework/PythonInterface/plugins/algorithms/LoadEXED.py b/Framework/PythonInterface/plugins/algorithms/LoadEXED.py index c76a2c2d680e..5ecc29bd2b46 100644 --- a/Framework/PythonInterface/plugins/algorithms/LoadEXED.py +++ b/Framework/PythonInterface/plugins/algorithms/LoadEXED.py @@ -22,13 +22,6 @@ import struct import numpy as np import copy -import types - -# Unicode type for both python2 and 3 -try: - UnicodeType = types.UnicodeType -except AttributeError: - UnicodeType = str class LoadEXED(PythonAlgorithm): @@ -121,7 +114,7 @@ def PyExec(self): ws.getSpectrum(i).setDetectorID(det_udet[i]) # Sample_logs the header values are written into the sample logs log_names = [str(sl.encode("ascii", "ignore").decode()) for sl in parms_dict.keys()] - log_values = [str(sl.encode("ascii", "ignore").decode()) if isinstance(sl, UnicodeType) else str(sl) for sl in parms_dict.values()] + log_values = [str(sl.encode("ascii", "ignore").decode()) if isinstance(sl, str) else str(sl) for sl in parms_dict.values()] for i in range(len(log_values)): if ("nan" in log_values[i]) or ("NaN" in log_values[i]): log_values[i] = "-1.0" diff --git a/Framework/PythonInterface/plugins/algorithms/SaveGEMMAUDParamFile.py b/Framework/PythonInterface/plugins/algorithms/SaveGEMMAUDParamFile.py index 13e13504e143..51f02446fcd5 100644 --- a/Framework/PythonInterface/plugins/algorithms/SaveGEMMAUDParamFile.py +++ b/Framework/PythonInterface/plugins/algorithms/SaveGEMMAUDParamFile.py @@ -8,7 +8,6 @@ import os import re from collections import defaultdict -from string import Formatter from mantid.api import * from mantid.kernel import * @@ -115,9 +114,7 @@ def create_empty_param_list(default_value="0"): output_params["bank_ids"] = "\n".join("Bank{}".format(i + 1) for i in range(num_banks)) with open(self.getProperty(self.PROP_OUTPUT_FILE).value, "w") as output_file: - # Note, once we've got rid of Python 2 support this can be simplified to - # template.format_map(**defaultdict(create_empty_param_list, output_params)) - output_file.write(Formatter().vformat(template, (), defaultdict(create_empty_param_list, output_params))) + output_file.write(template.format_map(defaultdict(create_empty_param_list, output_params))) def validateInputs(self): issues = {} diff --git a/Framework/PythonInterface/test/python/mantid/api/MDHistoWorkspaceTest.py b/Framework/PythonInterface/test/python/mantid/api/MDHistoWorkspaceTest.py index 35e0c4e523c5..55717da4b187 100644 --- a/Framework/PythonInterface/test/python/mantid/api/MDHistoWorkspaceTest.py +++ b/Framework/PythonInterface/test/python/mantid/api/MDHistoWorkspaceTest.py @@ -10,13 +10,6 @@ import numpy -try: - long -except NameError: - # Defined for backwards compatability with Python 2 - def long(x): - return x - class MDHistoWorkspaceTest(unittest.TestCase): """ @@ -321,7 +314,7 @@ def test_integrated_bin(self): ) BH = mtd["BH"] signal = BH.getSignalArray() - expected = (long(20), long(1), long(30)) + expected = (20, 1, 30) shape = signal.shape self.assertEqual(shape, expected) mtd.remove("BH") @@ -339,7 +332,7 @@ def test_composed_bin(self): ) BH = mtd["BH"] signal = BH.getSignalArray() - expected = (long(20), long(1)) + expected = (20, 1) shape = signal.shape self.assertEqual(shape, expected) mtd.remove("BH") @@ -375,7 +368,7 @@ def test_heterogeneous_bin(self): nEvents = BH.getNEvents() self.assertEqual(nEvents, 1000) signal = BH.getSignalArray() - expected = (long(20), long(5), long(40)) + expected = (20, 5, 40) shape = signal.shape self.assertEqual(shape, expected) diff --git a/Testing/SystemTests/tests/framework/ISIS_WISHPowderReductionTest.py b/Testing/SystemTests/tests/framework/ISIS_WISHPowderReductionTest.py index 23edf9666f42..bb365e12894d 100644 --- a/Testing/SystemTests/tests/framework/ISIS_WISHPowderReductionTest.py +++ b/Testing/SystemTests/tests/framework/ISIS_WISHPowderReductionTest.py @@ -8,9 +8,9 @@ from wish.reduce import Wish from mantid import config +from tempfile import TemporaryDirectory import mantid.simpleapi as mantid import os -import shutil DIRS = config["datasearch.directories"].split(";") @@ -19,7 +19,6 @@ # Relative to working folder input_folder_name = "input" -output_folder_name = "output" # Relative to input folder calibration_folder_name = "Cal" @@ -29,7 +28,6 @@ working_dir = os.path.join(DIRS[0], working_folder_name) input_dir = os.path.join(working_dir, input_folder_name) -output_dir = os.path.join(working_dir, output_folder_name) calibration_dir = os.path.join(input_dir, calibration_folder_name) # just test 5 and 6 to save time as process is the same for all other pairs @@ -45,17 +43,13 @@ def requiredFiles(self): input_files = [os.path.join(calibration_dir, files) for files in input_files] return input_files - def cleanup(self): - if os.path.isdir(output_dir): - shutil.rmtree(output_dir) - def runTest(self): - create_folder() - wish_test = Wish(calibration_dir, output_dir, True, input_dir + "/", False) - runs = [40503] + with TemporaryDirectory() as tmp_output_dir: + wish_test = Wish(calibration_dir, tmp_output_dir, True, input_dir + "/", False) + runs = [40503] - wish_test.reduce(runs, panels) - self.clearWorkspaces() + wish_test.reduce(runs, panels) + self.clearWorkspaces() def validate(self): self.tolerance = 1.0e-8 @@ -85,17 +79,13 @@ def requiredFiles(self): input_files = [os.path.join(calibration_dir, files) for files in input_files] return input_files - def cleanup(self): - if os.path.isdir(output_dir): - shutil.rmtree(output_dir) - def runTest(self): - create_folder() - wish_test = Wish(calibration_dir, output_dir, True, input_dir + "/") - runs = [40503] + with TemporaryDirectory() as temp_output_dir: + wish_test = Wish(calibration_dir, temp_output_dir, True, input_dir + "/") + runs = [40503] - wish_test.reduce(runs, panels) - self.clearWorkspaces() + wish_test.reduce(runs, panels) + self.clearWorkspaces() def validate(self): self.tolerance = 1.0e-8 @@ -125,14 +115,10 @@ def requiredFiles(self): input_files = [os.path.join(calibration_dir, files) for files in input_files] return input_files - def cleanup(self): - if os.path.isdir(output_dir): - shutil.rmtree(output_dir) - def runTest(self): - create_folder() - wish_test = Wish(calibration_dir, output_dir, True, input_dir + "/") - wish_test.create_vanadium_run(19612, 19618, panels) + with TemporaryDirectory() as tmp_output_dir: + wish_test = Wish(calibration_dir, tmp_output_dir, True, input_dir + "/") + wish_test.create_vanadium_run(19612, 19618, panels) def validate(self): self.tolerance = 1.0e-8 @@ -143,12 +129,3 @@ def validate(self): def requiredMemoryMB(self): return 12000 - - -def create_folder(): - # make folder in try catch because we can't guarantee that the cleanup has run, once we dont need to support - # python 2 we can use tempfile.TemporaryDirectory() which is automatically deleted like tempfile is - try: - os.makedirs(output_dir) - except OSError: - return diff --git a/Testing/SystemTests/tests/framework/WeightedLeastSquaresTest.py b/Testing/SystemTests/tests/framework/WeightedLeastSquaresTest.py index b4a626b1650c..764f80a3842d 100644 --- a/Testing/SystemTests/tests/framework/WeightedLeastSquaresTest.py +++ b/Testing/SystemTests/tests/framework/WeightedLeastSquaresTest.py @@ -119,15 +119,12 @@ class TwoGaussPeaksEVSData(unittest.TestCase): Representative of a processed Vesuvio dataset that contains a couple of peaks. """ - filename = "EVS14188-90_processed.txt" - workspace = None function_template = "name=Gaussian, {0} ; name=LinearBackground,A0=0,A1=0;" "name=Gaussian, {1}" - # Using this workaround as we still support Python 2.6 on rhel6, where setUpClass() - # is not available - def setUp(self): - if not self.__class__.workspace: - self.__class__.workspace = load_fitting_test_file_ascii(self.filename) + @classmethod + def setUpClass(cls) -> None: + filename = "EVS14188-90_processed.txt" + cls.workspace = load_fitting_test_file_ascii(filename) def test_good_initial_guess(self): """ @@ -180,13 +177,12 @@ class SineLikeMuonExperimentAsymmetry(unittest.TestCase): Any local minimizer should be very sensitive to the initial guess. """ - filename = "sine_fitting_test_muon_asymmetry.txt" - workspace = None function_template = "name=UserFunction, Formula=sin(w*x), w={0}" - def setUp(self): - if not self.__class__.workspace: - self.__class__.workspace = load_fitting_test_file_ascii(self.filename) + @classmethod + def setUpClass(cls) -> None: + filename = "sine_fitting_test_muon_asymmetry.txt" + cls.workspace = load_fitting_test_file_ascii(filename) def test_bad_initial_guess(self): """ @@ -218,13 +214,12 @@ class VanadiumPatternFromENGINXSmoothing(unittest.TestCase): a spline with 20-50 knots. This is used for calibration. """ - filename = "fitting_test_vanadium_pattern_enginx236516_bank1.txt" - workspace = None spline_user_def_function = "name=BSpline, Uniform=true, Order=3, StartX=0, EndX=5.5, NBreak={0}" - def setUp(self): - if not self.__class__.workspace: - self.__class__.workspace = load_fitting_test_file_ascii(self.filename) + @classmethod + def setUpClass(cls) -> None: + filename = "fitting_test_vanadium_pattern_enginx236516_bank1.txt" + cls.workspace = load_fitting_test_file_ascii(filename) def test_50breaks(self): """ diff --git a/buildconfig/CMake/Packaging/AddPythonPath.py.in b/buildconfig/CMake/Packaging/AddPythonPath.py.in index 7a56dd579341..b71109488e76 100644 --- a/buildconfig/CMake/Packaging/AddPythonPath.py.in +++ b/buildconfig/CMake/Packaging/AddPythonPath.py.in @@ -13,27 +13,31 @@ import traceback mantidpath = "@CMAKE_RUNTIME_OUTPUT_DIRECTORY@" # get a list of all *.egg-link files -eggfiles = [os.path.join(mantidpath, item) - for item in os.listdir(mantidpath) - # skip mantidplot looking files - if item.endswith('.egg-link') and 'plot' not in item.lower()] +eggfiles = [ + os.path.join(mantidpath, item) + for item in os.listdir(mantidpath) + # skip mantidplot looking files + if item.endswith(".egg-link") and "plot" not in item.lower() +] # directories to add are what are in those files pathdirs = [] for filename in eggfiles: with open(filename) as handle: for line in handle.readlines(): line = line.strip() - if (not line) or (line == '.'): # don't add current directory + if (not line) or (line == "."): # don't add current directory continue pathdirs.append(line) pathdirs = list(set(pathdirs)) # get unique directories + def die(msg=None): if msg: print(msg) traceback.print_exc() exit(1) + # modify the running path and check mantid can be loaded in this python pathdirs.insert(0, mantidpath) for directory in pathdirs: @@ -42,40 +46,19 @@ for directory in pathdirs: try: import mantid # noqa except ImportError as e: - # check for an error that appears to be python2 only - if 'No module named DLFCN' == str(e): - print('Looks like "/usr/lib64/python2.7/plat-####/" is missing from sys.path') - # can find platform path by comparing to (in vanilla python) - # python -c "import sys, pprint; pprint.pprint(sys.path)" - found_dlfcn = False - for platform_path in ['/usr/lib64/python2.7/plat-linux2/', - '/usr/lib/python2.7/plat-x86_64-linux-gnu']: - if os.path.exists(platform_path): - print('found "{}" ... adding to system path'.format(platform_path)) - sys.path.append(platform_path) - try: - import DLFCN # noqa - except ImportError: - die('Did not fix import error') - print(' {} ... adding to mantid.pth'.format(' ' * len(platform_path))) - pathdirs.append(platform_path) - found_dlfcn = True - if not found_dlfcn: # missing path wasn't found - die() - else: - die("Can't import mantid: {}".format(e)) + die("Can't import mantid: {}".format(e)) # where path file should go -pathfile = os.path.join(sc.get_python_lib(plat_specific=True), 'mantid.pth') +pathfile = os.path.join(sc.get_python_lib(plat_specific=True), "mantid.pth") if os.path.exists(pathfile): - print('over-writing', pathfile, 'with', pathdirs) + print("over-writing", pathfile, "with", pathdirs) else: - print('writing', pathdirs, 'to', pathfile) -with open(pathfile, 'w') as f: + print("writing", pathdirs, "to", pathfile) +with open(pathfile, "w") as f: for directory in pathdirs: # check that trailing `/` is there if not directory.endswith(os.sep): directory += os.sep f.write(directory) - f.write('\n') \ No newline at end of file + f.write("\n") diff --git a/dev-docs/source/DebuggingUnitTests.rst b/dev-docs/source/DebuggingUnitTests.rst index c10222dac225..feac981c3676 100644 --- a/dev-docs/source/DebuggingUnitTests.rst +++ b/dev-docs/source/DebuggingUnitTests.rst @@ -26,7 +26,7 @@ below. If the issue is with a python unit test, the call is slightly more complicated:: - $ env PYTHONPATH=$PWD/bin gdb --args python2 /full/path/to/mantid/Framework/PythonInterface/test/python/mantid/kernel/TimeSeriesPropertyTest.py + $ env PYTHONPATH=$PWD/bin gdb --args python /full/path/to/mantid/Framework/PythonInterface/test/python/mantid/kernel/TimeSeriesPropertyTest.py (gdb) run Within Eclipse diff --git a/dev-docs/source/Standards/RenameAlgorithm.rst b/dev-docs/source/Standards/RenameAlgorithm.rst index 647a2d54b70f..896a23ed1f22 100644 --- a/dev-docs/source/Standards/RenameAlgorithm.rst +++ b/dev-docs/source/Standards/RenameAlgorithm.rst @@ -69,7 +69,7 @@ Renaming a C++ algorithm can be achieved via the following steps: .. code-block:: bash - python2.7 move_class.py DataHandling OldAlgName DataHandling NewAlgName + python move_class.py DataHandling OldAlgName DataHandling NewAlgName Rename Python Algorithm diff --git a/qt/python/mantidqt/mantidqt/widgets/codeeditor/interpreter.py b/qt/python/mantidqt/mantidqt/widgets/codeeditor/interpreter.py index e356b56b0ce1..7247f195c585 100644 --- a/qt/python/mantidqt/mantidqt/widgets/codeeditor/interpreter.py +++ b/qt/python/mantidqt/mantidqt/widgets/codeeditor/interpreter.py @@ -389,13 +389,8 @@ def _on_exec_error(self, task_error): try: if exc_stack[0].lineno is not None: lineno = exc_stack[0].lineno + self._code_start_offset - except (AttributeError, IndexError): - # Python 2 fallback - try: - if exc_stack[-1][1] is not None: # Ensure the last stack trace entry has a valid lineno - lineno = exc_stack[-1][1] + self._code_start_offset - except IndexError: - pass + except IndexError: + pass sys.stderr.write(self._error_formatter.format(exc_type, exc_value, exc_stack) + os.linesep) self.view.editor.updateProgressMarker(lineno, True) diff --git a/qt/python/mantidqtinterfaces/mantidqtinterfaces/Muon/GUI/Common/muon_load_data.py b/qt/python/mantidqtinterfaces/mantidqtinterfaces/Muon/GUI/Common/muon_load_data.py index fccaace9e492..ad3963b7e40c 100644 --- a/qt/python/mantidqtinterfaces/mantidqtinterfaces/Muon/GUI/Common/muon_load_data.py +++ b/qt/python/mantidqtinterfaces/mantidqtinterfaces/Muon/GUI/Common/muon_load_data.py @@ -48,9 +48,6 @@ def __next__(self): else: raise StopIteration - # for Python 2/3 compatibility - next = __next__ - # Getters def num_items(self): diff --git a/qt/python/mantidqtinterfaces/mantidqtinterfaces/Muon/GUI/Common/utilities/muon_test_helpers.py b/qt/python/mantidqtinterfaces/mantidqtinterfaces/Muon/GUI/Common/utilities/muon_test_helpers.py index 474480dcf1ae..34b9d2f55af4 100644 --- a/qt/python/mantidqtinterfaces/mantidqtinterfaces/Muon/GUI/Common/utilities/muon_test_helpers.py +++ b/qt/python/mantidqtinterfaces/mantidqtinterfaces/Muon/GUI/Common/utilities/muon_test_helpers.py @@ -29,6 +29,3 @@ def __next__(self): else: self.n += 1 return next(self.iterable) - - # python 3 compatibility - next = __next__ diff --git a/qt/python/mantidqtinterfaces/test/Muon/LoadWidgetPresenter_test.py b/qt/python/mantidqtinterfaces/test/Muon/LoadWidgetPresenter_test.py index 619642054285..b5dd485a781c 100644 --- a/qt/python/mantidqtinterfaces/test/Muon/LoadWidgetPresenter_test.py +++ b/qt/python/mantidqtinterfaces/test/Muon/LoadWidgetPresenter_test.py @@ -32,12 +32,7 @@ def test_equalise_last_loaded_run_empty(self): self.assertEqual(self.presenter._current_run, None) def test_equalise_last_loaded_run_data(self): - # need to add each in tern for Python 2 - runs = OrderedDict() - runs[1] = [] - runs[2] = [] - runs[5] = [] - runs[3] = [] + runs = OrderedDict({1: [], 2: [], 5: [], 3: []}) self.presenter.co_model.loaded_runs = runs self.presenter.load_model.loaded_runs = runs self.presenter.equalise_last_loaded_run(runs) diff --git a/qt/python/mantidqtinterfaces/test/Muon/load_file_widget/loadfile_presenter_multiple_file_test.py b/qt/python/mantidqtinterfaces/test/Muon/load_file_widget/loadfile_presenter_multiple_file_test.py index ae2bb698b89b..07d336e1618a 100644 --- a/qt/python/mantidqtinterfaces/test/Muon/load_file_widget/loadfile_presenter_multiple_file_test.py +++ b/qt/python/mantidqtinterfaces/test/Muon/load_file_widget/loadfile_presenter_multiple_file_test.py @@ -40,8 +40,6 @@ def __next__(self): self.n += 1 return next(self.iterable) - next = __next__ - @start_qapplication class LoadFileWidgetPresenterMultipleFileModeTest(unittest.TestCase): diff --git a/qt/python/mantidqtinterfaces/test/TOFTOF/TOFTOFScriptElementTest.py b/qt/python/mantidqtinterfaces/test/TOFTOF/TOFTOFScriptElementTest.py index c9ba406d115f..231931f68eda 100644 --- a/qt/python/mantidqtinterfaces/test/TOFTOF/TOFTOFScriptElementTest.py +++ b/qt/python/mantidqtinterfaces/test/TOFTOF/TOFTOFScriptElementTest.py @@ -142,14 +142,10 @@ def test_that_inputs_saved_and_loaded_correctly(self): if not name.startswith("__") and not hasattr(getattr(self.scriptElement, name), "__call__"): self.assertEqual(getattr(self.scriptElement, name), getattr(scriptElement2, name)) - # due to an issue with 'exec()' in functtions with nested functions in python 2.7 (2.7.8 and earlier), - # this function has to stay outside of 'test_that_script_is_executable_in_mantid()'. - # see https://bugs.python.org/issue21591 - # cannot be a @staticmethod for the same reasons - def execScript(self, script): - exec(script, dict(), dict()) - def test_that_script_is_executable_in_mantid(self): + def exec_script(script): + exec(script, dict(), dict()) + # data files are here self.scriptElement.dataDir = "" @@ -177,9 +173,7 @@ def test_that_script_is_executable_in_mantid(self): self.scriptElement.createDiff = True self.scriptElement.keepSteps = True - testhelpers.assertRaisesNothing( - self, self.execScript, self.scriptElement.to_script().replace("import matplotlib.pyplot as plt", "") - ) + testhelpers.assertRaisesNothing(self, exec_script, self.scriptElement.to_script().replace("import matplotlib.pyplot as plt", "")) def test_that_script_has_correct_syntax(self): self.scriptElement.binEon = False diff --git a/scripts/Inelastic/Direct/PropertiesDescriptors.py b/scripts/Inelastic/Direct/PropertiesDescriptors.py index 3b3a248cf2bc..85d5071b55a6 100644 --- a/scripts/Inelastic/Direct/PropertiesDescriptors.py +++ b/scripts/Inelastic/Direct/PropertiesDescriptors.py @@ -285,11 +285,7 @@ def get_current(self): # - # Python 3 compatibility def __next__(self): - return self.next() - - def next(self): if isinstance(self._incident_energy, list): self._cur_iter_en += 1 if self._cur_iter_en >= len(self._incident_energy): diff --git a/scripts/SANS/SANSBatchMode.py b/scripts/SANS/SANSBatchMode.py index ffdb9438976b..97d25a8c6521 100644 --- a/scripts/SANS/SANSBatchMode.py +++ b/scripts/SANS/SANSBatchMode.py @@ -305,11 +305,9 @@ def BatchReduce( # noqa: C901 continue except ValueError as reason: issueWarning("Cannot load file :" + str(reason)) - # when we are all up to Python 2.5 replace the duplicated code below with one finally: - delete_workspaces(raw_workspaces) raise - - delete_workspaces(raw_workspaces) + finally: + delete_workspaces(raw_workspaces) if verbose: sanslog.notice(createColetteScript(run, format, reduced, centreit, plotresults, filename)) diff --git a/scripts/SANS/isis_instrument.py b/scripts/SANS/isis_instrument.py index 552fb779d3fe..4f8997712486 100644 --- a/scripts/SANS/isis_instrument.py +++ b/scripts/SANS/isis_instrument.py @@ -1340,7 +1340,7 @@ def _get_const_num(self, log_data, log_name): # return the log value if it stored as a single number return float(log_data.getLogData(log_name).value) except TypeError: - # Python 2.4 doesn't have datetime.strptime... + def format_date(date_string, format, date_str_len): if len(date_string) > date_str_len: date_string = date_string[:date_str_len] @@ -1574,7 +1574,7 @@ def _get_const_num(self, log_data, log_name): # return the log value if it stored as a single number return float(log_data.getLogData(log_name).value) except TypeError: - # Python 2.4 doesn't have datetime.strptime... + def format_date(date_string, format, date_str_len): if len(date_string) > date_str_len: date_string = date_string[:date_str_len] diff --git a/scripts/SANS/isis_reduction_steps.py b/scripts/SANS/isis_reduction_steps.py index f554dfa1c1c3..dec28f260514 100644 --- a/scripts/SANS/isis_reduction_steps.py +++ b/scripts/SANS/isis_reduction_steps.py @@ -2907,11 +2907,9 @@ def execute(self, reducer, workspace): else: raise NotImplementedError("The type of Q reduction has not been set, e.g. 1D or 2D") except: - # when we are all up to Python 2.5 replace the duplicated code below with one finally: - reducer.deleteWorkspaces([wave_adj, pixel_adj, wavepixeladj]) raise - - reducer.deleteWorkspaces([wave_adj, pixel_adj, wavepixeladj]) + finally: + reducer.deleteWorkspaces([wave_adj, pixel_adj, wavepixeladj]) def _get_q_resolution_workspace(self, det_bank_workspace): """ @@ -4207,24 +4205,6 @@ class StripEndNans(ReductionStep): def __init__(self): super(StripEndNans, self).__init__() - def _isNan(self, val): - """ - Can replaced by isNaN in Python 2.6 - @param val: float to check - """ - if val != val: - return True - else: - return False - - def _isInf(self, val): - """ - Check if the value is inf or not - @param val: float to check - @returns true if value is inf - """ - return math.isinf(val) - def execute(self, reducer, workspace): """ Trips leading and trailing Nan values from workspace @@ -4241,14 +4221,14 @@ def execute(self, reducer, workspace): # Find the first non-zero value start = 0 for i in range(0, length): - if not self._isNan(y_vals[i]) and not self._isInf(y_vals[i]): + if not math.isnan(y_vals[i]) and not math.isinf(y_vals[i]): start = i break # Now find the last non-zero value stop = 0 length -= 1 for j in range(length, 0, -1): - if not self._isNan(y_vals[j]) and not self._isInf(y_vals[j]): + if not math.isnan(y_vals[j]) and not math.isinf(y_vals[j]): stop = j break # Find the appropriate X values and call CropWorkspace diff --git a/scripts/SANS/sans/gui_logic/gui_common.py b/scripts/SANS/sans/gui_logic/gui_common.py index 5de46130193b..5506fa35e1cc 100644 --- a/scripts/SANS/sans/gui_logic/gui_common.py +++ b/scripts/SANS/sans/gui_logic/gui_common.py @@ -201,10 +201,9 @@ def get_reduction_mode_from_gui_selection(gui_selection: str): def get_detector_from_gui_selection(gui_selection): - # TODO when we hit only Python 3 this should use casefold rather than lower - case_folded_selection = gui_selection.lower() + case_folded_selection = gui_selection.casefold() - if any(case_folded_selection == hab.lower() for hab in HAB_STRINGS.values()): + if any(case_folded_selection == hab.casefold() for hab in HAB_STRINGS.values()): return DetectorType.HAB else: return DetectorType.LAB diff --git a/scripts/abins/input/crystalloader.py b/scripts/abins/input/crystalloader.py index c340379c8b02..02917a1109f4 100644 --- a/scripts/abins/input/crystalloader.py +++ b/scripts/abins/input/crystalloader.py @@ -172,10 +172,7 @@ def _read_atomic_coordinates(self, file_obj=None): coord_lines += [line.strip(b"\n")] for line in coord_lines: - # convert from unicode to str in case of Python 2 - temp = str(line.strip(b"\n")) - - logger.debug(temp) + logger.debug(line) return coord_lines diff --git a/scripts/abins/io.py b/scripts/abins/io.py index d68ac7b749a7..cc9cbc2979a0 100644 --- a/scripts/abins/io.py +++ b/scripts/abins/io.py @@ -397,20 +397,6 @@ def _get_subgrp_name(path): end = reversed_path.find("/") return reversed_path[:end] - @staticmethod - def _encode_utf8_if_text(item): - """ - Convert atom element from unicode to str - but only in Python 2 where unicode handling is a mess - - :param item: item to convert to unicode str if Python 2 str - :returns: laundered item - """ - if isinstance(item, str): - return item.encode("utf-8") - else: - return item - @validate_call(config=ConfigDict(arbitrary_types_allowed=True)) def _load_dataset(self, *, hdf_file: h5py.File, name: str, group: h5py.Group): """ diff --git a/scripts/reduction/instruments/sans/sans_reduction_steps.py b/scripts/reduction/instruments/sans/sans_reduction_steps.py index e8f845be3d44..504a4df95b7b 100644 --- a/scripts/reduction/instruments/sans/sans_reduction_steps.py +++ b/scripts/reduction/instruments/sans/sans_reduction_steps.py @@ -664,11 +664,9 @@ def execute(self, reducer, workspace): else: raise NotImplementedError("The type of Q reduction has not been set, e.g. 1D or 2D") except: - # when we are all up to Python 2.5 replace the duplicated code below with one finally: - self._deleteWorkspaces([wave_adj, pixel_adj]) raise - - self._deleteWorkspaces([wave_adj, pixel_adj]) + finally: + self._deleteWorkspaces([wave_adj, pixel_adj]) def _deleteWorkspaces(self, workspaces): """ @@ -937,16 +935,6 @@ class StripEndNans(ReductionStep): def __init__(self): super(StripEndNans, self).__init__() - def _isNan(self, val): - """ - Can replaced by isNaN in Python 2.6 - @param val: float to check - """ - if val != val: - return True - else: - return False - def execute(self, reducer, workspace): """ Trips leading and trailing Nan values from workspace @@ -963,14 +951,14 @@ def execute(self, reducer, workspace): # Find the first non-zero value start = 0 for i in range(0, length): - if not self._isNan(y_vals[i]): + if not math.isnan(y_vals[i]): start = i break # Now find the last non-zero value stop = 0 length -= 1 for j in range(length, 0, -1): - if not self._isNan(y_vals[j]): + if not math.isnan(y_vals[j]): stop = j break # Find the appropriate X values and call CropWorkspace diff --git a/scripts/reduction_gui/reduction/scripter.py b/scripts/reduction_gui/reduction/scripter.py index df16a5eee634..2fa6b3657152 100644 --- a/scripts/reduction_gui/reduction/scripter.py +++ b/scripts/reduction_gui/reduction/scripter.py @@ -310,6 +310,23 @@ def execute_script(script, error_cb=None): @param script :: A chunk of code to execute @param error_cb :: method to call on error, only registered in workbench """ + + def execute_script_async(script, error_cb=None): + # this is intentionally imported lazily to avoid a Qt dependency + # in the framework + from mantidqt.widgets.codeeditor.execution import PythonCodeExecution + + def on_error(arg): + Logger("scripter").error("Failed to execute script: {}".format(arg)) + + executioner = PythonCodeExecution() + if error_cb is None: + executioner.sig_exec_error.connect(on_error) + else: + executioner.sig_exec_error.connect(error_cb) + + executioner.execute_async(script, 0) + if SCRIPT_EXEC_METHOD == "async": Logger("scripter").information("using PythonCodeExecution") execute_script_async(script, error_cb) @@ -321,27 +338,6 @@ def execute_script(script, error_cb=None): exec(script, globals(), locals()) -def execute_script_async(script, error_cb=None): - # Cannot define this code in execute_script as Python 2.7.5 generates an error: - # SyntaxError: unqualified exec is not allowed in function 'execute_script' it - # contains a nested function with free variables - - # this is intentionally imported lazily to avoid a Qt dependency - # in the framework - from mantidqt.widgets.codeeditor.execution import PythonCodeExecution - - def on_error(arg): - Logger("scripter").error("Failed to execute script: {}".format(arg)) - - executioner = PythonCodeExecution() - if error_cb is None: - executioner.sig_exec_error.connect(on_error) - else: - executioner.sig_exec_error.connect(error_cb) - - executioner.execute_async(script, 0) - - class BaseReductionScripter(object): """ Organizes the set of reduction parameters that will be used to @@ -573,12 +569,10 @@ def apply(self): script = self.to_script(None) try: self.execute_script(script) - # Update scripter - for item in self._observers: - if item.state() is not None: - item.state().update() except: - # Update scripter [Duplicated code because we can't use 'finally' on python 2.4] + raise + finally: + # Update scripter for item in self._observers: if item.state() is not None: # Things might be broken, so update what we can @@ -586,7 +580,6 @@ def apply(self): item.state().update() except (StopIteration, ImportError, NameError, TypeError, ValueError, Warning): pass - raise # rethrow the exception else: raise RuntimeError("Reduction could not be executed: Mantid could not be imported") diff --git a/scripts/test/DirectPropertyManagerTest.py b/scripts/test/DirectPropertyManagerTest.py index d919eb55c72e..6666a9c55eca 100644 --- a/scripts/test/DirectPropertyManagerTest.py +++ b/scripts/test/DirectPropertyManagerTest.py @@ -986,12 +986,12 @@ def test_mon2_integration_range(self): self.assertAlmostEqual(range[0], 9.5) self.assertAlmostEqual(range[1], 10.5) - PropertyManager.incident_energy.next() + next(PropertyManager.incident_energy) range = propman.mon2_norm_energy_range self.assertAlmostEqual(range[0], 2 * 9.5) self.assertAlmostEqual(range[1], 2 * 10.5) - PropertyManager.incident_energy.next() + next(PropertyManager.incident_energy) range = propman.mon2_norm_energy_range self.assertAlmostEqual(range[0], 3 * 9.5) self.assertAlmostEqual(range[1], 3 * 10.5) @@ -1027,7 +1027,7 @@ def test_mono_correction_factor(self): PropertyManager.mono_correction_factor.set_val_to_cash(propman, 100) self.assertAlmostEqual(PropertyManager.mono_correction_factor.get_val_from_cash(propman), 100) - PropertyManager.incident_energy.next() + next(PropertyManager.incident_energy) self.assertEqual(PropertyManager.mono_correction_factor.get_val_from_cash(propman), None) PropertyManager.mono_correction_factor.set_val_to_cash(propman, 50) self.assertAlmostEqual(PropertyManager.mono_correction_factor.get_val_from_cash(propman), 50) diff --git a/scripts/test/ReductionWrapperTest.py b/scripts/test/ReductionWrapperTest.py index a0e409e13bdb..58fa2b479537 100644 --- a/scripts/test/ReductionWrapperTest.py +++ b/scripts/test/ReductionWrapperTest.py @@ -231,7 +231,7 @@ def test_custom_print_name(self): # generated by reduction self.assertEqual(save_file, "SOMETHINGSR_MAR000100#2_reduced_10.01meV_rings") - PropertyManager.incident_energy.next() + next(PropertyManager.incident_energy) save_file = th.reducer.prop_man.save_file_name # now reduction have not been run, and the name is generated from run number self.assertEqual(save_file, "SOMETHINGSR_MAR000100#2_reduced_20.00meV_rings") From cafd31285e390838739330a1a8204264bda432f8 Mon Sep 17 00:00:00 2001 From: Jonathan Haigh Date: Fri, 20 Sep 2024 09:49:44 +0100 Subject: [PATCH 2/4] update simpleapi to use Signature objects --- .../mantid/kernel/funcinspect.py | 78 +++---------------- Framework/PythonInterface/mantid/simpleapi.py | 25 +----- 2 files changed, 13 insertions(+), 90 deletions(-) diff --git a/Framework/PythonInterface/mantid/kernel/funcinspect.py b/Framework/PythonInterface/mantid/kernel/funcinspect.py index 9d5d07bfd94f..9a424746b8a3 100644 --- a/Framework/PythonInterface/mantid/kernel/funcinspect.py +++ b/Framework/PythonInterface/mantid/kernel/funcinspect.py @@ -17,69 +17,6 @@ import dis -def replace_signature(func, signature): - """ - Replace the signature of the given function object with that given by - varnames - :param func: Function whose signature is to be replaced - :param signature: A tuple of names of arguments and local variables in Python 2 - or a Signature object in Python 3 - """ - if hasattr(signature, "parameters"): - # A new Signature object - setattr(func, "__signature__", signature) - else: - # Drop this code when we dro Python 2 support - # Code object is different in Python 3 - if hasattr(func, "func_code"): - # Version 2 - code_attr = "func_code" - f = func.func_code - c = f.__new__( - f.__class__, - f.co_argcount, - f.co_nlocals, - f.co_stacksize, - f.co_flags, - f.co_code, - f.co_consts, - f.co_names, - signature, - f.co_filename, - f.co_name, - f.co_firstlineno, - f.co_lnotab, - f.co_freevars, - ) - else: - code_attr = "__code__" - f = func.__code__ - new_args = [ - f.__class__, - f.co_argcount, - f.co_kwonlyargcount, - f.co_nlocals, - f.co_stacksize, - f.co_flags, - f.co_code, - f.co_consts, - f.co_names, - signature, - f.co_filename, - f.co_name, - f.co_firstlineno, - f.co_lnotab, - f.co_freevars, - ] - # Python 3.8 supports positional-only arguments and has an extra - # keyword in the constructor - if hasattr(f, "co_posonlyargcount"): - new_args.insert(2, f.co_posonlyargcount) - c = f.__new__(*new_args) - # endif - setattr(func, code_attr, c) - - def customise_func(func, name, signature, docstring): """ Takes the definition of the algorithm function and replaces @@ -87,8 +24,7 @@ def customise_func(func, name, signature, docstring): function definition :param func: A function object holding the definition :param name: The name of the algorithm - :param signature: A new signature for the function. Expecting a 2-tuple - of arguments and local variables. See _replace_signature + :param signature: A new signature for the function :param docstring: A string containing the function documentation """ func.__name__ = str(name) @@ -106,7 +42,7 @@ class LazyFunctionSignature(inspect.Signature): to reduce the time spent initialising algorithms. """ - __slots__ = ("_alg_name", "__sig") + __slots__ = ("_alg_name", "__sig", "_include_self") def __init__(self, *args, **kwargs): if "alg_name" not in kwargs: @@ -116,6 +52,11 @@ def __init__(self, *args, **kwargs): self._alg_name = kwargs.pop("alg_name") self.__sig = None + if "include_self" in kwargs: + self._include_self = kwargs.pop("include_self") + else: + self._include_self = True + @property def _signature(self): if self.__sig is None: @@ -157,8 +98,9 @@ def _create_parameters(self, alg_name): # None is not quite accurate here, but we are reproducing the # behavior found in the C++ code for SimpleAPI. parameters.append(Parameter(name, pos_or_keyword, default=None)) - # Add a self parameter since these are called from a class. - parameters.insert(0, Parameter("self", Parameter.POSITIONAL_ONLY)) + if self._include_self: + # Add a self parameter since these are called from a class. + parameters.insert(0, Parameter("self", Parameter.POSITIONAL_ONLY)) return parameters diff --git a/Framework/PythonInterface/mantid/simpleapi.py b/Framework/PythonInterface/mantid/simpleapi.py index c9ae96b3216e..3e54097efb0b 100644 --- a/Framework/PythonInterface/mantid/simpleapi.py +++ b/Framework/PythonInterface/mantid/simpleapi.py @@ -41,11 +41,9 @@ from mantid import api as _api, kernel as _kernel from mantid import apiVersion # noqa: F401 from mantid.kernel import plugins as _plugin_helper -from mantid.kernel import ConfigService, logger from mantid.kernel.funcinspect import ( customise_func as _customise_func, lhs_info as _lhs_info, - replace_signature as _replace_signature, LazyFunctionSignature, ) @@ -321,7 +319,7 @@ def wrapper(*args, **kwargs): # end function_name = f.__name__ - signature = ("\bFunction, InputWorkspace", "**kwargs") + signature = LazyFunctionSignature(alg_name=function_name, include_self=False) fwrapper = _customise_func(wrapper, function_name, signature, f.__doc__) if function_name not in __SPECIALIZED_FUNCTIONS__: __SPECIALIZED_FUNCTIONS__.append(function_name) @@ -527,7 +525,7 @@ def CutMD(*args, **kwargs): # noqa: C901 return out_names[0] -_replace_signature(CutMD, ("\bInputWorkspace", "**kwargs")) +CutMD.__signature__ = LazyFunctionSignature(alg_name="CutMD", include_self=False) def RenameWorkspace(*args, **kwargs): @@ -572,7 +570,7 @@ def RenameWorkspace(*args, **kwargs): return _gather_returns("RenameWorkspace", lhs, algm) -_replace_signature(RenameWorkspace, ("\bInputWorkspace,[OutputWorkspace],[True||False]", "**kwargs")) +RenameWorkspace.__signature__ = LazyFunctionSignature(alg_name="RenameWorkspace", include_self=False) def _get_function_spec(func): @@ -1179,23 +1177,6 @@ def get_self(frame_arg): # ---------------------------------------------------------------------------------------------------------------------- -def _create_fake_function(name): - """Create fake functions for the given name""" - - # ------------------------------------------------------------------------------------------------ - def fake_function(*args, **kwargs): - raise RuntimeError( - "Mantid import error. The mock simple API functions have not been replaced!" - " This is an error in the core setup logic of the mantid module, " - "please contact the development team." - ) - - # ------------------------------------------------------------------------------------------------ - fake_function.__name__ = name - _replace_signature(fake_function, ("", "")) - globals()[name] = fake_function - - def _mockup(plugins): """ Creates fake, error-raising functions for any plugins given. From 3e8a9f41c42b65c4d309d7eb2b42bae9ef5f8a36 Mon Sep 17 00:00:00 2001 From: Jonathan Haigh <35813666+jhaigh0@users.noreply.github.com> Date: Tue, 8 Oct 2024 09:45:27 +0100 Subject: [PATCH 3/4] Explicitly include boost header Co-authored-by: James Clarke <139879523+jclarkeSTFC@users.noreply.github.com> --- .../mantid/api/src/Exports/SpectrumInfoPythonIterator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoPythonIterator.cpp b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoPythonIterator.cpp index ad91229ec8b4..1853a4e52f53 100644 --- a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoPythonIterator.cpp +++ b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoPythonIterator.cpp @@ -9,6 +9,7 @@ #include #include #include +#include using Mantid::PythonInterface::SpectrumInfoPythonIterator; using namespace boost::python; From 36656b4300b7dc1b0248d6f7792b7d4ae8fe689f Mon Sep 17 00:00:00 2001 From: Jonathan Haigh <35813666+jhaigh0@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:46:50 +0000 Subject: [PATCH 4/4] use f-string instead of .format Co-authored-by: thomashampson --- buildconfig/CMake/Packaging/AddPythonPath.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildconfig/CMake/Packaging/AddPythonPath.py.in b/buildconfig/CMake/Packaging/AddPythonPath.py.in index 0e80110cd27d..b8c322a5adea 100644 --- a/buildconfig/CMake/Packaging/AddPythonPath.py.in +++ b/buildconfig/CMake/Packaging/AddPythonPath.py.in @@ -46,7 +46,7 @@ for directory in pathdirs: try: import mantid # noqa except ImportError as e: - die("Can't import mantid: {}".format(e)) + die(f"Can't import mantid: {e}") # where path file should go