Merge lp:~niemeyer/pyjuju/no-regex-option into lp:pyjuju

Proposed by Gustavo Niemeyer
Status: Merged
Approved by: Kapil Thangavelu
Approved revision: 337
Merged at revision: 337
Proposed branch: lp:~niemeyer/pyjuju/no-regex-option
Merge into: lp:pyjuju
Diff against target: 267 lines (+85/-62)
6 files modified
ensemble/formula/config.py (+31/-27)
ensemble/formula/tests/repository/dummy/config.yaml (+3/-3)
ensemble/formula/tests/repository/wordpress/config.yaml (+1/-1)
ensemble/formula/tests/test_config.py (+48/-29)
examples/php/config.yaml (+1/-1)
examples/wordpress/config.yaml (+1/-1)
To merge this branch: bzr merge lp:~niemeyer/pyjuju/no-regex-option
Reviewer Review Type Date Requested Status
Kapil Thangavelu (community) Approve
William Reade (community) Approve
Review via email: mp+73448@code.launchpad.net

Description of the change

This handles the two changes suggested in the mailing list:

- Drop regex type
- Rename 'str' to 'string'

For the second change, it also introduces backwards compatibility logic
so that we can continue to work with 'str' for the moment while we warn
authors to move out of it.

To post a comment you must log in.
lp:~niemeyer/pyjuju/no-regex-option updated
337. By Gustavo Niemeyer

Fixed examples.

Revision history for this message
William Reade (fwereade) wrote :

[0]

+ _warned_str_is_obsolete = False

Unused?

Otherwise +1.

review: Approve
Revision history for this message
Kapil Thangavelu (hazmat) wrote :

This is modifying a global variable when a deprecated usage is found, but multiple formulas may be utilized in a single command (deploy with auto-resolve against the ppa, or --with syntax for example), and in that case deprecated usage by additional formulas would go un-logged. perhaps keying to config path in a class dict, or just yagni for now

review: Needs Information
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Note that the message doesn't really specify which formula is using the obsoleted format. My idea with that is that people would review all of their formulas once they get the warning, while at the same time we can avoid pestering too much poor unrelated users that can't do anything about it.

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

hmmm.. i almost think its worth changing the constructor to include the formula name for this case. Giving warnings on potentially third-party formulas isn't particularly useful without identifying the formula in question imo. The primary role i'm concerned about isn't the formula-author but the ensemble user consuming formulas. All that said, at this point those roles are a perfect intersection.

review: Approve
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

