Merge lp:~jtv/maas/maascli-profile-module into lp:maas/trunk

Proposed by Jeroen T. Vermeulen on 2012-10-08
Status: Merged
Approved by: Jeroen T. Vermeulen on 2012-10-08
Approved revision: 1221
Merged at revision: 1222
Proposed branch: lp:~jtv/maas/maascli-profile-module
Merge into: lp:maas/trunk
Diff against target: 134 lines (+125/-0)
2 files modified
src/maascli/profile.py (+66/-0)
src/maascli/tests/test_profile.py (+59/-0)
To merge this branch: bzr merge lp:~jtv/maas/maascli-profile-module
Reviewer Review Type Date Requested Status
Gavin Panella (community) 2012-10-08 Approve on 2012-10-08
Review via email: mp+128510@code.launchpad.net

Commit Message

Small maascli module for getting profiles.

Description of the Change

These will be needed for the main branch I'm working on. I'm putting these up in smaller chunks so that a human can reasonably review them.

Once this work is complete, when you only have one profile defined in maascli, it will default to operating on that profile. Specifying a profile will be optional in that case. The helpers you see here help the code figure out which profile to use. Several places in the code will need this information. And with varying consequences, which is why there isn't much intelligence in this module: additional cleverness here might suit one use-case but complicate another.

You may wonder why the weirdness with islice() in name_default_profile. It probably won't matter since the number of profiles will probably always be tiny, but this spelling retrieves exactly the necessary information without retrieving much more from the profile-config backing store: whether there are zero, one, or more profiles and if there is exactly one, its contents.

Jeroen

To post a comment you must log in.
Gavin Panella (allenap) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/maascli/profile.py'
2--- src/maascli/profile.py 1970-01-01 00:00:00 +0000
3+++ src/maascli/profile.py 2012-10-08 14:49:22 +0000
4@@ -0,0 +1,66 @@
5+# Copyright 2012 Canonical Ltd. This software is licensed under the
6+# GNU Affero General Public License version 3 (see the file LICENSE).
7+
8+"""Profile-related functionality."""
9+
10+from __future__ import (
11+ absolute_import,
12+ print_function,
13+ unicode_literals,
14+ )
15+
16+__metaclass__ = type
17+__all__ = [
18+ 'get_profile',
19+ 'select_profile',
20+ ]
21+
22+from itertools import islice
23+
24+
25+class InvalidProfile(Exception):
26+ """Unknown profile specified."""
27+
28+
29+def get_profile(profiles, profile_name):
30+ """Look up the named profile in `profiles`.
31+
32+ :param profiles: The result of `ProfileConfig.open()`.
33+ :param profile_name: The profile requested by the user.
34+ :return: The `ProfileConfig` option for the requested profile.
35+ :raise InvalidProfile: Requested profile was not found.
36+ """
37+ if profile_name not in profiles:
38+ raise InvalidProfile("'%s' is not an active profile." % profile_name)
39+ return profiles[profile_name]
40+
41+
42+def name_default_profile(profiles):
43+ """Return name of the default profile, or raise `NoDefaultProfile`.
44+
45+ :param profiles: The result of `ProfileConfig.open()`.
46+ :return: The name of the default profile, or None if there is no
47+ reasonable default.
48+ """
49+ profiles_sample = list(islice(profiles, 2))
50+ if len(profiles_sample) == 1:
51+ # There's exactly one profile. That makes a sensible default.
52+ return profiles_sample[0]
53+
54+ return None
55+
56+
57+def select_profile(profiles, profile_name=None):
58+ """Return name for the applicable profile: the given name, or the default.
59+
60+ :param profiles: The result of `ProfileConfig.open()`.
61+ :param profile_name: The profile requested by the user, if any. This may
62+ be `None`, in which case `select_profile` will look for a sensible
63+ default to use.
64+ :return: Name of the applicable profile, or `None` if no profile was
65+ explicitly requested and no sensible default presents itself.
66+ """
67+ if profile_name is None:
68+ return name_default_profile(profiles)
69+ else:
70+ return profile_name
71
72=== added file 'src/maascli/tests/test_profile.py'
73--- src/maascli/tests/test_profile.py 1970-01-01 00:00:00 +0000
74+++ src/maascli/tests/test_profile.py 2012-10-08 14:49:22 +0000
75@@ -0,0 +1,59 @@
76+# Copyright 2012 Canonical Ltd. This software is licensed under the
77+# GNU Affero General Public License version 3 (see the file LICENSE).
78+
79+"""Tests for `maascli.profile`."""
80+
81+from __future__ import (
82+ absolute_import,
83+ print_function,
84+ unicode_literals,
85+ )
86+
87+__metaclass__ = type
88+__all__ = []
89+
90+from maascli.profile import (
91+ get_profile,
92+ InvalidProfile,
93+ name_default_profile,
94+ select_profile,
95+ )
96+from maascli.testing.config import make_configs
97+from maastesting.factory import factory
98+from maastesting.testcase import TestCase
99+
100+
101+class TestProfile(TestCase):
102+
103+ def test_get_profile_finds_profile(self):
104+ profiles = make_configs()
105+ [name] = profiles.keys()
106+ self.assertEqual(profiles[name], get_profile(profiles, name))
107+
108+ def test_get_profile_raises_if_not_found(self):
109+ profiles = make_configs()
110+ self.assertRaises(
111+ InvalidProfile,
112+ get_profile, profiles, factory.make_name('nonexistent-profile'))
113+
114+ def test_name_default_profile_picks_single_profile(self):
115+ profiles = make_configs(1)
116+ [name] = profiles.keys()
117+ self.assertEqual(name, name_default_profile(profiles))
118+
119+ def test_name_default_profile_returns_None_if_no_profile_found(self):
120+ self.assertIsNone(name_default_profile(make_configs(0)))
121+
122+ def test_name_default_profile_returns_None_if_multiple_profiles(self):
123+ profiles = make_configs(2)
124+ self.assertIsNone(name_default_profile(profiles))
125+
126+ def test_select_profile_returns_named_profile(self):
127+ profiles = make_configs(3)
128+ profile_name = profiles.keys()[1]
129+ self.assertEqual(profile_name, select_profile(profiles, profile_name))
130+
131+ def test_select_profile_selects_default_if_no_profile_named(self):
132+ profiles = make_configs(1)
133+ [name] = profiles.keys()
134+ self.assertEqual(name, select_profile(profiles))