예제 #1
0
    def test_cls(self):
        x = ["a"]
        y = listify(x)
        self.assertEqual(x, y)
        self.assertTrue(x is y)
        x.append("b")
        self.assertEqual(x, y)

        x = ["a"]
        y = listify(x, cls=None)
        self.assertEqual(x, y)
        self.assertTrue(x is y)
        x.append("b")
        self.assertEqual(x, y)

        class sublist(list):
            pass

        x = ["a"]
        y = listify(x, cls=sublist)
        self.assertEqual(x, y)
        self.assertFalse(x is y)
        x.append("b")
        self.assertNotEqual(x, y)

        x = ["a"]
        y = listify(x, cls=deque)
        self.assertEqual(x, list(y))
        self.assertEqual(len(x), len(y))
        self.assertFalse(x is y)
        x.append("b")
        self.assertNotEqual(x, list(y))
        self.assertNotEqual(len(x), len(y))
예제 #2
0
    def test_cls(self):
        x = ['a']
        y = listify(x)
        assert x == y
        assert x is y
        x.append('b')
        assert x == y

        x = ['a']
        y = listify(x, cls=None)
        assert x == y
        assert x is y
        x.append('b')
        assert x == y

        class sublist(list):
            pass

        x = ['a']
        y = listify(x, cls=sublist)
        assert isinstance(y, sublist)
        assert x == y
        assert x is not y
        x.append('b')
        assert x != y

        x = ['a']
        y = listify(x, cls=deque)
        assert isinstance(y, deque)
        assert x == list(y)
        assert len(x) == len(y)
        assert x is not y
        x.append('b')
        assert x != list(y)
        assert len(x) != len(y)
예제 #3
0
    def test_default(self):
        assert [] == listify([], default=None)
        assert ['a'] == listify(['a'], minlen=1, default=None)
        assert ['a', None] == listify(['a'], minlen=2, default=None)
        assert ['a', None, None] == listify(['a'], minlen=3, default=None)

        assert [] == listify([], default='XXX')
        assert ['a'] == listify(['a'], minlen=1, default='XXX')
        assert ['a', 'XXX'] == listify(['a'], minlen=2, default='XXX')
        assert ['a', 'XXX', 'XXX'] == listify(['a'], minlen=3, default='XXX')
예제 #4
0
    def test_default(self):
        self.assertEqual([], listify([], default=None))
        self.assertEqual(["a"], listify(["a"], minlen=1, default=None))
        self.assertEqual(["a", None], listify(["a"], minlen=2, default=None))
        self.assertEqual(["a", None, None], listify(["a"], minlen=3,
                         default=None))

        self.assertEqual([], listify([], default="XXX"))
        self.assertEqual(["a"], listify(["a"], minlen=1, default="XXX"))
        self.assertEqual(["a", "XXX"], listify(["a"], minlen=2,
                         default="XXX"))
        self.assertEqual(["a", "XXX", "XXX"], listify(["a"], minlen=3,
                         default="XXX"))
예제 #5
0
파일: inspect.py 프로젝트: magfest/pockets
def collect_superclasses(cls, terminal_class=None, modules=None):
    """
    Recursively collects all ancestor superclasses in the inheritance
    hierarchy of the given class, including the class itself.

    Note:
        Inlcudes `cls` itself. Will not include `terminal_class`.

    Args:
        cls (class): The class object from which the collection should begin.
        terminal_class (class or list): If `terminal_class` is encountered in
            the hierarchy, we stop ascending the tree. `terminal_class` will
            not be included in the returned list.
        modules (string, module, or list): If `modules` is passed, we only
            return classes that are in the given module/modules. This can be
            used to exclude base classes that come from external libraries.

    Returns:
        list: A list of `class` objects from which `cls` inherits. This list
            will include `cls` itself.
    """
    terminal_class = listify(terminal_class)
    if modules is not None:
        modules = listify(modules)
        module_strings = []
        for m in modules:
            if isinstance(m, six.string_types):
                module_strings.append(m)
            else:
                module_strings.append(m.__name__)
        modules = module_strings

    superclasses = set()
    is_in_module = modules is None or cls.__module__ in modules
    if is_in_module and cls not in terminal_class:
        superclasses.add(cls)
        for base in cls.__bases__:
            superclasses.update(
                collect_superclasses(base, terminal_class, modules))

    return list(superclasses)
