diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 83f9ce5..94ce8d1 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: "3.11" - name: Install pre-commit run: pip install pre-commit diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index edbe6a9..ed970ef 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, 3.9, "3.10", "3.11"] os: [ubuntu-latest, macOS-latest, windows-latest] steps: - name: Checkout diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4bb706e..c713207 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: "3.9" + python-version: "3.11" - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/README.md b/README.md index da1b44c..09700b3 100644 --- a/README.md +++ b/README.md @@ -278,23 +278,7 @@ else: A couple of locations are checked, and we are happy to implement more if needed, just open an issue! -Currently, it looks in the following places: -- `__version__` -- `version` -- lookup `VERSION_ATTRIBUTES` in the scooby knowledge base -- lookup `VERSION_METHODS` in the scooby knowledge base - -`VERSION_ATTRIBUTES` is a dictionary of attributes for known python packages -with a non-standard place for the version, e.g. `VERSION_ATTRIBUTES['vtk'] = -'VTK_VERSION'`. You can add other known places via: - -```py -scooby.knowledge.VERSION_ATTRIBUTES['a_module'] = 'Awesome_version_location' -``` - -Similarly, `VERSION_METHODS` is a dictionary for methods to retrieve the -version, and you can similarly add your methods which will get the version -of a package. +Currently, it uses `importlib.metadata.version` to get the distribution version. ### Using scooby to get version information. diff --git a/scooby/knowledge.py b/scooby/knowledge.py index 84228ab..e03a992 100644 --- a/scooby/knowledge.py +++ b/scooby/knowledge.py @@ -4,34 +4,13 @@ Knowledge ========= -It contains, for instance, known odd locations of version information for -particular modules (``VERSION_ATTRIBUTES``, ``VERSION_METHODS``) +Utilities for detecting the environment. """ import os import sys import sysconfig -# Define unusual version locations -VERSION_ATTRIBUTES = { - 'vtk': 'VTK_VERSION', - 'vtkmodules.all': 'VTK_VERSION', - 'PyQt5': 'Qt.PYQT_VERSION_STR', - 'sip': 'SIP_VERSION_STR', -} - - -def get_pyqt5_version(): - """Return the PyQt5 version.""" - from PyQt5.Qt import PYQT_VERSION_STR - - return PYQT_VERSION_STR - - -VERSION_METHODS = { - 'PyQt5': get_pyqt5_version, -} - # Check the environments def in_ipython(): diff --git a/scooby/report.py b/scooby/report.py index 7bda5dd..85465d6 100644 --- a/scooby/report.py +++ b/scooby/report.py @@ -1,21 +1,17 @@ """The main module containing the `Report` class.""" import importlib +import importlib.metadata import sys import time from types import ModuleType -from .knowledge import ( - VERSION_ATTRIBUTES, - VERSION_METHODS, - get_filesystem_type, - in_ipykernel, - in_ipython, -) +from .knowledge import get_filesystem_type, in_ipykernel, in_ipython MODULE_NOT_FOUND = 'Module not found' MODULE_TROUBLE = 'Trouble importing' VERSION_NOT_FOUND = 'Version unknown' +NOT_PROPERLY_INSTALLED = '(not properly installed)' # Info classes @@ -419,19 +415,6 @@ def to_dict(self): return out -def pkg_resources_version_fallback(name): - """Use package-resources to get the distribution version.""" - try: - from pkg_resources import DistributionNotFound, get_distribution - except ImportError: - return - try: - return get_distribution(name).version - except (DistributionNotFound, Exception): # pragma: no cover - # Can run into ParseException, etc. when a bad name is passed - pass - - # This functionaliy might also be of interest on its own. def get_version(module): """Get the version of ``module`` by passing the package or it's name. @@ -441,7 +424,6 @@ def get_version(module): module : str or module Name of a module to import or the module itself. - Returns ------- name : str @@ -454,56 +436,40 @@ def get_version(module): # If (1), we have to load it, if (2), we have to get its name. if isinstance(module, str): # Case 1: module is a string; import name = module # The name is stored in module in this case. + elif isinstance(module, ModuleType): # Case 2: module is module; get name + name = module.__name__ + else: # If not str nor module raise error + raise TypeError("Cannot fetch version from type " "({})".format(type(module))) - # Import module `name`; set to None if it fails. + # Use importlib.metadata to get the version + try: + ver = importlib.metadata.version(name) + except (importlib.metadata.PackageNotFoundError): # pragma: no cover + ver = None # Package be be in PATH + except: # noqa + return name, MODULE_TROUBLE + + if ver is None: + # Handle scenario when package is on path but not installed + # `importlib.metadata.version` should handle this for py3.11+ try: module = importlib.import_module(name) except ImportError: - module = None + return name, MODULE_NOT_FOUND except: # noqa return name, MODULE_TROUBLE - - elif isinstance(module, ModuleType): # Case 2: module is module; get name - name = module.__name__ - - else: # If not str nor module raise error - raise TypeError("Cannot fetch version from type " "({})".format(type(module))) - - # Now get the version info from the module - if module is None: - ver = pkg_resources_version_fallback(name) - if ver is not None: - return name, ver - return name, MODULE_NOT_FOUND - else: - # Try common version names. + # Try common version names for v_string in ('__version__', 'version'): try: - return name, getattr(module, v_string) + ver = getattr(module, v_string) + break except AttributeError: pass - - # Try the VERSION_ATTRIBUTES library - try: - attr = VERSION_ATTRIBUTES[name] - return name, getattr(module, attr) - except (KeyError, AttributeError): - pass - - # Try the VERSION_METHODS library - try: - method = VERSION_METHODS[name] - return name, method() - except (KeyError, ImportError): - pass - - # Try package-resource distribution version - ver = pkg_resources_version_fallback(name) if ver is not None: - return name, ver + return name, f'{ver} {NOT_PROPERLY_INSTALLED}' + return name, f'{VERSION_NOT_FOUND} {NOT_PROPERLY_INSTALLED}' - # If not found, return VERSION_NOT_FOUND - return name, VERSION_NOT_FOUND + return name, ver def platform(): diff --git a/setup.py b/setup.py index f9981ac..3f7b999 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ 'Intended Audience :: Science/Research', 'Natural Language :: English', ), - python_requires='>=3.7.*', + python_requires='>=3.8.*', extras_require={ 'cpu': ['psutil', 'mkl'], # 'gpu': [], # TODO: what's needed? diff --git a/tests/test_scooby.py b/tests/test_scooby.py index b62e279..6eb5260 100644 --- a/tests/test_scooby.py +++ b/tests/test_scooby.py @@ -8,6 +8,7 @@ import pytest import scooby +from scooby.report import NOT_PROPERLY_INSTALLED, VERSION_NOT_FOUND # Write a package `dummy_module` without version number. ppath = os.path.join("tests", "dummy_module") @@ -99,14 +100,14 @@ def test_get_version(): assert version == numpy.__version__ assert name == "numpy" - # Package that was no version given by owner; gets 0.1.0 from setup/pip + # Package that has no `__version__` but has `0.1.0` from distribution name, version = scooby.get_version("no_version") assert version == "0.1.0" assert name == "no_version" - # Dummy module without version (not installed properly) + # Path dummy module without version (not installed properly) name, version = scooby.get_version("dummy_module") - assert version == scooby.report.VERSION_NOT_FOUND + assert version == f'{VERSION_NOT_FOUND} {NOT_PROPERLY_INSTALLED}' assert name == "dummy_module" name, version = scooby.get_version("does_not_exist")