setup: Require a minimum setuptools version for Python 3.
When the `setuptools_dso` module is used having older versions of setuptools installed, the `wheel` module (one of its dependencies) sets up a handler for the root logging that writes to stdout (https://github.com/pypa/wheel/issues/622). This breaks the expected output of the programs, and thus the wendelin.core tests. However, if `setuptools.logging` is available, `wheel` will use it instead and it won't set up a handler. Thus, I added a explicit dependency to a version of setuptools above 60.2 for Python 3, as that is the first version that provides this module. -------- kirr: Move setuptools pinning close to setuptools_dso requirement to which it relates and add corresponding comment. Setuptools pinning also fixes e.g. the following test failure inside pygolang itself: golang/golang_str_test.py::test_strings_print FAILED ============================== FAILURES ============================== _________________________ test_strings_print _________________________ def test_strings_print(): outok = readfile(dir_testprog + "/golang_test_str.txt") retcode, stdout, stderr = _pyrun(["golang_test_str.py"], cwd=dir_testprog, stdout=PIPE, stderr=PIPE) assert retcode == 0, (stdout, stderr) assert stderr == b"" > assertDoc(outok, stdout) golang/golang_str_test.py:121: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ want = 'print(qq(b)): "привет αβγ b"\nprint(qq(u)): "привет αβγ u"\n' got = 'Extend DSO search path to \'PYGOLANG/golang/runtime\'\nprint(qq(b)): "привет αβγ b"\nprint(qq(u)): "привет αβγ u"\n' def assertDoc(want, got): want = u(want) got = u(got) # normalize got to PYGOLANG udir_pygolang = abbrev_home(dir_pygolang) # /home/x/.../pygolang -> ~/.../pygolang got = got.replace(dir_pygolang, "PYGOLANG") # /home/x/.../pygolang -> PYGOLANG got = got.replace(udir_pygolang, "PYGOLANG") # ~/.../pygolang -> PYGOLANG # got: normalize PYGOLANG\a\b\c -> PYGOLANG/a/b/c # a\b\c\d.py -> a/b/c/d.py def _(m): return m.group(0).replace(os.path.sep, '/') got = re.sub(r"(?<=PYGOLANG)[^\s]+(?=\s)", _, got) got = re.sub(r"([\w\\\.]+)(?=\.py)", _, got) # want: process conditionals # PY39(...) -> ... if py ≥ 3.9 else ø (inline) # `... +PY39` -> ... if py ≥ 3.9 else ø (whole line) # `... -PY39` -> ... if py < 3.9 else ø (whole line) have = {} # 'PYxy' -> y/n for minor in (9,10,11): have['PY3%d' % minor] = (sys.version_info >= (3, minor)) for x, havex in have.items(): want = re.sub(r"%s\((.*)\)" % x, r"\1" if havex else "", want) r = re.compile(r'^(?P<main>.*?) +(?P<y>(\+|-))%s$' % x) v = [] for l in want.splitlines(): m = r.match(l) if m is not None: l = m.group('main') y = {'+':True, '-':False}[m.group('y')] if (y and not havex) or (havex and not y): continue v.append(l) want = '\n'.join(v)+'\n' # want: ^$ -> <BLANKLINE> while "\n\n" in want: want = want.replace("\n\n", "\n<BLANKLINE>\n") X = doctest.OutputChecker() if not X.check_output(want, got, doctest.ELLIPSIS): # output_difference wants Example object with .want attr class Ex: pass _ = Ex() _.want = want > fail("not equal:\n" + X.output_difference(_, got, doctest.ELLIPSIS | doctest.REPORT_UDIFF)) E Failed: not equal: E Expected: E print(qq(b)): "привет αβγ b" E print(qq(u)): "привет αβγ u" E Got: E Extend DSO search path to 'PYGOLANG/golang/runtime' E print(qq(b)): "привет αβγ b" E print(qq(u)): "привет αβγ u" see e.g. https://stack.nexedi.com/test_result_module/20240509-389AC427/3 for details. /reviewed-by @kirr /reviewed-on nexedi/pygolang!27