Skip to content

Commit 399cf70

Browse files
committed
Add staticmethod support from Nikolay Mladenov <nickm-at-sitius.com>
[SVN r16946]
1 parent 5717900 commit 399cf70

File tree

10 files changed

+233
-38
lines changed

10 files changed

+233
-38
lines changed

doc/news.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,16 @@ <h2 align="center">News/Change Log</h2>
2929
<hr>
3030

3131
<dl class="page-index">
32+
<dt>19 January 2003</dt>
33+
34+
<dd>Integrated <code>staticmethod</code> support from <a href=
35+
"mailto:nickm-at-sitius.com">Nikolay Mladenov</a>. Thanks,
36+
Nikolay!</dd>
37+
3238
<dt>29 December 2002</dt>
3339

3440
<dd>Added Visual Studio project file and instructions from Brett
35-
Calcott.</dd>
41+
Calcott. Thanks, Brett!</dd>
3642

3743
<dt>20 December 2002</dt>
3844

doc/v2/acknowledgments.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ <h2 align="center">Acknowledgments</h2>
7575
contributed and maintains the Visual Studio project files and
7676
documentation.</p>
7777

78+
<p><a href="mailto:nickm@sitius.com">Nikolay Mladenov</a> contributed
79+
<code>staticmethod</code> support.</p>
80+
7881
<p>Martin Casado solved some sticky problems which allow us to build the
7982
Boost.Python shared library for AIX's crazy dynamic linking model.</p>
8083

