2
0
mirror of https://github.com/boostorg/python.git synced 2026-02-01 08:42:16 +00:00

Patches by Nikolay Mladenov (nickm at sitius com): new pythonic signatures; docstring support for enums; fix unrelated Visual C++ 6 problem

[SVN r39191]
This commit is contained in:
Ralf W. Grosse-Kunstleve
2007-09-11 16:53:50 +00:00
parent 04e54d670c
commit 5809078ba9
82 changed files with 2523 additions and 341 deletions

View File

@@ -16,6 +16,7 @@
#include <boost/python/converter/registry.hpp>
#include <boost/python/converter/registrations.hpp>
#include <boost/python/converter/shared_ptr_deleter.hpp>
#include <boost/python/converter/pytype_function.hpp>
#include <boost/cast.hpp>
#include <string>
@@ -56,6 +57,7 @@ namespace
&slot_rvalue_from_python<T,SlotPolicy>::convertible
, &slot_rvalue_from_python<T,SlotPolicy>::construct
, type_id<T>()
, &SlotPolicy::get_pytype
);
}
@@ -100,6 +102,7 @@ namespace
return (PyInt_Check(obj) || PyLong_Check(obj))
? &number_methods->nb_int : 0;
}
static PyTypeObject const* get_pytype() { return &PyInt_Type;}
};
template <class T>
@@ -135,6 +138,7 @@ namespace
return (PyInt_Check(obj) || PyLong_Check(obj))
? &py_object_identity : 0;
}
static PyTypeObject const* get_pytype() { return &PyInt_Type;}
};
template <class T>
@@ -173,6 +177,7 @@ namespace
else
return 0;
}
static PyTypeObject const* get_pytype() { return &PyInt_Type;}
};
struct long_long_rvalue_from_python : long_long_rvalue_from_python_base
@@ -228,6 +233,15 @@ namespace
{
return PyObject_IsTrue(intermediate);
}
static PyTypeObject const* get_pytype()
{
#if PY_VERSION_HEX >= 0x02030000
return &PyBool_Type;
#else
return &PyInt_Type;
#endif
}
};
// A SlotPolicy for extracting floating types from Python objects.
@@ -259,6 +273,7 @@ namespace
return PyFloat_AS_DOUBLE(intermediate);
}
}
static PyTypeObject const* get_pytype() { return &PyFloat_Type;}
};
// A SlotPolicy for extracting C++ strings from Python objects.
@@ -276,6 +291,7 @@ namespace
{
return std::string(PyString_AsString(intermediate),PyString_Size(intermediate));
}
static PyTypeObject const* get_pytype() { return &PyString_Type;}
};
#if defined(Py_USING_UNICODE) && !defined(BOOST_NO_STD_WSTRING)
@@ -316,6 +332,7 @@ namespace
}
return result;
}
static PyTypeObject const* get_pytype() { return &PyUnicode_Type;}
};
#endif
@@ -346,6 +363,7 @@ namespace
return PyFloat_AS_DOUBLE(intermediate);
}
}
static PyTypeObject const* get_pytype() { return &PyComplex_Type;}
};
}
@@ -411,7 +429,7 @@ void initialize_builtin_converters()
slot_rvalue_from_python<std::complex<long double>,complex_rvalue_from_python>();
// Add an lvalue converter for char which gets us char const*
registry::insert(convert_to_cstring,type_id<char>());
registry::insert(convert_to_cstring,type_id<char>(),&converter::wrap_pytype<&PyString_Type>::get_pytype);
// Register by-value converters to std::string, std::wstring
#if defined(Py_USING_UNICODE) && !defined(BOOST_NO_STD_WSTRING)

View File