예제 #6
0
def argmod(*args):
    """
    Decorator that intercepts and modifies function arguments.

    Args:
        from_param (str|list): A parameter or list of possible parameters that
            should be modified using `modifier_func`. Passing a list of
            possible parameters is useful when a function's parameter names
            have changed, but you still want to support the old parameter
            names.
        to_param (str): Optional. If given, to_param will be used as the
            parameter name for the modified argument. If not given, to_param
            will default to the last parameter given in `from_param`.
        modifier_func (callable): The function used to modify the `from_param`.

    Returns:
        function: A function that modifies the given `from_param` before the
            function is called.
    """
    from_param = listify(args[0])
    to_param = from_param[-1] if len(args) < 3 else args[1]
    modifier_func = args[-1]

    def _decorator(func):
        try:
            argspec = inspect.getfullargspec(unwrap(func))
        except AttributeError:
            argspec = inspect.getargspec(unwrap(func))
        if to_param not in argspec.args:
            return func
        arg_index = argspec.args.index(to_param)

        @wraps(func)
        def _modifier(*args, **kwargs):
            kwarg = False
            for arg in from_param:
                if arg in kwargs:
                    kwarg = arg
                    break

            if kwarg:
                kwargs[to_param] = modifier_func(kwargs.pop(kwarg))
            elif arg_index < len(args):
                args = list(args)
                args[arg_index] = modifier_func(args[arg_index])
            return func(*args, **kwargs)

        return _modifier

    return _decorator
예제 #7
0
    def test_list_identity(self):
        x = ['a']
        y = listify(x)
        assert x == y
        assert x is y
        x.append('b')
        assert x == y

        class sublist(list):
            pass

        x = sublist('a')
        y = listify(x)
        assert x == y
        assert x is y
        x.append('b')
        assert x == y

        x = sublist('a')
        y = listify(x, cls=list)
        assert x == y
        assert x is y
        x.append('b')
        assert x == y
예제 #8
0
    def test_list_identity(self):
        x = ["a"]
        y = listify(x)
        self.assertEqual(x, y)
        self.assertTrue(x is y)
        x.append("b")
        self.assertEqual(x, y)

        class sublist(list):
            pass

        x = sublist("a")
        y = listify(x)
        self.assertEqual(x, y)
        self.assertTrue(x is y)
        x.append("b")
        self.assertEqual(x, y)

        x = sublist("a")
        y = listify(x, cls=list)
        self.assertEqual(x, y)
        self.assertTrue(x is y)
        x.append("b")
        self.assertEqual(x, y)
예제 #9
0
def splitify(value, separator=",", strip=True, include_empty=False):
    """
    Convert a value to a list using a supercharged `split()`.

    If `value` is a string, it is split by `separator`. If `separator` is
    `None` or empty, no attempt to split is made, and `value` is returned as
    the only item in a list.

    If `strip` is `True`, then the split strings will be stripped of
    whitespace. If `strip` is a string, then the split strings will be
    stripped of the given string.

    If `include_empty` is `False`, then empty split strings will not be
    included in the returned list.

    If `value` is `None` an empty list is returned.

    If `value` is already "listy", it is returned as-is.

    If `value` is any other type, it is returned as the only item in a list.

    >>> splitify("first item, second item")
    ['first item', 'second item']
    >>> splitify("first path: second path: :skipped empty path", ":")
    ['first path', 'second path', 'skipped empty path']
    >>> splitify(["already", "split"])
    ['already', 'split']
    >>> splitify(None)
    []
    >>> splitify(1969)
    [1969]
    """
    if is_listy(value):
        return value

    if isinstance(value, str) and separator:
        parts = value.split(separator)
        if strip:
            strip = None if strip is True else strip
            parts = [s.strip(strip) for s in parts]
        return [s for s in parts if include_empty or s]
    return listify(value)
