Utility Functions¶
New in version 3.3.
This is a collection of utility functions and classes that are used by the framework but can also be useful when writing regression tests. Functions or classes marked as draft should be used with caution, since they might change or be replaced without a deprecation warning.
General Utilities¶
- class reframe.utility.MappingView(mapping)[source]¶
Bases:
Mapping
A read-only view of a mapping.
See
collections.abc.Mapping
for a list of supported of operations.
- class reframe.utility.OrderedSet(*args)[source]¶
Bases:
MutableSet
An ordered set.
This container behaves like a normal set but remembers the insertion order of its elements. It can also inter-operate with standard Python sets.
Operations between ordered sets respect the order of the elements of the operands. For example, if
x
andy
are both ordered sets, thenx | y
will be a new ordered set with the (unique) elements ofx
andy
in the order they appear inx
andy
. The same holds for all the other set operations.
- class reframe.utility.ScopedDict(mapping={}, scope_sep=':', global_scope='*')[source]¶
Bases:
UserDict
This is a special dictionary that imposes scopes on its keys.
When a key is not found, it will be searched up in the scope hierarchy. If not found even at the global scope, a
KeyError
will be raised.A scoped dictionary is initialized using a two-level normal dictionary that defines the different scopes and the keys inside them. Scopes can be nested by concatenating them using the
:
separator by default:scope:subscope
. Below is an example of a scoped dictionary that also demonstrates key lookup:d = ScopedDict({ 'a': {'k1': 1, 'k2': 2}, 'a:b': {'k1': 3, 'k3': 4}, '*': {'k1': 7, 'k3': 9, 'k4': 10} }) assert d['a:k1'] == 1 # resolved in the scope 'a' assert d['a:k3'] == 9 # resolved in the global scope assert d['a:b:k1'] == 3 # resolved in the scope 'a:b' assert d['a:b:k2'] == 2 # resolved in the scope 'a' assert d['a:b:k4'] == 10 # resolved in the global scope d['a:k5'] # KeyError d['*:k2'] # KeyError
If no scope is specified in the key lookup, the global scope is assumed. For example,
d['k1']
will return7
. The syntaxesd[':k1']
andd['*:k1']
are all equivalent. If you try to retrieve a whole scope, e.g.,d['a:b']
,KeyError
will be raised. For retrieving scopes, you should use thescope()
function.Key deletion follows the same resolution mechanism as key retrieval, except that you are allowed to delete whole scopes. For example,
del d['*']
will delete the global scope, such that subsequent access ofd['a:k3']
will raise aKeyError
. If a key specification matches both a key and scope, the key will be deleted and not the scope.- Parameters
mapping –
A two-level mapping of the form
{ scope1: {k1: v1, k2: v2}, scope2: {k1: v1, k3: v3} }
Both the scope keys and the actual dictionary keys must be strings, otherwise a
TypeError
will be raised.scope_sep – A character that separates the scopes.
global_scope – A key that represents the global scope.
- property global_scope_mark¶
The key representing the global scope of this dictionary.
- scope(name)[source]¶
Retrieve a whole scope.
- Parameters
scope – The name of the scope to retrieve.
- Returns
A dictionary with the keys that are within the requested scope.
- property scope_separator¶
The scope separator of this dictionary.
- class reframe.utility.SequenceView(container)[source]¶
Bases:
Sequence
A read-only view of a sequence.
See
collections.abc.Sequence
for a list of supported of operations.- Parameters
container – The container to create a view on.
- Raises
TypeError – If the container does not fulfill the
collections.abc.Sequence
interface.
Note
You can concatenate a
SequenceView
with a container of the same type as the underlying container of the view, in which case a new container with the concatenated elements will be returned.- count(value)[source]¶
Count occurrences of
value
in the container.- Parameters
value – The value to search for.
- Returns
The number of occurrences.
- index(value, start=0, stop=None)[source]¶
Return the first index of
value
.- Parameters
value – The value to search for.
start – The position where the search starts.
stop – The position where the search stops. The element at this position is not looked at. If
None
, this equals to the sequence’s length.
- Returns
The index of the first element found that equals
value
.- Raises
ValueError – if the value is not present.
- reframe.utility.allx(iterable)[source]¶
Same as the built-in
all()
, except that it returnsFalse
ifiterable
is empty.
- reframe.utility.attr_validator(validate_fn)[source]¶
Validate object attributes recursively.
This returns a function which you can call with the object to check. It will return
True
if thevalidate_fn()
returnsTrue
for all object attributes recursively. If the object to be validated is an iterable, its elements will be validated individually.- Parameters
validate_fn – A callable that validates an object. It takes a single argument, which is the object to validate.
- Returns
A validation function that will perform the actual validation. It accepts a single argument, which is the object to validate. It returns a two-element tuple, containing the result of the validation as a boolean and a formatted string indicating the faulty attribute.
Note
Objects defining
__slots__
are passed directly to thevalidate_fn
function.New in version 3.3.
- reframe.utility.cache_return_value(fn)[source]¶
Decorator that caches the return value of the decorated function.
The function will only be called once and then the cached value will be returned each time.
- reframe.utility.decamelize(s, delim='_')[source]¶
Decamelize a string.
For example,
MyBaseClass
will be converted tomy_base_class
. The delimiter may be changed by setting thedelim
argument.- Parameters
s – A string in camel notation.
delim – The delimiter that will be used to separate words.
- Returns
The converted string.
- reframe.utility.find_modules(substr, environ_mapping=None)[source]¶
Return all modules in the current system that contain
substr
in their name.This function is a generator and will yield tuples of partition, environment and module combinations for each partition of the current system and for each environment of a partition.
The
environ_mapping
argument allows you to map module name patterns to ReFrame environments. This is useful for flat module name schemes, in order to avoid incompatible combinations of modules and environments.You can use this function to parametrize regression tests over the available environment modules. The following example will generate tests for all the available
netcdf
packages in the system:@rfm.simple_test class MyTest(rfm.RegressionTest): module_info = parameter(find_modules('netcdf')) @run_after('init') def apply_module_info(self): s, e, m = self.module_info self.valid_systems = [s] self.valid_prog_environs = [e] self.modules = [m] ...
The following example shows the use of
environ_mapping
with flat module name schemes. In this example, the toolchain for which the package was built is encoded in the module’s name. Using theenviron_mapping
argument we can map module name patterns to ReFrame environments, so that invalid combinations are pruned:my_find_modules = functools.partial(find_modules, environ_mapping={ r'.*CrayGNU.*': 'PrgEnv-gnu', r'.*CrayIntel.*': 'PrgEnv-intel', r'.*CrayCCE.*': 'PrgEnv-cray' }) @rfm.simple_test class MyTest(rfm.RegressionTest): module_info = parameter(my_find_modules('GROMACS')) @run_after('init') def apply_module_info(self): s, e, m = self.module_info self.valid_systems = [s] self.valid_prog_environs = [e] self.modules = [m] ...
- Parameters
substr – A substring that the returned module names must contain.
environ_mapping – A dictionary mapping regular expressions to environment names.
- Returns
An iterator that iterates over tuples of the module, partition and environment name combinations that were found.
- reframe.utility.import_from_module(module_name, symbol)[source]¶
Import a symbol from module.
- Parameters
module_name – The name of the module from which to import the symbol.
symbol – The symbol to import.
- Returns
The value of the requested symbol.
New in version 4.2.
- reframe.utility.import_module(module_name, force=False)[source]¶
Import a module.
This will not invoke directly the Python import mechanism. It will first derive a path from the module name and will then call
import_module_from_file()
.- Parameters
module_name – The name of the module to load.
force – Force reload of module in case it is already loaded.
- Returns
The loaded Python module.
New in version 4.2.
- reframe.utility.import_module_from_file(filename, force=False)[source]¶
Import module from file.
If the file location refers to a directory, the contained
__init__.py
will be loaded. If the filename resolves to a location that is within the current working directory, a module name will be derived from the supplied file name and Python’simportlib.import_module()
will be invoked to actually load the module. If the file location refers to a path outside the current working directory, then the module will be loaded directly from the file, but it will be assigned a mangled name insys.modules
, to avoid clashes with other modules loaded using the standard import mechanism.- Parameters
filename – The path to the filename of a Python module.
force – Force reload of module in case it is already loaded.
- Returns
The loaded Python module.
- reframe.utility.is_copyable(obj)[source]¶
Check if an object can be copied with
copy.deepcopy()
, without performing the copy.This is a superset of
is_picklable()
. It returnsTrue
also in the following cases:The object defines a
__copy__()
method.The object defines a
__deepcopy__()
method.The object is a function.
The object is a builtin type.
New in version 3.3.
- reframe.utility.is_trivially_callable(fn, *, non_def_args=0)[source]¶
Check that a callable object is trivially callable.
An object is trivially callable when it can be invoked by providing just an expected number of non-default arguments to its call method. For example, (non-static) member functions expect a single argument without a default value, which will passed as
cls
orself
during invocation depending on whether the function is a classmethod or not, respectively. On the other hand, member functions that are static methods are not passed any values by default when invoked. Therefore, these functions can only be trivially callable when their call method expects no arguments by default.
- reframe.utility.longest(*iterables)[source]¶
Return the longest sequence.
This function raises a
TypeError
if any of the iterables is notSized
.- Parameters
iterables – The iterables to check.
- Returns
The longest iterable.
- reframe.utility.nodelist_abbrev(nodes)[source]¶
Create an abbreviated string representation of the node list.
For example, the node list
['nid001', 'nid002', 'nid010', 'nid011', 'nid012', 'nid510', 'nid511']
will be abbreviated as follows:
nid00[1-2],nid0[10-12],nid51[0-1]
New in version 3.5.3.
- Parameters
nodes – The node list to abbreviate.
- Returns
The abbreviated list representation.
- reframe.utility.nodelist_expand(nodespec)[source]¶
Expand the nodes in
nodespec
to a list of nodes.- Parameters
nodespec – A node specification as the one returned by
nodelist_abbrev()
- Returns
The list of nodes corresponding to the given node specification.
New in version 4.0.0.
- reframe.utility.ppretty(value, htchar=' ', lfchar='\n', indent=4, basic_offset=0, repr=<built-in function repr>)[source]¶
Format value in a pretty way.
If value is a container, this function will recursively format the container’s elements.
- Parameters
value – The value to be formatted.
htchar – Horizontal-tab character.
lfchar – Linefeed character.
indent – Number of
htchar
characters for every indentation level.basic_offset – Basic offset for the representation, any additional indentation space is added to the
basic_offset
.repr – A
repr()
-like function that will be used for printing values. This function is allowed to accept all the arguments ofppretty()
except therepr
argument.
- Returns
A formatted string of
value
.
- reframe.utility.repr(obj, htchar=' ', lfchar='\n', indent=4, basic_offset=0)[source]¶
A
repr()
replacement function for debugging purposes printing all object attributes recursively.This function does not follow the standard
repr()
convention, but it prints each object as a set of key/value pairs along with its memory location. It also keeps track of the already visited objects, and abbreviates their representation.- Parameters
obj – The object to be dumped. For the rest of the arguments, see
ppretty()
.- Returns
The formatted object dump.
System Utilities¶
- class reframe.utility.osext.change_dir(dir_name)[source]¶
Bases:
object
Context manager to temporarily change the current working directory.
- Parameters
dir_name – The directory to temporarily change to.
- reframe.utility.osext.concat_files(dst, *files, sep='\n', overwrite=False)[source]¶
Concatenate
files
intodst
.- Parameters
dst – The name of the output file.
files – The files to concatenate.
sep – The separator to use during concatenation.
overwrite – Overwrite the
output
file if it already exists.
- Raises
TypeError – In case
files
it not an iterable object.ValueError – In case
output
already exists and ovewrite isFalse
.
- reframe.utility.osext.copytree(src, dst, symlinks=False, ignore=None, copy_function=<function copy2>, ignore_dangling_symlinks=False, dirs_exist_ok=False)[source]¶
Compatibility version of
shutil.copytree()
for Python < 3.8.This function will automatically delegate to
shutil.copytree()
for Python versions >= 3.8.
- reframe.utility.osext.copytree_virtual(src, dst, file_links=None, symlinks=False, copy_function=<function copy2>, ignore_dangling_symlinks=False, dirs_exist_ok=False)[source]¶
Copy
src
todst
, but create symlinks for the files listed infile_links
.If
file_links
is empty orNone
, this is equivalent tocopytree()
. The rest of the arguments are passed as-is tocopytree()
. Paths infile_links
must be relative tosrc
. If you try to pass'.'
infile_links
, anOSError
will be raised.
- reframe.utility.osext.cray_cdt_version()[source]¶
Return either the Cray Development Toolkit (CDT) version, the Cray Programming Environment (CPE) version or
None
if the version cannot be retrieved.
- reframe.utility.osext.cray_cle_info(filename='/etc/opt/cray/release/cle-release')[source]¶
Return the Cray Linux Environment (CLE) release information.
- Parameters
filename – The file that contains the CLE release information
- Returns
A named tuple with the following attributes that correspond to the release information:
release
,build
,date
,arch
,network
,patchset
.
- reframe.utility.osext.expandvars(s)[source]¶
Expand environment variables in
s
and perform any command substitution.This function is the same as
os.path.expandvars()
, except that it also recognizes the syntax of shell command substitution:$(cmd)
or`cmd`
.
- reframe.utility.osext.follow_link(path)[source]¶
Return the final target of a symlink chain.
If
path
is not a symlink, it will be returned as is.
- reframe.utility.osext.force_remove_file(filename)[source]¶
Remove filename ignoring
FileNotFoundError
.
- reframe.utility.osext.git_clone(url, targetdir=None, opts=None, timeout=5)[source]¶
Clone a git repository from a URL.
- Parameters
url – The URL to clone from.
opts – List of options to be passed to the git clone command
timeout – Timeout in seconds when checking if the url is a valid repository.
targetdir – The directory where the repository will be cloned to. If
None
, a new directory will be created with the repository name as ifgit clone {url}
was issued.
- reframe.utility.osext.git_repo_exists(url, timeout=5)[source]¶
Check if URL refers to a valid Git repository.
- Parameters
url – The URL to check.
timeout – Timeout in seconds.
- Returns
True
if URL is a Git repository,False
otherwise or if timeout is reached.
- reframe.utility.osext.git_repo_hash(commit='HEAD', short=True, wd=None)[source]¶
Return the SHA1 hash of a Git commit.
- Parameters
commit – The commit to look at.
short – Return a short hash. This always corresponds to the first 8 characters of the long hash. We don’t rely on Git for the short hash, since depending on the version it might return either 7 or 8 characters.
wd – Change to this directory before retrieving the hash. If
None
, ReFrame’s install prefix will be used.
- Returns
The Git commit hash or
None
if the hash could not be retrieved.
- reframe.utility.osext.inpath(entry, pathvar)[source]¶
Check if entry is in path.
- Parameters
entry – The entry to look for.
pathvar – A path variable in the form ‘entry1:entry2:entry3’.
- Returns
True
if the entry exists in the path variable,False
otherwise.
- reframe.utility.osext.mkstemp_path(*args, **kwargs)[source]¶
Create a temporary file and return its path.
This is a wrapper to
tempfile.mkstemp()
except that it closes the temporary file as soon as it creates it and returns the path.args
andkwargs
passed through totempfile.mkstemp()
.
- reframe.utility.osext.osgroup()[source]¶
Return the group name of the current OS user.
If the group name cannot be retrieved,
None
will be returned.
- reframe.utility.osext.osuser()[source]¶
Return the name of the current OS user.
If the user name cannot be retrieved,
None
will be returned.
- reframe.utility.osext.rmtree(*args, max_retries=3, **kwargs)[source]¶
Persistent version of
shutil.rmtree()
.If
shutil.rmtree()
fails withENOTEMPTY
orEBUSY
, ignore the error and retry up tomax_retries
times to delete the directory.This version of
rmtree()
is mostly provided to work around a race condition between whensacct
reports a job as completed and when the Slurm epilog runs. See gh #291 for more information. Furthermore, it offers a work around for NFS file systems where stale file handles may be present during thermtree()
call, causing it to throw a busy device/resource error. See gh #712 for more information.args
andkwargs
are passed through toshutil.rmtree()
.If
onerror
is specified inkwargs
and it is notNone
, this function is completely equivalent toshutil.rmtree()
.- Parameters
args – Arguments to be passed through to
shutil.rmtree()
.max_reties – Maximum number of retries if the target directory cannot be deleted.
kwargs – Keyword arguments to be passed through to
shutil.rmtree()
.
- reframe.utility.osext.run_command(cmd, check=False, timeout=None, **kwargs)[source]¶
Run command synchronously.
This function will block until the command executes or the timeout is reached. It essentially calls
run_command_async()
and waits for the command’s completion.- Parameters
cmd – The command to execute as a string or a sequence. See
run_command_async()
for more details.check – Raise an error if the command exits with a non-zero exit code.
timeout – Timeout in seconds.
kwargs – Keyword arguments to be passed
run_command_async()
.
- Returns
A
subprocess.CompletedProcess
object with information about the command’s outcome.- Raises
reframe.core.exceptions.SpawnedProcessError – If
check
isTrue
and the command fails.reframe.core.exceptions.SpawnedProcessTimeout – If the command times out.
- reframe.utility.osext.run_command_async(cmd, stdout=-1, stderr=-1, shell=False, log=True, **popen_args)[source]¶
Run command asynchronously.
A wrapper to
subprocess.Popen
with the following tweaks:It always passes
universal_newlines=True
toPopen
.If
shell=False
andcmd
is a string, it will lexically splitcmd
usingshlex.split(cmd)
.
- Parameters
cmd – The command to run either as a string or a sequence of arguments.
stdout – Same as the corresponding argument of
Popen
. Default issubprocess.PIPE
.stderr – Same as the corresponding argument of
Popen
. Default issubprocess.PIPE
.shell – Same as the corresponding argument of
Popen
.log – Log the execution of the command through ReFrame’s logging facility.
popen_args – Any additional arguments to be passed to
Popen
.
- Returns
A new
Popen
object.
- reframe.utility.osext.samefile(path1, path2)[source]¶
Check if paths refer to the same file.
If paths exist, this is equivalent to
os.path.samefile()
. If only one of the paths exists and is a symbolic link, it will be followed and its final target will be compared to the other path. If both paths do not exist, a simple string comparison will be performed (after the paths have been normalized).
- reframe.utility.osext.subdirs(dirname, recurse=False)[source]¶
Get the list of subdirectories of
dirname
includingdirname
.If
recurse
isTrue
, this function will retrieve all subdirectories in pre-order.- Parameters
dirname – The directory to start searching.
recurse – If
True
, then recursively search for subdirectories.
- Returns
The list of subdirectories found.
- reframe.utility.osext.unique_abs_paths(paths, prune_children=True)[source]¶
Get the unique absolute paths from a given list of
paths
.- Parameters
paths – An iterable of paths.
prune_children – Discard paths that are children of other paths in the list.
- Raises
TypeError – In case
paths
it not an iterable object.
Type Checking Utilities¶
Dynamic recursive type checking of collections.
This module defines types for collections, such as lists, dictionaries etc.,
that you can use with the isinstance()
builtin function to
recursively type check all the elements of the collection. Suppose you have a
list of integers, suchs as [1, 2, 3]
, the following checks should be true:
l = [1, 2, 3]
assert isinstance(l, List[int]) == True
assert isinstance(l, List[float]) == False
Aggregate types can be combined in an arbitrary depth, so that you can type check any complex data strcture:
d = {'a': [1, 2], 'b': [3, 4]}
assert isisntance(d, Dict) == True
assert isisntance(d, Dict[str, List[int]]) == True
This module offers the following aggregate types:
- List[T]
A list with elements of type
T
.
- Set[T]
A set with elements of type
T
.
- Dict[K,V]
A dictionary with keys of type
K
and values of typeV
.
- Tuple[T]
A tuple with elements of type
T
.
- Tuple[T1,T2,...,Tn]
A tuple with
n
elements, whose types are exactlyT1
,T2
, …,Tn
in that order.
- Str[patt]
A string type whose members are all the strings matching the regular expression
patt
.
Implementation details¶
Internally, this module leverages metaclasses and the
__isinstancecheck__()
method to customize the behaviour of the
isinstance()
builtin.
By implementing also the __getitem__()
accessor method, this module
follows the look-and-feel of the type hints proposed in PEP484. This method returns a new type
that is a subtype of the base container type. Using the facilities of
abc.ABCMeta
, builtin types, such as list
,
str
etc. are registered as subtypes of the base container types
offered by this module. The type hierarchy of the types defined in this module
is the following (example shown for List
, but it is analogous for
the rest of the types):
type
|
|
|
List
/ |
/ |
/ |
list List[T]
In the above example T
may refer to any type, so that
List[List[int]]
is an instance of List
, but not an instance
of List[int]
.
- class reframe.utility.typecheck.Bool(*args, **kwargs)[source]¶
Bases:
object
A boolean type accepting implicit conversions from strings.
This type represents a boolean value but allows implicit conversions from
str
. More specifically, the following conversions are supported:The strings
'yes'
,'true'
and'1'
are converted toTrue
.The strings
'no'
,'false'
and'0'
are converted toFalse
.
The built-in
bool
type is registered as a subclass of this type.Boolean test variables that are meant to be set properly from the command line must be declared of this type and not
bool
.
- class reframe.utility.typecheck.ConvertibleType(name, bases, namespace, **kwargs)[source]¶
Bases:
ABCMeta
A type that support conversions from other types.
This is a metaclass that allows classes that use it to support arbitrary conversions from other types using a cast-like syntax without having to change their constructor:
new_obj = convertible_type(another_type)
For example, a class whose constructor accepts and
int
may need to support a cast-from-string conversion. This is particular useful if you want a custom-typed testvariable
to be able to be set from the command line using the-S
option.In order to support such conversions, a class must use this metaclass and define a class method, named as
__rfm_cast_<type>__
, for each of the type conversion that needs to support .The following is an example of a class
X
that its normal constructor accepts two arguments but it also allows conversions from string:class X(metaclass=ConvertibleType): def __init__(self, x, y): self.data = (x, y) @classmethod def __rfm_cast_str__(cls, s): return X(*(int(x) for x in s.split(',', maxsplit=1))) assert X(2, 3).data == X('2,3').data
New in version 3.8.0.
Test Case Dependencies Management¶
Managing the test case “micro-dependencies” between two tests.
This module defines a set of basic functions that can be used with the how
argument of the reframe.core.pipeline.RegressionTest.depends_on()
function to control how the individual dependencies between the test cases of
two tests are formed.
All functions take two arguments, the source and destination vertices of an
edge in the test case dependency subgraph that connects two tests. In the
relation “T0 depends on T1”, the source are the test cases of “T0” and the
destination are the test cases of “T1.” The source and destination arguments
are two-element tuples containing the names of the partition and the
environment of the corresponding test cases. These functions return
True
if there is an edge connecting the two test cases or
False
otherwise.
A how
function will be called by the framework multiple times when the
test DAG is built. More specifically, for each test dependency relation, it
will be called once for each test case combination of the two tests.
The how
functions essentially split the test case subgraph of two
dependent tests into fully connected components based on the values of their
supported partitions and environments.
The How Test Dependencies Work In ReFrame page contains more information about test dependencies
and shows visually the test case subgraph connectivity that the different
how
functions described here achieve.
New in version 3.3.
- reframe.utility.udeps.by_case(src, dst)[source]¶
The test cases of two dependent tests will be split by partition and by environment.
Test cases from different partitions and different environments are independent.
- reframe.utility.udeps.by_env(src, dst)[source]¶
The test cases of two dependent tests will be split by environment.
Test cases from different environments are independent.
- reframe.utility.udeps.by_part(src, dst)[source]¶
The test cases of two dependent tests will be split by partition.
Test cases from different partitions are independent.
- reframe.utility.udeps.by_xcase(src, dst)[source]¶
The test cases of two dependent tests will be split by the exclusive disjunction (XOR) of their partitions and environments.
Test cases from the same environment and the same partition are independent.
- reframe.utility.udeps.by_xenv(src, dst)[source]¶
The test cases of two dependent tests will be split by the exclusive disjunction (XOR) of their environments.
Test cases from the same environment are independent.