• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright David Abrahams 2001.
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/docstring_options.hpp>
7 #include <boost/python/object/function_object.hpp>
8 #include <boost/python/object/function_handle.hpp>
9 #include <boost/python/object/function_doc_signature.hpp>
10 #include <boost/python/errors.hpp>
11 #include <boost/python/str.hpp>
12 #include <boost/python/object_attributes.hpp>
13 #include <boost/python/args.hpp>
14 #include <boost/python/refcount.hpp>
15 #include <boost/python/extract.hpp>
16 #include <boost/python/tuple.hpp>
17 #include <boost/python/list.hpp>
18 #include <boost/python/ssize_t.hpp>
19 
20 #include <boost/python/detail/signature.hpp>
21 #include <boost/python/detail/none.hpp>
22 #include <boost/mpl/vector/vector10.hpp>
23 
24 #include <boost/bind.hpp>
25 
26 #include <algorithm>
27 #include <cstring>
28 
29 #if BOOST_PYTHON_DEBUG_ERROR_MESSAGES
30 # include <cstdio>
31 #endif
32 
33 namespace boost { namespace python {
34   volatile bool docstring_options::show_user_defined_ = true;
35   volatile bool docstring_options::show_cpp_signatures_ = true;
36 #ifndef BOOST_PYTHON_NO_PY_SIGNATURES
37   volatile bool docstring_options::show_py_signatures_ = true;
38 #else
39   volatile bool docstring_options::show_py_signatures_ = false;
40 #endif
41 }}
42 
43 namespace boost { namespace python { namespace objects {
44 
~py_function_impl_base()45 py_function_impl_base::~py_function_impl_base()
46 {
47 }
48 
max_arity() const49 unsigned py_function_impl_base::max_arity() const
50 {
51     return this->min_arity();
52 }
53 
54 extern PyTypeObject function_type;
55 
function(py_function const & implementation,python::detail::keyword const * names_and_defaults,unsigned num_keywords)56 function::function(
57     py_function const& implementation
58 #if BOOST_WORKAROUND(__EDG_VERSION__, == 245)
59     , python::detail::keyword const*       names_and_defaults
60 #else
61     , python::detail::keyword const* const names_and_defaults
62 #endif
63     , unsigned num_keywords
64     )
65     : m_fn(implementation)
66     , m_nkeyword_values(0)
67 {
68     if (names_and_defaults != 0)
69     {
70         unsigned int max_arity = m_fn.max_arity();
71         unsigned int keyword_offset
72             = max_arity > num_keywords ? max_arity - num_keywords : 0;
73 
74 
75         ssize_t tuple_size = num_keywords ? max_arity : 0;
76         m_arg_names = object(handle<>(PyTuple_New(tuple_size)));
77 
78         if (num_keywords != 0)
79         {
80             for (unsigned j = 0; j < keyword_offset; ++j)
81                 PyTuple_SET_ITEM(m_arg_names.ptr(), j, incref(Py_None));
82         }
83 
84         for (unsigned i = 0; i < num_keywords; ++i)
85         {
86             tuple kv;
87 
88             python::detail::keyword const* const p = names_and_defaults + i;
89             if (p->default_value)
90             {
91                 kv = make_tuple(p->name, p->default_value);
92                 ++m_nkeyword_values;
93             }
94             else
95             {
96                 kv = make_tuple(p->name);
97             }
98 
99             PyTuple_SET_ITEM(
100                 m_arg_names.ptr()
101                 , i + keyword_offset
102                 , incref(kv.ptr())
103                 );
104         }
105     }
106 
107     PyObject* p = this;
108     if (Py_TYPE(&function_type) == 0)
109     {
110         Py_TYPE(&function_type) = &PyType_Type;
111         ::PyType_Ready(&function_type);
112     }
113 
114     (void)(     // warning suppression for GCC
115         PyObject_INIT(p, &function_type)
116     );
117 }
118 
~function()119 function::~function()
120 {
121 }
122 
call(PyObject * args,PyObject * keywords) const123 PyObject* function::call(PyObject* args, PyObject* keywords) const
124 {
125     std::size_t n_unnamed_actual = PyTuple_GET_SIZE(args);
126     std::size_t n_keyword_actual = keywords ? PyDict_Size(keywords) : 0;
127     std::size_t n_actual = n_unnamed_actual + n_keyword_actual;
128 
129     function const* f = this;
130 
131     // Try overloads looking for a match
132     do
133     {
134         // Check for a plausible number of arguments
135         unsigned min_arity = f->m_fn.min_arity();
136         unsigned max_arity = f->m_fn.max_arity();
137 
138         if (n_actual + f->m_nkeyword_values >= min_arity
139             && n_actual <= max_arity)
140         {
141             // This will be the args that actually get passed
142             handle<>inner_args(allow_null(borrowed(args)));
143 
144             if (n_keyword_actual > 0      // Keyword arguments were supplied
145                  || n_actual < min_arity) // or default keyword values are needed
146             {
147                 if (f->m_arg_names.is_none())
148                 {
149                     // this overload doesn't accept keywords
150                     inner_args = handle<>();
151                 }
152                 else
153                 {
154                     // "all keywords are none" is a special case
155                     // indicating we will accept any number of keyword
156                     // arguments
157                     if (PyTuple_Size(f->m_arg_names.ptr()) == 0)
158                     {
159                         // no argument preprocessing
160                     }
161                     else
162                     {
163                         // build a new arg tuple, will adjust its size later
164                         assert(max_arity <= static_cast<std::size_t>(ssize_t_max));
165                         inner_args = handle<>(
166                             PyTuple_New(static_cast<ssize_t>(max_arity)));
167 
168                         // Fill in the positional arguments
169                         for (std::size_t i = 0; i < n_unnamed_actual; ++i)
170                             PyTuple_SET_ITEM(inner_args.get(), i, incref(PyTuple_GET_ITEM(args, i)));
171 
172                         // Grab remaining arguments by name from the keyword dictionary
173                         std::size_t n_actual_processed = n_unnamed_actual;
174 
175                         for (std::size_t arg_pos = n_unnamed_actual; arg_pos < max_arity ; ++arg_pos)
176                         {
177                             // Get the keyword[, value pair] corresponding
178                             PyObject* kv = PyTuple_GET_ITEM(f->m_arg_names.ptr(), arg_pos);
179 
180                             // If there were any keyword arguments,
181                             // look up the one we need for this
182                             // argument position
183                             PyObject* value = n_keyword_actual
184                                 ? PyDict_GetItem(keywords, PyTuple_GET_ITEM(kv, 0))
185                                 : 0;
186 
187                             if (!value)
188                             {
189                                 // Not found; check if there's a default value
190                                 if (PyTuple_GET_SIZE(kv) > 1)
191                                     value = PyTuple_GET_ITEM(kv, 1);
192 
193                                 if (!value)
194                                 {
195                                     // still not found; matching fails
196                                     PyErr_Clear();
197                                     inner_args = handle<>();
198                                     break;
199                                 }
200                             }
201                             else
202                             {
203                                 ++n_actual_processed;
204                             }
205 
206                             PyTuple_SET_ITEM(inner_args.get(), arg_pos, incref(value));
207                         }
208 
209                         if (inner_args.get())
210                         {
211                             //check if we proccessed all the arguments
212                             if(n_actual_processed < n_actual)
213                                 inner_args = handle<>();
214                         }
215                     }
216                 }
217             }
218 
219             // Call the function.  Pass keywords in case it's a
220             // function accepting any number of keywords
221             PyObject* result = inner_args ? f->m_fn(inner_args.get(), keywords) : 0;
222 
223             // If the result is NULL but no error was set, m_fn failed
224             // the argument-matching test.
225 
226             // This assumes that all other error-reporters are
227             // well-behaved and never return NULL to python without
228             // setting an error.
229             if (result != 0 || PyErr_Occurred())
230                 return result;
231         }
232         f = f->m_overloads.get();
233     }
234     while (f);
235     // None of the overloads matched; time to generate the error message
236     argument_error(args, keywords);
237     return 0;
238 }
239 
signature(bool show_return_type) const240 object function::signature(bool show_return_type) const
241 {
242     py_function const& impl = m_fn;
243 
244     python::detail::signature_element const* return_type = impl.signature();
245     python::detail::signature_element const* s = return_type + 1;
246 
247     list formal_params;
248     if (impl.max_arity() == 0)
249         formal_params.append("void");
250 
251     for (unsigned n = 0; n < impl.max_arity(); ++n)
252     {
253         if (s[n].basename == 0)
254         {
255             formal_params.append("...");
256             break;
257         }
258 
259         str param(s[n].basename);
260         if (s[n].lvalue)
261             param += " {lvalue}";
262 
263         if (m_arg_names) // None or empty tuple will test false
264         {
265             object kv(m_arg_names[n]);
266             if (kv)
267             {
268                 char const* const fmt = len(kv) > 1 ? " %s=%r" : " %s";
269                 param += fmt % kv;
270             }
271         }
272 
273         formal_params.append(param);
274     }
275 
276     if (show_return_type)
277         return "%s(%s) -> %s" % make_tuple(
278             m_name, str(", ").join(formal_params), return_type->basename);
279     return "%s(%s)" % make_tuple(
280         m_name, str(", ").join(formal_params));
281 }
282 
signatures(bool show_return_type) const283 object function::signatures(bool show_return_type) const
284 {
285     list result;
286     for (function const* f = this; f; f = f->m_overloads.get()) {
287         result.append(f->signature(show_return_type));
288     }
289     return result;
290 }
291 
argument_error(PyObject * args,PyObject *) const292 void function::argument_error(PyObject* args, PyObject* /*keywords*/) const
293 {
294     static handle<> exception(
295         PyErr_NewException(const_cast<char*>("Boost.Python.ArgumentError"), PyExc_TypeError, 0));
296 
297     object message = "Python argument types in\n    %s.%s("
298         % make_tuple(this->m_namespace, this->m_name);
299 
300     list actual_args;
301     for (ssize_t i = 0; i < PyTuple_Size(args); ++i)
302     {
303         char const* name = PyTuple_GetItem(args, i)->ob_type->tp_name;
304         actual_args.append(str(name));
305     }
306     message += str(", ").join(actual_args);
307     message += ")\ndid not match C++ signature:\n    ";
308     message += str("\n    ").join(signatures());
309 
310 #if BOOST_PYTHON_DEBUG_ERROR_MESSAGES
311     std::printf("\n--------\n%s\n--------\n", extract<const char*>(message)());
312 #endif
313     PyErr_SetObject(exception.get(), message.ptr());
314     throw_error_already_set();
315 }
316 
add_overload(handle<function> const & overload_)317 void function::add_overload(handle<function> const& overload_)
318 {
319     function* parent = this;
320 
321     while (parent->m_overloads)
322         parent = parent->m_overloads.get();
323 
324     parent->m_overloads = overload_;
325 
326     // If we have no documentation, get the docs from the overload
327     if (!m_doc)
328         m_doc = overload_->m_doc;
329 }
330 
331 namespace
332 {
333   char const* const binary_operator_names[] =
334   {
335       "add__",
336       "and__",
337       "div__",
338       "divmod__",
339       "eq__",
340       "floordiv__",
341       "ge__",
342       "gt__",
343       "le__",
344       "lshift__",
345       "lt__",
346       "mod__",
347       "mul__",
348       "ne__",
349       "or__",
350       "pow__",
351       "radd__",
352       "rand__",
353       "rdiv__",
354       "rdivmod__",
355       "rfloordiv__",
356       "rlshift__",
357       "rmod__",
358       "rmul__",
359       "ror__",
360       "rpow__",
361       "rrshift__",
362       "rshift__",
363       "rsub__",
364       "rtruediv__",
365       "rxor__",
366       "sub__",
367       "truediv__",
368       "xor__"
369   };
370 
371   struct less_cstring
372   {
operator ()boost::python::objects::__anon5e309cf60111::less_cstring373       bool operator()(char const* x, char const* y) const
374       {
375           return BOOST_CSTD_::strcmp(x,y) < 0;
376       }
377   };
378 
is_binary_operator(char const * name)379   inline bool is_binary_operator(char const* name)
380   {
381       return name[0] == '_'
382           && name[1] == '_'
383           && std::binary_search(
384               &binary_operator_names[0]
385               , binary_operator_names + sizeof(binary_operator_names)/sizeof(*binary_operator_names)
386               , name + 2
387               , less_cstring()
388               );
389   }
390 
391   // Something for the end of the chain of binary operators
not_implemented(PyObject *,PyObject *)392   PyObject* not_implemented(PyObject*, PyObject*)
393   {
394       Py_INCREF(Py_NotImplemented);
395       return Py_NotImplemented;
396   }
397 
not_implemented_function()398   handle<function> not_implemented_function()
399   {
400 
401       static object keeper(
402           function_object(
403               py_function(&not_implemented, mpl::vector1<void>(), 2)
404             , python::detail::keyword_range())
405           );
406       return handle<function>(borrowed(downcast<function>(keeper.ptr())));
407   }
408 }
409 
add_to_namespace(object const & name_space,char const * name_,object const & attribute)410 void function::add_to_namespace(
411     object const& name_space, char const* name_, object const& attribute)
412 {
413     add_to_namespace(name_space, name_, attribute, 0);
414 }
415 
416 namespace detail
417 {
418     extern char py_signature_tag[];
419     extern char cpp_signature_tag[];
420 }
421 
add_to_namespace(object const & name_space,char const * name_,object const & attribute,char const * doc)422 void function::add_to_namespace(
423     object const& name_space, char const* name_, object const& attribute, char const* doc)
424 {
425     str const name(name_);
426     PyObject* const ns = name_space.ptr();
427 
428     if (attribute.ptr()->ob_type == &function_type)
429     {
430         function* new_func = downcast<function>(attribute.ptr());
431         handle<> dict;
432 
433 #if PY_VERSION_HEX < 0x03000000
434         // Old-style class gone in Python 3
435         if (PyClass_Check(ns))
436             dict = handle<>(borrowed(((PyClassObject*)ns)->cl_dict));
437         else
438 #endif
439         if (PyType_Check(ns))
440             dict = handle<>(borrowed(((PyTypeObject*)ns)->tp_dict));
441         else
442             dict = handle<>(PyObject_GetAttrString(ns, const_cast<char*>("__dict__")));
443 
444         if (dict == 0)
445             throw_error_already_set();
446 
447         assert(!PyErr_Occurred());
448         handle<> existing(allow_null(::PyObject_GetItem(dict.get(), name.ptr())));
449         PyErr_Clear();
450 
451         if (existing)
452         {
453             if (existing->ob_type == &function_type)
454             {
455                 new_func->add_overload(
456                     handle<function>(
457                         borrowed(
458                             downcast<function>(existing.get())
459                         )
460                     )
461                 );
462             }
463             else if (existing->ob_type == &PyStaticMethod_Type)
464             {
465                 char const* name_space_name = extract<char const*>(name_space.attr("__name__"));
466 
467                 ::PyErr_Format(
468                     PyExc_RuntimeError
469                     , "Boost.Python - All overloads must be exported "
470                       "before calling \'class_<...>(\"%s\").staticmethod(\"%s\")\'"
471                     , name_space_name
472                     , name_
473                     );
474                 throw_error_already_set();
475             }
476         }
477         else if (is_binary_operator(name_))
478         {
479             // Binary operators need an additional overload which
480             // returns NotImplemented, so that Python will try the
481             // __rxxx__ functions on the other operand. We add this
482             // when no overloads for the operator already exist.
483             new_func->add_overload(not_implemented_function());
484         }
485 
486         // A function is named the first time it is added to a namespace.
487         if (new_func->name().is_none())
488             new_func->m_name = name;
489 
490         assert(!PyErr_Occurred());
491         handle<> name_space_name(
492             allow_null(::PyObject_GetAttrString(name_space.ptr(), const_cast<char*>("__name__"))));
493         PyErr_Clear();
494 
495         if (name_space_name)
496             new_func->m_namespace = object(name_space_name);
497     }
498 
499     if (PyObject_SetAttr(ns, name.ptr(), attribute.ptr()) < 0)
500         throw_error_already_set();
501 
502     object mutable_attribute(attribute);
503 /*
504     if (doc != 0 && docstring_options::show_user_defined_)
505     {
506         // Accumulate documentation
507 
508         if (
509             PyObject_HasAttrString(mutable_attribute.ptr(), "__doc__")
510             && mutable_attribute.attr("__doc__"))
511         {
512             mutable_attribute.attr("__doc__") += "\n\n";
513             mutable_attribute.attr("__doc__") += doc;
514         }
515         else {
516             mutable_attribute.attr("__doc__") = doc;
517         }
518     }
519 
520     if (docstring_options::show_signatures_)
521     {
522         if (   PyObject_HasAttrString(mutable_attribute.ptr(), "__doc__")
523             && mutable_attribute.attr("__doc__")) {
524             mutable_attribute.attr("__doc__") += (
525               mutable_attribute.attr("__doc__")[-1] != "\n" ? "\n\n" : "\n");
526         }
527         else {
528             mutable_attribute.attr("__doc__") = "";
529         }
530         function* f = downcast<function>(attribute.ptr());
531         mutable_attribute.attr("__doc__") += str("\n    ").join(make_tuple(
532           "C++ signature:", f->signature(true)));
533     }
534     */
535     str _doc;
536 
537     if (docstring_options::show_py_signatures_)
538     {
539         _doc += str(const_cast<const char*>(detail::py_signature_tag));
540     }
541     if (doc != 0 && docstring_options::show_user_defined_)
542         _doc += doc;
543 
544     if (docstring_options::show_cpp_signatures_)
545     {
546         _doc += str(const_cast<const char*>(detail::cpp_signature_tag));
547     }
548     if(_doc)
549     {
550         object mutable_attribute(attribute);
551         mutable_attribute.attr("__doc__")= _doc;
552     }
553 }
554 
add_to_namespace(object const & name_space,char const * name,object const & attribute)555 BOOST_PYTHON_DECL void add_to_namespace(
556     object const& name_space, char const* name, object const& attribute)
557 {
558     function::add_to_namespace(name_space, name, attribute, 0);
559 }
560 
add_to_namespace(object const & name_space,char const * name,object const & attribute,char const * doc)561 BOOST_PYTHON_DECL void add_to_namespace(
562     object const& name_space, char const* name, object const& attribute, char const* doc)
563 {
564     function::add_to_namespace(name_space, name, attribute, doc);
565 }
566 
567 
568 namespace
569 {
570   struct bind_return
571   {
bind_returnboost::python::objects::__anon5e309cf60211::bind_return572       bind_return(PyObject*& result, function const* f, PyObject* args, PyObject* keywords)
573           : m_result(result)
574             , m_f(f)
575             , m_args(args)
576             , m_keywords(keywords)
577       {}
578 
operator ()boost::python::objects::__anon5e309cf60211::bind_return579       void operator()() const
580       {
581           m_result = m_f->call(m_args, m_keywords);
582       }
583 
584    private:
585       PyObject*& m_result;
586       function const* m_f;
587       PyObject* m_args;
588       PyObject* m_keywords;
589   };
590 }
591 
592 extern "C"
593 {
594     // Stolen from Python's funcobject.c
595     static PyObject *
function_descr_get(PyObject * func,PyObject * obj,PyObject * type_)596     function_descr_get(PyObject *func, PyObject *obj, PyObject *type_)
597     {
598 #if PY_VERSION_HEX >= 0x03000000
599         // The implement is different in Python 3 because of the removal of unbound method
600         if (obj == Py_None || obj == NULL) {
601             Py_INCREF(func);
602             return func;
603         }
604         return PyMethod_New(func, obj);
605 #else
606         if (obj == Py_None)
607             obj = NULL;
608         return PyMethod_New(func, obj, type_);
609 #endif
610     }
611 
612     static void
function_dealloc(PyObject * p)613     function_dealloc(PyObject* p)
614     {
615         delete static_cast<function*>(p);
616     }
617 
618     static PyObject *
function_call(PyObject * func,PyObject * args,PyObject * kw)619     function_call(PyObject *func, PyObject *args, PyObject *kw)
620     {
621         PyObject* result = 0;
622         handle_exception(bind_return(result, static_cast<function*>(func), args, kw));
623         return result;
624     }
625 
626     //
627     // Here we're using the function's tp_getset rather than its
628     // tp_members to set up __doc__ and __name__, because tp_members
629     // really depends on having a POD object type (it relies on
630     // offsets). It might make sense to reformulate function as a POD
631     // at some point, but this is much more expedient.
632     //
function_get_doc(PyObject * op,void *)633     static PyObject* function_get_doc(PyObject* op, void*)
634     {
635         function* f = downcast<function>(op);
636         list signatures = function_doc_signature_generator::function_doc_signatures(f);
637         if(!signatures) return python::detail::none();
638         signatures.reverse();
639         return python::incref( str("\n").join(signatures).ptr());
640     }
641 
function_set_doc(PyObject * op,PyObject * doc,void *)642     static int function_set_doc(PyObject* op, PyObject* doc, void*)
643     {
644         function* f = downcast<function>(op);
645         f->doc(doc ? object(python::detail::borrowed_reference(doc)) : object());
646         return 0;
647     }
648 
function_get_name(PyObject * op,void *)649     static PyObject* function_get_name(PyObject* op, void*)
650     {
651         function* f = downcast<function>(op);
652         if (f->name().is_none())
653 #if PY_VERSION_HEX >= 0x03000000
654             return PyUnicode_InternFromString("<unnamed Boost.Python function>");
655 #else
656             return PyString_InternFromString("<unnamed Boost.Python function>");
657 #endif
658         else
659             return python::incref(f->name().ptr());
660     }
661 
662     // We add a dummy __class__ attribute in order to fool PyDoc into
663     // treating these as built-in functions and scanning their
664     // documentation
function_get_class(PyObject *,void *)665     static PyObject* function_get_class(PyObject* /*op*/, void*)
666     {
667         return python::incref(upcast<PyObject>(&PyCFunction_Type));
668     }
669 
function_get_module(PyObject * op,void *)670     static PyObject* function_get_module(PyObject* op, void*)
671     {
672         function* f = downcast<function>(op);
673         object const& ns = f->get_namespace();
674         if (!ns.is_none()) {
675             return python::incref(ns.ptr());
676         }
677         PyErr_SetString(
678             PyExc_AttributeError, const_cast<char*>(
679                 "Boost.Python function __module__ unknown."));
680         return 0;
681     }
682 }
683 
684 static PyGetSetDef function_getsetlist[] = {
685     {const_cast<char*>("__name__"), (getter)function_get_name, 0, 0, 0 },
686     {const_cast<char*>("func_name"), (getter)function_get_name, 0, 0, 0 },
687     {const_cast<char*>("__module__"), (getter)function_get_module, 0, 0, 0 },
688     {const_cast<char*>("func_module"), (getter)function_get_module, 0, 0, 0 },
689     {const_cast<char*>("__class__"), (getter)function_get_class, 0, 0, 0 },    // see note above
690     {const_cast<char*>("__doc__"), (getter)function_get_doc, (setter)function_set_doc, 0, 0},
691     {const_cast<char*>("func_doc"), (getter)function_get_doc, (setter)function_set_doc, 0, 0},
692     {NULL, 0, 0, 0, 0} /* Sentinel */
693 };
694 
695 PyTypeObject function_type = {
696     PyVarObject_HEAD_INIT(NULL, 0)
697     const_cast<char*>("Boost.Python.function"),
698     sizeof(function),
699     0,
700     (destructor)function_dealloc,               /* tp_dealloc */
701     0,                                  /* tp_print */
702     0,                                  /* tp_getattr */
703     0,                                  /* tp_setattr */
704     0,                                  /* tp_compare */
705     0, //(reprfunc)func_repr,                   /* tp_repr */
706     0,                                  /* tp_as_number */
707     0,                                  /* tp_as_sequence */
708     0,                                  /* tp_as_mapping */
709     0,                                  /* tp_hash */
710     function_call,                              /* tp_call */
711     0,                                  /* tp_str */
712     0, // PyObject_GenericGetAttr,            /* tp_getattro */
713     0, // PyObject_GenericSetAttr,            /* tp_setattro */
714     0,                                  /* tp_as_buffer */
715     Py_TPFLAGS_DEFAULT /* | Py_TPFLAGS_HAVE_GC */,/* tp_flags */
716     0,                                  /* tp_doc */
717     0, // (traverseproc)func_traverse,          /* tp_traverse */
718     0,                                  /* tp_clear */
719     0,                                  /* tp_richcompare */
720     0, //offsetof(PyFunctionObject, func_weakreflist), /* tp_weaklistoffset */
721     0,                                  /* tp_iter */
722     0,                                  /* tp_iternext */
723     0,                                  /* tp_methods */
724     0, // func_memberlist,              /* tp_members */
725     function_getsetlist,                /* tp_getset */
726     0,                                  /* tp_base */
727     0,                                  /* tp_dict */
728     function_descr_get,                 /* tp_descr_get */
729     0,                                  /* tp_descr_set */
730     0, //offsetof(PyFunctionObject, func_dict),      /* tp_dictoffset */
731     0,                                      /* tp_init */
732     0,                                      /* tp_alloc */
733     0,                                      /* tp_new */
734     0,                                      /* tp_free */
735     0,                                      /* tp_is_gc */
736     0,                                      /* tp_bases */
737     0,                                      /* tp_mro */
738     0,                                      /* tp_cache */
739     0,                                      /* tp_subclasses */
740     0,                                      /* tp_weaklist */
741 #if PYTHON_API_VERSION >= 1012
742     0                                       /* tp_del */
743 #endif
744 };
745 
function_object(py_function const & f,python::detail::keyword_range const & keywords)746 object function_object(
747     py_function const& f
748     , python::detail::keyword_range const& keywords)
749 {
750     return python::object(
751         python::detail::new_non_null_reference(
752             new function(
753                 f, keywords.first, keywords.second - keywords.first)));
754 }
755 
function_object(py_function const & f)756 object function_object(py_function const& f)
757 {
758     return function_object(f, python::detail::keyword_range());
759 }
760 
761 
function_handle_impl(py_function const & f)762 handle<> function_handle_impl(py_function const& f)
763 {
764     return python::handle<>(
765         allow_null(
766             new function(f, 0, 0)));
767 }
768 
769 } // namespace objects
770 
771 namespace detail
772 {
make_raw_function(objects::py_function f)773   object BOOST_PYTHON_DECL make_raw_function(objects::py_function f)
774   {
775       static keyword k;
776 
777       return objects::function_object(
778           f
779           , keyword_range(&k,&k));
780   }
pure_virtual_called()781   void BOOST_PYTHON_DECL pure_virtual_called()
782   {
783       PyErr_SetString(
784           PyExc_RuntimeError, const_cast<char*>("Pure virtual function called"));
785       throw_error_already_set();
786   }
787 }
788 
789 }} // namespace boost::python
790