I agree with all of your points, and also that it feels a bit early in the game to be worrying too much about this. I hope all formulas get fixed ASAP so that we can obsolete the option completely.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ensemble/formula/config.py'
2--- ensemble/formula/config.py 2011-08-24 00:18:27 +0000
3+++ ensemble/formula/config.py 2011-08-30 21:40:24 +0000
4@@ -1,5 +1,5 @@
5+import sys
6 import os
7-import re
8
9 import yaml
10
11@@ -7,29 +7,24 @@
12 Constant, OneOf, Int, Float)
13 from ensemble.formula.errors import ServiceConfigError
14
15-# regex type requires validator
16-REGEX_TYPE_SCHEMA = KeyDict({"type": Constant("regex"),
17- "validator": String(),
18- "default": OneOf(String(), Int(), Float()),
19- "description": String(),
20- },
21- optional=["default", "description"])
22-
23-SIMPLE_TYPE_SCHEMA = KeyDict({"type": OneOf(Constant("str"),
24- Constant("int"),
25- Constant("float")),
26- "default": OneOf(String(), Int(), Float()),
27- "description": String(),
28- },
29- optional=["default", "description"])
30+OPTION_SCHEMA = KeyDict({
31+ "type": OneOf(Constant("string"),
32+ Constant("str"), # Obsolete
33+ Constant("int"),
34+ Constant("float")),
35+ "default": OneOf(String(), Int(), Float()),
36+ "description": String(),
37+ },
38+ optional=["default", "description"],
39+ )
40
41 # Schema used to validate ConfigOptions specifications
42 CONFIG_SCHEMA = KeyDict({
43- "options": Dict(
44- String(),
45- OneOf(REGEX_TYPE_SCHEMA, SIMPLE_TYPE_SCHEMA))
46+ "options": Dict(String(), OPTION_SCHEMA),
47 })
48
49+WARNED_STR_IS_OBSOLETE = False
50+
51
52 class ConfigOptions(object):
53 """Represents the configuration options exposed by a formula.
54@@ -90,6 +85,8 @@
55 data = self.parse_serialization_data(raw_data, pathname)
56 self._data = data
57
58+ _warned_str_is_obsolete = False
59+
60 def parse_serialization_data(self, data, pathname=None):
61 """Verify we have sensible option metadata.
62
63@@ -108,11 +105,23 @@
64 raise ServiceConfigError("Invalid options specification: %s%s" % (
65 error, pathname))
66
67+ # XXX Drop this after everyone has migrated their config to 'string'.
68+ global WARNED_STR_IS_OBSOLETE
69+ if not WARNED_STR_IS_OBSOLETE:
70+ for name, info in data["options"].iteritems():
71+ for field, value in info.iteritems():
72+ if field == "type" and value == "str":
73+ sys.stderr.write(
74+ "WARNING: Formula is using obsolete 'str' type "
75+ "in config.yaml. Rename it to 'string'.\n")
76+ WARNED_STR_IS_OBSOLETE = True
77+ break
78+
79 return data["options"]
80
81 def _validate_one(self, name, value):
82 # see if there is a type associated with the option
83- kind = self._data[name].get("type", "str")
84+ kind = self._data[name].get("type", "string")
85 if kind not in validation_kinds:
86 raise ServiceConfigError(
87 "Unknown service option type: %s" % kind)
88@@ -179,15 +188,10 @@
89 except ValueError:
90 return value, False
91
92-
93-def validate_regex(value, options):
94- pattern = options["validator"]
95- return value, re.match(pattern, value)
96-
97 # maps service option types to callables
98 validation_kinds = {
99- "str": validate_str,
100+ "string": validate_str,
101+ "str": validate_str, # Obsolete
102 "int": validate_int,
103 "float": validate_float,
104- "regex": validate_regex
105 }
106
107=== modified file 'ensemble/formula/tests/repository/dummy/config.yaml'
108--- ensemble/formula/tests/repository/dummy/config.yaml 2011-08-24 00:18:27 +0000
109+++ ensemble/formula/tests/repository/dummy/config.yaml 2011-08-30 21:40:24 +0000
110@@ -1,5 +1,5 @@
111 options:
112- title: {default: My Title, description: A descriptive title used for the service., type: str}
113- outlook: {description: No default outlook., type: str}
114- username: {default: admin001, description: The name of the initial account (given admin permissions)., type: regex, validator: '\w{8}'}
115+ title: {default: My Title, description: A descriptive title used for the service., type: string}
116+ outlook: {description: No default outlook., type: string}
117+ username: {default: admin001, description: The name of the initial account (given admin permissions)., type: string}
118 skill-level: {description: A number indicating skill., type: int}
119
120=== modified file 'ensemble/formula/tests/repository/wordpress/config.yaml'
121--- ensemble/formula/tests/repository/wordpress/config.yaml 2011-05-31 23:07:35 +0000
122+++ ensemble/formula/tests/repository/wordpress/config.yaml 2011-08-30 21:40:24 +0000
123@@ -1,3 +1,3 @@
124 options:
125- blog-title: {default: My Title, description: A descriptive title used for the blog., type: str}
126+ blog-title: {default: My Title, description: A descriptive title used for the blog., type: string}
127
128
129=== modified file 'ensemble/formula/tests/test_config.py'
130--- ensemble/formula/tests/test_config.py 2011-08-26 12:24:00 +0000
131+++ ensemble/formula/tests/test_config.py 2011-08-30 21:40:24 +0000
132@@ -1,16 +1,30 @@
133 # -*- encoding: utf-8 -*-
134+from StringIO import StringIO
135+import sys
136+
137 import yaml
138
139 from ensemble.lib.testing import TestCase
140 from ensemble.formula.config import ConfigOptions
141 from ensemble.formula.errors import ServiceConfigError
142+from ensemble.formula import config
143
144 sample_configuration = """
145 options:
146- title: {default: My Title, description: A descriptive title used for the service., type: str}
147- outlook: {description: No default outlook., type: str}
148- username: {default: admin001, description: The name of the initial account (given admin permissions)., type: regex, validator: '\w{8}'}
149- skill-level: {description: A number indicating skill., type: int}
150+ title:
151+ default: My Title
152+ description: A descriptive title used for the service.
153+ type: string
154+ outlook:
155+ description: No default outlook.
156+ type: string
157+ username:
158+ default: admin001
159+ description: The name of the initial account (given admin permissions).
160+ type: string
161+ skill-level:
162+ description: A number indicating skill.
163+ type: int
164 """
165
166 sample_yaml_data = yaml.load(sample_configuration)
167@@ -79,8 +93,7 @@
168 self.config.parse(sample_configuration)
169 data = self.config.validate(sample_input)
170
171- # This should include an overridden value, a default and a new
172- # value.
173+ # This should include an overridden value, a default and a new value.
174 self.assertEqual(data,
175 {"username": "admin001",
176 "outlook": "Peachy",
177@@ -88,7 +101,8 @@
178
179 # now try to set a value outside the expected
180 sample_input["bad"] = "value"
181- error = self.assertRaises(ServiceConfigError, self.config.validate, sample_input)
182+ error = self.assertRaises(ServiceConfigError,
183+ self.config.validate, sample_input)
184 self.assertEqual(error.message,
185 "bad is not a valid configuration option.")
186
187@@ -97,28 +111,6 @@
188 config = ConfigOptions()
189 self.assertRaises(ServiceConfigError, config.validate, sample_input)
190
191- def test_validate_regex(self):
192- self.config.parse(sample_configuration)
193-
194- error = self.assertRaises(ServiceConfigError,
195- self.config.validate, dict(username="1234"))
196- self.assertEquals(str(error), "Invalid value for username: 1234")
197-
198- data = self.config.validate(dict(username="12345678"))
199- self.assertEqual(data, {"username": "12345678", "title": "My Title"})
200-
201- def test_validate_regex_wo_validator(self):
202- """Assure that validator is required for type=regex."""
203- local_configuration = sample_configuration + \
204- """ fails: {description: missing required., type: regex}"""
205- error = self.assertRaises(ServiceConfigError,
206- self.config.parse,
207- local_configuration)
208- self.assertEquals(
209- str(error),
210- "Invalid options specification: options.fails.validator: "
211- "required value not found")
212-
213 def test_validate_types(self):
214 self.config.parse(sample_configuration)
215
216@@ -131,3 +123,30 @@
217 self.assertEqual(data, {"skill-level": 9001,
218 "title": "My Title",
219 "username": "admin001"})
220+
221+ def test_validate_with_obsolete_str(self):
222+ """
223+ Test the handling for the obsolete 'str' option type (it's
224+ 'string' now). Remove support for it after a while, and take
225+ this test with it.
226+ """
227+ config = yaml.load(sample_configuration)
228+ config["options"]["title"]["type"] = "str"
229+ obsolete_config = yaml.dump(config)
230+
231+ sio = StringIO()
232+ self.patch(sys, "stderr", sio)
233+
234+ self.config.parse(obsolete_config)
235+ data = self.config.validate({"title": "Helpful Title"})
236+ self.assertEqual(data["title"], "Helpful Title")
237+ self.assertIn("obsolete 'str'", sio.getvalue())
238+
239+ # Trying it again, it should not warn since we don't want
240+ # to pester the formula author.
241+ sio.truncate(0)
242+ self.config.parse(obsolete_config)
243+ data = self.config.validate({"title": "Helpful Title"})
244+ self.assertEqual(data["title"], "Helpful Title")
245+ self.assertEqual(sio.getvalue(), "")
246+
247
248=== modified file 'examples/php/config.yaml'
249--- examples/php/config.yaml 2011-07-01 11:40:20 +0000
250+++ examples/php/config.yaml 2011-08-30 21:40:24 +0000
251@@ -1,5 +1,5 @@
252 options:
253 application_file:
254 description: An application file to push.
255- type: str
256+ type: string
257
258
259=== modified file 'examples/wordpress/config.yaml'
260--- examples/wordpress/config.yaml 2011-06-13 20:33:35 +0000
261+++ examples/wordpress/config.yaml 2011-08-30 21:40:24 +0000
262@@ -3,5 +3,5 @@
263 description: |
264 The URL of a wordpress plugin which will be installed and made available in the Admin UI.
265 http://downloads.wordpress.org/plugin/buddypress.1.2.8.zip is a valid example.
266- type: str
267+ type: string
268

Subscribers

People subscribed via source and target branches

to status/vote changes: