1
+ # Copyright 2022 The Matrix.org Foundation C.I.C.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+
16
+ """
17
+ This module exposes a single function which checks synapse's dependencies are present
18
+ and correctly versioned. It makes use of `importlib.metadata` to do so. The details
19
+ are a bit murky: there's no easy way to get a map from "extras" to the packages they
20
+ require. But this is probably just symptomatic of Python's package management.
21
+ """
22
+
1
23
import logging
2
24
from typing import Iterable , NamedTuple , Optional
3
25
10
32
except ImportError :
11
33
import importlib_metadata as metadata # type: ignore[no-redef]
12
34
35
+ __all__ = ["check_requirements" ]
36
+
13
37
14
38
class DependencyException (Exception ):
15
39
@property
@@ -29,7 +53,17 @@ def dependencies(self) -> Iterable[str]:
29
53
yield '"' + i + '"'
30
54
31
55
32
- EXTRAS = set (metadata .metadata (DISTRIBUTION_NAME ).get_all ("Provides-Extra" ))
56
+ DEV_EXTRAS = {"lint" , "mypy" , "test" , "dev" }
57
+ RUNTIME_EXTRAS = (
58
+ set (metadata .metadata (DISTRIBUTION_NAME ).get_all ("Provides-Extra" )) - DEV_EXTRAS
59
+ )
60
+ VERSION = metadata .version (DISTRIBUTION_NAME )
61
+
62
+
63
+ def _is_dev_dependency (req : Requirement ) -> bool :
64
+ return req .marker is not None and any (
65
+ req .marker .evaluate ({"extra" : e }) for e in DEV_EXTRAS
66
+ )
33
67
34
68
35
69
class Dependency (NamedTuple ):
@@ -43,6 +77,9 @@ def _generic_dependencies() -> Iterable[Dependency]:
43
77
assert requirements is not None
44
78
for raw_requirement in requirements :
45
79
req = Requirement (raw_requirement )
80
+ if _is_dev_dependency (req ):
81
+ continue
82
+
46
83
# https://packaging.pypa.io/en/latest/markers.html#usage notes that
47
84
# > Evaluating an extra marker with no environment is an error
48
85
# so we pass in a dummy empty extra value here.
@@ -56,6 +93,8 @@ def _dependencies_for_extra(extra: str) -> Iterable[Dependency]:
56
93
assert requirements is not None
57
94
for raw_requirement in requirements :
58
95
req = Requirement (raw_requirement )
96
+ if _is_dev_dependency (req ):
97
+ continue
59
98
# Exclude mandatory deps by only selecting deps needed with this extra.
60
99
if (
61
100
req .marker is not None
@@ -67,18 +106,26 @@ def _dependencies_for_extra(extra: str) -> Iterable[Dependency]:
67
106
68
107
def _not_installed (requirement : Requirement , extra : Optional [str ] = None ) -> str :
69
108
if extra :
70
- return f"Need { requirement .name } for { extra } , but it is not installed"
109
+ return (
110
+ f"Synapse { VERSION } needs { requirement .name } for { extra } , "
111
+ f"but it is not installed"
112
+ )
71
113
else :
72
- return f"Need { requirement .name } , but it is not installed"
114
+ return f"Synapse { VERSION } needs { requirement .name } , but it is not installed"
73
115
74
116
75
117
def _incorrect_version (
76
118
requirement : Requirement , got : str , extra : Optional [str ] = None
77
119
) -> str :
78
120
if extra :
79
- return f"Need { requirement } for { extra } , but got { requirement .name } =={ got } "
121
+ return (
122
+ f"Synapse { VERSION } needs { requirement } for { extra } , "
123
+ f"but got { requirement .name } =={ got } "
124
+ )
80
125
else :
81
- return f"Need { requirement } , but got { requirement .name } =={ got } "
126
+ return (
127
+ f"Synapse { VERSION } needs { requirement } , but got { requirement .name } =={ got } "
128
+ )
82
129
83
130
84
131
def check_requirements (extra : Optional [str ] = None ) -> None :
@@ -100,10 +147,10 @@ def check_requirements(extra: Optional[str] = None) -> None:
100
147
# First work out which dependencies are required, and which are optional.
101
148
if extra is None :
102
149
dependencies = _generic_dependencies ()
103
- elif extra in EXTRAS :
150
+ elif extra in RUNTIME_EXTRAS :
104
151
dependencies = _dependencies_for_extra (extra )
105
152
else :
106
- raise ValueError (f"Synapse does not provide the feature '{ extra } '" )
153
+ raise ValueError (f"Synapse { VERSION } does not provide the feature '{ extra } '" )
107
154
108
155
deps_unfulfilled = []
109
156
errors = []
0 commit comments