예제 #10
0
 def test_tuple(self):
     assert [] == listify(tuple())
     assert ['a'] == listify(tuple('a'))
     assert ['a', 'b'] == listify(tuple(['a', 'b']))
예제 #11
0
 def test_string(self):
     assert [''] == listify('')
     assert ['a'] == listify('a')
     assert ['ab'] == listify('ab')
예제 #12
0
 def test_set(self):
     assert [] == listify(set())
     assert ['a'] == listify(set('a'))
     assert ['a', 'b'] == sorted(listify(set(['a', 'b'])))
예제 #13
0
    def test_falsey(self):
        assert [0] == listify(0)
        assert [0] == listify(0, minlen=0)
        assert [0] == listify(0, minlen=1)
        assert [0, None] == listify(0, minlen=2)

        assert [''] == listify('')
        assert [''] == listify('', minlen=0)
        assert [''] == listify('', minlen=1)
        assert ['', None] == listify('', minlen=2)

        assert [] == listify([])
        assert [] == listify([], minlen=0)
        assert [None] == listify([], minlen=1)
        assert [None, None] == listify([], minlen=2)
예제 #14
0
 def test_none(self):
     assert [] == listify(None)
     assert [] == listify(None, minlen=0)
     assert [None] == listify(None, minlen=1)
     assert [None, None] == listify(None, minlen=2)
예제 #15
0
 def test_set(self):
     self.assertEqual([], listify(set()))
     self.assertEqual(["a"], listify(set("a")))
     self.assertEqual(["a", "b"], sorted(listify(set(["a", "b"]))))
예제 #16
0
 def test_list(self):
     self.assertEqual([], listify([]))
     self.assertEqual(["a"], listify(["a"]))
     self.assertEqual(["a", "b"], listify(["a", "b"]))
예제 #17
0
 def test_dict(self):
     assert [{}] == listify({})
     assert [{'a': 'A'}] == listify({'a': 'A'})
     assert [{'a': 'A', 'b': 'B'}] == listify({'a': 'A', 'b': 'B'})
예제 #18
0
 def test_string(self):
     self.assertEqual([""], listify(""))
     self.assertEqual(["a"], listify("a"))
     self.assertEqual(["ab"], listify("ab"))
예제 #19
0
 def test_object(self):
     a = object()
     self.assertEqual([a], listify(a))
예제 #20
0
 def test_dict(self):
     self.assertEqual([{}], listify({}))
     self.assertEqual([{"a": "A"}], listify({"a": "A"}))
     self.assertEqual([{"a": "A", "b": "B"}], listify({"a": "A", "b": "B"}))
예제 #21
0
 def test_list(self):
     assert [] == listify([])
     assert ['a'] == listify(['a'])
     assert ['a', 'b'] == listify(['a', 'b'])
예제 #22
0
    def test_falsey(self):
        self.assertEqual([0], listify(0))
        self.assertEqual([0], listify(0, minlen=0))
        self.assertEqual([0], listify(0, minlen=1))
        self.assertEqual([0, None], listify(0, minlen=2))

        self.assertEqual([""], listify(""))
        self.assertEqual([""], listify("", minlen=0))
        self.assertEqual([""], listify("", minlen=1))
        self.assertEqual(["", None], listify("", minlen=2))

        self.assertEqual([], listify([]))
        self.assertEqual([], listify([], minlen=0))
        self.assertEqual([None], listify([], minlen=1))
        self.assertEqual([None, None], listify([], minlen=2))
