[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: Proof of concept higher-level python bindings for SoC project

From: David James <james_at_cs.toronto.edu>
Date: 2007-04-07 00:47:31 CEST

> > - Our SWIG bindings convert "svn_error_t" return codes into exceptions
>
> I personally consider this a feature. In Python, it's really Pythonic that
> errors are reported in form of exceptions. Having to look at the error code of
> each function I call is something I wouldn't expect from a Python library.

To be perfectly clear, I don't actually care whether our low-level C
wrapper bindings are Pythonic -- I just want to be able to do the same
things in Python that I can do in C, so that I can implement a
higher-level Pythonic API, which is very friendly to users, and
requires no knowledge of the Subversion C API or ctypes.

That said, in our higher level Python bindings API, we will definitely
need to throw exceptions, instead of returning error codes, because
that is the Python way. For this purpose, I've already written a
wrapper "SubversionException" class which converts a ctypes
"svn_error_t" class into a friendly Python exception.

Right now, our higher-level API checks the return code of every
function it calls, and throws a SubversionException if necessary.
Right now, we do this check every time we call a C-level function
using a simple if-statement, but it'd be simpler to delegate this work
to a function.

Should we write some code to edit lower-level API functions and
upgrade them so that they throw exceptions instead of returning error
codes? I think that this is a good idea, as long as this difference is
carefully documented.

Eric Gillespie wrote:
> Maybe you'll fix a deficiency in the current bindings in the
> process. svn_error_t can be stacked, but you only get the
> outermost svn_error_t in the SubversionException.

Unlike the SWIG bindings, the ctypes bindings do wrap the entire error
stack. Further, any error messages which are printed should be the
same as the output of our svn_handle_error2 function.

> > - Our SWIG bindings convert APR arrays and APR hashes into Python
> > arrays and Python hashes, if you write a whole bunch of boilerplate
> > typemap code.
>
> I guess that depends on how well you manage to wrap APR data structures in
> Python. My own personal preference is to provide a wrapper class built over
> the C-level APR functions, and making the class fully duckable with regular
> Python lists and maps (as much as possible). This lets the users write natural
> Python code to manage APR containers, and also allow maximum performance
> because data structures must not be converted every time between Python and APR.

Our higher-level Python bindings will certainly allow users to use
friendly Python lists and dictionaries, and will convert these Python
datatypes automatically into the appropriate APR datastructures.

It's tempting for me to consider providing some automatic type
conversion in the low-level ctypes bindings, as well, but I'd like to
stay away from this: I think that any type conversions in our
low-level bindings should only happen when explicitly requested, so
that we don't make our Python bindings too "magical" and confusing,
like our SWIG bindings.

We can provide easy convenience functions which convert between the
following classes:
   - apr_array_header_t <-> Python list
   - apr_hash_t <-> Python dictionary
   - svn_string_t <-> Python string
   - svn_stringbuf_t <-> Python string

It'd be possible for us to write a few scripts which automatically
apply the above conversions (like SWIG typemaps) to all of the
functions in the low-level API. Unfortunately, however, automatic
conversion is a tricky business. For example, if we convert a Python
list into an APR array, and then we call a function which modifies
that array, how can we ensure that the changes to the array are
propagated back to the original list? If the low-level developers do
this conversion manually, they can specifically request this check,
but it's a lot trickier for us to trigger this conversion
automatically.

I'd like to stay away from automatic type conversions, and instead
convert the types manually in the higher-level layer by calling the
appropriate conversion functions. It really doesn't take a lot of
typing to do this, and it's a lot safer than relying on broad-brush
generated code.

> > - Our SWIG bindings convert some pointer types into output
> > arguments, if you write the necessary typemap code to do this
> > conversion.
>
> Well, given that Python support multiple return types, I guess that's really
> necessary. How are you going to, eg., wrap a C function that returns an
> integer by the means of a pointer (given that the 'int' instances are
> immutable)? Leaking ctypes' specific types like c_int() into Python code
> doesn't feel like a good design either. You don't buy much in this case by
> forcing the user not to use natural data types, and make them goo "eewww" when
> they get weird exceptions because of c_int leaking around.

In the high-level bindings, we will certainly return output arguments
where appropriate, and will never force users to use or accept
ctypes-specific stuff like c_int().

I don't think that our low-level API should automatically, for
example, convert indirect pointers into return values, because this
isn't a safe conversion in general, and it's especially confusing for
users when we do the conversion automatically but wrong. I'd rather do
this conversion manually.

> > After working with ctypes for a while, I don't miss any of the
> > features of our SWIG bindings. In fact, I actually am happy that the
> > ctypes low-level API is exactly the same as our Subversion C API,
> > because it is easier for me to understand -- I don't need to think
> > about typemaps when I read the doxygen documentation.
>
> Remember that the Subversion API is not really Pythonic, nor very clear or
> well documented for a newbie. If your target are Subversion C programmers
> moving to Python, that'd would be a nice task. But you're doing a proof of
> concept for a "higher-level" API, so sticking **too much** to the C API
> doesn't look like something that should be preserved.

I certainly agree with you -- the Subversion API is not Pythonic. For
this reason, I recommend writing higher-level wrapper classes (written
in pure Python) for all of the important concepts in Subversion. These
pure Python classes will stick to a friendly Python interface. My
"Repos" class is an example of a friendly Python class.

That said, the higher-level API probably won't ever cover every
function that Subversion offers. For that reason, it's nice that users
can call the low-level functions directly if they like.

> > In my opinion, ctypes is much, much better than SWIG for what we want
> > to do.
>
> I mostly agree, based on previous experience with both tools (and similar
> tools). Of course, SWIG (and SWIG-like tools) are still useful for
> cross-language bindings (if that's a plus), and for C++ APIs (which Subversion
> hasn't got).
>
> HTH!

Thanks for your help!

Cheers,

David

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sat Apr 7 00:47:44 2007

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.