From e7ad95384d5a50a7630b2a4faa6d630811dee355 Mon Sep 17 00:00:00 2001 From: lukken <lukken@astron.nl> Date: Wed, 15 Jun 2022 06:57:02 +0000 Subject: [PATCH] L2SS-832: Version string as global without gitpython --- bin/start-ds.sh | 5 +- docker-compose/jupyter/requirements.txt | 1 - .../lofar-device-base/lofar-requirements.txt | 2 +- tangostationcontrol/VERSION | 1 + tangostationcontrol/pyproject.toml | 3 + tangostationcontrol/requirements.txt | 4 +- tangostationcontrol/setup.cfg | 9 +- tangostationcontrol/setup.py | 7 -- .../tangostationcontrol/__init__.py | 11 +- .../common/lofar_logging.py | 4 +- .../common/lofar_version.py | 116 ------------------ .../devices/lofar_device.py | 4 +- .../devices/observation_control.py | 4 +- .../test/common/test_lofar_version.py | 107 ---------------- 14 files changed, 26 insertions(+), 252 deletions(-) create mode 100644 tangostationcontrol/VERSION create mode 100644 tangostationcontrol/pyproject.toml delete mode 100644 tangostationcontrol/setup.py delete mode 100644 tangostationcontrol/tangostationcontrol/common/lofar_version.py delete mode 100644 tangostationcontrol/tangostationcontrol/test/common/test_lofar_version.py diff --git a/bin/start-ds.sh b/bin/start-ds.sh index b9b958ecd..9ae8ae900 100755 --- a/bin/start-ds.sh +++ b/bin/start-ds.sh @@ -32,10 +32,7 @@ if [[ $TANGOSTATIONCONTROL ]]; then else # Install the package, exit 1 if it fails cd tangostationcontrol || exit 1 - mkdir -p /tmp/tangostationcontrol - python3 setup.py build --build-base /tmp/tangostationcontrol egg_info --egg-base /tmp/tangostationcontrol bdist_wheel --dist-dir /tmp/tangostationcontrol || exit 1 - # shellcheck disable=SC2012 - pip install "$(ls -Art /tmp/tangostationcontrol/*.whl | tail -n 1)" + pip install ./ fi # Return to the stored the directory, this preserves the working_dir argument in diff --git a/docker-compose/jupyter/requirements.txt b/docker-compose/jupyter/requirements.txt index e0116405e..27a6d1ed3 100644 --- a/docker-compose/jupyter/requirements.txt +++ b/docker-compose/jupyter/requirements.txt @@ -1,4 +1,3 @@ -GitPython >= 3.1.24 # BSD ipython >=7.27.0,!=7.28.0 # BSD jupyter ipykernel diff --git a/docker-compose/lofar-device-base/lofar-requirements.txt b/docker-compose/lofar-device-base/lofar-requirements.txt index 718c3a13a..d150b6a4c 100644 --- a/docker-compose/lofar-device-base/lofar-requirements.txt +++ b/docker-compose/lofar-device-base/lofar-requirements.txt @@ -1,2 +1,2 @@ # Do not put tangostationcontrol dependencies here, only setup.py / __init__.py -GitPython >= 3.1.20 # BSD + diff --git a/tangostationcontrol/VERSION b/tangostationcontrol/VERSION new file mode 100644 index 000000000..ceab6e11e --- /dev/null +++ b/tangostationcontrol/VERSION @@ -0,0 +1 @@ +0.1 \ No newline at end of file diff --git a/tangostationcontrol/pyproject.toml b/tangostationcontrol/pyproject.toml new file mode 100644 index 000000000..3588ce52b --- /dev/null +++ b/tangostationcontrol/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ['setuptools>=42', 'wheel'] +build-backend = 'setuptools.build_meta' diff --git a/tangostationcontrol/requirements.txt b/tangostationcontrol/requirements.txt index 90ae3c178..aa00dc4a5 100644 --- a/tangostationcontrol/requirements.txt +++ b/tangostationcontrol/requirements.txt @@ -13,7 +13,5 @@ docker >= 5.0.3 # Apache 2 python-logstash-async >= 2.3.0 # MIT python-casacore >= 3.3.1 # LGPLv3 etrs-itrs@git+https://github.com/brentjens/etrs-itrs # Apache 2 -# numpy must be manually added even though etrs-itrs requires it -numpy >= 1.21.0 # BSD lofarantpos >= 0.5.0 # Apache 2 -python-geohash >= 0.8.5 # Apache 2MIT +python-geohash >= 0.8.5 # Apache 2 / MIT diff --git a/tangostationcontrol/setup.cfg b/tangostationcontrol/setup.cfg index c8e9a4390..5cf179bef 100644 --- a/tangostationcontrol/setup.cfg +++ b/tangostationcontrol/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = tangostationcontrol -version = attr: tangostationcontrol.__version__ +version = file: VERSION summary = LOFAR 2.0 Station Control description_file = README.md @@ -13,14 +13,12 @@ project_urls = license = Apache-2 classifier = Environment :: Console + Development Status :: 3 - Alpha License :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 [options] package_dir= @@ -28,7 +26,8 @@ package_dir= packages=find: python_requires => 3.7 install_requires = - GitPython>=3.1.20 + importlib-metadata>=0.12;python_version<"3.8" + pip>=1.5 [options.packages.find] where=. diff --git a/tangostationcontrol/setup.py b/tangostationcontrol/setup.py deleted file mode 100644 index 6356812fd..000000000 --- a/tangostationcontrol/setup.py +++ /dev/null @@ -1,7 +0,0 @@ -import setuptools - -with open('requirements.txt') as f: - required = f.read().splitlines() - -# Requires: setup.cfg -setuptools.setup(install_requires=required) diff --git a/tangostationcontrol/tangostationcontrol/__init__.py b/tangostationcontrol/tangostationcontrol/__init__.py index c6e48f3e8..2e6117676 100644 --- a/tangostationcontrol/tangostationcontrol/__init__.py +++ b/tangostationcontrol/tangostationcontrol/__init__.py @@ -1,3 +1,10 @@ -from tangostationcontrol.common.lofar_version import get_version +try: + from importlib import metadata +except ImportError: # for Python<3.8 + import importlib_metadata as metadata -__version__ = get_version() +__version__ = metadata.version("tangostationcontrol") + + +def print_version(*args, **kwargs): + print(__version__) diff --git a/tangostationcontrol/tangostationcontrol/common/lofar_logging.py b/tangostationcontrol/tangostationcontrol/common/lofar_logging.py index bc613361d..a2c4ad391 100644 --- a/tangostationcontrol/tangostationcontrol/common/lofar_logging.py +++ b/tangostationcontrol/tangostationcontrol/common/lofar_logging.py @@ -5,7 +5,7 @@ import traceback import socket import time -from .lofar_version import get_version +from tangostationcontrol import __version__ as version class TangoLoggingHandler(logging.Handler): LEVEL_TO_DEVICE_STREAM = { @@ -97,7 +97,7 @@ class LogAnnotator(logging.Formatter): record.lofar_id = f"tango - {record.tango_device}" # annotate record with the current software version - record.software_version = get_version() + record.software_version = version # we just annotate, we don't filter return True diff --git a/tangostationcontrol/tangostationcontrol/common/lofar_version.py b/tangostationcontrol/tangostationcontrol/common/lofar_version.py deleted file mode 100644 index 0a70002ed..000000000 --- a/tangostationcontrol/tangostationcontrol/common/lofar_version.py +++ /dev/null @@ -1,116 +0,0 @@ -import git -import os -import functools -import pkg_resources -import re - -basepath = os.path.dirname(os.path.abspath(__file__)) - -def get_repo(starting_directory: str = basepath, limit = 10) -> git.Repo: - """ Try finding the repository by traversing up the tree. - - By default, the repository containing this module is returned. - """ - - directory = starting_directory - - try: - return git.Repo(directory) - except git.InvalidGitRepositoryError: - pass - - # We now have to traverse up the tree up until limit diretories - for _i in range(limit): - if directory == "/" or not os.path.exists(directory): - break - - directory = os.path.abspath(directory + os.path.sep + "..") - - try: - return git.Repo(directory) - except git.InvalidGitRepositoryError: - pass - - # Could not find a repo within the limit so return None - return None - - -@functools.lru_cache(maxsize=None) -def get_version(repo: git.Repo = None) -> str: - """ Return a version string for the current commit. - - There is a practical issue: the repository changes over time, f.e. switching branches with 'git checkout'. We want - to know the version that is running in memory, not the one that is on disk. - - As a work-around, we cache the version information, in that it is at least consistent. It is up to the caller - to request the version early enough. - - The version string is of the following pattern: - - ${MAJOR}.${MINOR}.${PATCH}[.${BRANCH}$.{COMMIT}][.dirty] - - For releases only ${MAJOR}.${MINOR}.${PATCH} should be set. Versioning is - achieved by tagging commits using the `v${MAJOR}.${MINOR}.${PATCH}` pattern. - The leading `v` is none optional! - - """ - - if repo is None: - repo = get_repo() - - # When we can't find a git repo anymore, we must be packaged. Extract the - # package version directly - if repo is None: - try: - return pkg_resources.require("tangostationcontrol")[0].version - except Exception: - pass - - # Filter all tags so that they must match vMAJOR.MINOR.PATCH or - # vMAJOR.MINOR.PATCH.BRANCHCOMMIT - reg = re.compile(r'^v[0-9](\.[0-9]){2}(\.[a-z]*[0-9]*)?') - - commit = repo.commit() - filtered_tags = [tag.name for tag in repo.tags if reg.search(tag.name)] - # Order tags from newest to oldest - tags = _order_tags(repo, filtered_tags) - - # Find closest tag for commit - closest_tag = _find_closest_tag(commit, repo, tags) - - if commit in tags: - # a tag = production ready - commit_str = "{}".format(tags[commit].name[1:]) - elif repo.head.is_detached: - # no active branch - commit_str = "{}.{}".format(closest_tag.name[1:], commit) - else: - # HEAD of a branch - branch = repo.active_branch - commit_str = "{}.{}.{}".format(closest_tag.name[1:], branch, commit) - - return "{}{}".format(commit_str, ".dirty" if repo.is_dirty() else "") - -def _order_tags(repo, filtered_tags): - """ Helper function to order tags from newest to oldest """ - return {tag.commit: tag for tag in reversed(repo.tags) if tag.name in filtered_tags} - -def _find_closest_tag(commit, repo, tags): - """ Helper function to find closest tag for commit """ - closest_tag = type('',(object,),{"name": 'v0.0.0'})() - for item in commit.iter_items(repo, commit): - if item.type == 'commit' and item in tags: - closest_tag = tags[item] - break - return closest_tag - -# at least cache the current repo version immediately -try: - _ = get_version() -except Exception: - "B001 Do not use bare `except:`, it also catches unexpected events like" - "memory errors, interrupts, system exit" - pass - - -def main(args=None, **kwargs): - print(get_version()) diff --git a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py index 1ff328f78..0cc6fa0bb 100644 --- a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py +++ b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py @@ -20,9 +20,9 @@ import numpy import textwrap # Additional import +from tangostationcontrol import __version__ as version from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper from tangostationcontrol.common.lofar_logging import log_exceptions -from tangostationcontrol.common.lofar_version import get_version from tangostationcontrol.devices.device_decorators import only_in_states, fault_on_error from tangostationcontrol.toolkit.archiver import Archiver @@ -75,7 +75,7 @@ class lofar_device(Device, metaclass=DeviceMeta): # Attributes # ---------- - version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version()) + version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: version) # list of translator property names to be set by set_translator_defaults TRANSLATOR_DEFAULT_SETTINGS = [] diff --git a/tangostationcontrol/tangostationcontrol/devices/observation_control.py b/tangostationcontrol/tangostationcontrol/devices/observation_control.py index cfb3f2f17..f29c2032c 100644 --- a/tangostationcontrol/tangostationcontrol/devices/observation_control.py +++ b/tangostationcontrol/tangostationcontrol/devices/observation_control.py @@ -15,9 +15,9 @@ from tango import Except, DevFailed, DevState, AttrWriteType, DebugIt, DevicePro from tango.server import Device, command, attribute from tango import EventType +from tangostationcontrol import __version__ as version from tangostationcontrol.common.entrypoint import entry from tangostationcontrol.common.lofar_logging import device_logging_to_python, log_exceptions -from tangostationcontrol.common.lofar_version import get_version from tangostationcontrol.devices.device_decorators import only_when_on, fault_on_error from tangostationcontrol.devices.lofar_device import lofar_device from tangostationcontrol.devices.observation import Observation @@ -71,7 +71,7 @@ class ObservationControl(lofar_device): - string version """ # Attributes - version_R = attribute(dtype = str, access = AttrWriteType.READ, fget = lambda self: get_version()) + version_R = attribute(dtype = str, access = AttrWriteType.READ, fget = lambda self: version) running_observations_R = attribute(dtype = (numpy.int64, ), access = AttrWriteType.READ) # Core functions diff --git a/tangostationcontrol/tangostationcontrol/test/common/test_lofar_version.py b/tangostationcontrol/tangostationcontrol/test/common/test_lofar_version.py deleted file mode 100644 index 89ac894d9..000000000 --- a/tangostationcontrol/tangostationcontrol/test/common/test_lofar_version.py +++ /dev/null @@ -1,107 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of the LOFAR 2.0 Station Software -# -# -# -# Distributed under the terms of the APACHE license. -# See LICENSE.txt for more info. - -import git -from unittest import mock - -from tangostationcontrol.common import lofar_version - -from tangostationcontrol.test import base - - -class TestLofarVersion(base.TestCase): - - def setUp(self): - super(TestLofarVersion, self).setUp() - - # Clear the cache as this function of lofar_version uses LRU decorator - # This is a good demonstration of how unit tests in Python can have - # permanent effects, typically fixtures are needed to restore these. - lofar_version.get_version.cache_clear() - - def test_get_version(self): - """Test if attributes of get_repo are correctly used by get_version""" - - with mock.patch.object(lofar_version, 'get_repo') as m_get_repo: - m_commit = mock.Mock() - m_commit.return_value.__str__ = mock.Mock(return_value="123456") - m_commit.return_value.iter_items.return_value = [] - - m_is_dirty = mock.Mock() - m_is_dirty.return_value = False - - m_head = mock.Mock(is_detached=False) - - m_get_repo.return_value = mock.Mock( - active_branch="main", commit=m_commit, tags=[], - is_dirty=m_is_dirty, head=m_head) - - # No need for special string equal in Python - self.assertEqual("0.0.0.main.123456", lofar_version.get_version()) - - def test_get_version_tag(self): - """Test if get_version determines production_ready for tagged commit""" - - with mock.patch.object(lofar_version, 'get_repo') as m_get_repo: - m_commit = mock.Mock() - m_commit.return_value.__str__ = mock.Mock(return_value="123456") - m_commit.return_value.iter_items.return_value = [] - - m_is_dirty = mock.Mock() - m_is_dirty.return_value = False - - m_head = mock.Mock(is_detached=False) - - m_tag_commit = mock.Mock(type="commit") - m_tag_commit.__str__ = mock.Mock(return_value="123456") - - m_tag = mock.Mock(commit=m_tag_commit) - m_tag.name = "v0.0.3" - m_tag.__str__ = mock.Mock(return_value= "v0.0.3") - - m_commit.return_value = m_tag_commit - m_commit.return_value.iter_items.return_value = [m_tag_commit] - - m_get_repo.return_value = mock.Mock( - active_branch="main", commit=m_commit, - tags=[m_tag], is_dirty=m_is_dirty, head=m_head) - - self.assertEqual("0.0.3", lofar_version.get_version()) - - @mock.patch.object(lofar_version, 'get_repo') - def test_get_version_tag_dirty(self, m_get_repo): - - """Test if get_version determines dirty tagged commit""" - m_commit = mock.Mock() - m_commit.return_value.__str__ = mock.Mock(return_value="123456") - m_commit.return_value.iter_items.return_value = [] - - m_is_dirty = mock.Mock() - m_is_dirty.return_value = True - - m_head = mock.Mock(is_detached=False) - - m_get_repo.return_value = mock.Mock( - active_branch="main", commit=m_commit, tags=[], - is_dirty=m_is_dirty, head=m_head) - - # No need for special string equal in Python - self.assertEqual("0.0.0.main.123456.dirty", lofar_version.get_version()) - - def test_catch_repo_error(self): - """Test if invalid git directories will raise error""" - - with mock.patch.object(lofar_version, 'get_repo') as m_get_repo: - - # Configure lofar_version.get_repo to raise InvalidGitRepositoryError - m_get_repo.side_effect = git.InvalidGitRepositoryError - - # Test that error is raised by get_version - self.assertRaises( - git.InvalidGitRepositoryError, lofar_version.get_version) -- GitLab