launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #02668
[Merge] lp:~jml/launchpad/flush-out-canonical into lp:launchpad
Jonathan Lange has proposed merging lp:~jml/launchpad/flush-out-canonical into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~jml/launchpad/flush-out-canonical/+merge/50192
This branch flushes out a lot of the modules that were living in the 'canonical' package. Easiest to take them module-by-module.
autodecorate:
* moved to lp.services.utils
* doctest replaced with unit tests
base:
* moved to lp.services.utils
* doctest replaced with unit tests
* inspired new compress_hash method, changed all the call sites to use that
chunkydiff:
* Only used to display different output for page tests
* Saw the output wasn't that different, so deleted it and all its kind.
encoding:
* Moved to lp.services
functional:
* Only contained xmlrpc stuff, so moved to lp.services.xmlrpc
__init__:
* Moved the content to lp_sitecustomize (which is where it belongs)
* Added docstring
mem:
* Moved to lp.services.profile. Yes we are using this.
--
https://code.launchpad.net/~jml/launchpad/flush-out-canonical/+merge/50192
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jml/launchpad/flush-out-canonical into lp:launchpad.
=== modified file 'configs/testrunner/launchpad-lazr.conf'
--- configs/testrunner/launchpad-lazr.conf 2010-09-17 09:16:27 +0000
+++ configs/testrunner/launchpad-lazr.conf 2011-02-17 17:10:46 +0000
@@ -6,7 +6,6 @@
extends: ../development/launchpad-lazr.conf
[canonical]
-chunkydiff: False
cron_control_url: file:lib/lp/services/scripts/tests/cronscripts.ini
[archivepublisher]
=== modified file 'lib/canonical/__init__.py'
--- lib/canonical/__init__.py 2010-03-30 08:50:10 +0000
+++ lib/canonical/__init__.py 2011-02-17 17:10:46 +0000
@@ -1,21 +1,8 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
-# This is the python package that defines the 'canonical' package namespace.
-
-# We currently only translate launchpad specific stuff using the Zope3 i18n
-# routines, so this is not needed.
-#from zope.i18n.messageid import MessageFactory
-#_ = MessageFactory("canonical")
-
-# Filter all deprecation warnings for Zope 3.6, which eminate from
-# the zope package.
-import warnings
-filter_pattern = '.*(Zope 3.6|provide.*global site manager).*'
-warnings.filterwarnings(
- 'ignore', filter_pattern, category=DeprecationWarning)
-
-# XXX wgrant 2010-03-30 bug=551510:
-# Also filter apt_pkg warnings, since Lucid's python-apt has a new API.
-warnings.filterwarnings(
- 'ignore', '.*apt_pkg.*', category=DeprecationWarning)
+"""The canonical namespace package.
+
+WARNING: This package is deprecated. New code should go into the `lp`
+package.
+"""
=== removed file 'lib/canonical/autodecorate.py'
--- lib/canonical/autodecorate.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/autodecorate.py 1970-01-01 00:00:00 +0000
@@ -1,29 +0,0 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Metaclass to automatically decorate methods."""
-
-from types import FunctionType
-
-
-__metaclass__ = type
-__all__ = ['AutoDecorate']
-
-
-def AutoDecorate(*decorators):
- """Factory to generate metaclasses that automatically apply decorators."""
-
- class AutoDecorateMetaClass(type):
- def __new__(cls, class_name, bases, class_dict):
- new_class_dict = {}
- for name, value in class_dict.items():
- if type(value) == FunctionType:
- for decorator in decorators:
- value = decorator(value)
- assert callable(value), (
- "Decorator %s didn't return a callable."
- % repr(decorator))
- new_class_dict[name] = value
- return type.__new__(cls, class_name, bases, new_class_dict)
-
- return AutoDecorateMetaClass
=== removed file 'lib/canonical/base.py'
--- lib/canonical/base.py 2010-02-09 00:17:40 +0000
+++ lib/canonical/base.py 1970-01-01 00:00:00 +0000
@@ -1,81 +0,0 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Convert numbers to an arbitrary base numbering scheme
-
-This file is based on work from the Python Cookbook and is under the Python
-license.
-
-"""
-__all__ = ['base']
-
-import string
-abc = string.digits + string.ascii_letters
-
-def base(number, radix):
- """Inverse function to int(str,radix) and long(str,radix)
-
- >>> base(35, 36)
- 'z'
-
- We can go higher than base 36, but we do this by using upper
- case letters. This is not a standard representation, but
- useful for using this method as a compression algorithm.
-
- >>> base(61, 62)
- 'Z'
-
- We get identical results to the hex builtin, without the 0x prefix
-
- >>> [i for i in range(0, 5000, 9) if hex(i)[2:] != base(i, 16)]
- []
-
- This method is useful for shrinking sha1 and md5 hashes, but keeping
- them in simple ASCII suitable for URL's etc.
-
- >>> import hashlib
- >>> s = hashlib.sha1('foo').hexdigest()
- >>> s
- '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
- >>> i = long(s, 16)
- >>> i
- 68123873083688143418383284816464454849230703155L
- >>> base(i, 62)
- '1HyPQr2xj1nmnkQXBCJXUdQoy5l'
- >>> base(int(hashlib.md5('foo').hexdigest(), 16), 62)
- '5fX649Stem9fET0lD46zVe'
-
- A sha1 hash can be compressed to 27 characters or less
- >>> len(base(long('F'*40, 16), 62))
- 27
-
- A md5 hash can be compressed to 22 characters or less
- >>> len(base(long('F'*32, 16), 62))
- 22
-
- """
- if not 2 <= radix <= 62:
- raise ValueError, "radix must be in 2..62"
-
- result = []
- addon = result.append
- if number < 0:
- number = -number
- addon('-')
- elif number == 0:
- addon('0')
-
- _divmod, _abc = divmod, abc
- while number:
- number, rdigit = _divmod(number, radix)
- addon(_abc[rdigit])
-
- result.reverse()
- return ''.join(result)
-
-def _test():
- import doctest, base
- doctest.testmod(base)
-
-if __name__ == '__main__':
- _test()
=== removed file 'lib/canonical/chunkydiff.py'
--- lib/canonical/chunkydiff.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/chunkydiff.py 1970-01-01 00:00:00 +0000
@@ -1,249 +0,0 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Chunky diffs.
-
-Useful for page tests that have elisions.
-"""
-
-import re
-
-__metaclass__ = type
-
-def elided_source(tested, actual, debug=False, show=False,
- normalize_whitespace=False):
- if debug:
- import pdb; pdb.set_trace()
- chunks = tested.split('...')
-
- previous_chunk = None
- chunk = None
- Unknown = None
- currentpos = 0
- results = []
- for next_chunk in chunks + [None]:
- if chunk is None:
- chunk = next_chunk
- continue
- if chunk != '':
- if previous_chunk is None:
- chunk_starts_with_ellipsis = False
- else:
- chunk_starts_with_ellipsis = True
- if next_chunk is None:
- chunk_ends_with_ellipsis = False
- else:
- chunk_ends_with_ellipsis = True
-
- result = find_chunk(
- chunk, actual[currentpos:],
- anchor_start=not chunk_starts_with_ellipsis,
- anchor_end=not chunk_ends_with_ellipsis,
- debug=debug, show=show,
- normalize_whitespace=normalize_whitespace)
- if result is None:
- results.append(None)
- else:
- string, startofremainder = result
- currentpos += startofremainder
- # XXX: ddaa 2005-03-25:
- # Off by one. Should be += startofremainder + 1
- results.append(ResultChunk(string, currentpos))
- previous_chunk, chunk = chunk, next_chunk
-
- starts_with_ellipsis = chunks[0] == ''
- ends_with_ellipsis = chunks[-1] == ''
-
- resultsummary = ''.join(
- [mnemonic_for_result(result) for result in results]
- )
- if re.match('^N+$', resultsummary):
- # If all results are None...
- output = actual
- elif re.match('^S+$', resultsummary):
- # If no results are None...
- output = '...'.join([result.text for result in results])
- if starts_with_ellipsis:
- output = '...' + output
- if ends_with_ellipsis:
- output = output + '...'
- elif re.match('^S+N+$', resultsummary) and ends_with_ellipsis:
- # Where there are one or more None values at the end of results,
- # and ends_with_ellipsis, we can end without an ellipsis while
- # including the remainder of 'actual' from the end of the last
- # matched chunk.
-
- # Find last non-None result.
- for result in reversed(results):
- if result is not None:
- break
- # Get the remainder value from it.
- if starts_with_ellipsis:
- output = '...'
- else:
- # XXX: ddaa 2005-03-25: Test this code path!
- output = ''
- last_result = None
- for result in results:
- if result is not None:
- output += result.text
- last_result = result
- else:
- output += actual[last_result.remainderpos:]
- break
-
- else:
- # XXX: ddaa 2005-03-25: Test this code path!
- output = actual
-
- return output
-
-class ResultChunk:
-
- def __init__(self, text, remainderpos):
- self.text = text
- self.remainderpos = remainderpos
-
-
-def reversed(seq):
- L = list(seq)
- L.reverse()
- return L
-
-def mnemonic_for_result(result):
- """Returns 'N' if result is None, otherwise 'S'."""
- if result is None:
- return 'N'
- else:
- return 'S'
-
-def find_chunk(chunk, actual, anchor_start=False, anchor_end=False,
- debug=False, show=False, normalize_whitespace=False):
- if debug:
- import pdb; pdb.set_trace()
- if not anchor_start:
- # Find the start of the chunk.
- beginning = ''
- beginning_for_regex = ''
- manyfound = False
- for char in chunk:
- if normalize_whitespace and char.isspace():
- if beginning_for_regex[-2:] != r'\s':
- beginning_for_regex += r'\s'
- else:
- beginning_for_regex += re.escape(char)
- beginning += char
- numfound = len(re.findall(beginning_for_regex, actual))
- #numfound = actual.count(beginning)
- if numfound == 0:
- if manyfound:
- beginning = manyfound_beginning
- beginning_for_regex = manyfound_beginning_for_regex
- if anchor_end:
- beginning_pos = list(re.finditer(
- beginning_for_regex, actual))[-1].start()
- #beginning_pos = actual.rfind(beginning)
- else:
- beginning_pos = re.search(
- beginning_for_regex, actual).start()
- # XXX ddaa 2005-03-25: This should be .span()[1].
- # Needs a test.
- #beginning_pos = actual.find(beginning)
- break
- else:
- beginning = ''
- beginning_for_regex = ''
- beginning_pos = 0
- break
- elif numfound == 1:
- beginning_pos = re.search(
- beginning_for_regex, actual).span()[1]
- #beginning_pos = actual.find(beginning) + len(beginning)
- break
- else:
- manyfound = True
- manyfound_beginning = beginning
- manyfound_beginning_for_regex = beginning_for_regex
- else:
- if manyfound:
- if anchor_end:
- beginning_pos = list(re.finditer(
- beginning_for_regex, actual))[-1].start()
- #beginning_pos = actual.rfind(beginning)
- else:
- beginning_pos = re.search(
- beginning_for_regex, actual).start()
- # XXX ddaa 2005-03-25: This should be .span()[1].
- # Needs a test.
- #beginning_pos = actual.find(beginning)
- else:
- return None
- else:
- beginning_pos = 0
- beginning = ''
- beginning_for_regex = ''
-
- # Find the end of the chunk.
- end = ''
- end_for_regex = ''
- chunk_with_no_beginning = chunk[len(beginning):]
- if not chunk_with_no_beginning:
- end_pos = beginning_pos
- elif not anchor_end:
- # Remove the beginning from the chunk.
- reversed_chunk = list(chunk_with_no_beginning)
- reversed_chunk.reverse()
- manyfound = False
- for char in reversed_chunk:
- end = char + end
- if normalize_whitespace and char.isspace():
- if end_for_regex[:2] != r'\s':
- end_for_regex = r'\s' + end_for_regex
- else:
- end_for_regex = re.escape(char) + end_for_regex
- numfound = len(re.findall(end_for_regex, actual))
- #numfound = actual.count(end)
- if numfound == 0:
- # None found this time around. If we previously found more
- # than one match, then choose the closest to the beginning.
- if manyfound:
- end = manyfound_end
- end_for_regex = manyfound_end_for_regex
- end_pos = re.search(end_for_regex, actual).start()
- #end_pos = actual.find(end, beginning_pos)
- # XXX: ddaa 2005-03-25:
- # This was wrong -- shouldn't be beginning_pos as
- # we've already chopped off the beginning!
- # Or is it? We chopped the beginning of the chunk,
- # not the actual stuff. So, using beginning_pos
- # still holds. Need to chop that off and add on
- # its length.
- break
- else:
- return None
- elif numfound == 1:
- end_pos = re.search(end_for_regex, actual).start()
- #end_pos = actual.rfind(end)
- # XXX: ddaa 2005-03-25: Only one found, so why not use find()?
- break
- else:
- manyfound = True
- manyfound_end = end
- manyfound_end_for_regex = end_for_regex
- else:
- if manyfound:
- end_pos = re.search(end_for_regex, actual).start()
- else:
- return None
- else:
- end_pos = len(actual)
- end = ''
- end_for_regex = ''
-
- chunk_equivalent = actual[beginning_pos:end_pos]
- if show:
- output = '[%s]%s[%s]' % (beginning, chunk_equivalent, end)
- else:
- output = '%s%s%s' % (beginning, chunk_equivalent, end)
- # XXX: ddaa 2005-03-25: end_pos+1 is the end of chunk_equivalent, not end.
- return (output, end_pos+1)
=== modified file 'lib/canonical/config/schema-lazr.conf'
--- lib/canonical/config/schema-lazr.conf 2011-02-03 03:49:36 +0000
+++ lib/canonical/config/schema-lazr.conf 2011-02-17 17:10:46 +0000
@@ -193,9 +193,6 @@
[canonical]
# datatype: boolean
-chunkydiff: True
-
-# datatype: boolean
show_tracebacks: False
# datatype: string
=== modified file 'lib/canonical/launchpad/database/message.py'
--- lib/canonical/launchpad/database/message.py 2011-02-10 01:18:39 +0000
+++ lib/canonical/launchpad/database/message.py 2011-02-17 17:10:46 +0000
@@ -61,7 +61,6 @@
from canonical.database.datetimecol import UtcDateTimeCol
from canonical.database.enumcol import EnumCol
from canonical.database.sqlbase import SQLBase
-from canonical.encoding import guess as ensure_unicode
from canonical.launchpad.helpers import get_filename_from_message_id
from canonical.launchpad.interfaces.librarian import (
ILibraryFileAliasSet,
@@ -83,6 +82,7 @@
PersonCreationRationale,
validate_public_person,
)
+from lp.services.encoding import guess as ensure_unicode
from lp.services.job.model.job import Job
from lp.services.propertycache import cachedproperty
=== removed file 'lib/canonical/launchpad/doc/autodecorate.txt'
--- lib/canonical/launchpad/doc/autodecorate.txt 2009-03-27 03:29:31 +0000
+++ lib/canonical/launchpad/doc/autodecorate.txt 1970-01-01 00:00:00 +0000
@@ -1,39 +0,0 @@
-
-= AutoDecorate =
-
-AutoDecorate is a metaclass factory that can be used to make a class
-implicitely wrap all its methods with one or more decorators.
-
- >>> def decorator_1(func):
- ... def decorated_1(*args, **kw):
- ... print 'Decorated 1'
- ... return func(*args, **kw)
- ... return decorated_1
-
- >>> def decorator_2(func):
- ... def decorated_2(*args, **kw):
- ... print 'Decorated 2'
- ... return func(*args, **kw)
- ... return decorated_2
-
- >>> from canonical.autodecorate import AutoDecorate
-
- >>> class MyClass(object):
- ... __metaclass__ = AutoDecorate(decorator_1, decorator_2)
- ... def method_a(self):
- ... print 'Method A'
- ... def method_b(self):
- ... print 'Method B'
-
- >>> obj = MyClass()
-
- >>> obj.method_a()
- Decorated 2
- Decorated 1
- Method A
-
- >>> obj.method_b()
- Decorated 2
- Decorated 1
- Method B
-
=== modified file 'lib/canonical/launchpad/doc/old-testing.txt'
--- lib/canonical/launchpad/doc/old-testing.txt 2010-12-24 09:28:21 +0000
+++ lib/canonical/launchpad/doc/old-testing.txt 2011-02-17 17:10:46 +0000
@@ -18,12 +18,6 @@
zope, we should not be testing it with the full Z3 functional test
harness).
-canonical.functional.FunctionalTestCase
----------------------------------------
-
-This is a customised zope3 FunctionalTestCase and should be used when you
-simply need the zope3 utilities etc available.
-
PgTestSetup
-----------
=== modified file 'lib/canonical/launchpad/doc/private-xmlrpc.txt'
--- lib/canonical/launchpad/doc/private-xmlrpc.txt 2010-10-09 16:36:22 +0000
+++ lib/canonical/launchpad/doc/private-xmlrpc.txt 2011-02-17 17:10:46 +0000
@@ -15,7 +15,7 @@
external XML-RPC port.
>>> import xmlrpclib
- >>> from canonical.functional import XMLRPCTestTransport
+ >>> from lp.testing.xmlrpc import XMLRPCTestTransport
>>> external_api = xmlrpclib.ServerProxy(
... public_root + 'mailinglists/',
... transport=XMLRPCTestTransport())
=== modified file 'lib/canonical/launchpad/doc/profiling.txt'
--- lib/canonical/launchpad/doc/profiling.txt 2010-10-22 10:24:18 +0000
+++ lib/canonical/launchpad/doc/profiling.txt 2011-02-17 17:10:46 +0000
@@ -166,8 +166,8 @@
useful to try to figure out what requests are causing the memory usage of the
server to increase.
-This is not blessed for production use at this time: the implementation
-relies on lib/canonical/mem.py, which as of this writing warns in its
+This is not blessed for production use at this time: the implementation relies
+on lib/lp/services/profile/mem.py, which as of this writing warns in its
docstring that "[n]one of this should be in day-to-day use." We should
document the source of these concerns and evaluate them before using it in
production. Staging may be more acceptable.
=== modified file 'lib/canonical/launchpad/doc/xmlrpc-authserver.txt'
--- lib/canonical/launchpad/doc/xmlrpc-authserver.txt 2010-10-19 18:44:31 +0000
+++ lib/canonical/launchpad/doc/xmlrpc-authserver.txt 2011-02-17 17:10:46 +0000
@@ -28,7 +28,7 @@
about users.
>>> import xmlrpclib
- >>> from canonical.functional import XMLRPCTestTransport
+ >>> from lp.testing.xmlrpc import XMLRPCTestTransport
>>> authserver= xmlrpclib.ServerProxy(
... 'http://xmlrpc-private.launchpad.dev:8087/authserver',
... transport=XMLRPCTestTransport())
=== modified file 'lib/canonical/launchpad/doc/xmlrpc-selftest.txt'
--- lib/canonical/launchpad/doc/xmlrpc-selftest.txt 2010-11-08 14:16:17 +0000
+++ lib/canonical/launchpad/doc/xmlrpc-selftest.txt 2011-02-17 17:10:46 +0000
@@ -16,7 +16,7 @@
which talks with the publisher directly.
>>> import xmlrpclib
- >>> from canonical.functional import XMLRPCTestTransport
+ >>> from lp.testing.xmlrpc import XMLRPCTestTransport
>>> selftest = xmlrpclib.ServerProxy(
... 'http://xmlrpc.launchpad.dev/', transport=XMLRPCTestTransport())
>>> selftest.concatenate('foo', 'bar')
=== modified file 'lib/canonical/launchpad/ftests/test_system_documentation.py'
--- lib/canonical/launchpad/ftests/test_system_documentation.py 2010-12-13 15:25:03 +0000
+++ lib/canonical/launchpad/ftests/test_system_documentation.py 2011-02-17 17:10:46 +0000
@@ -194,10 +194,6 @@
'old-testing.txt': LayeredDocFileSuite(
'../doc/old-testing.txt', layer=FunctionalLayer),
- 'autodecorate.txt':
- LayeredDocFileSuite('../doc/autodecorate.txt', layer=BaseLayer),
-
-
# And this test want minimal environment too.
'package-relationship.txt': LayeredDocFileSuite(
'../doc/package-relationship.txt',
=== modified file 'lib/canonical/launchpad/helpers.py'
--- lib/canonical/launchpad/helpers.py 2010-12-02 16:13:51 +0000
+++ lib/canonical/launchpad/helpers.py 2011-02-17 17:10:46 +0000
@@ -20,16 +20,15 @@
import tarfile
import warnings
-import gettextpo
from zope.component import getUtility
from zope.security.interfaces import ForbiddenAttribute
-import canonical
from canonical.launchpad.webapp.interfaces import ILaunchBag
from lp.services.geoip.interfaces import (
IRequestLocalLanguages,
IRequestPreferredLanguages,
)
+from lp.services.utils import compress_hash
def text_replaced(text, replacements, _cache={}):
@@ -442,9 +441,7 @@
It generates a file name that's not easily guessable.
"""
- return '%s.msg' % (
- canonical.base.base(
- long(hashlib.sha1(message_id).hexdigest(), 16), 62))
+ return '%s.msg' % compress_hash(hashlib.sha1(message_id))
def intOrZero(value):
@@ -499,7 +496,7 @@
The templates are located in 'lib/canonical/launchpad/emailtemplates'.
"""
if app is None:
- base = os.path.dirname(canonical.launchpad.__file__)
+ base = os.path.dirname(__file__)
fullpath = os.path.join(base, 'emailtemplates', filename)
else:
import lp
=== modified file 'lib/canonical/launchpad/pagetests/standalone/xx-opstats.txt'
--- lib/canonical/launchpad/pagetests/standalone/xx-opstats.txt 2010-10-18 22:24:59 +0000
+++ lib/canonical/launchpad/pagetests/standalone/xx-opstats.txt 2011-02-17 17:10:46 +0000
@@ -4,7 +4,7 @@
We can access them via XML-RPC:
>>> import xmlrpclib
- >>> from canonical.functional import XMLRPCTestTransport
+ >>> from lp.testing.xmlrpc import XMLRPCTestTransport
>>> lp_xmlrpc = xmlrpclib.ServerProxy(
... 'http://xmlrpc.launchpad.dev/+opstats',
... transport=XMLRPCTestTransport()
=== modified file 'lib/canonical/launchpad/scripts/debsync.py'
--- lib/canonical/launchpad/scripts/debsync.py 2010-08-20 20:31:18 +0000
+++ lib/canonical/launchpad/scripts/debsync.py 2011-02-17 17:10:46 +0000
@@ -17,7 +17,6 @@
from zope.component import getUtility
from canonical.database.sqlbase import flush_database_updates
-from canonical.encoding import guess as ensure_unicode
from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
from canonical.launchpad.interfaces.message import (
IMessageSet,
@@ -31,6 +30,7 @@
from lp.bugs.interfaces.bugwatch import IBugWatchSet
from lp.bugs.interfaces.cve import ICveSet
from lp.bugs.scripts import debbugs
+from lp.services.encoding import guess as ensure_unicode
def bug_filter(bug, previous_import_set, target_bugs, target_package_set,
=== modified file 'lib/canonical/launchpad/scripts/logger.py'
--- lib/canonical/launchpad/scripts/logger.py 2011-02-08 15:19:24 +0000
+++ lib/canonical/launchpad/scripts/logger.py 2011-02-17 17:10:46 +0000
@@ -50,7 +50,6 @@
from zope.component import getUtility
from zope.exceptions.log import Formatter
-from canonical.base import base
from canonical.config import config
from canonical.launchpad.webapp.errorlog import (
globalErrorUtility,
@@ -61,6 +60,7 @@
UploadFailed,
)
from lp.services.log import loglevels
+from lp.services.utils import compress_hash
# Reexport our custom loglevels for old callsites. These callsites
# should be importing the symbols from lp.services.log.loglevels
@@ -149,8 +149,7 @@
expiry = datetime.now().replace(tzinfo=utc) + timedelta(days=90)
try:
- filename = base(long(
- hashlib.sha1(traceback).hexdigest(), 16), 62) + '.txt'
+ filename = compress_hash(hashlib.sha1(traceback)) + '.txt'
url = librarian.remoteAddFile(
filename, len(traceback), StringIO(traceback),
'text/plain;charset=%s' % sys.getdefaultencoding(),
=== modified file 'lib/canonical/launchpad/testing/pages.py'
--- lib/canonical/launchpad/testing/pages.py 2011-02-04 14:41:18 +0000
+++ lib/canonical/launchpad/testing/pages.py 2011-02-17 17:10:46 +0000
@@ -52,7 +52,6 @@
)
from canonical.launchpad.testing.systemdocs import (
LayeredDocFileSuite,
- SpecialOutputChecker,
stop,
strip_prefix,
)
@@ -918,7 +917,7 @@
unnumberedfilenames = sorted(unnumberedfilenames)
suite = unittest.TestSuite()
- checker = SpecialOutputChecker()
+ checker = doctest.OutputChecker()
# Add unnumbered tests to the suite individually.
if unnumberedfilenames:
suite.addTest(LayeredDocFileSuite(
=== modified file 'lib/canonical/launchpad/testing/systemdocs.py'
--- lib/canonical/launchpad/testing/systemdocs.py 2010-11-08 12:52:43 +0000
+++ lib/canonical/launchpad/testing/systemdocs.py 2011-02-17 17:10:46 +0000
@@ -7,7 +7,6 @@
__all__ = [
'default_optionflags',
'LayeredDocFileSuite',
- 'SpecialOutputChecker',
'setUp',
'setGlobs',
'stop',
@@ -26,7 +25,6 @@
from zope.component import getUtility
from zope.testing.loggingsupport import Handler
-from canonical.chunkydiff import elided_source
from canonical.config import config
from canonical.database.sqlbase import flush_database_updates
from canonical.launchpad.interfaces.launchpad import ILaunchBag
@@ -141,27 +139,6 @@
return suite
-class SpecialOutputChecker(doctest.OutputChecker):
- """An OutputChecker that runs the 'chunkydiff' checker if appropriate."""
- def output_difference(self, example, got, optionflags):
- if config.canonical.chunkydiff is False:
- return doctest.OutputChecker.output_difference(
- self, example, got, optionflags)
-
- if optionflags & doctest.ELLIPSIS:
- normalize_whitespace = optionflags & doctest.NORMALIZE_WHITESPACE
- newgot = elided_source(example.want, got,
- normalize_whitespace=normalize_whitespace)
- if newgot == example.want:
- # There was no difference. May be an error in
- # elided_source(). In any case, return the whole thing.
- newgot = got
- else:
- newgot = got
- return doctest.OutputChecker.output_difference(
- self, example, newgot, optionflags)
-
-
def ordered_dict_as_string(dict):
"""Return the contents of a dict as an ordered string.
=== removed file 'lib/canonical/launchpad/tests/test_chunkydiff_setting.py'
--- lib/canonical/launchpad/tests/test_chunkydiff_setting.py 2010-08-20 20:31:18 +0000
+++ lib/canonical/launchpad/tests/test_chunkydiff_setting.py 1970-01-01 00:00:00 +0000
@@ -1,26 +0,0 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Fail if the chunkydiff option is off.
-
-This ensures that people can't accidently commit the main config file with
-this option turned off to rocketfuel.
-"""
-__metaclass__ = type
-
-import unittest
-
-from canonical.config import config
-
-
-class TestChunkydiffSetting(unittest.TestCase):
-
- def test(self):
- self.failUnless(
- config.canonical.chunkydiff is False,
- 'This test is failing to ensure that the chunkydiff '
- 'config setting cannot be committed in "on" mode.'
- )
-
-def test_suite():
- return unittest.TestLoader().loadTestsFromName(__name__)
=== modified file 'lib/canonical/librarian/tests/test_upload.py'
--- lib/canonical/librarian/tests/test_upload.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/librarian/tests/test_upload.py 2011-02-17 17:10:46 +0000
@@ -54,9 +54,6 @@
"""Librarian upload server test helper, process a request and report what
happens.
- Inspired by the canonical.functional.http function used by the Launchpad
- page tests.
-
Hands a request to a librarian file upload protocol, and prints the reply
from the server, a summary of the file uploaded, and whether the connection
closed, e.g.::
=== removed file 'lib/canonical/tests/chunkydiff.txt'
--- lib/canonical/tests/chunkydiff.txt 2005-10-31 18:29:12 +0000
+++ lib/canonical/tests/chunkydiff.txt 1970-01-01 00:00:00 +0000
@@ -1,202 +0,0 @@
-============
-Chunky diffs
-============
-
- run this using
-
- python test.py -u canonical.tests.test_chunkydiff
-
-Consider this desired output
-
- nnnnnnABCxyzDEFnnnnnn
-
-and this actual output
-
- nnnnnnABClmnopDEFnnnnnn
-
-the test says this
-
- ...ABCxyzDEF...
-
-useful output would be
-
- ...ABClmnopDEF...
- ^^^^^
-
-and not
-
- nnnnnnABClmnopDEFnnnnnn
- ^^^^^
-
-How do we do this?
-
-If the comparison has failed, we take the first character after the
-ellipsis in the test, and look for that in the actual output. If it occurs
-only once, then fine. If it does not occur at all, then print out the whole
-diff. If it occurs more than once, take the next character from the test,
-and look for those two characters in the actual output. Repeat until
-there is no occurence, or there is just one occurence.
-
-The same goes for the characters before the trailing ellipsis.
-
-The search can be narrowed because the characters at the end must follow
-those at the start.
-
- >>> from canonical.chunkydiff import elided_source
-
- >>> actual = "nnnnnnABClmnopDEFnnnnnn"
- >>> tested = "...ABCxyzDEF..."
- >>> elided_source(tested, actual)
- '...ABClmnopDEF...'
-
-Trivial modification, so that the code needs to take the input into account
-and not just parrot out a hard-coded return value.
-
- >>> actual = "nnnnnnABClmnopzDEFnnnnnn"
- >>> tested = "...ABCxyzDEF..."
- >>> elided_source(tested, actual)
- '...ABClmnopzDEF...'
-
-No interesting output between the arbitrary markers. This is really no
-different from the above, as far as the system is concerned.
-
- >>> actual = "nnnnnnABCDEFnnnnnn"
- >>> tested = "...ABCxyzDEF..."
- >>> elided_source(tested, actual)
- '...ABCDEF...'
-
-If there are two chunks that differ by the second or third characters in,
-choose the one that matches best.
-
- >>> actual = "nnnnnnABClmnopzDEFnnnnnABXuuuuXEFnnnn"
- >>> tested = "...ABCxyzDEF..."
- >>> elided_source(tested, actual)
- '...ABClmnopzDEF...'
-
-What happens when there is no ellipsis at the start?
-
- >>> actual = "ABClmnopzDEFnnnn"
- >>> tested = "ABCxyzDEF..."
- >>> elided_source(tested, actual)
- 'ABClmnopzDEF...'
-
-What happens when there is no ellipsis at the end, but extra data at the end?
-
- >>> actual = "ABClmnopzDEFnnnn"
- >>> tested = "...ABCxyzDEF"
- >>> elided_source(tested, actual)
- '...ABClmnopzDEFnnnn'
-
-What happens when there is no ellipsis at the end?
-
- >>> actual = "nnnnABClmnopzDEF"
- >>> tested = "...ABCxyzDEF"
- >>> elided_source(tested, actual)
- '...ABClmnopzDEF'
-
-What happens when there is no ellipsis at all?
-
- >>> actual = "ABClmnopzDEF"
- >>> tested = "ABCxyzDEF"
- >>> elided_source(tested, actual)
- 'ABClmnopzDEF'
-
-What happens when there is more than one chunk?
-
- >>> actual = "nnnnnnABClmnopzDEFnnnnnGHIzponmJKLnnnn"
- >>> tested = "...ABCxyzDEF...GHIxyzJKL..."
- >>> elided_source(tested, actual)
- '...ABClmnopzDEF...GHIzponmJKL...'
-
-What about when the chunks are presented in the wrong order?
-
-The first chunk from "tested" will have been found, but the second chunk
-will be absent. We want to keep the "nnnn" at the end of 'actual' because
-it is not matched by anything. We want to elide the start as it matches the
-elision, but not the end, as there is unmatched stuff in tested that we may
-want to compare.
-
-We may want to choose from among the following possible output in this case:
-
-'...GHIzponmJKLnnnn'
-'...GHIzponmJKL...'
-'...ABCmnopzDEF...GHIzponmJKL...'
-
-We'll use the first case for now, and see how it works in practice.
-
-Implementing this involves recognising how much of the actual string has
-been consumed by matching each chunk, and using only that remainder for the
-next chunks.
-
- >>> actual = "nnnnnnABClmnopzDEFnnnnnGHIzponmJKLnnnn"
- >>> tested = "...GHIzponmJKL...ABClmnopzDEF..."
- >>> elided_source(tested, actual)
- '...GHIzponmJKLnnnn'
-
-Where there is more than one option for the end match, choose the closest
-one.
-
- >>> actual = "nnnnnnABClmnopzDEFnnnnnGHIzponmDEFnnnn"
- >>> tested = "...ABCxxxxDEF..."
- >>> elided_source(tested, actual)
- '...ABClmnopzDEF...'
-
-Check anchoring to the start with elided junk after the first matched chunk.
-
- >>> actual = "ABClmnopznnnDEFzponmGHInnnn"
- >>> tested = "ABC...DEFxxxGHI..."
- >>> elided_source(tested, actual)
- 'ABC...DEFzponmGHI...'
-
-Check anchoring to the end with elided junk immediately before.
-
- >>> actual = "ABCDEzxcvbX"
- >>> tested = "ABCDE...X"
- >>> elided_source(tested, actual)
- 'ABCDE...X'
-
-Test single character within ellipses.
-
- >>> actual = "abcdeXfghij"
- >>> tested = "...X..."
- >>> elided_source(tested, actual)
- '...X...'
-
-Multiple single characters.
-
- >>> actual = "ABCDEnnXnnXnnX"
- >>> tested = "ABCDE...X"
- >>> elided_source(tested, actual)
- 'ABCDE...X'
-
-
- >>> actual = "ABCDEnnXnn"
- >>> tested = "ABCDE...X..."
- >>> elided_source(tested, actual)
- 'ABCDE...X...'
-
-Test with differences in whitespace.
-
- >>> actual = "ABC\nxxx DEF"
- >>> tested = "ABC ... DEF"
- >>> elided_source(tested, actual)#xx
- 'ABC\nxxx ...DEF'
-
- >>> actual = "ABC xxx DEF"
- >>> tested = "ABC\n... DEF"
- >>> elided_source(tested, actual, normalize_whitespace=True)
- 'ABC\n... DEF'
-
-Test with multiple whitespace characters.
-
- >>> actual = "ABC xxx DEF"
- >>> tested = "ABC\n\n... DEF"
- >>> elided_source(tested, actual, normalize_whitespace=True)
- 'ABC\n\n... DEF'
-
-Test brad's case:
-
- >>> actual = '\'Bug #7: "firefox crashes all the time" added\'\n'
- >>> tested = "'...task added'\n"
- >>> elided_source(tested, actual)
- '\'...ttime" added\'\n'
=== removed file 'lib/canonical/tests/test_base.py'
--- lib/canonical/tests/test_base.py 2010-10-12 01:11:41 +0000
+++ lib/canonical/tests/test_base.py 1970-01-01 00:00:00 +0000
@@ -1,9 +0,0 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-from doctest import DocTestSuite
-import canonical.base
-
-def test_suite():
- suite = DocTestSuite(canonical.base)
- return suite
=== removed file 'lib/canonical/tests/test_chunkydiff.py'
--- lib/canonical/tests/test_chunkydiff.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/tests/test_chunkydiff.py 1970-01-01 00:00:00 +0000
@@ -1,9 +0,0 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-from canonical.launchpad.testing.systemdocs import LayeredDocFileSuite
-
-
-def test_suite():
- return LayeredDocFileSuite('chunkydiff.txt', stdout_logging=False)
-
=== modified file 'lib/lp/answers/doc/person.txt'
--- lib/lp/answers/doc/person.txt 2010-10-18 22:24:59 +0000
+++ lib/lp/answers/doc/person.txt 2011-02-17 17:10:46 +0000
@@ -169,7 +169,7 @@
But Carlos has one.
# Because not everyone uses a real editor <wink>
- >>> from canonical.encoding import ascii_smash
+ >>> from lp.services.encoding import ascii_smash
>>> carlos_raw = personset.getByName('carlos')
>>> carlos = IQuestionsPerson(carlos_raw)
>>> for question in carlos.searchQuestions(
=== modified file 'lib/lp/answers/doc/questionsets.txt'
--- lib/lp/answers/doc/questionsets.txt 2010-11-15 21:56:43 +0000
+++ lib/lp/answers/doc/questionsets.txt 2011-02-17 17:10:46 +0000
@@ -48,7 +48,7 @@
regular full text algorithm.
# Because not everyone uses a real editor <wink>
- >>> from canonical.encoding import ascii_smash
+ >>> from lp.services.encoding import ascii_smash
>>> for question in question_set.searchQuestions(search_text='firefox'):
... print ascii_smash(question.title), question.target.displayname
Problemas de Impressao no Firefox Mozilla Firefox
=== modified file 'lib/lp/app/stories/launchpad-root/site-search.txt'
--- lib/lp/app/stories/launchpad-root/site-search.txt 2010-09-27 19:39:21 +0000
+++ lib/lp/app/stories/launchpad-root/site-search.txt 2011-02-17 17:10:46 +0000
@@ -5,7 +5,7 @@
specific search with Launchpad's prominent objects (projects, bugs,
teams, etc.).
- >>> from canonical.encoding import ascii_smash
+ >>> from lp.services.encoding import ascii_smash
# Our very helpful function for printing all the page results.
=== modified file 'lib/lp/archivepublisher/utils.py'
--- lib/lp/archivepublisher/utils.py 2011-02-04 09:07:36 +0000
+++ lib/lp/archivepublisher/utils.py 2011-02-17 17:10:46 +0000
@@ -31,7 +31,7 @@
IStoreSelector,
MAIN_STORE,
)
-from canonical.mem import resident
+from lp.services.profile.mem import resident
from lp.soyuz.enums import ArchivePurpose
from lp.soyuz.interfaces.archive import (
default_name_by_purpose,
=== modified file 'lib/lp/archiveuploader/dscfile.py'
--- lib/lp/archiveuploader/dscfile.py 2010-12-02 16:15:46 +0000
+++ lib/lp/archiveuploader/dscfile.py 2011-02-17 17:10:46 +0000
@@ -28,7 +28,7 @@
from debian.deb822 import Deb822Dict
from zope.component import getUtility
-from canonical.encoding import guess as guess_encoding
+from lp.services.encoding import guess as guess_encoding
from canonical.launchpad.interfaces.gpghandler import (
GPGVerificationError,
IGPGHandler,
=== modified file 'lib/lp/archiveuploader/nascentuploadfile.py'
--- lib/lp/archiveuploader/nascentuploadfile.py 2010-10-19 09:00:29 +0000
+++ lib/lp/archiveuploader/nascentuploadfile.py 2011-02-17 17:10:46 +0000
@@ -30,7 +30,6 @@
from zope.component import getUtility
-from canonical.encoding import guess as guess_encoding
from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
from canonical.librarian.utils import filechunks
from lp.app.errors import NotFoundError
@@ -47,10 +46,10 @@
re_valid_version,
)
from lp.buildmaster.enums import BuildStatus
+from lp.services.encoding import guess as guess_encoding
from lp.soyuz.enums import (
BinaryPackageFormat,
PackagePublishingPriority,
- PackagePublishingStatus,
PackageUploadCustomFormat,
)
from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
=== modified file 'lib/lp/archiveuploader/utils.py'
--- lib/lp/archiveuploader/utils.py 2010-12-09 16:20:20 +0000
+++ lib/lp/archiveuploader/utils.py 2011-02-17 17:10:46 +0000
@@ -32,7 +32,7 @@
import signal
import subprocess
-from canonical.encoding import (
+from lp.services.encoding import (
ascii_smash,
guess as guess_encoding,
)
=== modified file 'lib/lp/bugs/doc/bugtracker-tokens.txt'
--- lib/lp/bugs/doc/bugtracker-tokens.txt 2010-10-09 16:36:22 +0000
+++ lib/lp/bugs/doc/bugtracker-tokens.txt 2011-02-17 17:10:46 +0000
@@ -4,7 +4,7 @@
>>> import xmlrpclib
>>> from zope.component import getUtility
- >>> from canonical.functional import XMLRPCTestTransport
+ >>> from lp.testing.xmlrpc import XMLRPCTestTransport
>>> from canonical.launchpad.interfaces.logintoken import ILoginTokenSet
>>> bugtracker_api = xmlrpclib.ServerProxy(
... 'http://xmlrpc-private.launchpad.dev:8087/bugs',
=== modified file 'lib/lp/bugs/doc/malone-xmlrpc.txt'
--- lib/lp/bugs/doc/malone-xmlrpc.txt 2010-10-19 18:44:31 +0000
+++ lib/lp/bugs/doc/malone-xmlrpc.txt 2011-02-17 17:10:46 +0000
@@ -3,7 +3,7 @@
Malone provides an XML-RPC interface for filing bugs.
>>> import xmlrpclib
- >>> from canonical.functional import XMLRPCTestTransport
+ >>> from lp.testing.xmlrpc import XMLRPCTestTransport
>>> filebug_api = xmlrpclib.ServerProxy(
... 'http://test@xxxxxxxxxxxxx:test@xxxxxxxxxxxxxxxxxxxx/bugs/',
... transport=XMLRPCTestTransport())
=== modified file 'lib/lp/bugs/stories/bugtracker/xx-bugtracker-handshake-tokens.txt'
--- lib/lp/bugs/stories/bugtracker/xx-bugtracker-handshake-tokens.txt 2009-10-22 13:02:12 +0000
+++ lib/lp/bugs/stories/bugtracker/xx-bugtracker-handshake-tokens.txt 2011-02-17 17:10:46 +0000
@@ -5,7 +5,7 @@
done using the internal XML-RPC service.
>>> import xmlrpclib
- >>> from canonical.functional import XMLRPCTestTransport
+ >>> from lp.testing.xmlrpc import XMLRPCTestTransport
>>> bugtracker_api = xmlrpclib.ServerProxy(
... 'http://xmlrpc-private.launchpad.dev:8087/bugs',
... transport=XMLRPCTestTransport())
=== modified file 'lib/lp/code/doc/branch-xmlrpc.txt'
--- lib/lp/code/doc/branch-xmlrpc.txt 2010-10-03 15:30:06 +0000
+++ lib/lp/code/doc/branch-xmlrpc.txt 2011-02-17 17:10:46 +0000
@@ -5,7 +5,7 @@
>>> from datetime import datetime
>>> import pytz
>>> import xmlrpclib
- >>> from canonical.functional import XMLRPCTestTransport
+ >>> from lp.testing.xmlrpc import XMLRPCTestTransport
>>> branchset_api = xmlrpclib.ServerProxy(
... 'http://foo.bar@xxxxxxxxxxxxx:test@xxxxxxxxxxxxxxxxxxxx/bazaar/',
... transport=XMLRPCTestTransport())
=== modified file 'lib/lp/code/doc/xmlrpc-codeimport-scheduler.txt'
--- lib/lp/code/doc/xmlrpc-codeimport-scheduler.txt 2010-10-03 15:30:06 +0000
+++ lib/lp/code/doc/xmlrpc-codeimport-scheduler.txt 2011-02-17 17:10:46 +0000
@@ -44,7 +44,7 @@
The point of all this is for it to be accessed over XMLRPC.
>>> import xmlrpclib
- >>> from canonical.functional import XMLRPCTestTransport
+ >>> from lp.testing.xmlrpc import XMLRPCTestTransport
>>> codeimportscheduler = xmlrpclib.ServerProxy(
... 'http://xmlrpc-private.launchpad.dev:8087/codeimportscheduler',
... transport=XMLRPCTestTransport())
=== modified file 'lib/lp/registry/tests/test_doc.py'
--- lib/lp/registry/tests/test_doc.py 2010-10-04 19:50:45 +0000
+++ lib/lp/registry/tests/test_doc.py 2011-02-17 17:10:46 +0000
@@ -51,7 +51,7 @@
# Use a real XMLRPC server proxy so that the same test is run through the
# full security machinery. This is more representative of the real-world,
# but more difficult to debug.
- from canonical.functional import XMLRPCTestTransport
+ from lp.testing.xmlrpc import XMLRPCTestTransport
from xmlrpclib import ServerProxy
mailinglist_api = ServerProxy(
'http://xmlrpc-private.launchpad.dev:8087/mailinglists/',
=== modified file 'lib/lp/registry/tests/test_xmlrpc.py'
--- lib/lp/registry/tests/test_xmlrpc.py 2010-10-20 20:51:26 +0000
+++ lib/lp/registry/tests/test_xmlrpc.py 2011-02-17 17:10:46 +0000
@@ -11,7 +11,6 @@
from zope.component import getUtility
from zope.security.proxy import removeSecurityProxy
-from canonical.functional import XMLRPCTestTransport
from canonical.launchpad.interfaces.account import AccountStatus
from canonical.launchpad.interfaces.launchpad import IPrivateApplication
from canonical.launchpad.webapp.servers import LaunchpadTestRequest
@@ -24,6 +23,7 @@
)
from lp.registry.xmlrpc.softwarecenteragent import SoftwareCenterAgentAPI
from lp.testing import TestCaseWithFactory
+from lp.testing.xmlrpc import XMLRPCTestTransport
class TestSoftwareCenterAgentAPI(TestCaseWithFactory):
=== modified file 'lib/lp/registry/xmlrpc/mailinglist.py'
--- lib/lp/registry/xmlrpc/mailinglist.py 2010-10-26 03:51:12 +0000
+++ lib/lp/registry/xmlrpc/mailinglist.py 2011-02-17 17:10:46 +0000
@@ -16,7 +16,7 @@
from zope.security.proxy import removeSecurityProxy
from canonical.config import config
-from canonical.encoding import escape_nonascii_uniquely
+from lp.services.encoding import escape_nonascii_uniquely
from canonical.launchpad.interfaces.emailaddress import (
EmailAddressStatus,
IEmailAddressSet,
=== renamed file 'lib/canonical/encoding.py' => 'lib/lp/services/encoding.py'
=== modified file 'lib/lp/services/memcache/tales.py'
--- lib/lp/services/memcache/tales.py 2010-12-13 18:04:24 +0000
+++ lib/lp/services/memcache/tales.py 2011-02-17 17:10:46 +0000
@@ -27,11 +27,11 @@
)
from zope.tales.interfaces import ITALESExpression
-from canonical.base import base
from canonical.config import config
from lp.app import versioninfo
from canonical.launchpad.webapp.interfaces import ILaunchBag
from lp.services.memcache.interfaces import IMemcacheClient
+from lp.services.utils import compress_hash
# Request annotation key.
COUNTER_KEY = 'lp.services.memcache.tales.counter'
@@ -238,7 +238,7 @@
# with a hash. A short hash is good, provided it is still unique,
# to preserve readability as much as possible. We include the
# unsanitized URL in the hash to ensure uniqueness.
- key_hash = base(int(md5(key + url).hexdigest(), 16), 62)
+ key_hash = compress_hash(md5(key + url))
key = key[:250-len(key_hash)] + key_hash
return key
=== modified file 'lib/lp/services/profile/__init__.py'
--- lib/lp/services/profile/__init__.py 2010-07-01 01:39:46 +0000
+++ lib/lp/services/profile/__init__.py 2011-02-17 17:10:46 +0000
@@ -1,7 +1,7 @@
-# Copyright 2010 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
-"""lp.services.profile - profiling for zope applications.
+"""Profiling for Python and Zope applications.
Tests for this package are currently services stories.
"""
=== renamed file 'lib/canonical/mem.py' => 'lib/lp/services/profile/mem.py'
=== modified file 'lib/lp/services/profile/profile.py'
--- lib/lp/services/profile/profile.py 2010-10-22 10:24:18 +0000
+++ lib/lp/services/profile/profile.py 2011-02-17 17:10:46 +0000
@@ -26,7 +26,7 @@
from canonical.config import config
import canonical.launchpad.webapp.adapter as da
from canonical.launchpad.webapp.interfaces import IStartRequestEvent
-from canonical.mem import (
+from lp.services.profile.mem import (
memory,
resident,
)
=== renamed file 'lib/canonical/tests/test_encoding.py' => 'lib/lp/services/tests/test_encoding.py'
--- lib/canonical/tests/test_encoding.py 2010-10-12 01:11:41 +0000
+++ lib/lp/services/tests/test_encoding.py 2011-02-17 17:10:46 +0000
@@ -3,8 +3,8 @@
from doctest import DocTestSuite, ELLIPSIS
-import canonical.encoding
+import lp.services.encoding
def test_suite():
- suite = DocTestSuite(canonical.encoding, optionflags=ELLIPSIS)
+ suite = DocTestSuite(lp.services.encoding, optionflags=ELLIPSIS)
return suite
=== modified file 'lib/lp/services/tests/test_utils.py'
--- lib/lp/services/tests/test_utils.py 2011-02-09 10:59:00 +0000
+++ lib/lp/services/tests/test_utils.py 2011-02-17 17:10:46 +0000
@@ -1,15 +1,19 @@
# Copyright 2009 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
-"""Module docstring goes here."""
+"""Tests for lp.services.utils."""
__metaclass__ = type
from contextlib import contextmanager
+import hashlib
import itertools
import unittest
from lp.services.utils import (
+ AutoDecorate,
+ base,
+ compress_hash,
CachingIterator,
decorate_with,
docstring_dedent,
@@ -19,6 +23,82 @@
from lp.testing import TestCase
+
+class TestAutoDecorate(TestCase):
+ """Tests for AutoDecorate."""
+
+ def setUp(self):
+ super(TestAutoDecorate, self).setUp()
+ self.log = None
+
+ def decorator_1(self, f):
+ def decorated(*args, **kwargs):
+ self.log.append(1)
+ return f(*args, **kwargs)
+ return decorated
+
+ def decorator_2(self, f):
+ def decorated(*args, **kwargs):
+ self.log.append(2)
+ return f(*args, **kwargs)
+ return decorated
+
+ def test_auto_decorate(self):
+ # All of the decorators passed to AutoDecorate are applied as
+ # decorators in reverse order.
+
+ class AutoDecoratedClass:
+ __metaclass__ = AutoDecorate(self.decorator_1, self.decorator_2)
+ def method_a(s):
+ self.log.append('a')
+ def method_b(s):
+ self.log.append('b')
+
+ obj = AutoDecoratedClass()
+ self.log = []
+ obj.method_a()
+ self.assertEqual([2, 1, 'a'], self.log)
+ self.log = []
+ obj.method_b()
+ self.assertEqual([2, 1, 'b'], self.log)
+
+
+class TestBase(TestCase):
+
+ def test_simple_base(self):
+ # 35 in base 36 is lowercase 'z'
+ self.assertEqual('z', base(35, 36))
+
+ def test_extended_base(self):
+ # There is no standard representation for numbers in bases above 36
+ # (all the digits, all the letters of the English alphabet). However,
+ # we can represent bases up to 62 by using upper case letters on top
+ # of lower case letters. This is useful as a cheap compression
+ # algorithm.
+ self.assertEqual('A', base(36, 62))
+ self.assertEqual('B', base(37, 62))
+ self.assertEqual('Z', base(61, 62))
+
+ def test_base_matches_builtin_hex(self):
+ # We get identical results to the hex builtin, without the 0x prefix
+ numbers = list(range(5000))
+ using_hex = [hex(i)[2:] for i in numbers]
+ using_base = [base(i, 16) for i in numbers]
+ self.assertEqual(using_hex, using_base)
+
+ def test_compress_md5_hash(self):
+ # compress_hash compresses MD5 hashes down to 22 URL-safe characters.
+ compressed = compress_hash(hashlib.md5('foo'))
+ self.assertEqual('5fX649Stem9fET0lD46zVe', compressed)
+ self.assertEqual(22, len(compressed))
+
+ def test_compress_sha1_hash(self):
+ # compress_hash compresses SHA1 hashes down to 27 URL-safe characters.
+ compressed = compress_hash(hashlib.sha1('foo'))
+ self.assertEqual('1HyPQr2xj1nmnkQXBCJXUdQoy5l', compressed)
+ self.assertEqual(27, len(compressed))
+
+
class TestIterateSplit(TestCase):
"""Tests for iter_split."""
=== modified file 'lib/lp/services/utils.py'
--- lib/lp/services/utils.py 2011-02-08 21:17:56 +0000
+++ lib/lp/services/utils.py 2011-02-17 17:10:46 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Generic Python utilities.
@@ -9,7 +9,10 @@
__metaclass__ = type
__all__ = [
+ 'AutoDecorate',
+ 'base',
'CachingIterator',
+ 'compress_hash',
'decorate_with',
'docstring_dedent',
'iter_split',
@@ -20,14 +23,77 @@
]
from itertools import tee
+import string
import sys
from textwrap import dedent
+from types import FunctionType
from lazr.enum import BaseItem
from twisted.python.util import mergeFunctionMetadata
from zope.security.proxy import isinstance as zope_isinstance
+def AutoDecorate(*decorators):
+ """Factory to generate metaclasses that automatically apply decorators.
+
+ AutoDecorate is a metaclass factory that can be used to make a class
+ implicitly wrap all of its methods with one or more decorators.
+ """
+
+ class AutoDecorateMetaClass(type):
+ def __new__(cls, class_name, bases, class_dict):
+ new_class_dict = {}
+ for name, value in class_dict.items():
+ if type(value) == FunctionType:
+ for decorator in decorators:
+ value = decorator(value)
+ assert callable(value), (
+ "Decorator %s didn't return a callable."
+ % repr(decorator))
+ new_class_dict[name] = value
+ return type.__new__(cls, class_name, bases, new_class_dict)
+
+ return AutoDecorateMetaClass
+
+
+def base(number, radix):
+ """Convert 'number' to an arbitrary base numbering scheme, 'radix'.
+
+ This function is based on work from the Python Cookbook and is under the
+ Python license.
+
+ Inverse function to int(str, radix) and long(str, radix)
+ """
+ if not 2 <= radix <= 62:
+ raise ValueError("radix must be between 2 and 62")
+
+ result = []
+ addon = result.append
+ if number < 0:
+ number = -number
+ addon('-')
+ elif number == 0:
+ addon('0')
+
+ ABC = string.digits + string.ascii_letters
+ while number:
+ number, rdigit = divmod(number, radix)
+ addon(ABC[rdigit])
+
+ result.reverse()
+ return ''.join(result)
+
+
+def compress_hash(hash_obj):
+ """Compress a hash_obj using `base`.
+
+ Given an ``md5`` or ``sha1`` hash object, compress it down to either 22 or
+ 27 characters in a way that's safe to be used in URLs. Takes the hex of
+ the hash and converts it to base 62.
+ """
+ return base(int(hash_obj.hexdigest(), 16), 62)
+
+
def iter_split(string, splitter):
"""Iterate over ways to split 'string' in two with 'splitter'.
=== modified file 'lib/lp/soyuz/model/queue.py'
--- lib/lp/soyuz/model/queue.py 2011-01-27 15:05:34 +0000
+++ lib/lp/soyuz/model/queue.py 2011-02-17 17:10:46 +0000
@@ -40,7 +40,7 @@
SQLBase,
sqlvalues,
)
-from canonical.encoding import (
+from lp.services.encoding import (
ascii_smash,
guess as guess_encoding,
)
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2011-02-16 11:18:48 +0000
+++ lib/lp/testing/factory.py 2011-02-17 17:10:46 +0000
@@ -61,7 +61,6 @@
removeSecurityProxy,
)
-from canonical.autodecorate import AutoDecorate
from canonical.config import config
from canonical.database.constants import (
DEFAULT,
@@ -233,6 +232,7 @@
from lp.services.mail.signedmessage import SignedMessage
from lp.services.openid.model.openididentifier import OpenIdIdentifier
from lp.services.propertycache import clear_property_cache
+from lp.services.utils import AutoDecorate
from lp.services.worlddata.interfaces.country import ICountrySet
from lp.services.worlddata.interfaces.language import ILanguageSet
from lp.soyuz.adapters.packagelocation import PackageLocation
=== renamed file 'lib/canonical/functional.py' => 'lib/lp/testing/xmlrpc.py'
--- lib/canonical/functional.py 2009-06-25 05:30:52 +0000
+++ lib/lp/testing/xmlrpc.py 2011-02-17 17:10:46 +0000
@@ -1,7 +1,11 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
-"""Alter the standard functional testing environment for Launchpad."""
+"""Tools for testing XML-RPC services."""
+
+__all__ = [
+ 'XMLRPCTestTransport',
+ ]
from cStringIO import StringIO
import httplib
=== modified file 'lib/lp_sitecustomize.py'
--- lib/lp_sitecustomize.py 2010-12-24 13:03:02 +0000
+++ lib/lp_sitecustomize.py 2011-02-17 17:10:46 +0000
@@ -92,6 +92,15 @@
"ignore",
category=DeprecationWarning,
module="Crypto")
+ # Filter all deprecation warnings for Zope 3.6, which eminate from
+ # the zope package.
+ filter_pattern = '.*(Zope 3.6|provide.*global site manager).*'
+ warnings.filterwarnings(
+ 'ignore', filter_pattern, category=DeprecationWarning)
+ # XXX wgrant 2010-03-30 bug=551510:
+ # Also filter apt_pkg warnings, since Lucid's python-apt has a new API.
+ warnings.filterwarnings(
+ 'ignore', '.*apt_pkg.*', category=DeprecationWarning)
def customize_logger():
=== modified file 'utilities/migrater/file-ownership.txt'
--- utilities/migrater/file-ownership.txt 2010-11-25 04:42:51 +0000
+++ utilities/migrater/file-ownership.txt 2011-02-17 17:10:46 +0000
@@ -3846,7 +3846,6 @@
./tests/test_branchtarget.py
./tests/test_branchurifield.py
./tests/test_bugnotification.py
- ./tests/test_chunkydiff_setting.py
./tests/test_datetimeutils.py
./tests/test_helpers.py
./tests/test_imports.py