diff options
author | Bram Moolenaar <Bram@vim.org> | 2013-05-30 13:01:18 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2013-05-30 13:01:18 +0200 |
commit | a9922d62e60142f1cb9889626e82e8cc7126be1a (patch) | |
tree | 6d2281fef42af5d2e2351d8eb8166445478930c4 | |
parent | a5b725c3f67f1c98d99fec71a3cbaad502a02291 (diff) |
updated for version 7.3.1061v7.3.1061
Problem: Python: Dictionary is not standard.
Solution: Python patch 20: Add standard methods and fields. (ZyX)
-rw-r--r-- | runtime/doc/if_pyth.txt | 134 | ||||
-rw-r--r-- | src/eval.c | 106 | ||||
-rw-r--r-- | src/if_py_both.h | 532 | ||||
-rw-r--r-- | src/if_python.c | 14 | ||||
-rw-r--r-- | src/if_python3.c | 7 | ||||
-rw-r--r-- | src/proto/eval.pro | 1 | ||||
-rw-r--r-- | src/testdir/test86.in | 56 | ||||
-rw-r--r-- | src/testdir/test86.ok | 28 | ||||
-rw-r--r-- | src/testdir/test87.in | 93 | ||||
-rw-r--r-- | src/testdir/test87.ok | 28 | ||||
-rw-r--r-- | src/version.c | 2 |
11 files changed, 821 insertions, 180 deletions
diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt index e9f3395793..5a0418e8b1 100644 --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -12,9 +12,10 @@ The Python Interface to Vim *python* *Python* 4. Range objects |python-range| 5. Window objects |python-window| 6. Tab page objects |python-tabpage| -7. pyeval(), py3eval() Vim functions |python-pyeval| -8. Dynamic loading |python-dynamic| -9. Python 3 |python3| +7. vim.bindeval objects |python-bindeval-objects| +8. pyeval(), py3eval() Vim functions |python-pyeval| +9. Dynamic loading |python-dynamic| +10. Python 3 |python3| {Vi does not have any of these commands} @@ -171,47 +172,9 @@ vim.eval(str) *python-eval* 'eval_expr', 'kind': 'f', 'filename': './src/eval.c'}] vim.bindeval(str) *python-bindeval* - Like |python-eval|, but - 1. if expression evaluates to |List| or |Dictionary| it is returned as - vimlist or vimdictionary python type that are connected to original - list or dictionary. Thus modifications to these objects imply - modifications of the original. - - Additionally, vim.List and vim.Dictionary type have read-write - `.locked` attribute that returns - Value Meaning ~ - zero Variable is not locked - vim.VAR_LOCKED Variable is locked, but can be unlocked - vim.VAR_FIXED Variable is locked and can't be unlocked - integer constants. If variable is not fixed, you can do - `var.locked=True` to lock it and `var.locked=False` to unlock. - There is no recursive locking like |:lockvar|! does. There is also - no way to lock a specific key or check whether it is locked (in any - case these locks are ignored by anything except |:let|: |extend()| - does not care, neither does python interface). - - vim.Dictionary type also supports `.scope` attribute which is one - of - Value Meaning ~ - zero Dictionary is not a scope one - vim.VAR_DEF_SCOPE Function-local or global scope dictionary - vim.VAR_SCOPE Other scope dictionary - - 2. if expression evaluates to a function reference, then it returns - callable vim.Function object. Use self keyword argument to assign - |self| object for dictionary functions. - - Note: this function has the same behavior as |lua-eval| (except that - lua does not support running vim functions), |python-eval| is - kept for backwards compatibility in order not to make scripts - relying on outputs of vim.eval() being a copy of original or - vim.eval("1") returning a string. - - You can use "List", "Dictionary" and "Function" vim module attributes - to test whether object has given type. These types are currently not - subclassable, neither they contain constructors, so you can use them - only for checks like `isinstance(obj, vim.List)`. - + Like |python-eval|, but returns special objects described in + |python-bindeval-objects|. These python objects let you modify (|List| + or |Dictionary|) or call (|Funcref|) vim objecs. Error object of the "vim" module @@ -497,13 +460,90 @@ Tab page attributes are: TabPage object type is available using "TabPage" attribute of vim module. ============================================================================== -7. pyeval() and py3eval() Vim functions *python-pyeval* +7. vim.bindeval objects *python-bindeval-objects* + +vim.Dictionary object *python-Dictionary* + Dictionary-like object providing access to vim |Dictionary| type. + Attributes: + Attribute Description ~ + locked One of *python-.locked* + Value Description ~ + zero Variable is not locked + vim.VAR_LOCKED Variable is locked, but can be unlocked + vim.VAR_FIXED Variable is locked and can't be unlocked + Read-write. You can unlock locked variable by assigning + `True` or `False` to this attribute. No recursive locking + is supported. + scope One of + Value Description ~ + zero Dictionary is not a scope one + vim.VAR_DEF_SCOPE |g:| or |l:| dictionary + vim.VAR_SCOPE Other scope dictionary, + see |internal-variables| + Methods: + Method Description ~ + keys() Returns a list with dictionary keys. + values() Returns a list with dictionary values. + items() Returns a list of 2-tuples with dictionary contents. + update(iterable) + update(dictionary) + update(**kwargs) + Adds keys to dictionary. + Examples: > + py d = vim.bindeval('{}') + d['a'] = 'b' # Item assignment + print d['a'] # getting item + d.update({'c': 'd'}) # .update(dictionary) + d.update(e='f') # .update(**kwargs) + d.update((('g', 'h'), ('i', 'j'))) # .update(iterable) + for key in d.keys(): # .keys() + for val in d.values(): # .values() + for key, val in d.items(): # .items() + print isinstance(d, vim.Dictionary) # True + for key in d: # Iteration over keys +< + Note: when iterating over keys you should not modify dictionary. + +vim.List object *python-List* + Sequence-like object providing access to vim |List| type. + Supports `.locked` attribute, see |python-.locked|. Also supports the + following methods: + Method Description ~ + extend(item) Add items to the list. + Examples: > + l = vim.bindeval('[]') + l.extend(['abc', 'def']) # .extend() method + print l[1:] # slicing + l[:0] = ['ghi', 'jkl'] # slice assignment + print l[0] # getting item + l[0] = 'mno' # assignment + for i in l: # iteration + print isinstance(l, vim.List) # True + +vim.Function object *python-Function* + Function-like object, acting like vim |Funcref| object. Supports `.name` + attribute and is callable. Accepts special keyword argument `self`, see + |Dictionary-function|. + Examples: > + f = vim.bindeval('function("tr")') + print f('abc', 'a', 'b') # Calls tr('abc', 'a', 'b') + vim.command(''' + function DictFun() dict + return self + endfunction + ''') + f = vim.bindeval('function("DictFun")') + print f(self={}) # Like call('DictFun', [], {}) + print isinstance(f, vim.Function) # True + +============================================================================== +8. pyeval() and py3eval() Vim functions *python-pyeval* To facilitate bi-directional interface, you can use |pyeval()| and |py3eval()| functions to evaluate Python expressions and pass their values to VimL. ============================================================================== -8. Dynamic loading *python-dynamic* +9. Dynamic loading *python-dynamic* On MS-Windows the Python library can be loaded dynamically. The |:version| output then includes |+python/dyn|. @@ -520,7 +560,7 @@ Currently the name is "python24.dll". That is for Python 2.4. To know for sure edit "gvim.exe" and search for "python\d*.dll\c". ============================================================================== -9. Python 3 *python3* +10. Python 3 *python3* *:py3* *:python3* The `:py3` and `:python3` commands work similar to `:python`. A simple check diff --git a/src/eval.c b/src/eval.c index 8db2a155a2..25785b7776 100644 --- a/src/eval.c +++ b/src/eval.c @@ -10157,6 +10157,62 @@ f_expand(argvars, rettv) } /* + * Go over all entries in "d2" and add them to "d1". + * When "action" is "error" then a duplicate key is an error. + * When "action" is "force" then a duplicate key is overwritten. + * Otherwise duplicate keys are ignored ("action" is "keep"). + */ + void +dict_extend(d1, d2, action) + dict_T *d1; + dict_T *d2; + char_u *action; +{ + dictitem_T *di1; + hashitem_T *hi2; + int todo; + + todo = (int)d2->dv_hashtab.ht_used; + for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) + { + if (!HASHITEM_EMPTY(hi2)) + { + --todo; + di1 = dict_find(d1, hi2->hi_key, -1); + if (d1->dv_scope != 0) + { + /* Disallow replacing a builtin function in l: and g:. + * Check the key to be valid when adding to any + * scope. */ + if (d1->dv_scope == VAR_DEF_SCOPE + && HI2DI(hi2)->di_tv.v_type == VAR_FUNC + && var_check_func_name(hi2->hi_key, + di1 == NULL)) + break; + if (!valid_varname(hi2->hi_key)) + break; + } + if (di1 == NULL) + { + di1 = dictitem_copy(HI2DI(hi2)); + if (di1 != NULL && dict_add(d1, di1) == FAIL) + dictitem_free(di1); + } + else if (*action == 'e') + { + EMSG2(_("E737: Key already exists: %s"), hi2->hi_key); + break; + } + else if (*action == 'f' && HI2DI(hi2) != di1) + { + clear_tv(&di1->di_tv); + copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); + } + } + } +} + +/* * "extend(list, list [, idx])" function * "extend(dict, dict [, action])" function */ @@ -10206,12 +10262,9 @@ f_extend(argvars, rettv) } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { - dict_T *d1, *d2; - dictitem_T *di1; - char_u *action; - int i; - hashitem_T *hi2; - int todo; + dict_T *d1, *d2; + char_u *action; + int i; d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; @@ -10238,46 +10291,7 @@ f_extend(argvars, rettv) else action = (char_u *)"force"; - /* Go over all entries in the second dict and add them to the - * first dict. */ - todo = (int)d2->dv_hashtab.ht_used; - for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) - { - if (!HASHITEM_EMPTY(hi2)) - { - --todo; - di1 = dict_find(d1, hi2->hi_key, -1); - if (d1->dv_scope != 0) - { - /* Disallow replacing a builtin function in l: and g:. - * Check the key to be valid when adding to any - * scope. */ - if (d1->dv_scope == VAR_DEF_SCOPE - && HI2DI(hi2)->di_tv.v_type == VAR_FUNC - && var_check_func_name(hi2->hi_key, - di1 == NULL)) - break; - if (!valid_varname(hi2->hi_key)) - break; - } - if (di1 == NULL) - { - di1 = dictitem_copy(HI2DI(hi2)); - if (di1 != NULL && dict_add(d1, di1) == FAIL) - dictitem_free(di1); - } - else if (*action == 'e') - { - EMSG2(_("E737: Key already exists: %s"), hi2->hi_key); - break; - } - else if (*action == 'f' && HI2DI(hi2) != di1) - { - clear_tv(&di1->di_tv); - copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); - } - } - } + dict_extend(d1, d2, action); copy_tv(&argvars[0], rettv); } diff --git a/src/if_py_both.h b/src/if_py_both.h index edae12bef0..e669d84099 100644 --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -31,7 +31,7 @@ typedef int Py_ssize_t; /* Python 2.4 and earlier don't have this type. */ #define INVALID_TABPAGE_VALUE ((tabpage_T *)(-1)) #define DICTKEY_DECL \ - PyObject *dictkey_todecref; + PyObject *dictkey_todecref = NULL; #define DICTKEY_CHECK_EMPTY(err) \ if (*key == NUL) \ { \ @@ -63,6 +63,7 @@ typedef void (*runner)(const char *, void * static int ConvertFromPyObject(PyObject *, typval_T *); static int _ConvertFromPyObject(PyObject *, typval_T *, PyObject *); +static int ConvertFromPyMapping(PyObject *, typval_T *); static PyObject *WindowNew(win_T *, tabpage_T *); static PyObject *BufferNew (buf_T *); static PyObject *LineToString(const char *); @@ -877,12 +878,16 @@ typedef struct pylinkedlist_T ref; } DictionaryObject; +static PyObject *DictionaryUpdate(DictionaryObject *, PyObject *, PyObject *); + +#define NEW_DICTIONARY(dict) DictionaryNew(&DictionaryType, dict) + static PyObject * -DictionaryNew(dict_T *dict) +DictionaryNew(PyTypeObject *subtype, dict_T *dict) { DictionaryObject *self; - self = PyObject_NEW(DictionaryObject, &DictionaryType); + self = (DictionaryObject *) subtype->tp_alloc(subtype, 0); if (self == NULL) return NULL; self->dict = dict; @@ -893,6 +898,49 @@ DictionaryNew(dict_T *dict) return (PyObject *)(self); } + static dict_T * +py_dict_alloc() +{ + dict_T *r; + + if (!(r = dict_alloc())) + { + PyErr_NoMemory(); + return NULL; + } + ++r->dv_refcount; + + return r; +} + + static PyObject * +DictionaryConstructor(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) +{ + DictionaryObject *self; + dict_T *dict; + + if (!(dict = py_dict_alloc())) + return NULL; + + self = (DictionaryObject *) DictionaryNew(subtype, dict); + + --dict->dv_refcount; + + if (kwargs || PyTuple_Size(args)) + { + PyObject *tmp; + if (!(tmp = DictionaryUpdate(self, args, kwargs))) + { + Py_DECREF(self); + return NULL; + } + + Py_DECREF(tmp); + } + + return (PyObject *)(self); +} + static void DictionaryDestructor(DictionaryObject *self) { @@ -918,7 +966,8 @@ DictionarySetattr(DictionaryObject *self, char *name, PyObject *val) { if (val == NULL) { - PyErr_SetString(PyExc_AttributeError, _("Cannot delete DictionaryObject attributes")); + PyErr_SetString(PyExc_AttributeError, + _("cannot delete vim.Dictionary attributes")); return -1; } @@ -926,7 +975,7 @@ DictionarySetattr(DictionaryObject *self, char *name, PyObject *val) { if (self->dict->dv_lock == VAR_FIXED) { - PyErr_SetString(PyExc_TypeError, _("Cannot modify fixed dictionary")); + PyErr_SetString(PyExc_TypeError, _("cannot modify fixed dictionary")); return -1; } else @@ -943,7 +992,7 @@ DictionarySetattr(DictionaryObject *self, char *name, PyObject *val) } else { - PyErr_SetString(PyExc_AttributeError, _("Cannot set this attribute")); + PyErr_SetString(PyExc_AttributeError, _("cannot set this attribute")); return -1; } } @@ -954,26 +1003,170 @@ DictionaryLength(DictionaryObject *self) return ((PyInt) (self->dict->dv_hashtab.ht_used)); } +#define DICT_FLAG_HAS_DEFAULT 0x01 +#define DICT_FLAG_POP 0x02 +#define DICT_FLAG_NONE_DEFAULT 0x04 +#define DICT_FLAG_RETURN_BOOL 0x08 /* Incompatible with DICT_FLAG_POP */ +#define DICT_FLAG_RETURN_PAIR 0x10 + static PyObject * -DictionaryItem(DictionaryObject *self, PyObject *keyObject) +_DictionaryItem(DictionaryObject *self, PyObject *args, int flags) { + PyObject *keyObject; + PyObject *defObject = ((flags & DICT_FLAG_NONE_DEFAULT)? Py_None : NULL); + PyObject *r; char_u *key; dictitem_T *di; + dict_T *dict = self->dict; + hashitem_T *hi; + DICTKEY_DECL + if (flags & DICT_FLAG_HAS_DEFAULT) + { + if (!PyArg_ParseTuple(args, "O|O", &keyObject, &defObject)) + return NULL; + } + else + keyObject = args; + + if (flags & DICT_FLAG_RETURN_BOOL) + defObject = Py_False; + DICTKEY_GET(NULL, 0) - di = dict_find(self->dict, key, -1); + hi = hash_find(&dict->dv_hashtab, key); DICTKEY_UNREF - if (di == NULL) + if (HASHITEM_EMPTY(hi)) { - PyErr_SetObject(PyExc_KeyError, keyObject); + if (defObject) + { + Py_INCREF(defObject); + return defObject; + } + else + { + PyErr_SetObject(PyExc_KeyError, keyObject); + return NULL; + } + } + else if (flags & DICT_FLAG_RETURN_BOOL) + { + Py_INCREF(Py_True); + return Py_True; + } + + di = dict_lookup(hi); + + if (!(r = ConvertToPyObject(&di->di_tv))) return NULL; + + if (flags & DICT_FLAG_POP) + { + if (dict->dv_lock) + { + PyErr_SetVim(_("dict is locked")); + Py_DECREF(r); + return NULL; + } + + hash_remove(&dict->dv_hashtab, hi); + dictitem_free(di); } - return ConvertToPyObject(&di->di_tv); + if (flags & DICT_FLAG_RETURN_PAIR) + { + PyObject *tmp = r; + + if (!(r = Py_BuildValue("(" Py_bytes_fmt "O)", hi->hi_key, tmp))) + { + Py_DECREF(tmp); + return NULL; + } + } + + return r; +} + + static PyObject * +DictionaryItem(DictionaryObject *self, PyObject *keyObject) +{ + return _DictionaryItem(self, keyObject, 0); +} + + static int +DictionaryContains(DictionaryObject *self, PyObject *keyObject) +{ + PyObject *rObj = _DictionaryItem(self, keyObject, DICT_FLAG_RETURN_BOOL); + int r; + + r = (rObj == Py_True); + + Py_DECREF(Py_True); + + return r; +} + +typedef struct +{ + hashitem_T *ht_array; + long_u ht_used; + hashtab_T *ht; + hashitem_T *hi; + int todo; +} dictiterinfo_T; + + static PyObject * +DictionaryIterNext(dictiterinfo_T **dii) +{ + PyObject *r; + + if (!(*dii)->todo) + return NULL; + + if ((*dii)->ht->ht_array != (*dii)->ht_array || + (*dii)->ht->ht_used != (*dii)->ht_used) + { + PyErr_SetString(PyExc_RuntimeError, + _("hashtab changed during iteration")); + return NULL; + } + + while (((*dii)->todo) && HASHITEM_EMPTY((*dii)->hi)) + ++((*dii)->hi); + + --((*dii)->todo); + + if (!(r = PyBytes_FromString((char *) (*dii)->hi->hi_key))) + return NULL; + + return r; +} + + static PyObject * +DictionaryIter(DictionaryObject *self) +{ + dictiterinfo_T *dii; + hashtab_T *ht; + + if (!(dii = PyMem_New(dictiterinfo_T, 1))) + { + PyErr_NoMemory(); + return NULL; + } + + ht = &self->dict->dv_hashtab; + dii->ht_array = ht->ht_array; + dii->ht_used = ht->ht_used; + dii->ht = ht; + dii->hi = dii->ht_array; + dii->todo = dii->ht_used; + + return IterNew(dii, + (destructorfun) PyMem_Free, (nextfun) DictionaryIterNext, + NULL, NULL); } static PyInt @@ -1016,18 +1209,19 @@ DictionaryAssItem(DictionaryObject *self, PyObject *keyObject, PyObject *valObje if (di == NULL) { - di = dictitem_alloc(key); - if (di == NULL) + if (!(di = dictitem_alloc(key))) { PyErr_NoMemory(); return -1; } di->di_tv.v_lock = 0; + di->di_tv.v_type = VAR_UNKNOWN; if (dict_add(dict, di) == FAIL) { DICTKEY_UNREF vim_free(di); + dictitem_free(di); PyErr_SetVim(_("failed to add key to dictionary")); return -1; } @@ -1042,21 +1236,34 @@ DictionaryAssItem(DictionaryObject *self, PyObject *keyObject, PyObject *valObje return 0; } +typedef PyObject *(*hi_to_py)(hashitem_T *); + static PyObject * -DictionaryListKeys(DictionaryObject *self) +DictionaryListObjects(DictionaryObject *self, hi_to_py hiconvert) { dict_T *dict = self->dict; long_u todo = dict->dv_hashtab.ht_used; Py_ssize_t i = 0; PyObject *r; hashitem_T *hi; + PyObject *newObj; r = PyList_New(todo); for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { - PyList_SetItem(r, i, PyBytes_FromString((char *)(hi->hi_key))); + if (!(newObj = hiconvert(hi))) + { + Py_DECREF(r); + return NULL; + } + if (PyList_SetItem(r, i, newObj)) + { + Py_DECREF(r); + Py_DECREF(newObj); + return NULL; + } --todo; ++i; } @@ -1064,6 +1271,235 @@ DictionaryListKeys(DictionaryObject *self) return r; } + static PyObject * +dict_key(hashitem_T *hi) +{ + return PyBytes_FromString((char *)(hi->hi_key)); +} + + static PyObject * +DictionaryListKeys(DictionaryObject *self) +{ + return DictionaryListObjects(self, dict_key); +} + + static PyObject * +dict_val(hashitem_T *hi) +{ + dictitem_T *di; + + di = dict_lookup(hi); + return ConvertToPyObject(&di->di_tv); +} + + static PyObject * +DictionaryListValues(DictionaryObject *self) +{ + return DictionaryListObjects(self, dict_val); +} + + static PyObject * +dict_item(hashitem_T *hi) +{ + PyObject *keyObject; + PyObject *valObject; + PyObject *r; + + if (!(keyObject = dict_key(hi))) + return NULL; + + if (!(valObject = dict_val(hi))) + { + Py_DECREF(keyObject); + return NULL; + } + + r = Py_BuildValue("(OO)", keyObject, valObject); + + Py_DECREF(keyObject); + Py_DECREF(valObject); + + return r; +} + + static PyObject * +DictionaryListItems(DictionaryObject *self) +{ + return DictionaryListObjects(self, dict_item); +} + + static PyObject * +DictionaryUpdate(DictionaryObject *self, PyObject *args, PyObject *kwargs) +{ + dict_T *dict = self->dict; + + if (dict->dv_lock) + { + PyErr_SetVim(_("dict is locked")); + return NULL; + } + + if (kwargs) + { + typval_T tv; + + if (ConvertFromPyMapping(kwargs, &tv) == -1) + return NULL; + + VimTryStart(); + dict_extend(self->dict, tv.vval.v_dict, (char_u *) "force"); + clear_tv(&tv); + if (VimTryEnd()) + return NULL; + } + else + { + PyObject *object; + + if (!PyArg_Parse(args, "(O)", &object)) + return NULL; + + if (PyObject_HasAttrString(object, "keys")) + return DictionaryUpdate(self, NULL, object); + else + { + PyObject *iterator; + PyObject *item; + + if (!(iterator = PyObject_GetIter(object))) + return NULL; + + while ((item = PyIter_Next(iterator))) + { + PyObject *fast; + PyObject *keyObject; + PyObject *valObject; + PyObject *todecref; + char_u *key; + dictitem_T *di; + + if (!(fast = PySequence_Fast(item, ""))) + { + Py_DECREF(iterator); + Py_DECREF(item); + return NULL; + } + + Py_DECREF(item); + + if (PySequence_Fast_GET_SIZE(fast) != 2) + { + Py_DECREF(iterator); + Py_DECREF(fast); + PyErr_SetString(PyExc_ValueError, + _("expected sequence element of size 2")); + return NULL; + } + + keyObject = PySequence_Fast_GET_ITEM(fast, 0); + + if (!(key = StringToChars(keyObject, &todecref))) + { + Py_DECREF(iterator); + Py_DECREF(fast); + return NULL; + } + + di = dictitem_alloc(key); + + Py_XDECREF(todecref); + + if (di == NULL) + { + Py_DECREF(fast); + Py_DECREF(iterator); + PyErr_NoMemory(); + return NULL; + } + di->di_tv.v_lock = 0; + di->di_tv.v_type = VAR_UNKNOWN; + + valObject = PySequence_Fast_GET_ITEM(fast, 1); + + if (ConvertFromPyObject(valObject, &di->di_tv) == -1) + { + Py_DECREF(iterator); + Py_DECREF(fast); + dictitem_free(di); + return NULL; + } + + Py_DECREF(fast); + + if (dict_add(dict, di) == FAIL) + { + Py_DECREF(iterator); + dictitem_free(di); + PyErr_SetVim(_("failed to add key to dictionary")); + return NULL; + } + } + + Py_DECREF(iterator); + + /* Iterator may have finished due to an exception */ + if (PyErr_Occurred()) + return NULL; + } + } + Py_INCREF(Py_None); + return Py_None; +} + + static PyObject * +DictionaryGet(DictionaryObject *self, PyObject *args) +{ + return _DictionaryItem(self, args, + DICT_FLAG_HAS_DEFAULT|DICT_FLAG_NONE_DEFAULT); +} + + static PyObject * +DictionaryPop(DictionaryObject *self, PyObject *args) +{ + return _DictionaryItem(self, args, DICT_FLAG_HAS_DEFAULT|DICT_FLAG_POP); +} + + static PyObject * +DictionaryPopItem(DictionaryObject *self, PyObject *args) +{ + PyObject *keyObject; + + if (!PyArg_ParseTuple(args, "O", &keyObject)) + return NULL; + + return _DictionaryItem(self, keyObject, + DICT_FLAG_POP|DICT_FLAG_RETURN_PAIR); +} + + static PyObject * +DictionaryHasKey(DictionaryObject *self, PyObject *args) +{ + PyObject *keyObject; + + if (!PyArg_ParseTuple(args, "O", &keyObject)) + return NULL; + + return _DictionaryItem(self, keyObject, DICT_FLAG_RETURN_BOOL); +} + +static PySequenceMethods DictionaryAsSeq = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc) DictionaryContains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + static PyMappingMethods DictionaryAsMapping = { (lenfunc) DictionaryLength, (binaryfunc) DictionaryItem, @@ -1072,6 +1508,13 @@ static PyMappingMethods DictionaryAsMapping = { static struct PyMethodDef DictionaryMethods[] = { {"keys", (PyCFunction)DictionaryListKeys, METH_NOARGS, ""}, + {"values", (PyCFunction)DictionaryListValues, METH_NOARGS, ""}, + {"items", (PyCFunction)DictionaryListItems, METH_NOARGS, ""}, + {"update", (PyCFunction)DictionaryUpdate, METH_VARARGS|METH_KEYWORDS, ""}, + {"get", (PyCFunction)DictionaryGet, METH_VARARGS, ""}, + {"pop", (PyCFunction)DictionaryPop, METH_VARARGS, ""}, + {"popitem", (PyCFunction)DictionaryPopItem, METH_VARARGS, ""}, + {"has_key", (PyCFunction)DictionaryHasKey, METH_VARARGS, ""}, {"__dir__", (PyCFunction)DictionaryDir, METH_NOARGS, ""}, { NULL, NULL, 0, NULL} }; @@ -1541,14 +1984,7 @@ FunctionCall(FunctionObject *self, PyObject *argsObject, PyObject *kwargs) selfdictObject = PyDict_GetItemString(kwargs, "self"); if (selfdictObject != NULL) { - if (!PyMapping_Check(selfdictObject)) - { - PyErr_SetString(PyExc_TypeError, - _("'self' argument must be a dictionary")); - clear_tv(&args); - return NULL; - } - if (ConvertFromPyObject(selfdictObject, &selfdicttv) == -1) + if (ConvertFromPyMapping(selfdictObject, &selfdicttv) == -1) { clear_tv(&args); return NULL; @@ -1994,7 +2430,7 @@ TabPageAttr(TabPageObject *self, char *name) else if (strcmp(name, "number") == 0) return PyLong_FromLong((long) get_tab_number(self->tab)); else if (strcmp(name, "vars") == 0) - return DictionaryNew(self->tab->tp_vars); + return NEW_DICTIONARY(self->tab->tp_vars); else if (strcmp(name, "window") == 0) { /* For current tab window.c does not bother to set or update tp_curwin @@ -2225,7 +2661,7 @@ WindowAttr(WindowObject *self, char *name) return PyLong_FromLong((long)(W_WINCOL(self->win))); #endif else if (strcmp(name, "vars") == 0) - return DictionaryNew(self->win->w_vars); + return NEW_DICTIONARY(self->win->w_vars); else if (strcmp(name, "options") == 0) return OptionsNew(SREQ_WIN, self->win, (checkfun) CheckWindow, (PyObject *) self); @@ -3402,7 +3838,7 @@ BufferAttr(BufferObject *self, char *name) else if (strcmp(name, "number") == 0) return Py_BuildValue(Py_ssize_t_fmt, self->buf->b_fnum); else if (strcmp(name, "vars") == 0) - return DictionaryNew(self->buf->b_vars); + return NEW_DICTIONARY(self->buf->b_vars); else if (strcmp(name, "options") == 0) return OptionsNew(SREQ_BUF, self->buf, (checkfun) CheckBuffer, (PyObject *) self); @@ -4307,6 +4743,36 @@ convert_dl(PyObject *obj, typval_T *tv, } static int +ConvertFromPyMapping(PyObject *obj, typval_T *tv) +{ + PyObject *lookup_dict; + int r; + + if (!(lookup_dict = PyDict_New())) + return -1; + + if (PyType_IsSubtype(obj->ob_type, &DictionaryType)) + { + tv->v_type = VAR_DICT; + tv->vval.v_dict = (((DictionaryObject *)(obj))->dict); + ++tv->vval.v_dict->dv_refcount; + r = 0; + } + else if (PyDict_Check(obj)) + r = convert_dl(obj, tv, pydict_to_tv, lookup_dict); + else if (PyMapping_Check(obj)) + r = convert_dl(obj, tv, pymap_to_tv, lookup_dict); + else + { + PyErr_SetString(PyExc_TypeError, + _("unable to convert object to vim dictionary")); + r = -1; + } + Py_DECREF(lookup_dict); + return r; +} + + static int ConvertFromPyObject(PyObject *obj, typval_T *tv) { PyObject *lookup_dict; @@ -4322,7 +4788,7 @@ ConvertFromPyObject(PyObject *obj, typval_T *tv) static int _ConvertFromPyObject(PyObject *obj, typval_T *tv, PyObject *lookup_dict) { - if (obj->ob_type == &DictionaryType) + if (PyType_IsSubtype(obj->ob_type, &DictionaryType)) { tv->v_type = VAR_DICT; tv->vval.v_dict = (((DictionaryObject *)(obj))->dict); @@ -4437,7 +4903,7 @@ ConvertToPyObject(typval_T *tv) case VAR_LIST: return ListNew(tv->vval.v_list); case VAR_DICT: - return DictionaryNew(tv->vval.v_dict); + return NEW_DICTIONARY(tv->vval.v_dict); case VAR_FUNC: return FunctionNew(tv->vval.v_string == NULL ? (char_u *)"" : tv->vval.v_string); @@ -4608,10 +5074,14 @@ init_structs(void) DictionaryType.tp_name = "vim.dictionary"; DictionaryType.tp_basicsize = sizeof(DictionaryObject); DictionaryType.tp_dealloc = (destructor)DictionaryDestructor; + DictionaryType.tp_as_sequence = &DictionaryAsSeq; DictionaryType.tp_as_mapping = &DictionaryAsMapping; - DictionaryType.tp_flags = Py_TPFLAGS_DEFAULT; + DictionaryType.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE; DictionaryType.tp_doc = "dictionary pushing modifications to vim structure"; DictionaryType.tp_methods = DictionaryMethods; + DictionaryType.tp_iter = (getiterfunc)DictionaryIter; + DictionaryType.tp_new = (newfunc)DictionaryConstructor; + DictionaryType.tp_alloc = (allocfunc)PyType_GenericAlloc; #if PY_MAJOR_VERSION >= 3 DictionaryType.tp_getattro = (getattrofunc)DictionaryGetattro; DictionaryType.tp_setattro = (setattrofunc)DictionarySetattro; @@ -4786,8 +5256,8 @@ populate_module(PyObject *m, object_adder add_object) return -1; ADD_OBJECT(m, "error", VimError); - ADD_CHECKED_OBJECT(m, "vars", DictionaryNew(&globvardict)); - ADD_CHECKED_OBJECT(m, "vvars", DictionaryNew(&vimvardict)); + ADD_CHECKED_OBJECT(m, "vars", NEW_DICTIONARY(&globvardict)); + ADD_CHECKED_OBJECT(m, "vvars", NEW_DICTIONARY(&vimvardict)); ADD_CHECKED_OBJECT(m, "options", OptionsNew(SREQ_GLOBAL, NULL, dummy_check, NULL)); return 0; diff --git a/src/if_python.c b/src/if_python.c index 16874e8557..188e1a2c7a 100644 --- a/src/if_python.c +++ b/src/if_python.c @@ -103,6 +103,7 @@ struct PyMethodDef { Py_ssize_t a; }; # define PyIntIntObjArgProc intintobjargproc # define Py_ssize_t_fmt "i" #endif +#define Py_bytes_fmt "s" /* Parser flags */ #define single_input 256 @@ -187,6 +188,7 @@ struct PyMethodDef { Py_ssize_t a; }; # define PySequence_Check dll_PySequence_Check # define PySequence_Size dll_PySequence_Size # define PySequence_GetItem dll_PySequence_GetItem +# define PySequence_Fast dll_PySequence_Fast # define PyTuple_Size dll_PyTuple_Size # define PyTuple_GetItem dll_PyTuple_GetItem # define PyTuple_Type (*dll_PyTuple_Type) @@ -207,6 +209,7 @@ struct PyMethodDef { Py_ssize_t a; }; # define PyRun_SimpleString dll_PyRun_SimpleString # define PyRun_String dll_PyRun_String # define PyObject_GetAttrString dll_PyObject_GetAttrString +# define PyObject_HasAttrString dll_PyObject_HasAttrString # define PyObject_SetAttrString dll_PyObject_SetAttrString # define PyObject_CallFunctionObjArgs dll_PyObject_CallFunctionObjArgs # define PyString_AsString dll_PyString_AsString @@ -227,6 +230,7 @@ struct PyMethodDef { Py_ssize_t a; }; # define PySys_SetArgv dll_PySys_SetArgv # define PyType_Type (*dll_PyType_Type) # define PyType_Ready (*dll_PyType_Ready) +# define PyType_GenericAlloc dll_PyType_GenericAlloc # define Py_BuildValue dll_Py_BuildValue # define Py_FindMethod dll_ |