From 487bfdfc0603ab7bf5fad381f52b8932cbcd355e Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Mon, 8 Aug 2011 16:33:48 -0700 Subject: Begin port of Python extension to new APIs. --- lang_ext/python/cext.c | 17 - lang_ext/python/cext.h | 48 -- lang_ext/python/definition.c | 336 -------------- lang_ext/python/definition.h | 49 -- lang_ext/python/pb.c | 920 -------------------------------------- lang_ext/python/setup.py | 10 +- lang_ext/python/test.py | 14 + lang_ext/python/upb.c | 190 ++++++++ lang_ext/python/upb/definition.py | 1 - lang_ext/python/upb/pb.py | 1 - upb/def.h | 6 +- 11 files changed, 211 insertions(+), 1381 deletions(-) delete mode 100644 lang_ext/python/cext.c delete mode 100644 lang_ext/python/cext.h delete mode 100644 lang_ext/python/definition.c delete mode 100644 lang_ext/python/definition.h delete mode 100644 lang_ext/python/pb.c create mode 100644 lang_ext/python/test.py create mode 100644 lang_ext/python/upb.c delete mode 100644 lang_ext/python/upb/definition.py delete mode 100644 lang_ext/python/upb/pb.py diff --git a/lang_ext/python/cext.c b/lang_ext/python/cext.c deleted file mode 100644 index 59e8346..0000000 --- a/lang_ext/python/cext.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ - -#include "cext.h" - -PyMODINIT_FUNC -initcext(void) -{ - PyObject *mod = Py_InitModule("upb.cext", NULL); - (void)mod; - initdefinition(); - initpb(); -} diff --git a/lang_ext/python/cext.h b/lang_ext/python/cext.h deleted file mode 100644 index f53e6e7..0000000 --- a/lang_ext/python/cext.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Joshua Haberman. See LICENSE for details. - * Author: Josh Haberman - */ - -#ifndef UPB_PYTHON_CEXT_H_ -#define UPB_PYTHON_CEXT_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - PyObject_HEAD - struct upb_context *context; - PyObject *created_defs; -} PyUpb_Context; - -typedef struct { - PyObject_HEAD - struct upb_msgdef *def; - PyUpb_Context *context; -} PyUpb_MsgDef; - -extern PyTypeObject PyUpb_MsgDefType; - -/* What format string should be passed to PyArg_ParseTuple to get just a raw - * string of bytes and a length. */ -#if PY_MAJOR_VERSION >= 3 -#define BYTES_FORMAT "y#" -#else -#define BYTES_FORMAT "s#" -#endif - -#define RETURN_BOOL(val) if(val) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } - -extern PyMODINIT_FUNC initdefinition(void); -extern PyMODINIT_FUNC initpb(void); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/lang_ext/python/definition.c b/lang_ext/python/definition.c deleted file mode 100644 index 8e8b852..0000000 --- a/lang_ext/python/definition.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This file defines the Python module upb.definition. This module - * defines the following Python classes, which wrap upb's internal - * definitions: - * - * - upb.definition.MessageDefintion - * - upb.definition.EnumDefinition (TODO) - * - upb.definition.ServiceDefinition (TODO) - * - * We also define a class upb.definition.Context, which provides the - * mechanism for loading the above definitions from .proto files or - * from binary descriptors. - * - * Once these definitions are loaded, we can use them to create - * the Python types for each .proto message type. That is covered - * in other files. - */ - -#include "definition.h" -#include "upb_context.h" -#include "upb_msg.h" - -static PyTypeObject PyUpb_ContextType; -static struct upb_strtable msgdefs; -static struct upb_strtable contexts; - -struct msgtab_entry { - struct upb_strtable_entry e; - PyUpb_MsgDef *msgdef; -}; - -struct contexttab_entry { - struct upb_strtable_entry e; - PyUpb_Context *context; -}; - -#define CheckContext(obj) \ - (void*)obj; do { \ - if(!PyObject_TypeCheck(obj, &PyUpb_ContextType)) { \ - PyErr_SetString(PyExc_TypeError, "Must be a upb.Context"); \ - return NULL; \ - } \ - } while(0) - -/* upb.def.MessageDefinition **************************************************/ - -/* Not implemented yet, but these methods will expose information about the - * message definition (the upb_msgdef). */ -static PyMethodDef msgdef_methods[] = { - {NULL, NULL} -}; - -static void msgdef_dealloc(PyObject *obj) -{ - PyUpb_MsgDef *md_obj = (void*)obj; - Py_DECREF(md_obj->context); - obj->ob_type->tp_free(obj); -} - -PyTypeObject PyUpb_MsgDefType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "upb.definition.MessageDefinition", /* tp_name */ - sizeof(PyUpb_MsgDef), /* tp_basicsize */ - 0, /* tp_itemsize */ - msgdef_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - msgdef_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* Can't be created in Python. */ /* tp_new */ - 0, /* tp_free */ -}; - -/* upb.Context ****************************************************************/ - -static PyObject *context_parsefds(PyObject *_context, PyObject *args) -{ - PyUpb_Context *context = CheckContext(_context); - struct upb_string str; - if(!PyArg_ParseTuple(args, BYTES_FORMAT, &str.ptr, &str.byte_len)) - return NULL; - str.byte_size = 0; /* We don't own that mem. */ - - if(!upb_context_parsefds(context->context, &str)) { - /* TODO: an appropriate error. */ - PyErr_SetString(PyExc_TypeError, "Failed to parse."); \ - return NULL; \ - } - Py_RETURN_NONE; -} - -static PyObject *get_or_create_def(struct upb_symtab_entry *e) -{ - switch(e->type) { - case UPB_SYM_MESSAGE: return (PyObject*)get_or_create_msgdef(e->ref.msg); - case UPB_SYM_ENUM: - case UPB_SYM_SERVICE: - case UPB_SYM_EXTENSION: - default: fprintf(stderr, "upb.pb, not implemented.\n"); abort(); return NULL; - } -} - -static PyUpb_Context *get_or_create_context(struct upb_context *context) -{ - PyUpb_Context *pycontext = NULL; - struct upb_string str = {.ptr = (char*)&context, .byte_len = sizeof(void*)}; - struct contexttab_entry *e = upb_strtable_lookup(&contexts, &str); - if(!e) { - pycontext = (void*)PyUpb_ContextType.tp_alloc(&PyUpb_ContextType, 0); - pycontext->context = context; - struct contexttab_entry new_e = { - .e = {.key = {.ptr = (char*)&pycontext->context, .byte_len = sizeof(void*)}}, - .context = pycontext - }; - upb_strtable_insert(&contexts, &new_e.e); - } else { - pycontext = e->context; - Py_INCREF(pycontext); - } - return pycontext; -} - -PyUpb_MsgDef *get_or_create_msgdef(struct upb_msgdef *def) -{ - PyUpb_MsgDef *pydef = NULL; - struct upb_string str = {.ptr = (char*)&def, .byte_len = sizeof(void*)}; - struct msgtab_entry *e = upb_strtable_lookup(&msgdefs, &str); - if(!e) { - pydef = (void*)PyUpb_MsgDefType.tp_alloc(&PyUpb_MsgDefType, 0); - pydef->def = def; - pydef->context = get_or_create_context(def->context); - struct msgtab_entry new_e = { - .e = {.key = {.ptr = (char*)&pydef->def, .byte_len = sizeof(void*)}}, - .msgdef = pydef - }; - upb_strtable_insert(&msgdefs, &new_e.e); - } else { - pydef = e->msgdef; - Py_INCREF(pydef); - } - return pydef; -} - -static PyObject *context_lookup(PyObject *self, PyObject *args) -{ - PyUpb_Context *context = CheckContext(self); - struct upb_string str; - if(!PyArg_ParseTuple(args, "s#", &str.ptr, &str.byte_len)) - return NULL; - str.byte_size = 0; /* We don't own that mem. */ - - struct upb_symtab_entry e; - if(upb_context_lookup(context->context, &str, &e)) { - return get_or_create_def(&e); - } else { - Py_RETURN_NONE; - } -} - -static PyObject *context_resolve(PyObject *self, PyObject *args) -{ - PyUpb_Context *context = CheckContext(self); - struct upb_string str; - struct upb_string base; - if(!PyArg_ParseTuple(args, "s#s#", &base.ptr, &base.byte_len, - &str.ptr, &str.byte_len)) - return NULL; - str.byte_size = 0; /* We don't own that mem. */ - - struct upb_symtab_entry e; - if(upb_context_resolve(context->context, &base, &str, &e)) { - return get_or_create_def(&e); - } else { - Py_RETURN_NONE; - } -} - -/* Callback for upb_context_enumerate below. */ -static void add_string(void *udata, struct upb_symtab_entry *entry) -{ - PyObject *list = udata; - struct upb_string *s = &entry->e.key; - /* TODO: check return. */ - PyObject *str = PyString_FromStringAndSize(s->ptr, s->byte_len); - PyList_Append(list, str); - Py_DECREF(str); -} - -static PyObject *context_symbols(PyObject *self, PyObject *args) -{ - PyUpb_Context *context = CheckContext(self); - PyObject *list = PyList_New(0); /* TODO: check return. */ - upb_context_enumerate(context->context, add_string, list); - return list; -} - -static PyMethodDef context_methods[] = { - {"parse_file_descriptor_set", context_parsefds, METH_VARARGS, - "Parses a string containing a serialized FileDescriptorSet and adds its " - "definitions to the context." - }, - {"lookup", context_lookup, METH_VARARGS, - "Finds a symbol by fully-qualified name (eg. foo.bar.MyType)." - }, - {"resolve", context_resolve, METH_VARARGS, - "Finds a symbol by a possibly-relative name, which will be interpreted " - "in the context of the given base." - }, - {"symbols", context_symbols, METH_NOARGS, - "Returns a list of symbol names that are defined in this context." - }, - {NULL, NULL} -}; - -static PyObject *context_new(PyTypeObject *subtype, - PyObject *args, PyObject *kwds) -{ - PyUpb_Context *obj = (void*)subtype->tp_alloc(subtype, 0); - obj->context = upb_context_new(); - struct contexttab_entry e = { - .e = {.key = {.ptr = (char*)&obj->context, .byte_len = sizeof(void*)}}, - .context = obj - }; - upb_strtable_insert(&contexts, &e.e); - return (void*)obj; -} - -static void context_dealloc(PyObject *obj) -{ - PyUpb_Context *c = (void*)obj; - upb_context_unref(c->context); - /* TODO: once strtable supports delete. */ - //struct upb_string ptrstr = {.ptr = (char*)&c->context, .byte_len = sizeof(void*)}; - //upb_strtable_delete(&contexts, &ptrstr); - obj->ob_type->tp_free(obj); -} - -static PyTypeObject PyUpb_ContextType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "upb.definition.Context", /* tp_name */ - sizeof(PyUpb_Context), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)context_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - context_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - context_new, /* tp_new */ - 0, /* tp_free */ -}; - -static PyMethodDef methods[] = { - {NULL, NULL} -}; - -PyMODINIT_FUNC -initdefinition(void) -{ - if(PyType_Ready(&PyUpb_ContextType) < 0) return; - if(PyType_Ready(&PyUpb_MsgDefType) < 0) return; - - /* PyModule_AddObject steals a reference. These objects are statically - * allocated and must not be deleted, so we increment their refcount. */ - Py_INCREF(&PyUpb_ContextType); - Py_INCREF(&PyUpb_MsgDefType); - - PyObject *mod = Py_InitModule("upb.cext.definition", methods); - PyModule_AddObject(mod, "Context", (PyObject*)&PyUpb_ContextType); - PyModule_AddObject(mod, "MessageDefinition", (PyObject*)&PyUpb_MsgDefType); - - upb_strtable_init(&contexts, 8, sizeof(struct contexttab_entry)); - upb_strtable_init(&msgdefs, 16, sizeof(struct msgtab_entry)); -} diff --git a/lang_ext/python/definition.h b/lang_ext/python/definition.h deleted file mode 100644 index f7162cf..0000000 --- a/lang_ext/python/definition.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * See def.h for a general description. These definitions - * must be shared so that specific Python message types (for the - * different proto APIs) can have access to the C definitions. */ - -#ifndef UPB_PYTHON_DEFINITION_H_ -#define UPB_PYTHON_DEFINITION_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - PyObject_HEAD - struct upb_context *context; -} PyUpb_Context; - -typedef struct { - PyObject_HEAD - struct upb_msgdef *def; - PyUpb_Context *context; -} PyUpb_MsgDef; - -extern PyTypeObject PyUpb_MsgDefType; - -/* What format string should be passed to PyArg_ParseTuple to get just a raw - * string of bytes and a length. */ -#if PY_MAJOR_VERSION >= 3 -#define BYTES_FORMAT "y#" -#else -#define BYTES_FORMAT "s#" -#endif - -PyUpb_MsgDef *get_or_create_msgdef(struct upb_msgdef *def); - -#define RETURN_BOOL(val) if(val) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/lang_ext/python/pb.c b/lang_ext/python/pb.c deleted file mode 100644 index 70d32d7..0000000 --- a/lang_ext/python/pb.c +++ /dev/null @@ -1,920 +0,0 @@ -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This file implements an interface to Python that is compatible - * (as much as possible) with proto1 (the first implementation of - * protocol buffers, which is only released internally to Google). - */ - -#include -#include -#include "upb_mm.h" -#include "definition.h" - -/* Opcodes that describe all of the operations you can perform on a field of a - * protobuf from Python. For example, foo.has_bar() uses opcode OP_HAS. */ -typedef enum { - /* For non-repeated fields. */ - OP_HAS, - /* For non-repeated fields that are not submessages. */ - OP_SET, - /* For non-repeated message fields. */ - OP_MUTABLE, - - /* For repeated fields. */ - OP_SIZE, OP_LIST, OP_ADD, - - /* For all types of fields. */ - OP_GET, OP_CLEAR -} PyUpb_PbBoundFieldOpCode; - -const char *opcode_names[] = { - "OP_HAS", "OP_SET", "OP_MUTABLE", "OP_SIZE", "OP_LIST", "OP_ADD", "OP_GET", "OP_CLEAR" -}; - -/* Structures for the Python objects we define. */ - -/* Callable that will create a new message object of a specific type. In this - * sense it sort of "pretends" to be a type, but it is not actually a type. */ -typedef struct { - PyObject_HEAD; - PyUpb_MsgDef *def; -} PyUpb_PbMsgCreator; - -/* Message object. All messages use this structure and have the same Python - * type (even if their .proto types are different). The type dictionary for - * this type does not include field accessors -- those are dynamically looked - * up in msg_getattro. */ -typedef struct { - PyObject_HEAD; - struct upb_mm_ref ref; - PyUpb_MsgDef *def; -} PyUpb_PbMsg; - -/* Represents a "bound" operation like obj.has_foo, that will perform the - * operation when called. This is necessary because proto1 has all of its - * operations modeled as methods, so one calls obj.has_foo(), not obj.has_foo - * alone. */ -typedef struct { - PyObject_HEAD; - PyUpb_PbMsg *msg; - struct upb_fielddef *f; - PyUpb_PbBoundFieldOpCode code; -} PyUpb_PbBoundFieldOp; - -static PyTypeObject PyUpb_PbMsgCreatorType; -static PyTypeObject PyUpb_PbMsgType; -static PyTypeObject PyUpb_PbBoundFieldOpType; - -#define Check_MsgCreator(obj) \ - (void*)obj; do { \ - if(!PyObject_TypeCheck(obj, &PyUpb_PbMsgCreatorType)) { \ - PyErr_SetString(PyExc_TypeError, "must be a MessageCreator"); \ - return NULL; \ - } \ - } while(0) - -#define Check_Message(obj) \ - (void*)obj; do { \ - if(!PyObject_TypeCheck(obj, &PyUpb_PbMsgType)) { \ - PyErr_SetString(PyExc_TypeError, "must be a Message"); \ - return NULL; \ - } \ - } while(0) - -#define Check_BoundFieldOp(obj) \ - (void*)obj; do { \ - if(!PyObject_TypeCheck(obj, &PyUpb_PbBoundFieldOpType)) { \ - PyErr_SetString(PyExc_TypeError, "must be a BoundFieldOp"); \ - return NULL; \ - } \ - } while(0) - -#define EXPECT_NO_ARGS if(!PyArg_ParseTuple(args, "")) return NULL; -#define MMREF_TO_PYOBJ(mmref) (PyObject*)((char*)(mmref)-offsetof(PyUpb_PbMsg, ref)) - -static struct upb_mm_ref *NewPyRef(struct upb_mm_ref *fromref, - union upb_mmptr p, upb_mm_ptrtype type) -{ - (void)fromref; /* Don't care. */ - struct upb_mm_ref *ref = NULL; - switch(type) { - case UPB_MM_MSG_REF: { - PyUpb_PbMsg *msg = (void*)PyUpb_PbMsgType.tp_alloc(&PyUpb_PbMsgType, 0); - msg->def = get_or_create_msgdef(p.msg->def); /* gets a ref. */ - ref = &msg->ref; - break; - } - case UPB_MM_STR_REF: { - } - case UPB_MM_ARR_REF: { - } - default: assert(false); abort(); break; /* Shouldn't happen. */ - } - return ref; -} - -struct upb_mm pymm = {NewPyRef}; - -/* upb.pb.BoundFieldOp ********************************************************/ - -static PyObject *upb_to_py(union upb_value_ptr p, upb_field_type_t type) -{ - switch(type) { - default: - PyErr_SetString(PyExc_RuntimeError, "internal: unexpected type"); - return NULL; - case UPB_TYPENUM(DOUBLE): - return PyFloat_FromDouble(*p._double); - case UPB_TYPENUM(FLOAT): - return PyFloat_FromDouble(*p._float); - case UPB_TYPENUM(INT64): - case UPB_TYPENUM(SINT64): - case UPB_TYPENUM(SFIXED64): - return PyLong_FromLongLong(*p.int64); - case UPB_TYPENUM(UINT64): - case UPB_TYPENUM(FIXED64): - return PyLong_FromUnsignedLongLong(*p.uint64); - case UPB_TYPENUM(SFIXED32): - case UPB_TYPENUM(SINT32): - case UPB_TYPENUM(INT32): - case UPB_TYPENUM(ENUM): -#if PY_MAJOR_VERSION >= 3 - return PyLong_FromLong(*p.int32); -#else - return PyInt_FromLong(*p.int32); -#endif - - case UPB_TYPENUM(FIXED32): - case UPB_TYPENUM(UINT32): - return PyLong_FromLong(*p.uint32); - case UPB_TYPENUM(BOOL): - RETURN_BOOL(*p._bool); - case UPB_TYPENUM(STRING): - case UPB_TYPENUM(BYTES): - /* Py3k will distinguish between these two. */ - return PyString_FromStringAndSize((*p.str)->ptr, (*p.str)->byte_len); - case UPB_TYPENUM(GROUP): - case UPB_TYPENUM(MESSAGE): { - union upb_mmptr mmptr = upb_mmptr_read(p, UPB_MM_MSG_REF); - bool created; - struct upb_mm_ref *ref = upb_mm_getref(mmptr, UPB_MM_MSG_REF, &pymm, &created); - PyObject *obj = MMREF_TO_PYOBJ(ref); - if(!created) Py_INCREF(obj); - return obj; - } - } -} - -static long convert_to_long(PyObject *val, long lobound, long hibound, bool *ok) -{ - PyObject *o = PyNumber_Int(val); - if(!o) { - PyErr_SetString(PyExc_OverflowError, "could not convert to long"); - *ok = false; - return -1; - } - long longval = PyInt_AS_LONG(o); - if(longval > hibound || longval < lobound) { - PyErr_SetString(PyExc_OverflowError, "value outside type bounds"); - *ok = false; - return -1; - } - *ok = true; - return longval; -} - -static void set_upbscalarfield(union upb_value_ptr p, PyObject *val, - upb_field_type_t type) -{ - switch(type) { - default: - PyErr_SetString(PyExc_RuntimeError, "internal error"); - return; - case UPB_TYPENUM(DOUBLE): { - PyObject *o = PyNumber_Float(val); - if(!o) { - PyErr_SetString(PyExc_ValueError, "could not convert to double"); - return; - } - *p._double = PyFloat_AS_DOUBLE(o); - return; - } - case UPB_TYPENUM(FLOAT): { - PyObject *o = PyNumber_Float(val); - if(!o) { - PyErr_SetString(PyExc_ValueError, "could not convert to float"); - return; - } - *p._float = PyFloat_AS_DOUBLE(o); - return; - } - case UPB_TYPENUM(INT64): - case UPB_TYPENUM(SINT64): - case UPB_TYPENUM(SFIXED64): { -#if LONG_MAX >= INT64_MAX - bool ok; - long longval = convert_to_long(val, INT64_MIN, INT64_MAX, &ok); - if(ok) *p.int32 = longval; - return; -#else - PyObject *o = PyNumber_Long(val); - if(!o) { - PyErr_SetString(PyExc_ValueError, "could not convert to int64"); - return; - } - *p.int64 = PyLong_AsLongLong(o); - return; -#endif - } - case UPB_TYPENUM(UINT64): - case UPB_TYPENUM(FIXED64): { - PyObject *o = PyNumber_Long(val); - if(!o) { - PyErr_SetString(PyExc_ValueError, "could not convert to uint64"); - return; - } - *p.uint64 = PyLong_AsUnsignedLongLong(o); - return; - } - case UPB_TYPENUM(SFIXED32): - case UPB_TYPENUM(SINT32): - case UPB_TYPENUM(INT32): - case UPB_TYPENUM(ENUM): { - bool ok; - long longval = convert_to_long(val, INT32_MIN, INT32_MAX, &ok); - if(ok) *p.int32 = longval; - return; - } - - case UPB_TYPENUM(FIXED32): - case UPB_TYPENUM(UINT32): { -#if LONG_MAX >= UINT32_MAX - bool ok; - long longval = convert_to_long(val, 0, UINT32_MAX, &ok); - if(ok) *p.int32 = longval; - return; -#else - PyObject *o = PyNumber_Long(val); - if(!o) { - PyErr_SetString(PyExc_ValueError, "could not convert to uint32"); - return; - } - *p.uint32 = PyLong_AsUnsignedLong(o); - return; -#endif - } - - case UPB_TYPENUM(BOOL): - if(!PyBool_Check(val)) { - PyErr_SetString(PyExc_ValueError, "should be true or false"); - return; - } - if(val == Py_True) *p._bool = true; - else if(val == Py_False) *p._bool = false; - else PyErr_SetString(PyExc_RuntimeError, "not true or false?"); - return; - - case UPB_TYPENUM(STRING): - case UPB_TYPENUM(BYTES): { - size_t len = PyString_GET_SIZE(val); - upb_string_resize(*p.str, len); - memcpy((*p.str)->ptr, PyString_AS_STRING(val), len); - return; - } - } -} - -static bool check_py_type(PyObject *obj, upb_field_type_t type) -{ - /* TODO */ - return true; -} - -PyObject* fieldop_call(PyObject *callable, PyObject *args, PyObject *kw) -{ - PyUpb_PbBoundFieldOp *op = Check_BoundFieldOp(callable); - PyUpb_PbMsg *pymsg = op->msg; - struct upb_mm_ref *msgref = &(pymsg->ref); - struct upb_msg *msg = pymsg->ref.p.msg; - struct upb_fielddef *f = op->f; - union upb_value_ptr p = upb_msg_getptr(msg, f); - switch(op->code) { - case OP_HAS: - /* obj.has_foo() */ - EXPECT_NO_ARGS; - RETURN_BOOL(upb_msg_isset(msg, f)); - case OP_SET: { - PyObject *val; - if(upb_isarray(f)) { - /* obj.set_repeatedfoo(i, val) */ - int i; - if(!PyArg_ParseTuple(args, "iO", &i, &val)) return NULL; - if(!upb_msg_isset(msg, f) || i >= (*p.arr)->len) { - PyErr_SetString(PyExc_IndexError, "assignment to invalid index"); - return NULL; - } - p = upb_array_getelementptr(*p.arr, i); - } else { - /* obj.set_foo(val) */ - if(!PyArg_ParseTuple(args, "O", &val)) return NULL; - } - set_upbscalarfield(p, val, f->type); - if(PyErr_Occurred()) return NULL; - Py_RETURN_NONE; - } - case OP_MUTABLE: { - /* obj.mutable_scalarmsg() */ - EXPECT_NO_ARGS; - bool created; - PyObject *obj = MMREF_TO_PYOBJ(upb_mm_getfieldref(msgref, f, &created)); - if(!created) Py_INCREF(obj); - return obj; - } - - /* For repeated fields. */ - case OP_SIZE: { - /* obj.repeatedfoo_size() */ - EXPECT_NO_ARGS; - long len = - upb_msg_isset(msg, f) ? (*upb_msg_getptr(msg, f).arr)->len : 0; - return PyInt_FromLong(len); - } - case OP_LIST: - /* obj.repeatedfoo_list() */ - case OP_ADD: { - /* Parse/Verify the args. */ - PyObject *val; - if(upb_issubmsg(f)) { - /* obj.add_submsgfoo() # returns the new submsg */ - EXPECT_NO_ARGS; - } else { - /* obj.add_scalarfoo(val) */ - if(!PyArg_ParseTuple(args, "O", &val)) return NULL; - if(!check_py_type(val, f->type)) return NULL; - } - - upb_arraylen_t len = (*p.arr)->len; - union upb_value_ptr elem_p = upb_array_getelementptr(*p.arr, len); - upb_array_append(*p.arr); - - if(upb_issubmsg(f)) { - /* string or submsg. */ - bool created; - upb_mm_ptrtype type = upb_elem_ptrtype(f); - union upb_mmptr mmptr = upb_mmptr_read(elem_p, type); - struct upb_mm_ref *valref = upb_mm_getref(mmptr, type, &pymm, &created); - assert(created); - PyObject *obj = MMREF_TO_PYOBJ(valref); - return obj; - } else { - set_upbscalarfield(elem_p, val, f->type); - if(PyErr_Occurred()) return NULL; - Py_RETURN_NONE; - } - } - - /* For all fields. */ - case OP_GET: { - if(upb_isarray(f)) { - /* obj.repeatedfoo(i) */ - int i; - if(!PyArg_ParseTuple(args, "i", &i)) return NULL; - if(!upb_msg_isset(msg, f) || i >= (*p.arr)->len) { - PyErr_SetString(PyExc_IndexError, "get from invalid index"); - return NULL; - } - p = upb_array_getelementptr(*p.arr, i); - } else { - /* obj.foo() */ - EXPECT_NO_ARGS; - } - return upb_to_py(p, f->type); - } - case OP_CLEAR: - /* obj.clear_foo() */ - EXPECT_NO_ARGS; - upb_mm_msgclear(msgref, f); - Py_RETURN_NONE; - - default: - PyErr_SetString(PyExc_RuntimeError, "invalid bound field opcode."); - return NULL; - } -} - -static void fieldop_dealloc(PyObject *obj) -{ - PyUpb_PbBoundFieldOp *op = (void*)obj; - Py_DECREF(op->msg); - obj->ob_type->tp_free(obj); -} - -static PyObject *fieldop_repr(PyObject *obj) -{ - PyUpb_PbBoundFieldOp *op = Check_BoundFieldOp(obj); - struct upb_string *name = op->msg->def->def->descriptor->name; - /* Need to get a NULL-terminated copy of name since PyString_FromFormat - * doesn't support ptr+len. */ - PyObject *nameobj = PyString_FromStringAndSize(name->ptr, name->byte_len); - struct google_protobuf_FieldDescriptorProto *fd = - upb_msg_field_descriptor(op->f, op->msg->def->def); - PyObject *fieldnameobj = PyString_FromStringAndSize(fd->name->ptr, fd->name->byte_len); - PyObject *ret = - PyString_FromFormat("", - PyString_AS_STRING(fieldnameobj), - opcode_names[op->code], PyString_AS_STRING(nameobj)); - Py_DECREF(nameobj); - Py_DECREF(fieldnameobj); - return ret; -} - -static PyTypeObject PyUpb_PbBoundFieldOpType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "upb.pb.BoundFieldOp", /* tp_name */ - sizeof(PyUpb_PbBoundFieldOp), /* tp_basicsize */ - 0, /* tp_itemsize */ - fieldop_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - fieldop_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - fieldop_call, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* Can't be created from Python. */ /* tp_new */ - 0, /* tp_free */ -}; - -/* upb.pb.Message *************************************************************/ - -#define Check_SameProtoType(obj1, obj2) \ - do { \ - if(self->ob_type != other->ob_type) { \ - PyErr_SetString(PyExc_TypeError, "other must be of the same type"); \ - return NULL; \ - } \ - } while(0); - -static PyObject *msg_clear(PyObject *self, PyObject *args) -{ - (void)args; - PyUpb_PbMsg *msg = Check_Message(self); - upb_mm_msgclear_all(&msg->ref); - Py_RETURN_NONE; -} - -//static PyObject *msg_encode(PyObject *self, PyObject *args) -//{ -// (void)args; -// PyUpb_PbMsg *msg = Check_Message(self); -// struct upb_msgsizes *sizes = upb_msgsizes_new(); -// struct upb_msg *upb_msg = msg->ref.p.msg; -// upb_msgsizes_read(sizes, upb_msg); -// -// size_t size = upb_msgsizes_totalsize(sizes); -// PyObject *str = PyString_FromStringAndSize(NULL, size); -// if(!str) return NULL; -// char *strbuf = PyString_AS_STRING(str); -// -// bool success = upb_msg_serialize_all(upb_msg, sizes, strbuf); -// upb_msgsizes_free(sizes); -// if(success) { -// return str; -// } else { -// /* TODO: better error than TypeError. */ -// PyErr_SetString(PyExc_TypeError, "Error serializing protobuf."); -// return NULL; -// } -//} - -static PyObject *msg_equals(PyObject *self, PyObject *other) -{ - PyUpb_PbMsg *msg1 = Check_Message(self); - PyUpb_PbMsg *msg2 = Check_Message(other); - Check_SameProtoType(msg1, msg2); - RETURN_BOOL(upb_msg_eql(msg1->ref.p.msg, msg2->ref.p.msg, true)) -} - -static PyObject *msg_isinitialized(PyObject *self, PyObject *args) -{ - (void)args; - PyUpb_PbMsg *msg = Check_Message(self); - RETURN_BOOL(upb_msg_all_required_fields_set(msg->ref.p.msg)) -} - -static PyObject *msg_parsefromstring(PyObject *self, PyObject *args) -{ - PyUpb_PbMsg *msg = Check_Message(self); - char *strdata; - size_t strlen; - if(!PyArg_ParseTuple(args, BYTES_FORMAT, &strdata, &strlen)) - return NULL; - - if(upb_msg_parsestr(msg->ref.p.msg, strdata, strlen) != UPB_STATUS_OK) { - /* TODO: better error than TypeError. */ - PyErr_SetString(PyExc_TypeError, "error parsing protobuf"); - return NULL; - } - Py_RETURN_NONE; -} - -static PyObject *msg_mergefromstring(PyObject *self, PyObject *args) -{ - PyUpb_PbMsg *msg = Check_Message(self); - char *strdata; - size_t strlen; - if(!PyArg_ParseTuple(args, BYTES_FORMAT, &strdata, &strlen)) - return NULL; - - if(upb_msg_parsestr(msg->ref.p.msg, strdata, strlen) != UPB_STATUS_OK) { - /* TODO: better error than TypeError. */ - PyErr_SetString(PyExc_TypeError, "error parsing protobuf"); - return NULL; - } - Py_RETURN_NONE; -} - -/* Commented-out methods are TODO. */ -static PyMethodDef msg_methods[] = { - {"Clear", msg_clear, METH_NOARGS, - "Erases all data from the ProtocolMessage, reseting fields to their defaults" - }, - //{"CopyFrom", msg_copyfrom, METH_O, - // "Copies data from another ProtocolMessage." - //}, - //{"Encode", msg_encode, METH_NOARGS, - // "Returns a string representing the ProtocolMessage." - //}, - {"Equals", msg_equals, METH_O, - "Returns true if the given ProtocolMessage has the same type and value." - }, - {"IsInitialized", msg_isinitialized, METH_NOARGS, - "Returns true iff all required fields have been set." - }, - //{"Merge", msg_merge, METH_O, - // "Merges data from the given Decoder." - //}, - //{"MergeFrom", msg_mergefrom, METH_O, - // "Merges data from another ProtocolMessage of the same type." - //}, - {"MergeFromString", msg_mergefromstring, METH_VARARGS, - "Merges data from the given string. Raises an exception if this does not " - "result in the ProtocolMessage being initialized." - }, - //{"Output", msg_output, METH_O, - // "Writes the ProtocolMessage to the given encoder." - //}, - //{"OutputUnchecked", msg_output, METH_O, - // "Writes the ProtocolMessage to the given encoder, without checking " - // "initialization" - //}, - //{"Parse", msg_parse, METH_O, - // "Parses data from the given Decoder." - //}, - //{"ParseASCII", msg_parseascii, METH_VARARGS, - // "Parses a string generated by ToASCII. Raises a ValueError if unknown " - // "fields are encountered." - //}, - //{"ParseASCIIIgnoreUnknown", msg_parseascii, METH_VARARGS, - // "Parses a string generated by ToASCII. Ignores unknown fields." - //}, - {"ParseFromString", msg_parsefromstring, METH_VARARGS, - "Parses data from the given string. Raises an exception if this does not " - "result in the ProtocolMessage being initialized." - }, - //{"ToASCII", msg_toascii, METH_NOARGS, - // "Returns the ProtocolMessage as a human-readable ASCII string." - //}, - //{"ToCompactASCII", msg_tocompactascii, METH_NOARGS, - // "Returns the ProtocolMessage as a human-readable ASCII string that uses " - // "tag numbers instead of field names." - //}, - //{"ToShortASCII", msg_toshortascii, METH_NOARGS, - // "Returns the ProtocolMessage as a human-readable ASCII string, all on one - // "line." - //}, - //{"TryMerge", msg_trymerge, METH_O, - // "Merges data from the given decoder. - //} - {NULL, NULL} -}; - -static bool starts_with(struct upb_string *str, struct upb_string *prefix, - struct upb_string *out_str) -{ - if(str->byte_len < prefix->byte_len) return false; - if(memcmp(str->ptr, prefix->ptr, prefix->byte_len) == 0) { - out_str->ptr = str->ptr + prefix->byte_len; - out_str->byte_len = str->byte_len - prefix->byte_len; - return true; - } else { - return false; - } -} - -static bool ends_with(struct upb_string *str, struct upb_string *suffix, - struct upb_string *out_str) -{ - if(str->byte_len < suffix->byte_len) return false; - if(memcmp(str->ptr + str->byte_len - suffix->byte_len, suffix->ptr, suffix->byte_len) == 0) { - out_str->ptr = str->ptr; - out_str->byte_len = str->byte_len - suffix->byte_len; - return true; - } else { - return false; - } -} - -PyObject *PyUpb_NewPbBoundFieldOp(PyUpb_PbMsg *msgobj, struct upb_fielddef *f, - PyUpb_PbBoundFieldOpCode code) -{ - /* Type check that this operation on a field of this type makes sense. */ - if(upb_isarray(f)) { - switch(code) { - case OP_HAS: - case OP_SET: - case OP_MUTABLE: - return NULL; - default: break; - } - } else { - if(upb_issubmsg(f)) { - switch(code) { - case OP_SET: - case OP_SIZE: - case OP_LIST: - case OP_ADD: - return NULL; - default: break; - } - } else { - switch(code) { - case OP_MUTABLE: - case OP_SIZE: - case OP_LIST: - case OP_ADD: - return NULL; - default: break; - } - } - } - - PyUpb_PbBoundFieldOp *op = - (void*)PyUpb_PbBoundFieldOpType.tp_alloc(&PyUpb_PbBoundFieldOpType, 0); - op->msg = msgobj; - op->f = f; - op->code = code; - Py_INCREF(op->msg); - return (PyObject*)op; -} - -PyObject* msg_getattro(PyObject *obj, PyObject *attr_name) -{ - /* Each protobuf field results in a set of four methods for a scalar or five - * methods for an array. To avoid putting 4f entries in our type dict, we - * dynamically scan the method to see if it is of these forms, and if so, - * look it up in the hash table that upb already keeps. - * - * If these repeated comparisons showed up as being a hot spot in a profile, - * there are several ways this dispatch could be optimized. */ - static struct upb_string set = {.ptr = "set_", .byte_len = 4}; - static struct upb_string has = {.ptr = "has_", .byte_len = 4}; - static struct upb_string clear = {.ptr = "clear_", .byte_len = 6}; - static struct upb_string size = {.ptr = "_size", .byte_len = 5}; - static struct upb_string mutable = {.ptr = "mutable_", .byte_len = 8}; - static struct upb_string add = {.ptr = "add_", .byte_len = 4}; - static struct upb_string list = {.ptr = "_list", .byte_len = 5}; - - struct upb_string str; - Py_ssize_t len; - PyString_AsStringAndSize(attr_name, &str.ptr, &len); - if(len > UINT32_MAX) { - PyErr_SetString(PyExc_TypeError, - "Wow, that's a long attribute name you've got there."); - return NULL; - } - str.byte_len = (uint32_t)len; - PyUpb_PbMsg *msgobj = Check_Message(obj); - struct upb_msgdef *def = msgobj->ref.p.msg->def; - - /* This can be a field reference iff the first letter is lowercase, because - * generic methods (eg. IsInitialized()) all start with uppercase. */ - if(islower(str.ptr[0])) { - PyUpb_PbBoundFieldOpCode opcode; - struct upb_string field_name; - if(starts_with(&str, &has, &field_name)) - opcode = OP_HAS; - else if(starts_with(&str, &set, &field_name)) - opcode = OP_SET; - else if(starts_with(&str, &mutable, &field_name)) - opcode = OP_MUTABLE; - else if(ends_with(&str, &size, &field_name)) - opcode = OP_SIZE; - else if(ends_with(&str, &list, &field_name)) - opcode = OP_LIST; - else if(starts_with(&str, &add, &field_name)) - opcode = OP_ADD; - else if(starts_with(&str, &clear, &field_name)) - opcode = OP_CLEAR; - else { - /* Could be a plain field reference (eg. obj.field(i)). */ - opcode = OP_GET; - field_name = str; - } - struct upb_fielddef *f = upb_msg_fieldbyname(def, &field_name); - if(f) { - PyObject *op = PyUpb_NewPbBoundFieldOp(msgobj, f, opcode); - if(op) return op; - } - } - - /* Fall back on regular attribute lookup. */ - return PyObject_GenericGetAttr(obj, attr_name); -} - -static void msg_dealloc(PyObject *obj) -{ - PyUpb_PbMsg *msg = (void*)obj; - upb_mm_release(&msg->ref); - Py_DECREF(msg->def); - obj->ob_type->tp_free(obj); -} - -static PyTypeObject PyUpb_PbMsgType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "upb.pb.Message", /* tp_name */ - sizeof(PyUpb_PbMsg), /* tp_basicsize */ - 0, /* tp_itemsize */ - msg_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr (TODO) */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - msg_getattro, /* tp_getattro */ - 0, /* Not allowed. */ /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse (TODO) */ - 0, /* tp_clear (TODO) */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - msg_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* Can't be created from Python. */ /* tp_new */ - 0, /* tp_free */ -}; - -/* upb.pb.MessageCreator ******************************************************/ - -static PyObject *creator_call(PyObject *callable, PyObject *args, PyObject *kw) -{ - PyUpb_PbMsgCreator *creator = Check_MsgCreator(callable); - return MMREF_TO_PYOBJ(upb_mm_newmsg_ref(creator->def->def, &pymm)); -} - -static PyObject *creator_repr(PyObject *obj) -{ - PyUpb_PbMsgCreator *creator = Check_MsgCreator(obj); - struct upb_string *name = creator->def->def->descriptor->name; - /* Need to get a NULL-terminated copy of name since PyString_FromFormat - * doesn't support ptr+len. */ - PyObject *nameobj = PyString_FromStringAndSize(name->ptr, name->byte_len); - PyObject *ret = PyString_FromFormat("", - PyString_AS_STRING(nameobj)); - Py_DECREF(nameobj); - return ret; -} - -static void creator_dealloc(PyObject *obj) -{ - PyUpb_PbMsgCreator *creator = (void*)obj; - Py_DECREF(creator->def); - obj->ob_type->tp_free(obj); -} - -static PyObject *creator_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyUpb_PbMsgCreator *creator = (void*)type->tp_alloc(type, 0); - PyUpb_MsgDef *def; - if(!PyArg_ParseTuple(args, "O!", &PyUpb_MsgDefType, &def)) return NULL; - creator->def = def; - Py_INCREF(creator->def); - return (PyObject*)creator; -} - -static PyTypeObject PyUpb_PbMsgCreatorType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "upb.pb.MessageCreator", /* tp_name */ - sizeof(PyUpb_PbMsgCreator), /* tp_basicsize */ - 0, /* tp_itemsize */ - creator_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - creator_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - creator_call, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - creator_new, /* tp_new */ - 0, /* tp_free */ -}; - -/* upb.pb module **************************************************************/ - -static PyMethodDef methods[] = { - {NULL, NULL} -}; - -PyMODINIT_FUNC -initpb(void) -{ - if(PyType_Ready(&PyUpb_PbBoundFieldOpType) < 0) return; - if(PyType_Ready(&PyUpb_PbMsgType) < 0) return; - if(PyType_Ready(&PyUpb_PbMsgCreatorType) < 0) return; - - /* PyModule_AddObject steals a reference. These objects are statically - * allocated and must not be deleted, so we increment their refcount. */ - Py_INCREF(&PyUpb_PbBoundFieldOpType); - Py_INCREF(&PyUpb_PbMsgType); - Py_INCREF(&PyUpb_PbMsgCreatorType); - - PyObject *mod = Py_InitModule("upb.cext.pb", methods); - PyModule_AddObject(mod, "BoundFieldOp", (PyObject*)&PyUpb_PbBoundFieldOpType); - PyModule_AddObject(mod, "Message", (PyObject*)&PyUpb_PbMsgType); - PyModule_AddObject(mod, "MessageCreator", (PyObject*)&PyUpb_PbMsgCreatorType); -} diff --git a/lang_ext/python/setup.py b/lang_ext/python/setup.py index ec34e7d..8abaff8 100644 --- a/lang_ext/python/setup.py +++ b/lang_ext/python/setup.py @@ -3,12 +3,10 @@ from distutils.core import setup, Extension setup(name='upb', version='0.1', ext_modules=[ - Extension('upb.cext', ['definition.c', 'pb.c', 'cext.c'], - include_dirs=['../../src', '../../descriptor'], - define_macros=[("UPB_USE_PTHREADS", 1), - ("UPB_UNALIGNED_READS_OK", 1), - ("UPB_THREAD_UNSAFE", 1)], - library_dirs=['../../src'], + Extension('upb.__init__', ['upb.c'], + include_dirs=['../../'], + define_macros=[("UPB_UNALIGNED_READS_OK", 1)], + library_dirs=['../../upb'], libraries=['upb_pic'], ), ], diff --git a/lang_ext/python/test.py b/lang_ext/python/test.py new file mode 100644 index 0000000..df152da --- /dev/null +++ b/lang_ext/python/test.py @@ -0,0 +1,14 @@ + +import upb +import unittest + +class TestFieldDef(unittest.TestCase): + def test_construction(self): + fielddef1 = upb.FieldDef() + self.assertTrue(fielddef1 is not None) + fielddef2 = upb.FieldDef(number=1, name="field1", label=2, type=3) + self.assertTrue(fielddef2 is not None) + self.assertTrue(id(fielddef1) != id(fielddef2)) + +if __name__ == '__main__': + unittest.main() diff --git a/lang_ext/python/upb.c b/lang_ext/python/upb.c new file mode 100644 index 0000000..9a48401 --- /dev/null +++ b/lang_ext/python/upb.c @@ -0,0 +1,190 @@ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2009 Google Inc. See LICENSE for details. + * Author: Josh Haberman + * + * Python extension exposing the core of upb: definitions, handlers, + * and a message type. + */ + +#include +#include "upb/def.h" + +static bool streql(const char *a, const char *b) { return strcmp(a, b) == 0; } + +PyObject *PyUpb_Error(const char *str) { + PyErr_SetString(PyExc_TypeError, str); + return NULL; +} + +int PyUpb_ErrorInt(const char *str) { + PyErr_SetString(PyExc_TypeError, str); + return -1; +} + +/* PyUpb_Def ******************************************************************/ + +// All the def types share the same C layout, even though they are different +// Python types. For the moment we don't bother trying to make them an actual +// inheritance hierarchy. + +typedef struct { + PyObject_HEAD; + upb_def *def; +} PyUpb_Def; + + +/* PyUpb_FieldDef *************************************************************/ + +typedef struct { + PyObject_HEAD; + upb_fielddef *field; +} PyUpb_FieldDef; + +static PyTypeObject PyUpb_FieldDefType; + +#define Check_FieldDef(obj, badret) \ + (void*)obj; do { \ + if(!PyObject_TypeCheck(obj, &PyUpb_FieldDefType)) { \ + PyErr_SetString(PyExc_TypeError, "must be a upb.FieldDef"); \ + return badret; \ + } \ + } while(0) + +static void PyUpb_FieldDef_dealloc(PyObject *obj) { + PyUpb_FieldDef *f = (void*)obj; + upb_fielddef_unref(f->field); + obj->ob_type->tp_free(obj); +} + +static PyObject* PyUpb_FieldDef_getattro(PyObject *obj, PyObject *attr_name) { + PyUpb_FieldDef *f = Check_FieldDef(obj, NULL); + const char *name = PyString_AsString(attr_name); + if (streql(name, "name")) { + return PyString_FromString(upb_fielddef_name(f->field)); + } else if (streql(name, "number")) { + return PyInt_FromLong(upb_fielddef_number(f->field)); + } else if (streql(name, "type")) { + return PyInt_FromLong(upb_fielddef_type(f->field)); + } else if (streql(name, "label")) { + return PyInt_FromLong(upb_fielddef_label(f->field)); + } else if (streql(name, "subdef")) { + // NYI; + return NULL; + } else if (streql(name, "msgdef")) { + // NYI; + return NULL; + } else { + return PyUpb_Error("Invalid fielddef member."); + } +} + +static int PyUpb_FieldDef_setattro(PyObject *o, PyObject *key, PyObject *val) { + PyUpb_FieldDef *f = Check_FieldDef(o, -1); + const char *field = PyString_AsString(key); + if (!upb_fielddef_ismutable(f->field)) + return PyUpb_ErrorInt("fielddef is not mutable."); + if (streql(field, "name")) { + const char *name = PyString_AsString(val); + if (!name || !upb_fielddef_setname(f->field, name)) + return PyUpb_ErrorInt("Invalid name"); + } else if (streql(field, "number")) { + // TODO: should check truncation. Non-security issue. + // Non-int will return -1, which is already invalid as a field number. + if (!upb_fielddef_setnumber(f->field, PyInt_AsLong(val))) + return PyUpb_ErrorInt("Invalid number"); + } else if (streql(field, "type")) { + // TODO: should check truncation. Non-security issue. + if (!upb_fielddef_settype(f->field, PyInt_AsLong(val))) + return PyUpb_ErrorInt("Invalid type"); + } else if (streql(field, "label")) { + // TODO: should check truncation. Non-security issue. + if (!upb_fielddef_setlabel(f->field, PyInt_AsLong(val))) + return PyUpb_ErrorInt("Invalid label"); + } else if (streql(field, "type_name")) { + const char *name = PyString_AsString(val); + if (!name || !upb_fielddef_settypename(f->field, name)) + return PyUpb_ErrorInt("Invalid type_name"); + } else if (streql(field, "default_value")) { + // NYI + return -1; + } else { + return PyUpb_ErrorInt("Invalid fielddef member."); + } + return 0; +} + +static int PyUpb_FieldDef_init(PyObject *self, PyObject *args, PyObject *kwds) { + if (!kwds) return 0; + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(kwds, &pos, &key, &value)) + PyUpb_FieldDef_setattro(self, key, value); + return 0; +} + +static PyObject *PyUpb_FieldDef_new(PyTypeObject *subtype, + PyObject *args, PyObject *kwds) { + PyUpb_FieldDef *f = (PyUpb_FieldDef*)subtype->tp_alloc(subtype, 0); + f->field = upb_fielddef_new(); + return (PyObject*)f; +} + +static PyTypeObject PyUpb_FieldDefType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "upb.FieldDef", /* tp_name */ + sizeof(PyUpb_FieldDef), /* tp_basicsize */ + 0, /* tp_itemsize */ + &PyUpb_FieldDef_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* TODO */ /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + &PyUpb_FieldDef_getattro, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + &PyUpb_FieldDef_init, /* tp_init */ + 0, /* tp_alloc */ + &PyUpb_FieldDef_new, /* tp_new */ + 0, /* tp_free */ +}; + +static PyMethodDef methods[] = { + {NULL, NULL} +}; + +PyMODINIT_FUNC initupb(void) { + PyObject *mod = Py_InitModule("upb", methods); + if (PyType_Ready(&PyUpb_FieldDefType) < 0) return; + + // PyModule_AddObject steals a ref, but our object is statically allocated + // and must not be deleted. + Py_INCREF(&PyUpb_FieldDefType); + + PyModule_AddObject(mod, "FieldDef", (PyObject*)&PyUpb_FieldDefType); +} diff --git a/lang_ext/python/upb/definition.py b/lang_ext/python/upb/definition.py deleted file mode 100644 index f03a2dc..0000000 --- a/lang_ext/python/upb/definition.py +++ /dev/null @@ -1 +0,0 @@ -from upb.cext.definition import * diff --git a/lang_ext/python/upb/pb.py b/lang_ext/python/upb/pb.py deleted file mode 100644 index 6b1654a..0000000 --- a/lang_ext/python/upb/pb.py +++ /dev/null @@ -1 +0,0 @@ -from upb.cext.pb import * diff --git a/upb/def.h b/upb/def.h index 8800712..9571773 100644 --- a/upb/def.h +++ b/upb/def.h @@ -95,7 +95,7 @@ typedef struct _upb_fielddef { struct _upb_accessor_vtbl *accessor; } upb_fielddef; -upb_fielddef *upb_fielddef_new(); +upb_fielddef *upb_fielddef_new(void); void upb_fielddef_ref(upb_fielddef *f); void upb_fielddef_unref(upb_fielddef *f); upb_fielddef *upb_fielddef_dup(upb_fielddef *f); @@ -186,7 +186,7 @@ typedef struct { upb_fielddef *f; } upb_ntof_ent; -upb_msgdef *upb_msgdef_new(); +upb_msgdef *upb_msgdef_new(void); INLINE void upb_msgdef_unref(upb_msgdef *md) { upb_def_unref(UPB_UPCAST(md)); } INLINE void upb_msgdef_ref(upb_msgdef *md) { upb_def_ref(UPB_UPCAST(md)); } @@ -284,7 +284,7 @@ typedef struct { char *str; } upb_iton_ent; -upb_enumdef *upb_enumdef_new(); +upb_enumdef *upb_enumdef_new(void); INLINE void upb_enumdef_ref(upb_enumdef *e) { upb_def_ref(UPB_UPCAST(e)); } INLINE void upb_enumdef_unref(upb_enumdef *e) { upb_def_unref(UPB_UPCAST(e)); } upb_enumdef *upb_enumdef_dup(upb_enumdef *e); -- cgit v1.2.3