• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright David Abrahams 2002.
2 // Distributed under the Boost Software License, Version 1.0. (See
3 // accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5 
6 #include <boost/python/object/enum_base.hpp>
7 #include <boost/python/cast.hpp>
8 #include <boost/python/scope.hpp>
9 #include <boost/python/object.hpp>
10 #include <boost/python/tuple.hpp>
11 #include <boost/python/dict.hpp>
12 #include <boost/python/str.hpp>
13 #include <boost/python/extract.hpp>
14 #include <boost/python/object_protocol.hpp>
15 #include <structmember.h>
16 
17 namespace boost { namespace python { namespace objects {
18 
19 struct enum_object
20 {
21 #if PY_VERSION_HEX >= 0x03000000
22     PyLongObject base_object;
23 #else
24     PyIntObject base_object;
25 #endif
26     PyObject* name;
27 };
28 
29 static PyMemberDef enum_members[] = {
30     {const_cast<char*>("name"), T_OBJECT_EX, offsetof(enum_object,name),READONLY, 0},
31     {0, 0, 0, 0, 0}
32 };
33 
34 
35 extern "C"
36 {
37     static void
enum_dealloc(enum_object * self)38     enum_dealloc(enum_object* self)
39     {
40         Py_XDECREF(self->name);
41         Py_TYPE(self)->tp_free((PyObject*)self);
42     }
43 
enum_repr(PyObject * self_)44     static PyObject* enum_repr(PyObject* self_)
45     {
46         PyObject *mod = PyObject_GetAttrString( self_, "__module__");
47         object auto_free = object(handle<>(mod));
48         enum_object* self = downcast<enum_object>(self_);
49         if (!self->name)
50         {
51             return
52 #if PY_VERSION_HEX >= 0x03000000
53                 PyUnicode_FromFormat("%S.%s(%ld)", mod, self_->ob_type->tp_name, PyLong_AsLong(self_));
54 #else
55                 PyString_FromFormat("%s.%s(%ld)", PyString_AsString(mod), self_->ob_type->tp_name, PyInt_AS_LONG(self_));
56 #endif
57         }
58         else
59         {
60             PyObject* name = self->name;
61             if (name == 0)
62                 return 0;
63 
64             return
65 #if PY_VERSION_HEX >= 0x03000000
66                 PyUnicode_FromFormat("%S.%s.%S", mod, self_->ob_type->tp_name, name);
67 #else
68                 PyString_FromFormat("%s.%s.%s",
69                         PyString_AsString(mod), self_->ob_type->tp_name, PyString_AsString(name));
70 #endif
71         }
72     }
73 
enum_str(PyObject * self_)74     static PyObject* enum_str(PyObject* self_)
75     {
76         enum_object* self = downcast<enum_object>(self_);
77         if (!self->name)
78         {
79 #if PY_VERSION_HEX >= 0x03000000
80             return PyLong_Type.tp_str(self_);
81 #else
82             return PyInt_Type.tp_str(self_);
83 #endif
84         }
85         else
86         {
87             return incref(self->name);
88         }
89     }
90 }
91 
92 static PyTypeObject enum_type_object = {
93     PyVarObject_HEAD_INIT(NULL, 0) // &PyType_Type
94     const_cast<char*>("Boost.Python.enum"),
95     sizeof(enum_object),                    /* tp_basicsize */
96     0,                                      /* tp_itemsize */
97     (destructor) enum_dealloc,              /* tp_dealloc */
98     0,                                      /* tp_print */
99     0,                                      /* tp_getattr */
100     0,                                      /* tp_setattr */
101     0,                                      /* tp_compare */
102     enum_repr,                              /* tp_repr */
103     0,                                      /* tp_as_number */
104     0,                                      /* tp_as_sequence */
105     0,                                      /* tp_as_mapping */
106     0,                                      /* tp_hash */
107     0,                                      /* tp_call */
108     enum_str,                               /* tp_str */
109     0,                                      /* tp_getattro */
110     0,                                      /* tp_setattro */
111     0,                                      /* tp_as_buffer */
112     Py_TPFLAGS_DEFAULT
113 #if PY_VERSION_HEX < 0x03000000
114     | Py_TPFLAGS_CHECKTYPES
115 #endif
116     | Py_TPFLAGS_HAVE_GC
117     | Py_TPFLAGS_BASETYPE,                  /* tp_flags */
118     0,                                      /* tp_doc */
119     0,                                      /* tp_traverse */
120     0,                                      /* tp_clear */
121     0,                                      /* tp_richcompare */
122     0,                                      /* tp_weaklistoffset */
123     0,                                      /* tp_iter */
124     0,                                      /* tp_iternext */
125     0,                                      /* tp_methods */
126     enum_members,                           /* tp_members */
127     0,                                      /* tp_getset */
128     0, //&PyInt_Type,                       /* tp_base */
129     0,                                      /* tp_dict */
130     0,                                      /* tp_descr_get */
131     0,                                      /* tp_descr_set */
132     0,                                      /* tp_dictoffset */
133     0,                                      /* tp_init */
134     0,                                      /* tp_alloc */
135     0,                                      /* tp_new */
136     0,                                      /* tp_free */
137     0,                                      /* tp_is_gc */
138     0,                                      /* tp_bases */
139     0,                                      /* tp_mro */
140     0,                                      /* tp_cache */
141     0,                                      /* tp_subclasses */
142     0,                                      /* tp_weaklist */
143 #if PYTHON_API_VERSION >= 1012
144     0                                       /* tp_del */
145 #endif
146 };
147 
148 object module_prefix();
149 
150 namespace
151 {
new_enum_type(char const * name,char const * doc)152   object new_enum_type(char const* name, char const *doc)
153   {
154       if (enum_type_object.tp_dict == 0)
155       {
156           Py_TYPE(&enum_type_object) = incref(&PyType_Type);
157 #if PY_VERSION_HEX >= 0x03000000
158           enum_type_object.tp_base = &PyLong_Type;
159 #else
160           enum_type_object.tp_base = &PyInt_Type;
161 #endif
162           if (PyType_Ready(&enum_type_object))
163               throw_error_already_set();
164       }
165 
166       type_handle metatype(borrowed(&PyType_Type));
167       type_handle base(borrowed(&enum_type_object));
168 
169       // suppress the instance __dict__ in these enum objects. There
170       // may be a slicker way, but this'll do for now.
171       dict d;
172       d["__slots__"] = tuple();
173       d["values"] = dict();
174       d["names"] = dict();
175 
176       object module_name = module_prefix();
177       if (module_name)
178          d["__module__"] = module_name;
179       if (doc)
180          d["__doc__"] = doc;
181 
182       object result = (object(metatype))(name, make_tuple(base), d);
183 
184       scope().attr(name) = result;
185 
186       return result;
187   }
188 }
189 
enum_base(char const * name,converter::to_python_function_t to_python,converter::convertible_function convertible,converter::constructor_function construct,type_info id,char const * doc)190 enum_base::enum_base(
191     char const* name
192     , converter::to_python_function_t to_python
193     , converter::convertible_function convertible
194     , converter::constructor_function construct
195     , type_info id
196     , char const *doc
197     )
198     : object(new_enum_type(name, doc))
199 {
200     converter::registration& converters
201         = const_cast<converter::registration&>(
202             converter::registry::lookup(id));
203 
204     converters.m_class_object = downcast<PyTypeObject>(this->ptr());
205     converter::registry::insert(to_python, id);
206     converter::registry::insert(convertible, construct, id);
207 }
208 
add_value(char const * name_,long value)209 void enum_base::add_value(char const* name_, long value)
210 {
211     // Convert name to Python string
212     object name(name_);
213 
214     // Create a new enum instance by calling the class with a value
215     object x = (*this)(value);
216 
217     // Store the object in the enum class
218     (*this).attr(name_) = x;
219 
220     dict d = extract<dict>(this->attr("values"))();
221     d[value] = x;
222 
223     // Set the name field in the new enum instanec
224     enum_object* p = downcast<enum_object>(x.ptr());
225     Py_XDECREF(p->name);
226     p->name = incref(name.ptr());
227 
228     dict names_dict = extract<dict>(this->attr("names"))();
229     names_dict[x.attr("name")] = x;
230 }
231 
export_values()232 void enum_base::export_values()
233 {
234     dict d = extract<dict>(this->attr("names"))();
235     list items = d.items();
236     scope current;
237 
238     for (unsigned i = 0, max = len(items); i < max; ++i)
239         api::setattr(current, items[i][0], items[i][1]);
240  }
241 
to_python(PyTypeObject * type_,long x)242 PyObject* enum_base::to_python(PyTypeObject* type_, long x)
243 {
244     object type((type_handle(borrowed(type_))));
245 
246     dict d = extract<dict>(type.attr("values"))();
247     object v = d.get(x, object());
248     return incref(
249         (v == object() ? type(x) : v).ptr());
250 }
251 
252 }}} // namespace boost::python::object
253