Skip to main content

cprotobuf maintained by LeanCloud

Project description

cprotobuf maintained by LeanCloud

  • Add generated cprotobuf.c back into repo, for installation without Cython

  • Switched to setuptools

cprotobuf

A minimal fast protobuf implementation with cython. Benchmark shows that it’s much faster than google official expremental cpp-python implementation.

I’ve been using it in production since 2013, only tested with python2.7, feedback on other python release is welcome.

Benchmark

$ ./setup.py build_ext --inplace
$ cd benchmark
$ ./bench.sh
encode[google official pure python]:
10 loops, best of 3: 68.8 msec per loop
encode[google official cpp python]:
100 loops, best of 3: 19.4 msec per loop
encode[py-protobuf][cprotobuf]:
100 loops, best of 3: 3.58 msec per loop
decode[google official pure python]:
10 loops, best of 3: 47.5 msec per loop
decode[google official cpp python]:
100 loops, best of 3: 4.55 msec per loop
decode[py-protobuf][cprotobuf]:
100 loops, best of 3: 3.98 msec per loop

Tutorial

Use plugin

You write a person.proto file like this:

package foo;

message Person {
  required int32 id = 1;
  required string name = 2;
  optional string email = 3;
}

And a people.proto file like this:

package foo;
import "person.proto";

message People {
  repeated Person people = 1;
}

Then you compile it with provided plugin:

$ protoc --cprotobuf_out=. person.proto people.proto

If you have trouble to run a protobuf plugin like on windows, you can directly run protoc-gen-cprotobuf like this:

$ protoc -ofoo.pb person.proto people.proto
$ protoc-gen-cprotobuf foo.pb -d .

Then you get a python module foo_pb.py , cprotobuf generate a python module for each package rather than each protocol file.

The generated code is quite readable:

# coding: utf-8
from cprotobuf import ProtoEntity, Field
# file: person.proto
class Person(ProtoEntity):
    id              = Field('int32',        1)
    name            = Field('string',       2)
    email           = Field('string',       3, required=False)

# file: people.proto
class People(ProtoEntity):
    people          = Field(Person, 1, repeated=True)

Actually, if you only use python, you can write this python module, avoid code generation.

The API

Now, you have this lovely python module, how to parse and serialize messages?

When design this package, We try to minimise the effort of migration, so we keep the names of api akin to protocol buffer’s.

encode/decode

>>> from foo_pb import Person, People
>>> msg = People()
>>> msg.people.add(
...    id = 1,
...    name = 'jim',
...    email = 'jim@gmail.com',
... )
>>> s = msg.SerializeToString()
>>> msg2 = People()
>>> msg2.ParseFromString(s)
>>> len(msg2)
1
>>> msg2.people[0].name
'jim'

reflection

>>> from foo_pb import Person, People
>>> dir(Person._fields[0])
['__class__', '__delattr__', '__doc__', '__format__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__pyx_vtable__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'index', 'name', 'packed', 'repeated', 'required', 'wire_type']
>>> Person._fields[0].name
'email'
>>> Person._fieldsmap
{1: <cprotobuf.Field object at 0xb74a538c>, 2: <cprotobuf.Field object at 0xb74a541c>, 3: <cprotobuf.Field object at 0xb74a5c8c>}
>>> Person._fieldsmap_by_name
{'email': <cprotobuf.Field object at 0xb74a5c8c>, 'name': <cprotobuf.Field object at 0xb74a541c>, 'id': <cprotobuf.Field object at 0xb74a538c>}

repeated container

We use RepeatedContainer to represent repeated field, RepeatedContainer is inherited from list, so you can manipulate it like a list, or with apis like google’s implementation.

>>> from foo_pb import Person, People
>>> msg = People()
>>> msg.people.add(
...    id = 1,
...    name = 'jim',
...    email = 'jim@gmail.com',
... )
>>> p = msg.people.add()
>>> p.id = 2
>>> p.name = 'jake'
>>> p.email = 'jake@gmail.com'
>>> p2 = Person(id=3, name='lucy', email='lucy@gmail.com')
>>> msg.people.append(p2)
>>> msg.people.append({
...     'id' : 4,
...     'name' : 'lily',
...     'email' : 'lily@gmail.com',
... })

encode raw data fast

If you already have your messages represented as list and dict, you can encode it without constructing intermidiate objects, getting ride of a lot of overhead:

>>> from cprotobuf import encode_data
>>> from foo_pb import Person, People
>>> s = encode_data(People, [
...     { 'id': 1, 'name': 'tom', 'email': 'tom@gmail.com' }
... ])
>>> msg = People()
>>> msg.ParseFromString(s)
>>> msg.people[0].name
'tom'

Run Tests

$ nosetests

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cprotobuf-lc-0.1.5.tar.gz (97.5 kB view details)

Uploaded Source

Built Distribution

cprotobuf_lc-0.1.5-cp27-cp27m-macosx_10_12_intel.whl (135.3 kB view details)

Uploaded CPython 2.7m macOS 10.12+ intel

File details

Details for the file cprotobuf-lc-0.1.5.tar.gz.

File metadata

File hashes

Hashes for cprotobuf-lc-0.1.5.tar.gz
Algorithm Hash digest
SHA256 5ffb60be89243c5debe53f1d4f64e8058080d68c62c924238bf7b49edf892c26
MD5 c42dfe62f3b2fed32b23fc307d00e79c
BLAKE2b-256 242a8fde3e954f75090fe663110b83c10bfbc574d25d6bbd2c29bccc67ddf411

See more details on using hashes here.

File details

Details for the file cprotobuf_lc-0.1.5-cp27-cp27m-macosx_10_12_intel.whl.

File metadata

File hashes

Hashes for cprotobuf_lc-0.1.5-cp27-cp27m-macosx_10_12_intel.whl
Algorithm Hash digest
SHA256 3e32ae3326cbb1590047b644b0c2aed1efdd37e7bcd5fb9a933fab3da87cfaf4
MD5 a601e578f71a1ee5ca7805d0eefa33c4
BLAKE2b-256 eed1d49c8d2b028c1fd342eb696c24f9521246c849dc9240a89fa79a3e4129c9

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page