Hypergolix IPC: the HGXLink
¶
-
class
HGXLink
(ipc_port=7772, autostart=True, *args, threaded=True, **kwargs)¶ New in version 0.1.
The inter-process communications link to the Hypergolix service. Uses Websockets over localhost, by default on port 7772. Runs in a dedicated event loop, typically within a separate thread. Must be explicitly stopped during cleanup.
Parameters: - ipc_port (int) – The localhost port where the Hypergolix service is currently running.
- autostart (bool) – Automatically connect to Hypergolix and start the
HGXLink
during__init__
. IfFalse
, theHGXLink
must be explicitly started withstart()
. - *args – Passed to
loopa.TaskCommander
. - threaded (bool) – If
True
, run theHGXLink
in a separate thread; ifFalse
, run it in the current thread. In non-threaded mode, theHGXLink
will block all operations. - **kwargs – Passed to
loopa.TaskCommander
.
Returns: The
HGXLink
instance.>>> import hgx >>> hgxlink = hgx.HGXLink()
-
whoami
¶ The
Ghid
representing the public key fingerprint of the currently-logged-in Hypergolix user. This address may be used for sharing objects. This attribute is read-only.Return Ghid: if successful Raises: RuntimeError – if the Hypergolix service is unavailable. >>> hgxlink.whoami Ghid(algo=1, address=b'\xf8A\xd6`\x11\xedN\x14\xab\xe5"\x16\x0fs\n\x02\x08\xa1\xca\xa6\xc6$\xa7D\xf7\xb9\xa2\xbc\xc0\x8c\xf3\xe1\xefP\xa1]dE\x87\tw\xb1\xc8\x003\xac>\x89U\xdd\xcc\xb5X\x1d\xcf\x8c\x0e\x0e\x03\x7f\x1e]IQ')
-
token
¶ The token for the current application (Python session). Only available after registering the application with the Hypergolix service through one of the
register_token()
methods. This attribute is read-only.Return bytes: if the current application has a token. Raises: RuntimeError – if the current application has no token. >>> hgxlink.token AppToken(b'(\x19i\x07&\xff$!h\xa6\x84\xbcr\xd0\xba\xd1')
-
wrap_threadsafe
(callback)¶ Wraps a blocking/synchronous function for use as a callback. The wrapped function will be called from within a single-use, dedicated thread from the
HGXLink
‘s internalThreadPoolExecutor
, so as not to block theHGXLink
event loop.This may also be used as a decorator.
>>> def threadsafe_callback(obj): ... print(obj.state) ... >>> threadsafe_callback <function threadsafe_callback at 0x00000000051CD620> >>> # Note that the memory address changes due to wrapping >>> hgxlink.wrap_threadsafe(threadsafe_callback) <function threadsafe_callback at 0x00000000051CD6A8> >>> @hgxlink.wrap_threadsafe >>> def threadsafe_callback(obj): ... print(obj.state) ... >>> threadsafe_callback <function threadsafe_callback at 0x000000000520B488>
-
wrap_loopsafe
(callback, *, target_loop)¶ Wraps an asynchronous coroutine for use as a callback. The callback will be run in
target_loop
, which must be different from theHGXLink
event loop (there is no need to wrap callbacks running natively from within theHGXLink
loop). Use this to have theHGXLink
run callbacks from within a different event loop (if your application is also usingasyncio
and providing its own event loop).This may also be used as a decorator.
>>> async def loopsafe_callback(obj): ... print(obj.state) ... >>> loopsafe_callback <function loopsafe_callback at 0x0000000005222488> >>> # Note that the memory address changes due to wrapping >>> hgxlink.wrap_loopsafe(loopsafe_callback, target_loop=byo_loop) <function loopsafe_callback at 0x00000000051CD6A8> >>> @hgxlink.wrap_loopsafe(target_loop=byo_loop) >>> async def loopsafe_callback(obj): ... print(obj.state) ... >>> loopsafe_callback <function loopsafe_callback at 0x000000000521A228>
-
start
()¶ Starts the HGXLink, connecting to Hypergolix and obtaining the current
whoami
. Must be called explicitly ifautostart
wasFalse
; otherwise, is called duringHGXLink.__init__
.>>> hgxlink.start() >>>
Note
The following methods each expose three equivalent APIs:
an API for the HGXLink event loop, written plainly (ex:
register_token()
).Warning
This method must only be awaited from within the internal
HGXLink
event loop, or it may break theHGXLink
, and will likely fail to work.This method is a coroutine. Example usage:
token = await register_token()
a threadsafe external API, denoted by the _threadsafe suffix (ex:
register_token_threadsafe()
).Warning
This method must not be called from within the internal
HGXLink
event loop, or it will deadlock.This method is a standard, blocking, synchronous method. Example usage:
token = register_token_threadsafe()
a loopsafe external API, denoted by the _loopsafe suffix (ex:
register_token_loopsafe()
).Warning
This method must not be awaited from within the internal
HGXLink
event loop, or it will deadlock.This method is a coroutine that may be awaited from your own external event loop. Example usage:
token = await register_token_loopsafe()
-
stop
()¶ -
stop_threadsafe
()¶ -
stop_loopsafe
()¶ Called to stop the
HGXLink
and disconnect from Hypergolix. Must be called before exiting the main thread, or the Python process will not exit, and must be manually halted from an operating system process manager.
-
new
(cls, state, api_id=None, dynamic=True, private=False)¶ -
new_threadsafe
(cls, state, api_id=None, dynamic=True, private=False)¶ -
new_loopsafe
(cls, state, api_id=None, dynamic=True, private=False)¶ Makes a new Hypergolix object.
Parameters: - cls (type) – the Hypergolix object class to use for this object. See Basic bytes interface.
- state – the state to initialize the object with. It will be immediately pushed upstream to Hypergolix during creation of the object.
- api_id (bytes) – the API id to use for this object. If
None
, defaults to thecls._hgx_DEFAULT_API
. - dynamic (bool) – determines whether the created object will be dynamic (and therefore mutable), or static (and wholly immutable).
- private (bool) – determines whether the created object will be restricted to this specific application, for this specific Hypergolix user. By default, objects created by any Hypergolix application are available to all other Hypergolix apps for the current Hypergolix user.
Returns: the created object.
Raises: - hypergolix.exceptions.IPCError – upon IPC failure, or improper object declaration.
- Exception – for serialization failures. The specific exception type is determined by the serialization process itself.
>>> obj = hgxlink.new_threadsafe( ... cls = hgx.Obj, ... state = b'Hello world!' ... ) >>> obj <Obj with state b'Hello world!' at Ghid('Abf3d...')> >>> # Get the full address to retrieve the object later >>> obj.ghid.as_str() 'Abf3dRNZAPhrqY93q4Q-wG0QvPnP_anV8XfauVMlFOvAgeC5JVWeXTUftJ6tmYveH0stGaAJ0jN9xKriTT1F6Mk='
-
get
(cls, ghid)¶ -
get_threadsafe
(cls, ghid)¶ -
get_loopsafe
(cls, ghid)¶ Retrieves an existing Hypergolix object.
Parameters: - cls (type) – the Hypergolix object class to use for this object. See Basic bytes interface.
- ghid (Ghid) – the
Ghid
address of the object to retrieve.
Returns: the retrieved object.
Raises: - hypergolix.exceptions.IPCError – upon IPC failure, or improper object declaration.
- Exception – for serialization failures. The specific exception type is determined by the serialization process itself.
>>> address = hgx.Ghid.from_str('Abf3dRNZAPhrqY93q4Q-wG0QvPnP_anV8XfauVMlFOvAgeC5JVWeXTUftJ6tmYveH0stGaAJ0jN9xKriTT1F6Mk=') >>> obj = hgxlink.get_threadsafe( ... cls = hgx.ObjBase, ... ghid = address ... ) >>> obj <Obj with state b'Hello world!' at Ghid('Abf3d...')>
-
register_token
(token=None)¶ -
register_token_threadsafe
(token=None)¶ -
register_token_loopsafe
(token=None)¶ Requests a new application token from the Hypergolix service or re-registers an existing application with the Hypergolix service. If previous instances of the app token have declared a startup object with the Hypergolix service, returns its address.
Tokens can only be registered once per application. Subsequent attempts to register a token will raise
IPCError
. Newly-registered tokens will be available attoken
.App tokens are required for some advanced features of Hypergolix. This token should be reused whenever (and wherever) that exact application is restarted. It is unique for every application, and every Hypergolix user.
Parameters: token (hypergolix.utils.AppToken) – the application’s pre-registered Hypergolix token, or None
to create one.Raises: hypergolix.exceptions.IPCError – if unsuccessful. Return None: if no startup object has been declared. Return hypergolix.Ghid: if a startup object has been declared. This is the address of the object, and can be used in a subsequent get()
call to retrieve it.>>> hgxlink.register_token_threadsafe() >>> hgxlink.token AppToken(b'(\x19i\x07&\xff$!h\xa6\x84\xbcr\xd0\xba\xd1') >>> # Some other time, in some other session >>> app_token = AppToken(b'(\x19i\x07&\xff$!h\xa6\x84\xbcr\xd0\xba\xd1') >>> hgxlink.register_token_threadsafe(app_token)
-
register_startup
(obj)¶ -
register_startup_threadsafe
(obj)¶ -
register_startup_loopsafe
(obj)¶ Registers an object as the startup object. Startup objects are useful to bootstrap configuration, settings, etc. They can be any Hypergolix object, and will be returned to the application at every subsequent call to
register_token()
. Startup objects may only be declared after registering an app token.Parameters: obj – The object to register. May be any Hypergolix object. Raises: hypergolix.exceptions.UnknownToken – if no token has been registered for the application. >>> obj = hgxlink.new_threadsafe(Obj, state=b'hello world') >>> hgxlink.register_startup_threadsafe(obj)
-
deregister_startup
()¶ -
deregister_startup_threadsafe
()¶ -
deregister_startup_loopsafe
()¶ Registers an object as the startup object. Startup objects are useful to bootstrap configuration, settings, etc. They can be any Hypergolix object, and will be returned to the application at every subsequent call to
register_token()
. Startup objects may only be declared after registering an app token.Raises: - hypergolix.exceptions.UnknownToken – if no token has been registered for the application.
- Exception – if no object has be registered for startup.
>>> hgxlink.deregister_startup_threadsafe()
Registers a handler for incoming, unsolicited object shares from other Hypergolix users. Without registering a share handler, Hypergolix applications cannot receive shared objects from other users.
The share handler will also be called when other applications from the same Hypergolix user create an object with the appropriate
api_id
.The share handler callback will be invoked with three arguments: the
Ghid
of the incoming object, the fingerprintGhid
of the share origin, and thehypergolix.utils.ApiID
of the incoming object.Parameters: - api_id (hypergolix.utils.ApiID) – determines what objects will be sent to the application. Any objects shared with the current Hypergolix user with a matching api_id will be sent to the application.
- handler – the share handler. Unless the
handler
can be used safely from within theHGXLink
internal event loop, it must be wrapped throughwrap_threadsafe()
orwrap_loopsafe()
prior to registering it as a share handler.
Raises: TypeError – If the api_id is not
hypergolix.utils.ApiID
or the handler is not a coroutine (wrap it usingwrap_threadsafe()
orwrap_loopsafe()
prior to registering it as a share handler).Warning
Any given API ID can have at most a single share handler per app. Subsequent calls to any of the
register_share_handler()
methods will overwrite the existing share handler without warning.>>> @hgxlink.wrap_threadsafe ... def handler(ghid, origin, api_id): ... print('Incoming object: ' + str(ghid)) ... print('Sent by: ' + str(origin)) ... print('With API ID: ' + str(api_id)) ... >>> hgxlink.register_share_handler_threadsafe( ... api_id = hypergolix.utils.ApiID.pseudorandom(), ... handler = handler ... )