Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Framework/PythonInterface/mantid/api/_adsimports.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Up @@ -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)
Expand All @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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


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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know why some algorithms need special treatment? E.g. CutMD and RenameWorkspace



def RenameWorkspace(*args, **kwargs):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
9 changes: 1 addition & 8 deletions Framework/PythonInterface/plugins/algorithms/LoadEXED.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import os
import re
from collections import defaultdict
from string import Formatter

from mantid.api import *
from mantid.kernel import *
Expand Down Expand Up @@ -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 = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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")
Expand All @@ -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")
Expand Down Expand Up @@ -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)

Expand Down
Loading
Loading