Skip to content

python usability #3087

@rid-dim

Description

@rid-dim

python usability of the libs is a bit of a pain tbh ... in particular when it comes to GraphEntries

to explain my critique about the ux:
... content needs to be precisely 32byte no less no more ...
... parents is a list of publickeys (passing a GraphEntryAddress - which was my intuitive idea - or a GraphEntry object) does result in an error) ...
... descendants is a list of tuple consisting of 1. again PublicKeys not Addresses 2. exactly 32byte - no more no less ...

none of this is obvious and I don't know where I could get the info except for putting in stuff and dealing with the error messages

Image

... but if we're honest it all already starts with all the functions being asynchronous already ... to use asynchronous stuff in Jupyter notebooks is not an issue - in a ipython/python terminal it is ...

then the whole differentiation Scratchpadaddress, PointerAddress, GraphEntryAddress, Chunkaddress is super cumbersome anyway ...

to get a Pointer I need to call client.pointer_get(addr) .... ofc the passed address will be a PointerAddress ?! what else should it be ...? why can't I just pass the hex string, the bytes or a generic "address" object?

to create a pointer I need to 1. create a public key from the hex string, then 2. create a pointer address from the public key object, 3. finally can create the pointer ...
oh wait - no - I need to pass a PointerTarget - 32bytes (like with the GraphEntry) isn't sufficient
(but here then suddenly a max of 48bytes to allow for pointer and scratchpad addresses is accepted) ... ah right

Image

again I need to pass a suitable Address and not simply the bytes and creating the ChunkAddress from_hex is not possible yet, because it requires to pass an XorName and that has neither a constructor nor any methods

Image

(I know this is being worked on an I highly appreciate it :-) ... still it again shows the hoops you need to go through just to recognize that something is missing here... )


but then again it's inconsistent too btw ...
... to create a Pointer I do:

pt = Pointer(
    SecretKey.from_hex(priv.hex()),
    0,
    PointerTarget.new_pointer(PointerAddress.from_hex("<somevalidpublickeyinhex>"))
)

just using the constructor ...

(and invalid pointer targets throw errors btw ... how do I mark a pointer then as "unused" ? ... being able to set an arbitrary value would be a feature not a bug imho ...)

for the PointerTarget there is no constructor but 4 different methods (that need to get Addresses as arguments)

and for the Addresses of the different DataTypes it's pretty random again

Image
  • Chunk has a constructor and needs XorAddres

  • GraphEntry doesn't have a constructor and only uses 'from_hex' or 'new'

  • Pointer has a constructor for PublicKey but also comes with from_hex and new (and a hex string doesn't automatically get converted to a publicly in the constructor)

  • Scratchpad same as pointer


I'm very sure you'd see many of the usability issues yourself if you would create some simple python examples yourself for the developer docs ...
(except for the chunkaddress ...) it all works ... but is a nightmare to use tbh ... to get devs excited this must be significantly more intuitive and more fun ...

e.g. like:

from autonomi-client import Client

client = Client()
scratchpad = client.get_scratchpad(secret=<demo-scratchpad-secret>)

print(f'content before write: {scratchpad.content}')

scratchpad.update(b'some new content')

# give the network some time to propagate the content
time.sleep(5)

print(f'content after write: {client.get_scratchpad(secret=<demo-scratchpad-secret>).content}')

to do this right now I need to:

import asyncio
from autonomi_client import Client, ScratchpadAddress, SecretKey, Scratchpad

async def main():
    client = await Client.init()

    owner = SecretKey.from_hex("<demo-scratchpad-secret>")
    address = owner.public_key().hex()

    scratchpad = await client.get_scratchpad(ScratchpadAddress.from_hex(address))
    print(f'Content before write: {scratchpad.decrypt_data(owner)}')

    await client.update_scratchpad(owner, 0, b'some new content')

    # give the network some time to propagate the content
    await asyncio.sleep(5)

    scratchpad = await client.get_scratchpad(ScratchpadAddress.from_hex(address))
    print(f'Content after write: {scratchpad.decrypt_data(owner)}')

if __name__ == "__main__":
    asyncio.run(main())

(and this is the flow without payments needed and just updating a simple scratchpad ... btw I'm still not sure what the "content type" field at the scratchpad is meant to be used for ... I just always write it to 0 ...)


please don't get offended by me criticizing and speaking out just what pops up my mind ... that's just my feelings when trying to use the python library as an end user ... maybe the library needs an additional wrapper to make it nicer to use ... you're doing amazing work and it's super exciting to finally see the network operational in the wild ...
(but without UX improvements we won't create excitement for any dev here I think ... it needs to feel easy ... the objects need a sync wrapper ... and some comfort functionality so that not every dev needs to create those utility functions locally ...)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions