File: Request.py

package info (click to toggle)
libcharon 5.0.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 576 kB
  • sloc: python: 1,575; sh: 388; makefile: 3
file content (184 lines) | stat: -rw-r--r-- 6,680 bytes parent folder | download | duplicates (3)
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import enum
import threading
import uuid
from typing import List, Dict, Any, Optional, Callable

from .DBusInterface import DBusInterface


##  Wrapper around the Charon DBus service that hides the DBus details.
#
#   This class encapsulates all the data and information needed for
#   retrieving some data from a file supported by the Charon file service.
#
#   It can be used to simplify dealing with the DBus service.
class Request:
    # The request state.
    class State(enum.IntEnum):
        Initial = 0  # Request was created, but not started yet.
        Running = 1  # Request was started.
        Completed = 2  # Request completed successfully.
        Error = 3  # Request encountered an error.

    ##  Constructor.
    #
    #   \param file_path The path to a file to get data from.
    #   \param virtual_paths A list of virtual paths with the data to retrieve.
    def __init__(self, file_path: str, virtual_paths: List[str]) -> None:
        self.__file_path = file_path
        self.__virtual_paths = virtual_paths

        self.__state = self.State.Initial
        self.__request_id = 0
        self.__data = {} # type: Dict[str, Any]
        self.__error_string = ""

        self.__event = threading.Event()

        self.__request_data_callback = None # type: Optional[Callable[["Request", Dict[str, Any]], None]]
        self.__request_completed_callback = None # type: Optional[Callable[["Request"], None]]
        self.__request_error_callback = None # type: Optional[Callable[["Request", str], None]]

    ##  Cleanup function.
    def __del__(self):
        if self.__state != self.State.Initial:
            self.stop()

            DBusInterface.disconnectSignal("requestData", self.__onRequestData)
            DBusInterface.disconnectSignal("requestCompleted", self.__onRequestCompleted)
            DBusInterface.disconnectSignal("requestError", self.__onRequestError)

    ##  The file path for this request.
    @property
    def filePath(self) -> str:
        return self.__file_path

    ##  The virtual paths for this request.
    @property
    def virtualPaths(self) -> List[str]:
        return self.__virtual_paths

    ##  The state of this request.
    @property
    def state(self) -> State:
        return self.__state

    ##  The data associated with this request.
    #
    #   Note that this will be an empty dictionary until the request
    #   completed.
    @property
    def data(self) -> Dict[str, Any]:
        return self.__data

    ##  A description of the error that was encountered during the request.
    #
    #   Note that this will be an empty string if there was no error.
    @property
    def errorString(self) -> str:
        return self.__error_string

    ##  Set the callbacks that should be called while the request is running.
    #
    #   Note: These parameters can only be passed as keyword arguments.
    #   \param data The callback to call when data is received. Will be passed the request object and a dict with data.
    #   \param completed The callback to call when the request has completed. Will be passed the request object.
    #   \param error The callback to call when the request encountered an error. Will be passed the request object and a string describing the error.
    #
    def setCallbacks(self, *,
            data: Callable[["Request", Dict[str, Any]], None] = None,
            completed: Callable[["Request"], None] = None,
            error: Callable[["Request", str], None] = None) -> None:
        self.__request_data_callback = data
        self.__request_completed_callback = completed
        self.__request_error_callback = error

    ##  Start the request.
    def start(self):
        if self.__state != self.State.Initial:
            return

        self.__request_id = str(uuid.uuid4())

        DBusInterface.connectSignal("requestData", self.__onRequestData)
        DBusInterface.connectSignal("requestCompleted", self.__onRequestCompleted)
        DBusInterface.connectSignal("requestError", self.__onRequestError)

        self.__state = self.State.Running

        DBusInterface.callAsync("startRequest", self.__startSuccess, self.__startError, "ssas", self.__request_id, self.__file_path, self.__virtual_paths)

    ##  Stop the request.
    #
    #   Note that this may fail if the file service was already processing the request.
    def stop(self):
        if self.__state != self.State.Running:
            return

        DBusInterface.callAsync("cancelRequest", None, None, "s", self.__request_id)

    ##  Wait until the request is finished.
    #
    #   Warning! This method will block the calling thread until it is finished. The DBus implementations
    #   require a running event loop for signal delivery to work. This means that if you block the main
    #   loop with this method, you will deadlock since the completed signal is never received.
    def waitForFinished(self):
        if self.__state == self.State.Initial:
            self.start()

        self.__event.clear()
        self.__event.wait()

    def __startSuccess(self, start_success: bool):
        if not start_success:
            self.__startError("Could not start the request")
            return

    def __startError(self, error: str):
        self.__state = self.State.Error
        self.__error_string = error
        self.__event.set()

        if self.__request_error_callback:
            self.__request_error_callback(self, error)

    def __onRequestData(self, request_id: str, data: Dict[str, Any]):
        if self.__state != self.State.Running:
            return

        if self.__request_id != request_id:
            return

        self.__data.update(data)

        if self.__request_data_callback:
            self.__request_data_callback(self, data)

    def __onRequestCompleted(self, request_id: str):
        if self.__state != self.State.Running:
            return

        if self.__request_id != request_id:
            return

        self.__state = self.State.Completed

        if self.__request_completed_callback:
            self.__request_completed_callback(self)

        self.__event.set()

    def __onRequestError(self, request_id: str, error_string: str):
        if self.__request_id != request_id:
            return

        self.__state = self.State.Error
        self.__error_string = error_string

        if self.__request_error_callback:
            self.__request_error_callback(self, error_string)

        self.__event.set()

    def __repr__(self):
        return "<Charon.Client.Request ({id}) file_path = {path} virtual_paths = {virtual} >".format(id = id(self), path = self.__file_path, virtual = self.__virtual_paths)