2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-23 05:42:30 +00:00

Add staticmethod support from Nikolay Mladenov <nickm-at-sitius.com>

[SVN r16946]
This commit is contained in:
Dave Abrahams
2003-01-19 19:12:30 +00:00
parent 571790097a
commit 399cf70b92
10 changed files with 233 additions and 38 deletions

View File

@@ -29,10 +29,16 @@
<hr>
<dl class="page-index">
<dt>19 January 2003</dt>
<dd>Integrated <code>staticmethod</code> support from <a href=
"mailto:nickm-at-sitius.com">Nikolay Mladenov</a>. Thanks,
Nikolay!</dd>
<dt>29 December 2002</dt>
<dd>Added Visual Studio project file and instructions from Brett
Calcott.</dd>
Calcott. Thanks, Brett!</dd>
<dt>20 December 2002</dt>

View File

@@ -75,6 +75,9 @@
contributed and maintains the Visual Studio project files and
documentation.</p>
<p><a href="mailto:nickm@sitius.com">Nikolay Mladenov</a> contributed
<code>staticmethod</code> support.</p>
<p>Martin Casado solved some sticky problems which allow us to build the
Boost.Python shared library for AIX's crazy dynamic linking model.</p>

View File

@@ -212,7 +212,7 @@
namespace boost { namespace python
{
template &lt;class T
<font color="#007F00"> , class Bases = bases&lt;&gt;
<font color="#007F00"> , class Bases = bases&lt;&gt;
, class HeldType = T
, class NonCopyable = <i>unspecified</i>
&gt;
@@ -242,6 +242,9 @@ namespace boost { namespace python
template &lt;class Fn, class A1, class A2, class A3&gt;
class_&amp; def(char const* name, Fn fn, A1 const&amp;, A2 const&amp;, A3 const&amp;);
// declaring method as static
class_&amp; staticmethod(char const* name);
// exposing operators
template &lt;<i>unspecified</i>&gt;
class_&amp; def(<a href=
@@ -344,10 +347,11 @@ class_&amp; def(Init init_expr);
generated constructs an object of <code>HeldType</code> according to
the semantics described <a href="#HeldType">above</a>, using a copy of
<code>init_expr</code>'s <a href="CallPolicies.html">call policies</a>.
If the longest <a href="init.html#init-expressions">valid prefix</a> of <code>Init</code> contains <em>N</em>
types and <code>init_expr</code> holds <em>M</em> keywords, an initial
sequence of the keywords are used for all but the first
<em>N</em>&nbsp;-&nbsp;<em>M</em> arguments of each overload.</dt>
If the longest <a href="init.html#init-expressions">valid prefix</a> of
<code>Init</code> contains <em>N</em> types and <code>init_expr</code>
holds <em>M</em> keywords, an initial sequence of the keywords are used
for all but the first <em>N</em>&nbsp;-&nbsp;<em>M</em> arguments of
each overload.</dt>
<dt><b>Returns:</b> <code>*this</code></dt>
@@ -378,20 +382,20 @@ class_&amp; def(char const* name, Fn fn, A1 const&amp; a1, A2 const&amp; a2, A3
<li>
If <code>a1</code> is the result of an <a href=
"overloads.html#overload-dispatch-expression"><em>overload-dispatch-expression</em></a>,
only the second form is allowed and fn must be a pointer
to function or pointer to member function whose <a
href="definitions.html#arity">arity</a> is the same as A1's <a href=
"overloads.html#overload-dispatch-expression"><em>maximum arity</em></a>.
only the second form is allowed and fn must be a pointer to
function or pointer to member function whose <a href=
"definitions.html#arity">arity</a> is the same as A1's <a href=
"overloads.html#overload-dispatch-expression"><em>maximum
arity</em></a>.
<dl>
<dt><b>Effects:</b> For each prefix <em>P</em> of
<code>Fn</code>'s sequence of argument types, beginning
with the one whose length is <code>A1</code>'s <a href=
"overloads.html#overload-dispatch-expression"><em>minimum
arity</em></a>, adds a
<code><em>name</em>(</code>...<code>)</code> method
overload to the extension class. Each overload generated
invokes
<code>Fn</code>'s sequence of argument types, beginning with
the one whose length is <code>A1</code>'s <a href=
"overloads.html#overload-dispatch-expression"><em>minimum
arity</em></a>, adds a
<code><em>name</em>(</code>...<code>)</code> method overload to
the extension class. Each overload generated invokes
<code>a1</code>'s call-expression with <em>P</em>, using a copy
of <code>a1</code>'s <a href="CallPolicies.html">call
policies</a>. If the longest valid prefix of <code>A1</code>
@@ -477,6 +481,37 @@ class_&amp; def(char const* name, Fn fn, A1 const&amp; a1, A2 const&amp; a2, A3
<dt><b>Returns:</b> <code>*this</code></dt>
</dl>
<pre>
class_&amp; staticmethod(char const* name);
</pre>
<dl class="function-semantics">
<dt><b>Requires:</b> <code>name</code> is an <a href=
"definitions.html#ntbs">ntbs</a> which conforms to Python's <a href=
"http://www.python.org/doc/current/ref/identifiers.html">identifier
naming rules</a>, and corresponds to a method whose overloads have all
been defined.</dt>
<dt><b>Effects:</b> Replaces the existing named attribute <i>x</i> with
the result of invoking <code>staticmethod(</code><i>x</i><code>)</code>
in Python. Specifies that the corresponding method is static and
therefore no object instance will be passed to it. This is equivalent
to the Python statement:</dt>
<dd>
<pre>
setattr(self, name, staticmethod(getattr(self, name)))
</pre>
</dd>
<dt><b>Note:</b> Attempting to invoke <code>def(name,...)</code> after
invoking <code>staticmethod(name)</code> will <a href=
"definitions.html#raise">raise</a> a RuntimeError.</dt>
<dt><b>Returns:</b> <code>*this</code></dt>
</dl>
<br>
<pre>
template &lt;<i>unspecified</i>&gt;
class_&amp; def(<a href=
"operators.html#operator_-spec">detail::operator_</a>&lt;unspecified&gt;);
@@ -597,27 +632,21 @@ class_&amp; def_pickle(PickleSuite const&amp;);
</pre>
<dl class="function-semantics">
<dt><b>Requires:</b> PickleSuite must be publically derived from
<a href="pickle.html"
><code>pickle_suite</code></a>.</dt>
<dt><b>Requires:</b> PickleSuite must be publically derived from <a
href="pickle.html"><code>pickle_suite</code></a>.</dt>
<dt><b>Effects:</b> Defines a legal combination of the special
attributes and methods:
<code>__getinitargs__</code>,
<code>__getstate__</code>,
<code>__setstate__</code>,
<code>__getstate_manages_dict__</code>,
<code>__safe_for_unpickling__</code>,
<code>__reduce__</code>
</dt>
attributes and methods: <code>__getinitargs__</code>,
<code>__getstate__</code>, <code>__setstate__</code>,
<code>__getstate_manages_dict__</code>,
<code>__safe_for_unpickling__</code>, <code>__reduce__</code></dt>
<dt><b>Returns:</b> <code>*this</code></dt>
<dt><b>Rationale:</b> Provides an
<a href="pickle.html"
>easy to use high-level interface</a>
for establishing complete pickle support for the wrapped class.
The user is protected by compile-time consistency checks.</dt>
<dt><b>Rationale:</b> Provides an <a href="pickle.html">easy to use
high-level interface</a> for establishing complete pickle support for
the wrapped class. The user is protected by compile-time consistency
checks.</dt>
</dl>
<br>
@@ -685,8 +714,8 @@ class_&lt;Derived, bases&lt;Base&gt; &gt;("Derived");
</pre>
Revised
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
13 November, 2002
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
13 November, 2002
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
<p><i>&copy; Copyright <a href=

View File

@@ -352,6 +352,11 @@ class class_ : public objects::class_base
return *this;
}
self& staticmethod(char const* name)
{
this->make_method_static(name);
return *this;
}
private: // helper functions
inline void register_() const;

View File

@@ -31,6 +31,12 @@ struct BOOST_PYTHON_DECL class_base : python::api::object
, char const* doc = 0 // Docstring, if any.
);
// Implementation detail. Hiding this in the private section would
// require use of template friend declarations.
void enable_pickling(bool getstate_manages_dict);
protected:
// Retrieve the underlying object
void add_property(char const* name, object const& fget);
void add_property(char const* name, object const& fget, object const& fset);
@@ -45,9 +51,9 @@ struct BOOST_PYTHON_DECL class_base : python::api::object
// for abstract classes.
void def_no_init();
// Implementation detail. Hiding this in the private section would
// require use of template friend declarations.
void enable_pickling(bool getstate_manages_dict);
// Effects:
// setattr(self, staticmethod(getattr(self, method_name)))
void make_method_static(const char *method_name);
};
}}} // namespace boost::python::objects