doc/v2/class.html

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ <h4><a name="class_-spec-synopsis"></a>Class template <code>class_</code>
212212
namespace boost { namespace python
213213
{
214214
template &lt;class T
215-
<font color="#007F00"> , class Bases = bases&lt;&gt;
215+
<font color="#007F00"> , class Bases = bases&lt;&gt;
216216
, class HeldType = T
217217
, class NonCopyable = <i>unspecified</i>
218218
&gt;
@@ -242,6 +242,9 @@ <h4><a name="class_-spec-synopsis"></a>Class template <code>class_</code>
242242
template &lt;class Fn, class A1, class A2, class A3&gt;
243243
class_&amp; def(char const* name, Fn fn, A1 const&amp;, A2 const&amp;, A3 const&amp;);
244244

245+
// declaring method as static
246+
class_&amp; staticmethod(char const* name);
247+
245248
// exposing operators
246249
template &lt;<i>unspecified</i>&gt;
247250
class_&amp; def(<a href=
@@ -344,10 +347,11 @@ <h4><a name="class_-spec-modifiers"></a>Class template
344347
generated constructs an object of <code>HeldType</code> according to
345348
the semantics described <a href="#HeldType">above</a>, using a copy of
346349
<code>init_expr</code>'s <a href="CallPolicies.html">call policies</a>.
347-
If the longest <a href="init.html#init-expressions">valid prefix</a> of <code>Init</code> contains <em>N</em>
348-
types and <code>init_expr</code> holds <em>M</em> keywords, an initial
349-
sequence of the keywords are used for all but the first
350-
<em>N</em>&nbsp;-&nbsp;<em>M</em> arguments of each overload.</dt>
350+
If the longest <a href="init.html#init-expressions">valid prefix</a> of
351+
<code>Init</code> contains <em>N</em> types and <code>init_expr</code>
352+
holds <em>M</em> keywords, an initial sequence of the keywords are used
353+
for all but the first <em>N</em>&nbsp;-&nbsp;<em>M</em> arguments of
354+
each overload.</dt>
351355

352356
<dt><b>Returns:</b> <code>*this</code></dt>
353357

@@ -378,20 +382,20 @@ <h4><a name="class_-spec-modifiers"></a>Class template
378382
<li>
379383
If <code>a1</code> is the result of an <a href=
380384
"overloads.html#overload-dispatch-expression"><em>overload-dispatch-expression</em></a>,
381-
only the second form is allowed and fn must be a pointer
382-
to function or pointer to member function whose <a
383-
href="definitions.html#arity">arity</a> is the same as A1's <a href=
384-
"overloads.html#overload-dispatch-expression"><em>maximum arity</em></a>.
385+
only the second form is allowed and fn must be a pointer to
386+
function or pointer to member function whose <a href=
387+
"definitions.html#arity">arity</a> is the same as A1's <a href=
388+
"overloads.html#overload-dispatch-expression"><em>maximum
389+
arity</em></a>.
385390

386391
<dl>
387392
<dt><b>Effects:</b> For each prefix <em>P</em> of
388-
<code>Fn</code>'s sequence of argument types, beginning
389-
with the one whose length is <code>A1</code>'s <a href=
390-
"overloads.html#overload-dispatch-expression"><em>minimum
391-
arity</em></a>, adds a
392-
<code><em>name</em>(</code>...<code>)</code> method
393-
overload to the extension class. Each overload generated
394-
invokes
393+
<code>Fn</code>'s sequence of argument types, beginning with
394+
the one whose length is <code>A1</code>'s <a href=
395+
"overloads.html#overload-dispatch-expression"><em>minimum
396+
arity</em></a>, adds a
397+
<code><em>name</em>(</code>...<code>)</code> method overload to
398+
the extension class. Each overload generated invokes
395399
<code>a1</code>'s call-expression with <em>P</em>, using a copy
396400
of <code>a1</code>'s <a href="CallPolicies.html">call
397401
policies</a>. If the longest valid prefix of <code>A1</code>
@@ -477,6 +481,37 @@ <h4><a name="class_-spec-modifiers"></a>Class template
477481
<dt><b>Returns:</b> <code>*this</code></dt>
478482
</dl>
479483
<pre>
484+
class_&amp; staticmethod(char const* name);
485+
</pre>
486+
487+
<dl class="function-semantics">
488+
<dt><b>Requires:</b> <code>name</code> is an <a href=
489+
"definitions.html#ntbs">ntbs</a> which conforms to Python's <a href=
490+
"http://www.python.org/doc/current/ref/identifiers.html">identifier
491+
naming rules</a>, and corresponds to a method whose overloads have all
492+
been defined.</dt>
493+
494+
<dt><b>Effects:</b> Replaces the existing named attribute <i>x</i> with
495+
the result of invoking <code>staticmethod(</code><i>x</i><code>)</code>
496+
in Python. Specifies that the corresponding method is static and
497+
therefore no object instance will be passed to it. This is equivalent
498+
to the Python statement:</dt>
499+
500+
<dd>
501+
<pre>
502+
setattr(self, name, staticmethod(getattr(self, name)))
503+
</pre>
504+
</dd>
505+
506+
<dt><b>Note:</b> Attempting to invoke <code>def(name,...)</code> after
507+
invoking <code>staticmethod(name)</code> will <a href=
508+
"definitions.html#raise">raise</a> a RuntimeError.</dt>
509+
510+
<dt><b>Returns:</b> <code>*this</code></dt>
511+
</dl>
512+
<br>
513+
514+
<pre>
480515
template &lt;<i>unspecified</i>&gt;
481516
class_&amp; def(<a href=
482517
"operators.html#operator_-spec">detail::operator_</a>&lt;unspecified&gt;);
@@ -597,27 +632,21 @@ <h4><a name="class_-spec-modifiers"></a>Class template
597632
</pre>
598633

599634
<dl class="function-semantics">
600-
<dt><b>Requires:</b> PickleSuite must be publically derived from
601-
<a href="pickle.html"
602-
><code>pickle_suite</code></a>.</dt>
635+
<dt><b>Requires:</b> PickleSuite must be publically derived from <a
636+
href="pickle.html"><code>pickle_suite</code></a>.</dt>
603637

604638
<dt><b>Effects:</b> Defines a legal combination of the special
605-
attributes and methods:
606-
<code>__getinitargs__</code>,
607-
<code>__getstate__</code>,
608-
<code>__setstate__</code>,
609-
<code>__getstate_manages_dict__</code>,
610-
<code>__safe_for_unpickling__</code>,
611-
<code>__reduce__</code>
612-
</dt>
639+
attributes and methods: <code>__getinitargs__</code>,
640+
<code>__getstate__</code>, <code>__setstate__</code>,
641+
<code>__getstate_manages_dict__</code>,
642+
<code>__safe_for_unpickling__</code>, <code>__reduce__</code></dt>
613643

614644
<dt><b>Returns:</b> <code>*this</code></dt>
615645

616-
<dt><b>Rationale:</b> Provides an
617-
<a href="pickle.html"
618-
>easy to use high-level interface</a>
619-
for establishing complete pickle support for the wrapped class.
620-
The user is protected by compile-time consistency checks.</dt>
646+
<dt><b>Rationale:</b> Provides an <a href="pickle.html">easy to use
647+
high-level interface</a> for establishing complete pickle support for
648+
the wrapped class. The user is protected by compile-time consistency
649+
checks.</dt>
621650
</dl>
622651
<br>
623652

@@ -685,8 +714,8 @@ <h2><a name="examples"></a>Example(s)</h2>
685714
</pre>
686715
Revised
687716
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
688-
13 November, 2002
689-
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
717+
13 November, 2002
718+
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
690719

691720

692721
<p><i>&copy; Copyright <a href=

include/boost/python/class.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,11 @@ class class_ : public objects::class_base
352352
return *this;
353353
}
354354

355+
self& staticmethod(char const* name)
356+
{
357+
this->make_method_static(name);
358+
return *this;
359+
}
355360
private: // helper functions
356361

357362
inline void register_() const;

include/boost/python/object/class.hpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ struct BOOST_PYTHON_DECL class_base : python::api::object
3131
, char const* doc = 0 // Docstring, if any.
3232
);
3333

34+
35+
// Implementation detail. Hiding this in the private section would
36+
// require use of template friend declarations.
37+
void enable_pickling(bool getstate_manages_dict);
38+
39+
protected:
3440
// Retrieve the underlying object
3541
void add_property(char const* name, object const& fget);
3642
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
4551
// for abstract classes.
4652
void def_no_init();
4753

48-
// Implementation detail. Hiding this in the private section would
49-
// require use of template friend declarations.
50-
void enable_pickling(bool getstate_manages_dict);
54+
// Effects:
55+
// setattr(self, staticmethod(getattr(self, method_name)))
56+
void make_method_static(const char *method_name);
5157
};
5258

