launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #26183
[Merge] ~cjwatson/launchpad:py3-services-exception-modules into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:py3-services-exception-modules into launchpad:master.
Commit message:
lp.services: Use IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/397315
This allows doctests that test tracebacks to work on both Python 2 and 3.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:py3-services-exception-modules into launchpad:master.
diff --git a/lib/lp/services/database/doc/db-policy.txt b/lib/lp/services/database/doc/db-policy.txt
index 5a7b372..aa90ed7 100644
--- a/lib/lp/services/database/doc/db-policy.txt
+++ b/lib/lp/services/database/doc/db-policy.txt
@@ -38,10 +38,10 @@ a single master.
False
>>> ro_janitor.display_name = 'Janice the Janitor'
- >>> transaction.commit()
+ >>> transaction.commit() # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- InternalError: ...
+ storm.database.InternalError: ...
>>> transaction.abort()
@@ -81,9 +81,10 @@ resources.
>>> with SlaveOnlyDatabasePolicy():
... whoops = IMasterStore(Person).find(
... Person, Person.name == 'janitor').one()
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- DisallowedStore: master
+ lp.services.database.interfaces.DisallowedStore: master
We can even ensure no database activity occurs at all, for instance
if we need to guarantee a potentially long running call doesn't access
@@ -94,9 +95,10 @@ database transaction.
>>> with DatabaseBlockedPolicy():
... whoops = IStore(Person).find(
... Person, Person.name == 'janitor').one()
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- DisallowedStore: ('main', 'default')
+ lp.services.database.interfaces.DisallowedStore: ('main', 'default')
Database policies can also be installed and uninstalled using the
IStoreSelector utility for cases where the 'with' syntax cannot
diff --git a/lib/lp/services/database/doc/multitablecopy.txt b/lib/lp/services/database/doc/multitablecopy.txt
index 83684de..fa68318 100644
--- a/lib/lp/services/database/doc/multitablecopy.txt
+++ b/lib/lp/services/database/doc/multitablecopy.txt
@@ -318,10 +318,10 @@ which means that the attempt to insert it will violate a unique constraint.
... % numeric_holding_table)
>>> copier.pour(transaction)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- IntegrityError: duplicate ... violates unique constraint ...
- <BLANKLINE>
+ storm.database.IntegrityError: duplicate ... violates unique constraint ...
Now we have a fun situation! Some data has been copied back into our source
tables, and we don't know how much. And some data remains in our holding
diff --git a/lib/lp/services/database/doc/storm.txt b/lib/lp/services/database/doc/storm.txt
index 93d3a7e..5d83c4e 100644
--- a/lib/lp/services/database/doc/storm.txt
+++ b/lib/lp/services/database/doc/storm.txt
@@ -72,10 +72,10 @@ from a store other than the correct Master.
>>> t = transaction.begin()
>>> person = main_slave.find(Person, name='mark').one()
>>> person.display_name = 'Cannot change'
- >>> transaction.commit()
+ >>> transaction.commit() # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- InternalError: ...
+ storm.database.InternalError: ...
>>> transaction.abort()
>>> t = transaction.begin()
@@ -94,9 +94,10 @@ similarly wrapped.
>>> person.displayname
u'No Privileges Person'
>>> person.name = 'foo'
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Unauthorized: ...
+ zope.security.interfaces.Unauthorized: ...
>>> person = IMasterObject(person)
>>> removeSecurityProxy(person) is person
@@ -104,9 +105,10 @@ similarly wrapped.
>>> person.displayname
u'No Privileges Person'
>>> person.name = 'foo'
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Unauthorized: ...
+ zope.security.interfaces.Unauthorized: ...
>>> person = IMasterObject(removeSecurityProxy(person))
>>> removeSecurityProxy(person) is person
diff --git a/lib/lp/services/feeds/doc/feeds.txt b/lib/lp/services/feeds/doc/feeds.txt
index 8b0b32d..b855388 100644
--- a/lib/lp/services/feeds/doc/feeds.txt
+++ b/lib/lp/services/feeds/doc/feeds.txt
@@ -55,9 +55,10 @@ not be found.
>>> verifyObject(IThing, thing)
True
>>> feed_view = getMultiAdapter((thing, request), name='thing-feed.xml')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- ComponentLookupError: ...
+ zope.interface.interfaces.ComponentLookupError: ...
Set the layer on the request for all subsequent uses.
@@ -69,15 +70,15 @@ found.
>>> thing = object()
>>> verifyObject(IThing, thing)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- MultipleInvalid: ...
- Does not declaratively implement the interface
- The lp.services.feeds.tests.helper.IThing.value attribute was not provided
+ zope.interface.exceptions.MultipleInvalid: ... Does not declaratively implement the interface The lp.services.feeds.tests.helper.IThing.value attribute was not provided
>>> feed_view = getMultiAdapter((thing, request), name='thing-feed.atom')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- ComponentLookupError: ...
+ zope.interface.interfaces.ComponentLookupError: ...
If the name is not one of the supported names the view will not be
found.
@@ -86,9 +87,10 @@ found.
>>> verifyObject(IThing, thing)
True
>>> feed_view = getMultiAdapter((thing, request), name='thing-feed.xml')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- ComponentLookupError: ...
+ zope.interface.interfaces.ComponentLookupError: ...
If the thing is an IThing and the name is supported the view will be
found, indicated by the absence of a ComponentLookupError.
diff --git a/lib/lp/services/feeds/stories/xx-navigation.txt b/lib/lp/services/feeds/stories/xx-navigation.txt
index a7cc583..719e10a 100644
--- a/lib/lp/services/feeds/stories/xx-navigation.txt
+++ b/lib/lp/services/feeds/stories/xx-navigation.txt
@@ -11,21 +11,25 @@ Trying to view a page which is not registered on the Feeds layer
returns a 404.
>>> browser.open('http://feeds.launchpad.test/jokosher')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NotFound: ...
+ zope.publisher.interfaces.NotFound: ...
>>> browser.open('http://feeds.launchpad.test/bugs')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NotFound: ...
+ zope.publisher.interfaces.NotFound: ...
>>> browser.open('http://feeds.launchpad.test/+dims')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NotFound: ...
+ zope.publisher.interfaces.NotFound: ...
>>> browser.open('http://feeds.launchpad.test/announcements.atom/foo')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NotFound: ...
+ zope.publisher.interfaces.NotFound: ...
== Redirection ==
diff --git a/lib/lp/services/fields/doc/uri-field.txt b/lib/lp/services/fields/doc/uri-field.txt
index 84409bf..ef9bb3e 100644
--- a/lib/lp/services/fields/doc/uri-field.txt
+++ b/lib/lp/services/fields/doc/uri-field.txt
@@ -34,9 +34,10 @@ valid URI:
>>> field = IURIFieldTest['field']
>>> field.validate(u'http://launchpad.net/')
>>> field.validate(u'not-a-uri')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- LaunchpadValidationError: "not-a-uri" is not a valid URI
+ lp.app.validators.LaunchpadValidationError: "not-a-uri" is not a valid URI
=== Scheme Restrictions ===
@@ -48,10 +49,10 @@ will result in a validation error:
>>> sftp_only = IURIFieldTest['sftp_only']
>>> sftp_only.validate(u'sFtp://launchpad.net/')
>>> sftp_only.validate(u'http://launchpad.net/')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- LaunchpadValidationError: The URI scheme "http" is not allowed.
- Only URIs with the following schemes may be used: sftp
+ lp.app.validators.LaunchpadValidationError: The URI scheme "http" is not allowed. Only URIs with the following schemes may be used: sftp
=== Disallowing Userinfo ===
@@ -62,14 +63,16 @@ product home page, where authentication is not generally required:
>>> no_userinfo = IURIFieldTest['no_userinfo']
>>> no_userinfo.validate(u'http://launchpad.net:80@127.0.0.1/ubuntu')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- LaunchpadValidationError: A username may not be specified in the URI.
+ lp.app.validators.LaunchpadValidationError: A username may not be specified in the URI.
>>> no_userinfo.validate(u'http://launchpad.net@127.0.0.1/ubuntu')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- LaunchpadValidationError: A username may not be specified in the URI.
+ lp.app.validators.LaunchpadValidationError: A username may not be specified in the URI.
=== Disallowing Non-default Ports ===
@@ -79,9 +82,10 @@ URIs. This can be done with the allow_port option:
>>> no_port = IURIFieldTest['no_port']
>>> no_port.validate(u'http://launchpad.net:21')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- LaunchpadValidationError: Non-default ports are not allowed.
+ lp.app.validators.LaunchpadValidationError: Non-default ports are not allowed.
Note that an error is not raised if the URI specifies a port but it is
known to be the default for that scheme:
@@ -97,9 +101,10 @@ reject those URIs:
>>> no_query = IURIFieldTest['no_query']
>>> no_query.validate(u'http://launchpad.net/?key=value')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- LaunchpadValidationError: URIs with query strings are not allowed.
+ lp.app.validators.LaunchpadValidationError: URIs with query strings are not allowed.
=== Disallowing the Fragment Component ===
@@ -108,9 +113,10 @@ The fragment component can also be disallowed:
>>> no_fragment = IURIFieldTest['no_fragment']
>>> no_fragment.validate(u'http://launchpad.net/#fragment')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- LaunchpadValidationError: URIs with fragment identifiers are not allowed.
+ lp.app.validators.LaunchpadValidationError: URIs with fragment identifiers are not allowed.
== Requiring or Forbidding a Trailing Slash ===
@@ -195,9 +201,10 @@ This widget is registered as an input widget:
Multiple values will cause an UnexpectedFormData exception:
>>> widget._toFieldValue(['http://launchpad.net', 'http://ubuntu.com'])
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- UnexpectedFormData: Only a single value is expected
+ lp.app.errors.UnexpectedFormData: Only a single value is expected
Values with leading and trailing whitespace are stripped.
diff --git a/lib/lp/services/gpg/doc/gpg-signatures.txt b/lib/lp/services/gpg/doc/gpg-signatures.txt
index eb15e2e..6ba60df 100644
--- a/lib/lp/services/gpg/doc/gpg-signatures.txt
+++ b/lib/lp/services/gpg/doc/gpg-signatures.txt
@@ -86,9 +86,10 @@ The text below was "clear signed" by 0xDFD20543 master key but tampered with:
... """
>>> master_sig = gpghandler.getVerifiedSignature(content)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGVerificationError: (7, 8, u'Bad signature')
+ lp.services.gpg.interfaces.GPGVerificationError: (7, 8, u'Bad signature')
If no signed content is found, an exception is raised:
@@ -101,9 +102,10 @@ If no signed content is found, an exception is raised:
... """
>>> master_sig = gpghandler.getVerifiedSignature(content)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGVerificationError: No signatures found
+ lp.services.gpg.interfaces.GPGVerificationError: No signatures found
The text below contains two clear signed sections. As there are two
@@ -140,14 +142,16 @@ change. -- Guilherme Salgado, 2007-04-05
(https://lists.ubuntu.com/mailman/private/launchpad/2007-April/015085.html)
>>> master_sig = gpghandler.getVerifiedSignature(content)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGVerificationError...
+ lp.services.gpg.interfaces.GPGVerificationError: ...
# >>> master_sig = gpghandler.getVerifiedSignature(content)
+# ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
# Traceback (most recent call last):
# ...
-# GPGVerificationError: Single signature expected, found multiple signatures
+# lp.services.gpg.interfaces.GPGVerificationError: Single signature expected, found multiple signatures
The text below was signed by key that's is not part of the
imported keyring. Note that we have extra debug information containing
@@ -166,10 +170,10 @@ the GPGME error codes (they may be helpful).
... -----END PGP SIGNATURE-----
... """
>>> gpghandler.getVerifiedSignature(content)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGKeyDoesNotExistOnServer: GPG key E192C0543B1BB2EB does not exist on the
- keyserver.
+ lp.services.gpg.interfaces.GPGKeyDoesNotExistOnServer: GPG key E192C0543B1BB2EB does not exist on the keyserver.
Due to unpredictable behaviour between the production system and
the external keyserver, we have a resilient signature verifier,
@@ -180,12 +184,10 @@ exception. The exception raised by this method will contain debug
information for the 3 failures.
>>> gpghandler.getVerifiedSignatureResilient(content)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGVerificationError: Verification failed 3 times:
- ['GPG key E192C0543B1BB2EB does not exist on the keyserver.',
- 'GPG key E192C0543B1BB2EB does not exist on the keyserver.',
- 'GPG key E192C0543B1BB2EB does not exist on the keyserver.']
+ lp.services.gpg.interfaces.GPGVerificationError: Verification failed 3 times: ['GPG key E192C0543B1BB2EB does not exist on the keyserver.', 'GPG key E192C0543B1BB2EB does not exist on the keyserver.', 'GPG key E192C0543B1BB2EB does not exist on the keyserver.']
Debugging exceptions
diff --git a/lib/lp/services/gpg/doc/gpghandler.txt b/lib/lp/services/gpg/doc/gpghandler.txt
index d57bd75..0fabddf 100644
--- a/lib/lp/services/gpg/doc/gpghandler.txt
+++ b/lib/lp/services/gpg/doc/gpghandler.txt
@@ -39,16 +39,18 @@ unit tests somewhere else at some point. -- Guilherme Salgado, 2006-08-23
A GPGKeyNotFoundError is raised if we try to import an empty content.
>>> key = gpghandler.importPublicKey('')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGKeyNotFoundError...
+ lp.services.gpg.interfaces.GPGKeyNotFoundError: ...
The same happens for bogus content.
>>> key = gpghandler.importPublicKey('XXXXXXXXX')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGKeyNotFoundError: ...
+ lp.services.gpg.interfaces.GPGKeyNotFoundError: ...
Let's recover some coherent data and verify if it works as expected:
@@ -93,9 +95,10 @@ SecretGPGKeyImportDetected exception to be raised.
>>> filepath = os.path.join(gpgkeysdir, 'test@xxxxxxxxxxxxxxxxx')
>>> seckey = open(filepath).read()
>>> key = gpghandler.importPublicKey(seckey)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- SecretGPGKeyImportDetected: ...
+ lp.services.gpg.interfaces.SecretGPGKeyImportDetected: ...
Now, try to import two public keys, causing a MoreThanOneGPGKeyFound
exception to be raised.
@@ -103,17 +106,19 @@ exception to be raised.
>>> filepath = os.path.join(gpgkeysdir, 'foo.bar@xxxxxxxxxxxxxxxxx')
>>> pubkey2 = open(filepath).read()
>>> key = gpghandler.importPublicKey('\n'.join([pubkey, pubkey2]))
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- MoreThanOneGPGKeyFound: ...
+ lp.services.gpg.interfaces.MoreThanOneGPGKeyFound: ...
Raise a GPGKeyNotFoundError if we try to import a public key with damaged
preamble.
>>> key = gpghandler.importPublicKey(pubkey[1:])
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGKeyNotFoundError: ...
+ lp.services.gpg.interfaces.GPGKeyNotFoundError: ...
Apparently GPGME is able to import an incomplete public key:
@@ -128,9 +133,10 @@ But we get an error if the damage is big:
(what probably happened in bug #2547)
>>> key = gpghandler.importPublicKey(pubkey[:-500])
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGKeyNotFoundError: ...
+ lp.services.gpg.interfaces.GPGKeyNotFoundError: ...
== Importing secret OpenPGP keys ==
@@ -209,10 +215,10 @@ hit the keyserver and import it automatically.
An attempt to upload an unknown key will fail.
>>> gpghandler.uploadPublicKey('F' * 40)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGKeyDoesNotExistOnServer: GPG key
- FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF does not exist on the keyserver.
+ lp.services.gpg.interfaces.GPGKeyDoesNotExistOnServer: GPG key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF does not exist on the keyserver.
Uploading the same key more than once is fine, it is handled on the
keyserver side.
@@ -224,10 +230,10 @@ in a error.
>>> tac.tearDown()
>>> gpghandler.uploadPublicKey(new_key.fingerprint)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGUploadFailure: Could not reach keyserver at
- http://localhost:11371...Connection refused...
+ lp.services.gpg.interfaces.GPGUploadFailure: Could not reach keyserver at http://localhost:11371...Connection refused...
== Fingerprint sanitizing ==
diff --git a/lib/lp/services/helpers.py b/lib/lp/services/helpers.py
index ca2f9ac..b102871 100644
--- a/lib/lp/services/helpers.py
+++ b/lib/lp/services/helpers.py
@@ -135,9 +135,10 @@ def shortlist(sequence, longest_expected=15, hardlimit=None):
sequences with no more than 2 items. There were 3 items.
>>> shortlist([1, 2, 3, 4], hardlimit=2)
+ ... # doctest: +NORMALIZE_WHITESPACE,+IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- ShortListTooBigError: Hard limit of 2 exceeded.
+ lp.services.helpers.ShortListTooBigError: Hard limit of 2 exceeded.
>>> shortlist(
... [1, 2, 3, 4], 2, hardlimit=4) #doctest: +NORMALIZE_WHITESPACE
@@ -153,10 +154,11 @@ def shortlist(sequence, longest_expected=15, hardlimit=None):
...
TypeError: ...
- >>> shortlist(iter(range(10)), 5, hardlimit=8) #doctest: +ELLIPSIS
+ >>> shortlist(iter(range(10)), 5, hardlimit=8)
+ ... # doctest: +ELLIPSIS,+IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- ShortListTooBigError: ...
+ lp.services.helpers.ShortListTooBigError: ...
"""
if hardlimit is not None:
diff --git a/lib/lp/services/identity/doc/emailaddress.txt b/lib/lp/services/identity/doc/emailaddress.txt
index 3ddd590..5d8d9d6 100644
--- a/lib/lp/services/identity/doc/emailaddress.txt
+++ b/lib/lp/services/identity/doc/emailaddress.txt
@@ -36,16 +36,18 @@ exception.
>>> personset = getUtility(IPersonSet)
>>> foobar = personset.getByName('name16')
>>> emailset.new(email.email, foobar)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- EmailAddressAlreadyTaken: The email address '...' is already registered.
+ lp.services.identity.interfaces.emailaddress.EmailAddressAlreadyTaken: The email address '...' is already registered.
The email address verification is case insensitive as well:
>>> emailset.new(email.email.upper(), foobar)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- EmailAddressAlreadyTaken: The email address '...' is already registered.
+ lp.services.identity.interfaces.emailaddress.EmailAddressAlreadyTaken: The email address '...' is already registered.
Registering a new email address works -- and preserves case -- though:
@@ -87,16 +89,17 @@ or the address of a team's mailing list.
Otherwise, UndeletableEmailAddress is raised.
>>> foobar.preferredemail.destroySelf()
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- UndeletableEmailAddress: This is a person's preferred email...
+ lp.services.identity.model.emailaddress.UndeletableEmailAddress: This is a person's preferred email...
>>> from lp.registry.tests.mailinglists_helper import (
... new_list_for_team)
>>> mailing_list = new_list_for_team(guadamen)
>>> email = emailset.getByEmail(guadamen.mailing_list.address)
>>> email.destroySelf()
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- UndeletableEmailAddress: This is the email address of a team's mailing
- list...
+ lp.services.identity.model.emailaddress.UndeletableEmailAddress: This is the email address of a team's mailing list...
diff --git a/lib/lp/services/librarian/doc/librarian.txt b/lib/lp/services/librarian/doc/librarian.txt
index 5f64a74..7c8b66c 100644
--- a/lib/lp/services/librarian/doc/librarian.txt
+++ b/lib/lp/services/librarian/doc/librarian.txt
@@ -364,14 +364,16 @@ If you try to retrieve this file through the standard ILibrarianClient,
you'll get a DownloadFailed error.
>>> client.getFileByAlias(private_file_id)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- DownloadFailed: Alias ... cannot be downloaded from this client.
+ lp.services.librarian.interfaces.client.DownloadFailed: Alias ... cannot be downloaded from this client.
>>> client.getURLForAlias(private_file_id)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- DownloadFailed: Alias ... cannot be downloaded from this client.
+ lp.services.librarian.interfaces.client.DownloadFailed: Alias ... cannot be downloaded from this client.
But using the restricted librarian will work:
@@ -389,9 +391,10 @@ Trying to access that file directly on the normal librarian will fail
... config.librarian.restricted_download_url,
... config.librarian.download_url)
>>> urlopen(sneaky_url).read()
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- HTTPError: HTTP Error 404: Not Found
+ urllib.error.HTTPError: HTTP Error 404: Not Found
But downloading it from the restricted host, will work.
@@ -412,14 +415,16 @@ also fails:
>>> transaction.commit()
>>> restricted_client.getURLForAlias(public_file_id)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- DownloadFailed: ...
+ lp.services.librarian.interfaces.client.DownloadFailed: ...
>>> restricted_client.getFileByAlias(public_file_id)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- DownloadFailed: ...
+ lp.services.librarian.interfaces.client.DownloadFailed: ...
The remoteAddFile() on the restricted client, also creates a restricted
file:
@@ -476,9 +481,10 @@ An UploadFailed will be raised if you try to create a file with no
content
>>> client.addFile('test.txt', 0, io.BytesIO(b'hello'), 'text/plain')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
[...]
- UploadFailed: Invalid length: 0
+ lp.services.librarian.interfaces.client.UploadFailed: Invalid length: 0
If you really want a zero length file you can do it:
@@ -631,9 +637,10 @@ The count starts at 0, and cannot be changed directly.
0
>>> public_file.hits = 10
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- ForbiddenAttribute: ...
+ zope.security.interfaces.ForbiddenAttribute: ...
To change that, we have to use the updateDownloadCount() method, which
takes care of creating/updating the necessary LibraryFileDownloadCount
diff --git a/lib/lp/services/mail/doc/emailauthentication.txt b/lib/lp/services/mail/doc/emailauthentication.txt
index fc37e8a..923fb15 100644
--- a/lib/lp/services/mail/doc/emailauthentication.txt
+++ b/lib/lp/services/mail/doc/emailauthentication.txt
@@ -99,9 +99,10 @@ InvalidSignature will be raised:
>>> name, addr = email.utils.parseaddr(msg['From'])
>>> from_user = getUtility(IPersonSet).getByEmail(addr)
>>> principal = authenticateEmail(msg, accept_any_timestamp)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- InvalidSignature:...
+ lp.services.mail.incoming.InvalidSignature: ...
Before the signature is verified, the signed text's line endings should
be canonicalised to \r\n. In order to ensure that the line endings in
@@ -122,9 +123,10 @@ message.
>>> from lp.services.gpg.interfaces import IGPGHandler
>>> getUtility(IGPGHandler).getVerifiedSignature(
... msg.signedContent, msg.signature)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- GPGVerificationError: (7, 8, u'Bad signature')
+ lp.services.gpg.interfaces.GPGVerificationError: (7, 8, u'Bad signature')
>>> getUtility(IGPGHandler).getVerifiedSignature(
... msg.signedContent.replace(b'\n', b'\r\n'), msg.signature)
diff --git a/lib/lp/services/mail/doc/mailbox.txt b/lib/lp/services/mail/doc/mailbox.txt
index 4da62d6..c3274cf 100644
--- a/lib/lp/services/mail/doc/mailbox.txt
+++ b/lib/lp/services/mail/doc/mailbox.txt
@@ -62,10 +62,10 @@ Before we can use it, it has to be opened.
To prevent two threads opening it the same time, if it's already open,
we can't open it again:
- >>> mailbox.open()
+ >>> mailbox.open() # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- MailBoxError: The mail box is already open.
+ lp.services.mail.mailbox.MailBoxError: The mail box is already open.
There's only one mail in the mail box, and it's the same as we sent
before:
@@ -103,9 +103,9 @@ Let's delete all mails in the mail box:
If we try to delete a mail that doesn't exist we get an error:
- >>> mailbox.delete(-1)
+ >>> mailbox.delete(-1) # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- MailBoxError: No such id: -1
+ lp.services.mail.mailbox.MailBoxError: No such id: -1
>>> mailbox.close()
diff --git a/lib/lp/services/mail/doc/notification-recipient-set.txt b/lib/lp/services/mail/doc/notification-recipient-set.txt
index 7dea670..a1030ee 100644
--- a/lib/lp/services/mail/doc/notification-recipient-set.txt
+++ b/lib/lp/services/mail/doc/notification-recipient-set.txt
@@ -114,14 +114,16 @@ Requesting the reason for somebody that is not a recipient raises a
UnknownRecipientError:
>>> recipients.getReason(no_priv)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- UnknownRecipientError: ...
+ lp.services.mail.interfaces.UnknownRecipientError: ...
>>> recipients.getReason(six.ensure_str('no-priv@xxxxxxxxxxxxx'))
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- UnknownRecipientError: 'no-priv@xxxxxxxxxxxxx'
+ lp.services.mail.interfaces.UnknownRecipientError: 'no-priv@xxxxxxxxxxxxx'
Passing something else than an IPerson or a string is forbidden:
diff --git a/lib/lp/services/mail/tests/incomingmail.txt b/lib/lp/services/mail/tests/incomingmail.txt
index f9fb29f..c42ef12 100644
--- a/lib/lp/services/mail/tests/incomingmail.txt
+++ b/lib/lp/services/mail/tests/incomingmail.txt
@@ -298,10 +298,10 @@ reporting in the web interface, are not ignored in the email interface.
... doesn't matter
... """)
>>> msgid = sendmail(msg, ['edit@malone-domain'])
- >>> handleMailForTest()
+ >>> handleMailForTest() # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
ERROR:process-mail:An exception was raised inside the handler:
...
- Unauthorized
+ twisted.cred.error.Unauthorized
>>> print(str(pop_notifications()[-1]))
From ...
@@ -401,10 +401,10 @@ logged.
>>> len(stub.test_emails)
2
- >>> handleMailForTest()
+ >>> handleMailForTest() # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
ERROR:...:Upload to Librarian failed...
...
- UploadFailed: ...Connection refused...
+ lp.services.librarian.interfaces.client.UploadFailed: ...Connection refused...
>>> len(stub.test_emails)
2
diff --git a/lib/lp/services/messages/doc/message.txt b/lib/lp/services/messages/doc/message.txt
index 1bd8b0d..3113703 100644
--- a/lib/lp/services/messages/doc/message.txt
+++ b/lib/lp/services/messages/doc/message.txt
@@ -385,9 +385,10 @@ explicitly told to do so:
...
... Foo Bar
... ''')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
[...]
- UnknownSender: u'invalid@xxxxxxxxxxx'
+ lp.services.messages.interfaces.message.UnknownSender: u'invalid@xxxxxxxxxxx'
>>> msg = msgset.fromEmail(b'''\
... From: invalid@xxxxxxxxxxx
@@ -418,9 +419,10 @@ passed through a broken MTA and we have no choice but to bounce them.
...
... Moo
... ''')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
[...]
- InvalidEmailMessage: Missing Message-Id
+ lp.services.messages.interfaces.message.InvalidEmailMessage: Missing Message-Id
>>> msg = msgset.fromEmail(b'''\
... Date: Fri, 17 Jun 2005 10:45:13 +0100
@@ -429,9 +431,10 @@ passed through a broken MTA and we have no choice but to bounce them.
...
... Moo
... ''')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
[...]
- InvalidEmailMessage: No From: or Reply-To: header
+ lp.services.messages.interfaces.message.InvalidEmailMessage: No From: or Reply-To: header
Also, we generally insist that a message has a date associated with it.
@@ -442,9 +445,10 @@ Also, we generally insist that a message has a date associated with it.
...
... Moo
... ''')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
[...]
- InvalidEmailMessage: Invalid date...
+ lp.services.messages.interfaces.message.InvalidEmailMessage: Invalid date...
However, we can override this behaviour by passing a date_created
parameter to fromEmail(). This is optional, and defaults to None, but it
diff --git a/lib/lp/services/oauth/doc/oauth-pages.txt b/lib/lp/services/oauth/doc/oauth-pages.txt
index 858a4ec..8c29dc9 100644
--- a/lib/lp/services/oauth/doc/oauth-pages.txt
+++ b/lib/lp/services/oauth/doc/oauth-pages.txt
@@ -154,14 +154,16 @@ package, it must specify the distribution and the package's name.
An error is raised if the context is not found.
>>> view, token = get_view_with_fresh_token({'lp.context': 'fooooo'})
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- UnexpectedFormData: ...
+ lp.app.errors.UnexpectedFormData: ...
Or if the user gives us a package in a non-existing distribution.
>>> view, token = get_view_with_fresh_token(
... {'lp.context': 'firefox/evolution'})
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- UnexpectedFormData: ...
+ lp.app.errors.UnexpectedFormData: ...
diff --git a/lib/lp/services/oauth/stories/access-token.txt b/lib/lp/services/oauth/stories/access-token.txt
index e1e8ed7..1d3c448 100644
--- a/lib/lp/services/oauth/stories/access-token.txt
+++ b/lib/lp/services/oauth/stories/access-token.txt
@@ -36,9 +36,10 @@ will fail because request tokens can be used only once.
>>> anon_browser.open(
... 'http://launchpad.test/+access-token', data=urlencode(data))
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- HTTPError: HTTP Error 401: Unauthorized
+ urllib.error.HTTPError: HTTP Error 401: Unauthorized
The token's context, when not None, is sent to the consumer together
with the token's key and secret.
diff --git a/lib/lp/services/oauth/stories/authorize-token.txt b/lib/lp/services/oauth/stories/authorize-token.txt
index 1ad5c47..e217b8a 100644
--- a/lib/lp/services/oauth/stories/authorize-token.txt
+++ b/lib/lp/services/oauth/stories/authorize-token.txt
@@ -36,9 +36,10 @@ it involves OpenID, which would complicate this test quite a bit.)
... oauth_token=token.key, oauth_callback='http://launchpad.test/bzr')
>>> url = "http://launchpad.test/+authorize-token?%s" % urlencode(params)
>>> browser.open(url)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Unauthorized:...
+ zope.security.interfaces.Unauthorized: ...
>>> browser = setupBrowser(auth='Basic no-priv@xxxxxxxxxxxxx:test')
>>> browser.open(url)
@@ -125,11 +126,10 @@ integration has its own section, below.)
>>> browser.open(
... "http://launchpad.test/+authorize-token?%s&%s"
... % (urlencode(params), allow_permission))
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Unauthorized: Consumer "foobar123451432" asked for desktop
- integration, but didn't say what kind of desktop it is, or name
- the computer being integrated.
+ zope.security.interfaces.Unauthorized: Consumer "foobar123451432" asked for desktop integration, but didn't say what kind of desktop it is, or name the computer being integrated.
An application may also specify a context, so that the access granted
by the user is restricted to things related to that context.
@@ -365,17 +365,17 @@ DESKTOP_INTEGRATION, since the whole point is to have a permission
level that specifically applies across the entire desktop.
>>> print_access_levels_for('allow_permission=WRITE_PRIVATE')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Unauthorized: Desktop integration token requested a permission
- ("Change Anything") not supported for desktop-wide use.
+ zope.security.interfaces.Unauthorized: Desktop integration token requested a permission ("Change Anything") not supported for desktop-wide use.
>>> print_access_levels_for(
... 'allow_permission=WRITE_PUBLIC&' + allow_desktop)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Unauthorized: Desktop integration token requested a permission
- ("Change Non-Private Data") not supported for desktop-wide use.
+ zope.security.interfaces.Unauthorized: Desktop integration token requested a permission ("Change Non-Private Data") not supported for desktop-wide use.
You can't specify a callback URL when authorizing a desktop-wide
token, since callback URLs should only be used when integrating
@@ -383,19 +383,19 @@ websites into Launchpad.
>>> params['oauth_callback'] = 'http://launchpad.test/bzr'
>>> print_access_levels_for(allow_desktop)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Unauthorized: A desktop integration may not specify an OAuth
- callback URL.
+ zope.security.interfaces.Unauthorized: A desktop integration may not specify an OAuth callback URL.
This is true even if the desktop token isn't asking for the
DESKTOP_INTEGRATION permission.
>>> print_access_levels_for('allow_permission=WRITE_PRIVATE')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Unauthorized: A desktop integration may not specify an OAuth
- callback URL.
+ zope.security.interfaces.Unauthorized: A desktop integration may not specify an OAuth callback URL.
>>> del params['oauth_callback']
diff --git a/lib/lp/services/oauth/stories/managing-tokens.txt b/lib/lp/services/oauth/stories/managing-tokens.txt
index d0fe794..a75ad04 100644
--- a/lib/lp/services/oauth/stories/managing-tokens.txt
+++ b/lib/lp/services/oauth/stories/managing-tokens.txt
@@ -130,6 +130,7 @@ That page is protected with the launchpad.Edit permission, for obvious
reasons, so users can only access their own.
>>> user_browser.open('http://launchpad.test/~salgado/+oauth-tokens')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Unauthorized: ...launchpad.Edit...
+ zope.security.interfaces.Unauthorized: ...launchpad.Edit...
diff --git a/lib/lp/services/temporaryblobstorage/doc/temporaryblobstorage.txt b/lib/lp/services/temporaryblobstorage/doc/temporaryblobstorage.txt
index 463966f..d8b67fd 100644
--- a/lib/lp/services/temporaryblobstorage/doc/temporaryblobstorage.txt
+++ b/lib/lp/services/temporaryblobstorage/doc/temporaryblobstorage.txt
@@ -52,9 +52,10 @@ Size limits can be enforced, although this is turned off by default:
... """
>>> config.push('max_blob_size', max_blob_size)
>>> uuid = tsm.new(data)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- BlobTooLarge: 7
+ lp.services.temporaryblobstorage.interfaces.BlobTooLarge: 7
>>> config_data = config.pop('max_blob_size')
diff --git a/lib/lp/services/tests/test_helpers.py b/lib/lp/services/tests/test_helpers.py
index ff3d68e..4a1aa84 100644
--- a/lib/lp/services/tests/test_helpers.py
+++ b/lib/lp/services/tests/test_helpers.py
@@ -5,6 +5,8 @@ from doctest import DocTestSuite
from textwrap import dedent
import unittest
+from zope.testing.renormalizing import OutputChecker
+
from lp.services import helpers
from lp.services.tarfile_helpers import LaunchpadWriteTarFile
@@ -190,7 +192,7 @@ class TruncateTextTest(unittest.TestCase):
def test_suite():
suite = unittest.TestSuite()
suite.addTest(DocTestSuite())
- suite.addTest(DocTestSuite(helpers))
+ suite.addTest(DocTestSuite(helpers, checker=OutputChecker()))
suite.addTest(
unittest.TestLoader().loadTestsFromTestCase(TruncateTextTest))
return suite
diff --git a/lib/lp/services/webapp/doc/canonical_url.txt b/lib/lp/services/webapp/doc/canonical_url.txt
index 1082d4b..ba277c9 100644
--- a/lib/lp/services/webapp/doc/canonical_url.txt
+++ b/lib/lp/services/webapp/doc/canonical_url.txt
@@ -110,10 +110,10 @@ Configure a browser:url for ITown. Our first attempt fails because we mistyped
... />
... </configure>
... """.format(module_name=module.__name__))
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- ZopeXMLConfigurationError: File "<string>", line ...
- AttributeError: The name "countryOopsTypo" is not in ....ITown
+ zope.configuration.xmlconfig.ZopeXMLConfigurationError: File "<string>", line ... AttributeError: The name "countryOopsTypo" is not in ....ITown
>>> zcmlcontext = xmlconfig.string("""
... <configure xmlns:browser="http://namespaces.zope.org/browser">
@@ -271,9 +271,10 @@ itself be adapted to ICanonicalUrlData.
>>> object_that_has_no_url = object()
>>> canonical_url(object_that_has_no_url)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NoCanonicalUrl: No url for <...object at ...> because <...object at ...> broke the chain.
+ lp.services.webapp.interfaces.NoCanonicalUrl: No url for <...object at ...> because <...object at ...> broke the chain.
Now, we must test the case where the object can be adapted to
ICanonicalUrlData, but its parent or its parent's parent (and so on) cannot.
@@ -285,9 +286,10 @@ ICanonicalUrlData, but its parent or its parent's parent (and so on) cannot.
... self.inside = parent
>>> unrooted_object = ObjectThatHasUrl('unrooted', object_that_has_no_url)
>>> canonical_url(unrooted_object)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NoCanonicalUrl: No url for <...ObjectThatHasUrl...> because <...object...> broke the chain.
+ lp.services.webapp.interfaces.NoCanonicalUrl: No url for <...ObjectThatHasUrl...> because <...object...> broke the chain.
The first argument to NoCanonicalUrl is the object that a canonical url was
requested for. The second argument is the object that broke the chain.
@@ -329,20 +331,20 @@ to work properly.
>>> iterator = canonical_url_iterator(object_that_has_no_url)
>>> next(iterator).__class__.__name__
'object'
- >>> next(iterator)
+ >>> next(iterator) # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NoCanonicalUrl: No url for <...object...> because <...object...> broke the chain.
+ lp.services.webapp.interfaces.NoCanonicalUrl: No url for <...object...> because <...object...> broke the chain.
>>> iterator = canonical_url_iterator(unrooted_object)
>>> next(iterator).__class__.__name__
'ObjectThatHasUrl'
>>> next(iterator).__class__.__name__
'object'
- >>> next(iterator)
+ >>> next(iterator) # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NoCanonicalUrl: No url for <...ObjectThatHasUrl...> because <...object...> broke the chain.
+ lp.services.webapp.interfaces.NoCanonicalUrl: No url for <...ObjectThatHasUrl...> because <...object...> broke the chain.
== canonical_url and requests ==
diff --git a/lib/lp/services/webapp/doc/menus.txt b/lib/lp/services/webapp/doc/menus.txt
index d2c1910..0803475 100644
--- a/lib/lp/services/webapp/doc/menus.txt
+++ b/lib/lp/services/webapp/doc/menus.txt
@@ -809,9 +809,10 @@ We also report when the selected facet does not exist with a
LocationError exception:
>>> test_tales('view/menu:broken/bar', view=view)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- LocationError: ..., 'broken')
+ zope.location.interfaces.LocationError: ..., 'broken')
We can also get a context menu as menu:context. It makes no difference
whether the TALES code is view/menu:context or context/menu:context,
@@ -844,9 +845,10 @@ When there is no menu for a thing, we get an empty iterator.
And thus, we don't have a facet to navigate to:
>>> test_tales('view/menu:foo/+first', view=view)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- LocationError: ..., 'foo')
+ zope.location.interfaces.LocationError: ..., 'foo')
>>> view = LaunchpadView(house, request)
>>> view.__launchpad_facetname__ = 'bar'
diff --git a/lib/lp/services/webapp/doc/navigation.txt b/lib/lp/services/webapp/doc/navigation.txt
index abb4133..9abe14f 100644
--- a/lib/lp/services/webapp/doc/navigation.txt
+++ b/lib/lp/services/webapp/doc/navigation.txt
@@ -220,9 +220,10 @@ The name doesn't begin with a 't', so it isn't found.
>>> INewLayer.providedBy(request)
False
>>> navigation.publishTraverse(request, 'xxx')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NotFound: ...ThingSet...name: 'xxx'
+ zope.publisher.interfaces.NotFound: ...ThingSet...name: 'xxx'
Note that the request has been put onto the INewLayer layer.
@@ -332,13 +333,15 @@ Let's create a subclass of ThingSetNavigation, and add a 'stepto'.
>>> navigation2.publishTraverse(request, 'thistle')
'A little thistle'
>>> navigation2.publishTraverse(request, 'neverthere')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NotFound: ...ThingSet..., name: 'neverthere'
+ zope.publisher.interfaces.NotFound: ...ThingSet..., name: 'neverthere'
>>> navigation2.publishTraverse(request, 'neverthere2')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NotFound: ...ThingSet..., name: 'neverthere2'
+ zope.publisher.interfaces.NotFound: ...ThingSet..., name: 'neverthere2'
== stepthrough traversals ==
@@ -381,15 +384,17 @@ Let's create another subclass and add a stepthrough.
>>> request.traversal_stack = ['prince', 'charming']
>>> navigation3 = ThingSetNavigation3(thingset, request)
>>> navigation3.publishTraverse(request, 'neverland')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NotFound: ...ThingSet..., name: 'charming'
+ zope.publisher.interfaces.NotFound: ...ThingSet..., name: 'charming'
>>> request.traversal_stack = ['prince', 'charming']
>>> navigation3 = ThingSetNavigation3(thingset, request)
>>> navigation3.publishTraverse(request, 'neverland2')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NotFound: ...ThingSet..., name: 'charming'
+ zope.publisher.interfaces.NotFound: ...ThingSet..., name: 'charming'
Check that the request's state is as it should be.
diff --git a/lib/lp/services/webapp/doc/renamed-view.txt b/lib/lp/services/webapp/doc/renamed-view.txt
index 08e9287..fafb350 100644
--- a/lib/lp/services/webapp/doc/renamed-view.txt
+++ b/lib/lp/services/webapp/doc/renamed-view.txt
@@ -87,9 +87,10 @@ raise an error. e.g. http://launchpad.test/ubuntu/+tickets/foo
>>> request = LaunchpadTestRequest()
>>> view = RenamedView(ubuntu, request, '+tickets')
>>> view.publishTraverse(request, u'foo')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- NotFound: Object: <Distribution 'Ubuntu' (ubuntu)>, name: u'foo'
+ zope.publisher.interfaces.NotFound: Object: <Distribution 'Ubuntu' (ubuntu)>, name: u'foo'
== Registering from ZCML ==
diff --git a/lib/lp/services/webapp/doc/test_adapter.txt b/lib/lp/services/webapp/doc/test_adapter.txt
index 656e4b1..2cb559d 100644
--- a/lib/lp/services/webapp/doc/test_adapter.txt
+++ b/lib/lp/services/webapp/doc/test_adapter.txt
@@ -212,12 +212,10 @@ timeout by sleeping for 200ms with a 100ms statement timeout:
>>> print current_statement_timeout(store)
100ms
>>> store.execute('SELECT pg_sleep(0.200)', noresult=True)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- LaunchpadTimeoutError: Statement: 'SELECT pg_sleep(0.200)'
- Parameters:()
- Original error: QueryCanceledError('canceling statement due to
- statement timeout\n',)
+ lp.services.webapp.adapter.LaunchpadTimeoutError: Statement: 'SELECT pg_sleep(0.200)' Parameters:() Original error: QueryCanceledError('canceling statement due to statement timeout\n',)
Even though the statement timed out, it is recorded in the statement log:
@@ -285,12 +283,10 @@ This final invokation, we will actually sleep to ensure that the
timeout being reported by PostgreSQL is actually working:
>>> store.execute('SELECT pg_sleep(0.2)', noresult=True)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- LaunchpadTimeoutError: Statement: 'SELECT pg_sleep(0.2)'
- Parameters:()
- Original error: QueryCanceledError('canceling statement due to
- statement timeout\n',)
+ lp.services.webapp.adapter.LaunchpadTimeoutError: Statement: 'SELECT pg_sleep(0.2)' Parameters:() Original error: QueryCanceledError('canceling statement due to statement timeout\n',)
>>> clear_request_started()
@@ -346,9 +342,10 @@ another query:
>>> set_request_started(time.time() - 60)
>>> store.execute('SELECT 2', noresult=True)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- RequestExpired: request expired.
+ lp.services.webapp.adapter.RequestExpired: request expired.
The statement about to be executed is not recorded in the statement log.
The request time limit was exceeded before the statement was issued to
@@ -365,10 +362,10 @@ transaction will be doomed:
>>> transaction.get().isDoomed()
True
>>> transaction.commit()
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- DoomedTransaction: transaction doomed, cannot commit
- <BLANKLINE>
+ transaction.interfaces.DoomedTransaction: transaction doomed, cannot commit
Cleanup:
@@ -409,9 +406,10 @@ remove the timout:
>>> thread.start()
>>> _ = started_request.wait()
>>> store.execute('SELECT 1', noresult=True)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- RequestExpired: request expired.
+ lp.services.webapp.adapter.RequestExpired: request expired.
>>> statement_issued.set()
>>> thread.join()
>>> clear_request_started()
@@ -449,10 +447,10 @@ config section. By default we connect as "launchpad"
>>> store.execute("""
... INSERT INTO SourcePackageName(name) VALUES ('fnord4')
... """, noresult=True)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- ProgrammingError: permission denied for relation sourcepackagename
- <BLANKLINE>
+ storm.database.ProgrammingError: permission denied for relation sourcepackagename
This is not reset at the end of the transaction:
@@ -462,10 +460,10 @@ This is not reset at the end of the transaction:
>>> store.execute("""
... INSERT INTO SourcePackageName(name) VALUES ('fnord4')
... """, noresult=True)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- ProgrammingError: permission denied for relation sourcepackagename
- <BLANKLINE>
+ storm.database.ProgrammingError: permission denied for relation sourcepackagename
>>> transaction.abort()
So you need to explicitly set the user back to the default:
diff --git a/lib/lp/services/webapp/doc/test_adapter_permissions.txt b/lib/lp/services/webapp/doc/test_adapter_permissions.txt
index 1751c38..aaf56ec 100644
--- a/lib/lp/services/webapp/doc/test_adapter_permissions.txt
+++ b/lib/lp/services/webapp/doc/test_adapter_permissions.txt
@@ -20,20 +20,20 @@ If a SLAVE_FLAVOR store is requested, it should trap all writes.
>>> main_slave = getUtility(IStoreSelector).get(MAIN_STORE, SLAVE_FLAVOR)
>>> janitor = main_slave.find(Person, name='janitor').one()
>>> janitor.display_name = 'Ben Dover'
- >>> transaction.commit()
+ >>> transaction.commit() # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- InternalError: ...
+ storm.database.InternalError: ...
Test this once more to ensure the settings stick across transactions.
>>> transaction.abort()
>>> t = transaction.begin()
>>> main_slave.find(Person, name='janitor').one().display_name = 'BenD'
- >>> transaction.commit()
+ >>> transaction.commit() # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- InternalError: ...
+ storm.database.InternalError: ...
If a MASTER_FLAVOR is requested, it should allow writes to table in that
Store's replication set.
diff --git a/lib/lp/services/webapp/doc/test_adapter_timeout.txt.disabled b/lib/lp/services/webapp/doc/test_adapter_timeout.txt.disabled
index 5940282..b2fe604 100644
--- a/lib/lp/services/webapp/doc/test_adapter_timeout.txt.disabled
+++ b/lib/lp/services/webapp/doc/test_adapter_timeout.txt.disabled
@@ -84,9 +84,10 @@ Now we actually demonstrate the behaviour. The view did raise a TimeoutError.
>>> browser = setupBrowser()
>>> browser.open('http://launchpad.test/doom_test')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- TimeoutError...
+ lp.services.timeout.TimeoutError: ...
The exception view did not.
diff --git a/lib/lp/services/webapp/doc/timeout.txt b/lib/lp/services/webapp/doc/timeout.txt
index 3387d72..9a80f1d 100644
--- a/lib/lp/services/webapp/doc/timeout.txt
+++ b/lib/lp/services/webapp/doc/timeout.txt
@@ -49,10 +49,10 @@ If the timeout is already expired, a RequestExpired error is raised:
>>> from lp.services.webapp.adapter import clear_request_started
>>> clear_request_started()
>>> adapter.set_request_started(time.time()-12)
- >>> timeout_func()
+ >>> timeout_func() # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- RequestExpired: request expired.
+ lp.services.webapp.adapter.RequestExpired: request expired.
Same thing if a function decorated using @with_timeout is called.
@@ -60,10 +60,10 @@ Same thing if a function decorated using @with_timeout is called.
>>> @with_timeout()
... def wait_a_little():
... time.sleep(1)
- >>> wait_a_little()
+ >>> wait_a_little() # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- RequestExpired: request expired.
+ lp.services.webapp.adapter.RequestExpired: request expired.
@with_timeout allows the actual timeout value to be specified, either as a
numeric argument or a function argument returning the required value. Here we
diff --git a/lib/lp/services/webapp/doc/webapp-publication.txt b/lib/lp/services/webapp/doc/webapp-publication.txt
index 3df0f24..8be85b6 100644
--- a/lib/lp/services/webapp/doc/webapp-publication.txt
+++ b/lib/lp/services/webapp/doc/webapp-publication.txt
@@ -533,9 +533,10 @@ UnexpectedFormData is raised if more than one value was submitted for
the field:
>>> request.form_ng.getOne('items_field')
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- UnexpectedFormData:...
+ lp.app.errors.UnexpectedFormData: ...
None is returned if the field wasn't submitted:
@@ -773,9 +774,10 @@ If that happens, though, we'll remove the
... except:
... publication.handleException(
... None, request, sys.exc_info(), retry_allowed=True)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Retry: foo
+ zope.publisher.interfaces.Retry: foo
>>> 'launchpad.publicationduration' in request._orig_env
False
@@ -801,9 +803,10 @@ If that happens, though, we'll remove the
... exc_info = sys.exc_info()
>>> publication.handleException(
... None, request, exc_info, retry_allowed=True)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Retry: foo DisconnectionError
+ zope.publisher.interfaces.Retry: foo DisconnectionError
>>> 'launchpad.publicationduration' in request._orig_env
False
@@ -833,9 +836,10 @@ WSGI env.
... except:
... publication.handleException(
... None, request, sys.exc_info(), retry_allowed=True)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Retry: foo
+ zope.publisher.interfaces.Retry: foo
>>> request._orig_env['launchpad.publicationduration']
0.5
@@ -1161,9 +1165,10 @@ raised, though.
>>> form2['oauth_nonce'] = '1764572616e48616d6d65724c61686'
>>> test_request = LaunchpadTestRequest(form=form2)
>>> publication.getPrincipal(test_request)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- TokenException: Expired token...
+ lp.services.oauth.interfaces.TokenException: Expired token...
>>> access_token.date_expires = now + timedelta(days=1)
@@ -1172,9 +1177,10 @@ raised, though.
>>> form2['oauth_nonce'] = '4572616e48616d6d65724c61686176'
>>> test_request = LaunchpadTestRequest(form=form2)
>>> publication.getPrincipal(test_request)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- TokenException: Unknown access token...
+ lp.services.oauth.interfaces.TokenException: Unknown access token...
The consumer must be registered as well, and the signature must be
correct.
@@ -1183,18 +1189,20 @@ correct.
>>> form2['oauth_consumer_key'] += 'z'
>>> test_request = LaunchpadTestRequest(form=form2)
>>> publication.getPrincipal(test_request)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Unauthorized: Unknown consumer (foobar123451432z).
+ zope.security.interfaces.Unauthorized: Unknown consumer (foobar123451432z).
>>> form2 = form.copy()
>>> form2['oauth_signature'] += 'z'
>>> form2['oauth_nonce'] = '2616e48616d6d65724c61686176457'
>>> test_request = LaunchpadTestRequest(form=form2)
>>> publication.getPrincipal(test_request)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- TokenException: Invalid signature.
+ lp.services.oauth.interfaces.TokenException: Invalid signature.
Close the bogus request that was started by the call to
beforeTraversal, in order to ensure we leave our state sane.
diff --git a/lib/lp/services/webservice/doc/launchpadlib.txt b/lib/lp/services/webservice/doc/launchpadlib.txt
index c3c0cde..30e7142 100644
--- a/lib/lp/services/webservice/doc/launchpadlib.txt
+++ b/lib/lp/services/webservice/doc/launchpadlib.txt
@@ -14,9 +14,10 @@ if a specific user exists...
'/'
>>> browser.open('%s/~stimpy' % root_url)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- HTTPError: HTTP Error 404: Not Found
+ urllib.error.HTTPError: HTTP Error 404: Not Found
...and when they don't, create them.
diff --git a/lib/lp/services/webservice/stories/launchpadlib.txt b/lib/lp/services/webservice/stories/launchpadlib.txt
index 5a79f52..70c552c 100644
--- a/lib/lp/services/webservice/stories/launchpadlib.txt
+++ b/lib/lp/services/webservice/stories/launchpadlib.txt
@@ -79,9 +79,10 @@ But trying to access information that requires a logged in user
results in an error.
>>> print(lp_anon.me.name)
+ ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
Traceback (most recent call last):
...
- Unauthorized: HTTP Error 401: Unauthorized...
+ lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized...
Caching