예제 #23
0
    def test_minlen(self):
        self.assertEqual([], listify([]))
        self.assertEqual([], listify([], minlen=None))
        self.assertEqual([], listify([], minlen=-1))
        self.assertEqual([], listify([], minlen=0))
        self.assertEqual([None], listify([], minlen=1))
        self.assertEqual([None, None], listify([], minlen=2))
        self.assertEqual([None, None, None], listify([], minlen=3))

        self.assertEqual(["a"], listify(["a"]))
        self.assertEqual(["a"], listify(["a"], minlen=None))
        self.assertEqual(["a"], listify(["a"], minlen=-1))
        self.assertEqual(["a"], listify(["a"], minlen=0))
        self.assertEqual(["a"], listify(["a"], minlen=1))
        self.assertEqual(["a", None], listify(["a"], minlen=2))
        self.assertEqual(["a", None, None], listify(["a"], minlen=3))

        self.assertEqual(["a", "b"], listify(["a", "b"]))
        self.assertEqual(["a", "b"], listify(["a", "b"], minlen=None))
        self.assertEqual(["a", "b"], listify(["a", "b"], minlen=-1))
        self.assertEqual(["a", "b"], listify(["a", "b"], minlen=0))
        self.assertEqual(["a", "b"], listify(["a", "b"], minlen=1))
        self.assertEqual(["a", "b"], listify(["a", "b"], minlen=2))
        self.assertEqual(["a", "b", None], listify(["a", "b"], minlen=3))
        self.assertEqual(["a", "b", None, None], listify(["a", "b"], minlen=4))
예제 #24
0
 def test_tuple(self):
     self.assertEqual([], listify(tuple()))
     self.assertEqual(["a"], listify(tuple("a")))
     self.assertEqual(["a", "b"], listify(tuple(["a", "b"])))
예제 #25
0
 def test_object(self):
     a = object()
     assert [a] == listify(a)
예제 #26
0
def resolve(name, modules=None):
    """Resolve a dotted name to an object (usually class, module, or function).

    If `name` is a string, attempt to resolve it according to Python
    dot notation, e.g. "path.to.MyClass". If `name` is anything other than a
    string, return it immediately:

    >>> resolve("calendar.TextCalendar")
    <class 'calendar.TextCalendar'>
    >>> resolve(object()) #doctest: +ELLIPSIS
    <object object at 0x...>

    If `modules` is specified, then resolution of `name` is restricted
    to the given modules. Leading dots are allowed in `name`, but they are
    ignored. Resolution **will not** traverse up the module path if `modules`
    is specified.

    If `modules` is not specified and `name` has leading dots, then resolution
    is first attempted relative to the calling function's module, and then
    absolutely. Resolution **will** traverse up the module path. If `name` has
    no leading dots, resolution is first attempted absolutely and then
    relative to the calling module.

    Warning:
        Do not resolve strings supplied by an end user without specifying
        `modules`. Instantiating an arbitrary object specified by an end user
        can introduce a potential security risk.

        To avoid this, restrict the search path by explicitly specifying
        `modules`.

    Restricting `name` resolution to a set of `modules`:

    >>> resolve("pockets.camel") #doctest: +ELLIPSIS
    <function camel at 0x...>
    >>> resolve("pockets.camel", modules=["re", "six"]) #doctest: +ELLIPSIS
    Traceback (most recent call last):
      ...
    ValueError: Unable to resolve 'pockets.camel' in modules: ['re', 'six']

    Args:
        name (str or object): A dotted name.

        modules (str or list, optional): A module or list of modules, under
            which to search for `name`.

    Returns:
        object: The object specified by `name`.

    Raises:
        ValueError: If `name` can't be resolved.

    """
    if not isinstance(name, string_types):
        return name

    obj_path = name.split('.')
    search_paths = []
    if modules:
        while not obj_path[0]:
            obj_path.pop(0)
        for module_path in listify(modules):
            search_paths.append(module_path.split('.') + obj_path)
    else:
        caller = inspect.getouterframes(inspect.currentframe())[1][0].f_globals
        module_path = caller['__name__'].split('.')
        if not obj_path[0]:
            obj_path.pop(0)
            while not obj_path[0]:
                obj_path.pop(0)
                if module_path:
                    module_path.pop()

            search_paths.append(module_path + obj_path)
            search_paths.append(obj_path)
        else:
            search_paths.append(obj_path)
            search_paths.append(module_path + obj_path)

    for path in search_paths:
        try:
            obj = functools.reduce(getattr, path[1:], __import__(path[0]))
        except (AttributeError, ImportError):
            pass
        else:
            return obj

    raise ValueError("Unable to resolve '{0}' "
                     "in modules: {1}".format(name, modules))