5359
}}} // namespace boost::python::objects

src/object/class.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,37 @@ namespace objects
440440
}
441441
}
442442

443+
namespace
444+
{
445+
PyObject* callable_check(PyObject* callable)
446+
{
447+
if (PyCallable_Check(expect_non_null(callable)))
448+
return callable;
449+
450+
::PyErr_Format(
451+
PyExc_TypeError
452+
, "staticmethod expects callable object; got an object of type %s, which is not callable"
453+
, callable->ob_type->tp_name
454+
);
455+
456+
throw_error_already_set();
457+
return 0;
458+
}
459+
}
460+
461+
void class_base::make_method_static(const char * method_name)
462+
{
463+
PyTypeObject* self = downcast<PyTypeObject>(this->ptr());
464+
dict d((handle<>(borrowed(self->tp_dict))));
465+
466+
object method(d[method_name]);
467+
468+
this->attr(method_name) = object(
469+
handle<>(
470+
PyStaticMethod_New((callable_check)(method.ptr()) )
471+
));
472+
}
473+
443474
BOOST_PYTHON_DECL type_handle registered_class_object(class_id id)
444475
{
445476
return query_class(id);

src/object/function.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <boost/python/object_attributes.hpp>
1313
#include <boost/python/args.hpp>
1414
#include <boost/python/refcount.hpp>
15+
#include <boost/python/extract.hpp>
1516

1617
#include <algorithm>
1718
#include <cstring>
@@ -261,7 +262,22 @@ void function::add_to_namespace(
261262
if (existing)
262263
{
263264
if (existing->ob_type == &function_type)
265+
{
264266
new_func->add_overload(existing);
267+
}
268+
else if (existing->ob_type == &PyStaticMethod_Type)
269+
{
270+
char const* name_space_name = extract<char const*>(name_space.attr("__name__"));
271+
272+
::PyErr_Format(
273+
PyExc_RuntimeError
274+
, "Boost.Python - All overloads must be exported "
275+
"before calling \'class_<...>(\"%s\").staticmethod(\"%s\")\'"
276+
, name_space_name
277+
, name_
278+
);
279+
throw_error_already_set();
280+
}
265281
}
266282
else if (is_binary_operator(name_))
267283
{

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ run ../test/embedding.cpp <dll>../build/boost_python
6363
<library-path>$(PYTHON_LIB_PATH)
6464
<find-library>$(PYTHON_EMBEDDED_LIBRARY) ;
6565

66+
bpl-test staticmethod ;
6667
bpl-test shared_ptr ;
6768
bpl-test polymorphism ;
6869
bpl-test auto_ptr ;

test/staticmethod.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright David Abrahams 2002. Permission to copy, use,
2+
// modify, sell and distribute this software is granted provided this
3+
// copyright notice appears in all copies. This software is provided
4+
// "as is" without express or implied warranty, and with no claim as
5+
// to its suitability for any purpose.
6+
#include <boost/python/class.hpp>
7+
#include <boost/python/module.hpp>
8+
#include <boost/python/def.hpp>
9+
#include <boost/python/call_method.hpp>
10+
#include <boost/ref.hpp>
11+
#include <boost/utility.hpp>
12+
13+
using namespace boost::python;
14+
15+
struct X
16+
{
17+
explicit X(int x) : x(x), magic(7654321) { ++counter; }
18+
X(X const& rhs) : x(rhs.x), magic(7654321) { ++counter; }
19+
virtual ~X() { assert(magic == 7654321); magic = 6666666; x = 9999; --counter; }
20+
21+
void set(int x) { assert(magic == 7654321); this->x = x; }
22+
int value() const { assert(magic == 7654321); return x; }
23+
static int count() { return counter; }
24+
private:
25+
void operator=(X const&);
26+
private:
27+
int x;
28+
long magic;
29+
static int counter;
30+
};
31+
int X::counter;
32+
int getXmagic(){return 7654321;}
33+
34+
BOOST_PYTHON_MODULE(staticmethod_ext)
35+
{
36+
class_<X>("X", init<int>())
37+
.def("value", &X::value)
38+
.def("set", &X::set)
39+
.def("count", &X::count)
40+
.staticmethod("count")
41+
.def("magic", &getXmagic)
42+
.staticmethod("magic")
43+
;
44+
}
45+
46+
#include "module_tail.cpp"

test/staticmethod.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'''
2+
>>> from staticmethod_ext import *
3+
4+
>>> class X1(X):
5+
... pass
6+
7+
8+
>>> x = X(16)
9+
>>> x1 = X1(17)
10+
11+
12+
13+
>>> x1.count()
14+
2
15+
16+
>>> x.count()
17+
2
18+
19+
>>> X1.count()
20+
2
21+
22+
>>> X.count()
23+
2
24+
25+
26+
>>> x1.magic()
27+
7654321
28+
29+
>>> x.magic()
30+
7654321
31+
32+
>>> X1.magic()
33+
7654321
34+
35+
>>> X.magic()
36+
7654321
37+
38+
39+
'''
40+
41+
def run(args = None):
42+
import sys
43+
import doctest
44+
45+
if args is not None:
46+
sys.argv = args
47+
return doctest.testmod(sys.modules.get(__name__))
48+
49+
if __name__ == '__main__':
50+
print "running..."
51+
import sys
52+
sys.exit(run()[0])

0 commit comments

Comments
 (0)