View File

@@ -440,6 +440,37 @@ namespace objects
}
}
namespace
{
PyObject* callable_check(PyObject* callable)
{
if (PyCallable_Check(expect_non_null(callable)))
return callable;
::PyErr_Format(
PyExc_TypeError
, "staticmethod expects callable object; got an object of type %s, which is not callable"
, callable->ob_type->tp_name
);
throw_error_already_set();
return 0;
}
}
void class_base::make_method_static(const char * method_name)
{
PyTypeObject* self = downcast<PyTypeObject>(this->ptr());
dict d((handle<>(borrowed(self->tp_dict))));
object method(d[method_name]);
this->attr(method_name) = object(
handle<>(
PyStaticMethod_New((callable_check)(method.ptr()) )
));
}
BOOST_PYTHON_DECL type_handle registered_class_object(class_id id)
{
return query_class(id);

View File

@@ -12,6 +12,7 @@
#include <boost/python/object_attributes.hpp>
#include <boost/python/args.hpp>
#include <boost/python/refcount.hpp>
#include <boost/python/extract.hpp>
#include <algorithm>
#include <cstring>
@@ -261,7 +262,22 @@ void function::add_to_namespace(
if (existing)
{
if (existing->ob_type == &function_type)
{
new_func->add_overload(existing);
}
else if (existing->ob_type == &PyStaticMethod_Type)
{
char const* name_space_name = extract<char const*>(name_space.attr("__name__"));
::PyErr_Format(
PyExc_RuntimeError
, "Boost.Python - All overloads must be exported "
"before calling \'class_<...>(\"%s\").staticmethod(\"%s\")\'"
, name_space_name
, name_
);
throw_error_already_set();
}
}
else if (is_binary_operator(name_))
{

View File

@@ -63,6 +63,7 @@ run ../test/embedding.cpp <dll>../build/boost_python
<library-path>$(PYTHON_LIB_PATH)
<find-library>$(PYTHON_EMBEDDED_LIBRARY) ;
bpl-test staticmethod ;
bpl-test shared_ptr ;
bpl-test polymorphism ;
bpl-test auto_ptr ;

46
test/staticmethod.cpp Normal file
View File

@@ -0,0 +1,46 @@
// Copyright David Abrahams 2002. Permission to copy, use,
// modify, sell and distribute this software is granted provided this
// copyright notice appears in all copies. This software is provided
// "as is" without express or implied warranty, and with no claim as
// to its suitability for any purpose.
#include <boost/python/class.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/call_method.hpp>
#include <boost/ref.hpp>
#include <boost/utility.hpp>
using namespace boost::python;
struct X
{
explicit X(int x) : x(x), magic(7654321) { ++counter; }
X(X const& rhs) : x(rhs.x), magic(7654321) { ++counter; }
virtual ~X() { assert(magic == 7654321); magic = 6666666; x = 9999; --counter; }
void set(int x) { assert(magic == 7654321); this->x = x; }
int value() const { assert(magic == 7654321); return x; }
static int count() { return counter; }
private:
void operator=(X const&);
private:
int x;
long magic;
static int counter;
};
int X::counter;
int getXmagic(){return 7654321;}
BOOST_PYTHON_MODULE(staticmethod_ext)
{
class_<X>("X", init<int>())
.def("value", &X::value)
.def("set", &X::set)
.def("count", &X::count)
.staticmethod("count")
.def("magic", &getXmagic)
.staticmethod("magic")
;
}
#include "module_tail.cpp"

52
test/staticmethod.py Normal file
View File

@@ -0,0 +1,52 @@
'''
>>> from staticmethod_ext import *
>>> class X1(X):
... pass
>>> x = X(16)
>>> x1 = X1(17)
>>> x1.count()
2
>>> x.count()
2
>>> X1.count()
2
>>> X.count()
2
>>> x1.magic()
7654321
>>> x.magic()
7654321
>>> X1.magic()
7654321
>>> X.magic()
7654321
'''
def run(args = None):
import sys
import doctest
if args is not None:
sys.argv = args
return doctest.testmod(sys.modules.get(__name__))
if __name__ == '__main__':
print "running..."
import sys
sys.exit(run()[0])