Skip to content

Commit

Permalink
Merge pull request #38048 from mantidproject/remove_python_2
Browse files Browse the repository at this point in the history
Remove (most of) the code intended for python 2
thomashampson authored Oct 29, 2024
2 parents 8462417 + 36656b4 commit 48e9b01
Showing 33 changed files with 98 additions and 342 deletions.
3 changes: 2 additions & 1 deletion Framework/PythonInterface/mantid/api/_adsimports.py
Original file line number Diff line number Diff line change
@@ -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


Original file line number Diff line number Diff line change
@@ -7,9 +7,9 @@
#include "MantidPythonInterface/api/SpectrumInfoPythonIterator.h"

#include <boost/python/class.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/python/iterator.hpp>
#include <boost/python/module.hpp>
#include <boost/python/reference_existing_object.hpp>

using Mantid::PythonInterface::SpectrumInfoPythonIterator;
using namespace boost::python;
@@ -20,14 +20,5 @@ void export_SpectrumInfoPythonIterator() {
// Export to Python
class_<SpectrumInfoPythonIterator>("SpectrumInfoPythonIterator", no_init)
.def("__iter__", objects::identity_function())
.def("__next__", &SpectrumInfoPythonIterator::next, return_value_policy<copy_const_reference>());
/*
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<reference_existing_object>());
}
78 changes: 10 additions & 68 deletions Framework/PythonInterface/mantid/kernel/funcinspect.py
Original file line number Diff line number Diff line change
@@ -17,78 +17,14 @@
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
the attributes of the instance to make it look like a handwritten
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


25 changes: 3 additions & 22 deletions Framework/PythonInterface/mantid/simpleapi.py
Original file line number Diff line number Diff line change
@@ -42,11 +42,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,
)

@@ -322,7 +320,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)
@@ -528,7 +526,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):
@@ -573,7 +571,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):
@@ -1180,23 +1178,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.
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -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:
#
Original file line number Diff line number Diff line change
@@ -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()
9 changes: 1 addition & 8 deletions Framework/PythonInterface/plugins/algorithms/LoadEXED.py
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@
import os
import re
from collections import defaultdict
from string import Formatter

from mantid.api import mtd, AlgorithmFactory, FileAction, FileProperty, PythonAlgorithm, WorkspaceGroupProperty
from mantid.kernel import logger, Direction, IntArrayProperty
@@ -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 = {}
Original file line number Diff line number Diff line change
@@ -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)

Loading

0 comments on commit 48e9b01

Please sign in to comment.