Skip to content

Commit

Permalink
Adapt reporting of depdency versions in info tool
Browse files Browse the repository at this point in the history
  • Loading branch information
maxnoe committed Nov 8, 2024
1 parent 8c0bb63 commit c121513
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 34 deletions.
56 changes: 49 additions & 7 deletions src/ctapipe/core/provenance.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
import warnings
from collections import UserList
from contextlib import contextmanager
from functools import cache
from importlib import import_module
from importlib.metadata import distributions, version
from importlib.metadata import Distribution, distributions
from os.path import abspath
from pathlib import Path
from types import ModuleType

import psutil
from astropy.time import Time
Expand Down Expand Up @@ -45,17 +47,59 @@
]


@cache
def modules_of_distribution(distribution: Distribution):
modules = distribution.read_text("top_level.txt")
if modules is None:
return None
return set(modules.splitlines())


@cache
def get_distribution_of_module(module: ModuleType | str):
"""Get the package distribution for an imported module"""
if isinstance(module, str):
name = module
module = import_module(module)
else:
name = module.__name__

path = Path(module.__file__).absolute()

for dist in distributions():
modules = modules_of_distribution(dist)
if modules is None:
base = dist.locate_file("")
if dist.files is not None and any(path == base / f for f in dist.files):
return dist
elif name in modules:
return dist

raise ValueError(f"Could not find a distribution for module: {module}")


def get_module_version(name):
"""
Get the version of a python *module*, something you can import.
If the module does not expose a ``__version__`` attribute, this function
will try to determine the *distribution* of the module and return its
version.
"""
# we try first with module.__version__
# to support editable installs
try:
module = import_module(name)
except ModuleNotFoundError:
return "not installed"

try:
return module.__version__
except AttributeError:
try:
return version(name)
return get_distribution_of_module(module).version
except Exception:
return "unknown"
except ImportError:
return "not installed"


class MissingReferenceMetadata(UserWarning):
Expand Down Expand Up @@ -351,15 +395,13 @@ def _sortkey(dist):


def _get_system_provenance():
"""return JSON string containing provenance for all things that are
"""return a dict containing provenance for all things that are
fixed during the runtime"""

bits, linkage = platform.architecture()

return dict(
ctapipe_version=__version__,
ctapipe_resources_version=get_module_version("ctapipe_resources"),
eventio_version=get_module_version("eventio"),
ctapipe_svc_path=os.getenv("CTAPIPE_SVC_PATH"),
executable=sys.executable,
platform=dict(
Expand Down
8 changes: 8 additions & 0 deletions src/ctapipe/core/tests/test_provenance.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,11 @@ def test_provenance_input_reference_meta(provenance: Provenance, dl1_file):
assert "reference_meta" in input_meta
assert "CTA PRODUCT ID" in input_meta["reference_meta"]
Reference.from_dict(input_meta["reference_meta"])


def test_get_distribution_of_module():
from ctapipe.core.provenance import get_distribution_of_module

assert get_distribution_of_module("yaml").name == "PyYAML"
assert get_distribution_of_module("sklearn").name == "scikit-learn"
assert get_distribution_of_module("ctapipe_test_plugin").version == "0.1.0"
50 changes: 23 additions & 27 deletions src/ctapipe/tools/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,19 @@
import logging
import os
import sys
from importlib.metadata import PackageNotFoundError, requires, version
from importlib.resources import files

from packaging.markers import default_environment
from packaging.requirements import Requirement

from ..core import Provenance, get_module_version
from ..core.plugins import detect_and_import_plugins
from ..utils import datasets
from .utils import get_parser

__all__ = ["info"]

# TODO: this list should be global (or generated at install time)
_dependencies = sorted(
[
"astropy",
"matplotlib",
"numpy",
"traitlets",
"sklearn",
"scipy",
"numba",
"pytest",
"iminuit",
"tables",
"eventio",
]
)

_optional_dependencies = sorted(
["ctapipe_resources", "pytest", "graphviz", "matplotlib"]
)


def main(args=None):
parser = get_parser(info)
Expand Down Expand Up @@ -162,15 +145,28 @@ def _info_dependencies():
"""Print info about dependencies."""
print("\n*** ctapipe core dependencies ***\n")

for name in _dependencies:
version = get_module_version(name)
print(f"{name:>20s} -- {version}")
env = default_environment()
requirements = [Requirement(r) for r in requires("ctapipe")]
dependencies = [
r.name for r in requirements if r.marker is None or r.marker.evaluate(env)
]

env["extra"] = "all"
optional_dependencies = [
r.name for r in requirements if r.marker is not None and r.marker.evaluate(env)
]

for name in dependencies:
print(f"{name:>20s} -- {version(name)}")

print("\n*** ctapipe optional dependencies ***\n")

for name in _optional_dependencies:
version = get_module_version(name)
print(f"{name:>20s} -- {version}")
for name in optional_dependencies:
try:
v = version(name)
except PackageNotFoundError:
v = "not installed"
print(f"{name:>20s} -- {v}")


def _info_resources():
Expand Down

0 comments on commit c121513

Please sign in to comment.