)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":6722,"name":"ChangBo Guo(gcb)","email":"guochbo@cn.ibm.com","username":"guochbo"},"change_message_id":"8911a73c9786dbb9af4e057acf6848500276ee22","unresolved":false,"context_lines":[{"line_number":8,"context_line":""},{"line_number":9,"context_line":"Currently oslo.config supports only basic set of types:"},{"line_number":10,"context_line":"strings, integers, floats, booleans, lists of strings and"},{"line_number":11,"context_line":"dictionaries of strings. The reason of existance of types"},{"line_number":12,"context_line":"other than string is to provide some kind of value"},{"line_number":13,"context_line":"validation (e.g. that integer contains only digits)."},{"line_number":14,"context_line":"There is a demand in community to have more complex value"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":12,"id":"AAAATn%2F969c%3D","line":11,"updated":"2013-12-04 13:52:21.000000000","message":"s/existance/existence","commit_id":"526fbd41f7d13622b11a429f83952790a1107c86"},{"author":{"_account_id":6722,"name":"ChangBo Guo(gcb)","email":"guochbo@cn.ibm.com","username":"guochbo"},"change_message_id":"8911a73c9786dbb9af4e057acf6848500276ee22","unresolved":false,"context_lines":[{"line_number":17,"context_line":"conforms to ip address format, etc."},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"This patch introduces support for custom Opt types that"},{"line_number":20,"context_line":"will allow a better control of value convertation and"},{"line_number":21,"context_line":"validation. Custom types are presented as type"},{"line_number":22,"context_line":"constructors - callable objects that take a string and"},{"line_number":23,"context_line":"either return converted value or raise ValueError in"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":12,"id":"AAAATn%2F9680%3D","line":20,"updated":"2013-12-04 13:52:21.000000000","message":"s/convertation/conversion/","commit_id":"526fbd41f7d13622b11a429f83952790a1107c86"},{"author":{"_account_id":6722,"name":"ChangBo Guo(gcb)","email":"guochbo@cn.ibm.com","username":"guochbo"},"change_message_id":"8911a73c9786dbb9af4e057acf6848500276ee22","unresolved":false,"context_lines":[{"line_number":28,"context_line":"usage of parametriezed type class instances)."},{"line_number":29,"context_line":""},{"line_number":30,"context_line":"Oslo.config has Opt subclasses that control value"},{"line_number":31,"context_line":"convertation and validation: StrOpt, IntOpt, FloatOpt, etc."},{"line_number":32,"context_line":"The type information is added to the base class - Opt -"},{"line_number":33,"context_line":"cause otherwise it would introduce confusion on what class"},{"line_number":34,"context_line":"to use when introducing e.g. NetworkPort: StrOpt or IntOpt."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":12,"id":"AAAATn%2F968s%3D","line":31,"updated":"2013-12-04 13:52:21.000000000","message":"s/convertation/conversion/","commit_id":"526fbd41f7d13622b11a429f83952790a1107c86"}],"oslo/config/cfg.py":[{"author":{"_account_id":1531,"name":"Ilya Kharin","email":"akscram@gmail.com","username":"akscram"},"change_message_id":"9c73dd55fa83cef0e6532f86861efbbab0f586e5","unresolved":false,"context_lines":[{"line_number":288,"context_line":"from six import moves"},{"line_number":289,"context_line":""},{"line_number":290,"context_line":"from oslo.config import iniparser"},{"line_number":291,"context_line":"from oslo.config.types import String, Boolean, Integer, Float, List, Dict"},{"line_number":292,"context_line":""},{"line_number":293,"context_line":"LOG \u003d logging.getLogger(__name__)"},{"line_number":294,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"AAAATn%2F%2BQGs%3D","line":291,"updated":"2013-11-28 10:20:33.000000000","message":"According to the HACKING (http://docs.openstack.org/developer/hacking/#imports) you should not import objects, just modules.","commit_id":"d149acb524f548c966f8a8cf9bef8e62dc50d225"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"8fbfe5f2eb5c6f72d3effe7a1f90be827258679e","unresolved":false,"context_lines":[{"line_number":532,"context_line":""},{"line_number":533,"context_line":"      name:"},{"line_number":534,"context_line":"        the name of the option, which may include hyphens"},{"line_number":535,"context_line":"      type:"},{"line_number":536,"context_line":"        a callable class or object that takes string and returns"},{"line_number":537,"context_line":"        converted and validated value"},{"line_number":538,"context_line":"      dest:"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BK1A%3D","line":535,"updated":"2013-11-30 00:13:51.000000000","message":"The argument name should be something other than \"type\" to avoid overriding the builtin of that name.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"191e428d118ab953c522649c96c125e78b957c08","unresolved":false,"context_lines":[{"line_number":532,"context_line":""},{"line_number":533,"context_line":"      name:"},{"line_number":534,"context_line":"        the name of the option, which may include hyphens"},{"line_number":535,"context_line":"      type:"},{"line_number":536,"context_line":"        a callable class or object that takes string and returns"},{"line_number":537,"context_line":"        converted and validated value"},{"line_number":538,"context_line":"      dest:"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKwA%3D","line":535,"in_reply_to":"AAAATn%2F%2BK1A%3D","updated":"2013-11-30 01:04:20.000000000","message":"The word \u0027type\u0027 is a perfect match for that and it doesn\u0027t interfere with other stuff. Also, argparse library uses exactly the same word for specifying argument types.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"8fbfe5f2eb5c6f72d3effe7a1f90be827258679e","unresolved":false,"context_lines":[{"line_number":550,"context_line":"    \"\"\""},{"line_number":551,"context_line":"    multi \u003d False"},{"line_number":552,"context_line":""},{"line_number":553,"context_line":"    def __init__(self, name, type, dest\u003dNone, short\u003dNone, default\u003dNone,"},{"line_number":554,"context_line":"                 positional\u003dFalse, metavar\u003dNone, help\u003dNone,"},{"line_number":555,"context_line":"                 secret\u003dFalse, required\u003dFalse,"},{"line_number":556,"context_line":"                 deprecated_name\u003dNone, deprecated_group\u003dNone,"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BK1M%3D","line":553,"updated":"2013-11-30 00:13:51.000000000","message":"Do we need the API for this class to be backwards compatible? Does anyone use Opt directly?","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"dbdc3047b0f801ab2c27e0203f94eec0adcf94a7","unresolved":false,"context_lines":[{"line_number":550,"context_line":"    \"\"\""},{"line_number":551,"context_line":"    multi \u003d False"},{"line_number":552,"context_line":""},{"line_number":553,"context_line":"    def __init__(self, name, type, dest\u003dNone, short\u003dNone, default\u003dNone,"},{"line_number":554,"context_line":"                 positional\u003dFalse, metavar\u003dNone, help\u003dNone,"},{"line_number":555,"context_line":"                 secret\u003dFalse, required\u003dFalse,"},{"line_number":556,"context_line":"                 deprecated_name\u003dNone, deprecated_group\u003dNone,"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKx0%3D","line":553,"in_reply_to":"AAAATn%2F%2BK1M%3D","updated":"2013-11-30 00:50:53.000000000","message":"I\u0027m have not seen any usages of that class directly although it seems like it should behave like a StrOpt.\n\nWe can provide a default so that it won\u0027t break backward compatibility.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"2b8aec4af46dd03ae8b98173176f7f5103b22db4","unresolved":false,"context_lines":[{"line_number":550,"context_line":"    \"\"\""},{"line_number":551,"context_line":"    multi \u003d False"},{"line_number":552,"context_line":""},{"line_number":553,"context_line":"    def __init__(self, name, type, dest\u003dNone, short\u003dNone, default\u003dNone,"},{"line_number":554,"context_line":"                 positional\u003dFalse, metavar\u003dNone, help\u003dNone,"},{"line_number":555,"context_line":"                 secret\u003dFalse, required\u003dFalse,"},{"line_number":556,"context_line":"                 deprecated_name\u003dNone, deprecated_group\u003dNone,"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BJoo%3D","line":553,"in_reply_to":"AAAATn%2F%2BKx0%3D","updated":"2013-12-01 19:25:59.000000000","message":"Done","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"8fbfe5f2eb5c6f72d3effe7a1f90be827258679e","unresolved":false,"context_lines":[{"line_number":579,"context_line":""},{"line_number":580,"context_line":"        if inspect.isclass(type):"},{"line_number":581,"context_line":"            type \u003d type()"},{"line_number":582,"context_line":"        if not callable(type):"},{"line_number":583,"context_line":"            raise TypeError(\u0027Type must be callable, got %s\u0027 % repr(type))"},{"line_number":584,"context_line":"        self.type \u003d type"},{"line_number":585,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BK1I%3D","line":582,"updated":"2013-11-30 00:13:51.000000000","message":"If a class is passed in, it is being converted to an instance on the previous line. So the value tested here is not the same as the value passed in.\n\nWhat constraint are you trying to enforce here? Is it not reasonable to pass a class? For example, could I not pass int as the type, or do I have to use one of the provided type classes?","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"dbdc3047b0f801ab2c27e0203f94eec0adcf94a7","unresolved":false,"context_lines":[{"line_number":579,"context_line":""},{"line_number":580,"context_line":"        if inspect.isclass(type):"},{"line_number":581,"context_line":"            type \u003d type()"},{"line_number":582,"context_line":"        if not callable(type):"},{"line_number":583,"context_line":"            raise TypeError(\u0027Type must be callable, got %s\u0027 % repr(type))"},{"line_number":584,"context_line":"        self.type \u003d type"},{"line_number":585,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKxo%3D","line":582,"in_reply_to":"AAAATn%2F%2BK1I%3D","updated":"2013-11-30 00:50:53.000000000","message":"The code is correct. We need object that should be callable. If you pass a class, fine, we instantiate it ourselves, but class instance should be callable.\n\nYes, you can pass \"int\" builtin function.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"2b8aec4af46dd03ae8b98173176f7f5103b22db4","unresolved":false,"context_lines":[{"line_number":579,"context_line":""},{"line_number":580,"context_line":"        if inspect.isclass(type):"},{"line_number":581,"context_line":"            type \u003d type()"},{"line_number":582,"context_line":"        if not callable(type):"},{"line_number":583,"context_line":"            raise TypeError(\u0027Type must be callable, got %s\u0027 % repr(type))"},{"line_number":584,"context_line":"        self.type \u003d type"},{"line_number":585,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BJnY%3D","line":582,"in_reply_to":"AAAATn%2F%2BKJ4%3D","updated":"2013-12-01 19:25:59.000000000","message":"Done","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"83976722f16da11bc5fb76c1cdbb8570138a2534","unresolved":false,"context_lines":[{"line_number":579,"context_line":""},{"line_number":580,"context_line":"        if inspect.isclass(type):"},{"line_number":581,"context_line":"            type \u003d type()"},{"line_number":582,"context_line":"        if not callable(type):"},{"line_number":583,"context_line":"            raise TypeError(\u0027Type must be callable, got %s\u0027 % repr(type))"},{"line_number":584,"context_line":"        self.type \u003d type"},{"line_number":585,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKJ4%3D","line":582,"in_reply_to":"AAAATn%2F%2BKxo%3D","updated":"2013-11-30 16:44:19.000000000","message":"inspect.isclass(int) returns True, so if I pass int as the type then on line 581 the int will be called and type will be set to 0. THen on line 582 the test will report false, and on line 583 an exception will be raised.\n\nConverting the class passed in to an instance makes the API more confusing. Let\u0027s stick with requiring a callable and just invoking it. To do that, I think the only change is to remove lines 580-581 and then ensure that all callers pass an instance of one of the special type classes you have defined.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"}],"oslo/config/types.py":[{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"8fbfe5f2eb5c6f72d3effe7a1f90be827258679e","unresolved":false,"context_lines":[{"line_number":64,"context_line":"            return value"},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        s \u003d value.lower()"},{"line_number":67,"context_line":"        if s in [\u0027true\u0027, \u00271\u0027, \u0027on\u0027, \u0027yes\u0027]:"},{"line_number":68,"context_line":"            return True"},{"line_number":69,"context_line":"        elif s in [\u0027false\u0027, \u00270\u0027, \u0027off\u0027, \u0027no\u0027]:"},{"line_number":70,"context_line":"            return False"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BK0o%3D","line":67,"updated":"2013-11-30 00:13:51.000000000","message":"This conditional could be converted to a single lookup if Boolean had a class attribute with a dictionary mapping the true values to True and the false values to False.\n\n  s \u003d value.lower()\n  if s not in self._CONVERSIONS:\n    raise ValueError(...)\n  return self._CONVERSIONS[s]","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"dbdc3047b0f801ab2c27e0203f94eec0adcf94a7","unresolved":false,"context_lines":[{"line_number":64,"context_line":"            return value"},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        s \u003d value.lower()"},{"line_number":67,"context_line":"        if s in [\u0027true\u0027, \u00271\u0027, \u0027on\u0027, \u0027yes\u0027]:"},{"line_number":68,"context_line":"            return True"},{"line_number":69,"context_line":"        elif s in [\u0027false\u0027, \u00270\u0027, \u0027off\u0027, \u0027no\u0027]:"},{"line_number":70,"context_line":"            return False"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKzQ%3D","line":67,"in_reply_to":"AAAATn%2F%2BK0o%3D","updated":"2013-11-30 00:50:53.000000000","message":"That\u0027s true, but then I would need to type words \"True\" and \"False\" 4 times:\n\n_CONVERSIONS \u003d {\n  \u0027true\u0027: True, \u00271\u0027: True, \u0027on\u0027: True, ...\n}","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"fbad77e95b10a4dea4c504e25182175d4b0c68b1","unresolved":false,"context_lines":[{"line_number":64,"context_line":"            return value"},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        s \u003d value.lower()"},{"line_number":67,"context_line":"        if s in [\u0027true\u0027, \u00271\u0027, \u0027on\u0027, \u0027yes\u0027]:"},{"line_number":68,"context_line":"            return True"},{"line_number":69,"context_line":"        elif s in [\u0027false\u0027, \u00270\u0027, \u0027off\u0027, \u0027no\u0027]:"},{"line_number":70,"context_line":"            return False"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKF4%3D","line":67,"in_reply_to":"AAAATn%2F%2BKJ0%3D","updated":"2013-11-30 19:36:56.000000000","message":"So I would prefer readability over performance here.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"83976722f16da11bc5fb76c1cdbb8570138a2534","unresolved":false,"context_lines":[{"line_number":64,"context_line":"            return value"},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        s \u003d value.lower()"},{"line_number":67,"context_line":"        if s in [\u0027true\u0027, \u00271\u0027, \u0027on\u0027, \u0027yes\u0027]:"},{"line_number":68,"context_line":"            return True"},{"line_number":69,"context_line":"        elif s in [\u0027false\u0027, \u00270\u0027, \u0027off\u0027, \u0027no\u0027]:"},{"line_number":70,"context_line":"            return False"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKJ0%3D","line":67,"in_reply_to":"AAAATn%2F%2BKzQ%3D","updated":"2013-11-30 16:44:19.000000000","message":"So?","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"8fbfe5f2eb5c6f72d3effe7a1f90be827258679e","unresolved":false,"context_lines":[{"line_number":110,"context_line":"        return v"},{"line_number":111,"context_line":""},{"line_number":112,"context_line":"    def __repr__(self):"},{"line_number":113,"context_line":"        return \u0027Integer\u0027"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":""},{"line_number":116,"context_line":"class Float(object):"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BK0c%3D","line":113,"updated":"2013-11-30 00:13:51.000000000","message":"Shouldn\u0027t the repr include the valid range?","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"dbdc3047b0f801ab2c27e0203f94eec0adcf94a7","unresolved":false,"context_lines":[{"line_number":110,"context_line":"        return v"},{"line_number":111,"context_line":""},{"line_number":112,"context_line":"    def __repr__(self):"},{"line_number":113,"context_line":"        return \u0027Integer\u0027"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":""},{"line_number":116,"context_line":"class Float(object):"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKy8%3D","line":113,"in_reply_to":"AAAATn%2F%2BK0c%3D","updated":"2013-11-30 00:50:53.000000000","message":"That\u0027s TBD. I added this to make argparse errors more nice. Still in doubt.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"2b8aec4af46dd03ae8b98173176f7f5103b22db4","unresolved":false,"context_lines":[{"line_number":110,"context_line":"        return v"},{"line_number":111,"context_line":""},{"line_number":112,"context_line":"    def __repr__(self):"},{"line_number":113,"context_line":"        return \u0027Integer\u0027"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":""},{"line_number":116,"context_line":"class Float(object):"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BJlI%3D","line":113,"in_reply_to":"AAAATn%2F%2BKy8%3D","updated":"2013-12-01 19:25:59.000000000","message":"Done","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"8fbfe5f2eb5c6f72d3effe7a1f90be827258679e","unresolved":false,"context_lines":[{"line_number":160,"context_line":"        values \u003d s.split(\u0027,\u0027)"},{"line_number":161,"context_line":"        while len(values) \u003e 0:"},{"line_number":162,"context_line":"            value \u003d values.pop(0)"},{"line_number":163,"context_line":"            while True:"},{"line_number":164,"context_line":"                first_error \u003d None"},{"line_number":165,"context_line":"                try:"},{"line_number":166,"context_line":"                    validated_value \u003d self.element_type(value.strip())"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BK0Q%3D","line":163,"updated":"2013-11-30 00:13:51.000000000","message":"I don\u0027t understand this processing. It appears to be converting all of the values, then raising an exception for the first one with an error. Why not save all of the errors, or just raise the exception immediately when it happens?","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"dbdc3047b0f801ab2c27e0203f94eec0adcf94a7","unresolved":false,"context_lines":[{"line_number":160,"context_line":"        values \u003d s.split(\u0027,\u0027)"},{"line_number":161,"context_line":"        while len(values) \u003e 0:"},{"line_number":162,"context_line":"            value \u003d values.pop(0)"},{"line_number":163,"context_line":"            while True:"},{"line_number":164,"context_line":"                first_error \u003d None"},{"line_number":165,"context_line":"                try:"},{"line_number":166,"context_line":"                    validated_value \u003d self.element_type(value.strip())"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKyc%3D","line":163,"in_reply_to":"AAAATn%2F%2BK0Q%3D","updated":"2013-11-30 00:50:53.000000000","message":"You can look at ListTypeTests.test_list_of_values_containing_commas. Basically, if item of list can contain commas, there is no way we can find out whether this comma belongs to item value or is an item separator. This code is a simple workaround for this problem. Otherwise we would need types to provide parsers that can consume just enough chars for them to be complete.\n\nSo, we split on commas and if current item doesn\u0027t validate, we guess that we split inside item value and try to add comma and next item and validate again. If we run out of items and it\u0027s still not valid, we return the first error.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"fbad77e95b10a4dea4c504e25182175d4b0c68b1","unresolved":false,"context_lines":[{"line_number":160,"context_line":"        values \u003d s.split(\u0027,\u0027)"},{"line_number":161,"context_line":"        while len(values) \u003e 0:"},{"line_number":162,"context_line":"            value \u003d values.pop(0)"},{"line_number":163,"context_line":"            while True:"},{"line_number":164,"context_line":"                first_error \u003d None"},{"line_number":165,"context_line":"                try:"},{"line_number":166,"context_line":"                    validated_value \u003d self.element_type(value.strip())"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKF0%3D","line":163,"in_reply_to":"AAAATn%2F%2BKJw%3D","updated":"2013-11-30 19:36:56.000000000","message":"There was no such conversion before, because previously lists could contain only string elements and string values are always valid. \nThis is a new functionality that is bundled with custom types feature.\n\nIt should not cause issues with existing code as lists could contain only string values and strings are always valid.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"83976722f16da11bc5fb76c1cdbb8570138a2534","unresolved":false,"context_lines":[{"line_number":160,"context_line":"        values \u003d s.split(\u0027,\u0027)"},{"line_number":161,"context_line":"        while len(values) \u003e 0:"},{"line_number":162,"context_line":"            value \u003d values.pop(0)"},{"line_number":163,"context_line":"            while True:"},{"line_number":164,"context_line":"                first_error \u003d None"},{"line_number":165,"context_line":"                try:"},{"line_number":166,"context_line":"                    validated_value \u003d self.element_type(value.strip())"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKJw%3D","line":163,"in_reply_to":"AAAATn%2F%2BKyc%3D","updated":"2013-11-30 16:44:19.000000000","message":"Is that the sort of conversion we were doing before? It seems exceptionally tricky and error prone.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"93c10279359095d351b792933b6d8005b3916290","unresolved":false,"context_lines":[{"line_number":21,"context_line":""},{"line_number":22,"context_line":"    String values do not get transformed and are returned as str objects."},{"line_number":23,"context_line":""},{"line_number":24,"context_line":"    :param choices: Optional sequence of valid values."},{"line_number":25,"context_line":"    \"\"\""},{"line_number":26,"context_line":""},{"line_number":27,"context_line":"    def __init__(self, choices\u003dNone, quotes\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAATn%2F90Rk%3D","line":24,"updated":"2013-12-05 16:26:48.000000000","message":"Add a docstring for quotes param here as well, including a description of what the validation actually does... :)","commit_id":"e7551c9aeb99af031dd38e7badcf08feccbf9304"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"f0b5d46db1735cc95dd84dfa43fb02379465d692","unresolved":false,"context_lines":[{"line_number":21,"context_line":""},{"line_number":22,"context_line":"    String values do not get transformed and are returned as str objects."},{"line_number":23,"context_line":""},{"line_number":24,"context_line":"    :param choices: Optional sequence of valid values."},{"line_number":25,"context_line":"    \"\"\""},{"line_number":26,"context_line":""},{"line_number":27,"context_line":"    def __init__(self, choices\u003dNone, quotes\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAATn%2F9zRw%3D","line":24,"in_reply_to":"AAAATn%2F90Rk%3D","updated":"2013-12-05 19:08:06.000000000","message":"Done","commit_id":"e7551c9aeb99af031dd38e7badcf08feccbf9304"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"93c10279359095d351b792933b6d8005b3916290","unresolved":false,"context_lines":[{"line_number":35,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":36,"context_line":"                if value[-1] !\u003d value[0]:"},{"line_number":37,"context_line":"                    raise ValueError(\u0027Non-closed quote: %s\u0027 % value)"},{"line_number":38,"context_line":"                value \u003d value[1:-1]"},{"line_number":39,"context_line":""},{"line_number":40,"context_line":"        if self.choices is None or value in self.choices:"},{"line_number":41,"context_line":"            return value"}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAATn%2F90RU%3D","line":38,"updated":"2013-12-05 16:26:48.000000000","message":"The above will catch some simple quote enclosures, but will not catch others, for example, this string:\n\n \"\u0027\u0027I have a dream\u0027\"\n\nWould pass the validation check above but would lead to a somewhat odd string value.. :) If that is intended, cool, I just figured I would point this out, and you may want to describe the behavior in the docstring above.","commit_id":"e7551c9aeb99af031dd38e7badcf08feccbf9304"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"f0b5d46db1735cc95dd84dfa43fb02379465d692","unresolved":false,"context_lines":[{"line_number":35,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":36,"context_line":"                if value[-1] !\u003d value[0]:"},{"line_number":37,"context_line":"                    raise ValueError(\u0027Non-closed quote: %s\u0027 % value)"},{"line_number":38,"context_line":"                value \u003d value[1:-1]"},{"line_number":39,"context_line":""},{"line_number":40,"context_line":"        if self.choices is None or value in self.choices:"},{"line_number":41,"context_line":"            return value"}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAATn%2F9zQs%3D","line":38,"in_reply_to":"AAAATn%2F90RU%3D","updated":"2013-12-05 19:08:06.000000000","message":"Well, I don\u0027t want to introduce some crazy logic of handling quotes, people probably won\u0027t do it it configs. This is a simple case to allow escaping (or should I say guarding) chars that belong to string from chars related to other types (like in List example).","commit_id":"e7551c9aeb99af031dd38e7badcf08feccbf9304"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"93c10279359095d351b792933b6d8005b3916290","unresolved":false,"context_lines":[{"line_number":225,"context_line":"    Dictionary type values are key:value pairs separated by commas."},{"line_number":226,"context_line":"    The resulting value is a dictionary of these key/value pairs."},{"line_number":227,"context_line":"    Type of dictionary key is always string, but dictionary value"},{"line_number":228,"context_line":"    type can be customized."},{"line_number":229,"context_line":"    \"\"\""},{"line_number":230,"context_line":""},{"line_number":231,"context_line":"    def __init__(self, value_type\u003dNone):"}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAATn%2F90OM%3D","line":228,"updated":"2013-12-05 16:26:48.000000000","message":"Here and for the List class above, did you want to docstring the value_type and item_type params in the same way you did params for the Integer and String classes above?","commit_id":"e7551c9aeb99af031dd38e7badcf08feccbf9304"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"f0b5d46db1735cc95dd84dfa43fb02379465d692","unresolved":false,"context_lines":[{"line_number":225,"context_line":"    Dictionary type values are key:value pairs separated by commas."},{"line_number":226,"context_line":"    The resulting value is a dictionary of these key/value pairs."},{"line_number":227,"context_line":"    Type of dictionary key is always string, but dictionary value"},{"line_number":228,"context_line":"    type can be customized."},{"line_number":229,"context_line":"    \"\"\""},{"line_number":230,"context_line":""},{"line_number":231,"context_line":"    def __init__(self, value_type\u003dNone):"}],"source_content_type":"text/x-python","patch_set":14,"id":"AAAATn%2F9zRs%3D","line":228,"in_reply_to":"AAAATn%2F90OM%3D","updated":"2013-12-05 19:08:06.000000000","message":"Done","commit_id":"e7551c9aeb99af031dd38e7badcf08feccbf9304"},{"author":{"_account_id":7073,"name":"mouad benchchaoui","email":"m.benchchaoui@x-ion.de","username":"mouad"},"change_message_id":"0b1292332b510060e42cc6e48123dcc508fe9614","unresolved":false,"context_lines":[{"line_number":76,"context_line":"            return value"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"        s \u003d value.lower()"},{"line_number":79,"context_line":"        if s in [\u0027true\u0027, \u00271\u0027, \u0027on\u0027, \u0027yes\u0027]:"},{"line_number":80,"context_line":"            return True"},{"line_number":81,"context_line":"        elif s in [\u0027false\u0027, \u00270\u0027, \u0027off\u0027, \u0027no\u0027]:"},{"line_number":82,"context_line":"            return False"}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3YE%3D","line":79,"updated":"2013-12-29 18:09:47.000000000","message":"Consider making this two lists class attribute to not make Python create same list over and over whenever this method is called.","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"b10f0397d37f16baee378800bdb3ada9277a57a0","unresolved":false,"context_lines":[{"line_number":76,"context_line":"            return value"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"        s \u003d value.lower()"},{"line_number":79,"context_line":"        if s in [\u0027true\u0027, \u00271\u0027, \u0027on\u0027, \u0027yes\u0027]:"},{"line_number":80,"context_line":"            return True"},{"line_number":81,"context_line":"        elif s in [\u0027false\u0027, \u00270\u0027, \u0027off\u0027, \u0027no\u0027]:"},{"line_number":82,"context_line":"            return False"}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3TE%3D","line":79,"in_reply_to":"AAAAT3%2F%2F3YE%3D","updated":"2013-12-29 21:46:07.000000000","message":"Done","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"},{"author":{"_account_id":7073,"name":"mouad benchchaoui","email":"m.benchchaoui@x-ion.de","username":"mouad"},"change_message_id":"0b1292332b510060e42cc6e48123dcc508fe9614","unresolved":false,"context_lines":[{"line_number":94,"context_line":""},{"line_number":95,"context_line":"    \"\"\"Integer type."},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"    :param min: Optional check that value is greater than min"},{"line_number":98,"context_line":"    :param max: Optional check that value is less than max"},{"line_number":99,"context_line":"    \"\"\""},{"line_number":100,"context_line":""}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3dM%3D","line":97,"updated":"2013-12-29 18:09:47.000000000","message":"Maybe it should explicitly say \"strictly greater\" ? same for max.","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"b10f0397d37f16baee378800bdb3ada9277a57a0","unresolved":false,"context_lines":[{"line_number":94,"context_line":""},{"line_number":95,"context_line":"    \"\"\"Integer type."},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"    :param min: Optional check that value is greater than min"},{"line_number":98,"context_line":"    :param max: Optional check that value is less than max"},{"line_number":99,"context_line":"    \"\"\""},{"line_number":100,"context_line":""}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3S4%3D","line":97,"in_reply_to":"AAAAT3%2F%2F3dM%3D","updated":"2013-12-29 21:46:07.000000000","message":"Done","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"},{"author":{"_account_id":7073,"name":"mouad benchchaoui","email":"m.benchchaoui@x-ion.de","username":"mouad"},"change_message_id":"0b1292332b510060e42cc6e48123dcc508fe9614","unresolved":false,"context_lines":[{"line_number":104,"context_line":"        self.max \u003d max"},{"line_number":105,"context_line":""},{"line_number":106,"context_line":"    def __call__(self, value):"},{"line_number":107,"context_line":"        if isinstance(value, int):"},{"line_number":108,"context_line":"            return value"},{"line_number":109,"context_line":""},{"line_number":110,"context_line":"        s \u003d value.strip()"}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3dU%3D","line":107,"updated":"2013-12-29 18:09:47.000000000","message":"Should you check whether self.min \u003c value \u003c self.max even if value is an integer ?","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"b10f0397d37f16baee378800bdb3ada9277a57a0","unresolved":false,"context_lines":[{"line_number":104,"context_line":"        self.max \u003d max"},{"line_number":105,"context_line":""},{"line_number":106,"context_line":"    def __call__(self, value):"},{"line_number":107,"context_line":"        if isinstance(value, int):"},{"line_number":108,"context_line":"            return value"},{"line_number":109,"context_line":""},{"line_number":110,"context_line":"        s \u003d value.strip()"}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3Ss%3D","line":107,"in_reply_to":"AAAAT3%2F%2F3dU%3D","updated":"2013-12-29 21:46:07.000000000","message":"Done","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"},{"author":{"_account_id":7073,"name":"mouad benchchaoui","email":"m.benchchaoui@x-ion.de","username":"mouad"},"change_message_id":"0b1292332b510060e42cc6e48123dcc508fe9614","unresolved":false,"context_lines":[{"line_number":111,"context_line":"        if s \u003d\u003d \u0027\u0027:"},{"line_number":112,"context_line":"            return None"},{"line_number":113,"context_line":""},{"line_number":114,"context_line":"        for i, c in enumerate(s):"},{"line_number":115,"context_line":"            if not c.isdigit() and not ((c \u003d\u003d \u0027-\u0027) and (i \u003d\u003d 0)):"},{"line_number":116,"context_line":"                raise ValueError("},{"line_number":117,"context_line":"                    \u0027Only digits are allowed, but found char \"%s\"\u0027 % c)"}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3dY%3D","line":114,"updated":"2013-12-29 18:09:47.000000000","message":"EAFP: Why not just do:\n\ntry:\n    v \u003d int(s)\nexcept (TypeError, ValueError):\n    raise ...\n\nActually i will argue that showing the char that is not a digit is useless and even can be confusing as to what value is wrong.","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"b10f0397d37f16baee378800bdb3ada9277a57a0","unresolved":false,"context_lines":[{"line_number":111,"context_line":"        if s \u003d\u003d \u0027\u0027:"},{"line_number":112,"context_line":"            return None"},{"line_number":113,"context_line":""},{"line_number":114,"context_line":"        for i, c in enumerate(s):"},{"line_number":115,"context_line":"            if not c.isdigit() and not ((c \u003d\u003d \u0027-\u0027) and (i \u003d\u003d 0)):"},{"line_number":116,"context_line":"                raise ValueError("},{"line_number":117,"context_line":"                    \u0027Only digits are allowed, but found char \"%s\"\u0027 % c)"}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3Q0%3D","line":114,"in_reply_to":"AAAAT3%2F%2F3dY%3D","updated":"2013-12-29 21:46:07.000000000","message":"Done","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"},{"author":{"_account_id":7073,"name":"mouad benchchaoui","email":"m.benchchaoui@x-ion.de","username":"mouad"},"change_message_id":"0b1292332b510060e42cc6e48123dcc508fe9614","unresolved":false,"context_lines":[{"line_number":196,"context_line":"            return result"},{"line_number":197,"context_line":""},{"line_number":198,"context_line":"        values \u003d s.split(\u0027,\u0027)"},{"line_number":199,"context_line":"        while len(values) \u003e 0:"},{"line_number":200,"context_line":"            value \u003d values.pop(0)"},{"line_number":201,"context_line":"            while True:"},{"line_number":202,"context_line":"                first_error \u003d None"}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3Zw%3D","line":199,"updated":"2013-12-29 18:09:47.000000000","message":"I think the logic of escaping comma should not be strictly for quotes string only, consider the use case of list of dict:\n\n    ctype \u003d types.List(types.Dict())\n    assert ctype(\"k1:v1,v2:k2\",\"k3:v3\") \u003d\u003d [{\"k1\": \"v1\", \"k2\": \"v2\"}, {\"k3\": \"v3\"}]\n\nThis will not work with the current code, which mean AFAIT there no way to do this right now.\n\nIs this a real use case ? That this make sense ? Or maybe i am missing something obvious !?\n\nIf so how about using escape char \"\\\" to escape comma in the list instead of relaying on quotes, this offer a more common way to escapes comma, easy to parse and also make it easy to  format config option value, check docstest of code below to see what i am trying to say.\n\n   def format_list(s):\n       r\"\"\"\n       \u003e\u003e\u003e test2(\u0027foo,\"bar\\, baz\",bam\u0027)\n       [\u0027foo\u0027, \u0027\"bar, baz\"\u0027, \u0027bam\u0027]\n       \u003e\u003e\u003e test2(\u0027k1:v1\\,v2:k2,k3:v3\u0027)\n       [\u0027k1:v1,v2:k2\u0027, \u0027k3:v3\u0027]\n       \u003e\u003e\u003e test2(\"a\\,b\\\",c,d\")\n       [\u0027a,b\"\u0027, \u0027c\u0027, \u0027d\u0027]\n       \u003e\u003e\u003e test2(\"c\\,d,\")\n       [\u0027c,d\u0027, \u0027\u0027]\n       \u003e\u003e\u003e test2(\",c,d,\")\n       [\u0027\u0027, \u0027c\u0027, \u0027d\u0027, \u0027\u0027]\n\n       \"\"\"\n       i \u003d 0\n       results \u003d []\n       for j, c in enumerate(s):\n          if c \u003d\u003d \u0027,\u0027:\n              if j \u003d\u003d 0 or (j \u003e 0 and s[j-1] !\u003d \u0027\\\\\u0027):\n                results.append(s[i:j].replace(\u0027\\\\\u0027, \u0027\u0027))\n                i \u003d j + 1\n      results.append(s[i:])\n    return results\n\nThoughts ?","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"b10f0397d37f16baee378800bdb3ada9277a57a0","unresolved":false,"context_lines":[{"line_number":196,"context_line":"            return result"},{"line_number":197,"context_line":""},{"line_number":198,"context_line":"        values \u003d s.split(\u0027,\u0027)"},{"line_number":199,"context_line":"        while len(values) \u003e 0:"},{"line_number":200,"context_line":"            value \u003d values.pop(0)"},{"line_number":201,"context_line":"            while True:"},{"line_number":202,"context_line":"                first_error \u003d None"}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3RA%3D","line":199,"in_reply_to":"AAAAT3%2F%2F3Zw%3D","updated":"2013-12-29 21:46:07.000000000","message":"I understand what you are saying, but I do not like having to escape value of some type if it is contained in some other type. You will have an escape mayhem if you will have a \"list of list of strings\" type.\n\nAfter all, checking for value validity can be implemented in child type (e.g. check for hanging \"\\\" char at the end of Dict value will fix your first example).\n\nI suggest adding an optional start/end chars for container types. For List those will be \"[\" and \"]\" and for Dict - \"{\"/\"}\". Then, a List of Dicts will look like \"{k1:v1,v2:k2},{k3:v3}\" and List of List of Strings will look like \"[foo,bar],[\u0027baz,bam\u0027]\" \u003d\u003d [[\"foo\", \"bar\"], [\"baz,bam\"]].","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"a807b51e148230a10d48a950ac9e9a1de24c72bf","unresolved":false,"context_lines":[{"line_number":12,"context_line":"#    distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT"},{"line_number":13,"context_line":"#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the"},{"line_number":14,"context_line":"#    License for the specific language governing permissions and limitations"},{"line_number":15,"context_line":"#    under the License."},{"line_number":16,"context_line":""},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"class String(object):"}],"source_content_type":"text/x-python","patch_set":17,"id":"AAAAUH%2F%2F2lQ%3D","line":15,"updated":"2014-01-07 17:13:01.000000000","message":"There\u0027s no base class for these types, to indicate which methods are part of the API that must be provided if someone wants to add a new type. Could you define a base class with abstract methods?","commit_id":"d2f5187a5011e206480e8d51abd11d0a5632c243"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"a0d18c7199566539c777ca86bfea04f9b6251813","unresolved":false,"context_lines":[{"line_number":12,"context_line":"#    distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT"},{"line_number":13,"context_line":"#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the"},{"line_number":14,"context_line":"#    License for the specific language governing permissions and limitations"},{"line_number":15,"context_line":"#    under the License."},{"line_number":16,"context_line":""},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"class String(object):"}],"source_content_type":"text/x-python","patch_set":17,"id":"AAAAUH%2F%2F2XE%3D","line":15,"in_reply_to":"AAAAUH%2F%2F2lQ%3D","updated":"2014-01-07 18:04:50.000000000","message":"I intentionally didn\u0027t add a base class to allow using converters from any other code. All in all, it should just be a callable object that takes string value and returns typed value or throws ValueError. For example, you can use \"int\" function for Integer type (although provided Integer type has some other features like range checking).\n\nAlthough, when defining types I followed some extra requirements:\n* __call__() function should be idempotent which means that if it gets an already converted value, it just returns it without causing exceptions.\n* it should be possible to compare type instances to find out if they are identical (to aid cases when this is not an error when you define the same option with the same parameters).\n* types should provide a pleasant __repr__ for error reporting.\n\nI do not think that this is worth documenting as this is mostly cosmetics. The main requirements for types are described in cfg.py docstring.","commit_id":"d2f5187a5011e206480e8d51abd11d0a5632c243"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"a807b51e148230a10d48a950ac9e9a1de24c72bf","unresolved":false,"context_lines":[{"line_number":35,"context_line":"        self.quotes \u003d quotes"},{"line_number":36,"context_line":""},{"line_number":37,"context_line":"    def __call__(self, value):"},{"line_number":38,"context_line":"        value \u003d str(value)"},{"line_number":39,"context_line":"        if self.quotes and len(value) \u003e 0:"},{"line_number":40,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":41,"context_line":"                if value[-1] !\u003d value[0]:"}],"source_content_type":"text/x-python","patch_set":17,"id":"AAAAUH%2F%2F2mU%3D","line":38,"updated":"2014-01-07 17:13:01.000000000","message":"Should this be using unicode()?","commit_id":"d2f5187a5011e206480e8d51abd11d0a5632c243"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"3a47ffaa75287791eb138b0d6e002e11608e6a06","unresolved":false,"context_lines":[{"line_number":35,"context_line":"        self.quotes \u003d quotes"},{"line_number":36,"context_line":""},{"line_number":37,"context_line":"    def __call__(self, value):"},{"line_number":38,"context_line":"        value \u003d str(value)"},{"line_number":39,"context_line":"        if self.quotes and len(value) \u003e 0:"},{"line_number":40,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":41,"context_line":"                if value[-1] !\u003d value[0]:"}],"source_content_type":"text/x-python","patch_set":17,"id":"AAAAUH%2F%2Fk9g%3D","line":38,"in_reply_to":"AAAAUH%2F%2F2Uw%3D","updated":"2014-01-10 20:46:39.000000000","message":"We should be using unicode internally, and letting the border code (log files, API responses, etc.) deal with translating. str() will mostly work for this under python 2, unless it is passed a unicode object that can\u0027t be encoded as ASCII. It\u0027s probably safer to use unicode via six.text_type, which is set appropriately to unicode() or str() depending on the Python version.","commit_id":"d2f5187a5011e206480e8d51abd11d0a5632c243"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"a0d18c7199566539c777ca86bfea04f9b6251813","unresolved":false,"context_lines":[{"line_number":35,"context_line":"        self.quotes \u003d quotes"},{"line_number":36,"context_line":""},{"line_number":37,"context_line":"    def __call__(self, value):"},{"line_number":38,"context_line":"        value \u003d str(value)"},{"line_number":39,"context_line":"        if self.quotes and len(value) \u003e 0:"},{"line_number":40,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":41,"context_line":"                if value[-1] !\u003d value[0]:"}],"source_content_type":"text/x-python","patch_set":17,"id":"AAAAUH%2F%2F2Uw%3D","line":38,"in_reply_to":"AAAAUH%2F%2F2mU%3D","updated":"2014-01-07 18:04:50.000000000","message":"Not sure about this. In Python3.3 \"str\" IS a unicode and there is no \"unicode()\".\nAs far as I know, support for unicode in python has problems. What is the guideline on dealing with unicode?","commit_id":"d2f5187a5011e206480e8d51abd11d0a5632c243"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"a807b51e148230a10d48a950ac9e9a1de24c72bf","unresolved":false,"context_lines":[{"line_number":36,"context_line":""},{"line_number":37,"context_line":"    def __call__(self, value):"},{"line_number":38,"context_line":"        value \u003d str(value)"},{"line_number":39,"context_line":"        if self.quotes and len(value) \u003e 0:"},{"line_number":40,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":41,"context_line":"                if value[-1] !\u003d value[0]:"},{"line_number":42,"context_line":"                    raise ValueError(\u0027Non-closed quote: %s\u0027 % value)"}],"source_content_type":"text/x-python","patch_set":17,"id":"AAAAUH%2F%2F2mI%3D","line":39,"updated":"2014-01-07 17:13:01.000000000","message":"if self.quotes and value:","commit_id":"d2f5187a5011e206480e8d51abd11d0a5632c243"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"a0d18c7199566539c777ca86bfea04f9b6251813","unresolved":false,"context_lines":[{"line_number":36,"context_line":""},{"line_number":37,"context_line":"    def __call__(self, value):"},{"line_number":38,"context_line":"        value \u003d str(value)"},{"line_number":39,"context_line":"        if self.quotes and len(value) \u003e 0:"},{"line_number":40,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":41,"context_line":"                if value[-1] !\u003d value[0]:"},{"line_number":42,"context_line":"                    raise ValueError(\u0027Non-closed quote: %s\u0027 % value)"}],"source_content_type":"text/x-python","patch_set":17,"id":"AAAAUH%2F%2F2Uk%3D","line":39,"in_reply_to":"AAAAUH%2F%2F2mI%3D","updated":"2014-01-07 18:04:50.000000000","message":"Done","commit_id":"d2f5187a5011e206480e8d51abd11d0a5632c243"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"a807b51e148230a10d48a950ac9e9a1de24c72bf","unresolved":false,"context_lines":[{"line_number":37,"context_line":"    def __call__(self, value):"},{"line_number":38,"context_line":"        value \u003d str(value)"},{"line_number":39,"context_line":"        if self.quotes and len(value) \u003e 0:"},{"line_number":40,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":41,"context_line":"                if value[-1] !\u003d value[0]:"},{"line_number":42,"context_line":"                    raise ValueError(\u0027Non-closed quote: %s\u0027 % value)"},{"line_number":43,"context_line":"                value \u003d value[1:-1]"}],"source_content_type":"text/x-python","patch_set":17,"id":"AAAAUH%2F%2F2l0%3D","line":40,"updated":"2014-01-07 17:13:01.000000000","message":"I don\u0027t understand the purpose of enforcing the quote handling here.","commit_id":"d2f5187a5011e206480e8d51abd11d0a5632c243"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"3a47ffaa75287791eb138b0d6e002e11608e6a06","unresolved":false,"context_lines":[{"line_number":37,"context_line":"    def __call__(self, value):"},{"line_number":38,"context_line":"        value \u003d str(value)"},{"line_number":39,"context_line":"        if self.quotes and len(value) \u003e 0:"},{"line_number":40,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":41,"context_line":"                if value[-1] !\u003d value[0]:"},{"line_number":42,"context_line":"                    raise ValueError(\u0027Non-closed quote: %s\u0027 % value)"},{"line_number":43,"context_line":"                value \u003d value[1:-1]"}],"source_content_type":"text/x-python","patch_set":17,"id":"AAAAUH%2F%2Fk9Q%3D","line":40,"in_reply_to":"AAAAUH%2F%2F2Zw%3D","updated":"2014-01-10 20:46:39.000000000","message":"OK, I guess that makes sense. I thought the usual approach for list options was repeat the option in the configuration file, rather than use something like Python\u0027s list syntax.","commit_id":"d2f5187a5011e206480e8d51abd11d0a5632c243"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"a0d18c7199566539c777ca86bfea04f9b6251813","unresolved":false,"context_lines":[{"line_number":37,"context_line":"    def __call__(self, value):"},{"line_number":38,"context_line":"        value \u003d str(value)"},{"line_number":39,"context_line":"        if self.quotes and len(value) \u003e 0:"},{"line_number":40,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":41,"context_line":"                if value[-1] !\u003d value[0]:"},{"line_number":42,"context_line":"                    raise ValueError(\u0027Non-closed quote: %s\u0027 % value)"},{"line_number":43,"context_line":"                value \u003d value[1:-1]"}],"source_content_type":"text/x-python","patch_set":17,"id":"AAAAUH%2F%2F2Zw%3D","line":40,"in_reply_to":"AAAAUH%2F%2F2l0%3D","updated":"2014-01-07 18:04:50.000000000","message":"This is an _optional_ feature of String type to ease using it in places like List(String) where someone might want to use quotes to explicitly define String value bounds (see unit tests for List type).\n\nI was considering adding a separate QuotedString type but decided to merge this functionality into the String type itself but leave it turned off by default.","commit_id":"d2f5187a5011e206480e8d51abd11d0a5632c243"},{"author":{"_account_id":6928,"name":"Ben Nemec","email":"openstack@nemebean.com","username":"bnemec"},"change_message_id":"0e7362affd35e3de80893b5970ca4c295b504cf8","unresolved":false,"context_lines":[{"line_number":35,"context_line":"        self.quotes \u003d quotes"},{"line_number":36,"context_line":""},{"line_number":37,"context_line":"    def __call__(self, value):"},{"line_number":38,"context_line":"        value \u003d str(value)"},{"line_number":39,"context_line":"        if self.quotes and value:"},{"line_number":40,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":41,"context_line":"                if value[-1] !\u003d value[0]:"}],"source_content_type":"text/x-python","patch_set":18,"id":"AAAAUH%2F%2FmVw%3D","line":38,"updated":"2014-01-10 16:46:49.000000000","message":"Should use six.text_type for this instead of str so we can handle unicode correctly in both versions of Python.\n\nhttp://pythonhosted.org/six/#six.text_type","commit_id":"d4516da0f0b8691bda64c79bc73ab7fc78591730"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"cdb61706544a199187d8a7e6a2b8fc7ab47ee879","unresolved":false,"context_lines":[{"line_number":37,"context_line":"        self.quotes \u003d quotes"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"    def __call__(self, value):"},{"line_number":40,"context_line":"        value \u003d six.text_type(value)"},{"line_number":41,"context_line":"        if self.quotes and value:"},{"line_number":42,"context_line":"            if value[0] in \"\\\"\u0027\":"},{"line_number":43,"context_line":"                if value[-1] !\u003d value[0]:"}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAUH%2F%2Fk8c%3D","line":40,"updated":"2014-01-10 20:48:39.000000000","message":"Looks good.","commit_id":"eccc333a368f1bbc57bc6b0d13fca8409291b697"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"cdb61706544a199187d8a7e6a2b8fc7ab47ee879","unresolved":false,"context_lines":[{"line_number":49,"context_line":""},{"line_number":50,"context_line":"        raise ValueError("},{"line_number":51,"context_line":"            \u0027Valid values are [%s], but found %s\u0027 % ("},{"line_number":52,"context_line":"                \u0027, \u0027.join([str(v) for v in self.choices]),"},{"line_number":53,"context_line":"                repr(value)))"},{"line_number":54,"context_line":""},{"line_number":55,"context_line":"    def __repr__(self):"}],"source_content_type":"text/x-python","patch_set":19,"id":"AAAAUH%2F%2Fk8Y%3D","line":52,"updated":"2014-01-10 20:48:39.000000000","message":"Missed a str()","commit_id":"eccc333a368f1bbc57bc6b0d13fca8409291b697"}],"tests/test_cfg.py":[{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"8fbfe5f2eb5c6f72d3effe7a1f90be827258679e","unresolved":false,"context_lines":[{"line_number":1662,"context_line":""},{"line_number":1663,"context_line":"    def _assert_str_sub(self):"},{"line_number":1664,"context_line":"        self.assertTrue(hasattr(self.conf, \u0027bar\u0027))"},{"line_number":1665,"context_line":"        self.assertEqual(self.conf.bar, 123)"},{"line_number":1666,"context_line":""},{"line_number":1667,"context_line":"    def test_sub_default_from_default(self):"},{"line_number":1668,"context_line":"        self._prep_test_str_sub(foo_default\u003d\u0027123\u0027, bar_default\u003d\u0027$foo\u0027)"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BK0I%3D","line":1665,"updated":"2013-11-30 00:13:51.000000000","message":"Some of these changes are modifying the meaning of the existing tests. I would prefer to see new tests if you want to check that, for example, string substitution works correctly with type conversion. That way if we have a failure, we know which aspect is failing (the substitution, or the substitution combined with the type conversion).","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"dbdc3047b0f801ab2c27e0203f94eec0adcf94a7","unresolved":false,"context_lines":[{"line_number":1662,"context_line":""},{"line_number":1663,"context_line":"    def _assert_str_sub(self):"},{"line_number":1664,"context_line":"        self.assertTrue(hasattr(self.conf, \u0027bar\u0027))"},{"line_number":1665,"context_line":"        self.assertEqual(self.conf.bar, 123)"},{"line_number":1666,"context_line":""},{"line_number":1667,"context_line":"    def test_sub_default_from_default(self):"},{"line_number":1668,"context_line":"        self._prep_test_str_sub(foo_default\u003d\u0027123\u0027, bar_default\u003d\u0027$foo\u0027)"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKyE%3D","line":1665,"in_reply_to":"AAAATn%2F%2BK0I%3D","updated":"2013-11-30 00:50:53.000000000","message":"Those tests are actually integration tests because there is much going on behind the scenes. If the tests fails, there could be tons of places where it could go wrong. So a single test doesn\u0027t indicate the source of the problem, it just signals that particular use case is broken.\n\nI was considering leaving string tests in place and adding new ones for mixing non-string types (because String is so special - it\u0027s always valid), but then I would need to just copy and paste all string tests, because we need to test all combinations of where option value can come from.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"2b8aec4af46dd03ae8b98173176f7f5103b22db4","unresolved":false,"context_lines":[{"line_number":1662,"context_line":""},{"line_number":1663,"context_line":"    def _assert_str_sub(self):"},{"line_number":1664,"context_line":"        self.assertTrue(hasattr(self.conf, \u0027bar\u0027))"},{"line_number":1665,"context_line":"        self.assertEqual(self.conf.bar, 123)"},{"line_number":1666,"context_line":""},{"line_number":1667,"context_line":"    def test_sub_default_from_default(self):"},{"line_number":1668,"context_line":"        self._prep_test_str_sub(foo_default\u003d\u0027123\u0027, bar_default\u003d\u0027$foo\u0027)"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BJmA%3D","line":1665,"in_reply_to":"AAAATn%2F%2BKJo%3D","updated":"2013-12-01 19:25:59.000000000","message":"Done","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"},{"author":{"_account_id":2472,"name":"Doug Hellmann","email":"dhellmann@redhat.com","username":"doug-hellmann"},"change_message_id":"83976722f16da11bc5fb76c1cdbb8570138a2534","unresolved":false,"context_lines":[{"line_number":1662,"context_line":""},{"line_number":1663,"context_line":"    def _assert_str_sub(self):"},{"line_number":1664,"context_line":"        self.assertTrue(hasattr(self.conf, \u0027bar\u0027))"},{"line_number":1665,"context_line":"        self.assertEqual(self.conf.bar, 123)"},{"line_number":1666,"context_line":""},{"line_number":1667,"context_line":"    def test_sub_default_from_default(self):"},{"line_number":1668,"context_line":"        self._prep_test_str_sub(foo_default\u003d\u0027123\u0027, bar_default\u003d\u0027$foo\u0027)"}],"source_content_type":"text/x-python","patch_set":5,"id":"AAAATn%2F%2BKJo%3D","line":1665,"in_reply_to":"AAAATn%2F%2BKyE%3D","updated":"2013-11-30 16:44:19.000000000","message":"Regardless, I am concerned that changing the existing tests means we are no longer testing the same thing. Please add new tests.","commit_id":"119bf294077c6ecaa2f03b937c18c46a41ccc5fa"}],"tests/test_types.py":[{"author":{"_account_id":7073,"name":"mouad benchchaoui","email":"m.benchchaoui@x-ion.de","username":"mouad"},"change_message_id":"0b1292332b510060e42cc6e48123dcc508fe9614","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    def assertConvertedValue(self, s, expected):"},{"line_number":29,"context_line":"        self.assertEqual(expected, self.type_instance(s))"},{"line_number":30,"context_line":""},{"line_number":31,"context_line":"    def assertValid(self, value):"},{"line_number":32,"context_line":"        self.type_instance(value)"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"    def assertInvalid(self, value):"}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3Xw%3D","line":31,"updated":"2013-12-29 18:09:47.000000000","message":"Method not used, if it was to be used IMHO a better approach is this:\n\n    try:\n        self.type_instance(value)\n    except ValueError:\n        raise AssertionError(...)\n        # Or self.fail(\"...\") if you make this inherit directly from TestCase instead.\n\nBecause currently this method will result to an Error instead of a Failure if the assertion fail.","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"},{"author":{"_account_id":9386,"name":"Maxim Kulkin","email":"mkulkin@mirantis.com","username":"mkulkin"},"change_message_id":"b10f0397d37f16baee378800bdb3ada9277a57a0","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    def assertConvertedValue(self, s, expected):"},{"line_number":29,"context_line":"        self.assertEqual(expected, self.type_instance(s))"},{"line_number":30,"context_line":""},{"line_number":31,"context_line":"    def assertValid(self, value):"},{"line_number":32,"context_line":"        self.type_instance(value)"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"    def assertInvalid(self, value):"}],"source_content_type":"text/x-python","patch_set":15,"id":"AAAAT3%2F%2F3Lo%3D","line":31,"in_reply_to":"AAAAT3%2F%2F3Xw%3D","updated":"2013-12-29 21:46:07.000000000","message":"Actually, this is an outdated method which was replaced by assertConvertedValue() so I just remove it.","commit_id":"1e24a58343a8c46b40f19e7952db5194c2961a92"}]}
