Basic bytes interface¶
Hypergolix objects¶
-
class
Obj(state, api_id, dynamic, private, *, hgxlink, ipc_manager, _legroom, ghid=None, binder=None, callback=None)¶ New in version 0.1.
The basic Hypergolix object. Create it using
HGXLink.get()orHGXLink.new(); these objects are not intended to be created directly. If you create the object directly, it won’t receive state updates from upstream.All Hypergolix objects have a unique, cryptographic-hash-based address (the
Ghid) and a binder (roughly speaking, the public key fingerprint of the object’s creator). They may be dynamic or static.All Hypergolix objects have a so-called “API ID” – an arbitrary, unique, implicit identifier for the structure of the object. In traditional web service parlance, it’s somewhere between an endpoint and a schema, which (unfortunately) is a pretty terrible analogy.
Hypergolix objects persist nonlocally until explicitly deleted through one of the
delete()methods.Parameters: - hgxlink (HGXLink) – The currently-active
HGXLinkobject used to connect to the Hypergolix service. - state – The state of the object.
- api_id (hgx.utils.ApiID) – The API ID for the object (see above).
- dynamic (bool) – A value of
Truewill result in a dynamic object, whose state may be updated.Falsewill result in a static object with immutable state. - private (bool) – Declare the object as available to this application
only (as opposed to any application for the logged-in Hypergolix user).
Setting this to
Truerequires anHGXLink.app_token. - ghid (Ghid) – The
Ghidaddress of the object. - binder (Ghid) – The
Ghidof the object’s binder.
Returns: The
Objinstance, with state declared, but not initialized with Hypergolix.Warning
Hypergolix objects are not intended to be created directly. Instead, they should always be created through the
HGXLink, using one of itsHGXLink.new()orHGXLink.get()methods.Creating the objects directly will result in them being unavailable for automatic updates, and forced to poll through their
sync()methods. Furthermore, theirbinderandghidproperties will be unavailable until after the first call topush().>>> obj = hgxlink.new_threadsafe( ... cls = hgx.Obj, ... state = b'Hello world!' ... ) >>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')>
-
state¶ The read-write value of the object itself. This will be serialized and uploaded through Hypergolix upon any call to
push().Warning
Updating
statewill not update Hypergolix. To upload the change, you must explicitly callpush().Return type: bytes >>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.state b'Hello world!' >>> # This change won't yet exist anywhere else >>> obj.state = b'Hello Hypergolix!' >>> obj <Obj with state b'Hello Hypergolix!' at Ghid('bf3dR')>
-
ghid¶ The read-only address for the object.
Return Ghid: read-only address. >>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.ghid Ghid(algo=1, address=b'\xb7\xf7u\x13Y\x00\xf8k\xa9\x8fw\xab\x84>\xc0m\x10\xbc\xf9\xcf\xfd\xa9\xd5\xf1w\xda\xb9S%\x14\xeb\xc0\x81\xe0\xb9%U\x9e]5\x1f\xb4\x9e\xad\x99\x8b\xde\x1fK-\x19\xa0\t\xd23}\xc4\xaa\xe2M=E\xe8\xc9') >>> str(obj.ghid) Ghid('bf3dR')
-
api_id¶ The read-only API ID for the object.
Return bytes: read-only API ID. >>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.api_id ApiID(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01')
-
private¶ Whether or not the object is restricted to this application only (see above). Read-only.
Return bool: read-only privacy setting. >>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.private False
-
dynamic¶ Is the object dynamic (
True) or static (False)? Read-only.Return bool: read-only dynamic/static status. >>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.dynamic True
-
binder¶ The read-only binder of the object. Roughly speaking, the public key fingerprint of its creator (see above).
Return Ghid: read-only binder. >>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.binder 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') >>> str(obj.binder) Ghid('fhB1m')
-
callback¶ Gets, sets, or deletes an update callback. This will be awaited every time the object receives an upstream update, but it will not be called when the application itself calls
push(). The callback will be passed a single argument: the object itself. The object’sstatewill already have been updated to the new upstream state before the callback is invoked.Because they are running independently of your actual application, and are called by the
HGXLinkitself, any exceptions raised by the callback will be swallowed and logged.Parameters: callback – An awaitable callback. Warning
For threadsafe or loopsafe usage, this callback must be appropriately wrapped using
HGXLink.wrap_threadsafe()orHGXLink.wrap_loopsafe()before setting it as a callback.Setting the callback:
>>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> async def handler(obj): ... print('Updated! ' + repr(obj)) ... >>> obj.callback = handler
The resulting call:
>>> Updated! <Obj with state b'Hello Hypergolix!' at Ghid('bf3dR')>
-
__eq__(other)¶ Compares two Hypergolix objects. The result will be
Trueif (and only if) all of the following conditions are satisfied:- They both have a
ghid(else,raise TypeError) - The
ghidcompares equally - They both have a
state(else,raise TypeError) - The
statecompares equally - They both have a
binder(else,raise TypeError) - The
bindercompares equally
Parameters: other – The Hypergolix object to compare with. Return bool: The comparison result. Raises: TypeError – when attempting to compare with a non-Hypergolix object. >>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj2 <Obj with state b'Hello world!' at Ghid('WFUmW')> >>> obj == obj2 False
- They both have a
Note
The following methods each expose three equivalent APIs:
an internal API (ex:
push()).Warning
This method must only be awaited from within the internal
HGXLinkevent loop, or it may break theHGXLink, and will likely fail to work.This method is a coroutine. Example usage:
await obj.push()
a threadsafe API, denoted by the _threadsafe suffix (ex:
push_threadsafe()).Warning
This method must not be called from within the internal
HGXLinkevent loop, or it will deadlock.This method is a standard, blocking, synchronous method. Example usage:
obj.push_threadsafe()
a loopsafe API, denoted by the _loopsafe suffix (ex:
push_loopsafe()).Warning
This method must not be awaited from within the internal
HGXLinkevent loop, or it will deadlock.This method is a coroutine that may be awaited from your own external event loop. Example usage:
await obj.push_loopsafe()
-
recast(cls)¶ -
recast_threadsafe(cls)¶ -
recast_loopsafe(cls)¶ Converts between Hypergolix object types. Returns a new copy of the current Hypergolix object, converted to type
cls.Parameters: cls – the typeof object to recast into.Returns: a new version of obj, in the current class.Warning
Recasting an object renders the previous Python object inoperable and dead. It will cease to receive updates from the
HGXLink, and subsequent manipulation of the old object is likely to cause bugs with the new object as well.>>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.recast_threadsafe(hgx.JsonObj) <JsonObj with state b'Hello world!' at Ghid('bf3dR')>
-
push()¶ -
push_threadsafe()¶ -
push_loopsafe()¶ Notifies the Hypergolix service (through the
HGXLink) of updates to the object. Must be called explicitly for any changes to be available outside of the current Python session.Raises: - hypergolix.exceptions.IPCError – if unsuccessful.
- hypergolix.exceptions.LocallyImmutable – if the object is static, or if the current Hypergolix user did not create it.
- hypergolix.exceptions.DeadObject – if the object is unavailable,
for example, as a result of a
discard()call.
>>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> # This state is unknown everywhere except in current memory >>> obj.state = b'Foo' >>> obj.state = b'Bar' >>> # Hypergolix now has no record of b'Foo' ever existing. >>> obj.push_threadsafe() >>> # The new state b'Bar' is now known to Hypergolix.
-
sync()¶ -
sync_threadsafe()¶ -
sync_loopsafe()¶ Manually initiates an update through Hypergolix. So long as you create and retrieve objects through the
HGXLink, you will not need these methods.Raises: - hypergolix.exceptions.IPCError – if unsuccessful.
- hypergolix.exceptions.DeadObject – if the object is unavailable,
for example, as a result of a
discard()call.
>>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.sync_threadsafe()
Shares the
Objinstance withrecipient. The recipient will receive a read-only copy of the object, which will automatically update upon any local changes that arepush()ed upstream.Parameters: recipient (Ghid) – The public key fingerprint “identity” of the entity to share with.
Raises: Note
Successful sharing does not imply successful receipt. The recipient could ignore the share, be temporarily unavailable, etc.
Note
In order to actually receive the object, the recipient must have a share handler defined for the
api_idof the object.>>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> bob = hgx.Ghid.from_str('AfhB1mAR7U4Uq-UiFg9zCgIIocqmxiSnRPe5orzAjPPh71ChXWRFhwl3scgAM6w-iVXdzLVYHc-MDg4Dfx5dSVE=') >>> obj.share_threadsafe(bob)
-
freeze()¶ -
freeze_threadsafe()¶ -
freeze_loopsafe()¶ Creates a static “snapshot” of a dynamic object. This new static object will be available at its own dedicated address.
Returns: a frozen copy of the
Obj(or subclass) instance. The class of the new instance will match the class of the original.Raises: - hypergolix.exceptions.IPCError – if unsuccessful.
- hypergolix.exceptions.LocallyImmutable – if the object is static.
- hypergolix.exceptions.DeadObject – if the object is unavailable,
for example, as a result of a
discard()call.
>>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.dynamic True >>> frozen = obj.freeze_threadsafe() >>> frozen <Obj with state b'hello world' at Ghid('RS48N')> >>> frozen.dynamic False
-
hold()¶ -
hold_threadsafe()¶ -
hold_loopsafe()¶ Creates a separate binding to the object, preventing its deletion. This does not necessarily prevent other applications at the currently-logged-in Hypergolix user session from removing the object.
Raises: - hypergolix.exceptions.IPCError – if unsuccessful.
- hypergolix.exceptions.DeadObject – if the object is unavailable,
for example, as a result of a
discard()call.
>>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.hold_threadsafe()
-
discard()¶ -
discard_threadsafe()¶ -
discard_loopsafe()¶ Notifies the Hypergolix service that the application is no longer interested in the object, but does not delete it. This renders the object inoperable and dead, preventing most future operations. However, a new copy of the object can still be retrieved through any of the
HGXLink.get()methods.Raises: - hypergolix.exceptions.IPCError – if unsuccessful.
- hypergolix.exceptions.DeadObject – if the object is unavailable,
for example, as a result of a
discard()call.
>>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.discard_threadsafe()
-
delete()¶ -
delete_threadsafe()¶ -
delete_loopsafe()¶ Attempts to permanently delete the object. If successful, it will be inoperable and dead. It will also be removed from Hypergolix and made unavailable to other applications, as well as unavailable to any recipients of an
share()call, unless they have calledhold().Raises: - hypergolix.exceptions.IPCError – if unsuccessful.
- hypergolix.exceptions.DeadObject – if the object is unavailable,
for example, as a result of a
discard()call.
>>> obj <Obj with state b'Hello world!' at Ghid('bf3dR')> >>> obj.delete_threadsafe()
- hgxlink (HGXLink) – The currently-active
Hypergolix proxies¶
-
class
Proxy(state, api_id, dynamic, private, *, hgxlink, ipc_manager, _legroom, ghid=None, binder=None, callback=None)¶ New in version 0.1.
The Hypergolix proxy, partly inspired by
weakref.proxy, is a mechanism by which almost any existing Python object can be encapsulated within a Hypergolix-aware wrapper. In every other way, the proxy behaves exactly like the original object. This is accomplished by overloading theProxy.__getattr__(),Proxy.__setattr__(), andProxy.__delattr__()methods.Except where otherwise noted, a Hypergolix
Proxyexposes the same API as anObj, except that the Hypergolix methods are given anhgx_prefix to avoid namespace collisions. For example,Obj.push()becomesProxy.hgx_push(), and so on.A proxy is hashable if its
hgx_ghidis defined, but unhashable otherwise. Note, however, that this hash has nothing to do with the proxied object. Also note thatisinstance(proxy_obj, collections.Hashable)will always identify anProxyas hashable, regardless of its actual runtime behavior.Parameters: - hgxlink (HGXLink) – The currently-active
HGXLinkobject used to connect to the Hypergolix service. - state – The state of the object. For objects using the default (ie
noop) serialization, this must be
bytes-like. For subclasses ofObj, this can be anything supported by the subclass’ serialization strategy. - api_id (bytes) – The API ID for the object (see above). Should be a
bytes-like object of length 64. - dynamic (bool) – A value of
Truewill result in a dynamic object, whose state may be update.Falsewill result in a static object with immutable state. - private (bool) – Declare the object as available to this application
only (as opposed to any application for the logged-in Hypergolix user).
Setting this to
Truerequires anHGXLink.app_token. - ghid (Ghid) – The
Ghidof the object. Used to instantiate a preexisting object. - binder (Ghid) – The
Ghidof the object’s binder. Used to instantiate a preexisting object.
Returns: The
Objinstance, with state declared, but not initialized with Hypergolix.Note
Support for Python special methods (aka “magic methods”, “dunder methods”, etc) is provided. However, due to implementation details in Python itself, this is accomplished by explicitly passing all possible
__dunder__methods used by Python to the proxied object.This has the result that IDEs will present a very long list of available methods for
Proxyobjects, even if these methods are not, in fact, available. However, the built-indir()command should still return a list limited to the methods actually supported by the proxied:proxy combination.Note
Proxy objects will detect other
Proxyinstances and subclasses, but they will not detectObjinstances or subclasses unless they also subclassProxy. This is intentional behavior.Warning
Because of how Python works, explicitly reassigning
hgx_stateis the only way to reassign the value of the proxied object directly. For example, this will fail, overwriting the name of the object, and leaving the original unchanged:>>> obj <Proxy to b'Hello world!' at Ghid('bf3dR')> >>> obj = b'Hello Hypergolix!' >>> obj b'Hello Hypergolix!'
whereas this will succeed in updating the object state:
>>> obj <Proxy to b'Hello world!' at Ghid('bf3dR')> >>> obj.hgx_state = b'Hello Hypergolix!' >>> obj <Proxy to b'Hello Hypergolix!' at Ghid('bf3dR')>
>>> obj = hgxlink.new_threadsafe( ... cls = hgx.Proxy, ... state = b'Hello world!' ... ) >>> obj <Proxy to b'hello world' at Ghid('bJQMj')> >>> obj += b' foo' >>> obj <Proxy to b'hello world foo' at Ghid('bJQMj')> >>> obj.state = b'bar' >>> obj <Proxy to b'bar' at Ghid('bJQMj')>
-
__eq__(other)¶ Compares the
Proxywith another object. The comparison recognizes other Hypergolix objects, comparing them more thoroughly than other objects.If
otheris a Hypergolix object, the comparison will returnTrueif and only if:- The
Obj.ghidattribute compares equally - The
Obj.stateattribute compares equally - The
Obj.binderattribute compares equally
If, on the other hand, the
otherobject is not a Hypergolix object or proxy, it will directly compareotherwithhgx_state.Parameters: other – The object to compare with Return type: bool >>> obj <Proxy to b'Hello world!' at Ghid('bf3dR')> >>> obj2 <Proxy to b'Hello world!' at Ghid('WFUmW')> >>> obj == obj2 False >>> not_hgx_obj = b'Hello world!' >>> not_hgx_obj == obj True >>> obj2 == not_hgx_obj True
- The
- hgxlink (HGXLink) – The currently-active