Source code for tableauhyperapi.hyperprocess

import enum
import warnings

from pathlib import PurePath
from typing import Mapping, Union

from .endpoint import Endpoint
from .warning import UnclosedObjectWarning
from .impl import hapi
from .impl.dll import ffi, lib
from .impl.dllutil import Error, Parameters, InteropUtil


[docs] class Telemetry(enum.Enum): """ Constants which define whether usage data is sent to Tableau to improve the Hyper API. """ DO_NOT_SEND_USAGE_DATA_TO_TABLEAU = hapi.HYPER_DISABLE_TELEMETRY """ Do not share usage data with Tableau. """ SEND_USAGE_DATA_TO_TABLEAU = hapi.HYPER_ENABLE_TELEMETRY """ Help us improve the Hyper API by sharing usage data with Tableau. """
[docs] class HyperProcess: """ Starts a local Hyper server instance managed by the library. :param telemetry: :any:`Telemetry` constant which defines whether telemetry should be enabled. :param user_agent: arbitrary string which identifies the application. May be left unspecified. :param hyper_path: path to the directory which contains hyperd executable. If not specified, the library will try to locate it in pre-determined places. :param parameters: Optional dictionary of parameters for starting the Hyper process. The available parameters are documented `in the Tableau Hyper documentation, chapter "Process Settings" <https://tableau.github.io/hyper-db/docs/hyper-api/hyper_process#process-settings>`__. Example usage: .. testsetup:: HyperProcess.__init__ import os if os.path.exists('mydb.hyper'): os.remove('mydb.hyper') from tableauhyperapi import * .. testcode:: HyperProcess.__init__ with HyperProcess(Telemetry.SEND_USAGE_DATA_TO_TABLEAU, 'myapp') as hyper: with Connection(hyper.endpoint, 'mydb.hyper', CreateMode.CREATE) as connection: # ... pass .. testcleanup:: HyperProcess.__init__ os.remove('mydb.hyper') The server must be stopped either implicitly by a ``with`` statement, or explicitly by the :any:`close()` or :any:`shutdown()` method. Even if it's not stopped, the server will be terminated when the process exits. You cannot use tableauhyperapi to start the server and let it stay running after the script exits. """ def __init__(self, telemetry: Telemetry, user_agent: str = "", hyper_path: Union[str, PurePath] = None, parameters: Mapping[str, str] = None): self.__cdata = None # Reference lib for correct gc order self.__lib_ref = lib native_params = Parameters.create_instance_parameters() if parameters: for key, value in parameters.items(): native_params.set_value(key, value) self.__user_agent = user_agent if isinstance(hyper_path, PurePath): hyper_path = str(hyper_path) pp = ffi.new('hyper_instance_t**') Error.check(hapi.hyper_instance_create(InteropUtil.string_to_char_p(hyper_path), telemetry == Telemetry.SEND_USAGE_DATA_TO_TABLEAU, native_params.cdata, pp)) self.__cdata = pp[0] @property def _cdata(self): return self.__cdata @property def is_open(self) -> bool: """ Returns true if the server has not been shut down yet. """ return self.__cdata is not None
[docs] def close(self): """ Stops the server, ignoring possible errors. This is called automatically when ``with`` statement is used. Use :any:`shutdown()` instead of :any:`close()` to get the shutdown errors as a :any:`HyperException`. """ if self.__cdata is not None: try: hapi.hyper_instance_close(self.__cdata) finally: self.__cdata = None
[docs] def shutdown(self, timeout_ms: int = -1): """ Shuts down the Hyper server. If `timeout_ms` > 0ms, wait for Hyper to shut down gracefully. If the process is still running after a timeout of `timeout_ms` milliseconds, forcefully terminate the process and raise an exception. If `timeout_ms` < 0ms, wait indefinitely for Hyper to shut down. If `timeout_ms` == 0ms, immediately terminate Hyper forcefully. Does not throw if the process already exited with a non-zero exit code. A :any:`HyperException` is raised if there was an error stopping the process, if the process was forcefully killed after the timeout, or if the process already exited with a non-zero exit code. :param timeout_ms: timeout in milliseconds. """ if self.__cdata is not None: try: Error.check(hapi.hyper_instance_shutdown(self.__cdata, timeout_ms)) finally: self.__cdata = None
@property def endpoint(self) -> Endpoint: """ Endpoint of this process to connect to. """ if self.__cdata is None: raise RuntimeError('Server is closed') descriptor = InteropUtil.char_p_to_string(hapi.hyper_instance_get_endpoint_descriptor(self.__cdata)) endpoint = Endpoint(descriptor, self.__user_agent) return endpoint def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.close() def __del__(self): if self.__cdata is not None: warnings.warn('Server has not been stopped. Use Server object in a with statement or call its close() or ' 'shutdown() method when done.', UnclosedObjectWarning) self.close() def __repr__(self): if self.is_open: pid = hapi.hyper_instance_get_pid(self.__cdata) details = f'state: open; pid: {pid}; endpoint: {self.endpoint!r}' else: details = 'state: closed' return f'<HyperProcess object at {id(self):#x}; {details}>'