A declarative implementation of BARE for Python
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
Noah Pederson 5609f3ae14 Bumps version 2 months ago
bare Fix unpacking of `Data` and `DataFixed` 2 months ago
.build.yml Adds .build.yml 3 months ago
.gitignore Updates gitignore 3 months ago
LICENSE Adds LICENSE 3 months ago
README.md Use proper builds.sr.ht badge 3 months ago
setup.py Bumps version 2 months ago

README.md

PyBARE

builds.sr.ht status

An declarative implementation of the BARE message format for Python 3.6+


pybare is a general purpose library for strongly typed primitives in Python that supports serializing to and from BARE messages.

pip install pybare

Goals

  • Provide a declarative structure for defining types
  • Validation on value updates
  • Support streaming messages
  • Codegen based on .schema files

Status

pybare fully implements all BARE types for both encoding and decoding. This includes reading multiple messages from the same BinaryIO stream.

TODO

  • [ ] Codegen based on .schema files
  • [ ] Better documentation
  • [ ] More tests
  • [ ] Fast C implementation for encoding

Examples

pybare currently requires you define your structures by hand. Examples can be found in the tests.

Quickstart

from bare import Struct, Map, Str, UInt, Optional, DataFixed

# Alternatively, DataFixed(length=64)
class PubKey(DataFixed):
    _length = 64 # 512 bits

class User(Struct):
    username = Str()
    userid = Int()
    email = Optional(Str)
    keys = Map(Str, PubKey)
    repos = Array(Str) # variable length array


noah = User(username="chiefnoah", userid=1)
noah.username == 'chiefnoah' # True
noah.username = 'someoneelse'
noah.username == 'someoneelse' # True
noah.userid == 1 # True
noah.username = 1 # raise: bare.ValidationError
noah.keys # {} (empty dict)
noah.keys['my key'] = bytes(64) #\x00\x00...
noah.keys['oops'] = bytes(1) # raise: bare.ValidationError
noah.email is None: # True
noah.email = 12345 # raise: bare.ValidationError
noah.pack() # \x00\x01 ... (binary data)

'Magic' values

pybare primitive types (refered to by their baseclass Field) and their subclasses implement the descriptor protocol to get their 'magic' behavior. When an instance is declared as a class field, the type can be treated as it's underlying value (ie. noah.username is just the str "chiefnoah" instead of bare.Str), while also providing validation and the ability to "pack" the values into their corresponding bare primitives.

Note: in order to be treated as Struct members, fields must be declared as instances, not just their types.

For example:

class User(Struct):
    username = Str # this is ignored!
    email = Str() # this isn't!

Struct and it's subclasses do not implement the descriptor protocol, as they are container types. On instances of Structs (or any other object that declares a Field type as a class field), the underlying value is stored as the class field name prefixed with _.

Example:

u = User(username="noah")
u.username # "noah"
u._username # "noah"

You can modify this 'internal' value directly to bypass any validation imposed by the Field type (but why would you want to do that).

Despite all of this, it's generally safe to include functions and other data on subclasses of Fields. Again, only fields that have been declared on the class will be serialized with pack.


To contribute, send patches to ~chiefnoah/inbox@lists.sr.ht