예제 #27
0
def camel(s, sep="_", lower_initial=False, upper_segments=None,
          preserve_upper=False):
    """Convert underscore_separated string (aka snake_case) to CamelCase.

    Works on full sentences as well as individual words:

    >>> camel("hello_world!")
    'HelloWorld!'
    >>> camel("Totally works as_expected, even_with_whitespace!")
    'Totally Works AsExpected, EvenWithWhitespace!'

    Args:
        sep (string, optional): Delineates segments of `s` that will be
            CamelCased. Defaults to an underscore "_".

            For example, if you want to CamelCase a dash separated word:

            >>> camel("xml-http-request", sep="-")
            'XmlHttpRequest'

        lower_initial (bool, int, or list, optional): If True, the initial
            character of each camelCased word will be lowercase. If False, the
            initial character of each CamelCased word will be uppercase.
            Defaults to False:

            >>> camel("http_request http_response")
            'HttpRequest HttpResponse'
            >>> camel("http_request http_response", lower_initial=True)
            'httpRequest httpResponse'

            Optionally, `lower_initial` can be an int or a list of ints,
            indicating which individual segments of each CamelCased word
            should start with a lowercase. Supports negative numbers to index
            segments from the right:

            >>> camel("xml_http_request", lower_initial=0)
            'xmlHttpRequest'
            >>> camel("xml_http_request", lower_initial=-1)
            'XmlHttprequest'
            >>> camel("xml_http_request", lower_initial=[0, 1])
            'xmlhttpRequest'

        upper_segments (int or list, optional): Indicates which segments of
           CamelCased words should be fully uppercased, instead of just
           capitalizing the first letter.

           Can be an int, indicating a single segment, or a list of ints,
           indicating multiple segments. Supports negative numbers to index
           segments from the right.

           `upper_segments` is helpful when dealing with acronyms:

            >>> camel("tcp_socket_id", upper_segments=0)
            'TCPSocketId'
            >>> camel("tcp_socket_id", upper_segments=[0, -1])
            'TCPSocketID'
            >>> camel("tcp_socket_id", upper_segments=[0, -1], lower_initial=1)
            'TCPsocketID'

        preserve_upper (bool): If True, existing uppercase characters will
            not be automatically lowercased. Defaults to False.

            >>> camel("xml_HTTP_reQuest")
            'XmlHttpRequest'
            >>> camel("xml_HTTP_reQuest", preserve_upper=True)
            'XmlHTTPReQuest'

    Returns:
        str: CamelCased version of `s`.

    """
    if isinstance(lower_initial, bool):
        lower_initial = [0] if lower_initial else []
    else:
        lower_initial = listify(lower_initial)
    upper_segments = listify(upper_segments)
    result = []
    for word in _whitespace_group_re.split(s):
        segments = [segment for segment in word.split(sep) if segment]
        count = len(segments)
        for i, segment in enumerate(segments):
            upper = i in upper_segments or (i - count) in upper_segments
            lower = i in lower_initial or (i - count) in lower_initial
            if upper and lower:
                if preserve_upper:
                    segment = segment[0] + segment[1:].upper()
                else:
                    segment = segment[0].lower() + segment[1:].upper()
            elif upper:
                segment = segment.upper()
            elif lower:
                if not preserve_upper:
                    segment = segment.lower()
            elif preserve_upper:
                segment = segment[0].upper() + segment[1:]
            else:
                segment = segment[0].upper() + segment[1:].lower()
            result.append(segment)

    return "".join(result)
