Skip to content

Commit 19c68f5

Browse files
committed
Avoid new dependencies that will likely break installs
See this explanation: Supervisor#1578 (comment)
1 parent ca54549 commit 19c68f5

File tree

6 files changed

+75
-54
lines changed

6 files changed

+75
-54
lines changed

CHANGES.rst

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
4.3.0.dev0 (Next Release)
22
-------------------------
33

4-
- The installation requirements have changed because Setuptools 67.5.0
5-
deprecated the use of ``pkg_resources``, which Supervisor used to load
6-
its plugins. The ``setuptools`` package is no longer a runtime dependency
7-
of Supervisor. On Python < 3.8 where ``importlib.metadata`` is not
8-
available in stdlib, Supervisor now requires the PyPI package
9-
``importlib-metadata``. Additionally, on Python < 3.7 where
10-
``importlib.resources`` is not available in stdlib, Supervisor now requires
11-
the PyPI package ``importlib-resources``. These new dependencies have been
12-
added as conditional requirements in ``setup.py``. Patch by Ofek Lev.
4+
- On Python 3.8 and later, ``setuptools`` is no longer a runtime
5+
dependency. Patch by Ofek Lev.
136

147
- ``supervisorctl`` now reads extra files included via the ``[include]``
158
section in ``supervisord.conf`` like ``supervisord`` does. This allows

setup.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@
2222
elif (3, 0) < py_version < (3, 4):
2323
raise RuntimeError('On Python 3, Supervisor requires Python 3.4 or later')
2424

25+
# setuptools is required as a runtime dependency only on
26+
# Python < 3.8. See the comments in supervisor/compat.py.
2527
requires = [
26-
"importlib-metadata; python_version < '3.8'",
27-
"importlib-resources; python_version < '3.7'",
28+
"setuptools; python_version < '3.8'",
2829
]
29-
tests_require = []
3030

