Return-Path: X-Original-To: notmuch@notmuchmail.org Delivered-To: notmuch@notmuchmail.org Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id D052E431FBC for ; Wed, 30 Dec 2009 02:51:18 -0800 (PST) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org Received: from olra.theworths.org ([127.0.0.1]) by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id qAbyH4mrVSfT for ; Wed, 30 Dec 2009 02:51:16 -0800 (PST) Received: from alice.connectical.com (alice.connectical.com [208.89.208.235]) by olra.theworths.org (Postfix) with ESMTP id A2D76431FAE for ; Wed, 30 Dec 2009 02:51:16 -0800 (PST) Received: (qmail 20357 invoked from network); 30 Dec 2009 10:51:07 -0000 Received: from 97.126.60.213.dynamic.mundo-r.com (HELO hikari.localdomain) (aperez@213.60.126.97) by alice.connectical.com with ESMTPA; 30 Dec 2009 10:51:07 -0000 Received: from hikari (localhost [127.0.0.1]) by hikari.localdomain (Postfix) with ESMTP id 8360330A5D47 for ; Wed, 30 Dec 2009 11:52:28 +0100 (CET) Date: Wed, 30 Dec 2009 11:52:23 +0100 From: Adrian Perez de Castro To: notmuch@notmuchmail.org Message-ID: <20091230115223.1b3472a1@hikari> In-Reply-To: <1262078148-sup-7891@ben-laptop> References: <1262078148-sup-7891@ben-laptop> Organization: Igalia X-Mailer: Claws Mail 3.7.3 (GTK+ 2.19.2; x86_64-redhat-linux-gnu) Face: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAAXNSR0IArs4c6QAAADBQTFRFBwcHFhYWKCgoNzc3SEhIV1dXaGhod3d3iIiIlpaWqKiouLi4x8fH2NjY5+fn/v7+rSjDkgAAAjVJREFUOE9l07tvE0EQwOHfrkV9O+eko7g701BBfECJsIigT2IpooIqaSiRUEB0REj00FBQgYSCkhry+gecUPJybJeIxLumTbilsH2PMNXufDOa3ZVW+1JkpbUmD/8+vXR3c7or4Gz93mH309Kz8/C9/RQge7VfhW/LW+PF8IkrQ7Z6OKmQr1tl+LU/yWP9mxJka9O88fZHPwf/7u0kLyCnX3I4fQhgjAgIfi+HHw5A1Y2ggIMcFKAEnRoL0M3BosI4TI2IATjuT8DvSNJoNNJgkIhxlr9TUHeSpDnfohlIrMBlU+BGmsZqfr69FMfGMw4NoG835+J62riWyjQ/uXlTQjNUIoYegMsBM0pCD8oDas7n4HQsBghXFxJTW42KDs+4XLfjsN0wOYgABqARjMKIHIaAQnmHjsI5Cvi9Cf6k03OoWBkpIP3Q7354+dEimFBKHbMP9oKjwfd9gbrxR5KDToczK4uPF8UgNomKU2GaENRi77zyDKICxKBS4xXYbONPMQMdYZTBwMiMWiUg9g6UJ3OBogzjV8E7sBVwyvfAOYdQhsABzuOxI1MGZbs98Q6Md5UOfbbR2R0eWOesrnRw5ajT6f60LrNhWIHZpBnUWv2s14ukArWWTqTes3YQxRXgFkcMu70TPYqqUBs0YwmO967OVIdTG4bY4a7WLaqgLm5vbHdH5np0Dri//fmg7y8scB4u3+zsuNlH0X+g19bby69b+TYH6isvns8VdQWgxj9tHP8AR5/hSdYqkwsAAAAASUVORK5CYII= Mime-Version: 1.0 Content-Type: multipart/signed; micalg=PGP-SHA1; boundary="Sig_/NJQn5hRW+avHgHHAlU7=Xa1"; protocol="application/pgp-signature" Subject: Re: [notmuch] SWIG (and particularly Python) bindings X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 30 Dec 2009 10:51:19 -0000 --Sig_/NJQn5hRW+avHgHHAlU7=Xa1 Content-Type: multipart/mixed; boundary="MP_/Qt51GuWb3Bo1f8da6MLGH4N" --MP_/Qt51GuWb3Bo1f8da6MLGH4N Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: quoted-printable Content-Disposition: inline On Tue, 29 Dec 2009 04:16:43 -0500, Ben wrote: > Regardless, I thought it might be nice to have access to the notmuch > backend from a language other than C (preferably my high-level language > of choice, python) [...] Funny, I was just doing the same: a Python binding. Haha, so now we have two just-backed Python bindings. What should we do? > [...] To this end, I took a few hours today acquainting > myself with SWIG and produced these bindings for Python. Unfortunately, > it doesn't appear that SWIG has particularly good support for > object-oriented C [...] I already used SWIG sometimes in the past (and did not like it a lot), so my binding is using Cython [*] (which is exactly like Pyrex plus some extra features), so the binding is partly manual. > While the bindings are currently in the form of a patch to notmuch > (creating a top-level swig directory in the source tree), they could > certainly be moved out-of-tree if the powers that be don't feel it > appropriate to include them. [...] Same here, see attached patch. It is currently unfinished, and I was just about to add support for iterating notmuch_threads_t and other similar structures. I can also publish a Git repo with the entire branch, just drop me a line if you want me to do that. > [...] Unfortunately, the build system is currently almost entirely > independent from the notmuch build system. If these are to be > included in-tree, I would be curious to hear people have > to say about how we might integrate it into the sup build system. ^^^ (Mmmh, I suppose you mean "notmuch build system" there :P) Mine is a little more cooked, as I have added a distutils "setup.py" script. The bad news is that Python modules need to be compiled as relocatable object files (-fPIC to teh rescue!), and the linker will refuse to link the generated code with "notmuch.a" -- so I am instructing distutils to compile *all* sources again. Not nice. BTW, I think that if more bindings start to appear, Notmuch might be built as a shared library, to avoid duplicating it everywhere. One option may be using *just* libtool but not the rest of auto-foo tools (for the record: I agree with Carl that they are slow and wicked). Regards, [*] http://www.cython.org/ --=20 Adrian Perez de Castro Igalia - Free Software Engineering --MP_/Qt51GuWb3Bo1f8da6MLGH4N Content-Type: text/x-patch Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename=notmuch-python-wip.patch Makefile | 1 + python/.gitignore | 2 + python/Makefile | 6 + python/Makefile.local | 15 ++ python/notmuch.pyx | 397 +++++++++++++++++++++++++++++++++++++++++++++= ++++ python/pyutil.h | 16 ++ python/setup.py | 89 +++++++++++ 7 files changed, 526 insertions(+), 0 deletions(-) diff --git a/Makefile b/Makefile index 021fdb8..081d670 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,7 @@ include Makefile.config =20 include lib/Makefile.local include compat/Makefile.local +include python/Makefile.local include Makefile.local =20 # The user has not set any verbosity, default to quiet mode and inform the diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 0000000..7f0efa8 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,2 @@ +notmuch.c +build/ diff --git a/python/Makefile b/python/Makefile new file mode 100644 index 0000000..e1e5c43 --- /dev/null +++ b/python/Makefile @@ -0,0 +1,6 @@ + +all: python + +%: + make -C .. $@ + diff --git a/python/Makefile.local b/python/Makefile.local new file mode 100644 index 0000000..140a701 --- /dev/null +++ b/python/Makefile.local @@ -0,0 +1,15 @@ +dir=3Dpython + +python: $(dir)/build/.stamp + (cd $(dir) && python setup.py build) + touch $@ + +$(dir)/build/.stamp: lib/notmuch.a + +clean: clean-python + +clean-python: + $(RM) -r $(dir)/build + +.PHONY: clean-python python + diff --git a/python/notmuch.pyx b/python/notmuch.pyx new file mode 100644 index 0000000..f38b719 --- /dev/null +++ b/python/notmuch.pyx @@ -0,0 +1,397 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# vim: fenc=3Dutf-8 ft=3Dpyrex +# +# Copyright =C2=A9 2009 Adrian Perez +# +# Distributed under terms of the GPLv3 license. +# + +cdef extern from "talloc.h": + void* talloc_init(char *fmt, ...) + int talloc_free(void *ctx) + + +cdef extern from "pyutil.h": + # + # Utility macros + # + char** pyutil_alloc_strv(void *ctx, unsigned nitems) + + +cdef extern from "notmuch.h": + # + # Return status handling + # + ctypedef enum notmuch_status_t: + NOTMUCH_STATUS_SUCCESS + NOTMUCH_STATUS_OUT_OF_MEMORY + NOTMUCH_STATUS_READONLY_DATABASE + NOTMUCH_STATUS_XAPIAN_EXCEPTION + NOTMUCH_STATUS_FILE_ERROR + NOTMUCH_STATUS_FILE_NOT_EMAIL + NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID + NOTMUCH_STATUS_NULL_POINTER + NOTMUCH_STATUS_TAG_TOO_LONG + NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW + + char* notmuch_status_to_string(notmuch_status_t status) + + # + # notmuch_database_* -> notmuch.Database + # + ctypedef enum notmuch_database_mode_t: + NOTMUCH_DATABASE_MODE_READ_ONLY + NOTMUCH_DATABASE_MODE_READ_WRITE + + ctypedef enum notmuch_sort_t: + NOTMUCH_SORT_OLDEST_FIRST + NOTMUCH_SORT_NEWEST_FIRST + NOTMUCH_SORT_MESSAGE_ID + + ctypedef struct notmuch_database_t + ctypedef struct notmuch_messages_t + ctypedef struct notmuch_message_t + ctypedef struct notmuch_threads_t + ctypedef struct notmuch_thread_t + ctypedef struct notmuch_query_t + ctypedef struct notmuch_tags_t + ctypedef int time_t + + int notmuch_threads_has_more(notmuch_threads_t *threads) + void notmuch_threads_advance(notmuch_threads_t *threads) + void notmuch_threads_destroy(notmuch_threads_t *threads) + notmuch_thread_t* notmuch_threads_get(notmuch_threads_t *threads) + + void notmuch_thread_destroy(notmuch_thread_t *thread) + char* notmuch_thread_get_authors(notmuch_thread_t *thread) + char* notmuch_thread_get_subject(notmuch_thread_t *thread) + char* notmuch_thread_get_thread_id(notmuch_thread_t *thread) + time_t notmuch_thread_get_oldest_date(notmuch_thread_t *thread) + time_t notmuch_thread_get_newest_date(notmuch_thread_t *thread) + int notmuch_thread_get_total_messages(notmuch_thread_t *thread) + int notmuch_thread_get_matched_messages(notmuch_thread_t *thread) + notmuch_tags_t* notmuch_thread_get_tags(notmuch_thread_t *thread) + notmuch_messages_t* notmuch_thread_get_toplevel_messages(notmuch_thread_t= *thread) + + notmuch_database_t* notmuch_database_create(char *path) + notmuch_database_t* notmuch_database_open(char *path, notmuch_database_mo= de_t mode) + char* notmuch_database_get_path(notmuch_database_t *db) + void notmuch_database_close(notmuch_database_t *db) + + time_t notmuch_database_get_timestamp(notmuch_database_t *db, char *key) + notmuch_status_t notmuch_database_set_timestamp( + notmuch_database_t *db, char *key, time_t timestamp) + + notmuch_status_t notmuch_database_add_message( + notmuch_database_t *db, char *filename, notmuch_message_t **mesage) + + notmuch_message_t* notmuch_database_find_message( + notmuch_database_t *db, char *message_id) + + notmuch_tags_t* notmuch_database_get_all_tags(notmuch_database_t *db) + + notmuch_query_t* notmuch_query_create(notmuch_database_t *db, char *qstri= ng) + + void notmuch_query_destroy(notmuch_query_t *query) + void notmuch_query_set_sort(notmuch_query_t *query, notmuch_sort_t sort) + unsigned notmuch_query_count_messages(notmuch_query_t *query) + notmuch_threads_t* notmuch_query_search_threads(notmuch_query_t *query) + notmuch_messages_t* notmuch_query_search_messages(notmuch_query_t *query) + + char* notmuch_message_get_message_id(notmuch_message_t *msg) + char* notmuch_message_get_thread_id(notmuch_message_t *msg) + char* notmuch_message_get_filename(notmuch_message_t *msg) + char* notmuch_message_get_header(notmuch_message_t *msg, char *name) + notmuch_status_t notmuch_message_add_tag(notmuch_message_t *msg, char *ta= g) + notmuch_status_t notmuch_message_remove_tag(notmuch_message_t *msg, char = *tag) + void notmuch_message_remove_all_tags(notmuch_message_t *msg) + void notmuch_message_destroy(notmuch_message_t *msg) + void notmuch_message_freeze(notmuch_message_t *msg) + void notmuch_message_thaw(notmuch_message_t *msg) + + +cdef extern from "notmuch-client.h": + # + # notmuch_config_* -> notmuch.Config + # + ctypedef struct notmuch_config_t + + notmuch_config_t* notmuch_config_open(void *ctx, char *filename, int *is_= new_ret) + void notmuch_config_close(notmuch_config_t *cfg) + int notmuch_config_save(notmuch_config_t *cfg) + char* notmuch_config_get_database_path(notmuch_config_t *cfg) + void notmuch_config_set_database_path(notmuch_config_t *cfg, char *path) + char* notmuch_config_get_user_name(notmuch_config_t *cfg) + void notmuch_config_set_user_name(notmuch_config_t *cfg, char *name) + char* notmuch_config_get_user_primary_email(notmuch_config_t *cfg) + void notmuch_config_set_user_primary_email(notmuch_config_t *cfg, char = *email) + char** notmuch_config_get_user_other_email(notmuch_config_t *cfg, size_t = *length) + void notmuch_config_set_user_other_email(notmuch_config_t *cfg, char **= other_email, size_t length) + + # + # Miscellaneous + # + int debugger_is_active() + + +# +# Import needed Python built-in modules +# +from datetime import datetime, date + +# +# Miscellaneous functions and information +# +debugger_active =3D bool(debugger_is_active()) + + +# +# notmuch_database_* -> notmuch.Database +# + + +# +# notmuch_config_* -> notmuch.Config +# +cdef class Config: + """Handles the Notmuch configuration file. + """ + cdef notmuch_config_t * _cfg + cdef void * _ctx + cdef object _filename + + def __init__(self, filename=3D"~/.notmuch-config"): + """Open a Notmuch configuration file. + """ + cdef int newret + self._ctx =3D talloc_init("notmuch.Config") + self._cfg =3D notmuch_config_open(self._ctx, filename, &newret) + self._filename =3D filename + + def __dealloc__(self): + notmuch_config_close(self._cfg) + talloc_free(self._ctx) + + property filename: + """File name containing the configuration (string)""" + def __get__(self): + return self._filename + + property database_path: + """Path to the Notmuch database (string)""" + def __get__(self): + return notmuch_config_get_database_path(self._cfg) + def __set__(self, path): + notmuch_config_set_database_path(self._cfg, path) + + property user_name: + """User name (string)""" + def __get__(self): + return notmuch_config_get_user_name(self._cfg) + def __set__(self, name): + notmuch_config_set_user_name(self._cfg, name) + + property user_primary_email: + """Primary e-mail address of the user (string)""" + def __get__(self): + return notmuch_config_get_user_primary_email(self._cfg) + def __set__(self, email): + notmuch_config_set_user_primary_email(self._cfg, email) + + property other_email: + """List of other e-mail addresses of the user (tuple of strings)""" + def __get__(self): + cdef size_t length + cdef size_t i + cdef char **emails + emails =3D notmuch_config_get_user_other_email(self._cfg, &length) + + result =3D [] + for i from 0 <=3D i < length: + result.append(emails[i]) + + # XXX We do not want the result to be modifiable, because the property + # must be assigned as a whole, and not just modified only in the + # Python side of the world. + return tuple(result) + + def __set__(self, emaillist): + cdef size_t length =3D len(emaillist) + cdef char **emails =3D pyutil_alloc_strv(self._ctx, length) + cdef size_t i + + for i from 0 <=3D i < len(emaillist): + emails[i] =3D emaillist[i] + notmuch_config_set_user_other_email(self._cfg, emails, len(emaillist)) + + + def save(self): + """Save the Notmuch configuration""" + notmuch_config_save(self._cfg) + + +cdef class Database + + +cdef class Message: + cdef notmuch_message_t *_msg + + def __cinit__(self, object messageptr): + # XXX Counterpart of bogus cast + self._msg =3D messageptr + + def __dealloc__(self): + notmuch_message_destroy(self._msg) + + property message_id: + def __get__(self): + return notmuch_message_get_message_id(self._msg) + + property thread_id: + def __get__(self): + return notmuch_message_get_thread_id(self._msg) + + property filename: + def __get__(self): + return notmuch_message_get_filename(self._msg) + + def get_header(self, name): + return notmuch_message_get_header(self._msg, name) + + def add_tag(self, tag): + cdef notmuch_status_t ret =3D notmuch_message_add_tag(self._msg, tag) + if ret !=3D NOTMUCH_STATUS_SUCCESS: + raise ValueError(notmuch_status_to_string(ret)) + + def remove_tag(self, tag): + cdef notmuch_status_t ret =3D notmuch_message_remove_tag(self._msg, tag) + if ret !=3D NOTMUCH_STATUS_SUCCESS: + raise ValueError(notmuch_status_to_string(ret)) + + def remove_all_tags(self): + notmuch_message_remove_all_tags(self._msg) + + def freeze(self): + notmuch_message_freeze(self._msg) + + def thaw(self): + notmuch_message_thaw(self._msg) + + +cdef class Thread: + cdef notmuch_thread_t *_thread + + def __cinit__(self, object threadptr): + self._thread =3D threadptr + + def __dealloc__(self): + notmuch_thread_destroy(self._thread) + + property authors: + def __get__(self): + return notmuch_thread_get_authors(self._thread) + + property subject: + def __get__(self): + return notmuch_thread_get_subject(self._thread) + + property thread_id: + def __get__(self): + return notmuch_thread_get_thread_id(self._thread) + + property oldest_date: + def __get__(self): + return datetime.fromtimestamp(notmuch_thread_get_oldest_date(self._thre= ad)) + + property newest_date: + def __get__(self): + return datetime.fromtimestamp(notmuch_thread_get_newest_date(self._thre= ad)) + + property total_messages: + def __get__(self): + return notmuch_thread_get_total_messages(self._thread) + + property matched_messages: + def __get__(self): + return notmuch_thread_get_matched_messages(self._thread) + + def __len__(self): + return self.matched_messages + + +cdef class Query: + cdef notmuch_query_t *_query + + def __cinit__(self, Database db not None, qs): + self._query =3D notmuch_query_create(db._db, qs) + + def __dealloc__(self): + notmuch_query_destroy(self._query) + + def __len__(self): + return notmuch_query_count_messages(self._query) + + def sort(self, notmuch_sort_t ordering): + notmuch_query_set_sort(self._query, ordering) + return self + + + +cdef class Database: + cdef notmuch_database_t *_db + + def __init__(self, path=3DNone, readonly=3DTrue): + cdef notmuch_database_mode_t mode =3D NOTMUCH_DATABASE_MODE_READ_WRITE + if path is None: + path =3D Config().database_path + if readonly: + mode =3D NOTMUCH_DATABASE_MODE_READ_ONLY + self._db =3D notmuch_database_open(path, mode) + + def __dealloc__(self): + notmuch_database_close(self._db) + + def get_timestamp(self, key): + cdef time_t ts =3D notmuch_database_get_timestamp(self._db, key) + return datetime.fromtimestamp(ts) + + def set_timestamp(self, key, timestamp): + cdef time_t ts + if isinstance(timestamp, date): + ts =3D int(timestamp.strftime("%s")) + elif isinstance(timestamp, float): + ts =3D timestamp + elif isinstance(timestamp, int): + ts =3D timestamp + else: + raise ValueError("Numeric timestamp or datetime.date object expected") + + def add_message(self, filename): + cdef notmuch_message_t *message =3D NULL + cdef notmuch_status_t status + status =3D notmuch_database_add_message(self._db, filename, &message) + if status =3D=3D NOTMUCH_STATUS_SUCCESS: + # XXX This cast seems bogus, it may work, though + return Message( message) + else: + if message !=3D NULL: + notmuch_message_destroy(message) + raise ValueError(notmuch_status_to_string(status)) + + def find_message(self, message_id): + cdef notmuch_message_t *message + message =3D notmuch_database_find_message(self._db, message_id) + if message =3D=3D NULL: + raise KeyError(message_id) + return Message( message) + + property path: + """Database path""" + def __get__(self): + return notmuch_database_get_path(self._db) + + def query(self, qstring): + return Query(self, qstring) + + diff --git a/python/pyutil.h b/python/pyutil.h new file mode 100644 index 0000000..64d93bf --- /dev/null +++ b/python/pyutil.h @@ -0,0 +1,16 @@ +/* + * pyutil.h + * Copyright (C) 2009 Adrian Perez + * + * Distributed under terms of the GPLv3 license. + */ + +#ifndef __pyutil_h__ +#define __pyutil_h__ + +#include + +#define pyutil_alloc_strv(_ctx, _n) talloc_array ((_ctx), char*, (_n)) + +#endif /* !__pyutil_h__ */ + diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 0000000..ffd43b2 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,89 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# vim:fenc=3Dutf-8 +# +# Copyright =C2=A9 2009 Adrian Perez +# +# Distributed under terms of the GPLv3 license. + +from distutils.core import setup +from distutils.extension import Extension +from Cython.Distutils import build_ext +import commands + + +class FlagOMatic(dict): + _KEYS =3D ("extra_link_args", "include_dirs", "library_dirs", "librari= es") + + def __init__(self, *arg, **kw): + super(FlagOMatic, self).__init__(*arg, **kw) + for key in self._KEYS: + self[key] =3D set(self.get(key, [])) + + extra_link_args =3D property(lambda self: self["extra_link_args"]) + include_dirs =3D property(lambda self: self["include_dirs"]) + library_dirs =3D property(lambda self: self["library_dirs"]) + libraries =3D property(lambda self: self["libraries"]) + + _FLAG_MAP =3D { + "-I": "include_dirs", + "-L": "library_dirs", + "-l": "libraries", + } + + def add_compiler_flags(self, text): + for token in text.split(): + key =3D self._FLAG_MAP.get(token[:2], "extra_link_args") + if key =3D=3D "extra_link_args": + self.extra_link_args.add(token) + else: + self[key].add(token[2:]) + + def add_pkgconfig_flags(self, *modules): + self.add_command_output_flags( + "pkg-config --libs --cflags %s" % " ".join(modules)) + + def add_command_output_flags(self, command): + self.add_compiler_flags(commands.getoutput(command)) + + @property + def kwargs(self): + return dict((k, list(v)) for k, v in self.iteritems()) + + +# Gather compiler flags +# +flags =3D FlagOMatic( + #extra_link_args =3D ("../lib/notmuch.a",), + include_dirs =3D ("..", "../lib", "../compat")) + +flags.add_pkgconfig_flags("gmime-2.4", "talloc") +flags.add_command_output_flags("xapian-config --cxxflags --libs") + +# We are building a extension module +# +import os + +srcs =3D ["notmuch.pyx"] +srcs.extend( + map(lambda s: "../"+s, + filter(lambda s: s.endswith(".c"), + os.listdir("..")))) +srcs.extend( + map(lambda s: "../lib/"+s, + filter(lambda s: s.endswith(".cc") or s.endswith(".c"), + os.listdir("../lib")))) + +ext_modules =3D [Extension("notmuch", srcs, **flags.kwargs)] + + +# And now for the easy part :-) +# +setup( + name =3D "notmuch", + author =3D "Adrian Perez", + author_email =3D "aperez@igalia.com", + cmdclass =3D {"build_ext": build_ext}, + ext_modules =3D ext_modules, +) + --MP_/Qt51GuWb3Bo1f8da6MLGH4N-- --Sig_/NJQn5hRW+avHgHHAlU7=Xa1 Content-Type: application/pgp-signature; name=signature.asc Content-Disposition: attachment; filename=signature.asc -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.13 (GNU/Linux) iEYEARECAAYFAks7MOsACgkQkcVZ2+TJEju5RACfQAjq3L+//wNVYgB+05f2OmI7 9P4AmgKqBUP79jj4EMuuNit64u94aj3q =RI1/ -----END PGP SIGNATURE----- --Sig_/NJQn5hRW+avHgHHAlU7=Xa1--