{
    "file_diffs": [
        {
            "old_file_content": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Helper for parameter validation, documentation and conversion\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nimport re\nfrom six.moves import map as map\n\n\ndef _strip_typerepr(s):\n    \"\"\"Strip away <class '...'> and <type '...'> decorations for docstrings\n    \"\"\"\n    return re.sub(\"<(class|type) '(\\S+)'>\", r'\\2', s)\n\n\ndef _type_str(t):\n    \"\"\"Get string human-readable representation of a data type\n\n    If type (t) is given as a tuple, assume ability to choose any of the\n    listed types, so those types listing get joined with |\n    \"\"\"\n    if isinstance(t, tuple):\n        s = ' or '.join(map(_type_str, t))\n        return (\"(%s)\" % s) if len(t) > 1 else s\n    return _strip_typerepr(str(t))\n\n\nclass Constraint(object):\n    \"\"\"Base class for input value conversion/validation.\n\n    These classes are also meant to be able to generate appropriate\n    documentation on an appropriate parameter value.\n    \"\"\"\n\n    # TODO: __str__ and/or __repr__ for every one of them\n\n    def __and__(self, other):\n        return Constraints(self, other)\n\n    def __or__(self, other):\n        return AltConstraints(self, other)\n\n    def __call__(self, value):\n        # do any necessary checks or conversions, potentially catch exceptions\n        # and generate a meaningful error message\n        raise NotImplementedError(\"abstract class\")\n\n    def long_description(self):\n        # return meaningful docs or None\n        # used as a comprehensive description in the parameter list\n        return self.short_description()\n\n    def short_description(self):\n        # return meaningful docs or None\n        # used as a condensed primer for the parameter lists\n        raise NotImplementedError(\"abstract class\")\n\n\nclass EnsureDType(Constraint):\n    \"\"\"Ensure that an input (or several inputs) are of a particular data type.\n    \"\"\"\n    # TODO extend to support numpy-like dtype specs, e.g. 'int64'\n    # in addition to functors\n    def __init__(self, dtype):\n        \"\"\"\n        Parameters\n        ----------\n        dtype : functor\n        \"\"\"\n        self._dtype = dtype\n\n    def __call__(self, value):\n        from six import binary_type, text_type\n        if hasattr(value, '__iter__') and \\\n                not (isinstance(value, (binary_type, text_type))):\n            return list(map(self._dtype, value))\n        else:\n            return self._dtype(value)\n\n    def short_description(self):\n        return _type_str(self._dtype)\n\n    def long_description(self):\n        return \"value must be convertible to type '%s'\" % self.short_description()\n\n\nclass EnsureInt(EnsureDType):\n    \"\"\"Ensure that an input (or several inputs) are of a data type 'int'.\n    \"\"\"\n    def __init__(self):\n        \"\"\"Initializes EnsureDType with int\"\"\"\n        EnsureDType.__init__(self, int)\n\n\nclass EnsureFloat(EnsureDType):\n    \"\"\"Ensure that an input (or several inputs) are of a data type 'float'.\n    \"\"\"\n    def __init__(self):\n        \"\"\"Initializes EnsureDType with float\"\"\"\n        EnsureDType.__init__(self, float)\n\n\nclass EnsureListOf(Constraint):\n    \"\"\"Ensure that an input is a list of a particular data type\n    \"\"\"\n    def __init__(self, dtype):\n        \"\"\"\n        Parameters\n        ----------\n        dtype : functor\n        \"\"\"\n        self._dtype = dtype\n        super(EnsureListOf, self).__init__()\n\n    def __call__(self, value):\n        return list(map(self._dtype, value))\n\n    def short_description(self):\n        return 'list(%s)' % _type_str(self._dtype)\n\n    def long_description(self):\n        return \"value must be convertible to %s\" % self.short_description()\n\n\nclass EnsureTupleOf(Constraint):\n    \"\"\"Ensure that an input is a tuple of a particular data type\n    \"\"\"\n    def __init__(self, dtype):\n        \"\"\"\n        Parameters\n        ----------\n        dtype : functor\n        \"\"\"\n        self._dtype = dtype\n        super(EnsureTupleOf, self).__init__()\n\n    def __call__(self, value):\n        return tuple(map(self._dtype, value))\n\n    def short_description(self):\n        return 'tuple(%s)' % _type_str(self._dtype)\n\n    def long_description(self):\n        return \"value must be convertible to %s\" % self.short_description()\n\n\nclass EnsureBool(Constraint):\n    \"\"\"Ensure that an input is a bool.\n\n    A couple of literal labels are supported, such as:\n    False: '0', 'no', 'off', 'disable', 'false'\n    True: '1', 'yes', 'on', 'enable', 'true'\n    \"\"\"\n    def __call__(self, value):\n        from six import binary_type, text_type\n        if isinstance(value, bool):\n            return value\n        elif isinstance(value, (binary_type, text_type)):\n            value = value.lower()\n            if value in ('0', 'no', 'off', 'disable', 'false'):\n                return False\n            elif value in ('1', 'yes', 'on', 'enable', 'true'):\n                return True\n        raise ValueError(\"value must be converted to boolean\")\n\n    def long_description(self):\n        return 'value must be convertible to type bool'\n\n    def short_description(self):\n        return 'bool'\n\n\nclass EnsureStr(Constraint):\n    \"\"\"Ensure an input is a string.\n\n    No automatic conversion is attempted.\n    \"\"\"\n    def __init__(self, min_len=0):\n        \"\"\"\n        Parameters\n        ----------\n        min_len: int, optional\n           Minimal length for a string.\n        \"\"\"\n        assert(min_len >= 0)\n        self._min_len = min_len\n        super(EnsureStr, self).__init__()\n\n    def __call__(self, value):\n        from six import binary_type, text_type\n        if not isinstance(value, (binary_type, text_type)):\n            # do not perform a blind conversion ala str(), as almost\n            # anything can be converted and the result is most likely\n            # unintended\n            raise ValueError(\"%s is not a string\" % repr(value))\n        if len(value) < self._min_len:\n            raise ValueError(\"%r is shorter than of minimal length %d\"\n                             % (value, self._min_len))\n        return value\n\n    def long_description(self):\n        return 'value must be a string'\n\n    def short_description(self):\n        return 'str'\n\n\nclass EnsureNone(Constraint):\n    \"\"\"Ensure an input is of value `None`\"\"\"\n    def __call__(self, value):\n        if value is None:\n            return None\n        else:\n            raise ValueError(\"value must be `None`\")\n\n    def short_description(self):\n        return 'None'\n\n    def long_description(self):\n        return 'value must be `None`'\n\n\nclass EnsureChoice(Constraint):\n    \"\"\"Ensure an input is element of a set of possible values\"\"\"\n\n    def __init__(self, *values):\n        \"\"\"\n        Parameters\n        ----------\n        *values\n           Possible accepted values.\n        \"\"\"\n        self._allowed = values\n        super(EnsureChoice, self).__init__()\n\n    def __call__(self, value):\n        if value not in self._allowed:\n            raise ValueError(\"value is not one of %s\" % (self._allowed,))\n        return value\n\n    def long_description(self):\n        return 'value must be one of %s' % (str(self._allowed),)\n\n    def short_description(self):\n        return '{%s}' % ', '.join([str(c) for c in self._allowed])\n\n\nclass EnsureRange(Constraint):\n    \"\"\"Ensure an input is within a particular range\n\n    No type checks are performed.\n    \"\"\"\n    def __init__(self, min=None, max=None):\n        \"\"\"\n        Parameters\n        ----------\n        min\n            Minimal value to be accepted in the range\n        max\n            Maximal value to be accepted in the range\n        \"\"\"\n        self._min = min\n        self._max = max\n        super(EnsureRange, self).__init__()\n\n    def __call__(self, value):\n        if self._min is not None:\n            if value < self._min:\n                raise ValueError(\"value must be at least %s\" % (self._min,))\n        if self._max is not None:\n            if value > self._max:\n                raise ValueError(\"value must be at most %s\" % (self._max,))\n        return value\n\n    def long_description(self):\n        min_str = '-inf' if self._min is None else str(self._min)\n        max_str = 'inf' if self._max is None else str(self._max)\n        return 'value must be in range [%s, %s]' % (min_str, max_str)\n\n    def short_description(self):\n        None\n\n\nclass AltConstraints(Constraint):\n    \"\"\"Logical OR for constraints.\n\n    An arbitrary number of constraints can be given. They are evaluated in the\n    order in which they were specified. The value returned by the first\n    constraint that does not raise an exception is the global return value.\n\n    Documentation is aggregated for all alternative constraints.\n    \"\"\"\n    def __init__(self, *constraints):\n        \"\"\"\n        Parameters\n        ----------\n        *constraints\n           Alternative constraints\n        \"\"\"\n        super(AltConstraints, self).__init__()\n        self.constraints = [EnsureNone() if c is None else c for c in constraints]\n\n    def __or__(self, other):\n        if isinstance(other, AltConstraints):\n            self.constraints.extend(other.constraints)\n        else:\n            self.constraints.append(other)\n        return self\n\n    def __call__(self, value):\n        e_list = []\n        for c in self.constraints:\n            try:\n                return c(value)\n            except Exception as e:\n                e_list.append(e)\n        raise ValueError(\"all alternative constraints (%s) violated while testing value %r\"\n                         % (self.constraints, value))\n\n    def long_description(self):\n        cs = [c.long_description() for c in self.constraints if hasattr(c, 'long_description')]\n        doc = ', or '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\n    def short_description(self):\n        cs = [c.short_description() for c in self.constraints\n              if hasattr(c, 'short_description') and not c.short_description() is None]\n        doc = ' or '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\n\nclass Constraints(Constraint):\n    \"\"\"Logical AND for constraints.\n\n    An arbitrary number of constraints can be given. They are evaluated in the\n    order in which they were specified. The return value of each constraint is\n    passed an input into the next. The return value of the last constraint\n    is the global return value. No intermediate exceptions are caught.\n\n    Documentation is aggregated for all constraints.\n    \"\"\"\n    def __init__(self, *constraints):\n        \"\"\"\n        Parameters\n        ----------\n        *constraints\n           Constraints all of which must be satisfied\n        \"\"\"\n        super(Constraints, self).__init__()\n        self.constraints = [EnsureNone() if c is None else c for c in constraints]\n\n    def __and__(self, other):\n        if isinstance(other, Constraints):\n            self.constraints.extend(other.constraints)\n        else:\n            self.constraints.append(other)\n        return self\n\n    def __call__(self, value):\n        for c in (self.constraints):\n            value = c(value)\n        return value\n\n    def long_description(self):\n        cs = [c.long_description() for c in self.constraints if hasattr(c, 'long_description')]\n        doc = ', and '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\n    def short_description(self):\n        cs = [c.short_description() for c in self.constraints\n              if hasattr(c, 'short_description') and not c.short_description() is None]\n        doc = ' and '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\nconstraint_spec_map = {\n    'float': EnsureFloat(),\n    'int': EnsureInt(),\n    'bool': EnsureBool(),\n    'str': EnsureStr(),\n}\n\n\ndef expand_contraint_spec(spec):\n    \"\"\"Helper to translate literal contraint specs into functional ones\n\n    e.g. 'float' -> EnsureFloat()\n    \"\"\"\n    if spec is None or hasattr(spec, '__call__'):\n        return spec\n    else:\n        try:\n            return constraint_spec_map[spec]\n        except KeyError:\n            raise ValueError(\"unsupport constraint specification '%r'\" % (spec,))\n",
            "new_file_content": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Helper for parameter validation, documentation and conversion\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nimport re\nfrom six.moves import map as map\n\n\ndef _strip_typerepr(s):\n    \"\"\"Strip away <class '...'> and <type '...'> decorations for docstrings\n    \"\"\"\n    return re.sub(\"<(class|type) '(\\S+)'>\", r'\\2', s)\n\n\ndef _type_str(t):\n    \"\"\"Get string human-readable representation of a data type\n\n    If type (t) is given as a tuple, assume ability to choose any of the\n    listed types, so those types listing get joined with |\n    \"\"\"\n    if isinstance(t, tuple):\n        s = ' or '.join(map(_type_str, t))\n        return (\"(%s)\" % s) if len(t) > 1 else s\n    return _strip_typerepr(str(t))\n\n\nclass Constraint(object):\n    \"\"\"Base class for input value conversion/validation.\n\n    These classes are also meant to be able to generate appropriate\n    documentation on an appropriate parameter value.\n    \"\"\"\n\n    # TODO: __str__ and/or __repr__ for every one of them\n\n    def __repr__(self):\n        \"\"\"Rudimentary repr to avoid default scary to the user Python repr\"\"\"\n        return \"constraint:%s\" % self.short_description()\n\n    def __and__(self, other):\n        return Constraints(self, other)\n\n    def __or__(self, other):\n        return AltConstraints(self, other)\n\n    def __call__(self, value):\n        # do any necessary checks or conversions, potentially catch exceptions\n        # and generate a meaningful error message\n        raise NotImplementedError(\"abstract class\")\n\n    def long_description(self):\n        # return meaningful docs or None\n        # used as a comprehensive description in the parameter list\n        return self.short_description()\n\n    def short_description(self):\n        # return meaningful docs or None\n        # used as a condensed primer for the parameter lists\n        raise NotImplementedError(\"abstract class\")\n\n\nclass EnsureDType(Constraint):\n    \"\"\"Ensure that an input (or several inputs) are of a particular data type.\n    \"\"\"\n    # TODO extend to support numpy-like dtype specs, e.g. 'int64'\n    # in addition to functors\n    def __init__(self, dtype):\n        \"\"\"\n        Parameters\n        ----------\n        dtype : functor\n        \"\"\"\n        self._dtype = dtype\n\n    def __call__(self, value):\n        from six import binary_type, text_type\n        if hasattr(value, '__iter__') and \\\n                not (isinstance(value, (binary_type, text_type))):\n            return list(map(self._dtype, value))\n        else:\n            return self._dtype(value)\n\n    def short_description(self):\n        return _type_str(self._dtype)\n\n    def long_description(self):\n        return \"value must be convertible to type '%s'\" % self.short_description()\n\n\nclass EnsureInt(EnsureDType):\n    \"\"\"Ensure that an input (or several inputs) are of a data type 'int'.\n    \"\"\"\n    def __init__(self):\n        \"\"\"Initializes EnsureDType with int\"\"\"\n        EnsureDType.__init__(self, int)\n\n\nclass EnsureFloat(EnsureDType):\n    \"\"\"Ensure that an input (or several inputs) are of a data type 'float'.\n    \"\"\"\n    def __init__(self):\n        \"\"\"Initializes EnsureDType with float\"\"\"\n        EnsureDType.__init__(self, float)\n\n\nclass EnsureListOf(Constraint):\n    \"\"\"Ensure that an input is a list of a particular data type\n    \"\"\"\n    def __init__(self, dtype):\n        \"\"\"\n        Parameters\n        ----------\n        dtype : functor\n        \"\"\"\n        self._dtype = dtype\n        super(EnsureListOf, self).__init__()\n\n    def __call__(self, value):\n        return list(map(self._dtype, value))\n\n    def short_description(self):\n        return 'list(%s)' % _type_str(self._dtype)\n\n    def long_description(self):\n        return \"value must be convertible to %s\" % self.short_description()\n\n\nclass EnsureTupleOf(Constraint):\n    \"\"\"Ensure that an input is a tuple of a particular data type\n    \"\"\"\n    def __init__(self, dtype):\n        \"\"\"\n        Parameters\n        ----------\n        dtype : functor\n        \"\"\"\n        self._dtype = dtype\n        super(EnsureTupleOf, self).__init__()\n\n    def __call__(self, value):\n        return tuple(map(self._dtype, value))\n\n    def short_description(self):\n        return 'tuple(%s)' % _type_str(self._dtype)\n\n    def long_description(self):\n        return \"value must be convertible to %s\" % self.short_description()\n\n\nclass EnsureBool(Constraint):\n    \"\"\"Ensure that an input is a bool.\n\n    A couple of literal labels are supported, such as:\n    False: '0', 'no', 'off', 'disable', 'false'\n    True: '1', 'yes', 'on', 'enable', 'true'\n    \"\"\"\n    def __call__(self, value):\n        from six import binary_type, text_type\n        if isinstance(value, bool):\n            return value\n        elif isinstance(value, (binary_type, text_type)):\n            value = value.lower()\n            if value in ('0', 'no', 'off', 'disable', 'false'):\n                return False\n            elif value in ('1', 'yes', 'on', 'enable', 'true'):\n                return True\n        raise ValueError(\"value must be converted to boolean\")\n\n    def long_description(self):\n        return 'value must be convertible to type bool'\n\n    def short_description(self):\n        return 'bool'\n\n\nclass EnsureStr(Constraint):\n    \"\"\"Ensure an input is a string.\n\n    No automatic conversion is attempted.\n    \"\"\"\n    def __init__(self, min_len=0):\n        \"\"\"\n        Parameters\n        ----------\n        min_len: int, optional\n           Minimal length for a string.\n        \"\"\"\n        assert(min_len >= 0)\n        self._min_len = min_len\n        super(EnsureStr, self).__init__()\n\n    def __call__(self, value):\n        from six import binary_type, text_type\n        if not isinstance(value, (binary_type, text_type)):\n            # do not perform a blind conversion ala str(), as almost\n            # anything can be converted and the result is most likely\n            # unintended\n            raise ValueError(\"%s is not a string\" % repr(value))\n        if len(value) < self._min_len:\n            raise ValueError(\"%r is shorter than of minimal length %d\"\n                             % (value, self._min_len))\n        return value\n\n    def long_description(self):\n        return 'value must be a string'\n\n    def short_description(self):\n        return 'str'\n\n\nclass EnsureNone(Constraint):\n    \"\"\"Ensure an input is of value `None`\"\"\"\n    def __call__(self, value):\n        if value is None:\n            return None\n        else:\n            raise ValueError(\"value must be `None`\")\n\n    def short_description(self):\n        return 'None'\n\n    def long_description(self):\n        return 'value must be `None`'\n\n\nclass EnsureChoice(Constraint):\n    \"\"\"Ensure an input is element of a set of possible values\"\"\"\n\n    def __init__(self, *values):\n        \"\"\"\n        Parameters\n        ----------\n        *values\n           Possible accepted values.\n        \"\"\"\n        self._allowed = values\n        super(EnsureChoice, self).__init__()\n\n    def __call__(self, value):\n        if value not in self._allowed:\n            raise ValueError(\"value is not one of %s\" % (self._allowed,))\n        return value\n\n    def long_description(self):\n        return 'value must be one of %s' % (str(self._allowed),)\n\n    def short_description(self):\n        return '{%s}' % ', '.join([str(c) for c in self._allowed])\n\n\nclass EnsureRange(Constraint):\n    \"\"\"Ensure an input is within a particular range\n\n    No type checks are performed.\n    \"\"\"\n    def __init__(self, min=None, max=None):\n        \"\"\"\n        Parameters\n        ----------\n        min\n            Minimal value to be accepted in the range\n        max\n            Maximal value to be accepted in the range\n        \"\"\"\n        self._min = min\n        self._max = max\n        super(EnsureRange, self).__init__()\n\n    def __call__(self, value):\n        if self._min is not None:\n            if value < self._min:\n                raise ValueError(\"value must be at least %s\" % (self._min,))\n        if self._max is not None:\n            if value > self._max:\n                raise ValueError(\"value must be at most %s\" % (self._max,))\n        return value\n\n    def long_description(self):\n        min_str = '-inf' if self._min is None else str(self._min)\n        max_str = 'inf' if self._max is None else str(self._max)\n        return 'value must be in range [%s, %s]' % (min_str, max_str)\n\n    def short_description(self):\n        None\n\n\nclass AltConstraints(Constraint):\n    \"\"\"Logical OR for constraints.\n\n    An arbitrary number of constraints can be given. They are evaluated in the\n    order in which they were specified. The value returned by the first\n    constraint that does not raise an exception is the global return value.\n\n    Documentation is aggregated for all alternative constraints.\n    \"\"\"\n    def __init__(self, *constraints):\n        \"\"\"\n        Parameters\n        ----------\n        *constraints\n           Alternative constraints\n        \"\"\"\n        super(AltConstraints, self).__init__()\n        self.constraints = [EnsureNone() if c is None else c for c in constraints]\n\n    def __or__(self, other):\n        if isinstance(other, AltConstraints):\n            self.constraints.extend(other.constraints)\n        else:\n            self.constraints.append(other)\n        return self\n\n    def __call__(self, value):\n        e_list = []\n        for c in self.constraints:\n            try:\n                return c(value)\n            except Exception as e:\n                e_list.append(e)\n        raise ValueError(\"all alternative constraints (%s) violated while testing value %r\"\n                         % (self.constraints, value))\n\n    def long_description(self):\n        cs = [c.long_description() for c in self.constraints if hasattr(c, 'long_description')]\n        doc = ', or '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\n    def short_description(self):\n        cs = [c.short_description() for c in self.constraints\n              if hasattr(c, 'short_description') and not c.short_description() is None]\n        doc = ' or '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\n\nclass Constraints(Constraint):\n    \"\"\"Logical AND for constraints.\n\n    An arbitrary number of constraints can be given. They are evaluated in the\n    order in which they were specified. The return value of each constraint is\n    passed an input into the next. The return value of the last constraint\n    is the global return value. No intermediate exceptions are caught.\n\n    Documentation is aggregated for all constraints.\n    \"\"\"\n    def __init__(self, *constraints):\n        \"\"\"\n        Parameters\n        ----------\n        *constraints\n           Constraints all of which must be satisfied\n        \"\"\"\n        super(Constraints, self).__init__()\n        self.constraints = [EnsureNone() if c is None else c for c in constraints]\n\n    def __and__(self, other):\n        if isinstance(other, Constraints):\n            self.constraints.extend(other.constraints)\n        else:\n            self.constraints.append(other)\n        return self\n\n    def __call__(self, value):\n        for c in (self.constraints):\n            value = c(value)\n        return value\n\n    def long_description(self):\n        cs = [c.long_description() for c in self.constraints if hasattr(c, 'long_description')]\n        doc = ', and '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\n    def short_description(self):\n        cs = [c.short_description() for c in self.constraints\n              if hasattr(c, 'short_description') and not c.short_description() is None]\n        doc = ' and '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\nconstraint_spec_map = {\n    'float': EnsureFloat(),\n    'int': EnsureInt(),\n    'bool': EnsureBool(),\n    'str': EnsureStr(),\n}\n\n\ndef expand_contraint_spec(spec):\n    \"\"\"Helper to translate literal contraint specs into functional ones\n\n    e.g. 'float' -> EnsureFloat()\n    \"\"\"\n    if spec is None or hasattr(spec, '__call__'):\n        return spec\n    else:\n        try:\n            return constraint_spec_map[spec]\n        except KeyError:\n            raise ValueError(\"unsupport constraint specification '%r'\" % (spec,))\n",
            "header": {
                "file": {
                    "path": "datalad/support/constraints.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "daf015a0e",
                "new_commit_hash": "bb70c3a75",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/datalad/support/constraints.py"
            },
            "plus_file": {
                "path": "b/datalad/support/constraints.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 41,
                            "length": 6
                        },
                        "new_range": {
                            "start": 41,
                            "length": 10
                        },
                        "section": "class Constraint(object):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    # TODO: __str__ and/or __repr__ for every one of them",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def __repr__(self):",
                                "type": "added"
                            },
                            {
                                "content": "        \"\"\"Rudimentary repr to avoid default scary to the user Python repr\"\"\"",
                                "type": "added"
                            },
                            {
                                "content": "        return \"constraint:%s\" % self.short_description()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def __and__(self, other):",
                                "type": "context"
                            },
                            {
                                "content": "        return Constraints(self, other)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/support/constraints.py",
                            "type": "class",
                            "name": "Constraint",
                            "content": "class Constraint(object):\n    \"\"\"Base class for input value conversion/validation.\n\n    These classes are also meant to be able to generate appropriate\n    documentation on an appropriate parameter value.\n    \"\"\"\n\n    # TODO: __str__ and/or __repr__ for every one of them\n\n    def __repr__(self):\n        \"\"\"Rudimentary repr to avoid default scary to the user Python repr\"\"\"\n        return \"constraint:%s\" % self.short_description()\n\n    def __and__(self, other):\n        return Constraints(self, other)\n\n    def __or__(self, other):\n        return AltConstraints(self, other)\n\n    def __call__(self, value):\n        # do any necessary checks or conversions, potentially catch exceptions\n        # and generate a meaningful error message\n        raise NotImplementedError(\"abstract class\")\n\n    def long_description(self):\n        # return meaningful docs or None\n        # used as a comprehensive description in the parameter list\n        return self.short_description()\n\n    def short_description(self):\n        # return meaningful docs or None\n        # used as a condensed primer for the parameter lists\n        raise NotImplementedError(\"abstract class\")",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 35,
                            "end_lineno": 67,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "datalad/support/constraints.py",
                            "type": "method",
                            "name": "Constraint.__repr__",
                            "content": "class Constraint(object):\n    \"\"\"Base class for input value conversion/validation.\n\n    These classes are also meant to be able to generate appropriate\n    documentation on an appropriate parameter value.\n    \"\"\"\n\n    # TODO: __str__ and/or __repr__ for every one of them\n\n    def __repr__(self):\n        \"\"\"Rudimentary repr to avoid default scary to the user Python repr\"\"\"\n        return \"constraint:%s\" % self.short_description()\n\n    def __and__(self, other):\n        return Constraints(self, other)\n\n    def __or__(self, other):\n        return AltConstraints(self, other)\n\n    def __call__(self, value):\n        # do any necessary checks or conversions, potentially catch exceptions\n        # and generate a meaningful error message\n        raise NotImplementedError(\"abstract class\")\n\n    def long_description(self):\n        # return meaningful docs or None\n        # used as a comprehensive description in the parameter list\n        return self.short_description()\n\n    def short_description(self):\n        # return meaningful docs or None\n        # used as a condensed primer for the parameter lists\n        raise NotImplementedError(\"abstract class\")",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 44,
                            "end_lineno": 46,
                            "parent": {
                                "file_name": "datalad/support/constraints.py",
                                "type": "class",
                                "name": "Constraint",
                                "content": "class Constraint(object):\n    \"\"\"Base class for input value conversion/validation.\n\n    These classes are also meant to be able to generate appropriate\n    documentation on an appropriate parameter value.\n    \"\"\"\n\n    # TODO: __str__ and/or __repr__ for every one of them\n\n    def __repr__(self):\n        \"\"\"Rudimentary repr to avoid default scary to the user Python repr\"\"\"\n        return \"constraint:%s\" % self.short_description()\n\n    def __and__(self, other):\n        return Constraints(self, other)\n\n    def __or__(self, other):\n        return AltConstraints(self, other)\n\n    def __call__(self, value):\n        # do any necessary checks or conversions, potentially catch exceptions\n        # and generate a meaningful error message\n        raise NotImplementedError(\"abstract class\")\n\n    def long_description(self):\n        # return meaningful docs or None\n        # used as a comprehensive description in the parameter list\n        return self.short_description()\n\n    def short_description(self):\n        # return meaningful docs or None\n        # used as a condensed primer for the parameter lists\n        raise NotImplementedError(\"abstract class\")",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 35,
                                "end_lineno": 67,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "# emacs: -*- mode: python-mode; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Test command call wrapper\n\"\"\"\n\nimport re\nfrom nose.tools import assert_is, assert_equal, assert_raises, assert_true\n\nfrom ..support.param import Parameter\nfrom ..support import constraints as cnstr\nfrom ..interface.base import Interface, get_api_name, get_cmdline_command_name\n\nfrom ..utils import swallow_outputs\nfrom .utils import assert_re_in\n\nclass Demo(Interface):\n    \"\"\"I am a demo\"\"\"\n    _params_ = dict(\n\n        demoposarg=Parameter(\n            doc=\"demoposdoc\",\n            constraints=cnstr.EnsureInt(),\n            nargs=2),\n\n        demooptposarg1=Parameter(\n            args=('demooptposarg1',),\n            doc=\"demooptposdoc1\",\n            constraints=cnstr.EnsureInt(),\n            nargs='?'),\n\n        demooptposarg2=Parameter(\n            args=('demooptposarg2',),\n            doc=\"demooptposdoc2\",\n            constraints=cnstr.EnsureInt(),\n            nargs='?'),\n\n        demoarg=Parameter(\n            doc=\"demodoc\",\n            constraints=cnstr.EnsureInt()))\n\n    def __call__(self, demoposarg, demooptposarg1=99, demooptposarg2=999, demoarg=100):\n        return demoarg\n\n\ndef test_param():\n    # having a parametr with no information is fine\n    # it doesn't need a name, because it comes from the signatur\n    # of the actual implementation that is described\n    p = Parameter()\n    pname = 'testname'\n    # minimal docstring\n    assert_equal(pname, p.get_autodoc('testname'))\n    doc = 'somedoc'\n    p = Parameter(doc=doc)\n    assert_equal('%s\\n  %s.' % (pname, doc), p.get_autodoc('testname'))\n    # constraints\n    p = Parameter(doc=doc, constraints=cnstr.EnsureInt() | cnstr.EnsureStr())\n    autodoc = p.get_autodoc('testname')\n    assert_true(\"convertible to type 'int'\" in autodoc)\n    assert_true('must be a string' in autodoc)\n    assert_true('int or str' in autodoc)\n\n\ndef test_interface():\n    di = Demo()\n\n    import argparse\n    parser = argparse.ArgumentParser()\n\n    di.setup_parser(parser)\n    with swallow_outputs() as cmo:\n        assert_equal(parser.print_help(), None)\n        assert(cmo.out)\n        assert_equal(cmo.err, '')\n    args = parser.parse_args(['42', '11', '1', '2', '--demoarg', '23'])\n    assert_is(args.demoarg, 23)\n    assert_equal(args.demoposarg, [42, 11])\n    assert_equal(args.demooptposarg1, 1)\n    assert_equal(args.demooptposarg2, 2)\n\n    # wrong type\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, ['--demoarg', 'abc'])\n        # that is what we dump upon folks atm. TODO: improve reporting of illspecified options\n        assert_re_in(\".*invalid <datalad.support.constraints.EnsureInt object at .*> value:.*\",\n                     cmo.err, re.DOTALL)\n\n    # missing argument to option\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, ['--demoarg'])\n        assert_re_in(\".*--demoarg: expected one argument\", cmo.err, re.DOTALL)\n\n    # missing positional argument\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, [''])\n        # PY2|PY3\n        assert_re_in(\".*error: (too few arguments|the following arguments are required: demoposarg)\",\n                     cmo.err, re.DOTALL)\n\n\ndef test_name_generation():\n    assert_equal(\n        get_api_name((\"some.module\", \"SomeClass\")),\n        'module')\n    assert_equal(\n        get_api_name((\"some.module\", \"SomeClass\", \"cmdline-override\")),\n        'module')\n    assert_equal(\n        get_api_name((\"some.module\",\n                      \"SomeClass\",\n                      \"cmdline_override\",\n                      \"api_override-dont-touch\")),\n        \"api_override-dont-touch\")\n    assert_equal(\n        get_cmdline_command_name((\"some.module_something\", \"SomeClass\")),\n        \"module-something\")\n    assert_equal(\n        get_cmdline_command_name((\n            \"some.module_something\",\n            \"SomeClass\",\n            \"override\")),\n        \"override\")\n    assert_equal(\n        get_cmdline_command_name((\n            \"some.module_something\",\n            \"SomeClass\",\n            \"override\",\n            \"api_ignore\")),\n        \"override\")\n",
            "new_file_content": "# emacs: -*- mode: python-mode; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Test command call wrapper\n\"\"\"\n\nimport re\nfrom nose.tools import assert_is, assert_equal, assert_raises, assert_true\n\nfrom ..support.param import Parameter\nfrom ..support import constraints as cnstr\nfrom ..interface.base import Interface, get_api_name, get_cmdline_command_name\n\nfrom ..utils import swallow_outputs\nfrom .utils import assert_re_in\n\nclass Demo(Interface):\n    \"\"\"I am a demo\"\"\"\n    _params_ = dict(\n\n        demoposarg=Parameter(\n            doc=\"demoposdoc\",\n            constraints=cnstr.EnsureInt(),\n            nargs=2),\n\n        demooptposarg1=Parameter(\n            args=('demooptposarg1',),\n            doc=\"demooptposdoc1\",\n            constraints=cnstr.EnsureInt(),\n            nargs='?'),\n\n        demooptposarg2=Parameter(\n            args=('demooptposarg2',),\n            doc=\"demooptposdoc2\",\n            constraints=cnstr.EnsureInt(),\n            nargs='?'),\n\n        demoarg=Parameter(\n            doc=\"demodoc\",\n            constraints=cnstr.EnsureInt()))\n\n    def __call__(self, demoposarg, demooptposarg1=99, demooptposarg2=999, demoarg=100):\n        return demoarg\n\n\ndef test_param():\n    # having a parametr with no information is fine\n    # it doesn't need a name, because it comes from the signatur\n    # of the actual implementation that is described\n    p = Parameter()\n    pname = 'testname'\n    # minimal docstring\n    assert_equal(pname, p.get_autodoc('testname'))\n    doc = 'somedoc'\n    p = Parameter(doc=doc)\n    assert_equal('%s\\n  %s.' % (pname, doc), p.get_autodoc('testname'))\n    # constraints\n    p = Parameter(doc=doc, constraints=cnstr.EnsureInt() | cnstr.EnsureStr())\n    autodoc = p.get_autodoc('testname')\n    assert_true(\"convertible to type 'int'\" in autodoc)\n    assert_true('must be a string' in autodoc)\n    assert_true('int or str' in autodoc)\n\n\ndef test_interface():\n    di = Demo()\n\n    import argparse\n    parser = argparse.ArgumentParser()\n\n    di.setup_parser(parser)\n    with swallow_outputs() as cmo:\n        assert_equal(parser.print_help(), None)\n        assert(cmo.out)\n        assert_equal(cmo.err, '')\n    args = parser.parse_args(['42', '11', '1', '2', '--demoarg', '23'])\n    assert_is(args.demoarg, 23)\n    assert_equal(args.demoposarg, [42, 11])\n    assert_equal(args.demooptposarg1, 1)\n    assert_equal(args.demooptposarg2, 2)\n\n    # wrong type\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, ['--demoarg', 'abc'])\n        # that is what we dump upon folks atm. TODO: improve reporting of illspecified options\n        assert_re_in(\".*invalid constraint:int value:.*\",\n                     cmo.err, re.DOTALL)\n\n    # missing argument to option\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, ['--demoarg'])\n        assert_re_in(\".*--demoarg: expected one argument\", cmo.err, re.DOTALL)\n\n    # missing positional argument\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, [''])\n        # PY2|PY3\n        assert_re_in(\".*error: (too few arguments|the following arguments are required: demoposarg)\",\n                     cmo.err, re.DOTALL)\n\n\ndef test_name_generation():\n    assert_equal(\n        get_api_name((\"some.module\", \"SomeClass\")),\n        'module')\n    assert_equal(\n        get_api_name((\"some.module\", \"SomeClass\", \"cmdline-override\")),\n        'module')\n    assert_equal(\n        get_api_name((\"some.module\",\n                      \"SomeClass\",\n                      \"cmdline_override\",\n                      \"api_override-dont-touch\")),\n        \"api_override-dont-touch\")\n    assert_equal(\n        get_cmdline_command_name((\"some.module_something\", \"SomeClass\")),\n        \"module-something\")\n    assert_equal(\n        get_cmdline_command_name((\n            \"some.module_something\",\n            \"SomeClass\",\n            \"override\")),\n        \"override\")\n    assert_equal(\n        get_cmdline_command_name((\n            \"some.module_something\",\n            \"SomeClass\",\n            \"override\",\n            \"api_ignore\")),\n        \"override\")\n",
            "header": {
                "file": {
                    "path": "datalad/tests/test_interface.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "1fb342d4f",
                "new_commit_hash": "5879b5748",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/datalad/tests/test_interface.py"
            },
            "plus_file": {
                "path": "b/datalad/tests/test_interface.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 88,
                            "length": 7
                        },
                        "new_range": {
                            "start": 88,
                            "length": 7
                        },
                        "section": "def test_interface():"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    with swallow_outputs() as cmo:",
                                "type": "context"
                            },
                            {
                                "content": "        assert_raises(SystemExit, parser.parse_args, ['--demoarg', 'abc'])",
                                "type": "context"
                            },
                            {
                                "content": "        # that is what we dump upon folks atm. TODO: improve reporting of illspecified options",
                                "type": "context"
                            },
                            {
                                "content": "        assert_re_in(\".*invalid <datalad.support.constraints.EnsureInt object at .*> value:.*\",",
                                "type": "deleted"
                            },
                            {
                                "content": "        assert_re_in(\".*invalid constraint:int value:.*\",",
                                "type": "added"
                            },
                            {
                                "content": "                     cmo.err, re.DOTALL)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    # missing argument to option",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/tests/test_interface.py",
                            "type": "function",
                            "name": "test_interface",
                            "content": "def test_interface():\n    di = Demo()\n\n    import argparse\n    parser = argparse.ArgumentParser()\n\n    di.setup_parser(parser)\n    with swallow_outputs() as cmo:\n        assert_equal(parser.print_help(), None)\n        assert(cmo.out)\n        assert_equal(cmo.err, '')\n    args = parser.parse_args(['42', '11', '1', '2', '--demoarg', '23'])\n    assert_is(args.demoarg, 23)\n    assert_equal(args.demoposarg, [42, 11])\n    assert_equal(args.demooptposarg1, 1)\n    assert_equal(args.demooptposarg2, 2)\n\n    # wrong type\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, ['--demoarg', 'abc'])\n        # that is what we dump upon folks atm. TODO: improve reporting of illspecified options\n        assert_re_in(\".*invalid <datalad.support.constraints.EnsureInt object at .*> value:.*\",\n                     cmo.err, re.DOTALL)\n\n    # missing argument to option\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, ['--demoarg'])\n        assert_re_in(\".*--demoarg: expected one argument\", cmo.err, re.DOTALL)\n\n    # missing positional argument\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, [''])\n        # PY2|PY3\n        assert_re_in(\".*error: (too few arguments|the following arguments are required: demoposarg)\",\n                     cmo.err, re.DOTALL)",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 70,
                            "end_lineno": 104,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "b7077e8b2024e1ac150268cc4dbd73a521ca46d2^",
    "new_commit_hash": "b7077e8b2024e1ac150268cc4dbd73a521ca46d2",
    "commit_message": "Merge pull request #510 from yarikoptic/enh-some-repr-for-constraints\n\nENH: use short_description for constraints repr",
    "commit_date": "2016-06-09T19:40:13+02:00",
    "metadata": {}
}
{
    "repo_name": "datalad",
    "new_commit_hash": "b7077e8b2024e1ac150268cc4dbd73a521ca46d2",
    "test_file_codes": [
        "# emacs: -*- mode: python-mode; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Test command call wrapper\n\"\"\"\n\nimport re\nfrom nose.tools import assert_is, assert_equal, assert_raises, assert_true\n\nfrom datalad.support.param import Parameter\nfrom datalad.support import constraints as cnstr\nfrom datalad.interface.base import Interface, get_api_name, get_cmdline_command_name\n\nfrom datalad.utils import swallow_outputs\nfrom datalad.tests.utils import assert_re_in\n\nclass Demo(Interface):\n    \"\"\"I am a demo\"\"\"\n    _params_ = dict(\n\n        demoposarg=Parameter(\n            doc=\"demoposdoc\",\n            constraints=cnstr.EnsureInt(),\n            nargs=2),\n\n        demooptposarg1=Parameter(\n            args=('demooptposarg1',),\n            doc=\"demooptposdoc1\",\n            constraints=cnstr.EnsureInt(),\n            nargs='?'),\n\n        demooptposarg2=Parameter(\n            args=('demooptposarg2',),\n            doc=\"demooptposdoc2\",\n            constraints=cnstr.EnsureInt(),\n            nargs='?'),\n\n        demoarg=Parameter(\n            doc=\"demodoc\",\n            constraints=cnstr.EnsureInt()))\n\n    def __call__(self, demoposarg, demooptposarg1=99, demooptposarg2=999, demoarg=100):\n        return demoarg\n\n\ndef test_param():\n    # having a parametr with no information is fine\n    # it doesn't need a name, because it comes from the signatur\n    # of the actual implementation that is described\n    p = Parameter()\n    pname = 'testname'\n    # minimal docstring\n    assert_equal(pname, p.get_autodoc('testname'))\n    doc = 'somedoc'\n    p = Parameter(doc=doc)\n    assert_equal('%s\\n  %s.' % (pname, doc), p.get_autodoc('testname'))\n    # constraints\n    p = Parameter(doc=doc, constraints=cnstr.EnsureInt() | cnstr.EnsureStr())\n    autodoc = p.get_autodoc('testname')\n    assert_true(\"convertible to type 'int'\" in autodoc)\n    assert_true('must be a string' in autodoc)\n    assert_true('int or str' in autodoc)\n\n\ndef test_interface():\n    di = Demo()\n\n    import argparse\n    parser = argparse.ArgumentParser()\n\n    di.setup_parser(parser)\n    with swallow_outputs() as cmo:\n        assert_equal(parser.print_help(), None)\n        assert(cmo.out)\n        assert_equal(cmo.err, '')\n    args = parser.parse_args(['42', '11', '1', '2', '--demoarg', '23'])\n    assert_is(args.demoarg, 23)\n    assert_equal(args.demoposarg, [42, 11])\n    assert_equal(args.demooptposarg1, 1)\n    assert_equal(args.demooptposarg2, 2)\n\n    # wrong type\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, ['--demoarg', 'abc'])\n        # that is what we dump upon folks atm. TODO: improve reporting of illspecified options\n        assert_re_in(\".*invalid constraint:int value:.*\",\n                     cmo.err, re.DOTALL)\n\n    # missing argument to option\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, ['--demoarg'])\n        assert_re_in(\".*--demoarg: expected one argument\", cmo.err, re.DOTALL)\n\n    # missing positional argument\n    with swallow_outputs() as cmo:\n        assert_raises(SystemExit, parser.parse_args, [''])\n        # PY2|PY3\n        assert_re_in(\".*error: (too few arguments|the following arguments are required: demoposarg)\",\n                     cmo.err, re.DOTALL)\n\n\ndef test_name_generation():\n    assert_equal(\n        get_api_name((\"some.module\", \"SomeClass\")),\n        'module')\n    assert_equal(\n        get_api_name((\"some.module\", \"SomeClass\", \"cmdline-override\")),\n        'module')\n    assert_equal(\n        get_api_name((\"some.module\",\n                      \"SomeClass\",\n                      \"cmdline_override\",\n                      \"api_override-dont-touch\")),\n        \"api_override-dont-touch\")\n    assert_equal(\n        get_cmdline_command_name((\"some.module_something\", \"SomeClass\")),\n        \"module-something\")\n    assert_equal(\n        get_cmdline_command_name((\n            \"some.module_something\",\n            \"SomeClass\",\n            \"override\")),\n        \"override\")\n    assert_equal(\n        get_cmdline_command_name((\n            \"some.module_something\",\n            \"SomeClass\",\n            \"override\",\n            \"api_ignore\")),\n        \"override\")\n",
        "import pytest\nimport tempfile\nimport os\n\n\n@pytest.fixture\ndef path():\n    with tempfile.TemporaryDirectory() as tmpdir:\n        yield tmpdir\n\n\n@pytest.fixture(autouse=True)\ndef setup_git_config():\n    with tempfile.TemporaryDirectory() as home:\n        old_home = os.environ.get(\"HOME\")\n        os.environ[\"HOME\"] = home\n        os.system('git config --global user.name \"DataLad Tester\"')\n        os.system('git config --global user.email \"test@example.com\"')\n        yield\n        if old_home:\n            os.environ[\"HOME\"] = old_home\n"
    ],
    "test_file_names": [
        "test_1.py",
        "conftest.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Datalad installation attempts...\nVerifying installation...\n❌ Verification failed\nPython 3.9 installation failed, trying Python 3.7...\nVerifying installation...\nDatalad version: 0.2.dev223\n✅ Installation successful!\nSuccessfully installed Datalad using Python 3.7\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 25 packages in 18ms\nInstalled 25 packages in 4.91s\n + coverage==7.6.10\n + docopt==0.6.2\n + exceptiongroup==1.2.2\n + grabbit==0.2.6\n + importlib-resources==6.4.5\n + iniconfig==2.0.0\n + nibabel==5.3.2\n + num2words==0.5.14\n + numpy==2.0.2\n + packaging==24.2\n + pandas==2.2.3\n + patsy==1.0.1\n + pluggy==1.5.0\n + pybids==0.6.5\n + pytest==8.3.4\n + pytest-cov==6.0.0\n + python-dateutil==2.9.0.post0\n + pytz==2024.2\n + scipy==1.13.1\n + setuptools==75.6.0\n + six==1.17.0\n + tomli==2.2.1\n + typing-extensions==4.12.2\n + tzdata==2024.2\n + zipp==3.21.0\nerror: The build backend returned an error\n  Caused by: Call to `setuptools.build_meta:__legacy__.build_editable` failed (exit status: 1)\n\n[stderr]\nTraceback (most recent call last):\n  File \"<string>\", line 14, in <module>\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpSvibs4/lib/python3.9/site-packages/setuptools/build_meta.py\", line 483, in get_requires_for_build_editable\n    return self.get_requires_for_build_wheel(config_settings)\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpSvibs4/lib/python3.9/site-packages/setuptools/build_meta.py\", line 334, in get_requires_for_build_wheel\n    return self._get_build_requires(config_settings, requirements=[])\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpSvibs4/lib/python3.9/site-packages/setuptools/build_meta.py\", line 304, in _get_build_requires\n    self.run_setup()\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpSvibs4/lib/python3.9/site-packages/setuptools/build_meta.py\", line 522, in run_setup\n    super().run_setup(setup_script=setup_script)\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpSvibs4/lib/python3.9/site-packages/setuptools/build_meta.py\", line 320, in run_setup\n    exec(code, locals())\n  File \"<string>\", line 35, in <module>\nAttributeError: module 'platform' has no attribute 'dist'\n\nhint: This usually indicates a problem with the package or the build environment.\nerror: The build backend returned an error\n  Caused by: Call to `setuptools.build_meta:__legacy__.build_editable` failed (exit status: 1)\n\n[stderr]\nTraceback (most recent call last):\n  File \"<string>\", line 14, in <module>\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmps71Cx9/lib/python3.9/site-packages/setuptools/build_meta.py\", line 483, in get_requires_for_build_editable\n    return self.get_requires_for_build_wheel(config_settings)\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmps71Cx9/lib/python3.9/site-packages/setuptools/build_meta.py\", line 334, in get_requires_for_build_wheel\n    return self._get_build_requires(config_settings, requirements=[])\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmps71Cx9/lib/python3.9/site-packages/setuptools/build_meta.py\", line 304, in _get_build_requires\n    self.run_setup()\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmps71Cx9/lib/python3.9/site-packages/setuptools/build_meta.py\", line 522, in run_setup\n    super().run_setup(setup_script=setup_script)\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmps71Cx9/lib/python3.9/site-packages/setuptools/build_meta.py\", line 320, in run_setup\n    exec(code, locals())\n  File \"<string>\", line 35, in <module>\nAttributeError: module 'platform' has no attribute 'dist'\n\nhint: This usually indicates a problem with the package or the build environment.\nTraceback (most recent call last):\n  File \"<string>\", line 1, in <module>\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_b7077e8b2024e1ac150268cc4dbd73a521ca46d2/datalad/__init__.py\", line 20, in <module>\n    from .config import ConfigManager\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_b7077e8b2024e1ac150268cc4dbd73a521ca46d2/datalad/config.py\", line 17, in <module>\n    from appdirs import AppDirs\nModuleNotFoundError: No module named 'appdirs'\nUsing CPython 3.7.9\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 26 packages in 299ms\nInstalled 26 packages in 4.60s\n + bids==0.0\n + coverage==7.2.7\n + docopt==0.6.2\n + exceptiongroup==1.2.2\n + fasteners==0.19\n + grabbit==0.2.6\n + importlib-metadata==6.7.0\n + iniconfig==2.0.0\n + nibabel==4.0.2\n + num2words==0.5.14\n + numpy==1.21.6\n + packaging==24.0\n + pandas==1.3.5\n + patsy==1.0.1\n + pluggy==1.2.0\n + pybids==0.6.5\n + pytest==7.4.4\n + pytest-cov==4.1.0\n + python-dateutil==2.9.0.post0\n + pytz==2024.2\n + scipy==1.7.3\n + setuptools==68.0.0\n + six==1.17.0\n + tomli==2.0.1\n + typing-extensions==4.7.1\n + zipp==3.15.0\nResolved 67 packages in 2.90s\nPrepared 1 package in 2.25s\nInstalled 61 packages in 94ms\n + appdirs==1.4.4\n + attrs==24.2.0\n + automat==22.10.0\n + beautifulsoup4==4.12.3\n + boto==2.49.0\n + certifi==2024.12.14\n + cffi==1.15.1\n + charset-normalizer==3.4.1\n + constantly==15.1.0\n + cryptography==44.0.0\n + cssselect==1.2.0\n + datalad==0.2.dev1 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_b7077e8b2024e1ac150268cc4dbd73a521ca46d2)\n + filelock==3.12.2\n + gitdb==4.0.12\n + gitpython==3.1.44\n + httpretty==1.1.4\n + humanize==4.6.0\n + hyperlink==21.0.0\n + idna==3.10\n + importlib-resources==5.12.0\n + incremental==22.10.0\n + itemadapter==0.8.0\n + itemloaders==1.1.0\n + jaraco-classes==3.2.3\n + jeepney==0.8.0\n + jmespath==1.0.1\n + keyring==24.1.1\n + keyrings-alt==4.2.0\n + lxml==5.3.0\n + mock==5.1.0\n + more-itertools==9.1.0\n + msgpack-python==0.5.6\n + multidict==6.0.5\n + nose==1.3.7\n + parsel==1.8.1\n + patool==1.12\n + protego==0.3.0\n + pyasn1==0.5.1\n + pyasn1-modules==0.3.0\n + pycparser==2.21\n + pydispatcher==2.0.7\n + pyopenssl==24.3.0\n + pyyaml==6.0.1\n + queuelib==1.6.2\n + requests==2.31.0\n + requests-file==2.1.0\n + scrapy==2.9.0\n + secretstorage==3.3.3\n + service-identity==21.1.0\n + smmap==5.0.2\n + soupsieve==2.4.1\n + testtools==2.7.1\n + tldextract==4.0.0\n + tqdm==4.67.1\n + twisted==23.8.0\n + urllib3==2.0.7\n + vcrpy==4.3.0\n + w3lib==2.1.2\n + wrapt==1.16.0\n + yarl==1.9.4\n + zope-interface==6.4.post2\nAudited 1 package in 4ms\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_b7077e8b2024e1ac150268cc4dbd73a521ca46d2\nplugins: cov-4.1.0\ncollected 3 items\n\nr2e_tests/test_1.py ...                                                  [100%]\n\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_param\nPASSED r2e_tests/test_1.py::test_interface\nPASSED r2e_tests/test_1.py::test_name_generation\n============================== 3 passed in 1.01s ===============================\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_b7077e8b2024e1ac150268cc4dbd73a521ca46d2\nplugins: cov-4.1.0\ncollected 3 items\n\nr2e_tests/test_1.py .F.                                                  [100%]\n\n=================================== FAILURES ===================================\n________________________________ test_interface ________________________________\n\n    def test_interface():\n        di = Demo()\n    \n        import argparse\n        parser = argparse.ArgumentParser()\n    \n        di.setup_parser(parser)\n        with swallow_outputs() as cmo:\n            assert_equal(parser.print_help(), None)\n            assert(cmo.out)\n            assert_equal(cmo.err, '')\n        args = parser.parse_args(['42', '11', '1', '2', '--demoarg', '23'])\n        assert_is(args.demoarg, 23)\n        assert_equal(args.demoposarg, [42, 11])\n        assert_equal(args.demooptposarg1, 1)\n        assert_equal(args.demooptposarg2, 2)\n    \n        # wrong type\n        with swallow_outputs() as cmo:\n            assert_raises(SystemExit, parser.parse_args, ['--demoarg', 'abc'])\n            # that is what we dump upon folks atm. TODO: improve reporting of illspecified options\n            assert_re_in(\".*invalid constraint:int value:.*\",\n>                        cmo.err, re.DOTALL)\n\nr2e_tests/test_1.py:92: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nregex = '.*invalid constraint:int value:.*'\nc = [\"usage: __main__.py [-h] [--demoarg DEMOARG]\\n                   demoposarg demoposarg [demooptposarg1] [demooptposar...y: error: argument --demoarg: invalid <datalad.support.constraints.EnsureInt object at 0x7846219e6650> value: 'abc'\\n\"]\nflags = <RegexFlag.DOTALL: 16>\n\n    def assert_re_in(regex, c, flags=0):\n        \"\"\"Assert that container (list, str, etc) contains entry matching the regex\n        \"\"\"\n        if not isinstance(c, (list, tuple)):\n            c = [c]\n        for e in c:\n            if re.match(regex, e, flags=flags):\n                return\n>       raise AssertionError(\"Not a single entry matched %r in %r\" % (regex, c))\nE       AssertionError: Not a single entry matched '.*invalid constraint:int value:.*' in [\"usage: __main__.py [-h] [--demoarg DEMOARG]\\n                   demoposarg demoposarg [demooptposarg1] [demooptposarg2]\\n__main__.py: error: argument --demoarg: invalid <datalad.support.constraints.EnsureInt object at 0x7846219e6650> value: 'abc'\\n\"]\n\ndatalad/tests/utils.py:839: AssertionError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_param\nPASSED r2e_tests/test_1.py::test_name_generation\nFAILED r2e_tests/test_1.py::test_interface - AssertionError: Not a single ent...\n========================= 1 failed, 2 passed in 0.84s ==========================\n",
    "old_commit_res_stderr": ""
}
datalad/support/constraints.py datalad/tests/test_interface.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]

**Title:** Error message for invalid constraint includes internal class representation

**Description:**
When providing an invalid value to the `--demoarg` option, the error message includes the internal class representation, making it unclear to users.

**Example Code:**
```python
args = parser.parse_args(['--demoarg', 'abc'])
```

**Output:**
```
usage: __main__.py ... error: argument --demoarg: invalid <datalad.support.constraints.EnsureInt object at 0x7846219e6650> value: 'abc'
```

**Expected Behavior:**
The error message should be concise and user-friendly, displaying the constraint type without the internal class details, such as:
```
usage: __main__.py ... error: argument --demoarg: invalid constraint:int value: 'abc'
```

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### datalad/support/constraints.py
# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
# ex: set sts=4 ts=4 sw=4 noet:
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
#   See COPYING file distributed along with the datalad package for the
#   copyright and license terms.
#
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""Helper for parameter validation, documentation and conversion"""

__docformat__ = 'restructuredtext'

import re
from six.moves import map as map


def _strip_typerepr(s):
    """Strip away <class '...'> and <type '...'> decorations for docstrings
    """
    return re.sub("<(class|type) '(\S+)'>", r'\2', s)


def _type_str(t):
    """Get string human-readable representation of a data type

    If type (t) is given as a tuple, assume ability to choose any of the
    listed types, so those types listing get joined with |
    """
    if isinstance(t, tuple):
        s = ' or '.join(map(_type_str, t))
        return ("(%s)" % s) if len(t) > 1 else s
    return _strip_typerepr(str(t))


class Constraint(object):
    """Base class for input value conversion/validation.

    These classes are also meant to be able to generate appropriate
    documentation on an appropriate parameter value.
    """

    # TODO: __str__ and/or __repr__ for every one of them

    def __and__(self, other):
        return Constraints(self, other)

    def __or__(self, other):
        return AltConstraints(self, other)

    def __call__(self, value):
        # do any necessary checks or conversions, potentially catch exceptions
        # and generate a meaningful error message
        raise NotImplementedError("abstract class")

    def long_description(self):
        # return meaningful docs or None
        # used as a comprehensive description in the parameter list
        return self.short_description()

    def short_description(self):
        # return meaningful docs or None
        # used as a condensed primer for the parameter lists
        raise NotImplementedError("abstract class")


class EnsureDType(Constraint):
    """Ensure that an input (or several inputs) are of a particular data type.
    """
    # TODO extend to support numpy-like dtype specs, e.g. 'int64'
    # in addition to functors
    def __init__(self, dtype):
        """
        Parameters
        ----------
        dtype : functor
        """
        self._dtype = dtype

    def __call__(self, value):
        from six import binary_type, text_type
        if hasattr(value, '__iter__') and \
                not (isinstance(value, (binary_type, text_type))):
            return list(map(self._dtype, value))
        else:
            return self._dtype(value)

    def short_description(self):
        return _type_str(self._dtype)

    def long_description(self):
        return "value must be convertible to type '%s'" % self.short_description()


class EnsureInt(EnsureDType):
    """Ensure that an input (or several inputs) are of a data type 'int'.
    """
    def __init__(self):
        """Initializes EnsureDType with int"""
        EnsureDType.__init__(self, int)


class EnsureFloat(EnsureDType):
    """Ensure that an input (or several inputs) are of a data type 'float'.
    """
    def __init__(self):
        """Initializes EnsureDType with float"""
        EnsureDType.__init__(self, float)


class EnsureListOf(Constraint):
    """Ensure that an input is a list of a particular data type
    """
    def __init__(self, dtype):
        """
        Parameters
        ----------
        dtype : functor
        """
        self._dtype = dtype
        super(EnsureListOf, self).__init__()

    def __call__(self, value):
        return list(map(self._dtype, value))

    def short_description(self):
        return 'list(%s)' % _type_str(self._dtype)

    def long_description(self):
        return "value must be convertible to %s" % self.short_description()


class EnsureTupleOf(Constraint):
    """Ensure that an input is a tuple of a particular data type
    """
    def __init__(self, dtype):
        """
        Parameters
        ----------
        dtype : functor
        """
        self._dtype = dtype
        super(EnsureTupleOf, self).__init__()

    def __call__(self, value):
        return tuple(map(self._dtype, value))

    def short_description(self):
        return 'tuple(%s)' % _type_str(self._dtype)

    def long_description(self):
        return "value must be convertible to %s" % self.short_description()


class EnsureBool(Constraint):
    """Ensure that an input is a bool.

    A couple of literal labels are supported, such as:
    False: '0', 'no', 'off', 'disable', 'false'
    True: '1', 'yes', 'on', 'enable', 'true'
    """
    def __call__(self, value):
        from six import binary_type, text_type
        if isinstance(value, bool):
            return value
        elif isinstance(value, (binary_type, text_type)):
            value = value.lower()
            if value in ('0', 'no', 'off', 'disable', 'false'):
                return False
            elif value in ('1', 'yes', 'on', 'enable', 'true'):
                return True
        raise ValueError("value must be converted to boolean")

    def long_description(self):
        return 'value must be convertible to type bool'

    def short_description(self):
        return 'bool'


class EnsureStr(Constraint):
    """Ensure an input is a string.

    No automatic conversion is attempted.
    """
    def __init__(self, min_len=0):
        """
        Parameters
        ----------
        min_len: int, optional
           Minimal length for a string.
        """
        assert(min_len >= 0)
        self._min_len = min_len
        super(EnsureStr, self).__init__()

    def __call__(self, value):
        from six import binary_type, text_type
        if not isinstance(value, (binary_type, text_type)):
            # do not perform a blind conversion ala str(), as almost
            # anything can be converted and the result is most likely
            # unintended
            raise ValueError("%s is not a string" % repr(value))
        if len(value) < self._min_len:
            raise ValueError("%r is shorter than of minimal length %d"
                             % (value, self._min_len))
        return value

    def long_description(self):
        return 'value must be a string'

    def short_description(self):
        return 'str'


class EnsureNone(Constraint):
    """Ensure an input is of value `None`"""
    def __call__(self, value):
        if value is None:
            return None
        else:
            raise ValueError("value must be `None`")

    def short_description(self):
        return 'None'

    def long_description(self):
        return 'value must be `None`'


class EnsureChoice(Constraint):
    """Ensure an input is element of a set of possible values"""

    def __init__(self, *values):
        """
        Parameters
        ----------
        *values
           Possible accepted values.
        """
        self._allowed = values
        super(EnsureChoice, self).__init__()

    def __call__(self, value):
        if value not in self._allowed:
            raise ValueError("value is not one of %s" % (self._allowed,))
        return value

    def long_description(self):
        return 'value must be one of %s' % (str(self._allowed),)

    def short_description(self):
        return '{%s}' % ', '.join([str(c) for c in self._allowed])


class EnsureRange(Constraint):
    """Ensure an input is within a particular range

    No type checks are performed.
    """
    def __init__(self, min=None, max=None):
        """
        Parameters
        ----------
        min
            Minimal value to be accepted in the range
        max
            Maximal value to be accepted in the range
        """
        self._min = min
        self._max = max
        super(EnsureRange, self).__init__()

    def __call__(self, value):
        if self._min is not None:
            if value < self._min:
                raise ValueError("value must be at least %s" % (self._min,))
        if self._max is not None:
            if value > self._max:
                raise ValueError("value must be at most %s" % (self._max,))
        return value

    def long_description(self):
        min_str = '-inf' if self._min is None else str(self._min)
        max_str = 'inf' if self._max is None else str(self._max)
        return 'value must be in range [%s, %s]' % (min_str, max_str)

    def short_description(self):
        None


class AltConstraints(Constraint):
    """Logical OR for constraints.

    An arbitrary number of constraints can be given. They are evaluated in the
    order in which they were specified. The value returned by the first
    constraint that does not raise an exception is the global return value.

    Documentation is aggregated for all alternative constraints.
    """
    def __init__(self, *constraints):
        """
        Parameters
        ----------
        *constraints
           Alternative constraints
        """
        super(AltConstraints, self).__init__()
        self.constraints = [EnsureNone() if c is None else c for c in constraints]

    def __or__(self, other):
        if isinstance(other, AltConstraints):
            self.constraints.extend(other.constraints)
        else:
            self.constraints.append(other)
        return self

    def __call__(self, value):
        e_list = []
        for c in self.constraints:
            try:
                return c(value)
            except Exception as e:
                e_list.append(e)
        raise ValueError("all alternative constraints (%s) violated while testing value %r"
                         % (self.constraints, value))

    def long_description(self):
        cs = [c.long_description() for c in self.constraints if hasattr(c, 'long_description')]
        doc = ', or '.join(cs)
        if len(cs) > 1:
            return '(%s)' % doc
        else:
            return doc

    def short_description(self):
        cs = [c.short_description() for c in self.constraints
              if hasattr(c, 'short_description') and not c.short_description() is None]
        doc = ' or '.join(cs)
        if len(cs) > 1:
            return '(%s)' % doc
        else:
            return doc


class Constraints(Constraint):
    """Logical AND for constraints.

    An arbitrary number of constraints can be given. They are evaluated in the
    order in which they were specified. The return value of each constraint is
    passed an input into the next. The return value of the last constraint
    is the global return value. No intermediate exceptions are caught.

    Documentation is aggregated for all constraints.
    """
    def __init__(self, *constraints):
        """
        Parameters
        ----------
        *constraints
           Constraints all of which must be satisfied
        """
        super(Constraints, self).__init__()
        self.constraints = [EnsureNone() if c is None else c for c in constraints]

    def __and__(self, other):
        if isinstance(other, Constraints):
            self.constraints.extend(other.constraints)
        else:
            self.constraints.append(other)
        return self

    def __call__(self, value):
        for c in (self.constraints):
            value = c(value)
        return value

    def long_description(self):
        cs = [c.long_description() for c in self.constraints if hasattr(c, 'long_description')]
        doc = ', and '.join(cs)
        if len(cs) > 1:
            return '(%s)' % doc
        else:
            return doc

    def short_description(self):
        cs = [c.short_description() for c in self.constraints
              if hasattr(c, 'short_description') and not c.short_description() is None]
        doc = ' and '.join(cs)
        if len(cs) > 1:
            return '(%s)' % doc
        else:
            return doc

constraint_spec_map = {
    'float': EnsureFloat(),
    'int': EnsureInt(),
    'bool': EnsureBool(),
    'str': EnsureStr(),
}


def expand_contraint_spec(spec):
    """Helper to translate literal contraint specs into functional ones

    e.g. 'float' -> EnsureFloat()
    """
    if spec is None or hasattr(spec, '__call__'):
        return spec
    else:
        try:
            return constraint_spec_map[spec]
        except KeyError:
            raise ValueError("unsupport constraint specification '%r'" % (spec,))

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]

**Title:** Error message for invalid constraint includes internal class representation

**Description:**
When providing an invalid value to the `--demoarg` option, the error message includes the internal class representation, making it unclear to users.

**Example Code:**
```python
args = parser.parse_args(['--demoarg', 'abc'])
```

**Output:**
```
usage: __main__.py ... error: argument --demoarg: invalid <datalad.support.constraints.EnsureInt object at 0x7846219e6650> value: 'abc'
```

**Expected Behavior:**
The error message should be concise and user-friendly, displaying the constraint type without the internal class details, such as:
```
usage: __main__.py ... error: argument --demoarg: invalid constraint:int value: 'abc'
```

[/ISSUE]
{
    "test_param": "PASSED",
    "test_interface": "PASSED",
    "test_name_generation": "PASSED"
}
diff --git a/datalad/support/constraints.py b/datalad/support/constraints.py
index daf015a0e..bb70c3a75 100644
--- a/datalad/support/constraints.py
+++ b/datalad/support/constraints.py
@@ -41,6 +41,10 @@ class Constraint(object):
 
     # TODO: __str__ and/or __repr__ for every one of them
 
+    def __repr__(self):
+        """Rudimentary repr to avoid default scary to the user Python repr"""
+        return "constraint:%s" % self.short_description()
+
     def __and__(self, other):
         return Constraints(self, other)
 

{"datalad/support/constraints.py": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Helper for parameter validation, documentation and conversion\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nimport re\nfrom six.moves import map as map\n\n\ndef _strip_typerepr(s):\n    \"\"\"Strip away <class '...'> and <type '...'> decorations for docstrings\n    \"\"\"\n    return re.sub(\"<(class|type) '(\\S+)'>\", r'\\2', s)\n\n\ndef _type_str(t):\n    \"\"\"Get string human-readable representation of a data type\n\n    If type (t) is given as a tuple, assume ability to choose any of the\n    listed types, so those types listing get joined with |\n    \"\"\"\n    if isinstance(t, tuple):\n        s = ' or '.join(map(_type_str, t))\n        return (\"(%s)\" % s) if len(t) > 1 else s\n    return _strip_typerepr(str(t))\n\n\nclass Constraint(object):\n    \"\"\"Base class for input value conversion/validation.\n\n    These classes are also meant to be able to generate appropriate\n    documentation on an appropriate parameter value.\n    \"\"\"\n\n    # TODO: __str__ and/or __repr__ for every one of them\n\n    def __and__(self, other):\n        return Constraints(self, other)\n\n    def __or__(self, other):\n        return AltConstraints(self, other)\n\n    def __call__(self, value):\n        # do any necessary checks or conversions, potentially catch exceptions\n        # and generate a meaningful error message\n        raise NotImplementedError(\"abstract class\")\n\n    def long_description(self):\n        # return meaningful docs or None\n        # used as a comprehensive description in the parameter list\n        return self.short_description()\n\n    def short_description(self):\n        # return meaningful docs or None\n        # used as a condensed primer for the parameter lists\n        raise NotImplementedError(\"abstract class\")\n\n\nclass EnsureDType(Constraint):\n    \"\"\"Ensure that an input (or several inputs) are of a particular data type.\n    \"\"\"\n    # TODO extend to support numpy-like dtype specs, e.g. 'int64'\n    # in addition to functors\n    def __init__(self, dtype):\n        \"\"\"\n        Parameters\n        ----------\n        dtype : functor\n        \"\"\"\n        self._dtype = dtype\n\n    def __call__(self, value):\n        from six import binary_type, text_type\n        if hasattr(value, '__iter__') and \\\n                not (isinstance(value, (binary_type, text_type))):\n            return list(map(self._dtype, value))\n        else:\n            return self._dtype(value)\n\n    def short_description(self):\n        return _type_str(self._dtype)\n\n    def long_description(self):\n        return \"value must be convertible to type '%s'\" % self.short_description()\n\n\nclass EnsureInt(EnsureDType):\n    \"\"\"Ensure that an input (or several inputs) are of a data type 'int'.\n    \"\"\"\n    def __init__(self):\n        \"\"\"Initializes EnsureDType with int\"\"\"\n        EnsureDType.__init__(self, int)\n\n\nclass EnsureFloat(EnsureDType):\n    \"\"\"Ensure that an input (or several inputs) are of a data type 'float'.\n    \"\"\"\n    def __init__(self):\n        \"\"\"Initializes EnsureDType with float\"\"\"\n        EnsureDType.__init__(self, float)\n\n\nclass EnsureListOf(Constraint):\n    \"\"\"Ensure that an input is a list of a particular data type\n    \"\"\"\n    def __init__(self, dtype):\n        \"\"\"\n        Parameters\n        ----------\n        dtype : functor\n        \"\"\"\n        self._dtype = dtype\n        super(EnsureListOf, self).__init__()\n\n    def __call__(self, value):\n        return list(map(self._dtype, value))\n\n    def short_description(self):\n        return 'list(%s)' % _type_str(self._dtype)\n\n    def long_description(self):\n        return \"value must be convertible to %s\" % self.short_description()\n\n\nclass EnsureTupleOf(Constraint):\n    \"\"\"Ensure that an input is a tuple of a particular data type\n    \"\"\"\n    def __init__(self, dtype):\n        \"\"\"\n        Parameters\n        ----------\n        dtype : functor\n        \"\"\"\n        self._dtype = dtype\n        super(EnsureTupleOf, self).__init__()\n\n    def __call__(self, value):\n        return tuple(map(self._dtype, value))\n\n    def short_description(self):\n        return 'tuple(%s)' % _type_str(self._dtype)\n\n    def long_description(self):\n        return \"value must be convertible to %s\" % self.short_description()\n\n\nclass EnsureBool(Constraint):\n    \"\"\"Ensure that an input is a bool.\n\n    A couple of literal labels are supported, such as:\n    False: '0', 'no', 'off', 'disable', 'false'\n    True: '1', 'yes', 'on', 'enable', 'true'\n    \"\"\"\n    def __call__(self, value):\n        from six import binary_type, text_type\n        if isinstance(value, bool):\n            return value\n        elif isinstance(value, (binary_type, text_type)):\n            value = value.lower()\n            if value in ('0', 'no', 'off', 'disable', 'false'):\n                return False\n            elif value in ('1', 'yes', 'on', 'enable', 'true'):\n                return True\n        raise ValueError(\"value must be converted to boolean\")\n\n    def long_description(self):\n        return 'value must be convertible to type bool'\n\n    def short_description(self):\n        return 'bool'\n\n\nclass EnsureStr(Constraint):\n    \"\"\"Ensure an input is a string.\n\n    No automatic conversion is attempted.\n    \"\"\"\n    def __init__(self, min_len=0):\n        \"\"\"\n        Parameters\n        ----------\n        min_len: int, optional\n           Minimal length for a string.\n        \"\"\"\n        assert(min_len >= 0)\n        self._min_len = min_len\n        super(EnsureStr, self).__init__()\n\n    def __call__(self, value):\n        from six import binary_type, text_type\n        if not isinstance(value, (binary_type, text_type)):\n            # do not perform a blind conversion ala str(), as almost\n            # anything can be converted and the result is most likely\n            # unintended\n            raise ValueError(\"%s is not a string\" % repr(value))\n        if len(value) < self._min_len:\n            raise ValueError(\"%r is shorter than of minimal length %d\"\n                             % (value, self._min_len))\n        return value\n\n    def long_description(self):\n        return 'value must be a string'\n\n    def short_description(self):\n        return 'str'\n\n\nclass EnsureNone(Constraint):\n    \"\"\"Ensure an input is of value `None`\"\"\"\n    def __call__(self, value):\n        if value is None:\n            return None\n        else:\n            raise ValueError(\"value must be `None`\")\n\n    def short_description(self):\n        return 'None'\n\n    def long_description(self):\n        return 'value must be `None`'\n\n\nclass EnsureChoice(Constraint):\n    \"\"\"Ensure an input is element of a set of possible values\"\"\"\n\n    def __init__(self, *values):\n        \"\"\"\n        Parameters\n        ----------\n        *values\n           Possible accepted values.\n        \"\"\"\n        self._allowed = values\n        super(EnsureChoice, self).__init__()\n\n    def __call__(self, value):\n        if value not in self._allowed:\n            raise ValueError(\"value is not one of %s\" % (self._allowed,))\n        return value\n\n    def long_description(self):\n        return 'value must be one of %s' % (str(self._allowed),)\n\n    def short_description(self):\n        return '{%s}' % ', '.join([str(c) for c in self._allowed])\n\n\nclass EnsureRange(Constraint):\n    \"\"\"Ensure an input is within a particular range\n\n    No type checks are performed.\n    \"\"\"\n    def __init__(self, min=None, max=None):\n        \"\"\"\n        Parameters\n        ----------\n        min\n            Minimal value to be accepted in the range\n        max\n            Maximal value to be accepted in the range\n        \"\"\"\n        self._min = min\n        self._max = max\n        super(EnsureRange, self).__init__()\n\n    def __call__(self, value):\n        if self._min is not None:\n            if value < self._min:\n                raise ValueError(\"value must be at least %s\" % (self._min,))\n        if self._max is not None:\n            if value > self._max:\n                raise ValueError(\"value must be at most %s\" % (self._max,))\n        return value\n\n    def long_description(self):\n        min_str = '-inf' if self._min is None else str(self._min)\n        max_str = 'inf' if self._max is None else str(self._max)\n        return 'value must be in range [%s, %s]' % (min_str, max_str)\n\n    def short_description(self):\n        None\n\n\nclass AltConstraints(Constraint):\n    \"\"\"Logical OR for constraints.\n\n    An arbitrary number of constraints can be given. They are evaluated in the\n    order in which they were specified. The value returned by the first\n    constraint that does not raise an exception is the global return value.\n\n    Documentation is aggregated for all alternative constraints.\n    \"\"\"\n    def __init__(self, *constraints):\n        \"\"\"\n        Parameters\n        ----------\n        *constraints\n           Alternative constraints\n        \"\"\"\n        super(AltConstraints, self).__init__()\n        self.constraints = [EnsureNone() if c is None else c for c in constraints]\n\n    def __or__(self, other):\n        if isinstance(other, AltConstraints):\n            self.constraints.extend(other.constraints)\n        else:\n            self.constraints.append(other)\n        return self\n\n    def __call__(self, value):\n        e_list = []\n        for c in self.constraints:\n            try:\n                return c(value)\n            except Exception as e:\n                e_list.append(e)\n        raise ValueError(\"all alternative constraints (%s) violated while testing value %r\"\n                         % (self.constraints, value))\n\n    def long_description(self):\n        cs = [c.long_description() for c in self.constraints if hasattr(c, 'long_description')]\n        doc = ', or '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\n    def short_description(self):\n        cs = [c.short_description() for c in self.constraints\n              if hasattr(c, 'short_description') and not c.short_description() is None]\n        doc = ' or '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\n\nclass Constraints(Constraint):\n    \"\"\"Logical AND for constraints.\n\n    An arbitrary number of constraints can be given. They are evaluated in the\n    order in which they were specified. The return value of each constraint is\n    passed an input into the next. The return value of the last constraint\n    is the global return value. No intermediate exceptions are caught.\n\n    Documentation is aggregated for all constraints.\n    \"\"\"\n    def __init__(self, *constraints):\n        \"\"\"\n        Parameters\n        ----------\n        *constraints\n           Constraints all of which must be satisfied\n        \"\"\"\n        super(Constraints, self).__init__()\n        self.constraints = [EnsureNone() if c is None else c for c in constraints]\n\n    def __and__(self, other):\n        if isinstance(other, Constraints):\n            self.constraints.extend(other.constraints)\n        else:\n            self.constraints.append(other)\n        return self\n\n    def __call__(self, value):\n        for c in (self.constraints):\n            value = c(value)\n        return value\n\n    def long_description(self):\n        cs = [c.long_description() for c in self.constraints if hasattr(c, 'long_description')]\n        doc = ', and '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\n    def short_description(self):\n        cs = [c.short_description() for c in self.constraints\n              if hasattr(c, 'short_description') and not c.short_description() is None]\n        doc = ' and '.join(cs)\n        if len(cs) > 1:\n            return '(%s)' % doc\n        else:\n            return doc\n\nconstraint_spec_map = {\n    'float': EnsureFloat(),\n    'int': EnsureInt(),\n    'bool': EnsureBool(),\n    'str': EnsureStr(),\n}\n\n\ndef expand_contraint_spec(spec):\n    \"\"\"Helper to translate literal contraint specs into functional ones\n\n    e.g. 'float' -> EnsureFloat()\n    \"\"\"\n    if spec is None or hasattr(spec, '__call__'):\n        return spec\n    else:\n        try:\n            return constraint_spec_map[spec]\n        except KeyError:\n            raise ValueError(\"unsupport constraint specification '%r'\" % (spec,))\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]

**Title:** Error message for invalid constraint includes internal class representation

**Description:**
When providing an invalid value to the `--demoarg` option, the error message includes the internal class representation, making it unclear to users.

**Example Code:**
```python
args = parser.parse_args(['--demoarg', 'abc'])
```

**Output:**
```
usage: __main__.py ... error: argument --demoarg: invalid <datalad.support.constraints.EnsureInt object at 0x7846219e6650> value: 'abc'
```

**Expected Behavior:**
The error message should be concise and user-friendly, displaying the constraint type without the internal class details, such as:
```
usage: __main__.py ... error: argument --demoarg: invalid constraint:int value: 'abc'
```

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### datalad/support/constraints.py
# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
# ex: set sts=4 ts=4 sw=4 noet:
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
#   See COPYING file distributed along with the datalad package for the
#   copyright and license terms.
#
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""Helper for parameter validation, documentation and conversion"""

__docformat__ = 'restructuredtext'

import re
from six.moves import map as map


def _strip_typerepr(s):
    """Strip away <class '...'> and <type '...'> decorations for docstrings
    """
    return re.sub("<(class|type) '(\S+)'>", r'\2', s)


def _type_str(t):
    """Get string human-readable representation of a data type

    If type (t) is given as a tuple, assume ability to choose any of the
    listed types, so those types listing get joined with |
    """
    if isinstance(t, tuple):
        s = ' or '.join(map(_type_str, t))
        return ("(%s)" % s) if len(t) > 1 else s
    return _strip_typerepr(str(t))


class Constraint(object):
    """Base class for input value conversion/validation.

    These classes are also meant to be able to generate appropriate
    documentation on an appropriate parameter value.
    """

    # TODO: __str__ and/or __repr__ for every one of them

    def __and__(self, other):
        return Constraints(self, other)

    def __or__(self, other):
        return AltConstraints(self, other)

    def __call__(self, value):
        # do any necessary checks or conversions, potentially catch exceptions
        # and generate a meaningful error message
        raise NotImplementedError("abstract class")

    def long_description(self):
        # return meaningful docs or None
        # used as a comprehensive description in the parameter list
        return self.short_description()

    def short_description(self):
        # return meaningful docs or None
        # used as a condensed primer for the parameter lists
        raise NotImplementedError("abstract class")


class EnsureDType(Constraint):
    """Ensure that an input (or several inputs) are of a particular data type.
    """
    # TODO extend to support numpy-like dtype specs, e.g. 'int64'
    # in addition to functors
    def __init__(self, dtype):
        """
        Parameters
        ----------
        dtype : functor
        """
        self._dtype = dtype

    def __call__(self, value):
        from six import binary_type, text_type
        if hasattr(value, '__iter__') and \
                not (isinstance(value, (binary_type, text_type))):
            return list(map(self._dtype, value))
        else:
            return self._dtype(value)

    def short_description(self):
        return _type_str(self._dtype)

    def long_description(self):
        return "value must be convertible to type '%s'" % self.short_description()


class EnsureInt(EnsureDType):
    """Ensure that an input (or several inputs) are of a data type 'int'.
    """
    def __init__(self):
        """Initializes EnsureDType with int"""
        EnsureDType.__init__(self, int)


class EnsureFloat(EnsureDType):
    """Ensure that an input (or several inputs) are of a data type 'float'.
    """
    def __init__(self):
        """Initializes EnsureDType with float"""
        EnsureDType.__init__(self, float)


class EnsureListOf(Constraint):
    """Ensure that an input is a list of a particular data type
    """
    def __init__(self, dtype):
        """
        Parameters
        ----------
        dtype : functor
        """
        self._dtype = dtype
        super(EnsureListOf, self).__init__()

    def __call__(self, value):
        return list(map(self._dtype, value))

    def short_description(self):
        return 'list(%s)' % _type_str(self._dtype)

    def long_description(self):
        return "value must be convertible to %s" % self.short_description()


class EnsureTupleOf(Constraint):
    """Ensure that an input is a tuple of a particular data type
    """
    def __init__(self, dtype):
        """
        Parameters
        ----------
        dtype : functor
        """
        self._dtype = dtype
        super(EnsureTupleOf, self).__init__()

    def __call__(self, value):
        return tuple(map(self._dtype, value))

    def short_description(self):
        return 'tuple(%s)' % _type_str(self._dtype)

    def long_description(self):
        return "value must be convertible to %s" % self.short_description()


class EnsureBool(Constraint):
    """Ensure that an input is a bool.

    A couple of literal labels are supported, such as:
    False: '0', 'no', 'off', 'disable', 'false'
    True: '1', 'yes', 'on', 'enable', 'true'
    """
    def __call__(self, value):
        from six import binary_type, text_type
        if isinstance(value, bool):
            return value
        elif isinstance(value, (binary_type, text_type)):
            value = value.lower()
            if value in ('0', 'no', 'off', 'disable', 'false'):
                return False
            elif value in ('1', 'yes', 'on', 'enable', 'true'):
                return True
        raise ValueError("value must be converted to boolean")

    def long_description(self):
        return 'value must be convertible to type bool'

    def short_description(self):
        return 'bool'


class EnsureStr(Constraint):
    """Ensure an input is a string.

    No automatic conversion is attempted.
    """
    def __init__(self, min_len=0):
        """
        Parameters
        ----------
        min_len: int, optional
           Minimal length for a string.
        """
        assert(min_len >= 0)
        self._min_len = min_len
        super(EnsureStr, self).__init__()

    def __call__(self, value):
        from six import binary_type, text_type
        if not isinstance(value, (binary_type, text_type)):
            # do not perform a blind conversion ala str(), as almost
            # anything can be converted and the result is most likely
            # unintended
            raise ValueError("%s is not a string" % repr(value))
        if len(value) < self._min_len:
            raise ValueError("%r is shorter than of minimal length %d"
                             % (value, self._min_len))
        return value

    def long_description(self):
        return 'value must be a string'

    def short_description(self):
        return 'str'


class EnsureNone(Constraint):
    """Ensure an input is of value `None`"""
    def __call__(self, value):
        if value is None:
            return None
        else:
            raise ValueError("value must be `None`")

    def short_description(self):
        return 'None'

    def long_description(self):
        return 'value must be `None`'


class EnsureChoice(Constraint):
    """Ensure an input is element of a set of possible values"""

    def __init__(self, *values):
        """
        Parameters
        ----------
        *values
           Possible accepted values.
        """
        self._allowed = values
        super(EnsureChoice, self).__init__()

    def __call__(self, value):
        if value not in self._allowed:
            raise ValueError("value is not one of %s" % (self._allowed,))
        return value

    def long_description(self):
        return 'value must be one of %s' % (str(self._allowed),)

    def short_description(self):
        return '{%s}' % ', '.join([str(c) for c in self._allowed])


class EnsureRange(Constraint):
    """Ensure an input is within a particular range

    No type checks are performed.
    """
    def __init__(self, min=None, max=None):
        """
        Parameters
        ----------
        min
            Minimal value to be accepted in the range
        max
            Maximal value to be accepted in the range
        """
        self._min = min
        self._max = max
        super(EnsureRange, self).__init__()

    def __call__(self, value):
        if self._min is not None:
            if value < self._min:
                raise ValueError("value must be at least %s" % (self._min,))
        if self._max is not None:
            if value > self._max:
                raise ValueError("value must be at most %s" % (self._max,))
        return value

    def long_description(self):
        min_str = '-inf' if self._min is None else str(self._min)
        max_str = 'inf' if self._max is None else str(self._max)
        return 'value must be in range [%s, %s]' % (min_str, max_str)

    def short_description(self):
        None


class AltConstraints(Constraint):
    """Logical OR for constraints.

    An arbitrary number of constraints can be given. They are evaluated in the
    order in which they were specified. The value returned by the first
    constraint that does not raise an exception is the global return value.

    Documentation is aggregated for all alternative constraints.
    """
    def __init__(self, *constraints):
        """
        Parameters
        ----------
        *constraints
           Alternative constraints
        """
        super(AltConstraints, self).__init__()
        self.constraints = [EnsureNone() if c is None else c for c in constraints]

    def __or__(self, other):
        if isinstance(other, AltConstraints):
            self.constraints.extend(other.constraints)
        else:
            self.constraints.append(other)
        return self

    def __call__(self, value):
        e_list = []
        for c in self.constraints:
            try:
                return c(value)
            except Exception as e:
                e_list.append(e)
        raise ValueError("all alternative constraints (%s) violated while testing value %r"
                         % (self.constraints, value))

    def long_description(self):
        cs = [c.long_description() for c in self.constraints if hasattr(c, 'long_description')]
        doc = ', or '.join(cs)
        if len(cs) > 1:
            return '(%s)' % doc
        else:
            return doc

    def short_description(self):
        cs = [c.short_description() for c in self.constraints
              if hasattr(c, 'short_description') and not c.short_description() is None]
        doc = ' or '.join(cs)
        if len(cs) > 1:
            return '(%s)' % doc
        else:
            return doc


class Constraints(Constraint):
    """Logical AND for constraints.

    An arbitrary number of constraints can be given. They are evaluated in the
    order in which they were specified. The return value of each constraint is
    passed an input into the next. The return value of the last constraint
    is the global return value. No intermediate exceptions are caught.

    Documentation is aggregated for all constraints.
    """
    def __init__(self, *constraints):
        """
        Parameters
        ----------
        *constraints
           Constraints all of which must be satisfied
        """
        super(Constraints, self).__init__()
        self.constraints = [EnsureNone() if c is None else c for c in constraints]

    def __and__(self, other):
        if isinstance(other, Constraints):
            self.constraints.extend(other.constraints)
        else:
            self.constraints.append(other)
        return self

    def __call__(self, value):
        for c in (self.constraints):
            value = c(value)
        return value

    def long_description(self):
        cs = [c.long_description() for c in self.constraints if hasattr(c, 'long_description')]
        doc = ', and '.join(cs)
        if len(cs) > 1:
            return '(%s)' % doc
        else:
            return doc

    def short_description(self):
        cs = [c.short_description() for c in self.constraints
              if hasattr(c, 'short_description') and not c.short_description() is None]
        doc = ' and '.join(cs)
        if len(cs) > 1:
            return '(%s)' % doc
        else:
            return doc

constraint_spec_map = {
    'float': EnsureFloat(),
    'int': EnsureInt(),
    'bool': EnsureBool(),
    'str': EnsureStr(),
}


def expand_contraint_spec(spec):
    """Helper to translate literal contraint specs into functional ones

    e.g. 'float' -> EnsureFloat()
    """
    if spec is None or hasattr(spec, '__call__'):
        return spec
    else:
        try:
            return constraint_spec_map[spec]
        except KeyError:
            raise ValueError("unsupport constraint specification '%r'" % (spec,))

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user