Merge lp:~jelmer/bzr-keywords/lazy into lp:bzr-keywords

Proposed by Jelmer Vernooij
Status: Merged
Approved by: John A Meinel
Approved revision: no longer in the source branch.
Merged at revision: 18
Proposed branch: lp:~jelmer/bzr-keywords/lazy
Merge into: lp:bzr-keywords
Diff against target: 652 lines (+294/-266)
4 files modified
__init__.py (+23/-258)
keywords.py (+264/-0)
tests/test_conversion.py (+4/-1)
tests/test_keywords_in_trees.py (+3/-7)
To merge this branch: bzr merge lp:~jelmer/bzr-keywords/lazy
Reviewer Review Type Date Requested Status
John A Meinel Needs Information
Martin Pool (community) Approve
Review via email: mp+51444@code.launchpad.net

Description of the change

Lazily load the keywords plugin.

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) :
review: Approve
Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 2/27/2011 4:57 AM, Jelmer Vernooij wrote:
> Jelmer Vernooij has proposed merging lp:~jelmer/bzr-keywords/lazy into lp:bzr-keywords.
>
> Requested reviews:
> Bazaar Developers (bzr)
>
> For more details, see:
> https://code.launchpad.net/~jelmer/bzr-keywords/lazy/+merge/51444
>
> Lazily load the keywords plugin.

I'm a bit surprised at how much code is added here, versus how much is
removed.
All the format_date, extract_name, etc don't seem to come from somewhere
else.

Is this just a large rewrite of the internals?

The changes seem fine to me, but I didn't go over them in detail, with
the change being surprisingly large.

Care to explain a bit more what you changed?

 review: needsinfo

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk1uFfAACgkQJdeBCYSNAAPKpQCcD0EpuFviTVfxSMY5ikxIwPrW
KV8AoIGp7aUs4DDgGztSHhpYnyzoNk5l
=3j0u
-----END PGP SIGNATURE-----

review: Needs Information
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On 2/27/2011 4:57 AM, Jelmer Vernooij wrote:
> > Jelmer Vernooij has proposed merging lp:~jelmer/bzr-keywords/lazy into lp
> :bzr-keywords.
> >
> > Requested reviews:
> > Bazaar Developers (bzr)
> >
> > For more details, see:
> > https://code.launchpad.net/~jelmer/bzr-keywords/lazy/+merge/51444
> >
> > Lazily load the keywords plugin.
>
> I'm a bit surprised at how much code is added here, versus how much is
> removed.
> All the format_date, extract_name, etc don't seem to come from somewhere
> else.
>
> Is this just a large rewrite of the internals?
>
> The changes seem fine to me, but I didn't go over them in detail, with
> the change being surprisingly large.
>
> Care to explain a bit more what you changed?
There was a conflict with some earlier changes, which caused the code I moved around to stay in __init__.py in the conflicts. Should be fixed now.

lp:~jelmer/bzr-keywords/lazy updated
18. By Jelmer Vernooij

