version_locations = config.get_main_option("version_locations")
if version_locations:
- version_locations = _split_on_space_comma.split(version_locations)
+ version_path_separator = config.get_main_option(
+ "version_path_separator"
+ )
+
+ split_on_path = {
+ None: None,
+ "space": " ",
+ "os": os.pathsep,
+ ":": ":",
+ ";": ";",
+ }
+
+ try:
+ split_char = split_on_path[version_path_separator]
+ except KeyError as ke:
+ util.raise_(
+ ValueError(
+ "'%s' is not a valid value for "
+ "version_path_separator; "
+ "expected 'space', 'os', ':', ';'"
+ % version_path_separator
+ ),
+ from_=ke,
+ )
+ else:
+ if split_char is None:
+ # legacy behaviour for backwards compatibility
+ version_locations = _split_on_space_comma.split(
+ version_locations
+ )
+ else:
+ version_locations = [
+ x for x in version_locations.split(split_char) if x
+ ]
prepend_sys_path = config.get_main_option("prepend_sys_path")
if prepend_sys_path:
# versions/ directory
# sourceless = false
-# version location specification; this defaults
+# version location specification; This defaults
# to ${script_location}/versions. When using multiple version
-# directories, initial revisions must be specified with --version-path
-# version_locations = %(here)s/bar %(here)s/bat ${script_location}/versions
+# directories, initial revisions must be specified with --version-path.
+# The path separator used here should be the separator specified by "version_path_separator"
+# version_locations = %(here)s/bar:%(here)s/bat:${script_location}/versions
+
+# version path separator; As mentioned above, this is the character used to split
+# version_locations. Valid values are:
+#
+# version_path_separator = :
+# version_path_separator = ;
+# version_path_separator = space
+version_path_separator = os # default: use os.pathsep
# the output encoding used when revision files
# are written from script.py.mako
# versions/ directory
# sourceless = false
-# version location specification; this defaults
+# version location specification; This defaults
# to ${script_location}/versions. When using multiple version
-# directories, initial revisions must be specified with --version-path
-# version_locations = %(here)s/bar %(here)s/bat ${script_location}/versions
+# directories, initial revisions must be specified with --version-path.
+# The path separator used here should be the separator specified by "version_path_separator"
+# version_locations = %(here)s/bar:%(here)s/bat:${script_location}/versions
+
+# version path separator; As mentioned above, this is the character used to split
+# version_locations. Valid values are:
+#
+# version_path_separator = :
+# version_path_separator = ;
+# version_path_separator = space
+version_path_separator = os # default: use os.pathsep
# the output encoding used when revision files
# are written from script.py.mako
# versions/ directory
# sourceless = false
-# version location specification; this defaults
+# version location specification; This defaults
# to ${script_location}/versions. When using multiple version
-# directories, initial revisions must be specified with --version-path
-# version_locations = %(here)s/bar %(here)s/bat ${script_location}/versions
+# directories, initial revisions must be specified with --version-path.
+# The path separator used here should be the separator specified by "version_path_separator"
+# version_locations = %(here)s/bar:%(here)s/bat:${script_location}/versions
+
+# version path separator; As mentioned above, this is the character used to split
+# version_locations. Valid values are:
+#
+# version_path_separator = :
+# version_path_separator = ;
+# version_path_separator = space
+version_path_separator = os # default: use os.pathsep
# the output encoding used when revision files
# are written from script.py.mako
# versions/ directory
# sourceless = false
-# version location specification; this defaults
+# version location specification; This defaults
# to ${script_location}/versions. When using multiple version
-# directories, initial revisions must be specified with --version-path
-# version_locations = %(here)s/bar %(here)s/bat ${script_location}/versions
+# directories, initial revisions must be specified with --version-path.
+# The path separator used here should be the separator specified by "version_path_separator"
+# version_locations = %(here)s/bar:%(here)s/bat:${script_location}/versions
+
+# version path separator; As mentioned above, this is the character used to split
+# version_locations. Valid values are:
+#
+# version_path_separator = :
+# version_path_separator = ;
+# version_path_separator = space
+version_path_separator = os # default: use os.pathsep
# the output encoding used when revision files
# are written from script.py.mako
# version location specification; this defaults
# to foo/versions. When using multiple version
# directories, initial revisions must be specified with --version-path
+ version_path_separator = space
version_locations = %(here)s/model/networking %(here)s/alembic/versions
The new directory ``%(here)s/model/networking`` is in terms of where
# versions/ directory
# sourceless = false
- # version location specification; this defaults
- # to alembic/versions. When using multiple version
- # directories, initial revisions must be specified with --version-path
- # version_locations = %(here)s/bar %(here)s/bat alembic/versions
+ # version location specification; This defaults
+ # to ${script_location}/versions. When using multiple version
+ # directories, initial revisions must be specified with --version-path.
+ # The path separator used here should be the separator specified by "version_path_separator"
+ # version_locations = %(here)s/bar:%(here)s/bat:${script_location}/versions
+
+ # version path separator; As mentioned above, this is the character used to split
+ # version_locations. Valid values are:
+ #
+ # version_path_separator = :
+ # version_path_separator = ;
+ # version_path_separator = space
+ version_path_separator = os # default: use os.pathsep
# the output encoding used when revision files
# are written from script.py.mako
--- /dev/null
+.. change::
+ :tags: bug, environment
+ :tickets: 842
+
+ Enhance ``version_locations`` parsing to handle paths containing spaces.
+ The new configuration option ``version_path_separator`` specifies the
+ character to use when splitting the ``version_locations`` string. The
+ default for new configurations is ``version_path_separator = os``,
+ which will use ``os.pathsep`` (e.g., ``;`` on Windows).
#!coding: utf-8
-
+import os
+import tempfile
from alembic import config
+from alembic import testing
from alembic import util
from alembic.migration import MigrationContext
from alembic.operations import Operations
from alembic.testing import assert_raises_message
from alembic.testing import eq_
from alembic.testing import mock
+from alembic.testing.assertions import expect_raises_message
from alembic.testing.env import _no_sql_testing_config
from alembic.testing.env import _write_config_file
from alembic.testing.env import clear_staging_env
cfg.attributes["connection"] = m2
eq_(cfg.attributes, {"m1": m1, "connection": m2})
+ @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"],
+ ),
+ (
+ "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 version_path_separator; "
+ "expected 'space', 'os', ':', ';'"
+ ),
+ ),
+ id_="iaaa",
+ argnames="separator, string_value, expected_result",
+ )
+ def test_version_locations(self, separator, string_value, expected_result):
+ cfg = config.Config()
+ if separator is not None:
+ cfg.set_main_option(
+ "version_path_separator",
+ separator,
+ )
+ cfg.set_main_option("script_location", tempfile.gettempdir())
+ cfg.set_main_option("version_locations", string_value)
+
+ if isinstance(expected_result, ValueError):
+ with expect_raises_message(ValueError, expected_result.args[0]):
+ ScriptDirectory.from_config(cfg)
+ else:
+ s = ScriptDirectory.from_config(cfg)
+ eq_(s.version_locations, expected_result)
+
class StdoutOutputEncodingTest(TestBase):
def test_plain(self):