22 Determine which packages are being used in the system and their versions 24 There are a few different types of packages, and their versions are collected in different ways: 25 1. Run-time libraries (e.g., cfitsio, fftw): we get their version from interrogating the dynamic library 26 2. Python modules (e.g., afw, numpy; galsim is also in this group even though we only use it through the 27 library, because no version information is currently provided through the library): we get their version 28 from the __version__ module variable. Note that this means that we're only aware of modules that have 29 already been imported. 30 3. Other packages provide no run-time accessible version information (e.g., astrometry_net): we get their 31 version from interrogating the environment. Currently, that means EUPS; if EUPS is replaced or dropped then 32 we'll need to consider an alternative means of getting this version information. 33 4. Local versions of packages (a non-installed EUPS package, selected with "setup -r /path/to/package"): we 34 identify these through the environment (EUPS again) and use as a version the path supplemented with the 35 git SHA and, if the git repo isn't clean, an MD5 of the diff. 37 These package versions are collected and stored in a Packages object, which provides useful comparison and 42 from lsst.base import Packages 43 pkgs = Packages.fromSystem() 44 print "Current packages:", pkgs 45 old = Packages.read("/path/to/packages.pickle") 46 print "Old packages:", old 47 print "Missing packages compared to before:", pkgs.missing(old) 48 print "Extra packages compared to before:", pkgs.extra(old) 49 print "Different packages: ", pkgs.difference(old) 50 old.update(pkgs) # Include any new packages in the old 51 old.write("/path/to/packages.pickle") 53 from builtins
import object
60 import pickle
as pickle
61 from collections
import Mapping
63 from .versions
import getRuntimeVersions
65 from future
import standard_library
66 standard_library.install_aliases()
68 __all__ = [
"getVersionFromPythonModule",
"getPythonPackages",
"getEnvironmentPackages",
"Packages"]
72 BUILDTIME = set([
"boost",
"eigen",
"tmv"])
76 PYTHON = set([
"galsim"])
80 ENVIRONMENT = set([
"astrometry_net",
"astrometry_net_data",
"minuit2",
"xpa"])
84 """Determine the version of a python module. 89 Module for which to get version. 98 Raised if __version__ attribute is not set. 102 We supplement the version with information from the __dependency_versions__ 103 (a specific variable set by LSST's sconsUtils at build time) only for packages 104 that are typically used only at build-time. 106 version = module.__version__
107 if hasattr(module,
"__dependency_versions__"):
109 deps = module.__dependency_versions__
110 buildtime = BUILDTIME & set(deps.keys())
112 version +=
" with " +
" ".join(
"%s=%s" % (pkg, deps[pkg])
113 for pkg
in sorted(buildtime))
118 """Get imported python packages and their versions. 123 Keys (type `str`) are package names; values (type `str`) are their versions. 127 We wade through sys.modules and attempt to determine the version for each 128 module. Note, therefore, that we can only report on modules that have 129 *already* been imported. 131 We don't include any module for which we cannot determine a version. 134 for module
in PYTHON:
136 importlib.import_module(module)
140 packages = {
"python": sys.version}
142 moduleNames = list(sys.modules.keys())
143 for name
in moduleNames:
144 module = sys.modules[name]
152 for ending
in (
".version",
"._version"):
153 if name.endswith(ending):
154 name = name[:-len(ending)]
156 assert ver == packages[name]
157 elif name
in packages:
158 assert ver == packages[name]
164 name = name.replace(
"lsst.",
"").replace(
".",
"_")
175 """Get products and their versions from the environment. 180 Keys (type `str`) are product names; values (type `str`) are their versions. 184 We use EUPS to determine the version of certain products (those that don't provide 185 a means to determine the version any other way) and to check if uninstalled packages 186 are being used. We only report the product/version for these packages. 189 from eups
import Eups
190 from eups.Product
import Product
192 from lsst.pex.logging
import getDefaultLog
193 getDefaultLog().warn(
"Unable to import eups, so cannot determine package versions from environment")
200 products = _eups.findProducts(tags=[
"setup"])
204 packages = {prod.name: prod.version
for prod
in products
if prod
in ENVIRONMENT}
210 for prod
in products:
211 if not prod.version.startswith(Product.LocalVersionPrefix):
215 gitDir = os.path.join(prod.dir,
".git")
216 if os.path.exists(gitDir):
218 revCmd = [
"git",
"--git-dir=" + gitDir,
"--work-tree=" + prod.dir,
"rev-parse",
"HEAD"]
219 diffCmd = [
"git",
"--no-pager",
"--git-dir=" + gitDir,
"--work-tree=" + prod.dir,
"diff",
222 rev = subprocess.check_output(revCmd).decode().strip()
223 diff = subprocess.check_output(diffCmd)
229 ver +=
"+" + hashlib.md5(diff).hexdigest()
233 packages[prod.name] = ver
238 """A table of packages and their versions. 243 A mapping {package: version} where both keys and values are type `str`. 247 This is essentially a wrapper around a dict with some conveniences. 251 assert isinstance(packages, Mapping)
253 self.
_names = set(packages.keys())
257 """Construct a `Packages` by examining the system. 259 Determine packages by examining python's sys.modules, runtime libraries and EUPS. 263 packages : `Packages` 273 """Read packages from filename. 278 Filename from which to read. 282 packages : `Packages` 284 with open(filename,
"rb")
as ff:
285 return pickle.load(ff)
293 Filename to which to write. 295 with open(filename,
"wb")
as ff:
296 pickle.dump(self, ff)
302 ss =
"%s({\n" % self.__class__.__name__
304 ss +=
",\n".join(
"%s: %s" % (repr(prod), repr(self.
_packages[prod]))
for 305 prod
in sorted(self.
_names))
310 return "%s(%s)" % (self.__class__.__name__, repr(self.
_packages))
319 """Update packages with contents of another set of packages. 324 Other packages to merge with self. 328 No check is made to see if we're clobbering anything. 334 """Get packages in self but not in another `Packages` object. 339 Other packages to compare against. 345 Keys (type `str`) are package names; values (type `str`) are their versions. 347 return {pkg: self.
_packages[pkg]
for pkg
in self.
_names - other._names}
350 """Get packages in another `Packages` object but missing from self. 355 Other packages to compare against. 361 Keys (type `str`) are package names; values (type `str`) are their versions. 363 return {pkg: other._packages[pkg]
for pkg
in other._names - self.
_names}
366 """Get packages in symmetric difference of self and another `Packages` object. 371 Other packages to compare against. 376 Packages in symmetric difference. 377 Keys (type `str`) are package names; values (type `str`) are their versions. 379 return {pkg: (self.
_packages[pkg], other._packages[pkg])
for 380 pkg
in self.
_names & other._names
if self.
_packages[pkg] != other._packages[pkg]}
def __init__(self, packages)
def write(self, filename)
std::map< std::string, std::string > getRuntimeVersions()
Return version strings for dependencies.
def getEnvironmentPackages()
def getVersionFromPythonModule(module)
def __contains__(self, pkg)
def difference(self, other)