Merge lazy loading support.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '__init__.py'
2--- __init__.py 2010-10-01 19:44:29 +0000
3+++ __init__.py 2011-03-02 12:34:10 +0000
4@@ -105,18 +105,11 @@
5 '''
6
7
8-import re, time
9 from bzrlib import (
10 builtins,
11 commands,
12- config,
13- debug,
14 filters,
15 option,
16- osutils,
17- registry,
18- trace,
19- xml8,
20 )
21
22
23@@ -137,249 +130,13 @@
24 return suite
25
26
27-# Expansion styles
28-# Note: Round-tripping is only required between the raw and cooked styles
29-_keyword_style_registry = registry.Registry()
30-_keyword_style_registry.register('raw', '$%(name)s$')
31-_keyword_style_registry.register('cooked', '$%(name)s: %(value)s $')
32-_keyword_style_registry.register('publish', '%(name)s: %(value)s')
33-_keyword_style_registry.register('publish-values', '%(value)s')
34-_keyword_style_registry.register('publish-names', '%(name)s')
35-_keyword_style_registry.default_key = 'cooked'
36-
37-
38-# Regular expressions for matching the raw and cooked patterns
39-_KW_RAW_RE = re.compile(r'\$([\w\-]+)(:[^$]*)?\$')
40-_KW_COOKED_RE = re.compile(r'\$([\w\-]+):([^$]+)\$')
41-
42-
43-# The registry of keywords. Other plugins may wish to add entries to this.
44-keyword_registry = registry.Registry()
45-
46-# Revision-related keywords
47-keyword_registry.register('Date',
48- lambda c: format_date(c.revision().timestamp, c.revision().timezone,
49- c.config(), 'Date'))
50-keyword_registry.register('Committer',
51- lambda c: c.revision().committer)
52-keyword_registry.register('Authors',
53- lambda c: ", ".join(c.revision().get_apparent_authors()))
54-keyword_registry.register('Revision-Id',
55- lambda c: c.revision_id())
56-keyword_registry.register('Path',
57- lambda c: c.relpath())
58-keyword_registry.register('Directory',
59- lambda c: osutils.split(c.relpath())[0])
60-keyword_registry.register('Filename',
61- lambda c: osutils.split(c.relpath())[1])
62-keyword_registry.register('File-Id',
63- lambda c: c.file_id())
64-
65-# Environment-related keywords
66-keyword_registry.register('Now',
67- lambda c: format_date(time.time(), time.timezone, c.config(), 'Now'))
68-keyword_registry.register('User',
69- lambda c: c.config().username())
70-
71-# Keywords for finer control over name & address formatting
72-keyword_registry.register('Committer-Name',
73- lambda c: extract_name(c.revision().committer))
74-keyword_registry.register('Committer-Email',
75- lambda c: extract_email(c.revision().committer))
76-keyword_registry.register('Author1-Name',
77- lambda c: extract_name_item(c.revision().get_apparent_authors(), 0))
78-keyword_registry.register('Author1-Email',
79- lambda c: extract_email_item(c.revision().get_apparent_authors(), 0))
80-keyword_registry.register('Author2-Name',
81- lambda c: extract_name_item(c.revision().get_apparent_authors(), 1))
82-keyword_registry.register('Author2-Email',
83- lambda c: extract_email_item(c.revision().get_apparent_authors(), 1))
84-keyword_registry.register('Author3-Name',
85- lambda c: extract_name_item(c.revision().get_apparent_authors(), 2))
86-keyword_registry.register('Author3-Email',
87- lambda c: extract_email_item(c.revision().get_apparent_authors(), 2))
88-keyword_registry.register('User-Name',
89- lambda c: extract_name(c.config().username()))
90-keyword_registry.register('User-Email',
91- lambda c: extract_email(c.config().username()))
92-
93-
94-def format_date(timestamp, offset=0, cfg=None, name=None):
95- """Return a formatted date string.
96-
97- :param timestamp: Seconds since the epoch.
98- :param offset: Timezone offset in seconds east of utc.
99- """
100- if cfg is not None and name is not None:
101- cfg_key = 'keywords.format.%s' % (name,)
102- format = cfg.get_user_option(cfg_key)
103- else:
104- format = None
105- return osutils.format_date(timestamp, offset, date_fmt=format)
106-
107-
108-def extract_name(userid):
109- """Extract the name out of a user-id string.
110-
111- user-id strings have the format 'name <email>'.
112- """
113- if userid and userid[-1] == '>':
114- return userid[:-1].rsplit('<', 1)[0].rstrip()
115- else:
116- return userid
117-
118-
119-def extract_email(userid):
120- """Extract the email address out of a user-id string.
121-
122- user-id strings have the format 'name <email>'.
123- """
124- if userid and userid[-1] == '>':
125- return userid[:-1].rsplit('<', 1)[1]
126- else:
127- return userid
128-
129-def extract_name_item(seq, n):
130- """Extract the name out of the nth item in a sequence of user-ids.
131-
132- :return: the user-name or an empty string
133- """
134- try:
135- return extract_name(seq[n])
136- except IndexError:
137- return ""
138-
139-
140-def extract_email_item(seq, n):
141- """Extract the email out of the nth item in a sequence of user-ids.
142-
143- :return: the email address or an empty string
144- """
145- try:
146- return extract_email(seq[n])
147- except IndexError:
148- return ""
149-
150-
151-def compress_keywords(s, keyword_dicts):
152- """Replace cooked style keywords with raw style in a string.
153-
154- Note: If the keyword is not known, the text is not modified.
155-
156- :param s: the string
157- :param keyword_dicts: an iterable of keyword dictionaries.
158- :return: the string with keywords compressed
159- """
160- _raw_style = _keyword_style_registry.get('raw')
161- result = ''
162- rest = s
163- while (True):
164- match = _KW_COOKED_RE.search(rest)
165- if not match:
166- break
167- result += rest[:match.start()]
168- keyword = match.group(1)
169- expansion = _get_from_dicts(keyword_dicts, keyword)
170- if expansion is None:
171- # Unknown expansion - leave as is
172- result += match.group(0)
173- else:
174- result += _raw_style % {'name': keyword}
175- rest = rest[match.end():]
176- return result + rest
177-
178-
179-def expand_keywords(s, keyword_dicts, context=None, encoder=None, style=None):
180- """Replace raw style keywords with another style in a string.
181-
182- Note: If the keyword is already in the expanded style, the value is
183- not replaced.
184-
185- :param s: the string
186- :param keyword_dicts: an iterable of keyword dictionaries. If values
187- are callables, they are executed to find the real value.
188- :param context: the parameter to pass to callable values
189- :param style: the style of expansion to use of None for the default
190- :return: the string with keywords expanded
191- """
192- _expanded_style = _keyword_style_registry.get(style)
193- result = ''
194- rest = s
195- while (True):
196- match = _KW_RAW_RE.search(rest)
197- if not match:
198- break
199- result += rest[:match.start()]
200- keyword = match.group(1)
201- expansion = _get_from_dicts(keyword_dicts, keyword)
202- if callable(expansion):
203- try:
204- expansion = expansion(context)
205- except AttributeError, err:
206- if 'error' in debug.debug_flags:
207- trace.note("error evaluating %s for keyword %s: %s",
208- expansion, keyword, err)
209- expansion = "(evaluation error)"
210- if expansion is None:
211- # Unknown expansion - leave as is
212- result += match.group(0)
213- rest = rest[match.end():]
214- continue
215- if '$' in expansion:
216- # Expansion is not safe to be collapsed later
217- expansion = "(value unsafe to expand)"
218- if encoder is not None:
219- expansion = encoder(expansion)
220- params = {'name': keyword, 'value': expansion}
221- result += _expanded_style % params
222- rest = rest[match.end():]
223- return result + rest
224-
225-
226-def _get_from_dicts(dicts, key, default=None):
227- """Search a sequence of dictionaries or registries for a key.
228-
229- :return: the value, or default if not found
230- """
231- for dict in dicts:
232- if key in dict:
233- return dict.get(key)
234- return default
235-
236-
237-def _xml_escape(s):
238- """Escape a string so it can be included safely in XML/HTML."""
239- # Complie the regular expressions if not already done
240- xml8._ensure_utf8_re()
241- # Convert and strip the trailing quote
242- return xml8._encode_and_escape(s)[:-1]
243-
244-
245-def _kw_compressor(chunks, context=None):
246- """Filter that replaces keywords with their compressed form."""
247- text = ''.join(chunks)
248- return [compress_keywords(text, [keyword_registry])]
249-
250-
251-def _kw_expander(chunks, context, encoder=None):
252- """Keyword expander."""
253- text = ''.join(chunks)
254- return [expand_keywords(text, [keyword_registry], context=context,
255- encoder=encoder)]
256-
257-
258-def _normal_kw_expander(chunks, context=None):
259- """Filter that replaces keywords with their expanded form."""
260- return _kw_expander(chunks, context)
261-
262-
263-def _xml_escape_kw_expander(chunks, context=None):
264- """Filter that replaces keywords with a form suitable for use in XML."""
265- return _kw_expander(chunks, context, encoder=_xml_escape)
266-
267-
268 # Define and register the filter stack map
269 def _keywords_filter_stack_lookup(k):
270+ from bzrlib.plugins.keywords.keywords import (
271+ _kw_compressor,
272+ _normal_kw_expander,
273+ _xml_escape_kw_expander,
274+ )
275 filter_stack_map = {
276 'off': [],
277 'on':
278@@ -416,21 +173,25 @@
279 # override the inherited run() and help() methods
280
281 takes_options = builtins.cmd_cat.takes_options + [
282- option.RegistryOption('keywords',
283- registry=_keyword_style_registry,
284- converter=lambda s: s,
285- help='Keyword expansion style.')]
286-
287+ option.RegistryOption('keywords',
288+ lazy_registry=("bzrlib.plugins.keywords.keywords",
289+ "_keyword_style_registry"),
290+ converter=lambda s: s,
291+ help='Keyword expansion style.')]
292+
293 def run(self, *args, **kwargs):
294 """Process special options and delegate to superclass."""
295 if 'keywords' in kwargs:
296+ from bzrlib.plugins.keywords.keywords import (
297+ _keyword_style_registry,
298+ )
299 # Implicitly set the filters option
300 kwargs['filters'] = True
301 style = kwargs['keywords']
302 _keyword_style_registry.default_key = style
303 del kwargs['keywords']
304 return super(cmd_cat, self).run(*args, **kwargs)
305-
306+
307 def help(self):
308 """Return help message including text from superclass."""
309 from inspect import getdoc
310@@ -442,21 +203,25 @@
311 # override the inherited run() and help() methods
312
313 takes_options = builtins.cmd_export.takes_options + [
314- option.RegistryOption('keywords',
315- registry=_keyword_style_registry,
316+ option.RegistryOption('keywords',
317+ lazy_registry=("bzrlib.plugins.keywords.keywords",
318+ "_keyword_style_registry"),
319 converter=lambda s: s,
320 help='Keyword expansion style.')]
321-
322+
323 def run(self, *args, **kwargs):
324 """Process special options and delegate to superclass."""
325 if 'keywords' in kwargs:
326+ from bzrlib.plugins.keywords.keywords import (
327+ _keyword_style_registry,
328+ )
329 # Implicitly set the filters option
330 kwargs['filters'] = True
331 style = kwargs['keywords']
332 _keyword_style_registry.default_key = style
333 del kwargs['keywords']
334 return super(cmd_export, self).run(*args, **kwargs)
335-
336+
337 def help(self):
338 """Return help message including text from superclass."""
339 from inspect import getdoc
340
341=== added file 'keywords.py'
342--- keywords.py 1970-01-01 00:00:00 +0000
343+++ keywords.py 2011-03-02 12:34:10 +0000
344@@ -0,0 +1,264 @@
345+# Copyright (C) 2008 Canonical Ltd
346+#
347+# This program is free software; you can redistribute it and/or modify
348+# it under the terms of the GNU General Public License as published by
349+# the Free Software Foundation; either version 2 of the License, or
350+# (at your option) any later version.
351+#
352+# This program is distributed in the hope that it will be useful,
353+# but WITHOUT ANY WARRANTY; without even the implied warranty of
354+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
355+# GNU General Public License for more details.
356+#
357+# You should have received a copy of the GNU General Public License
358+# along with this program; if not, write to the Free Software
359+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
360+
361+import re, time
362+from bzrlib import (
363+ debug,
364+ osutils,
365+ registry,
366+ trace,
367+ )
368+
369+# Expansion styles
370+# Note: Round-tripping is only required between the raw and cooked styles
371+_keyword_style_registry = registry.Registry()
372+_keyword_style_registry.register('raw', '$%(name)s$')
373+_keyword_style_registry.register('cooked', '$%(name)s: %(value)s $')
374+_keyword_style_registry.register('publish', '%(name)s: %(value)s')
375+_keyword_style_registry.register('publish-values', '%(value)s')
376+_keyword_style_registry.register('publish-names', '%(name)s')
377+_keyword_style_registry.default_key = 'cooked'
378+
379+
380+# Regular expressions for matching the raw and cooked patterns
381+_KW_RAW_RE = re.compile(r'\$([\w\-]+)(:[^$]*)?\$')
382+_KW_COOKED_RE = re.compile(r'\$([\w\-]+):([^$]+)\$')
383+
384+
385+# The registry of keywords. Other plugins may wish to add entries to this.
386+keyword_registry = registry.Registry()
387+
388+# Revision-related keywords
389+keyword_registry.register('Date',
390+ lambda c: format_date(c.revision().timestamp, c.revision().timezone,
391+ c.config(), 'Date'))
392+keyword_registry.register('Committer',
393+ lambda c: c.revision().committer)
394+keyword_registry.register('Authors',
395+ lambda c: ", ".join(c.revision().get_apparent_authors()))
396+keyword_registry.register('Revision-Id',
397+ lambda c: c.revision_id())
398+keyword_registry.register('Path',
399+ lambda c: c.relpath())
400+keyword_registry.register('Directory',
401+ lambda c: osutils.split(c.relpath())[0])
402+keyword_registry.register('Filename',
403+ lambda c: osutils.split(c.relpath())[1])
404+keyword_registry.register('File-Id',
405+ lambda c: c.file_id())
406+
407+# Environment-related keywords
408+keyword_registry.register('Now',
409+ lambda c: format_date(time.time(), time.timezone, c.config(), 'Now'))
410+keyword_registry.register('User',
411+ lambda c: c.config().username())
412+
413+# Keywords for finer control over name & address formatting
414+keyword_registry.register('Committer-Name',
415+ lambda c: extract_name(c.revision().committer))
416+keyword_registry.register('Committer-Email',
417+ lambda c: extract_email(c.revision().committer))
418+keyword_registry.register('Author1-Name',
419+ lambda c: extract_name_item(c.revision().get_apparent_authors(), 0))
420+keyword_registry.register('Author1-Email',
421+ lambda c: extract_email_item(c.revision().get_apparent_authors(), 0))
422+keyword_registry.register('Author2-Name',
423+ lambda c: extract_name_item(c.revision().get_apparent_authors(), 1))
424+keyword_registry.register('Author2-Email',
425+ lambda c: extract_email_item(c.revision().get_apparent_authors(), 1))
426+keyword_registry.register('Author3-Name',
427+ lambda c: extract_name_item(c.revision().get_apparent_authors(), 2))
428+keyword_registry.register('Author3-Email',
429+ lambda c: extract_email_item(c.revision().get_apparent_authors(), 2))
430+keyword_registry.register('User-Name',
431+ lambda c: extract_name(c.config().username()))
432+keyword_registry.register('User-Email',
433+ lambda c: extract_email(c.config().username()))
434+
435+
436+def format_date(timestamp, offset=0, cfg=None, name=None):
437+ """Return a formatted date string.
438+
439+ :param timestamp: Seconds since the epoch.
440+ :param offset: Timezone offset in seconds east of utc.
441+ """
442+ if cfg is not None and name is not None:
443+ cfg_key = 'keywords.format.%s' % (name,)
444+ format = cfg.get_user_option(cfg_key)
445+ else:
446+ format = None
447+ return osutils.format_date(timestamp, offset, date_fmt=format)
448+
449+
450+def extract_name(userid):
451+ """Extract the name out of a user-id string.
452+
453+ user-id strings have the format 'name <email>'.
454+ """
455+ if userid and userid[-1] == '>':
456+ return userid[:-1].rsplit('<', 1)[0].rstrip()
457+ else:
458+ return userid
459+
460+
461+def extract_email(userid):
462+ """Extract the email address out of a user-id string.
463+
464+ user-id strings have the format 'name <email>'.
465+ """
466+ if userid and userid[-1] == '>':
467+ return userid[:-1].rsplit('<', 1)[1]
468+ else:
469+ return userid
470+
471+def extract_name_item(seq, n):
472+ """Extract the name out of the nth item in a sequence of user-ids.
473+
474+ :return: the user-name or an empty string
475+ """
476+ try:
477+ return extract_name(seq[n])
478+ except IndexError:
479+ return ""
480+
481+
482+def extract_email_item(seq, n):
483+ """Extract the email out of the nth item in a sequence of user-ids.
484+
485+ :return: the email address or an empty string
486+ """
487+ try:
488+ return extract_email(seq[n])
489+ except IndexError:
490+ return ""
491+
492+
493+def compress_keywords(s, keyword_dicts):
494+ """Replace cooked style keywords with raw style in a string.
495+
496+ Note: If the keyword is not known, the text is not modified.
497+
498+ :param s: the string
499+ :param keyword_dicts: an iterable of keyword dictionaries.
500+ :return: the string with keywords compressed
501+ """
502+ _raw_style = _keyword_style_registry.get('raw')
503+ result = ''
504+ rest = s
505+ while (True):
506+ match = _KW_COOKED_RE.search(rest)
507+ if not match:
508+ break
509+ result += rest[:match.start()]
510+ keyword = match.group(1)
511+ expansion = _get_from_dicts(keyword_dicts, keyword)
512+ if expansion is None:
513+ # Unknown expansion - leave as is
514+ result += match.group(0)
515+ else:
516+ result += _raw_style % {'name': keyword}
517+ rest = rest[match.end():]
518+ return result + rest
519+
520+
521+def expand_keywords(s, keyword_dicts, context=None, encoder=None, style=None):
522+ """Replace raw style keywords with another style in a string.
523+
524+ Note: If the keyword is already in the expanded style, the value is
525+ not replaced.
526+
527+ :param s: the string
528+ :param keyword_dicts: an iterable of keyword dictionaries. If values
529+ are callables, they are executed to find the real value.
530+ :param context: the parameter to pass to callable values
531+ :param style: the style of expansion to use of None for the default
532+ :return: the string with keywords expanded
533+ """
534+ _expanded_style = _keyword_style_registry.get(style)
535+ result = ''
536+ rest = s
537+ while (True):
538+ match = _KW_RAW_RE.search(rest)
539+ if not match:
540+ break
541+ result += rest[:match.start()]
542+ keyword = match.group(1)
543+ expansion = _get_from_dicts(keyword_dicts, keyword)
544+ if callable(expansion):
545+ try:
546+ expansion = expansion(context)
547+ except AttributeError, err:
548+ if 'error' in debug.debug_flags:
549+ trace.note("error evaluating %s for keyword %s: %s",
550+ expansion, keyword, err)
551+ expansion = "(evaluation error)"
552+ if expansion is None:
553+ # Unknown expansion - leave as is
554+ result += match.group(0)
555+ rest = rest[match.end():]
556+ continue
557+ if '$' in expansion:
558+ # Expansion is not safe to be collapsed later
559+ expansion = "(value unsafe to expand)"
560+ if encoder is not None:
561+ expansion = encoder(expansion)
562+ params = {'name': keyword, 'value': expansion}
563+ result += _expanded_style % params
564+ rest = rest[match.end():]
565+ return result + rest
566+
567+
568+def _get_from_dicts(dicts, key, default=None):
569+ """Search a sequence of dictionaries or registries for a key.
570+
571+ :return: the value, or default if not found
572+ """
573+ for dict in dicts:
574+ if key in dict:
575+ return dict.get(key)
576+ return default
577+
578+
579+def _xml_escape(s):
580+ """Escape a string so it can be included safely in XML/HTML."""
581+ # Compile the regular expressions if not already done
582+ from bzrlib import xml8
583+ xml8._ensure_utf8_re()
584+ # Convert and strip the trailing quote
585+ return xml8._encode_and_escape(s)[:-1]
586+
587+
588+def _kw_compressor(chunks, context=None):
589+ """Filter that replaces keywords with their compressed form."""
590+ text = ''.join(chunks)
591+ return [compress_keywords(text, [keyword_registry])]
592+
593+
594+def _kw_expander(chunks, context, encoder=None):
595+ """Keyword expander."""
596+ text = ''.join(chunks)
597+ return [expand_keywords(text, [keyword_registry], context=context,
598+ encoder=encoder)]
599+
600+
601+def _normal_kw_expander(chunks, context=None):
602+ """Filter that replaces keywords with their expanded form."""
603+ return _kw_expander(chunks, context)
604+
605+
606+def _xml_escape_kw_expander(chunks, context=None):
607+ """Filter that replaces keywords with a form suitable for use in XML."""
608+ return _kw_expander(chunks, context, encoder=_xml_escape)
609
610=== modified file 'tests/test_conversion.py'
611--- tests/test_conversion.py 2010-10-01 17:31:51 +0000
612+++ tests/test_conversion.py 2011-03-02 12:34:10 +0000
613@@ -18,7 +18,10 @@
614
615
616 from bzrlib import tests
617-from bzrlib.plugins.keywords import compress_keywords, expand_keywords
618+from bzrlib.plugins.keywords.keywords import (
619+ compress_keywords,
620+ expand_keywords,
621+ )
622
623
624 # Sample unexpanded and expanded pairs for a keyword dictionary
625
626=== modified file 'tests/test_keywords_in_trees.py'
627--- tests/test_keywords_in_trees.py 2009-08-11 05:14:31 +0000
628+++ tests/test_keywords_in_trees.py 2011-03-02 12:34:10 +0000
629@@ -18,12 +18,8 @@
630
631 ## TODO: add tests for xml_escaped
632
633-from cStringIO import StringIO
634-import sys
635-
636-from bzrlib import config, rules
637-from bzrlib.tests import TestCaseWithTransport, TestSkipped
638-from bzrlib.tests.per_workingtree import TestCaseWithWorkingTree
639+from bzrlib import rules
640+from bzrlib.tests import TestCaseWithTransport
641 from bzrlib.workingtree import WorkingTree
642
643
644@@ -90,7 +86,7 @@
645 t.add('file1', 'file1-id')
646 t.commit("add file1", rev_id="rev1-id",
647 committer="Jane Smith <jane@example.com>",
648- author="Sue Smith <sue@example.com>")
649+ authors=["Sue Smith <sue@example.com>"])
650 basis = t.basis_tree()
651 basis.lock_read()
652 self.addCleanup(basis.unlock)

Subscribers

People subscribed via source and target branches