41. software — Detecting and checking installed software

A module to help with detecting required software and helper software, and to check the versions of it.

41.1. Classes defined in module software

class software.Version(vstring)[source]

Abstract base class for version numbering classes. Just provides constructor (__init__) and reproducer (__repr__), because those seem to be the same for all version numbering classes; and route rich comparisons to _cmp.

Based on distutils.version, which is scheduled for withdrawal from Python.

Interface for version-number classes – must be implemented by the following classes (the concrete ones – Version should be treated as an abstract class):

__init__ (string) - create and take same action as 'parse'
parse (string)    - convert a string representation to whatever
                    internal representation is appropriate for
                    this style of version numbering
__str__ (self)    - convert back to a string; should be very similar
                    (if not identical to) the string supplied to parse
__repr__ (self)   - generate Python code to recreate
                    the instance
_cmp (self, other) - compare two version numbers ('other' may
                    be an unparsed version string, or another
                    instance of your version class)
class software.SaneVersion(vstring)[source]

Version numbering for anarchists and software realists.

Based on distutils.version, which is scheduled for removal from Python.

Implements the standard interface for version number classes as described above. A version number consists of a series of numbers, separated by either periods or strings of letters. When comparing version numbers, the numeric components will be compared numerically, and the alphabetic components lexically. The following are all valid version numbers, in no particular order:

1.5.1 1.5.2b2 161 3.10a 8.02 3.4j 1996.07.12 3.2.pl0 3.1.1.6 2g6 11g 0.960923 2.2beta29 1.13++ 5.5.kw 2.0b1pl0

In fact, there is no such thing as an invalid version number under this scheme; the rules for comparison are simple and predictable, but may not always give the results you want (for some definition of “want”).

Examples

>>> SaneVersion('2.2beta29').version
[2, 2, 'beta', 29]
class software.Software(name)[source]

Register for software versions.

This class holds a register of the version of installed software. The class is not intended to be used directly, but rather through the derived classes Module and External.

Parameters:

name (str) – The software name as known in pyFormex: this is often the same as the real software name, but can be different if the real software name is complex. We try to use simple lower case names in pyFormex.

The default software object only has two attributes:

Examples

>>> np = Software('numpy')
>>> Software.print_all()
  numpy (** Not Found **)
>>> Software.has('numpy')
''
>>> np.detect('detected')
'detected'
>>> Software.has('numpy')
'detected'
>>> Software.require('numpy')
>>> Software.has('foo')
Traceback (most recent call last):
pyformex.software.RequirementError: foo is not a registered Software
>>> foo = Software('foo')
>>> Software.require('foo')
Traceback (most recent call last):
pyformex.software.RequirementError: Required Software 'foo' (foo) not found
>>> Software.print_all()
  numpy (detected)
  foo (** Not Found **)
>>> Software('foo')
Traceback (most recent call last):
ValueError: A Software with name 'foo' is already registered
property version

Return the version of the software, if installed.

If the software has already been detected, just returns the stored version string. Else, runs the software detect() method and stores and returns the version string.

Returns:

str – The version of the software, or an empty string if the software can not be detected.

detect(version='', fatal=False, quiet=False)[source]

Detect the version of the software.

Parameters:
  • version (str) – The version string to be stored for a detected software. The default empty string means the software was not detected. Derived classes should call this method fromt their detect method and pass a non-empty string for detected softwares.

  • fatal (bool, optional) – If True and the software can not be loaded, a fatal exception is raised. Default is to silently ignore the problem and return an empty version string.

  • quiet (bool, optional) – If True, the whole operation is done silently. The only information about failure will be the returned empty version string.

Returns:

str – The version string of the software, empty if the software can not be loaded.

Notes

As a side effect, the detected version string is stored for later reuse. Thus subsequent tests will not try to re-detect.

classmethod detect_all()[source]

Detect all registered softwares.

Usually, the detection is only performed when needed. Calling this method will perform the detection for all registered softwares.

classmethod print_all()[source]

Print the list of registered softwares

classmethod detected(all=False)[source]

Return the successfully detected softwares and their version

Returns:

dict – A dict with the software name as key and the detected version as value.