31+
tests_require = []
3132
testing_extras = tests_require + [
3233
'pytest',
3334
'pytest-cov',

supervisor/compat.py

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -150,24 +150,51 @@ def is_text_stream(stream):
150150
except ImportError: # pragma: no cover
151151
from HTMLParser import HTMLParser
152152

153+
# Begin importlib/setuptools compatibility code
154+
155+
# Supervisor used pkg_resources (a part of setuptools) to load package
156+
# resources for 15 years, until setuptools 67.5.0 (2023-03-05) deprecated
157+
# the use of pkg_resources. On Python 3.8 or later, Supervisor now uses
158+
# importlib (part of Python 3 stdlib). Unfortunately, on Python < 3.8,
159+
# Supervisor needs to use pkg_resources despite its deprecation. The PyPI
160+
# backport packages "importlib-resources" and "importlib-metadata" couldn't
161+
# be added as dependencies to Supervisor because they require even more
162+
# dependencies that would likely cause some Supervisor installs to fail.
163+
from warnings import filterwarnings as _fw
164+
_fw("ignore", message="pkg_resources is deprecated as an API")
165+
153166
try: # pragma: no cover
154-
import importlib.metadata as importlib_metadata
167+
from importlib.metadata import EntryPoint as _EntryPoint
168+
169+
def import_spec(spec):
170+
return _EntryPoint(None, spec, None).load()
171+
155172
except ImportError: # pragma: no cover
156-
# fall back to PyPI backport if not in stdlib
157-
import importlib_metadata
173+
from pkg_resources import EntryPoint as _EntryPoint
174+
175+
def import_spec(spec):
176+
ep = _EntryPoint.parse("x=" + spec)
177+
if hasattr(ep, 'resolve'):
178+
# this is available on setuptools >= 10.2
179+
return ep.resolve()
180+
else:
181+
# this causes a DeprecationWarning on setuptools >= 11.3
182+
return ep.load(False)
158183

159184
try: # pragma: no cover
160-
import importlib.resources as importlib_resources
161-
except ImportError: # pragma: no cover
162-
# fall back to PyPI backport if not in stdlib
163-
import importlib_resources
185+
import importlib.resources as _importlib_resources
164186

165-
if hasattr(importlib_resources, "files"):
166-
def resource_file(package, path): # pragma: no cover
167-
return str(importlib_resources.files(package).joinpath(path))
187+
if hasattr(_importlib_resources, "files"):
188+
def resource_filename(package, path):
189+
return str(_importlib_resources.files(package).joinpath(path))
168190

169-
else: # pragma: no cover
170-
# fall back to deprecated .path if .files is not available
171-
def resource_file(package, path): # pragma: no cover
172-
with importlib_resources.path(package, '__init__.py') as p:
173-
return str(p.parent.joinpath(path))
191+
else:
192+
# fall back to deprecated .path if .files is not available
193+
def resource_filename(package, path):
194+
with _importlib_resources.path(package, '__init__.py') as p:
195+
return str(p.parent.joinpath(path))
196+
197+
except ImportError: # pragma: no cover
198+
from pkg_resources import resource_filename
199+
200+
# End importlib/setuptools compatibility code

supervisor/confecho.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import sys
22
from supervisor.compat import as_string
3-
from supervisor.compat import resource_file
3+
from supervisor.compat import resource_filename
44

55

66
def main(out=sys.stdout):
7-
with open(resource_file(__package__, 'skel/sample.conf'), 'r') as f:
7+
with open(resource_filename(__package__, 'skel/sample.conf'), 'r') as f:
88
out.write(as_string(f.read()))

supervisor/options.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from supervisor.compat import xmlrpclib
2222
from supervisor.compat import StringIO
2323
from supervisor.compat import basestring
24-
from supervisor.compat import importlib_metadata
24+
from supervisor.compat import import_spec
2525

2626
from supervisor.medusa import asyncore_25 as asyncore
2727

@@ -391,7 +391,7 @@ def get_plugins(self, parser, factory_key, section_prefix):
391391

392392
def import_spec(self, spec):
393393
"""On failure, raises either AttributeError or ImportError"""
394-
return importlib_metadata.EntryPoint(None, spec, None).load()
394+
return import_spec(spec)
395395

396396
def read_include_config(self, fp, parser, expansions):
397397
if parser.has_section('include'):

supervisor/tests/test_end_to_end.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import signal
66
import sys
77
import unittest
8-
from supervisor.compat import resource_file
8+
from supervisor.compat import resource_filename
99
from supervisor.compat import xmlrpclib
1010
from supervisor.xmlrpc import SupervisorTransport
1111

@@ -25,7 +25,7 @@ def test_issue_291a_percent_signs_in_original_env_are_preserved(self):
2525
passed to the child without the percent sign being mangled."""
2626
key = "SUPERVISOR_TEST_1441B"
2727
val = "foo_%s_%_%%_%%%_%2_bar"
28-
filename = resource_file(__package__, 'fixtures/issue-291a.conf')
28+
filename = resource_filename(__package__, 'fixtures/issue-291a.conf')
2929
args = ['-m', 'supervisor.supervisord', '-c', filename]
3030
try:
3131
os.environ[key] = val
@@ -38,7 +38,7 @@ def test_issue_291a_percent_signs_in_original_env_are_preserved(self):
3838
def test_issue_550(self):
3939
"""When an environment variable is set in the [supervisord] section,
4040
it should be put into the environment of the subprocess."""
41-
filename = resource_file(__package__, 'fixtures/issue-550.conf')
41+
filename = resource_filename(__package__, 'fixtures/issue-550.conf')
4242
args = ['-m', 'supervisor.supervisord', '-c', filename]
4343
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
4444
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -54,7 +54,7 @@ def test_issue_550(self):
5454
def test_issue_565(self):
5555
"""When a log file has Unicode characters in it, 'supervisorctl
5656
tail -f name' should still work."""
57-
filename = resource_file(__package__, 'fixtures/issue-565.conf')
57+
filename = resource_filename(__package__, 'fixtures/issue-565.conf')
5858
args = ['-m', 'supervisor.supervisord', '-c', filename]
5959
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
6060
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -71,7 +71,7 @@ def test_issue_565(self):
7171
def test_issue_638(self):
7272
"""When a process outputs something on its stdout or stderr file
7373
descriptor that is not valid UTF-8, supervisord should not crash."""
74-
filename = resource_file(__package__, 'fixtures/issue-638.conf')
74+
filename = resource_filename(__package__, 'fixtures/issue-638.conf')
7575
args = ['-m', 'supervisor.supervisord', '-c', filename]
7676
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
7777
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -90,7 +90,7 @@ def test_issue_638(self):
9090
def test_issue_663(self):
9191
"""When Supervisor is run on Python 3, the eventlistener protocol
9292
should work."""
93-
filename = resource_file(__package__, 'fixtures/issue-663.conf')
93+
filename = resource_filename(__package__, 'fixtures/issue-663.conf')
9494
args = ['-m', 'supervisor.supervisord', '-c', filename]
9595
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
9696
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -102,7 +102,7 @@ def test_issue_664(self):
102102
"""When a subprocess name has Unicode characters, 'supervisord'
103103
should not send incomplete XML-RPC responses and 'supervisorctl
104104
status' should work."""
105-
filename = resource_file(__package__, 'fixtures/issue-664.conf')
105+
filename = resource_filename(__package__, 'fixtures/issue-664.conf')
106106
args = ['-m', 'supervisor.supervisord', '-c', filename]
107107
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
108108
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -121,7 +121,7 @@ def test_issue_664(self):
121121
def test_issue_733(self):
122122
"""When a subprocess enters the FATAL state, a one-line eventlistener
123123
can be used to signal supervisord to shut down."""
124-
filename = resource_file(__package__, 'fixtures/issue-733.conf')
124+
filename = resource_filename(__package__, 'fixtures/issue-733.conf')
125125
args = ['-m', 'supervisor.supervisord', '-c', filename]
126126
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
127127
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -130,7 +130,7 @@ def test_issue_733(self):
130130
supervisord.expect(pexpect.EOF)
131131

132132
def test_issue_835(self):
133-
filename = resource_file(__package__, 'fixtures/issue-835.conf')
133+
filename = resource_filename(__package__, 'fixtures/issue-835.conf')
134134
args = ['-m', 'supervisor.supervisord', '-c', filename]
135135
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
136136
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -146,7 +146,7 @@ def test_issue_835(self):
146146
transport.connection.close()
147147

148148
def test_issue_836(self):
149-
filename = resource_file(__package__, 'fixtures/issue-836.conf')
149+
filename = resource_filename(__package__, 'fixtures/issue-836.conf')
150150
args = ['-m', 'supervisor.supervisord', '-c', filename]
151151
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
152152
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -169,7 +169,7 @@ def test_issue_836(self):
169169
def test_issue_986_command_string_with_double_percent(self):
170170
"""A percent sign can be used in a command= string without being
171171
expanded if it is escaped by a second percent sign."""
172-
filename = resource_file(__package__, 'fixtures/issue-986.conf')
172+
filename = resource_filename(__package__, 'fixtures/issue-986.conf')
173173
args = ['-m', 'supervisor.supervisord', '-c', filename]
174174
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
175175
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -178,7 +178,7 @@ def test_issue_986_command_string_with_double_percent(self):
178178
def test_issue_1054(self):
179179
"""When run on Python 3, the 'supervisorctl avail' command
180180
should work."""
181-
filename = resource_file(__package__, 'fixtures/issue-1054.conf')
181+
filename = resource_filename(__package__, 'fixtures/issue-1054.conf')
182182
args = ['-m', 'supervisor.supervisord', '-c', filename]
183183
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
184184
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -196,7 +196,7 @@ def test_issue_1170a(self):
196196
"""When the [supervisord] section has a variable defined in
197197
environment=, that variable should be able to be used in an
198198
%(ENV_x) expansion in a [program] section."""
199-
filename = resource_file(__package__, 'fixtures/issue-1170a.conf')
199+
filename = resource_filename(__package__, 'fixtures/issue-1170a.conf')
200200
args = ['-m', 'supervisor.supervisord', '-c', filename]
201201
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
202202
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -207,7 +207,7 @@ def test_issue_1170b(self):
207207
environment=, and a variable by the same name is defined in
208208
enviroment= of a [program] section, the one in the [program]
209209
section should be used."""
210-
filename = resource_file(__package__, 'fixtures/issue-1170b.conf')
210+
filename = resource_filename(__package__, 'fixtures/issue-1170b.conf')
211211
args = ['-m', 'supervisor.supervisord', '-c', filename]
212212
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
213213
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -218,7 +218,7 @@ def test_issue_1170c(self):
218218
environment=, and a variable by the same name is defined in
219219
enviroment= of an [eventlistener] section, the one in the
220220
[eventlistener] section should be used."""
221-
filename = resource_file(__package__, 'fixtures/issue-1170c.conf')
221+
filename = resource_filename(__package__, 'fixtures/issue-1170c.conf')
222222
args = ['-m', 'supervisor.supervisord', '-c', filename]
223223
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
224224
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -229,7 +229,7 @@ def test_issue_1224(self):
229229
then the non-rotating logger will be used to avoid an
230230
IllegalSeekError in the case that the user has configured a
231231
non-seekable file like /dev/stdout."""
232-
filename = resource_file(__package__, 'fixtures/issue-1224.conf')
232+
filename = resource_filename(__package__, 'fixtures/issue-1224.conf')
233233
args = ['-m', 'supervisor.supervisord', '-c', filename]
234234
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
235235
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -238,7 +238,7 @@ def test_issue_1224(self):
238238
def test_issue_1231a(self):
239239
"""When 'supervisorctl tail -f name' is run and the log contains
240240
unicode, it should not fail."""
241-
filename = resource_file(__package__, 'fixtures/issue-1231a.conf')
241+
filename = resource_filename(__package__, 'fixtures/issue-1231a.conf')
242242
args = ['-m', 'supervisor.supervisord', '-c', filename]
243243
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
244244
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -255,7 +255,7 @@ def test_issue_1231a(self):
255255
def test_issue_1231b(self):
256256
"""When 'supervisorctl tail -f name' is run and the log contains
257257
unicode, it should not fail."""
258-
filename = resource_file(__package__, 'fixtures/issue-1231b.conf')
258+
filename = resource_filename(__package__, 'fixtures/issue-1231b.conf')
259259
args = ['-m', 'supervisor.supervisord', '-c', filename]
260260
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
261261
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -289,7 +289,7 @@ def test_issue_1231b(self):
289289
def test_issue_1231c(self):
290290
"""When 'supervisorctl tail -f name' is run and the log contains
291291
unicode, it should not fail."""
292-
filename = resource_file(__package__, 'fixtures/issue-1231c.conf')
292+
filename = resource_filename(__package__, 'fixtures/issue-1231c.conf')
293293
args = ['-m', 'supervisor.supervisord', '-c', filename]
294294
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
295295
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -331,7 +331,7 @@ def test_issue_1298(self):
331331
"""When the output of 'supervisorctl tail -f worker' is piped such as
332332
'supervisor tail -f worker | grep something', 'supervisorctl' should
333333
not crash."""
334-
filename = resource_file(__package__, 'fixtures/issue-1298.conf')
334+
filename = resource_filename(__package__, 'fixtures/issue-1298.conf')
335335
args = ['-m', 'supervisor.supervisord', '-c', filename]
336336
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
337337
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -365,7 +365,7 @@ def test_issue_1418_pidproxy_cmd_with_args(self):
365365
def test_issue_1483a_identifier_default(self):
366366
"""When no identifier is supplied on the command line or in the config
367367
file, the default is used."""
368-
filename = resource_file(__package__, 'fixtures/issue-1483a.conf')
368+
filename = resource_filename(__package__, 'fixtures/issue-1483a.conf')
369369
args = ['-m', 'supervisor.supervisord', '-c', filename]
370370
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
371371
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -384,7 +384,7 @@ def test_issue_1483a_identifier_default(self):
384384
def test_issue_1483b_identifier_from_config_file(self):
385385
"""When the identifier is supplied in the config file only, that
386386
identifier is used instead of the default."""
387-
filename = resource_file(__package__, 'fixtures/issue-1483b.conf')
387+
filename = resource_filename(__package__, 'fixtures/issue-1483b.conf')
388388
args = ['-m', 'supervisor.supervisord', '-c', filename]
389389
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
390390
self.addCleanup(supervisord.kill, signal.SIGINT)
@@ -403,7 +403,7 @@ def test_issue_1483b_identifier_from_config_file(self):
403403
def test_issue_1483c_identifier_from_command_line(self):
404404
"""When an identifier is supplied in both the config file and on the
405405
command line, the one from the command line is used."""
406-
filename = resource_file(__package__, 'fixtures/issue-1483c.conf')
406+
filename = resource_filename(__package__, 'fixtures/issue-1483c.conf')
407407
args = ['-m', 'supervisor.supervisord', '-c', filename, '-i', 'from_command_line']
408408
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
409409
self.addCleanup(supervisord.kill, signal.SIGINT)

0 commit comments

Comments
 (0)