Merge lp:~jtv/launchpad/recife-552639 into lp:~launchpad/launchpad/recife
| Status: | Merged | ||||
|---|---|---|---|---|---|
| Approved by: | Jeroen T. Vermeulen on 2010-06-30 | ||||
| Approved revision: | no longer in the source branch. | ||||
| Merged at revision: | 9140 | ||||
| Proposed branch: | lp:~jtv/launchpad/recife-552639 | ||||
| Merge into: | lp:~launchpad/launchpad/recife | ||||
| Diff against target: |
752 lines (+509/-30) 8 files modified
database/schema/security.cfg (+1/-1) lib/lp/translations/interfaces/potmsgset.py (+21/-0) lib/lp/translations/interfaces/translationmessage.py (+3/-0) lib/lp/translations/interfaces/translations.py (+12/-3) lib/lp/translations/model/potmsgset.py (+308/-24) lib/lp/translations/model/translationmessage.py (+5/-0) lib/lp/translations/tests/test_potmsgset.py (+122/-1) lib/lp/translations/tests/test_translationmessage.py (+37/-1) |
||||
| To merge this branch: | bzr merge lp:~jtv/launchpad/recife-552639 | ||||
| Related bugs: |
|
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Henning Eggers (community) | code | 2010-05-07 | Approve on 2010-06-21 |
|
Review via email:
|
|||
Commit Message
Implement setCurrentTrans
Description of the Change
= Bug 552639 =
This implements the unconditional setting of a translation for a particular message as current in Ubuntu, or upstream, or both. See the spec: https:/
| Jeroen T. Vermeulen (jtv) wrote : | # |
| Jeroen T. Vermeulen (jtv) wrote : | # |
The tests in danilo's separate branch now pass. I've made a few design changes, fixed one or two mistakes in the code, and changed the expected behavior of a few tests. But for something this big, not bad.
The few relevant tests that are in this branch you can run with:
{{{
./bin/test -vv -t test_potmsgset
}}}
If you're not using the feature branch (lp:~launchpad/launchpad/recife) as a merge base you will see some failures from resetCurrentTra
I'm going to put up the test suite for separate review, in order to keep line count in check.
| Henning Eggers (henninge) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
> === modified file 'database/
OK
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -214,6 +214,10 @@
> force_edition_
> """Update or create a translation message using `new_translations`.
>
> + This method is Launchpad Translations's sliderule: it does
> + everything, nobody fully understands it all, and we intend to
> + replace it with a range of less flexible tools.
> +
> :param pofile: a `POFile` to add `new_translations` to.
> :param submitter: author of the translations.
> :param new_translations: a dictionary of plural forms, with the
> @@ -261,6 +265,20 @@
> If a translation conflict is detected, TranslationConflict is raised.
> """
>
> + def setCurrentTrans
> + translation_side, share_with_
> + """Set the message's translation in Ubuntu, or upstream, or both.
> +
> + :param pofile:
> + :param submitter:
> + :param translations:
> + :param origin:
> + :param translation_side: a `TranslationSide` value.
> + :param share_with_
> + """
> + # XXX: Check signature before completing branch.
> + # XXX: Update docstring.
So, these XXX are supposed to be gone before we land the feature, right?
> +
> def resetCurrentTra
> """Reset the currently used translation.
>
> === modified file 'lib/lp/
OK
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -4,11 +4,16 @@
> # pylint: disable-
>
> __metaclass__ = type
> -__all__ = ['POTMsgSet']
> +__all__ = [
> + 'make_translati
> + 'POTMsgSet',
> + ]
> +
>
> import datetime
> import logging
> import pytz
> +import re
>
> from zope.interface import implements
> from zope.component import getUtility
> @@ -41,7 +46,8 @@
> RosettaTranslat
> TranslationConf
> TranslationVali
> -from lp.translations
> +from lp.translations
> + TranslationCons
> from lp.translations
> from lp.translations
> from lp.translations
> @@ -79,6 +85,91 @@
> u'credits are counted as translated.')
>
>
> +class TranslationSide
> + """Dealing with a `POTMsgSet` on either `TranslationSide`.
> +
> + Encapsulates primitives that depend on translation side: finding the
> + message that ...
| Jeroen T. Vermeulen (jtv) wrote : | # |
> > === modified file 'lib/lp/
> > --- lib/lp/
> +0000
> > +++ lib/lp/
> +0000
> > @@ -214,6 +214,10 @@
> > force_edition_
> > """Update or create a translation message using `new_translations`.
> >
> > + This method is Launchpad Translations's sliderule: it does
> > + everything, nobody fully understands it all, and we intend to
> > + replace it with a range of less flexible tools.
> > +
> > :param pofile: a `POFile` to add `new_translations` to.
> > :param submitter: author of the translations.
> > :param new_translations: a dictionary of plural forms, with the
> > @@ -261,6 +265,20 @@
> > If a translation conflict is detected, TranslationConflict is
> raised.
> > """
> >
> > + def setCurrentTrans
> > + translation_side,
> share_with_
> > + """Set the message's translation in Ubuntu, or upstream, or both.
> > +
> > + :param pofile:
> > + :param submitter:
> > + :param translations:
> > + :param origin:
> > + :param translation_side: a `TranslationSide` value.
> > + :param share_with_
> > + """
> > + # XXX: Check signature before completing branch.
> > + # XXX: Update docstring.
>
> So, these XXX are supposed to be gone before we land the feature, right?
Yes, I've fixed them now.
Once a long time ago I proposed allowing "reviewer notes" in the code. That was turned down, but now I sometimes include these malformed XXX comments to ensure that the reviewer will say something if I let them through.
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
> > @@ -4,11 +4,16 @@
> > # pylint: disable-
> >
> > __metaclass__ = type
> > -__all__ = ['POTMsgSet']
> > +__all__ = [
> > + 'make_translati
> > + 'POTMsgSet',
> > + ]
> > +
> >
> > import datetime
> > import logging
> > import pytz
> > +import re
> >
> > from zope.interface import implements
> > from zope.component import getUtility
> > @@ -41,7 +46,8 @@
> > RosettaTranslat
> > TranslationConf
> > TranslationVali
> > -from lp.translations
> > +from lp.translations
> > + TranslationCons
> > from lp.translations
> > from lp.translations
> > from lp.translations
> > @@ -79,6 +85,91 @@
> > u'credits are counted as translated.')
> >
> >
> > +class TranslationSide
> > + """Dealing with a `POTMsgSet` on either `Translatio...
| Jeroen T. Vermeulen (jtv) wrote : | # |
No idea how I managed to post my unfinished reply by accident just now, but I'll just continue where I left off.
> > + elif character == 'Z':
> > + # There is no incumbent message, so do nothing to it.
> > + assert incumbent_message is None, (
> > + "Incorrect Z in decision matrix.")
>
> Ok, first you hide this check in _nameMessageStatus, now you pull it out in
> the open for an assert. Maybe you should trust your matrix more and just put a
> "pass" in here?
Again rather not in this phase, where we want to be sure we can tweak things without breaking them. Once we start refactoring, the Z probably becomes a no-op anyway.
> > + elif character == '1':
> > + # Create & activate.
> > + message = self._makeTrans
> > + pofile, submitter, translations, origin)
> > + elif character == '2':
> > + # Create, diverge, activate.
> > + message = self._makeTrans
> > + pofile, submitter, translations, origin, diverged=True)
> > + elif character == '4':
> > + # Activate.
> > + message = twin
> > + elif character == '5':
> > + # If other is a suggestion, diverge and activate.
> > + # (If not, it's already active and has been unmasked by
> > + # our deactivating the incumbent).
> > + message = twin
> > + if not traits.
> > + assert not traits.
> > + "Decision matrix says '5' for a message that's "
> > + "active on the other side.")
>
> Yes, that is what is done in _nameMessageStatus, so duplicated here. Also, the
> error message is quite cryptic to an outsider.
Alright, I've made the message clearer.
> > +
> > + traits.
> > +
> > + return message
> > +
> > +>>>>>>> MERGE-SOURCE
> > def resetCurrentTra
> > """See `IPOTMsgSet`."""
> >
> > @@ -1145,7 +1423,7 @@
> > # The credits message has a fixed "translator."
> > translator = getUtility(
> >
> > - message = self.updateTran
> > + self.updateTran
> > pofile, translator, [credits_
> > is_current_
> > force_shared=True, force_edition_
> > === modified file 'lib/lp/
>
> The tests look very good although I am sure there could be even more.... ;-)
I'm not sure what you mean. As you know (and as mentioned several times in the initial comments for this MP) there is a separate branch with exhaustive tests.
Jeroen
| Henning Eggers (henninge) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Am 19.06.2010 07:27, schrieb Jeroen T. Vermeulen:
[...]
>>> +=======
>>> + def _nameMessageSta
>>> + """Figure out the decision-matrix status of a message.
>>> +
>>> + This is used in navigating the decision matrix in
>>> + `setCurrentTran
>>> + """
>>> + if message is None:
>>> + return 'none'
>>> + elif message.potemplate is None:
>>> + if translation_
>>> + return 'other_shared'
>>> + else:
>>> + return 'shared'
>>> + else:
>>> + assert message.potemplate is not None, "Confused message
>> state."
>>
>> Your either forgot this in here or it's paranoia. Since the preceding elif
>> checks the exact same condition, this really is not needed.
>
> It's also partly caused by the "there must be an else" rule. I resolved this by:
Well, the usual way to fulfill that is "comment and pass". But the new 'if'
statement is much better anyway.
>
> 1. Adding an ITranslationMes
> 2. Moving the "else" clause above the "elif" clause.
> 3. Flattening the "if" inside the "elif" clause to be part of the main if/elif/else.
>
> Looks much nicer now. We should have added is_diverged earlier.
Yes, definitely! Thanks for finally doing this.
>
>
>>> + def setCurrentTrans
>> origin,
>>> + translation_side,
>> share_with_
>>> + """See `IPOTMsgSet`."""
>>> + traits = make_translatio
>>> + translation_side, self, pofile.potemplate, pofile.language,
>>> + variant=
>>> +
>>> + translations = self._findPOTra
>>> + incumbent_message = traits.
>>> + twin = self._findTrans
>>> + pofile, translations, prefer_
>>
>> Hm, I am not 100% sure what a twin is. Maybe you could explain that here?
>
> I thought the blueprint made that clear, but I've now documented it in the code.
Oh, I guess I was too slow. But "twin" just never appeared before. Thanks for
documenting it now.
>
>
>>> + # Summary of the matrix:
>>> + # * If the incumbent message is diverged and we're setting a
>>> + # translation that's already shared: converge.
>>> + # * If the incumbent message is diverged and we're setting a
>>> + # translation that's not already shared: maintain divergence.
>>> + # * If the incumbent message is shared, replace it.
>>> + # * If there is no twin, simply create a new message (shared or
>>> + # diverged depending; see above).
>>> + # * If there is a shared twin, activate it (but also diverge if
>>> + # necessary; see above).
>>> + # * If there is a diverged twin, activate it (and converge it
>>> + # if appropriate; see above).
>>> + # * If there is a twin that's shared on the other side,
>>> +
>...
| Henning Eggers (henninge) wrote : | # |
Here one comment, I forgot.
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -1,4 +1,4 @@
> -# Copyright 2009 Canonical Ltd. This software is licensed under the
> +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
> # GNU Affero General Public License version 3 (see the file LICENSE).
>
> """Unit tests for `TranslationMes
> @@ -10,12 +10,46 @@
> from zope.component import getUtility
> from zope.security.proxy import removeSecurityProxy
>
> +from canonical.
> from lp.services.
> from lp.testing import TestCaseWithFactory
> from lp.translations
> +from lp.translations
> +from lp.translations
> + ITranslationMes
> from lp.translations
> from canonical.testing import ZopelessDatabas
>
> +
> +class TestTranslation
> + """Unit tests for `TranslationMes
> +
> + There aren't many of these. We didn't do much unit testing back
> + then.
> + """
And thanks a lot for adding these! ;-)
> +
> + layer = ZopelessDatabas
> +
> + def test_baseline(
> + message = self.factory.
> + verifyObject(
I'd have split this test here.
> +
> + pofile = self.factory.
> + potmsgset = self.factory.
> + dummy = DummyTranslatio
> + verifyObject(
> +
> + def test_is_
> + # ITranslationMes
> + # say "message.
> + # "message.potemplate is not None."
> + message = self.factory.
> + self.assertFals
> +
And this one here.
> + message = self.factory.
> + self.assertTrue
But that is just how I would do that. Makes test failures easier to spot.
Also, unrelated parts of a test will not be run if one fails. Maybe that
second reasoning is even stronger.
> +
> +
> class TestTranslation
> """Tests for `TranslationMes
>
Cheers, Henning
| Jeroen T. Vermeulen (jtv) wrote : | # |
Hi Henning,
Thanks for the review. You were right: my change to that assertion text was lost somehow. Good thing you spotted that. Hope it's fixed this time.
> > === modified file 'lib/lp/
> > --- lib/lp/
> 20:15:38 +0000
> > +++ lib/lp/
> 03:38:37 +0000
> > """Unit tests for `TranslationMes
> > + def test_baseline(
> > + message = self.factory.
> > + verifyObject(
>
> I'd have split this test here.
Good idea. I've been letting them grow longer so they'd waste less time on setup and teardown, sort of like our old doctests, but it's not always a good thing.
> > + def test_is_
> > + # ITranslationMes
> > + # say "message.
> > + # "message.potemplate is not None."
> > + message = self.factory.
> > + self.assertFals
> > +
>
> And this one here.
Both split now.
Jeroen

The "real" tests are being written separately by danilo. I only included some because I couldn't bear not to do it.
The code follows the decision matrix cited in the spec. There's ways to make it prettier, but we'll get to that sometime after we have good tests.
Jeroen