commit 0a1d9b0a251b70df7eb4b4004c86e169cddd8ed1 Author: Alexander Kukushkin Date: Mon Aug 26 09:38:47 2019 +0200 Get rid from distutils module dependency (#1146) We are using only one function from there, `find_executable()` and it is better to implement a similar function in Patroni rather than add `distutils` module into requirements.txt Index: patroni/patroni/ctl.py =================================================================== --- patroni.orig/patroni/ctl.py +++ patroni/patroni/ctl.py @@ -25,7 +25,6 @@ import yaml from click import ClickException from contextlib import contextmanager -from distutils.spawn import find_executable from patroni.config import Config from patroni.dcs import get_dcs as _get_dcs from patroni.exceptions import PatroniException @@ -1106,6 +1105,24 @@ def apply_yaml_file(data, filename): return format_config_for_editing(changed_data), changed_data +def find_executable(executable, path=None): + _, ext = os.path.splitext(executable) + + if (sys.platform == 'win32') and (ext != '.exe'): + executable = executable + '.exe' + + if os.path.isfile(executable): + return executable + + if path is None: + path = os.environ.get('PATH', os.defpath) + + for p in path.split(os.pathsep): + f = os.path.join(p, executable) + if os.path.isfile(f): + return f + + def invoke_editor(before_editing, cluster_name): """Starts editor command to edit configuration in human readable format Index: patroni/tests/test_ctl.py =================================================================== --- patroni.orig/tests/test_ctl.py +++ patroni/tests/test_ctl.py @@ -9,7 +9,7 @@ from datetime import datetime, timedelta from mock import patch, Mock from patroni.ctl import ctl, store_config, load_config, output_members, request_patroni, get_dcs, parse_dcs, \ get_all_members, get_any_member, get_cursor, query_member, configure, PatroniCtlException, apply_config_changes, \ - format_config_for_editing, show_diff, invoke_editor, format_pg_version + format_config_for_editing, show_diff, invoke_editor, format_pg_version, find_executable from patroni.dcs.etcd import Client, Failover from patroni.utils import tzutc from psycopg2 import OperationalError @@ -587,3 +587,12 @@ class TestCtl(unittest.TestCase): def test_format_pg_version(self): self.assertEqual(format_pg_version(100001), '10.1') self.assertEqual(format_pg_version(90605), '9.6.5') + + @patch('sys.platform', 'win32') + def test_find_executable(self): + with patch('os.path.isfile', Mock(return_value=True)): + self.assertEqual(find_executable('vim'), 'vim.exe') + with patch('os.path.isfile', Mock(return_value=False)): + self.assertIsNone(find_executable('vim')) + with patch('os.path.isfile', Mock(side_effect=[False, True])): + self.assertEqual(find_executable('vim', '/'), '/vim.exe')