From 399cf70b92480913b7f76a296f4ae7d08d53fc94 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sun, 19 Jan 2003 19:12:30 +0000 Subject: [PATCH] Add staticmethod support from Nikolay Mladenov [SVN r16946] --- doc/news.html | 8 ++- doc/v2/acknowledgments.html | 3 + doc/v2/class.html | 97 +++++++++++++++++---------- include/boost/python/class.hpp | 5 ++ include/boost/python/object/class.hpp | 12 +++- src/object/class.cpp | 31 +++++++++ src/object/function.cpp | 16 +++++ test/Jamfile | 1 + test/staticmethod.cpp | 46 +++++++++++++ test/staticmethod.py | 52 ++++++++++++++ 10 files changed, 233 insertions(+), 38 deletions(-) create mode 100644 test/staticmethod.cpp create mode 100644 test/staticmethod.py diff --git a/doc/news.html b/doc/news.html index b06efbc4..ac0fb176 100644 --- a/doc/news.html +++ b/doc/news.html @@ -29,10 +29,16 @@
+
19 January 2003
+ +
Integrated staticmethod support from Nikolay Mladenov. Thanks, + Nikolay!
+
29 December 2002
Added Visual Studio project file and instructions from Brett - Calcott.
+ Calcott. Thanks, Brett!
20 December 2002
diff --git a/doc/v2/acknowledgments.html b/doc/v2/acknowledgments.html index 7b6321f7..82cea6a2 100644 --- a/doc/v2/acknowledgments.html +++ b/doc/v2/acknowledgments.html @@ -75,6 +75,9 @@ contributed and maintains the Visual Studio project files and documentation.

+

Nikolay Mladenov contributed + staticmethod support.

+

Martin Casado solved some sticky problems which allow us to build the Boost.Python shared library for AIX's crazy dynamic linking model.

diff --git a/doc/v2/class.html b/doc/v2/class.html index 6e63f0a4..85eda64e 100644 --- a/doc/v2/class.html +++ b/doc/v2/class.html @@ -212,7 +212,7 @@ namespace boost { namespace python { template <class T - , class Bases = bases<> + , class Bases = bases<> , class HeldType = T , class NonCopyable = unspecified > @@ -242,6 +242,9 @@ namespace boost { namespace python template <class Fn, class A1, class A2, class A3> class_& def(char const* name, Fn fn, A1 const&, A2 const&, A3 const&); + // declaring method as static + class_& staticmethod(char const* name); + // exposing operators template <unspecified> class_& def(HeldType according to the semantics described above, using a copy of init_expr's call policies. - If the longest valid prefix of Init contains N - types and init_expr holds M keywords, an initial - sequence of the keywords are used for all but the first - N - M arguments of each overload. + If the longest valid prefix of + Init contains N types and init_expr + holds M keywords, an initial sequence of the keywords are used + for all but the first N - M arguments of + each overload.
Returns: *this
@@ -378,20 +382,20 @@ class_& def(char const* name, Fn fn, A1 const& a1, A2 const& a2, A3
  • If a1 is the result of an overload-dispatch-expression, - only the second form is allowed and fn must be a pointer - to function or pointer to member function whose arity is the same as A1's maximum arity. + only the second form is allowed and fn must be a pointer to + function or pointer to member function whose arity is the same as A1's maximum + arity.
    Effects: For each prefix P of - Fn's sequence of argument types, beginning - with the one whose length is A1's minimum - arity, adds a - name(...) method - overload to the extension class. Each overload generated - invokes + Fn's sequence of argument types, beginning with + the one whose length is A1's minimum + arity, adds a + name(...) method overload to + the extension class. Each overload generated invokes a1's call-expression with P, using a copy of a1's call policies. If the longest valid prefix of A1 @@ -477,6 +481,37 @@ class_& def(char const* name, Fn fn, A1 const& a1, A2 const& a2, A3
    Returns: *this
    +class_& staticmethod(char const* name);
    +
    + +
    +
    Requires: name is an ntbs which conforms to Python's identifier + naming rules, and corresponds to a method whose overloads have all + been defined.
    + +
    Effects: Replaces the existing named attribute x with + the result of invoking staticmethod(x) + 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:
    + +
    +
    +setattr(self, name, staticmethod(getattr(self, name)))
    +
    +
    + +
    Note: Attempting to invoke def(name,...) after + invoking staticmethod(name) will raise a RuntimeError.
    + +
    Returns: *this
    +
    +
    + +
     template <unspecified>
     class_& def(detail::operator_<unspecified>);
    @@ -597,27 +632,21 @@ class_& def_pickle(PickleSuite const&);
     
    -
    Requires: PickleSuite must be publically derived from - pickle_suite.
    +
    Requires: PickleSuite must be publically derived from pickle_suite.
    Effects: Defines a legal combination of the special - attributes and methods: - __getinitargs__, - __getstate__, - __setstate__, - __getstate_manages_dict__, - __safe_for_unpickling__, - __reduce__ -
    + attributes and methods: __getinitargs__, + __getstate__, __setstate__, + __getstate_manages_dict__, + __safe_for_unpickling__, __reduce__
    Returns: *this
    -
    Rationale: Provides an - easy to use high-level interface - for establishing complete pickle support for the wrapped class. - The user is protected by compile-time consistency checks.
    +
    Rationale: Provides an easy to use + high-level interface for establishing complete pickle support for + the wrapped class. The user is protected by compile-time consistency + checks.

    @@ -685,8 +714,8 @@ class_<Derived, bases<Base> >("Derived"); Revised - 13 November, 2002 - + 13 November, 2002 +

    © Copyright make_method_static(name); + return *this; + } private: // helper functions inline void register_() const; diff --git a/include/boost/python/object/class.hpp b/include/boost/python/object/class.hpp index f551c29c..78cf06b5 100644 --- a/include/boost/python/object/class.hpp +++ b/include/boost/python/object/class.hpp @@ -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 diff --git a/src/object/class.cpp b/src/object/class.cpp index 3c261026..dc560fef 100644 --- a/src/object/class.cpp +++ b/src/object/class.cpp @@ -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(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); diff --git a/src/object/function.cpp b/src/object/function.cpp index 2a9f0121..bf6e8665 100644 --- a/src/object/function.cpp +++ b/src/object/function.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -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(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_)) { diff --git a/test/Jamfile b/test/Jamfile index 67c2c118..eb767745 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -63,6 +63,7 @@ run ../test/embedding.cpp ../build/boost_python $(PYTHON_LIB_PATH) $(PYTHON_EMBEDDED_LIBRARY) ; +bpl-test staticmethod ; bpl-test shared_ptr ; bpl-test polymorphism ; bpl-test auto_ptr ; diff --git a/test/staticmethod.cpp b/test/staticmethod.cpp new file mode 100644 index 00000000..a62f6275 --- /dev/null +++ b/test/staticmethod.cpp @@ -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 +#include +#include +#include +#include +#include + +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", init()) + .def("value", &X::value) + .def("set", &X::set) + .def("count", &X::count) + .staticmethod("count") + .def("magic", &getXmagic) + .staticmethod("magic") + ; +} + +#include "module_tail.cpp" diff --git a/test/staticmethod.py b/test/staticmethod.py new file mode 100644 index 00000000..7fcaae60 --- /dev/null +++ b/test/staticmethod.py @@ -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])