@@ -20,6 +20,35 @@
#endif
namespace boost { namespace python { namespace converter {
BOOST_PYTHON_DECL PyTypeObject const* registration::expected_from_python_type() const
{
if (this->m_class_object != 0)
return this->m_class_object;
std::set<PyTypeObject const*> pool;
for(rvalue_from_python_chain* r = rvalue_chain; r ; r=r->next)
if(r->expected_pytype)
pool.insert(r->expected_pytype());
//for now I skip the search for common base
if (pool.size()==1)
return *pool.begin();
return 0;
}
BOOST_PYTHON_DECL PyTypeObject const* registration::to_python_target_type() const
{
if (this->m_class_object != 0)
return this->m_class_object;
if (this->m_to_python_target_type != 0)
return this->m_to_python_target_type();
return 0;
}
BOOST_PYTHON_DECL PyTypeObject* registration::get_class_object() const
{
@@ -168,15 +197,15 @@ namespace // <unnamed>
namespace registry
{
void insert(to_python_function_t f, type_info source_t)
void insert(to_python_function_t f, type_info source_t, PyTypeObject const* (*to_python_target_type)())
{
# ifdef BOOST_PYTHON_TRACE_REGISTRY
std::cout << "inserting to_python " << source_t << "\n";
# endif
to_python_function_t& slot = get(source_t)->m_to_python;
entry* slot = get(source_t);
assert(slot == 0); // we have a problem otherwise
if (slot != 0)
assert(slot->m_to_python == 0); // we have a problem otherwise
if (slot->m_to_python != 0)
{
std::string msg = (
std::string("to-Python converter for ")
@@ -189,11 +218,12 @@ namespace registry
throw_error_already_set();
}
}
slot = f;
slot->m_to_python = f;
slot->m_to_python_target_type = to_python_target_type;
}
// Insert an lvalue from_python converter
void insert(convertible_function convert, type_info key)
void insert(convertible_function convert, type_info key, PyTypeObject const* (*exp_pytype)())
{
# ifdef BOOST_PYTHON_TRACE_REGISTRY
std::cout << "inserting lvalue from_python " << key << "\n";
@@ -204,13 +234,14 @@ namespace registry
registration->next = found->lvalue_chain;
found->lvalue_chain = registration;
insert(convert, 0, key);
insert(convert, 0, key,exp_pytype);
}
// Insert an rvalue from_python converter
void insert(void* (*convertible)(PyObject*)
, constructor_function construct
, type_info key)
, type_info key
, PyTypeObject const* (*exp_pytype)())
{
# ifdef BOOST_PYTHON_TRACE_REGISTRY
std::cout << "inserting rvalue from_python " << key << "\n";
@@ -219,6 +250,7 @@ namespace registry
rvalue_from_python_chain *registration = new rvalue_from_python_chain;
registration->convertible = convertible;
registration->construct = construct;
registration->expected_pytype = exp_pytype;
registration->next = found->rvalue_chain;
found->rvalue_chain = registration;
}
@@ -226,7 +258,8 @@ namespace registry
// Insert an rvalue from_python converter
void push_back(void* (*convertible)(PyObject*)
, constructor_function construct
, type_info key)
, type_info key
, PyTypeObject const* (*exp_pytype)())
{
# ifdef BOOST_PYTHON_TRACE_REGISTRY
std::cout << "push_back rvalue from_python " << key << "\n";
@@ -238,6 +271,7 @@ namespace registry
rvalue_from_python_chain *registration = new rvalue_from_python_chain;
registration->convertible = convertible;
registration->construct = construct;
registration->expected_pytype = exp_pytype;
registration->next = 0;
*found = registration;
}

View File

@@ -171,4 +171,14 @@ list dict_base::values() const
}
}
static struct register_dict_pytype_ptr
{
register_dict_pytype_ptr()
{
const_cast<converter::registration &>(
converter::registry::lookup(boost::python::type_id<boost::python::dict>())
).m_class_object = &PyDict_Type;
}
}register_dict_pytype_ptr_;
}}} // namespace boost::python

View File

@@ -137,4 +137,14 @@ long list_base::count(object_cref value) const
return result;
}
static struct register_list_pytype_ptr
{
register_list_pytype_ptr()
{
const_cast<converter::registration &>(
converter::registry::lookup(boost::python::type_id<boost::python::list>())
).m_class_object = &PyList_Type;
}
}register_list_pytype_ptr_;
}}} // namespace boost::python

View File

