Source code for idaes.ver

#################################################################################
# The Institute for the Design of Advanced Energy Systems Integrated Platform
# Framework (IDAES IP) was produced under the DOE Institute for the
# Design of Advanced Energy Systems (IDAES).
#
# Copyright (c) 2018-2024 by the software owners: The Regents of the
# University of California, through Lawrence Berkeley National Laboratory,
# National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon
# University, West Virginia University Research Corporation, et al.
# All rights reserved.  Please see the files COPYRIGHT.md and LICENSE.md
# for full copyright and license information.
#################################################################################
"""The API in this module is mostly for internal use, e.g. from 'setup.py' to get the version of
the package. But :class:`Version` has been written to be usable as a general
versioning interface.

Example of using the class directly:

.. doctest::

    >>> from idaes.ver import Version
    >>> my_version = Version(1, 2, 3)
    >>> print(my_version)
    1.2.3
    >>> tuple(my_version)
    (1, 2, 3)
    >>> my_version = Version(1, 2, 3, 'alpha')
    >>> print(my_version)
    1.2.3.a
    >>> tuple(my_version)
    (1, 2, 3, 'alpha')
    >>> my_version = Version(1, 2, 3, 'candidate', 1)
    >>> print(my_version)
    1.2.3.rc1
    >>> tuple(my_version)
    (1, 2, 3, 'candidate', 1)

If you want to add a version to a class, e.g. a model, then
simply inherit from ``HasVersion`` and initialize it with the
same arguments you would give the :class:`Version` constructor:

.. doctest::

    >>> from idaes.ver import HasVersion
    >>> class MyClass(HasVersion):
    ...     def __init__(self):
    ...         super(MyClass, self).__init__(1, 2, 3, 'alpha')
    ...
    >>> obj = MyClass()
    >>> print(obj.version)
    1.2.3.a

"""
import os
import re
import sys

__author__ = "Dan Gunter"


[docs] class Version(object): """This class attempts to be compliant with a subset of `PEP 440 <https://www.python.org/dev/peps/pep-0440/>`_. Note: If you actually happen to read the PEP, you will notice that pre- and post- releases, as well as "release epochs", are not supported. """ _specifiers = { "alpha": "a", "beta": "b", "candidate": "rc", "development": "dev", "final": "", # this is the default }
[docs] def __init__( self, major, minor, micro, releaselevel="final", serial=None, label=None ): """Create new version object. Provided arguments are stored in public class attributes by the same name. Args: major (int): Major version minor (int): Minor version micro (int): Micro (aka patchlevel) version releaselevel (str): Optional PEP 440 specifier serial (int): Optional number associated with releaselevel label (str): Optional local version label """ if releaselevel not in self._specifiers: raise ValueError( f'Value "{releaselevel}" for releaselevel not in ' f'({",".join(sorted(self._specifiers.keys()))})' ) self.major, self.minor, self.micro = major, minor, micro self.releaselevel, self.serial, self.label = releaselevel, serial, label
[docs] def __iter__(self): """Return version information as a sequence.""" items = [self.major, self.minor, self.micro] if self.releaselevel != "final": items.append(self.releaselevel) if self.serial is not None: items.append(self.serial) if self.label is not None: items.append(self.label) elif self.label is not None: items.append(0) # placeholder for serial items.append(self.label) for it in items: yield it
[docs] def __str__(self): """Return version information as a string.""" if self.releaselevel == "final": tag = "" else: tag = ( "." + self._specifiers[self.releaselevel] + ("" if self.serial is None else str(self.serial)) + ("" if self.label is None else "+" + self.label) ) return f"{self.major}.{self.minor}.{self.micro}{tag}"
[docs] class HasVersion(object): """Interface for a versioned class."""
[docs] def __init__(self, *args): """Constructor creates a `version` attribute that is an instance of :class:`Version` initialized with the provided args. Args: *args: Arguments to be passed to Version constructor. """ self.version = Version(*args)
def git_hash(): """Get current git hash, with no dependencies on external packages.""" # find git root (in grandparent dir to this file, if anywhere) git_root = os.path.realpath(os.path.join(__file__, "..", "..", ".git")) if not os.path.exists(git_root) or not os.path.isdir(git_root): raise ValueError(f"git root '{git_root}' not found") # get HEAD ref's file try: head = open(os.path.join(git_root, "HEAD")) except FileNotFoundError as err: raise ValueError(f"cannot open HEAD: {err}") # parse file looking for 'ref: <path>' head_ref = None for line in head: ref_match = re.match(r"ref:\s+(\S+)", line) if ref_match: head_ref = ref_match.group(1) break if head_ref is None: raise ValueError(f"no ref found in HEAD '{head}'") # read value of ref in <path> found previously ref_file = os.path.join(git_root, head_ref) try: ref = open(ref_file).read().strip() except FileNotFoundError: raise ValueError(f"ref file '{ref_file}' not found") return ref # Get git hash. No output unless IDAES_DEBUG is set in env gh = None try: try: gh = git_hash() if os.environ.get("IDAES_DEBUG", None): print(f"git hash = {gh}", file=sys.stderr) except ValueError as err: if os.environ.get("IDAES_DEBUG", None): print(f"git_hash() error: {err}", file=sys.stderr) except NameError: # eg, if invoked from setup.py pass #: Package's version as an object package_version = Version(2, 8, 0, "development", 0, gh) #: Package's version as a simple string __version__ = str(package_version)