aboutsummaryrefslogtreecommitdiff
blob: 60dfe38e1612649c54224ccea44a986d43583723 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
"""
Package source manager is responsible for selecting the correct source
for a requested package, by a provided string.

Package sources can provide several handlers with different priorities.
A handler is a String -> Result function, which tries to parse the passed
value and return Ok(value).

The package would be handled by the handler with the lowest priority, which
was added the first.

Example:
    @dispatcher.source
    class BgoSource():
        @dispatcher.handler(priority=5)
        def parse_int(uri):
            if uri.is_decimal():
                return Result.Ok(int(uri))
            elif uri[0] == '#' and uri[1:].is_decimal():
                return Result.Ok(int(uri[1:]))
            return Result.Err('NaN')

        @dispatcher.handler(priority=1)
        def parse_url(uri):
            if uri.startswith('http://bugs.gentoo.org/'):
            ...
"""
#TODO: efficient sorted insertion
#import bisect
import inspect

from pomu.util.result import Result

class PackageDispatcher():
    def __init__(self):
        self.handlers = []
        self.backends = {}

    def source(self, cls):
        """
        A decorator to mark package source modules
        It would register all the methods of the class marked by @handler
        with the dispatcher.
        """
        try:
            from pomu.source.base import BaseSource
        except ImportError: #circular import
            return cls
        if cls == BaseSource:
            return cls
        self.backends[cls.__cname__] = cls
        for m, obj in inspect.getmembers(cls):
            if isinstance(obj, self.handler._handler):
                self.register_package_handler(cls, obj.handler, obj.priority)
        return cls

    class handler():
        """
        A decorator to denote package source module handler, which
        should attempt to parse a package descriptor. If it succeeds,
        the result would be passed to the module for further processing.
        """
        class _handler():
            def __init__(self, handler):
                self.handler = handler
            def __call__(self, *args, **kwargs):
                return self.handler(*args, **kwargs)

        def __init__(self, priority=1000, *args, **kwargs):
            self.priority = priority

        def __call__(self, func, *args, **kwargs):
            x = self._handler(func)
            x.priority = self.priority
            return staticmethod(x)

    def register_package_handler(self, source, handler, priority):
        """
        Register a package handler for a specified source.
        Handlers with lower priority get called first.
        """
        i = 0
        for i in range(len(self.handlers)):
            if self.handlers[i][0] > priority:
                break
        self.handlers.insert(i, (priority, source, handler))

    def get_package_source(self, uri):
        """Get a source which accepts the package"""
        for priority, source, handler in self.handlers:
            if handler(uri).is_ok():
                return Result.Ok(source)
        return Result.Err('No handler found for package ' + uri)

    def get_package(self, uri):
        """Fetch a package specified by the descriptor"""
        for priority, source, handler in self.handlers:
            res = handler(uri)
            if res.is_ok():
                return Result.Ok(source.fetch_package(res.ok()))
        return Result.Err('No handler found for package ' + uri)

    def install_package(self, repo, uri):
        """Install a package specified by the descriptor into the repository"""
        pkg = self.get_package(uri).unwrap()
        return repo.merge(pkg)

    def uninstall_package(self, repo, uri):
        """Uninstall a package specified by the descriptor"""
        pkg = self.get_package(uri).unwrap()
        return repo.unmerge(pkg)