Switch to passlib.PasswordHash.hash
[mudpy.git] / mudpy / version.py
1 """Version and diagnostic information for the mudpy engine."""
2
3 # Copyright (c) 2018 mudpy authors. Permission to use, copy,
4 # modify, and distribute this software is granted under terms
5 # provided in the LICENSE file distributed with this software.
6
7 import json
8 import pkg_resources
9 import sys
10
11
12 class VersionDetail:
13
14     """Version detail for a Python package."""
15
16     def __init__(self, package):
17         self.project_name = _normalize_project(package.project_name)
18         version = package.version
19         self.version_info = tuple(version.split('.'))
20
21         # Build up a human-friendly version string for display purposes
22         self.text = "%s %s" % (self.project_name, version)
23
24         # Obtain Git commit ID from PBR metadata if present
25         dist = pkg_resources.get_distribution(self.project_name)
26         try:
27             self.git_version = json.loads(
28                 dist.get_metadata("pbr.json"))["git_version"]
29             self.text = "%s (%s)" % (self.text, self.git_version)
30         except (IOError, KeyError):
31             self.git_version = None
32
33     def __repr__(self):
34         return self.text
35
36
37 class Versions:
38
39     """Tracks info on known Python package versions."""
40
41     def __init__(self, project_name):
42         # Normalize the supplied name
43         project_name = _normalize_project(project_name)
44
45         # Python info for convenience
46         self.python_version = "%s Python %s" % (
47             sys.platform, sys.version.split(" ")[0])
48
49         # List of package names for this package's declared dependencies
50         requirements = []
51         for package in pkg_resources.get_distribution(project_name).requires():
52             requirements.append(_normalize_project(package.project_name))
53
54         # Accumulators for Python package versions
55         self.dependencies = {}
56         self.environment = {}
57
58         # Loop over all installed packages
59         for package in pkg_resources.working_set:
60             version = VersionDetail(package)
61             # Sort packages into the corresponding buckets
62             if version.project_name in requirements:
63                 # This is a dependency
64                 self.dependencies[version.project_name] = version
65             elif version.project_name == project_name:
66                 # This is our main package
67                 self.version = version
68             else:
69                 # This may be a transitive dep, or merely installed
70                 self.environment[version.project_name] = version
71
72         self.dependencies_text = ", ".join(
73             sorted([x.text for x in self.dependencies.values()]))
74         self.environment_text = ", ".join(
75             sorted([x.text for x in self.environment.values()]))
76
77     def __repr__(self):
78         return "Running %s on %s with dependencies %s." % (
79             self.version.text,
80             self.python_version,
81             self.dependencies_text,
82             )
83
84
85 def _normalize_project(project_name):
86     """Convenience function to normalize Python project names."""
87     return pkg_resources.safe_name(project_name).lower()