1#!/usr/bin/env python3 2""" 3# Created: Thu Dec 8 10:23:34 2022 (-0500) 4# @author: Jacob Faibussowitsch 5""" 6from __future__ import annotations 7 8import sys 9 10def _read_versions() -> tuple[tuple[int, int, int], str, tuple[int, int, int]]: 11 import os 12 import re 13 14 config_file = 'pyproject.toml' 15 try: 16 # since 3.11 17 # novermin 18 import tomllib # type: ignore[import] 19 except (ModuleNotFoundError, ImportError): 20 try: 21 import tomli as tomllib # type: ignore[import] 22 except (ModuleNotFoundError, ImportError): 23 try: 24 from pip._vendor import tomli as tomllib # type: ignore[import] 25 except (ModuleNotFoundError, ImportError) as mnfe: 26 raise RuntimeError( 27 f'No package installed to read the {config_file} file! Install tomli via ' 28 'python3 -m pip install tomli' 29 ) from mnfe 30 31 def tuplify_version_str(version_str: str) -> tuple[int, int, int]: 32 assert isinstance(version_str, str) 33 version = list(map(int, version_str.split('.'))) 34 while len(version) < 3: 35 version.append(0) 36 # type checkers complain: 37 # 38 # Incompatible return value type (got "Tuple[int, ...]", expected 39 # "Tuple[int, int, int]") [return-value] 40 # 41 # but we know that version is of length 3, so we can safely ignore this 42 return tuple(version) # type: ignore[return-value] 43 44 # open ./../pyproject.toml 45 toml_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, config_file)) 46 with open(toml_path, 'rb') as fd: 47 toml_data = tomllib.load(fd) 48 49 project_data = toml_data['project'] 50 version_str = project_data['version'] 51 version_tup = tuplify_version_str(version_str) 52 py_version_match = re.search(r'([\d.]+)', project_data['requires-python']) 53 assert py_version_match is not None # pacify type checkers 54 min_py_version = tuplify_version_str(py_version_match.group(1)) 55 return version_tup, version_str, min_py_version 56 57__version__, __version_str__, __MIN_PYTHON_VERSION__ = _read_versions() 58 59class RedundantMinVersionCheckError(Exception): 60 """ 61 Exception thrown when code checks for minimum python version which is less than the minimum 62 requirement for petsclinter 63 """ 64 pass 65 66def py_version_lt(major: int, minor: int, sub_minor: int = 0) -> bool: 67 r"""Determines if python version is less than a particular version. 68 69 This should be used whenever back-porting some code as it will automatically raise an 70 error if the version check is useless. 71 72 Parameters 73 ---------- 74 major : 75 major version number, e.g. 3 76 minor : 77 minor version number 78 sub_minor : optional 79 sub-minor or patch version 80 81 Returns 82 ------- 83 ret : 84 True if python version is less than `major`.`minor`.`sub_minor`, False otherwise 85 86 Raises 87 ------ 88 RedundantMinVersionCheckError 89 If the given version is below petsclinter.__MIN_PYTHON_VERSION__ (and therefore the version check 90 is pointless) this raises RedundantMinVersionCheckError. This should not be caught. 91 """ 92 version = (major, minor, sub_minor) 93 if version <= __MIN_PYTHON_VERSION__: 94 raise RedundantMinVersionCheckError( 95 f'Minimum required version {version_str()} already >= checked version {version}. ' 96 f'There is no need to include this version check!' 97 ) 98 return sys.version_info < version 99 100def version_tuple() -> tuple[int, int, int]: 101 r"""Return the package version as a tuple 102 103 Returns 104 ------- 105 version : 106 the package version as a tuple 107 """ 108 return __version__ 109 110def version_str() -> str: 111 r"""Return the package version as a string 112 113 Returns 114 ------- 115 version : 116 the package version as a string 117 """ 118 return __version_str__ 119