mirror of
https://github.com/python/cpython.git
synced 2025-11-26 21:33:10 +00:00
bpo-40077: Convert _operator to use PyType_FromSpec (GH-21954)
This commit is contained in:
parent
a2118a1462
commit
31967fd8d0
2 changed files with 143 additions and 156 deletions
|
|
@ -0,0 +1 @@
|
||||||
|
Convert :mod:`_operator` to use :c:func:`PyType_FromSpec`.
|
||||||
|
|
@ -3,6 +3,20 @@
|
||||||
|
|
||||||
#include "clinic/_operator.c.h"
|
#include "clinic/_operator.c.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject *itemgetter_type;
|
||||||
|
PyObject *attrgetter_type;
|
||||||
|
PyObject *methodcaller_type;
|
||||||
|
} _operator_state;
|
||||||
|
|
||||||
|
static inline _operator_state*
|
||||||
|
get_operator_state(PyObject *module)
|
||||||
|
{
|
||||||
|
void *state = PyModule_GetState(module);
|
||||||
|
assert(state != NULL);
|
||||||
|
return (_operator_state *)state;
|
||||||
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
module _operator
|
module _operator
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
|
|
@ -942,8 +956,6 @@ typedef struct {
|
||||||
Py_ssize_t index; // -1 unless *item* is a single non-negative integer index
|
Py_ssize_t index; // -1 unless *item* is a single non-negative integer index
|
||||||
} itemgetterobject;
|
} itemgetterobject;
|
||||||
|
|
||||||
static PyTypeObject itemgetter_type;
|
|
||||||
|
|
||||||
/* AC 3.5: treats first argument as an iterable, otherwise uses *args */
|
/* AC 3.5: treats first argument as an iterable, otherwise uses *args */
|
||||||
static PyObject *
|
static PyObject *
|
||||||
itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
|
|
@ -960,13 +972,15 @@ itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
if (nitems <= 1) {
|
if (nitems <= 1) {
|
||||||
if (!PyArg_UnpackTuple(args, "itemgetter", 1, 1, &item))
|
if (!PyArg_UnpackTuple(args, "itemgetter", 1, 1, &item))
|
||||||
return NULL;
|
return NULL;
|
||||||
} else
|
} else {
|
||||||
item = args;
|
item = args;
|
||||||
|
}
|
||||||
|
_operator_state *state = PyType_GetModuleState(type);
|
||||||
/* create itemgetterobject structure */
|
/* create itemgetterobject structure */
|
||||||
ig = PyObject_GC_New(itemgetterobject, &itemgetter_type);
|
ig = PyObject_GC_New(itemgetterobject, (PyTypeObject *) state->itemgetter_type);
|
||||||
if (ig == NULL)
|
if (ig == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
Py_INCREF(item);
|
Py_INCREF(item);
|
||||||
ig->item = item;
|
ig->item = item;
|
||||||
|
|
@ -994,9 +1008,11 @@ itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
itemgetter_dealloc(itemgetterobject *ig)
|
itemgetter_dealloc(itemgetterobject *ig)
|
||||||
{
|
{
|
||||||
|
PyTypeObject *tp = Py_TYPE(ig);
|
||||||
PyObject_GC_UnTrack(ig);
|
PyObject_GC_UnTrack(ig);
|
||||||
Py_XDECREF(ig->item);
|
Py_XDECREF(ig->item);
|
||||||
PyObject_GC_Del(ig);
|
tp->tp_free(ig);
|
||||||
|
Py_DECREF(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
@ -1093,49 +1109,25 @@ Return a callable object that fetches the given item(s) from its operand.\n\
|
||||||
After f = itemgetter(2), the call f(r) returns r[2].\n\
|
After f = itemgetter(2), the call f(r) returns r[2].\n\
|
||||||
After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3])");
|
After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3])");
|
||||||
|
|
||||||
static PyTypeObject itemgetter_type = {
|
static PyType_Slot itemgetter_type_slots[] = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
{Py_tp_doc, (void *)itemgetter_doc},
|
||||||
"operator.itemgetter", /* tp_name */
|
{Py_tp_dealloc, itemgetter_dealloc},
|
||||||
sizeof(itemgetterobject), /* tp_basicsize */
|
{Py_tp_call, itemgetter_call},
|
||||||
0, /* tp_itemsize */
|
{Py_tp_traverse, itemgetter_traverse},
|
||||||
/* methods */
|
{Py_tp_methods, itemgetter_methods},
|
||||||
(destructor)itemgetter_dealloc, /* tp_dealloc */
|
{Py_tp_new, itemgetter_new},
|
||||||
0, /* tp_vectorcall_offset */
|
{Py_tp_getattro, PyObject_GenericGetAttr},
|
||||||
0, /* tp_getattr */
|
{Py_tp_repr, itemgetter_repr},
|
||||||
0, /* tp_setattr */
|
{0, 0}
|
||||||
0, /* tp_as_async */
|
|
||||||
(reprfunc)itemgetter_repr, /* tp_repr */
|
|
||||||
0, /* tp_as_number */
|
|
||||||
0, /* tp_as_sequence */
|
|
||||||
0, /* tp_as_mapping */
|
|
||||||
0, /* tp_hash */
|
|
||||||
(ternaryfunc)itemgetter_call, /* tp_call */
|
|
||||||
0, /* tp_str */
|
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
|
||||||
0, /* tp_setattro */
|
|
||||||
0, /* tp_as_buffer */
|
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
|
||||||
itemgetter_doc, /* tp_doc */
|
|
||||||
(traverseproc)itemgetter_traverse, /* tp_traverse */
|
|
||||||
0, /* tp_clear */
|
|
||||||
0, /* tp_richcompare */
|
|
||||||
0, /* tp_weaklistoffset */
|
|
||||||
0, /* tp_iter */
|
|
||||||
0, /* tp_iternext */
|
|
||||||
itemgetter_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 */
|
|
||||||
itemgetter_new, /* tp_new */
|
|
||||||
0, /* tp_free */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static PyType_Spec itemgetter_type_spec = {
|
||||||
|
.name = "operator.itemgetter",
|
||||||
|
.basicsize = sizeof(itemgetterobject),
|
||||||
|
.itemsize = 0,
|
||||||
|
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||||
|
.slots = itemgetter_type_slots,
|
||||||
|
};
|
||||||
|
|
||||||
/* attrgetter object **********************************************************/
|
/* attrgetter object **********************************************************/
|
||||||
|
|
||||||
|
|
@ -1145,8 +1137,6 @@ typedef struct {
|
||||||
PyObject *attr;
|
PyObject *attr;
|
||||||
} attrgetterobject;
|
} attrgetterobject;
|
||||||
|
|
||||||
static PyTypeObject attrgetter_type;
|
|
||||||
|
|
||||||
/* AC 3.5: treats first argument as an iterable, otherwise uses *args */
|
/* AC 3.5: treats first argument as an iterable, otherwise uses *args */
|
||||||
static PyObject *
|
static PyObject *
|
||||||
attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
|
|
@ -1246,8 +1236,9 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_operator_state *state = PyType_GetModuleState(type);
|
||||||
/* create attrgetterobject structure */
|
/* create attrgetterobject structure */
|
||||||
ag = PyObject_GC_New(attrgetterobject, &attrgetter_type);
|
ag = PyObject_GC_New(attrgetterobject, (PyTypeObject *)state->attrgetter_type);
|
||||||
if (ag == NULL) {
|
if (ag == NULL) {
|
||||||
Py_DECREF(attr);
|
Py_DECREF(attr);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -1263,9 +1254,11 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
attrgetter_dealloc(attrgetterobject *ag)
|
attrgetter_dealloc(attrgetterobject *ag)
|
||||||
{
|
{
|
||||||
|
PyTypeObject *tp = Py_TYPE(ag);
|
||||||
PyObject_GC_UnTrack(ag);
|
PyObject_GC_UnTrack(ag);
|
||||||
Py_XDECREF(ag->attr);
|
Py_XDECREF(ag->attr);
|
||||||
PyObject_GC_Del(ag);
|
tp->tp_free(ag);
|
||||||
|
Py_DECREF(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
@ -1438,47 +1431,24 @@ After g = attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).\n\
|
||||||
After h = attrgetter('name.first', 'name.last'), the call h(r) returns\n\
|
After h = attrgetter('name.first', 'name.last'), the call h(r) returns\n\
|
||||||
(r.name.first, r.name.last).");
|
(r.name.first, r.name.last).");
|
||||||
|
|
||||||
static PyTypeObject attrgetter_type = {
|
static PyType_Slot attrgetter_type_slots[] = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
{Py_tp_doc, (void *)attrgetter_doc},
|
||||||
"operator.attrgetter", /* tp_name */
|
{Py_tp_dealloc, attrgetter_dealloc},
|
||||||
sizeof(attrgetterobject), /* tp_basicsize */
|
{Py_tp_call, attrgetter_call},
|
||||||
0, /* tp_itemsize */
|
{Py_tp_traverse, attrgetter_traverse},
|
||||||
/* methods */
|
{Py_tp_methods, attrgetter_methods},
|
||||||
(destructor)attrgetter_dealloc, /* tp_dealloc */
|
{Py_tp_new, attrgetter_new},
|
||||||
0, /* tp_vectorcall_offset */
|
{Py_tp_getattro, PyObject_GenericGetAttr},
|
||||||
0, /* tp_getattr */
|
{Py_tp_repr, attrgetter_repr},
|
||||||
0, /* tp_setattr */
|
{0, 0}
|
||||||
0, /* tp_as_async */
|
};
|
||||||
(reprfunc)attrgetter_repr, /* tp_repr */
|
|
||||||
0, /* tp_as_number */
|
static PyType_Spec attrgetter_type_spec = {
|
||||||
0, /* tp_as_sequence */
|
.name = "operator.attrgetter",
|
||||||
0, /* tp_as_mapping */
|
.basicsize = sizeof(attrgetterobject),
|
||||||
0, /* tp_hash */
|
.itemsize = 0,
|
||||||
(ternaryfunc)attrgetter_call, /* tp_call */
|
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||||
0, /* tp_str */
|
.slots = attrgetter_type_slots,
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
|
||||||
0, /* tp_setattro */
|
|
||||||
0, /* tp_as_buffer */
|
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
|
||||||
attrgetter_doc, /* tp_doc */
|
|
||||||
(traverseproc)attrgetter_traverse, /* tp_traverse */
|
|
||||||
0, /* tp_clear */
|
|
||||||
0, /* tp_richcompare */
|
|
||||||
0, /* tp_weaklistoffset */
|
|
||||||
0, /* tp_iter */
|
|
||||||
0, /* tp_iternext */
|
|
||||||
attrgetter_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 */
|
|
||||||
attrgetter_new, /* tp_new */
|
|
||||||
0, /* tp_free */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1491,8 +1461,6 @@ typedef struct {
|
||||||
PyObject *kwds;
|
PyObject *kwds;
|
||||||
} methodcallerobject;
|
} methodcallerobject;
|
||||||
|
|
||||||
static PyTypeObject methodcaller_type;
|
|
||||||
|
|
||||||
/* AC 3.5: variable number of arguments, not currently support by AC */
|
/* AC 3.5: variable number of arguments, not currently support by AC */
|
||||||
static PyObject *
|
static PyObject *
|
||||||
methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
|
|
@ -1513,10 +1481,12 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_operator_state *state = PyType_GetModuleState(type);
|
||||||
/* create methodcallerobject structure */
|
/* create methodcallerobject structure */
|
||||||
mc = PyObject_GC_New(methodcallerobject, &methodcaller_type);
|
mc = PyObject_GC_New(methodcallerobject, (PyTypeObject *)state->methodcaller_type);
|
||||||
if (mc == NULL)
|
if (mc == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
name = PyTuple_GET_ITEM(args, 0);
|
name = PyTuple_GET_ITEM(args, 0);
|
||||||
Py_INCREF(name);
|
Py_INCREF(name);
|
||||||
|
|
@ -1539,11 +1509,13 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
methodcaller_dealloc(methodcallerobject *mc)
|
methodcaller_dealloc(methodcallerobject *mc)
|
||||||
{
|
{
|
||||||
|
PyTypeObject *tp = Py_TYPE(mc);
|
||||||
PyObject_GC_UnTrack(mc);
|
PyObject_GC_UnTrack(mc);
|
||||||
Py_XDECREF(mc->name);
|
Py_XDECREF(mc->name);
|
||||||
Py_XDECREF(mc->args);
|
Py_XDECREF(mc->args);
|
||||||
Py_XDECREF(mc->kwds);
|
Py_XDECREF(mc->kwds);
|
||||||
PyObject_GC_Del(mc);
|
tp->tp_free(mc);
|
||||||
|
Py_DECREF(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
@ -1704,63 +1676,52 @@ After f = methodcaller('name'), the call f(r) returns r.name().\n\
|
||||||
After g = methodcaller('name', 'date', foo=1), the call g(r) returns\n\
|
After g = methodcaller('name', 'date', foo=1), the call g(r) returns\n\
|
||||||
r.name('date', foo=1).");
|
r.name('date', foo=1).");
|
||||||
|
|
||||||
static PyTypeObject methodcaller_type = {
|
static PyType_Slot methodcaller_type_slots[] = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
{Py_tp_doc, (void *)methodcaller_doc},
|
||||||
"operator.methodcaller", /* tp_name */
|
{Py_tp_dealloc, methodcaller_dealloc},
|
||||||
sizeof(methodcallerobject), /* tp_basicsize */
|
{Py_tp_call, methodcaller_call},
|
||||||
0, /* tp_itemsize */
|
{Py_tp_traverse, methodcaller_traverse},
|
||||||
/* methods */
|
{Py_tp_methods, methodcaller_methods},
|
||||||
(destructor)methodcaller_dealloc, /* tp_dealloc */
|
{Py_tp_new, methodcaller_new},
|
||||||
0, /* tp_vectorcall_offset */
|
{Py_tp_getattro, PyObject_GenericGetAttr},
|
||||||
0, /* tp_getattr */
|
{Py_tp_repr, methodcaller_repr},
|
||||||
0, /* tp_setattr */
|
{0, 0}
|
||||||
0, /* tp_as_async */
|
|
||||||
(reprfunc)methodcaller_repr, /* tp_repr */
|
|
||||||
0, /* tp_as_number */
|
|
||||||
0, /* tp_as_sequence */
|
|
||||||
0, /* tp_as_mapping */
|
|
||||||
0, /* tp_hash */
|
|
||||||
(ternaryfunc)methodcaller_call, /* tp_call */
|
|
||||||
0, /* tp_str */
|
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
|
||||||
0, /* tp_setattro */
|
|
||||||
0, /* tp_as_buffer */
|
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
|
|
||||||
methodcaller_doc, /* tp_doc */
|
|
||||||
(traverseproc)methodcaller_traverse, /* tp_traverse */
|
|
||||||
0, /* tp_clear */
|
|
||||||
0, /* tp_richcompare */
|
|
||||||
0, /* tp_weaklistoffset */
|
|
||||||
0, /* tp_iter */
|
|
||||||
0, /* tp_iternext */
|
|
||||||
methodcaller_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 */
|
|
||||||
methodcaller_new, /* tp_new */
|
|
||||||
0, /* tp_free */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static PyType_Spec methodcaller_type_spec = {
|
||||||
|
.name = "operator.methodcaller",
|
||||||
|
.basicsize = sizeof(methodcallerobject),
|
||||||
|
.itemsize = 0,
|
||||||
|
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||||
|
.slots = methodcaller_type_slots,
|
||||||
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
operator_exec(PyObject *module)
|
operator_exec(PyObject *module)
|
||||||
{
|
{
|
||||||
PyTypeObject *types[] = {
|
_operator_state *state = get_operator_state(module);
|
||||||
&itemgetter_type,
|
state->attrgetter_type = PyType_FromModuleAndSpec(module, &attrgetter_type_spec, NULL);
|
||||||
&attrgetter_type,
|
if (state->attrgetter_type == NULL) {
|
||||||
&methodcaller_type
|
return -1;
|
||||||
};
|
}
|
||||||
|
if (PyModule_AddType(module, (PyTypeObject *)state->attrgetter_type) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < Py_ARRAY_LENGTH(types); i++) {
|
state->itemgetter_type = PyType_FromModuleAndSpec(module, &itemgetter_type_spec, NULL);
|
||||||
if (PyModule_AddType(module, types[i]) < 0) {
|
if (state->itemgetter_type == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (PyModule_AddType(module, (PyTypeObject *)state->itemgetter_type) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->methodcaller_type = PyType_FromModuleAndSpec(module, &methodcaller_type_spec, NULL);
|
||||||
|
if (state->methodcaller_type == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (PyModule_AddType(module, (PyTypeObject *)state->methodcaller_type) < 0) {
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1772,17 +1733,42 @@ static struct PyModuleDef_Slot operator_slots[] = {
|
||||||
{0, NULL}
|
{0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
operator_traverse(PyObject *module, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
_operator_state *state = get_operator_state(module);
|
||||||
|
Py_VISIT(state->attrgetter_type);
|
||||||
|
Py_VISIT(state->itemgetter_type);
|
||||||
|
Py_VISIT(state->methodcaller_type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
operator_clear(PyObject *module)
|
||||||
|
{
|
||||||
|
_operator_state *state = get_operator_state(module);
|
||||||
|
Py_CLEAR(state->attrgetter_type);
|
||||||
|
Py_CLEAR(state->itemgetter_type);
|
||||||
|
Py_CLEAR(state->methodcaller_type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
operator_free(void *module)
|
||||||
|
{
|
||||||
|
operator_clear((PyObject *)module);
|
||||||
|
}
|
||||||
|
|
||||||
static struct PyModuleDef operatormodule = {
|
static struct PyModuleDef operatormodule = {
|
||||||
PyModuleDef_HEAD_INIT,
|
PyModuleDef_HEAD_INIT,
|
||||||
"_operator",
|
.m_name = "_operator",
|
||||||
operator_doc,
|
.m_doc = operator_doc,
|
||||||
0,
|
.m_size = sizeof(_operator_state),
|
||||||
operator_methods,
|
.m_methods = operator_methods,
|
||||||
operator_slots,
|
.m_slots = operator_slots,
|
||||||
NULL,
|
.m_traverse = operator_traverse,
|
||||||
NULL,
|
.m_clear = operator_clear,
|
||||||
NULL
|
.m_free = operator_free,
|
||||||
};
|
};
|
||||||
|
|
||||||
PyMODINIT_FUNC
|
PyMODINIT_FUNC
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue