Status: | Merged |
---|---|
Merged at revision: | 493 |
Proposed branch: | lp:~jderose/degu/py3.10 |
Merge into: | lp:degu |
Diff against target: |
439 lines (+61/-52) 9 files modified
debian/control (+1/-0) degu/client.py (+3/-3) degu/server.py (+3/-3) degu/tests/test_base.py (+22/-16) degu/tests/test_client.py (+10/-10) degu/tests/test_server.py (+12/-12) degu/tests/test_sslhelpers.py (+8/-5) doc/degu.client.rst (+1/-1) setup.py (+1/-2) |
To merge this branch: | bzr merge lp:~jderose/degu/py3.10 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jeremy Soller (community) | Approve | ||
Review via email: mp+424913@code.launchpad.net |
Commit message
Support Python 3.10
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'debian/control' |
2 | --- debian/control 2021-01-13 13:00:43 +0000 |
3 | +++ debian/control 2022-06-16 23:17:03 +0000 |
4 | @@ -6,6 +6,7 @@ |
5 | dh-python, |
6 | python3-all-dev (>= 3.8), |
7 | python3-all-dbg (>= 3.8), |
8 | + python3-setuptools, |
9 | python3-sphinx, |
10 | pyflakes3, |
11 | clang-tools, |
12 | |
13 | === modified file 'degu/client.py' |
14 | --- degu/client.py 2016-05-18 04:00:39 +0000 |
15 | +++ degu/client.py 2022-06-16 23:17:03 +0000 |
16 | @@ -82,7 +82,7 @@ |
17 | ) |
18 | ) |
19 | |
20 | - sslctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) |
21 | + sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) |
22 | sslctx.verify_mode = ssl.CERT_REQUIRED |
23 | sslctx.set_ciphers( |
24 | 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384' |
25 | @@ -116,8 +116,8 @@ |
26 | |
27 | if not isinstance(sslctx, ssl.SSLContext): |
28 | raise TypeError('sslctx must be an ssl.SSLContext') |
29 | - if sslctx.protocol != ssl.PROTOCOL_TLSv1_2: |
30 | - raise ValueError('sslctx.protocol must be ssl.PROTOCOL_TLSv1_2') |
31 | + if sslctx.protocol != ssl.PROTOCOL_TLS_CLIENT: |
32 | + raise ValueError('sslctx.protocol must be ssl.PROTOCOL_TLS_CLIENT') |
33 | if not (sslctx.options & ssl.OP_NO_COMPRESSION): |
34 | raise ValueError('sslctx.options must include ssl.OP_NO_COMPRESSION') |
35 | if sslctx.verify_mode != ssl.CERT_REQUIRED: |
36 | |
37 | === modified file 'degu/server.py' |
38 | --- degu/server.py 2019-11-26 01:04:54 +0000 |
39 | +++ degu/server.py 2022-06-16 23:17:03 +0000 |
40 | @@ -69,7 +69,7 @@ |
41 | ) |
42 | ) |
43 | |
44 | - sslctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) |
45 | + sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) |
46 | sslctx.set_ciphers( |
47 | 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384' |
48 | ) |
49 | @@ -109,8 +109,8 @@ |
50 | |
51 | if not isinstance(sslctx, ssl.SSLContext): |
52 | raise TypeError('sslctx must be an ssl.SSLContext') |
53 | - if sslctx.protocol != ssl.PROTOCOL_TLSv1_2: |
54 | - raise ValueError('sslctx.protocol must be ssl.PROTOCOL_TLSv1_2') |
55 | + if sslctx.protocol != ssl.PROTOCOL_TLS_SERVER: |
56 | + raise ValueError('sslctx.protocol must be ssl.PROTOCOL_TLS_SERVER') |
57 | |
58 | # We consider ssl.CERT_OPTIONAL to be a bad grey area: |
59 | if sslctx.verify_mode == ssl.CERT_OPTIONAL: |
60 | |
61 | === modified file 'degu/tests/test_base.py' |
62 | --- degu/tests/test_base.py 2021-01-11 18:50:42 +0000 |
63 | +++ degu/tests/test_base.py 2022-06-16 23:17:03 +0000 |
64 | @@ -425,9 +425,9 @@ |
65 | if self.backend is _base: |
66 | msg = ARG_MSG_C.format(fullname, number, len(args)) |
67 | elif len(args) < number: |
68 | - msg = ARG_MSG_Py_1.format(name, missing) |
69 | + msg = ARG_MSG_Py_1.format(fullname, missing) |
70 | else: |
71 | - msg = ARG_MSG_Py_2.format(name, number + 1, number + 2) |
72 | + msg = ARG_MSG_Py_2.format(fullname, number + 1, number + 2) |
73 | self.assertEqual(str(cm.exception), msg) |
74 | |
75 | def check_method_args(self, inst, name, number, missing): |
76 | @@ -803,10 +803,6 @@ |
77 | self.assertEqual(str(r), 'bytes=0-9999999999999998') |
78 | |
79 | # Check reference counting: |
80 | - if self.backend is _base: |
81 | - delmsg = 'readonly attribute' |
82 | - else: |
83 | - delmsg = "can't delete attribute" |
84 | for i in range(1000): |
85 | stop = random.randrange(1, MAX_LENGTH + 1) |
86 | start = random.randrange(0, stop) |
87 | @@ -827,6 +823,10 @@ |
88 | # start, stop should be read-only: |
89 | r = self.Range(start, stop) |
90 | for name in ('start', 'stop'): |
91 | + if self.backend is _base: |
92 | + delmsg = 'readonly attribute' |
93 | + else: |
94 | + delmsg = "can't delete attribute '" + name + "'" |
95 | with self.assertRaises(AttributeError) as cm: |
96 | delattr(r, name) |
97 | self.assertEqual(str(cm.exception), delmsg) |
98 | @@ -1139,10 +1139,6 @@ |
99 | ) |
100 | |
101 | # Check reference counting: |
102 | - if self.backend is _base: |
103 | - delmsg = 'readonly attribute' |
104 | - else: |
105 | - delmsg = "can't delete attribute" |
106 | for i in range(1000): |
107 | stop = random.randrange(1, MAX_LENGTH + 1) |
108 | start = random.randrange(0, stop) |
109 | @@ -1172,6 +1168,10 @@ |
110 | # start, stop, total should be read-only: |
111 | r = self.ContentRange(start, stop, total) |
112 | for name in ('start', 'stop', 'total'): |
113 | + if self.backend is _base: |
114 | + delmsg = 'readonly attribute' |
115 | + else: |
116 | + delmsg = "can't delete attribute '" + name + "'" |
117 | with self.assertRaises(AttributeError) as cm: |
118 | delattr(r, name) |
119 | self.assertEqual(str(cm.exception), delmsg) |
120 | @@ -2170,6 +2170,7 @@ |
121 | self.assertEqual(str(cm.exception), |
122 | "'MissingReadline' object has no attribute 'readline'" |
123 | ) |
124 | + del cm # Must del context manager before testing refcount (py 3.10+): |
125 | self.assertEqual(sys.getrefcount(rfile), 2) |
126 | |
127 | # rfile.readline() not callable: |
128 | @@ -2195,6 +2196,7 @@ |
129 | self.assertEqual(str(cm.exception), |
130 | "'MissingRead' object has no attribute 'readinto'" |
131 | ) |
132 | + del cm # Must del context manager before testing refcount (py 3.10+): |
133 | self.assertEqual(sys.getrefcount(rfile), 2) |
134 | |
135 | # rfile.readinto() not callable: |
136 | @@ -3505,13 +3507,13 @@ |
137 | Check body instance attributes that should be read-only. |
138 | """ |
139 | assert len(members) >= 2 |
140 | - if self.backend is _basepy: |
141 | - setmsg = "can't set attribute" |
142 | - delmsg = "can't delete attribute" |
143 | - else: |
144 | - setmsg = 'readonly attribute' |
145 | - delmsg = 'readonly attribute' |
146 | for name in members: |
147 | + if self.backend is _basepy: |
148 | + setmsg = "can't set attribute '" + name + "'" |
149 | + delmsg = "can't delete attribute '" + name + "'" |
150 | + else: |
151 | + setmsg = 'readonly attribute' |
152 | + delmsg = 'readonly attribute' |
153 | value = getattr(body, name) |
154 | with self.assertRaises(AttributeError) as cm: |
155 | setattr(body, name, value) |
156 | @@ -3993,6 +3995,7 @@ |
157 | self.assertEqual(str(cm.exception), |
158 | "'MissingReadline' object has no attribute 'readline'" |
159 | ) |
160 | + del cm # Must del context manager before testing refcount (py 3.10+): |
161 | self.assertEqual(sys.getrefcount(rfile), 2) |
162 | |
163 | # Not a backend.SocketWrapper, rfile.readline() not callable: |
164 | @@ -4018,6 +4021,7 @@ |
165 | self.assertEqual(str(cm.exception), |
166 | "'MissingRead' object has no attribute 'readinto'" |
167 | ) |
168 | + del cm # Must del context manager before testing refcount (py 3.10+): |
169 | self.assertEqual(sys.getrefcount(rfile), 2) |
170 | |
171 | # Not a backend.SocketWrapper, rfile.readinto() not callable: |
172 | @@ -6245,6 +6249,7 @@ |
173 | self.assertEqual(str(cm.exception), |
174 | "'BadSocket1' object has no attribute 'recv_into'" |
175 | ) |
176 | + del cm # Must del context manager before testing refcount (py 3.10+): |
177 | self.assertEqual(sys.getrefcount(sock), 2) |
178 | self.assertEqual(sock._calls, ['close']) |
179 | |
180 | @@ -6259,6 +6264,7 @@ |
181 | self.assertEqual(str(cm.exception), |
182 | "'BadSocket2' object has no attribute 'send'" |
183 | ) |
184 | + del cm # Must del context manager before testing refcount (py 3.10+): |
185 | self.assertEqual(sys.getrefcount(sock), 2) |
186 | self.assertEqual(sock._calls, ['close']) |
187 | |
188 | |
189 | === modified file 'degu/tests/test_client.py' |
190 | --- degu/tests/test_client.py 2017-09-04 23:38:30 +0000 |
191 | +++ degu/tests/test_client.py 2022-06-16 23:17:03 +0000 |
192 | @@ -142,7 +142,7 @@ |
193 | for func in client_sslctx_funcs: |
194 | sslctx = func({}) |
195 | self.assertIsInstance(sslctx, ssl.SSLContext) |
196 | - self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLSv1_2) |
197 | + self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLS_CLIENT) |
198 | self.assertEqual(sslctx.verify_mode, ssl.CERT_REQUIRED) |
199 | self.assertIs(sslctx.check_hostname, True) |
200 | |
201 | @@ -159,7 +159,7 @@ |
202 | for func in client_sslctx_funcs: |
203 | sslctx = func({'check_hostname': True}) |
204 | self.assertIsInstance(sslctx, ssl.SSLContext) |
205 | - self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLSv1_2) |
206 | + self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLS_CLIENT) |
207 | self.assertEqual(sslctx.verify_mode, ssl.CERT_REQUIRED) |
208 | self.assertIs(sslctx.check_hostname, True) |
209 | |
210 | @@ -173,7 +173,7 @@ |
211 | for func in client_sslctx_funcs: |
212 | sslctx = func(sslconfig) |
213 | self.assertIsInstance(sslctx, ssl.SSLContext) |
214 | - self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLSv1_2) |
215 | + self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLS_CLIENT) |
216 | self.assertEqual(sslctx.verify_mode, ssl.CERT_REQUIRED) |
217 | self.assertIs(sslctx.check_hostname, False) |
218 | |
219 | @@ -182,7 +182,7 @@ |
220 | for func in client_sslctx_funcs: |
221 | sslctx = func(sslconfig) |
222 | self.assertIsInstance(sslctx, ssl.SSLContext) |
223 | - self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLSv1_2) |
224 | + self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLS_CLIENT) |
225 | self.assertEqual(sslctx.verify_mode, ssl.CERT_REQUIRED) |
226 | self.assertIs(sslctx.check_hostname, True) |
227 | |
228 | @@ -193,7 +193,7 @@ |
229 | for func in client_sslctx_funcs: |
230 | sslctx = func(sslconfig) |
231 | self.assertIsInstance(sslctx, ssl.SSLContext) |
232 | - self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLSv1_2) |
233 | + self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLS_CLIENT) |
234 | self.assertEqual(sslctx.verify_mode, ssl.CERT_REQUIRED) |
235 | self.assertIs(sslctx.check_hostname, False) |
236 | |
237 | @@ -202,7 +202,7 @@ |
238 | for func in client_sslctx_funcs: |
239 | sslctx = func(sslconfig) |
240 | self.assertIsInstance(sslctx, ssl.SSLContext) |
241 | - self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLSv1_2) |
242 | + self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLS_CLIENT) |
243 | self.assertEqual(sslctx.verify_mode, ssl.CERT_REQUIRED) |
244 | self.assertIs(sslctx.check_hostname, True) |
245 | |
246 | @@ -470,11 +470,11 @@ |
247 | self.assertEqual(str(cm.exception), 'sslctx must be an ssl.SSLContext') |
248 | |
249 | # Bad SSL protocol version: |
250 | - sslctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) |
251 | + sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) |
252 | with self.assertRaises(ValueError) as cm: |
253 | client.SSLClient(sslctx, None) |
254 | self.assertEqual(str(cm.exception), |
255 | - 'sslctx.protocol must be ssl.PROTOCOL_TLSv1_2' |
256 | + 'sslctx.protocol must be ssl.PROTOCOL_TLS_CLIENT' |
257 | ) |
258 | |
259 | # Note: Python 3.3.4 (and presumably 3.4.0) now disables SSLv2 by |
260 | @@ -482,7 +482,7 @@ |
261 | # we cannot unset the ssl.OP_NO_SSLv2 bit, we can't unit test to check |
262 | # that Degu enforces this, so for now, we set the bit here so it works |
263 | # with Python 3.3.3 still; see: http://bugs.python.org/issue20207 |
264 | - sslctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) |
265 | + sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) |
266 | sslctx.options |= ssl.OP_NO_SSLv2 |
267 | |
268 | # not (options & ssl.OP_NO_COMPRESSION) |
269 | @@ -494,7 +494,7 @@ |
270 | ) |
271 | |
272 | # verify_mode is not ssl.CERT_REQUIRED: |
273 | - sslctx.options |= ssl.OP_NO_COMPRESSION |
274 | + sslctx.verify_mode = ssl.CERT_OPTIONAL |
275 | with self.assertRaises(ValueError) as cm: |
276 | client.SSLClient(sslctx, None) |
277 | self.assertEqual(str(cm.exception), |
278 | |
279 | === modified file 'degu/tests/test_server.py' |
280 | --- degu/tests/test_server.py 2019-11-26 01:04:54 +0000 |
281 | +++ degu/tests/test_server.py 2022-06-16 23:17:03 +0000 |
282 | @@ -120,7 +120,7 @@ |
283 | ) |
284 | for func in server_sslctx_funcs: |
285 | sslctx = func(pki.server_sslconfig) |
286 | - self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLSv1_2) |
287 | + self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLS_SERVER) |
288 | self.assertEqual(sslctx.verify_mode, ssl.CERT_REQUIRED) |
289 | |
290 | # New in Degu 0.3: should not be able to accept connections from |
291 | @@ -138,7 +138,7 @@ |
292 | sslconfig['allow_unauthenticated_clients'] = True |
293 | for func in server_sslctx_funcs: |
294 | sslctx = func(sslconfig) |
295 | - self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLSv1_2) |
296 | + self.assertEqual(sslctx.protocol, ssl.PROTOCOL_TLS_SERVER) |
297 | self.assertEqual(sslctx.verify_mode, ssl.CERT_NONE) |
298 | |
299 | # Cannot mix ca_file/ca_path with allow_unauthenticated_clients: |
300 | @@ -176,13 +176,13 @@ |
301 | |
302 | # Wrong protocol: |
303 | with self.assertRaises(ValueError) as cm: |
304 | - server._validate_server_sslctx(ssl.SSLContext(ssl.PROTOCOL_TLSv1)) |
305 | + server._validate_server_sslctx(ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)) |
306 | self.assertEqual(str(cm.exception), |
307 | - 'sslctx.protocol must be ssl.PROTOCOL_TLSv1_2' |
308 | + 'sslctx.protocol must be ssl.PROTOCOL_TLS_SERVER' |
309 | ) |
310 | |
311 | # Don't allow ssl.CERT_OPTIONAL: |
312 | - sslctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) |
313 | + sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) |
314 | sslctx.verify_mode = ssl.CERT_OPTIONAL |
315 | with self.assertRaises(ValueError) as cm: |
316 | server._validate_server_sslctx(sslctx) |
317 | @@ -191,13 +191,13 @@ |
318 | ) |
319 | |
320 | # All good: |
321 | - sslctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) |
322 | + sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) |
323 | self.assertIs(sslctx.verify_mode, ssl.CERT_NONE) |
324 | self.assertIs(server._validate_server_sslctx(sslctx), sslctx) |
325 | |
326 | # Now again, this time with CERT_REQUIRED: |
327 | # options missing OP_NO_COMPRESSION: |
328 | - sslctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) |
329 | + sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) |
330 | sslctx.verify_mode = ssl.CERT_REQUIRED |
331 | self.assertIs(sslctx.verify_mode, ssl.CERT_REQUIRED) |
332 | self.assertIs(server._validate_server_sslctx(sslctx), sslctx) |
333 | @@ -426,15 +426,15 @@ |
334 | self.assertEqual(str(cm.exception), 'sslctx must be an ssl.SSLContext') |
335 | |
336 | # Bad SSL protocol version: |
337 | - sslctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) |
338 | + sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) |
339 | with self.assertRaises(ValueError) as cm: |
340 | server.SSLServer(sslctx, degu.IPv6_LOOPBACK, good_app) |
341 | self.assertEqual(str(cm.exception), |
342 | - 'sslctx.protocol must be ssl.PROTOCOL_TLSv1_2' |
343 | + 'sslctx.protocol must be ssl.PROTOCOL_TLS_SERVER' |
344 | ) |
345 | |
346 | # Good sslctx from here on: |
347 | - sslctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) |
348 | + sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) |
349 | |
350 | # Bad address type: |
351 | with self.assertRaises(TypeError) as cm: |
352 | @@ -1003,7 +1003,7 @@ |
353 | with self.assertRaises(ssl.SSLError) as cm: |
354 | client.connect() |
355 | self.assertTrue( |
356 | - str(cm.exception).startswith('[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE]') |
357 | + str(cm.exception).startswith('[SSL: CERTIFICATE_VERIFY_FAILED]') |
358 | ) |
359 | self.assertIs(conn.closed, True) |
360 | |
361 | @@ -1017,7 +1017,7 @@ |
362 | with self.assertRaises(ssl.SSLError) as cm: |
363 | client.connect() |
364 | self.assertTrue( |
365 | - str(cm.exception).startswith('[SSL: TLSV1_ALERT_UNKNOWN_CA]') |
366 | + str(cm.exception).startswith('[SSL: CERTIFICATE_VERIFY_FAILED]') |
367 | ) |
368 | self.assertIs(conn.closed, True) |
369 | |
370 | |
371 | === modified file 'degu/tests/test_sslhelpers.py' |
372 | --- degu/tests/test_sslhelpers.py 2016-03-13 18:28:18 +0000 |
373 | +++ degu/tests/test_sslhelpers.py 2022-06-16 23:17:03 +0000 |
374 | @@ -36,22 +36,26 @@ |
375 | def test_create_key(self): |
376 | # 1024 bit: |
377 | key_data = sslhelpers.create_key(1024) |
378 | - self.assertIn(len(key_data), [883, 887, 891]) |
379 | + self.assertGreater(len(key_data), 512) |
380 | + self.assertLess(len(key_data), 1024) |
381 | sslhelpers.get_pubkey(key_data) |
382 | |
383 | # 2048 bit: |
384 | key_data = sslhelpers.create_key(2048) |
385 | - self.assertIn(len(key_data), [1671, 1675, 1679]) |
386 | + self.assertGreater(len(key_data), 1024) |
387 | + self.assertLess(len(key_data), 2048) |
388 | sslhelpers.get_pubkey(key_data) |
389 | |
390 | # 3072 bit: |
391 | key_data = sslhelpers.create_key(3072) |
392 | - self.assertIn(len(key_data), [2455, 2459]) |
393 | + self.assertGreater(len(key_data), 2048) |
394 | + self.assertLess(len(key_data), 3072) |
395 | sslhelpers.get_pubkey(key_data) |
396 | |
397 | # 4096 bit: |
398 | key_data = sslhelpers.create_key(4096) |
399 | - self.assertIn(len(key_data), [3239, 3243, 3247]) |
400 | + self.assertGreater(len(key_data), 3072) |
401 | + self.assertLess(len(key_data), 4096) |
402 | sslhelpers.get_pubkey(key_data) |
403 | |
404 | def test_create_ca(self): |
405 | @@ -179,7 +183,6 @@ |
406 | set([ |
407 | ca_id + '.key', |
408 | ca_id + '.ca', |
409 | - ca_id + '.srl', |
410 | cert_id + '.csr', |
411 | cert_id + '.cert', |
412 | ]) |
413 | |
414 | === modified file 'doc/degu.client.rst' |
415 | --- doc/degu.client.rst 2017-07-18 18:07:33 +0000 |
416 | +++ doc/degu.client.rst 2022-06-16 23:17:03 +0000 |
417 | @@ -42,7 +42,7 @@ |
418 | server certificate (more or less how a browser would configure SSL): |
419 | |
420 | >>> import ssl |
421 | ->>> sslclient.sslctx.protocol == ssl.PROTOCOL_TLSv1_2 |
422 | +>>> sslclient.sslctx.protocol == ssl.PROTOCOL_TLS_CLIENT |
423 | True |
424 | >>> sslclient.sslctx.verify_mode == ssl.CERT_REQUIRED |
425 | True |
426 | |
427 | === modified file 'setup.py' |
428 | --- setup.py 2021-01-13 13:00:43 +0000 |
429 | +++ setup.py 2022-06-16 23:17:03 +0000 |
430 | @@ -33,8 +33,7 @@ |
431 | import os |
432 | from os import path |
433 | import subprocess |
434 | -from distutils.core import setup, Extension |
435 | -from distutils.cmd import Command |
436 | +from setuptools import setup, Command, Extension |
437 | |
438 | import degu |
439 | from degu.tests.run import run_tests |
Changes all look good to me