예제 #28
0
 def test_none(self):
     self.assertEqual([], listify(None))
     self.assertEqual([], listify(None, minlen=0))
     self.assertEqual([None], listify(None, minlen=1))
     self.assertEqual([None, None], listify(None, minlen=2))
예제 #29
0
def resolve(name, modules=None):
    """Resolve a dotted name to an object (usually class, module, or function).

    If `name` is a string, attempt to resolve it according to Python
    dot notation, e.g. "path.to.MyClass". If `name` is anything other than a
    string, return it immediately:

    >>> resolve("calendar.TextCalendar")
    <class 'calendar.TextCalendar'>
    >>> resolve(object()) #doctest: +ELLIPSIS
    <object object at 0x...>

    If `modules` is specified, then resolution of `name` is restricted
    to the given modules. Leading dots are allowed in `name`, but they are
    ignored. Resolution **will not** traverse up the module path if `modules`
    is specified.

    If `modules` is not specified and `name` has leading dots, then resolution
    is first attempted relative to the calling function's module, and then
    absolutely. Resolution **will** traverse up the module path. If `name` has
    no leading dots, resolution is first attempted absolutely and then
    relative to the calling module.

    Warning:
        Do not resolve strings supplied by an end user without specifying
        `modules`. Instantiating an arbitrary object specified by an end user
        can introduce a potential security risk.

        To avoid this, restrict the search path by explicitly specifying
        `modules`.

    Restricting `name` resolution to a set of `modules`:

    >>> resolve("pockets.camel") #doctest: +ELLIPSIS
    <function camel at 0x...>
    >>> resolve("pockets.camel", modules=["re", "six"]) #doctest: +ELLIPSIS
    Traceback (most recent call last):
      ...
    ValueError: Unable to resolve 'pockets.camel' in modules: ['re', 'six']

    Args:
        name (str or object): A dotted name.

        modules (str or list, optional): A module or list of modules, under
            which to search for `name`.

    Returns:
        object: The object specified by `name`.

    Raises:
        ValueError: If `name` can't be resolved.

    """
    if not isinstance(name, string_types):
        return name

    obj_path = name.split('.')
    search_paths = []
    if modules:
        while not obj_path[0]:
            obj_path.pop(0)
        for module_path in listify(modules):
            search_paths.append(module_path.split('.') + obj_path)
    else:
        caller = inspect.getouterframes(inspect.currentframe())[1][0].f_globals
        module_path = caller['__name__'].split('.')
        if not obj_path[0]:
            obj_path.pop(0)
            while not obj_path[0]:
                obj_path.pop(0)
                if module_path:
                    module_path.pop()

            search_paths.append(module_path + obj_path)
            search_paths.append(obj_path)
        else:
            search_paths.append(obj_path)
            search_paths.append(module_path + obj_path)

    for path in search_paths:
        try:
            obj = functools.reduce(getattr, path[1:], __import__(path[0]))
        except (AttributeError, ImportError):
            pass
        else:
            return obj

    raise ValueError("Unable to resolve '{0}' "
                     "in modules: {1}".format(name, modules))
예제 #30
0
    def test_minlen(self):
        assert [] == listify([])
        assert [] == listify([], minlen=None)
        assert [] == listify([], minlen=-1)
        assert [] == listify([], minlen=0)
        assert [None] == listify([], minlen=1)
        assert [None, None] == listify([], minlen=2)
        assert [None, None, None] == listify([], minlen=3)

        assert ['a'] == listify(['a'])
        assert ['a'] == listify(['a'], minlen=None)
        assert ['a'] == listify(['a'], minlen=-1)
        assert ['a'] == listify(['a'], minlen=0)
        assert ['a'] == listify(['a'], minlen=1)
        assert ['a', None] == listify(['a'], minlen=2)
        assert ['a', None, None] == listify(['a'], minlen=3)

        assert ['a', 'b'] == listify(['a', 'b'])
        assert ['a', 'b'] == listify(['a', 'b'], minlen=None)
        assert ['a', 'b'] == listify(['a', 'b'], minlen=-1)
        assert ['a', 'b'] == listify(['a', 'b'], minlen=0)
        assert ['a', 'b'] == listify(['a', 'b'], minlen=1)
        assert ['a', 'b'] == listify(['a', 'b'], minlen=2)
        assert ['a', 'b', None] == listify(['a', 'b'], minlen=3)
        assert ['a', 'b', None, None] == listify(['a', 'b'], minlen=4)
