iibin 2.0.0a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
iibin-2.0.0a1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jochen Schultz <js@intelligent-intern.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ include native/python.c native/core/wire.c native/core/wire.h
2
+ include RELEASING.md
iibin-2.0.0a1/PKG-INFO ADDED
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.4
2
+ Name: iibin
3
+ Version: 2.0.0a1
4
+ Summary: Schema-defined IIBIN binary messages with a native C codec
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/Intelligent-Intern/iibin-py
7
+ Project-URL: Issues, https://github.com/Intelligent-Intern/iibin-py/issues
8
+ Keywords: iibin,serialization,binary,protobuf,codec
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: Implementation :: CPython
13
+ Classifier: Programming Language :: C
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Dynamic: license-file
18
+
19
+ # IIBIN Python
20
+
21
+ Native C protobuf wire codec with a CPython extension and a small schema registration API. The portable C wire buffer/varint implementation is in `native/core`; `native/python.c` binds Python values. Original PHP/Zend sources under `native/king` are reference only and are not compiled or imported.
22
+
23
+ ```python
24
+ from iibin import Schema
25
+ message = Schema({'text': {'id': 1, 'type': 'string'}})
26
+ message.decode(message.encode({'text': 'Hallo'}))
27
+ ```
28
+
29
+ Install the 2.0 prerelease with `pip install --pre iibin`. `pip install .` builds the extension from source. A C compiler and Python development headers are needed when a wheel is unavailable for your interpreter/platform. `make build` creates a source distribution and a local wheel; see [RELEASING.md](RELEASING.md) for PyPI uploads.
30
+
31
+ Supported field types: string, bytes, bool, int32/64, uint32/64, sint32/64, fixed32/64, sfixed32/64, float, double, message. Fields may be repeated; nested schemas use `schema`. Numeric repeated fields decode packed and unpacked; encoding is unpacked. Unknown incoming fields are skipped. Absent fields remain absent. Invalid tags, wire mismatches, invalid UTF-8, numeric range violations, non-finite values, nesting above 32 and frames larger than 16 MiB are rejected. Python integers preserve the full 64-bit range.
32
+
33
+ The JS package has the same wire contract. King enum registries, map/oneof declarations and recursive named schema references are not exposed by these bindings yet. This is not complete API parity with the PHP extension. MIT license.
34
+
35
+ Source: [iibin-py](https://github.com/Intelligent-Intern/iibin-py). TypeScript codec: [iibin-js](https://github.com/Intelligent-Intern/iibin-js). Lunar Charge Control and Shopfloor demo: [iibin-demo](https://github.com/Intelligent-Intern/iibin-demo).
@@ -0,0 +1,17 @@
1
+ # IIBIN Python
2
+
3
+ Native C protobuf wire codec with a CPython extension and a small schema registration API. The portable C wire buffer/varint implementation is in `native/core`; `native/python.c` binds Python values. Original PHP/Zend sources under `native/king` are reference only and are not compiled or imported.
4
+
5
+ ```python
6
+ from iibin import Schema
7
+ message = Schema({'text': {'id': 1, 'type': 'string'}})
8
+ message.decode(message.encode({'text': 'Hallo'}))
9
+ ```
10
+
11
+ Install the 2.0 prerelease with `pip install --pre iibin`. `pip install .` builds the extension from source. A C compiler and Python development headers are needed when a wheel is unavailable for your interpreter/platform. `make build` creates a source distribution and a local wheel; see [RELEASING.md](RELEASING.md) for PyPI uploads.
12
+
13
+ Supported field types: string, bytes, bool, int32/64, uint32/64, sint32/64, fixed32/64, sfixed32/64, float, double, message. Fields may be repeated; nested schemas use `schema`. Numeric repeated fields decode packed and unpacked; encoding is unpacked. Unknown incoming fields are skipped. Absent fields remain absent. Invalid tags, wire mismatches, invalid UTF-8, numeric range violations, non-finite values, nesting above 32 and frames larger than 16 MiB are rejected. Python integers preserve the full 64-bit range.
14
+
15
+ The JS package has the same wire contract. King enum registries, map/oneof declarations and recursive named schema references are not exposed by these bindings yet. This is not complete API parity with the PHP extension. MIT license.
16
+
17
+ Source: [iibin-py](https://github.com/Intelligent-Intern/iibin-py). TypeScript codec: [iibin-js](https://github.com/Intelligent-Intern/iibin-js). Lunar Charge Control and Shopfloor demo: [iibin-demo](https://github.com/Intelligent-Intern/iibin-demo).
@@ -0,0 +1,42 @@
1
+ # Publishing to PyPI
2
+
3
+ ```bash
4
+ make build
5
+ .venv/bin/python -m pip install twine
6
+ .venv/bin/python -m twine check --strict dist/*.tar.gz dist/*.whl
7
+ ```
8
+
9
+ Install the wheel into a fresh environment and run the workspace's
10
+ `../interop/check.py` against JavaScript and the rebuilt King extension.
11
+ Also test installation from the source distribution in a fresh environment.
12
+ The extension currently builds interpreter-specific wheels. A local
13
+ `linux_x86_64` wheel is not accepted by PyPI: on Linux, repair and inspect it:
14
+
15
+ ```bash
16
+ .venv/bin/python -m pip install auditwheel patchelf
17
+ mkdir -p dist/release
18
+ PATH="$PWD/.venv/bin:$PATH" .venv/bin/python -m auditwheel repair dist/iibin-2.0.0a1-cp312-cp312-linux_x86_64.whl -w dist/release
19
+ cp dist/iibin-2.0.0a1.tar.gz dist/release/
20
+ .venv/bin/python -m twine check --strict dist/release/*
21
+ ```
22
+
23
+ The example uses CPython 3.12 on Linux x86_64; adapt the wheel name for other
24
+ builds. Produce other platform/interpreter wheels in their matching build
25
+ environments. Other users can build from source with a C compiler and Python
26
+ development headers.
27
+
28
+ Create a PyPI API token and store it locally in the OS keyring:
29
+
30
+ ```bash
31
+ .venv/bin/python -m keyring set https://upload.pypi.org/legacy/ __token__
32
+ ```
33
+
34
+ Upload only the checked, installation-tested release files:
35
+
36
+ ```bash
37
+ .venv/bin/python -m twine upload --username __token__ dist/release/*
38
+ ```
39
+
40
+ Do not upload the raw local Linux wheel. The version is a prerelease; install
41
+ with `pip install --pre iibin`. PyPI release filenames cannot be reused;
42
+ increase the version for the next release.
@@ -0,0 +1,33 @@
1
+ #include "wire.h"
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ int iibin_append(iibin_buffer *b, const void *data, size_t n) {
5
+ if (n > IIBIN_MAX_BYTES || b->size > IIBIN_MAX_BYTES-n) return 0;
6
+ size_t needed=b->size+n;
7
+ if (needed>b->capacity) {
8
+ size_t cap=b->capacity?b->capacity:128;
9
+ while(cap<needed) cap*=2;
10
+ if(cap>IIBIN_MAX_BYTES) cap=IIBIN_MAX_BYTES;
11
+ void *p=realloc(b->data,cap); if(!p) return 0;
12
+ b->data=p; b->capacity=cap;
13
+ }
14
+ if(n) memcpy(b->data+b->size,data,n);
15
+ b->size=needed; return 1;
16
+ }
17
+ int iibin_write_varint(iibin_buffer *b, uint64_t v) {
18
+ unsigned char bytes[10]; size_t n=0;
19
+ do { bytes[n++]=(unsigned char)((v&127)|(v>127?128:0)); v>>=7; }while(v);
20
+ return iibin_append(b,bytes,n);
21
+ }
22
+ int iibin_read_varint(const unsigned char *d,size_t n,size_t *p,uint64_t *v) {
23
+ *v=0;
24
+ for(unsigned i=0;i<10;i++) {
25
+ if(*p>=n) return 0;
26
+ unsigned char c=d[(*p)++];
27
+ if(i==9 && c>1) return 0;
28
+ *v|=((uint64_t)(c&127))<<(7*i);
29
+ if(!(c&128)) return 1;
30
+ }
31
+ return 0;
32
+ }
33
+ void iibin_buffer_free(iibin_buffer *b){free(b->data);b->data=NULL;b->size=b->capacity=0;}
@@ -0,0 +1,11 @@
1
+ #ifndef IIBIN_WIRE_H
2
+ #define IIBIN_WIRE_H
3
+ #include <stddef.h>
4
+ #include <stdint.h>
5
+ #define IIBIN_MAX_BYTES (16u * 1024u * 1024u)
6
+ typedef struct { unsigned char *data; size_t size, capacity; } iibin_buffer;
7
+ int iibin_append(iibin_buffer *, const void *, size_t);
8
+ int iibin_write_varint(iibin_buffer *, uint64_t);
9
+ int iibin_read_varint(const unsigned char *, size_t, size_t *, uint64_t *);
10
+ void iibin_buffer_free(iibin_buffer *);
11
+ #endif
@@ -0,0 +1,92 @@
1
+ #define PY_SSIZE_T_CLEAN
2
+ #include <Python.h>
3
+ #include <math.h>
4
+ #include <string.h>
5
+ #include "wire.h"
6
+ static int error(const char *msg){PyErr_SetString(PyExc_ValueError,msg);return 0;}
7
+ static const char *type_of(PyObject *f){PyObject *t=PyDict_GetItemString(f,"type");return t?PyUnicode_AsUTF8(t):NULL;}
8
+ static int repeated(PyObject *f){PyObject *v=PyDict_GetItemString(f,"repeated");return v&&PyObject_IsTrue(v);}
9
+ static int wire_type(const char *t){if(!strcmp(t,"string")||!strcmp(t,"bytes")||!strcmp(t,"message"))return 2;if(!strcmp(t,"double")||!strcmp(t,"fixed64")||!strcmp(t,"sfixed64"))return 1;if(!strcmp(t,"float")||!strcmp(t,"fixed32")||!strcmp(t,"sfixed32"))return 5;return 0;}
10
+ static int put(iibin_buffer *b,const void *d,size_t n){return iibin_append(b,d,n)||error("Message exceeds 16 MiB or allocation failed");}
11
+ static int varint(iibin_buffer *b,uint64_t v){return iibin_write_varint(b,v)||error("Message exceeds 16 MiB or allocation failed");}
12
+ static int encode_message(PyObject *,PyObject *,iibin_buffer *,int);
13
+ static PyObject *decode_message(PyObject *,const unsigned char *,size_t,int);
14
+ static int number(PyObject *v,const char *t,uint64_t *out){
15
+ if(!strcmp(t,"bool")){if(!PyBool_Check(v))return error("Expected bool");*out=v==Py_True;return 1;}
16
+ if(!PyLong_Check(v)||PyBool_Check(v))return error("Expected integer");
17
+ int uns=!strcmp(t,"uint32")||!strcmp(t,"uint64")||!strcmp(t,"fixed32")||!strcmp(t,"fixed64");
18
+ if(uns){*out=PyLong_AsUnsignedLongLong(v);if(PyErr_Occurred())return 0;if((strstr(t,"32"))&&*out>UINT32_MAX)return error("Unsigned integer out of range");}
19
+ else {long long n=PyLong_AsLongLong(v);if(PyErr_Occurred())return 0;if(strstr(t,"32")&&(n<INT32_MIN||n>INT32_MAX))return error("Signed integer out of range");*out=(uint64_t)n;}
20
+ return 1;
21
+ }
22
+ static int encode_value(PyObject *f,PyObject *v,iibin_buffer *b,int depth){
23
+ const char *t=type_of(f);if(!t)return 0;int w=wire_type(t);
24
+ if(w==2){
25
+ if(!strcmp(t,"message")){iibin_buffer nested={0};int ok=encode_message(PyDict_GetItemString(f,"schema"),v,&nested,depth+1);if(ok)ok=varint(b,nested.size)&&put(b,nested.data,nested.size);iibin_buffer_free(&nested);return ok;}
26
+ if(!strcmp(t,"string")){if(!PyUnicode_Check(v))return error("Expected string");Py_ssize_t n;const char *s=PyUnicode_AsUTF8AndSize(v,&n);if(!s)return 0;return varint(b,(uint64_t)n)&&put(b,s,(size_t)n);}
27
+ Py_buffer view;if(PyObject_GetBuffer(v,&view,PyBUF_SIMPLE)<0)return 0;int ok=varint(b,view.len)&&put(b,view.buf,view.len);PyBuffer_Release(&view);return ok;
28
+ }
29
+ uint64_t bits=0;
30
+ if(!strcmp(t,"float")||!strcmp(t,"double")){if(PyBool_Check(v)||(!PyFloat_Check(v)&&!PyLong_Check(v)))return error("Expected finite number");double d=PyFloat_AsDouble(v);if(PyErr_Occurred())return 0;if(!isfinite(d))return error("Non-finite number");if(w==5){float x=(float)d;if(!isfinite(x))return error("Float out of range");uint32_t u;memcpy(&u,&x,4);bits=u;}else memcpy(&bits,&d,8);}
31
+ else if(!number(v,t,&bits))return 0;
32
+ if(w==0){if(!strncmp(t,"sint",4))bits=(bits<<1)^((bits>>63)?UINT64_MAX:0);return varint(b,bits);}
33
+ unsigned char buf[8];size_t count=w==5?4:8;for(size_t i=0;i<count;i++)buf[i]=(unsigned char)(bits>>(8*i));return put(b,buf,count);
34
+ }
35
+ static int encode_message(PyObject *schema,PyObject *obj,iibin_buffer *b,int depth){
36
+ if(depth>32)return error("Message nesting exceeds 32");if(!PyDict_Check(obj))return error("Message must be a dictionary");
37
+ PyObject *key,*value;Py_ssize_t pos=0;
38
+ while(PyDict_Next(obj,&pos,&key,&value))if(!PyDict_Contains(schema,key))return error("Unknown message field");
39
+ pos=0;
40
+ while(PyDict_Next(schema,&pos,&key,&value)){
41
+ PyObject *v=PyDict_GetItemWithError(obj,key);if(!v){if(PyErr_Occurred())return 0;continue;}
42
+ const char *t=type_of(value);if(!t)return 0;uint64_t tag=(PyLong_AsUnsignedLong(PyDict_GetItemString(value,"id"))<<3)|wire_type(t);
43
+ if(PyErr_Occurred())return 0;
44
+ if(repeated(value)){if(!PyList_Check(v)&&!PyTuple_Check(v))return error("Repeated field must be a list or tuple");Py_ssize_t n=PySequence_Size(v);for(Py_ssize_t i=0;i<n;i++){PyObject *item=PySequence_GetItem(v,i);if(!item)return 0;int ok=varint(b,tag)&&encode_value(value,item,b,depth);Py_DECREF(item);if(!ok)return 0;}}
45
+ else if(!varint(b,tag)||!encode_value(value,v,b,depth))return 0;
46
+ }return 1;
47
+ }
48
+ static PyObject *decode_value(PyObject *f,const unsigned char *data,size_t n,size_t *pos,int depth){
49
+ const char *t=type_of(f);if(!t)return NULL;int w=wire_type(t);uint64_t u=0;
50
+ if(w==2){uint64_t len;if(!iibin_read_varint(data,n,pos,&len)||len>n-*pos){error("Truncated length-delimited field");return NULL;}const unsigned char *s=data+*pos;*pos+=(size_t)len;
51
+ if(!strcmp(t,"string"))return PyUnicode_DecodeUTF8((const char*)s,(Py_ssize_t)len,"strict");
52
+ if(!strcmp(t,"bytes"))return PyBytes_FromStringAndSize((const char*)s,(Py_ssize_t)len);
53
+ return decode_message(PyDict_GetItemString(f,"schema"),s,(size_t)len,depth+1);
54
+ }
55
+ if(w==0){if(!iibin_read_varint(data,n,pos,&u)){error("Truncated or overflowing varint");return NULL;}}
56
+ else {size_t count=w==5?4:8;if(n-*pos<count){error("Truncated fixed-width field");return NULL;}for(size_t i=0;i<count;i++)u|=((uint64_t)data[(*pos)++])<<(8*i);}
57
+ if(!strcmp(t,"bool"))return PyBool_FromLong(u!=0);
58
+ if(!strcmp(t,"float")){uint32_t v=(uint32_t)u;float x;memcpy(&x,&v,4);if(!isfinite(x)){error("Non-finite float");return NULL;}return PyFloat_FromDouble(x);}
59
+ if(!strcmp(t,"double")){double x;memcpy(&x,&u,8);if(!isfinite(x)){error("Non-finite double");return NULL;}return PyFloat_FromDouble(x);}
60
+ if(!strncmp(t,"sint",4)){if(strstr(t,"32")&&u>UINT32_MAX){error("sint32 out of range");return NULL;}return PyLong_FromLongLong((int64_t)((u>>1)^(uint64_t)-(int64_t)(u&1)));}
61
+ if(!strcmp(t,"int32")){
62
+ if(u>INT32_MAX && u<(uint64_t)(int64_t)INT32_MIN){error("int32 out of range");return NULL;}
63
+ return PyLong_FromLong((int32_t)u);
64
+ }
65
+ if(!strcmp(t,"sfixed32"))return PyLong_FromLong((int32_t)u);
66
+ if(!strcmp(t,"uint32")||!strcmp(t,"fixed32")){if(u>UINT32_MAX){error("uint32 out of range");return NULL;}return PyLong_FromUnsignedLong((uint32_t)u);}
67
+ if(!strcmp(t,"int64")||!strcmp(t,"sfixed64"))return PyLong_FromLongLong((int64_t)u);
68
+ return PyLong_FromUnsignedLongLong(u);
69
+ }
70
+ static int skip(const unsigned char *d,size_t n,size_t *p,int w){uint64_t v;if(w==0)return iibin_read_varint(d,n,p,&v);if(w==2){if(!iibin_read_varint(d,n,p,&v)||v>n-*p)return 0;*p+=(size_t)v;return 1;}size_t k=w==1?8:w==5?4:0;if(!k||n-*p<k)return 0;*p+=k;return 1;}
71
+ static PyObject *decode_message(PyObject *schema,const unsigned char *d,size_t n,int depth){
72
+ if(depth>32){error("Message nesting exceeds 32");return NULL;}
73
+ PyObject *result=PyDict_New();if(!result)return NULL;size_t p=0;
74
+ while(p<n){uint64_t tag;if(!iibin_read_varint(d,n,&p,&tag)||tag<8||tag>>3>=((uint64_t)1<<29)){error("Invalid field tag");goto fail;}int w=tag&7;PyObject *key,*field,*found=NULL,*name=NULL;Py_ssize_t it=0;
75
+ while(PyDict_Next(schema,&it,&key,&field)){unsigned long id=PyLong_AsUnsignedLong(PyDict_GetItemString(field,"id"));if(id==(tag>>3)){found=field;name=key;break;}}
76
+ if(!found){if(!skip(d,n,&p,w)){error("Invalid unknown field");goto fail;}continue;}
77
+ const char *t=type_of(found);if(!t)goto fail;int expected=wire_type(t);
78
+ int packed=w==2&&expected!=2&&repeated(found);size_t packed_end=0;
79
+ if(packed){uint64_t len;if(!iibin_read_varint(d,n,&p,&len)||len>n-p){error("Truncated packed field");goto fail;}if(!len)continue;packed_end=p+(size_t)len;}
80
+ PyObject *list=NULL;
81
+ if(repeated(found)){list=PyDict_GetItemWithError(result,name);if(!list){list=PyList_New(0);if(!list)goto fail;int rc=PyDict_SetItem(result,name,list);Py_DECREF(list);if(rc<0)goto fail;list=PyDict_GetItem(result,name);}}
82
+ if(packed){while(p<packed_end){PyObject *v=decode_value(found,d,packed_end,&p,depth);if(!v)goto fail;int rc=PyList_Append(list,v);Py_DECREF(v);if(rc<0)goto fail;}continue;}
83
+ if(w!=expected){error("Field wire type mismatch");goto fail;}
84
+ PyObject *v=decode_value(found,d,n,&p,depth);if(!v)goto fail;int rc=list?PyList_Append(list,v):PyDict_SetItem(result,name,v);Py_DECREF(v);if(rc<0)goto fail;
85
+ }return result;
86
+ fail:Py_DECREF(result);return NULL;
87
+ }
88
+ static PyObject *encode(PyObject *self,PyObject *args){(void)self;PyObject *schema,*obj;if(!PyArg_ParseTuple(args,"O!O!",&PyDict_Type,&schema,&PyDict_Type,&obj))return NULL;iibin_buffer b={0};int ok=encode_message(schema,obj,&b,0);PyObject *out=ok?PyBytes_FromStringAndSize((char*)b.data,(Py_ssize_t)b.size):NULL;iibin_buffer_free(&b);return out;}
89
+ static PyObject *decode(PyObject *self,PyObject *args){(void)self;PyObject *schema;Py_buffer view;if(!PyArg_ParseTuple(args,"O!y*",&PyDict_Type,&schema,&view))return NULL;if(view.len>IIBIN_MAX_BYTES){PyBuffer_Release(&view);error("Message exceeds 16 MiB");return NULL;}PyObject *out=decode_message(schema,view.buf,view.len,0);PyBuffer_Release(&view);return out;}
90
+ static PyMethodDef methods[]={{"encode",encode,METH_VARARGS,"Encode schema message in native C."},{"decode",decode,METH_VARARGS,"Decode schema message in native C."},{NULL,NULL,0,NULL}};
91
+ static struct PyModuleDef module={PyModuleDef_HEAD_INIT,"_native",NULL,-1,methods,NULL,NULL,NULL,NULL};
92
+ PyMODINIT_FUNC PyInit__native(void){return PyModule_Create(&module);}
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+ [project]
5
+ name = "iibin"
6
+ version = "2.0.0a1"
7
+ description = "Schema-defined IIBIN binary messages with a native C codec"
8
+ readme = "README.md"
9
+ requires-python = ">=3.10"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ keywords = ["iibin", "serialization", "binary", "protobuf", "codec"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Intended Audience :: Developers",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: Implementation :: CPython",
18
+ "Programming Language :: C",
19
+ ]
20
+ [tool.setuptools.packages.find]
21
+ where = ["src"]
22
+
23
+ [project.urls]
24
+ Repository = "https://github.com/Intelligent-Intern/iibin-py"
25
+ Issues = "https://github.com/Intelligent-Intern/iibin-py/issues"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
iibin-2.0.0a1/setup.py ADDED
@@ -0,0 +1,10 @@
1
+ import sys
2
+
3
+ from setuptools import Extension, setup
4
+
5
+ setup(ext_modules=[Extension(
6
+ 'iibin._native',
7
+ ['native/python.c', 'native/core/wire.c'],
8
+ include_dirs=['native/core'],
9
+ extra_compile_args=['/std:c11', '/W3'] if sys.platform == 'win32' else ['-std=c11', '-Wall', '-Wextra'],
10
+ )])
@@ -0,0 +1,28 @@
1
+ """Schema-defined protobuf-wire messages. No JSON envelope or content digests."""
2
+ from ._native import encode as _encode, decode as _decode
3
+
4
+ _TYPES = {'string','bytes','bool','int32','uint32','sint32','int64','uint64','sint64','fixed32','sfixed32','fixed64','sfixed64','float','double','message'}
5
+
6
+ def _validate(schema, depth=0):
7
+ if depth > 32 or not isinstance(schema, dict):
8
+ raise ValueError('Invalid or excessively nested schema')
9
+ result, ids = {}, set()
10
+ for name, original in schema.items():
11
+ f = dict(original)
12
+ i, t = f.get('id'), f.get('type')
13
+ if not isinstance(name,str) or not name or type(i) is not int or not 0<i<2**29 or 19000<=i<=19999 or i in ids or t not in _TYPES:
14
+ raise ValueError(f'Invalid field {name!r}')
15
+ ids.add(i)
16
+ if t == 'message': f['schema'] = _validate(f.get('schema'), depth+1)
17
+ result[name] = f
18
+ return dict(sorted(result.items(), key=lambda item: item[1]['id']))
19
+
20
+ class Schema:
21
+ def __init__(self, fields):
22
+ self.fields = _validate(fields)
23
+ def encode(self, message):
24
+ return _encode(self.fields, message)
25
+ def decode(self, data):
26
+ return _decode(self.fields, data)
27
+
28
+ __all__ = ['Schema']
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.4
2
+ Name: iibin
3
+ Version: 2.0.0a1
4
+ Summary: Schema-defined IIBIN binary messages with a native C codec
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/Intelligent-Intern/iibin-py
7
+ Project-URL: Issues, https://github.com/Intelligent-Intern/iibin-py/issues
8
+ Keywords: iibin,serialization,binary,protobuf,codec
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: Implementation :: CPython
13
+ Classifier: Programming Language :: C
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Dynamic: license-file
18
+
19
+ # IIBIN Python
20
+
21
+ Native C protobuf wire codec with a CPython extension and a small schema registration API. The portable C wire buffer/varint implementation is in `native/core`; `native/python.c` binds Python values. Original PHP/Zend sources under `native/king` are reference only and are not compiled or imported.
22
+
23
+ ```python
24
+ from iibin import Schema
25
+ message = Schema({'text': {'id': 1, 'type': 'string'}})
26
+ message.decode(message.encode({'text': 'Hallo'}))
27
+ ```
28
+
29
+ Install the 2.0 prerelease with `pip install --pre iibin`. `pip install .` builds the extension from source. A C compiler and Python development headers are needed when a wheel is unavailable for your interpreter/platform. `make build` creates a source distribution and a local wheel; see [RELEASING.md](RELEASING.md) for PyPI uploads.
30
+
31
+ Supported field types: string, bytes, bool, int32/64, uint32/64, sint32/64, fixed32/64, sfixed32/64, float, double, message. Fields may be repeated; nested schemas use `schema`. Numeric repeated fields decode packed and unpacked; encoding is unpacked. Unknown incoming fields are skipped. Absent fields remain absent. Invalid tags, wire mismatches, invalid UTF-8, numeric range violations, non-finite values, nesting above 32 and frames larger than 16 MiB are rejected. Python integers preserve the full 64-bit range.
32
+
33
+ The JS package has the same wire contract. King enum registries, map/oneof declarations and recursive named schema references are not exposed by these bindings yet. This is not complete API parity with the PHP extension. MIT license.
34
+
35
+ Source: [iibin-py](https://github.com/Intelligent-Intern/iibin-py). TypeScript codec: [iibin-js](https://github.com/Intelligent-Intern/iibin-js). Lunar Charge Control and Shopfloor demo: [iibin-demo](https://github.com/Intelligent-Intern/iibin-demo).
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ RELEASING.md
5
+ pyproject.toml
6
+ setup.py
7
+ native/python.c
8
+ native/core/wire.c
9
+ native/core/wire.h
10
+ src/iibin/__init__.py
11
+ src/iibin.egg-info/PKG-INFO
12
+ src/iibin.egg-info/SOURCES.txt
13
+ src/iibin.egg-info/dependency_links.txt
14
+ src/iibin.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ iibin