diff --git a/alembic/config.py b/alembic/config.py index 2c52e7cd..95502cb4 100644 --- a/alembic/config.py +++ b/alembic/config.py @@ -339,6 +339,27 @@ def messaging_opts(self) -> MessagingOptions: ), ) + def get_separator_char(self, name: str) -> Optional[str]: + separator = self.get_main_option(name) + + split_on_path = { + None: None, + "space": " ", + "newline": "\n", + "os": os.pathsep, + ":": ":", + ";": ";", + } + + try: + return split_on_path[separator] + except KeyError as ke: + raise ValueError( + "'%s' is not a valid value for %s; " + "expected 'space', 'newline', 'os', ':', ';'" + % (separator, name) + ) from ke + class MessagingOptions(TypedDict, total=False): quiet: bool diff --git a/alembic/script/base.py b/alembic/script/base.py index 30df6ddb..f556f354 100644 --- a/alembic/script/base.py +++ b/alembic/script/base.py @@ -180,51 +180,42 @@ def from_config(cls, config: Config) -> ScriptDirectory: version_locations_str = config.get_main_option("version_locations") version_locations: Optional[List[str]] if version_locations_str: - version_path_separator = config.get_main_option( + split_char = config.get_separator_char( "version_path_separator" ) - split_on_path = { - None: None, - "space": " ", - "newline": "\n", - "os": os.pathsep, - ":": ":", - ";": ";", - } - - try: - split_char: Optional[str] = split_on_path[ - version_path_separator - ] - except KeyError as ke: - raise ValueError( - "'%s' is not a valid value for " - "version_path_separator; " - "expected 'space', 'newline', 'os', ':', ';'" - % version_path_separator - ) from ke + if split_char is None: + # legacy behaviour for backwards compatibility + version_locations = _split_on_space_comma.split( + version_locations_str + ) else: - if split_char is None: - # legacy behaviour for backwards compatibility - version_locations = _split_on_space_comma.split( - version_locations_str - ) - else: - version_locations = [ - x.strip() - for x in version_locations_str.split(split_char) - if x - ] + version_locations = [ + x.strip() + for x in version_locations_str.split(split_char) + if x + ] else: version_locations = None prepend_sys_path = config.get_main_option("prepend_sys_path") if prepend_sys_path: - sys.path[:0] = list( - _split_on_space_comma_colon.split(prepend_sys_path) + split_char = config.get_separator_char( + "prepend_sys_path_separator" ) + if split_char is None: + # legacy behaviour for backwards compatibility + sys.path[:0] = list( + _split_on_space_comma_colon.split(prepend_sys_path) + ) + else: + sys.path[:0] = ( + path.strip() + for path in prepend_sys_path.split(split_char) + if path + ) + rvl = config.get_main_option("recursive_version_locations") == "true" return ScriptDirectory( util.coerce_resource_to_filename(script_location), diff --git a/tests/test_config.py b/tests/test_config.py index a98994c4..e295f585 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,4 +1,5 @@ import os +import sys import tempfile from alembic import config @@ -201,6 +202,104 @@ def test_version_locations(self, separator, string_value, expected_result): s = ScriptDirectory.from_config(cfg) eq_(s.version_locations, expected_result) + @testing.combinations( + ( + "legacy raw string 1", + None, + "/foo", + ["/foo"], + ), + ( + "legacy raw string 2", + None, + "/foo /bar", + ["/foo", "/bar"], + ), + ( + "legacy raw string 3", + "space", + "/foo", + ["/foo"], + ), + ( + "legacy raw string 4", + "space", + "/foo /bar", + ["/foo", "/bar"], + ), + ( + "multiline string 1", + "newline", + " /foo \n/bar ", + ["/foo", "/bar"], + ), + ( + "Linux pathsep 1", + ":", + "/Project A", + ["/Project A"], + ), + ( + "Linux pathsep 2", + ":", + "/Project A:/Project B", + ["/Project A", "/Project B"], + ), + ( + "Windows pathsep 1", + ";", + r"C:\Project A", + [r"C:\Project A"], + ), + ( + "Windows pathsep 2", + ";", + r"C:\Project A;C:\Project B", + [r"C:\Project A", r"C:\Project B"], + ), + ( + "os pathsep", + "os", + r"path_number_one%(sep)spath_number_two%(sep)s" + % {"sep": os.pathsep}, + [r"path_number_one", r"path_number_two"], + ), + ( + "invalid pathsep 2", + "|", + "/foo|/bar", + ValueError( + "'|' is not a valid value for prepend_sys_path_separator; " + "expected 'space', 'newline', 'os', ':', ';'" + ), + ), + id_="iaaa", + argnames="separator, string_value, expected_result", + ) + def test_prepend_sys_path_locations(self, separator, string_value, expected_result): + cfg = config.Config() + if separator is not None: + cfg.set_main_option( + "prepend_sys_path_separator", + separator, + ) + cfg.set_main_option("script_location", tempfile.gettempdir()) + cfg.set_main_option("prepend_sys_path", string_value) + + if isinstance(expected_result, ValueError): + message = str(expected_result) + with expect_raises_message(ValueError, message, text_exact=True): + ScriptDirectory.from_config(cfg) + else: + restore_path = list(sys.path) + try: + sys.path.clear() + + ScriptDirectory.from_config(cfg) + eq_(sys.path, expected_result) + finally: + sys.path = restore_path + class StdoutOutputEncodingTest(TestBase): def test_plain(self):