This repository has been archived by the owner on Apr 26, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Make the dependencies more like a standard Python project and hook up the optional dependencies to setuptools #4298
Merged
Merged
Changes from 8 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
3001276
make the deps more python like and remove things that setuptools does…
hawkowl 6fd6ec4
fix
hawkowl ae4037f
changelog
hawkowl 7f923a9
fixup
hawkowl 5d63be7
review comments
hawkowl 87ad49b
review comments
hawkowl 92202d0
review comments
hawkowl ce96284
pep8
hawkowl abd5074
add comment
hawkowl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Synapse can now have its conditional/extra dependencies installed by pip. This functionality can be used by using `pip install matrix-synapse[feature]`, where feature is a comma separated list with the possible values "email.enable_notifs", "ldap3", "postgres", "saml2", "url_preview", and "test". If you want to install all optional dependencies, you can use "all" instead. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,175 +15,109 @@ | |
# limitations under the License. | ||
|
||
import logging | ||
from distutils.version import LooseVersion | ||
|
||
logger = logging.getLogger(__name__) | ||
from pkg_resources import DistributionNotFound, VersionConflict, get_distribution | ||
|
||
# this dict maps from python package name to a list of modules we expect it to | ||
# provide. | ||
# | ||
# the key is a "requirement specifier", as used as a parameter to `pip | ||
# install`[1], or an `install_requires` argument to `setuptools.setup` [2]. | ||
# | ||
# the value is a sequence of strings; each entry should be the name of the | ||
# python module, optionally followed by a version assertion which can be either | ||
# ">=<ver>" or "==<ver>". | ||
# | ||
# [1] https://pip.pypa.io/en/stable/reference/pip_install/#requirement-specifiers. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: it'd be nice to keep this link so I don't have to go and look it up. |
||
# [2] https://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-dependencies | ||
REQUIREMENTS = { | ||
"jsonschema>=2.5.1": ["jsonschema>=2.5.1"], | ||
"frozendict>=1": ["frozendict"], | ||
"unpaddedbase64>=1.1.0": ["unpaddedbase64>=1.1.0"], | ||
"canonicaljson>=1.1.3": ["canonicaljson>=1.1.3"], | ||
"signedjson>=1.0.0": ["signedjson>=1.0.0"], | ||
"pynacl>=1.2.1": ["nacl>=1.2.1", "nacl.bindings"], | ||
"service_identity>=16.0.0": ["service_identity>=16.0.0"], | ||
"Twisted>=17.1.0": ["twisted>=17.1.0"], | ||
"treq>=15.1": ["treq>=15.1"], | ||
logger = logging.getLogger(__name__) | ||
|
||
REQUIREMENTS = [ | ||
"jsonschema>=2.5.1", | ||
"frozendict>=1", | ||
"unpaddedbase64>=1.1.0", | ||
"canonicaljson>=1.1.3", | ||
"signedjson>=1.0.0", | ||
"pynacl>=1.2.1", | ||
"service_identity>=16.0.0", | ||
"Twisted>=17.1.0", | ||
"treq>=15.1", | ||
# Twisted has required pyopenssl 16.0 since about Twisted 16.6. | ||
"pyopenssl>=16.0.0": ["OpenSSL>=16.0.0"], | ||
|
||
"pyyaml>=3.11": ["yaml"], | ||
"pyasn1>=0.1.9": ["pyasn1"], | ||
"pyasn1-modules>=0.0.7": ["pyasn1_modules"], | ||
"daemonize>=2.3.1": ["daemonize"], | ||
"bcrypt>=3.1.0": ["bcrypt>=3.1.0"], | ||
"pillow>=3.1.2": ["PIL"], | ||
"sortedcontainers>=1.4.4": ["sortedcontainers"], | ||
"psutil>=2.0.0": ["psutil>=2.0.0"], | ||
"pymacaroons-pynacl>=0.9.3": ["pymacaroons"], | ||
"msgpack-python>=0.4.2": ["msgpack"], | ||
"phonenumbers>=8.2.0": ["phonenumbers"], | ||
"six>=1.10": ["six"], | ||
|
||
"pyopenssl>=16.0.0", | ||
"pyyaml>=3.11", | ||
"pyasn1>=0.1.9", | ||
"pyasn1-modules>=0.0.7", | ||
"daemonize>=2.3.1", | ||
"bcrypt>=3.1.0", | ||
"pillow>=3.1.2", | ||
"sortedcontainers>=1.4.4", | ||
"psutil>=2.0.0", | ||
"pymacaroons-pynacl>=0.9.3", | ||
"msgpack-python>=0.4.2", | ||
"phonenumbers>=8.2.0", | ||
"six>=1.10", | ||
# prometheus_client 0.4.0 changed the format of counter metrics | ||
# (cf https://github.com/matrix-org/synapse/issues/4001) | ||
"prometheus_client>=0.0.18,<0.4.0": ["prometheus_client"], | ||
|
||
"prometheus_client>=0.0.18,<0.4.0", | ||
# we use attr.s(slots), which arrived in 16.0.0 | ||
"attrs>=16.0.0": ["attr>=16.0.0"], | ||
"netaddr>=0.7.18": ["netaddr"], | ||
} | ||
"attrs>=16.0.0", | ||
"netaddr>=0.7.18", | ||
] | ||
|
||
CONDITIONAL_REQUIREMENTS = { | ||
"email.enable_notifs": { | ||
"Jinja2>=2.8": ["Jinja2>=2.8"], | ||
"bleach>=1.4.2": ["bleach>=1.4.2"], | ||
}, | ||
"matrix-synapse-ldap3": { | ||
"matrix-synapse-ldap3>=0.1": ["ldap_auth_provider"], | ||
}, | ||
"postgres": { | ||
"psycopg2>=2.6": ["psycopg2"] | ||
}, | ||
"saml2": { | ||
"pysaml2>=4.5.0": ["saml2"], | ||
}, | ||
"email.enable_notifs": ["Jinja2>=2.8", "bleach>=1.4.2"], | ||
"matrix-synapse-ldap3": ["matrix-synapse-ldap3>=0.1"], | ||
"postgres": ["psycopg2>=2.6"], | ||
"saml2": ["pysaml2>=4.5.0"], | ||
"url_preview": ["lxml>=3.5.0"], | ||
"test": ["mock>=2.0"], | ||
} | ||
|
||
|
||
def requirements(config=None, include_conditional=False): | ||
reqs = REQUIREMENTS.copy() | ||
if include_conditional: | ||
for _, req in CONDITIONAL_REQUIREMENTS.items(): | ||
reqs.update(req) | ||
return reqs | ||
def list_requirements(): | ||
deps = set(REQUIREMENTS) | ||
for opt in CONDITIONAL_REQUIREMENTS.values(): | ||
deps = set(opt) | deps | ||
|
||
return list(deps) | ||
|
||
|
||
def github_link(project, version, egg): | ||
return "https://github.com/%s/tarball/%s/#egg=%s" % (project, version, egg) | ||
class DependencyException(Exception): | ||
@property | ||
def dependencies(self): | ||
for i in self.args[0]: | ||
yield '"' + i + '"' | ||
|
||
|
||
DEPENDENCY_LINKS = { | ||
} | ||
def check_requirements(_get_distribution=get_distribution): | ||
|
||
deps_needed = [] | ||
errors = [] | ||
|
||
class MissingRequirementError(Exception): | ||
def __init__(self, message, module_name, dependency): | ||
super(MissingRequirementError, self).__init__(message) | ||
self.module_name = module_name | ||
self.dependency = dependency | ||
|
||
|
||
def check_requirements(config=None): | ||
"""Checks that all the modules needed by synapse have been correctly | ||
installed and are at the correct version""" | ||
for dependency, module_requirements in ( | ||
requirements(config, include_conditional=False).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), 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 | ||
# Check the base dependencies exist -- they all must be installed. | ||
for dependency in REQUIREMENTS: | ||
try: | ||
_get_distribution(dependency) | ||
except VersionConflict as e: | ||
deps_needed.append(dependency) | ||
errors.append( | ||
"Needed %s, got %s==%s" | ||
% (dependency, e.dist.project_name, e.dist.version) | ||
) | ||
except DistributionNotFound: | ||
deps_needed.append(dependency) | ||
errors.append("Needed %s but it was not installed" % (dependency,)) | ||
|
||
if version_test == ">=": | ||
if version is None: | ||
raise MissingRequirementError( | ||
"Version of %r isn't set as __version__ of module %r" | ||
% (dependency, module_name), module_name, dependency | ||
) | ||
if LooseVersion(version) < LooseVersion(required_version): | ||
raise MissingRequirementError( | ||
"Version of %r in %r is too old. %r < %r" | ||
% (dependency, file_path, version, required_version), | ||
module_name, dependency | ||
) | ||
elif version_test == "==": | ||
if version is None: | ||
raise MissingRequirementError( | ||
"Version of %r isn't set as __version__ of module %r" | ||
% (dependency, module_name), module_name, dependency | ||
) | ||
if LooseVersion(version) != LooseVersion(required_version): | ||
raise MissingRequirementError( | ||
"Unexpected version of %r in %r. %r != %r" | ||
% (dependency, file_path, version, required_version), | ||
module_name, dependency | ||
) | ||
# Check the optional dependencies are up to date. We allow them to not be | ||
# installed. | ||
OPTS = sum(CONDITIONAL_REQUIREMENTS.values(), []) | ||
|
||
for dependency in OPTS: | ||
try: | ||
_get_distribution(dependency) | ||
except VersionConflict: | ||
deps_needed.append(dependency) | ||
errors.append("Needed %s but it was not installed" % (dependency,)) | ||
except DistributionNotFound: | ||
# If it's not found, we don't care | ||
pass | ||
|
||
def list_requirements(): | ||
result = [] | ||
linked = [] | ||
for link in DEPENDENCY_LINKS.values(): | ||
egg = link.split("#egg=")[1] | ||
linked.append(egg.split('-')[0]) | ||
result.append(link) | ||
for requirement in requirements(include_conditional=True): | ||
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 deps_needed: | ||
for e in errors: | ||
logging.exception(e) | ||
|
||
raise DependencyException(deps_needed) | ||
|
||
|
||
if __name__ == "__main__": | ||
import sys | ||
|
||
sys.stdout.writelines(req + "\n" for req in list_requirements()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we not keep this? looks like it should be easy enough, and it will avoid confusion when people don't install the optional deps and then enable url previews or something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[per discussion online: it never worked anyway]