예제 #31
0
def camel(s,
          sep="_",
          lower_initial=False,
          upper_segments=None,
          preserve_upper=False):
    """Convert underscore_separated string (aka snake_case) to CamelCase.

    Works on full sentences as well as individual words:

    >>> camel("hello_world!")
    'HelloWorld!'
    >>> camel("Totally works as_expected, even_with_whitespace!")
    'Totally Works AsExpected, EvenWithWhitespace!'

    Args:
        sep (string, optional): Delineates segments of `s` that will be
            CamelCased. Defaults to an underscore "_".

            For example, if you want to CamelCase a dash separated word:

            >>> camel("xml-http-request", sep="-")
            'XmlHttpRequest'

        lower_initial (bool, int, or list, optional): If True, the initial
            character of each camelCased word will be lowercase. If False, the
            initial character of each CamelCased word will be uppercase.
            Defaults to False:

            >>> camel("http_request http_response")
            'HttpRequest HttpResponse'
            >>> camel("http_request http_response", lower_initial=True)
            'httpRequest httpResponse'

            Optionally, `lower_initial` can be an int or a list of ints,
            indicating which individual segments of each CamelCased word
            should start with a lowercase. Supports negative numbers to index
            segments from the right:

            >>> camel("xml_http_request", lower_initial=0)
            'xmlHttpRequest'
            >>> camel("xml_http_request", lower_initial=-1)
            'XmlHttprequest'
            >>> camel("xml_http_request", lower_initial=[0, 1])
            'xmlhttpRequest'

        upper_segments (int or list, optional): Indicates which segments of
           CamelCased words should be fully uppercased, instead of just
           capitalizing the first letter.

           Can be an int, indicating a single segment, or a list of ints,
           indicating multiple segments. Supports negative numbers to index
           segments from the right.

           `upper_segments` is helpful when dealing with acronyms:

            >>> camel("tcp_socket_id", upper_segments=0)
            'TCPSocketId'
            >>> camel("tcp_socket_id", upper_segments=[0, -1])
            'TCPSocketID'
            >>> camel("tcp_socket_id", upper_segments=[0, -1], lower_initial=1)
            'TCPsocketID'

        preserve_upper (bool): If True, existing uppercase characters will
            not be automatically lowercased. Defaults to False.

            >>> camel("xml_HTTP_reQuest")
            'XmlHttpRequest'
            >>> camel("xml_HTTP_reQuest", preserve_upper=True)
            'XmlHTTPReQuest'

    Returns:
        str: CamelCased version of `s`.

    """
    if isinstance(lower_initial, bool):
        lower_initial = [0] if lower_initial else []
    else:
        lower_initial = listify(lower_initial)
    upper_segments = listify(upper_segments)
    result = []
    for word in _whitespace_group_re.split(s):
        segments = [segment for segment in word.split(sep) if segment]
        count = len(segments)
        for i, segment in enumerate(segments):
            upper = i in upper_segments or (i - count) in upper_segments
            lower = i in lower_initial or (i - count) in lower_initial
            if upper and lower:
                if preserve_upper:
                    segment = segment[0] + segment[1:].upper()
                else:
                    segment = segment[0].lower() + segment[1:].upper()
            elif upper:
                segment = segment.upper()
            elif lower:
                if not preserve_upper:
                    segment = segment.lower()
            elif preserve_upper:
                segment = segment[0].upper() + segment[1:]
            else:
                segment = segment[0].upper() + segment[1:].lower()
            result.append(segment)

    return "".join(result)
