Source code for tango.utils._platform
# SPDX-FileCopyrightText: All Contributors to the PyTango project
# SPDX-License-Identifier: LGPL-3.0-or-later
"""Version, environment and interpreter-level helpers."""
import contextlib
import os
import sys
from argparse import HelpFormatter
from packaging.version import Version
from tango import Database, DevFailed, _tango
from tango.constants import TgLibVers
from tango.release import Release
__device_classes = None
def __import(name):
__import__(name)
return sys.modules[name]
def __requires(package_name, min_version=None, conflicts=(), software_name="Software"):
package_name_l = package_name.lower()
if package_name_l == "pytango":
curr_version = Version(Release.version)
elif package_name_l == "tango":
curr_version = Version(TgLibVers)
else:
try:
package = __import(package_name)
curr_version = Version(package.__version__)
except ImportError:
msg = f"Could not find package {package_name} required by {software_name}"
raise Exception(msg) from None
except Exception:
msg = f"Error importing package {package_name} required by {software_name}"
raise Exception(msg) from None
if min_version is not None:
min_version = Version(min_version)
if min_version > curr_version:
msg = f"{software_name} requires {package_name} {min_version} but {curr_version} installed"
raise Exception(msg)
conflicts = map(Version, conflicts)
if curr_version in conflicts:
msg = f"{software_name} cannot run with {package_name} {curr_version}"
raise Exception(msg)
return True
[docs]
def requires_pytango(min_version=None, conflicts=(), software_name="Software"):
"""
Determines if the required PyTango version for the running
software is present. If not an exception is thrown.
Example usage::
from tango import requires_pytango
requires_pytango('7.1', conflicts=['8.1.1'], software_name='MyDS')
:param min_version:
minimum PyTango version [default: None, meaning no minimum
required]. If a string is given, it must be in the valid
version number format
(see: :class:`~packaging.version.Version`)
:type min_version:
None, str, :class:`~packaging.version.Version`
:param conflicts:
a sequence of PyTango versions which conflict with the
software using it
:type conflicts:
seq<str|Version>
:param software_name:
software name using tango. Used in the exception message
:type software_name: str
:raises Exception: if the required PyTango version is not met
.. versionadded:: 8.1.4
"""
return __requires(
"pytango",
min_version=min_version,
conflicts=conflicts,
software_name=software_name,
)
[docs]
def requires_tango(min_version=None, conflicts=(), software_name="Software"):
"""
Determines if the required cppTango version for the running
software is present. If not an exception is thrown.
Example usage::
from tango import requires_tango
requires_tango('7.1', conflicts=['8.1.1'], software_name='MyDS')
:param min_version:
minimum Tango version [default: None, meaning no minimum
required]. If a string is given, it must be in the valid
version number format
(see: :class:`~packaging.version.Version`)
:type min_version:
None, str, :class:`~packaging.version.Version`
:param conflicts:
a sequence of Tango versions which conflict with the
software using it
:type conflicts:
seq<str|Version>
:param software_name:
software name using Tango. Used in the exception message
:type software_name: str
:raises Exception: if the required Tango version is not met
.. versionadded:: 8.1.4
"""
return __requires(
"Tango",
min_version=min_version,
conflicts=conflicts,
software_name=software_name,
)
def get_tango_device_classes():
global __device_classes
if __device_classes is None:
__device_classes = [_tango.DeviceImpl]
i = 2
while True:
dc = f"Device_{i}Impl"
try:
__device_classes.append(getattr(_tango, dc))
i = i + 1
except AttributeError:
break
return __device_classes
def get_latest_device_class():
return get_tango_device_classes()[-1]
[docs]
def get_home():
"""
Find user's home directory if possible. Otherwise raise error.
:return: user's home directory
:rtype: :py:obj:`str`
.. versionadded:: 7.1.4
"""
path = ""
with contextlib.suppress(Exception):
path = os.path.expanduser("~")
if not os.path.isdir(path):
for evar in ("HOME", "USERPROFILE", "TMP"):
try:
path = os.environ[evar]
if os.path.isdir(path):
break
except Exception:
pass
if path:
return path
else:
raise RuntimeError("please define environment variable $HOME")
def from_version_str_to_hex_str(version_str):
v = list(map(int, version_str.split(".")))
return f"0x{v[0]:02x}{v[1]:02x}{v[2]:02x}00"
def from_version_str_to_int(version_str):
return int(from_version_str_to_hex_str(version_str), 16)
def info():
# Compile and Runtime are set by `tango.pytango_init.init`
from tango.constants import Compile, Runtime
msg = f"""\
PyTango {Release.version_long} {Release.version_info}
PyTango compiled with:
Python : {Compile.PY_VERSION}
Numpy : {Compile.NUMPY_VERSION}
Tango : {Compile.TANGO_VERSION}
pybind11 : {Compile.PYBIND11_VERSION}
PyTango runtime is:
Python : {Runtime.PY_VERSION}
Numpy : {Runtime.NUMPY_VERSION}
Tango : {Runtime.TANGO_VERSION}
PyTango running on:
{Runtime.UNAME}
"""
return msg
class PyTangoHelpFormatter(HelpFormatter):
def _format_usage(self, usage, actions, groups, prefix):
usage = super()._format_usage(usage, actions, groups, prefix)
try:
db = Database()
servers_list = db.get_instance_name_list(self._prog)
if servers_list.size():
usage += f"Instance names defined in database for server {self._prog}:\n"
for server in servers_list:
usage += "\t" + str(server) + "\n"
else:
usage += f"Warning! No defined instance in database for server {self._prog} found!\n"
except DevFailed:
pass
return usage