@@ -121,7 +121,7 @@ object module_prefix();
namespace
{
object new_enum_type(char const* name)
object new_enum_type(char const* name, char const *doc)
{
if (enum_type_object.tp_dict == 0)
{
@@ -143,6 +143,8 @@ namespace
object module_name = module_prefix();
if (module_name)
d["__module__"] = module_name;
if (doc)
d["__doc__"] = doc;
object result = (object(metatype))(name, make_tuple(base), d);
@@ -158,8 +160,9 @@ enum_base::enum_base(
, converter::convertible_function convertible
, converter::constructor_function construct
, type_info id
, char const *doc
)
: object(new_enum_type(name))
: object(new_enum_type(name, doc))
{
converter::registration& converters
= const_cast<converter::registration&>(

View File

@@ -6,6 +6,7 @@
#include <boost/python/docstring_options.hpp>
#include <boost/python/object/function_object.hpp>
#include <boost/python/object/function_handle.hpp>
#include <boost/python/object/function_doc_signature.hpp>
#include <boost/python/errors.hpp>
#include <boost/python/str.hpp>
#include <boost/python/object_attributes.hpp>
@@ -17,6 +18,7 @@
#include <boost/python/ssize_t.hpp>
#include <boost/python/detail/signature.hpp>
#include <boost/python/detail/none.hpp>
#include <boost/mpl/vector/vector10.hpp>
#include <boost/bind.hpp>
@@ -30,7 +32,12 @@
namespace boost { namespace python {
volatile bool docstring_options::show_user_defined_ = true;
volatile bool docstring_options::show_signatures_ = true;
volatile bool docstring_options::show_cpp_signatures_ = true;
#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
volatile bool docstring_options::show_py_signatures_ = true;
#else
volatile bool docstring_options::show_py_signatures_ = false;
#endif
}}
namespace boost { namespace python { namespace objects {
@@ -411,6 +418,12 @@ void function::add_to_namespace(
add_to_namespace(name_space, name_, attribute, 0);
}
namespace detail
{
extern char py_signature_tag[];
extern char cpp_signature_tag[];
}
void function::add_to_namespace(
object const& name_space, char const* name_, object const& attribute, char const* doc)
{
@@ -487,6 +500,7 @@ void function::add_to_namespace(
throw_error_already_set();
object mutable_attribute(attribute);
/*
if (doc != 0 && docstring_options::show_user_defined_)
{
// Accumulate documentation
@@ -517,6 +531,28 @@ void function::add_to_namespace(
mutable_attribute.attr("__doc__") += str("\n ").join(make_tuple(
"C++ signature:", f->signature(true)));
}
*/
str _doc;
if (docstring_options::show_py_signatures_)
{
_doc += str(reinterpret_cast<const char*>(detail::py_signature_tag));
}
if (doc != 0 && docstring_options::show_user_defined_)
_doc += doc;
if (docstring_options::show_cpp_signatures_)
{
if(len(_doc))
_doc += "\n "+str(reinterpret_cast<const char*>(detail::cpp_signature_tag));
else
_doc += " "+str(reinterpret_cast<const char*>(detail::cpp_signature_tag));
}
if(_doc)
{
object mutable_attribute(attribute);
mutable_attribute.attr("__doc__")= _doc;
}
}
BOOST_PYTHON_DECL void add_to_namespace(
@@ -591,7 +627,10 @@ extern "C"
static PyObject* function_get_doc(PyObject* op, void*)
{
function* f = downcast<function>(op);
return python::incref(f->doc().ptr());
list signatures = function_doc_signature_generator::function_doc_signatures(f);
if(!signatures) return python::detail::none();
signatures.reverse();
return python::incref( str("\n ").join(signatures).ptr());
}
static int function_set_doc(PyObject* op, PyObject* doc, void*)

View File

@@ -0,0 +1,314 @@
// Copyright Nikolay Mladenov 2007.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/python/converter/registrations.hpp>
#include <boost/python/object/function_doc_signature.hpp>
#include <boost/python/errors.hpp>
#include <boost/python/str.hpp>
#include <boost/python/args.hpp>
#include <boost/python/tuple.hpp>
#include <boost/python/detail/signature.hpp>
#include <vector>
namespace boost { namespace python { namespace objects {
bool function_doc_signature_generator::arity_cmp( function const *f1, function const *f2 )
{
return f1->m_fn.max_arity() < f2->m_fn.max_arity();
}
bool function_doc_signature_generator::are_seq_overloads( function const *f1, function const *f2 , bool check_docs)
{
py_function const & impl1 = f1->m_fn;
py_function const & impl2 = f2->m_fn;
//the number of parameters differs by 1
if (impl2.max_arity()-impl1.max_arity() != 1)
return false;
// if check docs then f1 shold not have docstring or have the same docstring as f2
if (check_docs && f2->doc() != f1->doc() && f1->doc())
return false;
python::detail::signature_element const* s1 = impl1.signature();
python::detail::signature_element const* s2 = impl2.signature();
unsigned size = impl1.max_arity()+1;
for (unsigned i = 0; i != size; ++i)
{
//check if the argument types are the same
if (s1[i].basename != s2[i].basename)
return false;
//return type
if (!i) continue;
//check if the argument default values are the same
bool f1_has_names = bool(f1->m_arg_names);
bool f2_has_names = bool(f2->m_arg_names);
if ( f1_has_names && f2_has_names && f2->m_arg_names[i-1]!=f1->m_arg_names[i-1]
|| f1_has_names && !f2_has_names
|| !f1_has_names && f2_has_names && f2->m_arg_names[i-1]!=python::object()
)
return false;
}
return true;
}
std::vector<function const*> function_doc_signature_generator::flatten(function const *f)
{
object name = f->name();
std::vector<function const*> res;
while (f) {
//this if takes out the not_implemented_function
if (f->name() == name)
res.push_back(f);
f=f->m_overloads.get();
}
//std::sort(res.begin(),res.end(), &arity_cmp);
return res;
}
std::vector<function const*> function_doc_signature_generator::split_seq_overloads( const std::vector<function const *> &funcs, bool split_on_doc_change)
{
std::vector<function const*> res;
std::vector<function const*>::const_iterator fi = funcs.begin();
function const * last = *fi;
while (++fi != funcs.end()){
//check if fi starts a new chain of overloads
if (!are_seq_overloads( last, *fi, split_on_doc_change ))
res.push_back(last);
last = *fi;
}
if (last)
res.push_back(last);
return res;
}
str function_doc_signature_generator::raw_function_pretty_signature(function const *f, size_t n_overloads, bool cpp_types )
{
str res("object");
res = str("%s %s(%s)" % make_tuple( res, f->m_name, str("tuple args, dict kwds")) );
return res;
}
const char * function_doc_signature_generator::py_type_str(const python::detail::signature_element &s)
{
if (s.basename==std::string("void")){
static const char * none = "None";
return none;
}
PyTypeObject const * py_type = s.pytype_f?s.pytype_f():0;
if ( py_type )
return py_type->tp_name;
else{
static const char * object = "object";
return object;
}
}
str function_doc_signature_generator::parameter_string(py_function const &f, size_t n, object arg_names, bool cpp_types)
{
str param;
python::detail::signature_element const * s = f.signature();
if (cpp_types)
{
if(!n)
s = &f.get_return_type();
if (s[n].basename == 0)
{
return str("...");
}
param = str(s[n].basename);
if (s[n].lvalue)
param += " {lvalue}";
}
else
{
if (n) //we are processing an argument and trying to come up with a name for it
{
object kv;
if ( arg_names && (kv = arg_names[n-1]) )
param = str( " (%s)%s" % make_tuple(py_type_str(s[n]),kv[0]) );
else
param = str(" (%s)%s%d" % make_tuple(py_type_str(s[n]),"arg", n) );
}
else //we are processing the return type
param = py_type_str(f.get_return_type());
}
//an argument - check for default value and append it
if(n && arg_names)
{
object kv(arg_names[n-1]);
if (kv && len(kv) == 2)
{
param = str("%s=%r" % make_tuple(param, kv[1]));
}
}
return param;
}
str function_doc_signature_generator::pretty_signature(function const *f, size_t n_overloads, bool cpp_types )
{
py_function
const& impl = f->m_fn;
;
unsigned arity = impl.max_arity();
if(arity == unsigned(-1))// is this the proper raw function test?
{
return raw_function_pretty_signature(f,n_overloads,cpp_types);
}
list formal_params;
size_t n_extra_default_args=0;
for (unsigned n = 0; n <= arity; ++n)
{
str param;
formal_params.append(
parameter_string(impl, n, f->m_arg_names, cpp_types)
);
// find all the arguments with default values preceeding the arity-n_overloads
if (n && f->m_arg_names)
{
object kv(f->m_arg_names[n-1]);
if (kv && len(kv) == 2)
{
//default argument preceeding the arity-n_overloads
if( n <= arity-n_overloads)
++n_extra_default_args;
}
else
//argument without default, preceeding the arity-n_overloads
if( n <= arity-n_overloads)
n_extra_default_args = 0;
}
}
n_overloads+=n_extra_default_args;
if (!arity && cpp_types)
formal_params.append("void");
str ret_type (formal_params.pop(0));
if (cpp_types )
{
return str(
"%s %s(%s%s%s%s)"
% make_tuple
( ret_type
, f->m_name
, str(",").join(formal_params.slice(0,arity-n_overloads))
, n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str()
, str(" [,").join(formal_params.slice(arity-n_overloads,arity))
, std::string(n_overloads,']')
));
}else{
return str(
"%s(%s%s%s%s) -> %s"
% make_tuple
( f->m_name
, str(",").join(formal_params.slice(0,arity-n_overloads))
, n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str()
, str(" [,").join(formal_params.slice(arity-n_overloads,arity))
, std::string(n_overloads,']')
, ret_type
));
}
return str(
"%s %s(%s%s%s%s) %s"
% make_tuple
( cpp_types?ret_type:str("")
, f->m_name
, str(",").join(formal_params.slice(0,arity-n_overloads))
, n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str()
, str(" [,").join(formal_params.slice(arity-n_overloads,arity))
, std::string(n_overloads,']')
, cpp_types?str(""):ret_type
));
}
namespace detail {
char py_signature_tag[] = "PY signature : ";
char cpp_signature_tag[] = "C++ signature:";
}
list function_doc_signature_generator::function_doc_signatures( function const * f)
{
list signatures;
std::vector<function const*> funcs = flatten( f);
std::vector<function const*> split_funcs = split_seq_overloads( funcs, true);
std::vector<function const*>::const_iterator sfi=split_funcs.begin(), fi;
size_t n_overloads=0;
for (fi=funcs.begin(); fi!=funcs.end(); ++fi)
{
if(*sfi == *fi){
if((*fi)->doc()){
str func_doc = str((*fi)->doc());
int doc_len = len(func_doc);
bool show_py_signature = doc_len >=int(sizeof(detail::py_signature_tag)/sizeof(char)-1)
&& str(detail::py_signature_tag)==func_doc.slice(0, int(sizeof(detail::py_signature_tag)/sizeof(char))-1);
bool show_cpp_signature = doc_len >=int(sizeof(detail::cpp_signature_tag)/sizeof(char))
&& str(detail::cpp_signature_tag)==func_doc.slice(- int(sizeof(detail::cpp_signature_tag)/sizeof(char))+1, _);
str res;
if(show_py_signature)
{
str sig = pretty_signature(*fi, n_overloads,false);
res+=sig;
if(doc_len > int(sizeof(detail::py_signature_tag)/sizeof(char))-1 )
res+=" : "+func_doc.slice(int(sizeof(detail::py_signature_tag)/sizeof(char))-1,_);
}else
res+=func_doc;
if( show_cpp_signature)
res+=str("\n ")+pretty_signature(*fi, n_overloads,true);
signatures.append(res);
}
++sfi;
n_overloads = 0;
}else
++n_overloads ;
}
return signatures;
}
}}}

View File

@@ -349,5 +349,15 @@ BOOST_PYTHON_DEFINE_STR_METHOD(title, 0)
BOOST_PYTHON_DEFINE_STR_METHOD(translate, 1)
BOOST_PYTHON_DEFINE_STR_METHOD(translate, 2)
BOOST_PYTHON_DEFINE_STR_METHOD(upper, 0)
static struct register_str_pytype_ptr
{
register_str_pytype_ptr()
{
const_cast<converter::registration &>(
converter::registry::lookup(boost::python::type_id<boost::python::str>())
).m_class_object = &PyString_Type;
}
}register_str_pytype_ptr_;
}}} // namespace boost::python

View File

@@ -21,4 +21,15 @@ tuple_base::tuple_base(object_cref sequence)
: object(call(sequence))
{}
static struct register_tuple_pytype_ptr
{
register_tuple_pytype_ptr()
{
const_cast<converter::registration &>(
converter::registry::lookup(boost::python::type_id<boost::python::tuple>())
).m_class_object = &PyTuple_Type;
}
}register_tuple_pytype_ptr_;
}}} // namespace boost::python