classmethod has(name, check=False, fatal=False, quiet=False)[source]

Test if we have the named software available.

Returns a nonzero (version) string if the software is available, or an empty string if it is not.

By default, the software is only checked on the first call. The optional argument check==True forces a new detection.

classmethod check(name, version)[source]

Check that we have a required version of a software.

classmethod require(name, version=None)[source]

Ensure that the named Python software/version is available.

Checks that the specified software is available, and that its version number is not lower than the specified version. If no version is specified, any version is ok.

Returns if the required software/version could be loaded, else an error is raised.

class software.Module(name, modname=None, attr=None, incompatible=None)[source]

Register for Python module version detection rules.

This class holds a register of version detection rules for installed Python modules. Each instance holds the rule for one module, and it is automatically registered at instantiation. The modules used by pyFormex are declared in this module, but users can add their own just by creating a Module instance.

Parameters:
  • name (str) – The module name as known in pyFormex: this is often the same as the Python module name, but can be different if the Python module name is complex. We try to use simple lower case names in pyFormex.

  • modname (str, optional) – The correct Python package.module name. If not provided, it is equal to the pyFormex name.

  • attr (str or tuple of str, optional) – If a str, it is the name of the attribute holding the module version. This should be an attribute of the module modname. The default is ‘__version__’, as it is used by many projects. If the version is not stored in a direct attribute of the same module as used for the detection, then a tuple of strings can be specified, starting with the Python module name in which the version attribute is stored, and a list of subsequent attribute names leading to the version. In this case the first element of the tuple is always a module name. If it is the same as modname, an empty string may be specified. If the final attribute is a callable, it will be called to get the version. The result is always converted to str before being stored as the version.

  • incompatible (tuple of str, optional) – A list of incompatible modules. If any of these modules is loaded, the Module will not be tried. Beware: this should be the actual module names, not the pyFormex component name, which is all lower case.

Examples

>>> Module.register.clear()
>>> Module.detect_all()
>>> Module.print_all()
>>> np = Module('numpy')
>>> pil = Module('pil', modname='PIL', attr='VERSION')
>>> Module.print_all()
  numpy (1...)
  pil (** Not Found **)
>>> SaneVersion(Module.has('numpy')) >= SaneVersion('1.16')
True
>>> Module.print_all()
  numpy (1...)
  pil (** Not Found **)
>>> Module.has('foo')
Traceback (most recent call last):
pyformex.software.RequirementError: foo is not a registered Module
>>> Module.require('foo')
Traceback (most recent call last):
pyformex.software.RequirementError: foo is not a registered Module
>>> foo = Module('foo','FooBar')
>>> Module.has('foo')
''
>>> Module.require('foo')
Traceback (most recent call last):
pyformex.software.RequirementError: Required Module 'foo' (FooBar) not found

Now fake a detection of Module ‘foo’

>>> Module.register['foo'].version = '1.2.3'
>>> Module.has('foo')
'1.2.3'
>>> Module.require('foo')
>>> Module.require('foo','>= 1.1.7')
>>> Module.require('foo','>= 1.3.0')
Traceback (most recent call last):
pyformex.software.RequirementError: Required version (>= 1.3.0) of Module 'foo' (FooBar) not found
detect(fatal=False, quiet=False)[source]

Detect the version of the module.

Parameters:
  • fatal (bool, optional) – If True and the module can not be loaded, a fatal exception is raised. Default is to silently ignore the problem and return an empty version string.

  • quiet (bool, optional) – If True, the whole operation is done silently. The only information about failure will be the returned empty version string.

Returns:

str – The version string of the module, empty if the module can not be loaded.

Notes

As a side effect, the detected version string is stored for later reuse. Thus subsequent tests will not try to re-detect.

class software.External(name, command, regex)[source]

Register for external application version detection rules.

This class holds a register of version detection rules for installed external applications. Each instance holds the rule for one application, and it is automatically registered at instantiation. The applications used by pyFormex are declared in this module, but users can add their own just by creating an External instance.

