~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Classes that implement ICelebrity interfaces."""

__metaclass__ = type
__all__ = ['LaunchpadCelebrities']

from zope.component import getUtility
from zope.interface import implements

from lp.app.errors import NotFoundError
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
from lp.bugs.interfaces.bugtracker import IBugTrackerSet
from lp.registry.interfaces.distribution import IDistributionSet
from lp.registry.interfaces.distributionmirror import IDistributionMirrorSet
from lp.registry.interfaces.person import IPersonSet
from lp.registry.interfaces.product import IProductSet
from lp.services.worlddata.interfaces.language import ILanguageSet


class MutatedCelebrityError(Exception):
    """A celebrity has had its id or name changed in the database.

    This would indicate a major prodution screwup.
    """


class MissingCelebrityError(Exception):
    """A celebrity cannot be found in the database.

    Usually this means it has not yet been created.
    """


class CelebrityDescriptor:
    """An attribute of LaunchpadCelebrities

    This descriptor removes unnecessary boilerplate from the
    LaunchpadCelebrities attribute, as well as optimizing database
    access to ensure that using a celebrity causes at most one
    database query per request.

    TODO: By implementing a suitably clever wrapper, we should be able
    to reduce the queries further, as we will only ever need to really
    query the database if code attempts to access attributes of the
    celebrity besides the non-volatile id and name attributes. However,
    this is non trivial as we need to ensure that security and interface
    declarations remain unchanged. Perhaps we need a way of instantiating
    SQLObject instances in a 'lazy' mode? Or perhaps we should not worry
    about volatile attribute changes and pass a selectResults value through
    to the SQLObject.get method, which should allow us to instantiate a
    real instance without hitting the database. -- StuartBishop 20060123
    """
    interface = None
    name = None
    id = None

    def __init__(self, interface, name):
        """Interface is used to lookup a utility which must have both
        a get method to lookup by id, and a getByName method to lookup by
        name.
        """
        self.interface = interface
        self.name = name

    def _getCelebrityByName(self, utility):
        """Find the celebrity by name."""
        return utility.getByName(self.name)

    def _isRightCelebrity(self, celebrity):
        """Is this the celebrity we were looking for?"""
        return celebrity.name == self.name

    def __get__(self, instance, cls=None):
        if instance is None:
            return self

        utility = getUtility(self.interface)
        if self.id is None:
            try:
                celebrity = self._getCelebrityByName(utility)
                if celebrity is None:
                    raise MissingCelebrityError(self.name)
            except NotFoundError:
                raise MissingCelebrityError(self.name)
            self.id = celebrity.id
        else:
            try:
                celebrity = utility.get(self.id)
                if celebrity is None or not self._isRightCelebrity(celebrity):
                    raise MutatedCelebrityError(self.name)
            except NotFoundError:
                raise MutatedCelebrityError(self.name)
        return celebrity


class PersonCelebrityDescriptor(CelebrityDescriptor):
    """A `CelebrityDescriptor` for celebrities that are people.

    This descriptor maintains a list of names so code can detect
    if a given person is a celebrity for special handling.
    """
    names = set() # Populated by the constructor.

    def __init__(self, name):
        PersonCelebrityDescriptor.names.add(name)
        super(PersonCelebrityDescriptor, self).__init__(IPersonSet, name)


class LanguageCelebrityDescriptor(CelebrityDescriptor):
    """A `CelebrityDescriptor` for celebrities that are languages.

    Unlike most other celebrities, languages are retrieved by code.
    """

    def _getCelebrityByName(self, utility):
        """See `CelebrityDescriptor`."""
        return utility.getLanguageByCode(self.name)

    def _isRightCelebrity(self, celebrity):
        """See `CelebrityDescriptor`."""
        return celebrity.code == self.name


class LaunchpadCelebrities:
    """See `ILaunchpadCelebrities`."""
    implements(ILaunchpadCelebrities)

    admin = PersonCelebrityDescriptor('admins')
    bazaar_experts = PersonCelebrityDescriptor('bazaar-experts')
    software_center_agent = PersonCelebrityDescriptor(
        'software-center-agent')
    bug_importer = PersonCelebrityDescriptor('bug-importer')
    bug_watch_updater = PersonCelebrityDescriptor('bug-watch-updater')
    buildd_admin = PersonCelebrityDescriptor('launchpad-buildd-admins')
    commercial_admin = PersonCelebrityDescriptor('commercial-admins')
    debbugs = CelebrityDescriptor(IBugTrackerSet, 'debbugs')
    debian = CelebrityDescriptor(IDistributionSet, 'debian')
    english = LanguageCelebrityDescriptor(ILanguageSet, 'en')
    gnome_bugzilla = CelebrityDescriptor(IBugTrackerSet, 'gnome-bugs')
    hwdb_team = PersonCelebrityDescriptor('hwdb-team')
    janitor = PersonCelebrityDescriptor('janitor')
    katie = PersonCelebrityDescriptor('katie')
    launchpad = CelebrityDescriptor(IProductSet, 'launchpad')
    launchpad_beta_testers = PersonCelebrityDescriptor(
        'launchpad-beta-testers')
    launchpad_developers = PersonCelebrityDescriptor('launchpad')
    obsolete_junk = CelebrityDescriptor(IProductSet, 'obsolete-junk')
    ppa_key_guard = PersonCelebrityDescriptor('ppa-key-guard')
    registry_experts = PersonCelebrityDescriptor('registry')
    rosetta_experts = PersonCelebrityDescriptor('rosetta-admins')
    savannah_tracker = CelebrityDescriptor(IBugTrackerSet, 'savannah')
    sourceforge_tracker = CelebrityDescriptor(IBugTrackerSet, 'sf')
    ubuntu = CelebrityDescriptor(IDistributionSet, 'ubuntu')
    ubuntu_branches = PersonCelebrityDescriptor('ubuntu-branches')
    ubuntu_bugzilla = CelebrityDescriptor(IBugTrackerSet, 'ubuntu-bugzilla')
    ubuntu_security = PersonCelebrityDescriptor('ubuntu-security')
    ubuntu_techboard = PersonCelebrityDescriptor('techboard')
    vcs_imports = PersonCelebrityDescriptor('vcs-imports')

    @property
    def ubuntu_archive_mirror(self):
        """See `ILaunchpadCelebrities`."""
        mirror = getUtility(IDistributionMirrorSet).getByHttpUrl(
            'http://archive.ubuntu.com/ubuntu/')
        if mirror is None:
            raise MissingCelebrityError('http://archive.ubuntu.com/ubuntu/')
        assert mirror.isOfficial(), "Main mirror must be an official one."
        return mirror

    @property
    def ubuntu_cdimage_mirror(self):
        """See `ILaunchpadCelebrities`."""
        mirror = getUtility(IDistributionMirrorSet).getByHttpUrl(
            'http://releases.ubuntu.com/')
        if mirror is None:
            raise MissingCelebrityError('http://releases.ubuntu.com/')
        assert mirror.isOfficial(), "Main mirror must be an official one."
        return mirror

    def isCelebrityPerson(self, name):
        return str(name) in PersonCelebrityDescriptor.names