ouisync.service

In-process embedding of the Ouisync service

 1"""In-process embedding of the Ouisync service"""
 2
 3import asyncio
 4import ctypes
 5
 6from ouisync.session import ErrorCode, dispatch_error
 7
 8from ._bindings import StatusCallback, bindings
 9
10__all__ = ["Service", "init_log"]
11
12
13class Service:
14    """Manages the repositories and runs the sync protocol. It can be
15    interacted with using a [Session][ouisync.session.Session] connected to
16    the same config directory.
17
18    Note: to create a service, use `Service.start`.
19    """
20
21    def __init__(self, handle: int):
22        self._handle: int | None = handle
23
24    @classmethod
25    async def start(cls, config_path, debug_label: str | None = None) -> "Service":
26        """Starts the service.
27
28        `config_path` is the path to the config directory of this service.
29        If it doesn't exist, it's created automatically. The service requires
30        both read and write access to it.
31
32        `debug_label` is an optional label used to distinguish multiple
33        services running in the same process. Used mainly for testing and
34        debugging the library itself.
35        """
36        error_code, handle = await _invoke(
37            bindings().start_service,
38            str(config_path).encode(),
39            debug_label.encode() if debug_label is not None else None,
40        )
41
42        if error_code != ErrorCode.OK:
43            raise dispatch_error(error_code)
44
45        return cls(handle)
46
47    async def stop(self):
48        """Stops this service. Has no effect if the service has already been
49        stopped."""
50        handle = self._handle
51        if handle is None:
52            return
53
54        self._handle = None
55
56        error_code, _ = await _invoke(bindings().stop_service, handle)
57
58        if error_code != ErrorCode.OK:
59            raise dispatch_error(error_code)
60
61
62async def _invoke(func, *args):
63    """Calls one of the `start_service`/`stop_service` native functions,
64    appending the status callback and its (unused) context, and awaits its
65    completion. Returns `(error_code, return_value)`."""
66    loop = asyncio.get_running_loop()
67    future: asyncio.Future = loop.create_future()
68
69    # The callback may be invoked from a thread other than this one, so it
70    # must hand the result back to the event loop thread-safely. Kept alive
71    # by this frame for as long as the native side may still call it.
72    @StatusCallback
73    def callback(_context: ctypes.c_void_p, error_code: int):
74        loop.call_soon_threadsafe(_resolve, future, error_code)
75
76    return_value = func(*args, callback, None)
77    error_code = ErrorCode(await future)
78
79    return error_code, return_value
80
81
82def _resolve(future: asyncio.Future, error_code: int):
83    if not future.done():
84        future.set_result(error_code)
85
86
87def init_log():
88    """Enables logging of Ouisync's internal messages.
89
90    Calling this function more than once has no effect. Currently there is
91    no way to disable the logging once it's been enabled.
92    """
93    bindings().init_log()
class Service:
14class Service:
15    """Manages the repositories and runs the sync protocol. It can be
16    interacted with using a [Session][ouisync.session.Session] connected to
17    the same config directory.
18
19    Note: to create a service, use `Service.start`.
20    """
21
22    def __init__(self, handle: int):
23        self._handle: int | None = handle
24
25    @classmethod
26    async def start(cls, config_path, debug_label: str | None = None) -> "Service":
27        """Starts the service.
28
29        `config_path` is the path to the config directory of this service.
30        If it doesn't exist, it's created automatically. The service requires
31        both read and write access to it.
32
33        `debug_label` is an optional label used to distinguish multiple
34        services running in the same process. Used mainly for testing and
35        debugging the library itself.
36        """
37        error_code, handle = await _invoke(
38            bindings().start_service,
39            str(config_path).encode(),
40            debug_label.encode() if debug_label is not None else None,
41        )
42
43        if error_code != ErrorCode.OK:
44            raise dispatch_error(error_code)
45
46        return cls(handle)
47
48    async def stop(self):
49        """Stops this service. Has no effect if the service has already been
50        stopped."""
51        handle = self._handle
52        if handle is None:
53            return
54
55        self._handle = None
56
57        error_code, _ = await _invoke(bindings().stop_service, handle)
58
59        if error_code != ErrorCode.OK:
60            raise dispatch_error(error_code)

Manages the repositories and runs the sync protocol. It can be interacted with using a [Session][ouisync.session.Session] connected to the same config directory.

Note: to create a service, use Service.start.

Service(handle: int)
22    def __init__(self, handle: int):
23        self._handle: int | None = handle
@classmethod
async def start( cls, config_path, debug_label: str | None = None) -> Service:
25    @classmethod
26    async def start(cls, config_path, debug_label: str | None = None) -> "Service":
27        """Starts the service.
28
29        `config_path` is the path to the config directory of this service.
30        If it doesn't exist, it's created automatically. The service requires
31        both read and write access to it.
32
33        `debug_label` is an optional label used to distinguish multiple
34        services running in the same process. Used mainly for testing and
35        debugging the library itself.
36        """
37        error_code, handle = await _invoke(
38            bindings().start_service,
39            str(config_path).encode(),
40            debug_label.encode() if debug_label is not None else None,
41        )
42
43        if error_code != ErrorCode.OK:
44            raise dispatch_error(error_code)
45
46        return cls(handle)

Starts the service.

config_path is the path to the config directory of this service. If it doesn't exist, it's created automatically. The service requires both read and write access to it.

debug_label is an optional label used to distinguish multiple services running in the same process. Used mainly for testing and debugging the library itself.

async def stop(self):
48    async def stop(self):
49        """Stops this service. Has no effect if the service has already been
50        stopped."""
51        handle = self._handle
52        if handle is None:
53            return
54
55        self._handle = None
56
57        error_code, _ = await _invoke(bindings().stop_service, handle)
58
59        if error_code != ErrorCode.OK:
60            raise dispatch_error(error_code)

Stops this service. Has no effect if the service has already been stopped.

def init_log():
88def init_log():
89    """Enables logging of Ouisync's internal messages.
90
91    Calling this function more than once has no effect. Currently there is
92    no way to disable the logging once it's been enabled.
93    """
94    bindings().init_log()

Enables logging of Ouisync's internal messages.

Calling this function more than once has no effect. Currently there is no way to disable the logging once it's been enabled.