Completed
Push — master ( 52e99c...3e76cf )
by Satoru
38s
created

list_processors_by_ext()   B

Complexity

Conditions 4

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 4
dl 0
loc 23
rs 8.7972
c 2
b 0
f 1
1
#
2
# Copyright (C) 2018 Satoru SATOH <ssato @ redhat.com>
3
# License: MIT
4
#
5
r"""Abstract processor module.
6
7
.. versionadded:: 0.9.5
8
9
   - Add to abstract processors such like Parsers (loaders and dumpers).
10
"""
11
from __future__ import absolute_import
12
13
import operator
14
import pkg_resources
15
16
import anyconfig.compat
17
18
19
class Processor(object):
20
    """
21
    Abstract processor class to provide basic implementation of some methods,
22
    interfaces and members.
23
24
    - _type: type indicates data types it can process
25
    - _priority: Priority to select it if there are others of same type
26
    - _extensions: File extensions of data type it can process
27
    """
28
    _type = None
29
    _priority = 0   # 0 (lowest priority) .. 99  (highest priority)
30
    _extensions = []
31
32
    @classmethod
33
    def type(cls):
34
        """Processors' type
35
        """
36
        return cls._type
37
38
    @classmethod
39
    def priority(cls):
40
        """Processors's priority
41
        """
42
        return cls._priority
43
44
    @classmethod
45
    def extensions(cls):
46
        """File extensions which this process can process
47
        """
48
        return cls._extensions
49
50
51
def _load_plugins_itr(pgroup, safe=True):
52
    """
53
    .. seealso:: the doc of :func:`load_plugins`
54
    """
55
    for res in pkg_resources.iter_entry_points(pgroup):
56
        try:
57
            yield res.load()
58
        except ImportError:
59
            if safe:
60
                continue
61
            raise
62
63
64
def load_plugins(pgroup, safe=True):
65
    """
66
    :param pgroup: A string represents plugin type, e.g. anyconfig_backends
67
    :param safe: Do not raise ImportError during load if True
68
    :raises: ImportError
69
    """
70
    return list(_load_plugins_itr(pgroup, safe=safe))
71
72
73
def list_processors_by_type(prs):
74
    """
75
    :param prs: A list of instances of :class:`Processor`
76
    :return: List (generator) of (processor_type, [processor_cls])
77
78
    >>> class A(Processor):
79
    ...    _type = "a"
80
    >>> class A2(A):
81
    ...    _priority = 2
82
    >>> class B(Processor):
83
    ...    _type = "b"
84
    >>> g = list_processors_by_type([B, A2, A])
85
    >>> list(g)  # doctest: +NORMALIZE_WHITESPACE
86
    [('a', [<class 'anyconfig.processors.A'>,
87
            <class 'anyconfig.processors.A2'>]),
88
     ('b', [<class 'anyconfig.processors.B'>])]
89
    """
90
    return ((t, sorted(ps, key=operator.methodcaller("priority"))) for t, ps
91
            in anyconfig.utils.groupby(prs, operator.methodcaller("type")))
92
93
94
def _ext_proc_tpls_to_procs(xps):
95
    """List processors by each priority.
96
97
    :param xps: A list of (file_extension, processor_cls)
98
    :return: List of [processor_cls]
99
    """
100
    return sorted((operator.itemgetter(1)(xp) for xp in xps),
101
                  key=operator.methodcaller("priority"))
102
103
104
def list_processors_by_ext(prs):
105
    """
106
    :param prs: A list of instances of :class:`Processor`
107
    :return: List (generator) of (file_extension, [processor_cls])
108
109
    >>> class A(Processor):
110
    ...    _extensions = ['json']
111
    >>> class A2(A):
112
    ...    _priority = 2
113
    >>> class B(Processor):
114
    ...    _extensions = ['yaml', 'yml']
115
    >>> g = list_processors_by_ext([B, A2, A])
116
    >>> list(g)  # doctest: +NORMALIZE_WHITESPACE
117
    [('json', [<class 'anyconfig.processors.A'>,
118
              <class 'anyconfig.processors.A2'>]),
119
     ('yaml', [<class 'anyconfig.processors.B'>]),
120
     ('yml', [<class 'anyconfig.processors.B'>])]
121
    """
122
    ps_by_ext = anyconfig.utils.concat(([(x, p) for x in p.extensions()] for p
123
                                        in prs))  # [(ext, proc_cls)]
124
125
    return ((x, _ext_proc_tpls_to_procs(xps)) for x, xps
126
            in anyconfig.utils.groupby(ps_by_ext, operator.itemgetter(0)))
127
128
129
def list_types(tps):
130
    """List types that any processors can process them are available.
131
132
    :param tps: A list (generator) of (processor_type, [processor_cls])
133
    :return: [processor_type]
134
    """
135
    return sorted(set(next(anyconfig.compat.izip(*tps))))
136
137
# vim:sw=4:ts=4:et:
138