Rev 5288: Resolve NEWS conflicts. in http://bazaar.launchpad.net/~lifeless/bzr/loomsupport
Robert Collins
robertc at robertcollins.net
Mon Jun 14 22:41:34 BST 2010
At http://bazaar.launchpad.net/~lifeless/bzr/loomsupport
------------------------------------------------------------
revno: 5288 [merge]
revision-id: robertc at robertcollins.net-20100614214132-uozv55rswcinlva6
parent: robertc at robertcollins.net-20100614083211-b3e6tybqgir06g1v
parent: pqm at pqm.ubuntu.com-20100614175824-nq51rf1uetnut04t
committer: Robert Collins <robertc at robertcollins.net>
branch nick: loomsupport
timestamp: Tue 2010-06-15 09:41:32 +1200
message:
Resolve NEWS conflicts.
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/branch.py branch.py-20050309040759-e4baf4e0d046576e
bzrlib/bundle/bundle_data.py read_changeset.py-20050619171944-c0d95aa685537640
bzrlib/log.py log.py-20050505065812-c40ce11702fe5fb1
bzrlib/plugin.py plugin.py-20050622060424-829b654519533d69
bzrlib/recordcounter.py recordcounter.py-20100512152727-q0kn8gah0tre0uqs-1
bzrlib/tests/blackbox/test_log.py test_log.py-20060112090212-78f6ea560c868e24
bzrlib/tests/per_branch/test_locking.py test_locking.py-20060707151933-tav3o2hpibwi53u4-4
bzrlib/tests/test_log.py testlog.py-20050728115707-1a514809d7d49309
bzrlib/tests/test_plugins.py plugins.py-20050622075746-32002b55e5e943e9
bzrlib/tests/test_urlutils.py test_urlutils.py-20060502192900-46b1f9579987cf9c
bzrlib/urlutils.py urlutils.py-20060502195429-e8a161ecf8fac004
=== modified file 'NEWS'
--- a/NEWS 2010-06-14 06:57:25 +0000
+++ b/NEWS 2010-06-14 21:41:32 +0000
@@ -14,6 +14,11 @@
Compatibility Breaks
********************
+* URLs like ``foo:bar/baz`` are now always parsed as a URL with scheme "foo"
+ and path "bar/baz", even if bzr does not recognize "foo" as a known URL
+ scheme. Previously these URLs would be treated as local paths.
+ (Gordon Tyler)
+
New Features
************
@@ -28,20 +33,27 @@
leading to faster init for directories with existing content.
(Martin [gz], Parth Malwankar, #501307)
+* ``bzr log --exclude-common-ancestry`` is now taken into account for
+ linear ancetries. (Vincent Ladeuil, #575631)
+
+* Ensure that wrong path specifications in ``BZR_PLUGINS_AT`` display
+ proper error messages. (Vincent Ladeuil, #591215)
+
* Explicitly removing ``--profile-imports`` option from parsed command-line
arguments on Windows, because bzr script does the same.
(Alexander Belchenko, #588277)
* Fetching was slightly confused about the best code to use and was
using a new code path for all branches, resulting in more lookups than
- necessary on old branches.
- (Robert Collins, #593515)
+ necessary on old branches. (Robert Collins, #593515)
* Final fix for 'no help for command' issue. We now show a clean message
when a command has no help, document how to set help more clearly, and
test that all commands available to the test suite have help.
(Robert Collins, #177500)
+* Relative imports in plugins are now handled correctly when using
+ BZR_PLUGINS_AT. (Vincent Ladeuil, #588959)
Improvements
************
@@ -77,6 +89,8 @@
Internals
*********
+* Improved ``bzrlib.urlutils`` to handle lp:foo/bar URLs. (Gordon Tyler)
+
Testing
*******
=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py 2010-06-14 08:32:11 +0000
+++ b/bzrlib/branch.py 2010-06-14 21:41:32 +0000
@@ -198,6 +198,13 @@
return self.supports_tags() and self.tags.get_tag_dict()
def get_config(self):
+ """Get a bzrlib.config.BranchConfig for this Branch.
+
+ This can then be used to get and set configuration options for the
+ branch.
+
+ :return: A bzrlib.config.BranchConfig.
+ """
return BranchConfig(self)
def _get_config(self):
=== modified file 'bzrlib/bundle/bundle_data.py'
--- a/bzrlib/bundle/bundle_data.py 2009-09-20 22:12:36 +0000
+++ b/bzrlib/bundle/bundle_data.py 2010-06-09 22:48:52 +0000
@@ -331,7 +331,7 @@
try:
name, value = info_item.split(':', 1)
except ValueError:
- raise 'Value %r has no colon' % info_item
+ raise ValueError('Value %r has no colon' % info_item)
if name == 'last-changed':
last_changed = value
elif name == 'executable':
=== modified file 'bzrlib/log.py'
--- a/bzrlib/log.py 2010-05-11 08:44:59 +0000
+++ b/bzrlib/log.py 2010-06-08 09:50:27 +0000
@@ -522,7 +522,7 @@
elif not generate_merge_revisions:
# If we only want to see linear revisions, we can iterate ...
iter_revs = _generate_flat_revisions(branch, start_rev_id, end_rev_id,
- direction)
+ direction, exclude_common_ancestry)
if direction == 'forward':
iter_revs = reversed(iter_revs)
else:
@@ -544,8 +544,11 @@
return [(rev_id, revno_str, 0)]
-def _generate_flat_revisions(branch, start_rev_id, end_rev_id, direction):
- result = _linear_view_revisions(branch, start_rev_id, end_rev_id)
+def _generate_flat_revisions(branch, start_rev_id, end_rev_id, direction,
+ exclude_common_ancestry=False):
+ result = _linear_view_revisions(
+ branch, start_rev_id, end_rev_id,
+ exclude_common_ancestry=exclude_common_ancestry)
# If a start limit was given and it's not obviously an
# ancestor of the end limit, check it before outputting anything
if direction == 'forward' or (start_rev_id
@@ -572,7 +575,7 @@
if delayed_graph_generation:
try:
for rev_id, revno, depth in _linear_view_revisions(
- branch, start_rev_id, end_rev_id):
+ branch, start_rev_id, end_rev_id, exclude_common_ancestry):
if _has_merges(branch, rev_id):
# The end_rev_id can be nested down somewhere. We need an
# explicit ancestry check. There is an ambiguity here as we
@@ -643,14 +646,17 @@
return True
-def _linear_view_revisions(branch, start_rev_id, end_rev_id):
+def _linear_view_revisions(branch, start_rev_id, end_rev_id,
+ exclude_common_ancestry=False):
"""Calculate a sequence of revisions to view, newest to oldest.
:param start_rev_id: the lower revision-id
:param end_rev_id: the upper revision-id
+ :param exclude_common_ancestry: Whether the start_rev_id should be part of
+ the iterated revisions.
:return: An iterator of (revision_id, dotted_revno, merge_depth) tuples.
:raises _StartNotLinearAncestor: if a start_rev_id is specified but
- is not found walking the left-hand history
+ is not found walking the left-hand history
"""
br_revno, br_rev_id = branch.last_revision_info()
repo = branch.repository
@@ -667,7 +673,8 @@
revno = branch.revision_id_to_dotted_revno(revision_id)
revno_str = '.'.join(str(n) for n in revno)
if not found_start and revision_id == start_rev_id:
- yield revision_id, revno_str, 0
+ if not exclude_common_ancestry:
+ yield revision_id, revno_str, 0
found_start = True
break
else:
=== modified file 'bzrlib/plugin.py'
--- a/bzrlib/plugin.py 2010-04-06 07:22:31 +0000
+++ b/bzrlib/plugin.py 2010-06-11 08:02:42 +0000
@@ -81,6 +81,33 @@
return path.rstrip("\\/")
+def _get_specific_plugin_paths(paths):
+ """Returns the plugin paths from a string describing the associations.
+
+ :param paths: A string describing the paths associated with the plugins.
+
+ :returns: A list of (plugin name, path) tuples.
+
+ For example, if paths is my_plugin@/test/my-test:her_plugin@/production/her,
+ [('my_plugin', '/test/my-test'), ('her_plugin', '/production/her')]
+ will be returned.
+
+ Note that ':' in the example above depends on the os.
+ """
+ if not paths:
+ return []
+ specs = []
+ for spec in paths.split(os.pathsep):
+ try:
+ name, path = spec.split('@')
+ except ValueError:
+ raise errors.BzrCommandError(
+ '"%s" is not a valid <plugin_name>@<plugin_path> description '
+ % spec)
+ specs.append((name, path))
+ return specs
+
+
def set_plugins_path(path=None):
"""Set the path for plugins to be loaded from.
@@ -98,10 +125,8 @@
for name in disabled_plugins.split(os.pathsep):
PluginImporter.blacklist.add('bzrlib.plugins.' + name)
# Set up a the specific paths for plugins
- specific_plugins = os.environ.get('BZR_PLUGINS_AT', None)
- if specific_plugins is not None:
- for spec in specific_plugins.split(os.pathsep):
- plugin_name, plugin_path = spec.split('@')
+ for plugin_name, plugin_path in _get_specific_plugin_paths(os.environ.get(
+ 'BZR_PLUGINS_AT', None)):
PluginImporter.specific_paths[
'bzrlib.plugins.%s' % plugin_name] = plugin_path
return path
@@ -562,7 +587,6 @@
# We are called only for specific paths
plugin_path = self.specific_paths[fullname]
loading_path = None
- package = False
if os.path.isdir(plugin_path):
for suffix, mode, kind in imp.get_suffixes():
if kind not in (imp.PY_SOURCE, imp.PY_COMPILED):
@@ -570,8 +594,12 @@
continue
init_path = osutils.pathjoin(plugin_path, '__init__' + suffix)
if os.path.isfile(init_path):
- loading_path = init_path
- package = True
+ # We've got a module here and load_module needs specific
+ # parameters.
+ loading_path = plugin_path
+ suffix = ''
+ mode = ''
+ kind = imp.PKG_DIRECTORY
break
else:
for suffix, mode, kind in imp.get_suffixes():
@@ -581,17 +609,18 @@
if loading_path is None:
raise ImportError('%s cannot be loaded from %s'
% (fullname, plugin_path))
- f = open(loading_path, mode)
+ if kind is imp.PKG_DIRECTORY:
+ f = None
+ else:
+ f = open(loading_path, mode)
try:
mod = imp.load_module(fullname, f, loading_path,
(suffix, mode, kind))
- if package:
- # The plugin can contain modules, so be ready
- mod.__path__ = [plugin_path]
mod.__package__ = fullname
return mod
finally:
- f.close()
+ if f is not None:
+ f.close()
# Install a dedicated importer for plugins requiring special handling
=== modified file 'bzrlib/recordcounter.py'
--- a/bzrlib/recordcounter.py 2010-05-19 13:30:09 +0000
+++ b/bzrlib/recordcounter.py 2010-06-12 02:58:42 +0000
@@ -31,10 +31,10 @@
# Users of RecordCounter instance update progress bar every
# _STEP_ records. We choose are reasonably high number to keep
- # display updates from being two frequent. This is an odd number
+ # display updates from being too frequent. This is an odd number
# to ensure that the last digit of the records fetched in
# fetches vs estimate ratio changes periodically.
- self.STEP = 71
+ self.STEP = 7
def is_initialized(self):
return self.initialized
=== modified file 'bzrlib/tests/blackbox/test_log.py'
--- a/bzrlib/tests/blackbox/test_log.py 2010-04-14 12:30:17 +0000
+++ b/bzrlib/tests/blackbox/test_log.py 2010-06-08 08:24:39 +0000
@@ -159,6 +159,14 @@
self.assertLogRevnos(['-c1'], ['1'])
+class TestLogExcludeCommonAncestry(TestLogWithLogCatcher):
+
+ def test_exclude_common_ancestry_simple_revnos(self):
+ self.make_linear_branch()
+ self.assertLogRevnos(['-r1..3', '--exclude-common-ancestry'],
+ ['3', '2'])
+
+
class TestLogMergedLinearAncestry(TestLogWithLogCatcher):
def setUp(self):
@@ -167,6 +175,18 @@
# stop calling run_bzr, there is no point) --vila 100118.
builder = branchbuilder.BranchBuilder(self.get_transport())
builder.start_series()
+ # 1
+ # | \
+ # 2 1.1.1
+ # | / |
+ # 3 1.1.2
+ # | |
+ # | 1.1.3
+ # | / |
+ # 4 1.1.4
+ # | /
+ # 5
+
# mainline
builder.build_snapshot('1', None, [
('add', ('', 'root-id', 'directory', ''))])
=== modified file 'bzrlib/tests/per_branch/test_locking.py'
--- a/bzrlib/tests/per_branch/test_locking.py 2010-05-20 03:20:04 +0000
+++ b/bzrlib/tests/per_branch/test_locking.py 2010-06-11 07:59:43 +0000
@@ -533,6 +533,7 @@
def test_lock_write_raises_in_lock_read(self):
branch = self.make_branch('b')
branch.lock_read()
+ self.addCleanup(branch.unlock)
err = self.assertRaises(errors.ReadOnlyError, branch.lock_write)
def test_lock_and_unlock_leaves_repo_unlocked(self):
=== modified file 'bzrlib/tests/test_log.py'
--- a/bzrlib/tests/test_log.py 2010-05-05 08:18:32 +0000
+++ b/bzrlib/tests/test_log.py 2010-06-08 09:50:27 +0000
@@ -1724,13 +1724,13 @@
return br
def assertLogRevnos(self, expected_revnos, b, start, end,
- exclude_common_ancestry):
+ exclude_common_ancestry, generate_merge_revisions=True):
# FIXME: the layering in log makes it hard to test intermediate levels,
# I wish adding filters with their parameters were easier...
# -- vila 20100413
iter_revs = log._calc_view_revisions(
b, start, end, direction='reverse',
- generate_merge_revisions=True,
+ generate_merge_revisions=generate_merge_revisions,
exclude_common_ancestry=exclude_common_ancestry)
self.assertEqual(expected_revnos,
[revid for revid, revno, depth in iter_revs])
@@ -1738,11 +1738,19 @@
def test_merge_sorted_exclude_ancestry(self):
b = self.make_branch_with_alternate_ancestries()
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
- b, '1', '3', False)
+ b, '1', '3', exclude_common_ancestry=False)
# '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
# it should be mentioned even if merge_sort order will make it appear
# after 1.1.1
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
- b, '1.1.1', '3', True)
+ b, '1.1.1', '3', exclude_common_ancestry=True)
+ def test_merge_sorted_simple_revnos_exclude_ancestry(self):
+ b = self.make_branch_with_alternate_ancestries()
+ self.assertLogRevnos(['3', '2'],
+ b, '1', '3', exclude_common_ancestry=True,
+ generate_merge_revisions=False)
+ self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2'],
+ b, '1', '3', exclude_common_ancestry=True,
+ generate_merge_revisions=True)
=== modified file 'bzrlib/tests/test_plugins.py'
--- a/bzrlib/tests/test_plugins.py 2010-05-16 13:49:19 +0000
+++ b/bzrlib/tests/test_plugins.py 2010-06-11 08:02:42 +0000
@@ -27,6 +27,7 @@
import bzrlib
from bzrlib import (
+ errors,
osutils,
plugin,
plugins,
@@ -79,6 +80,16 @@
if getattr(bzrlib.plugins, name, None) is not None:
delattr(bzrlib.plugins, name)
+ def _unregister_plugin_submodule(self, plugin_name, submodule_name):
+ """Remove the submodule from sys.modules and the bzrlib namespace."""
+ py_name = 'bzrlib.plugins.%s.%s' % (plugin_name, submodule_name)
+ if py_name in sys.modules:
+ del sys.modules[py_name]
+ plugin = getattr(bzrlib.plugins, plugin_name, None)
+ if plugin is not None:
+ if getattr(plugin, submodule_name, None) is not None:
+ delattr(plugin, submodule_name)
+
def assertPluginUnknown(self, name):
self.failIf(getattr(bzrlib.plugins, name, None) is not None)
self.failIf('bzrlib.plugins.%s' % name in sys.modules)
@@ -798,14 +809,35 @@
self.assertLength(0, self.warnings)
+class TestLoadPluginAtSyntax(tests.TestCase):
+
+ def _get_paths(self, paths):
+ return plugin._get_specific_plugin_paths(paths)
+
+ def test_empty(self):
+ self.assertEquals([], self._get_paths(None))
+ self.assertEquals([], self._get_paths(''))
+
+ def test_one_path(self):
+ self.assertEquals([('b', 'man')], self._get_paths('b at man'))
+
+ def test_bogus_path(self):
+ # We need a '@'
+ self.assertRaises(errors.BzrCommandError, self._get_paths, 'batman')
+ # Too much '@' isn't good either
+ self.assertRaises(errors.BzrCommandError, self._get_paths,
+ 'batman at mobile@cave')
+ # An empty description probably indicates a problem
+ self.assertRaises(errors.BzrCommandError, self._get_paths,
+ os.pathsep.join(['batman at cave', '', 'robin at mobile']))
+
+
class TestLoadPluginAt(tests.TestCaseInTempDir, TestPluginMixin):
def setUp(self):
super(TestLoadPluginAt, self).setUp()
# Make sure we don't pollute the plugins namespace
self.overrideAttr(plugins, '__path__')
- # Be paranoid in case a test fail
- self.addCleanup(self._unregister_plugin, 'test_foo')
# Reset the flag that protect against double loading
self.overrideAttr(plugin, '_loaded', False)
# Create the same plugin in two directories
@@ -813,6 +845,8 @@
# The "normal" directory, we use 'standard' instead of 'plugins' to
# avoid depending on the precise naming.
self.create_plugin_package('test_foo', dir='standard/test_foo')
+ # All the tests will load the 'test_foo' plugin from various locations
+ self.addCleanup(self._unregister_plugin, 'test_foo')
def assertTestFooLoadedFrom(self, path):
self.assertPluginKnown('test_foo')
@@ -860,6 +894,8 @@
def test_submodule_loading(self):
# We create an additional directory under the one for test_foo
self.create_plugin_package('test_bar', dir='non-standard-dir/test_bar')
+ self.addCleanup(self._unregister_plugin_submodule,
+ 'test_foo', 'test_bar')
osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo at non-standard-dir')
plugin.set_plugins_path(['standard'])
import bzrlib.plugins.test_foo
@@ -869,6 +905,22 @@
self.assertIsSameRealPath('non-standard-dir/test_bar/__init__.py',
bzrlib.plugins.test_foo.test_bar.__file__)
+ def test_relative_submodule_loading(self):
+ self.create_plugin_package('test_foo', dir='another-dir', source='''
+import test_bar
+''')
+ # We create an additional directory under the one for test_foo
+ self.create_plugin_package('test_bar', dir='another-dir/test_bar')
+ self.addCleanup(self._unregister_plugin_submodule,
+ 'test_foo', 'test_bar')
+ osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo at another-dir')
+ plugin.set_plugins_path(['standard'])
+ import bzrlib.plugins.test_foo
+ self.assertEqual('bzrlib.plugins.test_foo',
+ bzrlib.plugins.test_foo.__package__)
+ self.assertIsSameRealPath('another-dir/test_bar/__init__.py',
+ bzrlib.plugins.test_foo.test_bar.__file__)
+
def test_loading_from___init__only(self):
# We rename the existing __init__.py file to ensure that we don't load
# a random file
=== modified file 'bzrlib/tests/test_urlutils.py'
--- a/bzrlib/tests/test_urlutils.py 2010-05-27 22:10:42 +0000
+++ b/bzrlib/tests/test_urlutils.py 2010-06-08 01:31:34 +0000
@@ -156,7 +156,7 @@
# Weird stuff
# Can't have slashes or colons in the scheme
test_one('/path/to/://foo', None)
- test_one('path:path://foo', None)
+ test_one('scheme:stuff://foo', ('scheme', 'stuff://foo'))
# Must have more than one character for scheme
test_one('C://foo', None)
test_one('ab://foo', ('ab', 'foo'))
@@ -210,6 +210,8 @@
test('http://foo/bar/baz', 'http://foo', 'bar/baz')
test('http://foo/baz', 'http://foo', 'bar/../baz')
test('http://foo/baz', 'http://foo/bar/', '../baz')
+ test('lp:foo/bar', 'lp:foo', 'bar')
+ test('lp:foo/bar/baz', 'lp:foo', 'bar/baz')
# Absolute paths
test('http://foo', 'http://foo') # abs url with nothing is preserved.
@@ -219,6 +221,9 @@
test('http://bar/', 'http://foo', 'http://bar/')
test('http://bar/a', 'http://foo', 'http://bar/a')
test('http://bar/a/', 'http://foo', 'http://bar/a/')
+ test('lp:bar', 'http://foo', 'lp:bar')
+ test('lp:bar', 'lp:foo', 'lp:bar')
+ test('file:///stuff', 'lp:foo', 'file:///stuff')
# From a base path
test('file:///foo', 'file:///', 'foo')
=== modified file 'bzrlib/urlutils.py'
--- a/bzrlib/urlutils.py 2010-06-02 05:03:31 +0000
+++ b/bzrlib/urlutils.py 2010-06-14 17:58:24 +0000
@@ -101,7 +101,7 @@
first_path_slash = path.find('/')
if first_path_slash == -1:
return len(scheme), None
- return len(scheme), first_path_slash+len(scheme)+3
+ return len(scheme), first_path_slash+m.start('path')
def join(base, *args):
@@ -118,67 +118,26 @@
"""
if not args:
return base
- match = _url_scheme_re.match(base)
- scheme = None
- if match:
- scheme = match.group('scheme')
- path = match.group('path').split('/')
- if path[-1:] == ['']:
- # Strip off a trailing slash
- # This helps both when we are at the root, and when
- # 'base' has an extra slash at the end
- path = path[:-1]
- else:
- path = base.split('/')
-
- if scheme is not None and len(path) >= 1:
- host = path[:1]
- # the path should be represented as an abs path.
- # we know this must be absolute because of the presence of a URL scheme.
- remove_root = True
- path = [''] + path[1:]
- else:
- # create an empty host, but dont alter the path - this might be a
- # relative url fragment.
- host = []
- remove_root = False
-
+ scheme_end, path_start = _find_scheme_and_separator(base)
+ if scheme_end is None and path_start is None:
+ path_start = 0
+ elif path_start is None:
+ path_start = len(base)
+ path = base[path_start:]
for arg in args:
- match = _url_scheme_re.match(arg)
- if match:
- # Absolute URL
- scheme = match.group('scheme')
- # this skips .. normalisation, making http://host/../../..
- # be rather strange.
- path = match.group('path').split('/')
- # set the host and path according to new absolute URL, discarding
- # any previous values.
- # XXX: duplicates mess from earlier in this function. This URL
- # manipulation code needs some cleaning up.
- if scheme is not None and len(path) >= 1:
- host = path[:1]
- path = path[1:]
- # url scheme implies absolute path.
- path = [''] + path
- else:
- # no url scheme we take the path as is.
- host = []
+ arg_scheme_end, arg_path_start = _find_scheme_and_separator(arg)
+ if arg_scheme_end is None and arg_path_start is None:
+ arg_path_start = 0
+ elif arg_path_start is None:
+ arg_path_start = len(arg)
+ if arg_scheme_end is not None:
+ base = arg
+ path = arg[arg_path_start:]
+ scheme_end = arg_scheme_end
+ path_start = arg_path_start
else:
- path = '/'.join(path)
path = joinpath(path, arg)
- path = path.split('/')
- if remove_root and path[0:1] == ['']:
- del path[0]
- if host:
- # Remove the leading slash from the path, so long as it isn't also the
- # trailing slash, which we want to keep if present.
- if path and path[0] == '' and len(path) > 1:
- del path[0]
- path = host + path
-
- if scheme is None:
- return '/'.join(path)
- return scheme + '://' + '/'.join(path)
+ return base[:path_start] + path
def joinpath(base, *args):
@@ -303,7 +262,7 @@
MIN_ABS_FILEURL_LENGTH = WIN32_MIN_ABS_FILEURL_LENGTH
-_url_scheme_re = re.compile(r'^(?P<scheme>[^:/]{2,})://(?P<path>.*)$')
+_url_scheme_re = re.compile(r'^(?P<scheme>[^:/]{2,}):(//)?(?P<path>.*)$')
_url_hex_escapes_re = re.compile(r'(%[0-9a-fA-F]{2})')
@@ -339,18 +298,18 @@
:param url: Either a hybrid URL or a local path
:return: A normalized URL which only includes 7-bit ASCII characters.
"""
- m = _url_scheme_re.match(url)
- if not m:
+ scheme_end, path_start = _find_scheme_and_separator(url)
+ if scheme_end is None:
return local_path_to_url(url)
- scheme = m.group('scheme')
- path = m.group('path')
+ prefix = url[:path_start]
+ path = url[path_start:]
if not isinstance(url, unicode):
for c in url:
if c not in _url_safe_characters:
raise errors.InvalidURL(url, 'URLs can only contain specific'
' safe characters (not %r)' % c)
path = _url_hex_escapes_re.sub(_unescape_safe_chars, path)
- return str(scheme + '://' + ''.join(path))
+ return str(prefix + ''.join(path))
# We have a unicode (hybrid) url
path_chars = list(path)
@@ -362,7 +321,7 @@
['%%%02X' % ord(c) for c in path_chars[i].encode('utf-8')])
path = ''.join(path_chars)
path = _url_hex_escapes_re.sub(_unescape_safe_chars, path)
- return str(scheme + '://' + path)
+ return str(prefix + path)
def relative_url(base, other):
More information about the bazaar-commits
mailing list