diff --git a/synapse/python_dependencies.py b/synapse/python_dependencies.py
new file mode 100644
index 0000000000..a89d618606
--- /dev/null
+++ b/synapse/python_dependencies.py
@@ -0,0 +1,122 @@
+import logging
+from distutils.version import LooseVersion
+
+logger = logging.getLogger(__name__)
+
+REQUIREMENTS = {
+ "syutil==0.0.2": ["syutil"],
+ "matrix_angular_sdk==0.6.0": ["syweb>=0.6.0"],
+ "Twisted==14.0.2": ["twisted==14.0.2"],
+ "service_identity>=1.0.0": ["service_identity>=1.0.0"],
+ "pyopenssl>=0.14": ["OpenSSL>=0.14"],
+ "pyyaml": ["yaml"],
+ "pyasn1": ["pyasn1"],
+ "pynacl": ["nacl"],
+ "daemonize": ["daemonize"],
+ "py-bcrypt": ["bcrypt"],
+ "frozendict>=0.4": ["frozendict"],
+ "pillow": ["PIL"],
+ "pydenticon": ["pydenticon"],
+}
+
+def github_link(project, version, egg):
+ return "https://github.com/%s/tarball/%s/#egg=%s" % (project, version, egg)
+
+DEPENDENCY_LINKS=[
+ github_link(
+ project="matrix-org/syutil",
+ version="v0.0.2",
+ egg="syutil-0.0.2",
+ ),
+ github_link(
+ project="matrix-org/matrix-angular-sdk",
+ version="v0.6.0",
+ egg="matrix_angular_sdk-0.6.0",
+ ),
+ github_link(
+ project="pyca/pynacl",
+ version="d4d3175589b892f6ea7c22f466e0e223853516fa",
+ egg="pynacl-0.3.0",
+ )
+]
+
+
+class MissingRequirementError(Exception):
+ pass
+
+
+def check_requirements():
+ """Checks that all the modules needed by synapse have been correctly
+ installed and are at the correct version"""
+ for dependency, module_requirements in REQUIREMENTS.items():
+ for module_requirement in module_requirements:
+ if ">=" in module_requirement:
+ module_name, required_version = module_requirement.split(">=")
+ version_test = ">="
+ elif "==" in module_requirement:
+ module_name, required_version = module_requirement.split("==")
+ version_test = "=="
+ else:
+ module_name = module_requirement
+ version_test = None
+
+ try:
+ module = __import__(module_name)
+ except ImportError:
+ logging.exception(
+ "Can't import %r which is part of %r",
+ module_name, dependency
+ )
+ raise MissingRequirementError(
+ "Can't import %r which is part of %r"
+ % (module_name, dependency)
+ )
+ version = getattr(module, "__version__", None)
+ file_path = getattr(module, "__file__", None)
+ logger.info(
+ "Using %r version %r from %r to satisfy %r",
+ module_name, version, file_path, dependency
+ )
+
+ if version_test == ">=":
+ if version is None:
+ raise MissingRequirementError(
+ "Version of %r isn't set as __version__ of module %r"
+ % (dependency, module_name)
+ )
+ if LooseVersion(version) < LooseVersion(required_version):
+ raise MissingRequirementError(
+ "Version of %r in %r is too old. %r < %r"
+ % (dependency, file_path, version, required_version)
+ )
+ elif version_test == "==":
+ if version is None:
+ raise MissingRequirementError(
+ "Version of %r isn't set as __version__ of module %r"
+ % (dependency, module_name)
+ )
+ if LooseVersion(version) != LooseVersion(required_version):
+ raise MissingRequirementError(
+ "Unexpected version of %r in %r. %r != %r"
+ % (dependency, file_path, version, required_version)
+ )
+
+def list_requirements():
+ result = []
+ linked = []
+ for link in DEPENDENCY_LINKS:
+ egg = link.split("#egg=")[1]
+ linked.append(egg.split('-')[0])
+ result.append(link)
+ for requirement in REQUIREMENTS:
+ is_linked = False
+ for link in linked:
+ if requirement.replace('-','_').startswith(link):
+ is_linked = True
+ if not is_linked:
+ result.append(requirement)
+ return result
+
+if __name__ == "__main__":
+ import sys
+ sys.stdout.writelines(req + "\n" for req in list_requirements())
|