Parameters:
  • name (str) – The application name as known in pyFormex: this is often the same as the executable name, but can be different if the executable name is complex. We try to use simple lower case names in pyFormex.

  • command (str) – The command to run the application. Usually this includes an option to make the application just report its version and then exit. The command should be directly executable as-is, without invoking a new shell. If a shell is required, it should be made part of the command (see e.g. tetgen). Do not use commands that take a long time to load and run.

  • regex (r-string) – A regular expression that extracts the version from the output of the command. If the application does not have or report a version, any non-empty string is accepted as a positive detection (for example the executable’s name in a bin path). The regex string should contain one set of grouping parentheses, delimiting the part of the output that will be stored as version. If the output of the command does not match, an empty string is stored.

Examples

>>> External.register.clear()
>>> External.detect_all()
>>> External.print_all()
detect(fatal=False, quiet=False)[source]

Detect the version of the external.

Parameters:
  • fatal (bool, optional) – If True and the external can not be run, a fatal exception is raised. Default is to silently ignore the problem and return an empty version string.

  • quiet (bool, optional) – If True, the whole operation is done silently. The only information about failure will be the returned empty version string.

Returns:

str – The version string of the external, empty if the external can not be run.

Notes

As a side effect, the detected version string is stored for later reuse. Thus subsequent tests will not try to re-detect.

41.2. Functions defined in module software

software.listLibraries()[source]

Return a list with the acceleration libraries

software.listShaders()[source]

Return a list with the available GPU shader programs.

Returns:

list – A list of the shader versions available.

Notes

Shader programs are stored in the pyformex/glsl directory and consist of at least of two files: ‘vertex_shader_SHADER.c’ and ‘fragment_shader_SHADER.c’. The SHADER part is the version mnemomic which can be used in the ‘–shader SHADER’ option of the pyformex command.

software.detectedSoftware(comp='SME')[source]

Return a dict with detected software.

Parameters:
  • comp (str) –

    The components to be detected. The string is composed of the

    characters ‘S’, M’ and ‘E’, each identifying one of the components:

    • S: detects system software including Python and pyFormex,

    • M: detects Python modules used by pyFormex,

    • E: detects software used by pyFormex as external commands.

    Returns

  • -------

  • dict

    A dict with one or more of the keys ‘System’, ‘Modules’ and ‘Externals’, each having a dict as value:

    • System: contains information about system, pyFormex, Python

    • Modules: the detected Python modules

    • Externals: the detected external programs

software.compareVersion(has, want)[source]

Check whether a detected version matches the requirements.

has is the version string detected. want is the required version string, possibly preceded by one of the doubly underscored comparison operators: __gt__, etc. If no comparison operator is specified, ‘__eq__’ is assumed.

Note that any tail behind x.y.z version is considered to be later version than x.y.z.

Returns the result of the comparison: True or False .. rubric:: Examples

>>> compareVersion('2.7','2.4.3')
False
>>> compareVersion('2.7','>2.4.3')
True
>>> compareVersion('2.7','>= 2.4.3')
True
>>> compareVersion('2.7','>= 2.7-rc3')
False
>>> compareVersion('2.7-rc4','>= 2.7-rc3')
True
software.checkDict(has, want)[source]

Check that software dict has has the versions required in want

software.checkSoftware(req, report=False)[source]

Check that we have the matching components

Returns True or False. If report=True, also returns a string with a full report.

software.registerSoftware(req)[source]

Register the current values of required software

software.formatDict(d, indent=2, sort_keys=False, mode='json')[source]

Format a possibly nested dict

>>> d = {'a':0, 'd':{'y':'s', 'x':'t'}, 'b':1}
>>> print(formatDict(d))
{
  "a": 0,
  "d": {
    "y": "s",
    "x": "t"
  },
  "b": 1
}
>>> print(formatDict(d, mode='python'))
{'a': 0, 'd': {'y': 's', 'x': 't'}, 'b': 1}
software.storeSoftware(soft, fn, mode='json', indent=2)[source]

Store the software collection on file.

The collection is by default stored in JSON format.

software.readSoftware(fn)[source]

Read the software collection from file.

Default mode is ‘python’ because it reads json as well as python. ‘legacy’ can be used to read old software files, though it is recommended to change the files by removing the ‘soft = ‘ at the start.