예제 #32
0
def resolve(name, modules=None):
    """
    Resolve a dotted name to an object (usually class, module, or function).

    If `name` is a string, attempt to resolve it according to Python
    dot notation, e.g. "path.to.MyClass". If `name` is anything other than a
    string, return it immediately:

    >>> resolve("calendar.TextCalendar")
    <class 'calendar.TextCalendar'>
    >>> resolve(object())
    <object object at 0x...>

    If `modules` is specified, then resolution of `name` is restricted
    to the given modules. Leading dots are allowed in `name`, but they are
    ignored. Resolution **will not** traverse up the module path if `modules`
    is specified.

    If `modules` is not specified and `name` has leading dots, then resolution
    is first attempted relative to the calling function's module, and then
    absolutely. Resolution **will** traverse up the module path. If `name` has
    no leading dots, resolution is first attempted absolutely and then
    relative to the calling module.

    Pass an empty string for `modules` to only use absolute resolution.

    Warning:
        Do not resolve strings supplied by an end user without specifying
        `modules`. Instantiating an arbitrary object specified by an end user
        can introduce a potential security risk.

        To avoid this, restrict the search path by explicitly specifying
        `modules`.

    Restricting `name` resolution to a set of `modules`:

    >>> resolve("pockets.camel")
    <function camel at 0x...>
    >>> resolve("pockets.camel", modules=["re", "six"])
    Traceback (most recent call last):
    ValueError: Unable to resolve 'pockets.camel' in modules: ['re', 'six']
      ...

    Args:
        name (str or object): A dotted name.

        modules (str, module, or list, optional): A module or list of modules,
            under which to search for `name`.

    Returns:
        object: The object specified by `name`.

    Raises:
        ValueError: If `name` can't be resolved.

    """
    if not isinstance(name, string_types):
        return name

    obj_path = splitify(name, ".", include_empty=True)
    search_paths = []
    if modules is not None:
        while not obj_path[0].strip():
            obj_path.pop(0)
        for module_path in listify(modules):
            search_paths.append(splitify(module_path, ".") + obj_path)
    else:
        caller = inspect.getouterframes(inspect.currentframe())[1][0].f_globals
        module_path = caller["__name__"].split(".")
        if not obj_path[0]:
            obj_path.pop(0)
            while not obj_path[0]:
                obj_path.pop(0)
                if module_path:
                    module_path.pop()

            search_paths.append(module_path + obj_path)
            search_paths.append(obj_path)
        else:
            search_paths.append(obj_path)
            search_paths.append(module_path + obj_path)

    exceptions = []
    for path in search_paths:
        # Import the most deeply nested module available
        module = None
        module_path = []
        obj_path = list(path)
        while obj_path:
            module_name = obj_path.pop(0)
            while not module_name:
                module_name = obj_path.pop(0)
            if isinstance(module_name, string_types):
                package = ".".join(module_path + [module_name])
                try:
                    module = __import__(package, fromlist=module_name)
                except ImportError as ex:
                    exceptions.append(ex)
                    obj_path = [module_name] + obj_path
                    break
                else:
                    module_path.append(module_name)
            else:
                module = module_name
                module_path.append(module.__name__)

        if module:
            if obj_path:
                try:
                    return functools.reduce(getattr, obj_path, module)
                except AttributeError as ex:
                    exceptions.append(ex)
            else:
                return module

    if modules:
        msg = "Unable to resolve '{0}' in modules: {1}".format(name, modules)
    else:
        msg = "Unable to resolve '{0}'".format(name)

    if exceptions:
        msgs = ["{0}: {1}".format(type(e).__name__, e) for e in exceptions]
        raise ValueError("\n    ".join([msg] + msgs))
    else:
        raise ValueError(msg)