Commit 50a25535 authored by Denis Bilenko's avatar Denis Bilenko

move c-ares wrappers from gevent.core to gevent.ares; make it optional in setup.py

in gevent.ares module:
 - rename ares_channel to channel
 - rename ares_strerror() to ares.strerror()
 - remove _strerror()
 - do not use gevent_handle_error C function, use loop.handle_error
 - do not use loop and io watcher definitions (use Python calls instead of fast vtab calls)

add python.pxd with a few common python functions
do not include c-ares stuff, compile it separately
add inet_ntop.c to gevent/ since it's not exported by c-ares and we use it

make ares extension optional - retry setup.py command if it failed

--HG--
rename : gevent/cares.pxi => gevent/ares.pyx
parent 73b25755
cimport cares
import sys
from python cimport *
from gevent.core import EVENTS
from gevent.core import io, loop
DEF EV_READ = 1
DEF EV_WRITE = 1
cdef extern from "dnshelper.c":
......@@ -28,9 +35,6 @@ cdef extern from "dnshelper.c":
void free(void*)
void memset(void*, int, int)
cdef extern from "callbacks.h":
void gevent_handle_error(void* loop, void* callback)
ARES_SUCCESS = cares.ARES_SUCCESS
ARES_ENODATA = cares.ARES_ENODATA
......@@ -123,19 +127,15 @@ cpdef _convert_cares_flags(int flags, int default=cares.ARES_NI_LOOKUPHOST|cares
raise get_socket_gaierror()(-1, "Bad value for ai_flags: 0x%x" % flags)
def _ares_strerror(code):
return cares.ares_strerror(code)
cpdef ares_strerror(code):
cpdef strerror(code):
return '%s: %s' % (_ares_errors.get(code) or code, cares.ares_strerror(code))
cdef void gevent_sock_state_callback(void *data, int s, int read, int write):
if not data:
return
cdef ares_channel channel = <ares_channel>data
channel._sock_state_callback(s, read, write)
cdef channel ch = <channel>data
ch._sock_state_callback(s, read, write)
cdef class result:
......@@ -153,6 +153,7 @@ cdef class result:
return '%s(exception=%r)' % (self.__class__.__name__, self.exception)
else:
return '%s(value=%r, exception=%r)' % (self.__class__.__name__, self.value, self.exception)
# add repr_recursive precaution
def successful(self):
return self.exception is None
......@@ -172,14 +173,14 @@ class ares_host_result(tuple):
cdef void gevent_ares_host_callback(void *arg, int status, int timeouts, hostent* host):
cdef ares_channel channel
cdef channel channel
cdef object callback
channel, callback = <tuple>arg
Py_DECREF(<PyObjectPtr>arg)
cdef object host_result
try:
if status or not host:
callback(result(None, get_socket_gaierror()(status, ares_strerror(status))))
callback(result(None, get_socket_gaierror()(status, strerror(status))))
else:
try:
host_result = ares_host_result(host.h_addrtype, (host.h_name, parse_h_aliases(host), parse_h_addr_list(host)))
......@@ -188,11 +189,11 @@ cdef void gevent_ares_host_callback(void *arg, int status, int timeouts, hostent
else:
callback(result(host_result))
except:
gevent_handle_error(<void*>channel.loop, <void*>callback)
channel.loop.handle_error(callback, *sys.exc_info())
cdef void gevent_ares_nameinfo_callback(void *arg, int status, int timeouts, char *c_node, char *c_service):
cdef ares_channel channel
cdef channel channel
cdef object callback
channel, callback = <tuple>arg
Py_DECREF(<PyObjectPtr>arg)
......@@ -200,7 +201,7 @@ cdef void gevent_ares_nameinfo_callback(void *arg, int status, int timeouts, cha
cdef object service
try:
if status:
callback(result(None, get_socket_gaierror()(status, ares_strerror(status))))
callback(result(None, get_socket_gaierror()(status, strerror(status))))
else:
if c_node:
node = PyString_FromString(c_node)
......@@ -212,16 +213,16 @@ cdef void gevent_ares_nameinfo_callback(void *arg, int status, int timeouts, cha
service = None
callback(result((node, service)))
except:
gevent_handle_error(<void*>channel.loop, <void*>callback)
channel.loop.handle_error(callback, *sys.exc_info())
cdef public class ares_channel [object PyGeventAresChannelObject, type PyGeventAresChannel_Type]:
cdef public class channel [object PyGeventAresChannelObject, type PyGeventAresChannel_Type]:
cdef public loop loop
cdef public object loop
cdef void* channel
cdef public dict _watchers
def __init__(self, loop loop, flags=None, timeout=None, tries=None, ndots=None,
def __init__(self, object loop, flags=None, timeout=None, tries=None, ndots=None,
udp_port=None, tcp_port=None, servers=None):
cdef void* channel = NULL
cdef cares.ares_options options
......@@ -249,10 +250,10 @@ cdef public class ares_channel [object PyGeventAresChannelObject, type PyGeventA
optmask |= cares.ARES_OPT_TCP_PORT
cdef int result = cares.ares_library_init(cares.ARES_LIB_INIT_ALL) # ARES_LIB_INIT_WIN32 -DUSE_WINSOCK?
if result:
raise get_socket_gaierror()(result, ares_strerror(result))
raise get_socket_gaierror()(result, strerror(result))
result = cares.ares_init_options(&channel, &options, optmask)
if result:
raise get_socket_gaierror()(result, ares_strerror(result))
raise get_socket_gaierror()(result, strerror(result))
try:
self.channel = channel
if servers is not None:
......@@ -273,12 +274,13 @@ cdef public class ares_channel [object PyGeventAresChannelObject, type PyGeventA
def __dealloc__(self):
if self.channel:
# XXX ares_library_cleanup?
cares.ares_destroy(self.channel)
self.channel = NULL
def set_servers(self, object servers=[]):
if not self.channel:
raise get_socket_gaierror()(cares.ARES_EDESTRUCTION, 'this ares_channel has been destroyed')
raise get_socket_gaierror()(cares.ARES_EDESTRUCTION, 'this ares channel has been destroyed')
cdef int length = len(servers)
cdef int result, index
cdef char* string
......@@ -306,7 +308,7 @@ cdef public class ares_channel [object PyGeventAresChannelObject, type PyGeventA
c_servers[length - 1].next = NULL
index = cares.ares_set_servers(self.channel, c_servers)
if index:
raise ValueError(ares_strerror(index))
raise ValueError(strerror(index))
finally:
free(c_servers)
......@@ -317,43 +319,42 @@ cdef public class ares_channel [object PyGeventAresChannelObject, type PyGeventA
cdef _sock_state_callback(self, int socket, int read, int write):
if not self.channel:
return
cdef io watcher = self._watchers.get(socket)
cdef object watcher = self._watchers.get(socket)
cdef int events = 0
if read:
events |= libev.EV_READ
events |= EV_READ
if write:
events |= libev.EV_WRITE
events |= EV_WRITE
if watcher is None:
if not events:
return
watcher = self.loop.io(socket, events)
self._watchers[socket] = watcher
elif events:
if watcher._watcher.events == events:
if watcher.events == events:
return
watcher.stop()
watcher._watcher.events = events
watcher.events = events
else:
watcher.stop()
self._watchers.pop(socket, None)
return
watcher._start(self._process_fd, (GEVENT_CORE_EVENTS, watcher))
watcher.start(self._process_fd, EVENTS, watcher)
def _process_fd(self, int events, io watcher):
def _process_fd(self, int events, object watcher):
if not self.channel:
return
#print '_process_fd', watcher, events
cdef int read_fd = watcher._watcher.fd
cdef int read_fd = watcher.fd
cdef int write_fd = read_fd
if not (events & libev.EV_READ):
if not (events & EV_READ):
read_fd = cares.ARES_SOCKET_BAD
if not (events & libev.EV_WRITE):
if not (events & EV_WRITE):
write_fd = cares.ARES_SOCKET_BAD
cares.ares_process_fd(self.channel, read_fd, write_fd)
def gethostbyname(self, object callback, char* name, int family=AF_INET):
if not self.channel:
raise get_socket_gaierror()(cares.ARES_EDESTRUCTION, 'this ares_channel has been destroyed')
raise get_socket_gaierror()(cares.ARES_EDESTRUCTION, 'this ares channel has been destroyed')
# if family == AF_INET, send request for AF_INET
# if family == AF_UNSPEC, send request for AF_INET6 for AF_INET6 then for AF_INET if the former fails
# if family == AF_INET6, the bundled c-ares sends requests for AF_INET6 only whereas the stock c-ares
......@@ -365,7 +366,7 @@ cdef public class ares_channel [object PyGeventAresChannelObject, type PyGeventA
def gethostbyaddr(self, object callback, char* addr):
if not self.channel:
raise get_socket_gaierror()(cares.ARES_EDESTRUCTION, 'this ares_channel has been destroyed')
raise get_socket_gaierror()(cares.ARES_EDESTRUCTION, 'this ares channel has been destroyed')
# will guess the family
cdef char addr_packed[16]
cdef int family
......@@ -377,6 +378,7 @@ cdef public class ares_channel [object PyGeventAresChannelObject, type PyGeventA
family = AF_INET6
length = 16
else:
# XXX raise immediatelly?
callback(result(exception=ValueError('illegal IP address string: %r' % addr)))
return
cdef object arg = (self, callback)
......@@ -385,7 +387,7 @@ cdef public class ares_channel [object PyGeventAresChannelObject, type PyGeventA
cpdef _getnameinfo(self, object callback, tuple sockaddr, int flags):
if not self.channel:
raise get_socket_gaierror()(cares.ARES_EDESTRUCTION, 'this ares_channel has been destroyed')
raise get_socket_gaierror()(cares.ARES_EDESTRUCTION, 'this ares channel has been destroyed')
cdef char* hostp
cdef int port = 0
cdef int flowinfo = 0
......@@ -399,6 +401,7 @@ cdef public class ares_channel [object PyGeventAresChannelObject, type PyGeventA
raise get_socket_gaierror()('invalid value for port: %r' % port)
cdef int length = gevent_make_sockaddr(hostp, port, flowinfo, scope_id, &sa6)
if length <= 0:
# XXX raise immediatelly? like TypeError and gaierror raised above?
callback(result(exception=ValueError('illegal IP address string: %r' % hostp)))
return
cdef object arg = (self, callback)
......
`#' DO NOT EDIT -- this file is auto generated from __file__ on syscmd(date)
cimport cython
cimport libev
from python cimport *
__all__ = ['get_version',
......@@ -12,24 +13,6 @@ __all__ = ['get_version',
'loop']
cdef extern from "Python.h":
struct PyObject:
pass
ctypedef PyObject* PyObjectPtr "PyObject*"
void Py_INCREF(PyObjectPtr)
void Py_DECREF(PyObjectPtr)
void Py_XDECREF(PyObjectPtr)
int Py_ReprEnter(PyObjectPtr)
void Py_ReprLeave(PyObjectPtr)
int PyCallable_Check(PyObjectPtr)
cdef extern from "frameobject.h":
ctypedef struct PyThreadState:
PyObjectPtr exc_type
PyObjectPtr exc_value
PyObjectPtr exc_traceback
PyThreadState* PyThreadState_GET()
cdef extern from "callbacks.h":
void gevent_callback_io(libev.ev_loop, void*, int)
void gevent_callback_timer(libev.ev_loop, void*, int)
......@@ -628,6 +611,3 @@ def set_exc_info(object type, object value):
Py_INCREF(<PyObjectPtr>value)
tstate.exc_value = <PyObjectPtr>value
tstate.exc_traceback = NULL
include "cares.pxi"
#include "Python.h"
#include "ares_setup.h"
#include "ares__close_sockets.c"
#include "ares_data.c"
#include "ares_destroy.c"
#include "ares_expand_name.c"
#include "ares_free_hostent.c"
#include "ares_free_string.c"
#include "ares_gethostbyaddr.c"
#include "ares_gethostbyname.c"
#include "ares__get_hostent.c"
#include "ares_getnameinfo.c"
#include "ares_init.c"
#include "ares_library_init.c"
#include "ares_llist.c"
#include "ares_mkquery.c"
#include "ares_nowarn.c"
#include "ares_options.c"
#include "ares_parse_aaaa_reply.c"
#include "ares_parse_a_reply.c"
#include "ares_parse_ptr_reply.c"
#include "ares_process.c"
#include "ares_query.c"
#include "ares__read_line.c"
#include "ares_search.c"
#include "ares_send.c"
#include "ares_strerror.c"
//#include "ares_timeout.c"
#include "ares__timeval.c"
//#include "ares_version.c"
#include "bitncmp.c"
#include "inet_net_pton.c"
#include "inet_ntop.c"
#ifdef _WIN32
#include "windows_port.c"
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include "ares.h"
#include "inet_ntop.c"
static PyObject* _socket_error = 0;
static PyObject* _socket_gaierror = 0;
static inline PyObject* get_socket_object(PyObject** pobject, const char* name, int incref)
static PyObject*
get_socket_object(PyObject** pobject, const char* name, int incref)
{
if (!*pobject) {
PyObject* _socket;
......@@ -64,10 +38,18 @@ static inline PyObject* get_socket_object(PyObject** pobject, const char* name,
return *pobject;
}
static inline PyObject* get_socket_error() { return get_socket_object(&_socket_error, "error", 1); }
static inline PyObject* get_socket_gaierror() { return get_socket_object(&_socket_gaierror, "gaierror", 1); }
static PyObject*
get_socket_error() {
return get_socket_object(&_socket_error, "error", 1);
}
static PyObject*
get_socket_gaierror() {
return get_socket_object(&_socket_gaierror, "gaierror", 1);
}
static int gevent_append_addr(PyObject* list, int family, void* src, char* tmpbuf, size_t tmpsize) {
static int
gevent_append_addr(PyObject* list, int family, void* src, char* tmpbuf, size_t tmpsize) {
int status = -1;
PyObject* tmp;
if (ares_inet_ntop(family, src, tmpbuf, tmpsize)) {
......@@ -154,7 +136,8 @@ parse_h_addr_list(struct hostent *h)
}
static inline int gevent_make_sockaddr(char* hostp, int port, int flowinfo, int scope_id, struct sockaddr_in6* sa6) {
static int
gevent_make_sockaddr(char* hostp, int port, int flowinfo, int scope_id, struct sockaddr_in6* sa6) {
if ( ares_inet_pton(AF_INET, hostp, &((struct sockaddr_in*)sa6)->sin_addr.s_addr) > 0 ) {
((struct sockaddr_in*)sa6)->sin_family = AF_INET;
((struct sockaddr_in*)sa6)->sin_port = htons(port);
......
/* copied from c-ares since we need this in dnshelper.c and c-ares does not export these functions */
/* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
#define NS_IN6ADDRSZ 16
#define NS_INT16SZ 2
#ifdef SPRINTF_CHAR
# define SPRINTF(x) strlen(sprintf/**/x)
#else
# define SPRINTF(x) ((size_t)sprintf x)
#endif
/*
* WARNING: Don't even consider trying to compile this on a system where
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
*/
static const char *inet_ntop4(const unsigned char *src, char *dst, size_t size);
static const char *inet_ntop6(const unsigned char *src, char *dst, size_t size);
/* char *
* inet_ntop(af, src, dst, size)
* convert a network format address to presentation format.
* return:
* pointer to presentation format address (`dst'), or NULL (see errno).
* note:
* On Windows we store the error in the thread errno, not
* in the winsock error code. This is to avoid loosing the
* actual last winsock error. So use macro ERRNO to fetch the
* errno this funtion sets when returning NULL, not SOCKERRNO.
* author:
* Paul Vixie, 1996.
*/
static const char *
ares_inet_ntop(int af, const void *src, char *dst, size_t size)
{
switch (af)
{
case AF_INET:
return (inet_ntop4(src, dst, size));
case AF_INET6:
return (inet_ntop6(src, dst, size));
default:
SET_ERRNO(EAFNOSUPPORT);
return (NULL);
}
/* NOTREACHED */
}
/* const char *
* inet_ntop4(src, dst, size)
* format an IPv4 address, more or less like inet_ntoa()
* return:
* `dst' (as a const)
* notes:
* (1) uses no statics
* (2) takes a unsigned char* not an in_addr as input
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop4(const unsigned char *src, char *dst, size_t size)
{
static const char fmt[] = "%u.%u.%u.%u";
char tmp[sizeof "255.255.255.255"];
if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) > size)
{
SET_ERRNO(ENOSPC);
return (NULL);
}
strcpy(dst, tmp);
return (dst);
}
/* const char *
* inet_ntop6(src, dst, size)
* convert IPv6 binary address into presentation (printable) format
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop6(const unsigned char *src, char *dst, size_t size)
{
/*
* Note that int32_t and int16_t need only be "at least" large enough
* to contain a value of the specified size. On some systems, like
* Crays, there is no such thing as an integer variable with 16 bits.
* Keep this in mind if you think this function should have been coded
* to use pointer overlays. All the world's not a VAX.
*/
char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
char *tp;
struct {
long base;
long len;
} best, cur;
unsigned long words[NS_IN6ADDRSZ / NS_INT16SZ];
int i;
/*
* Preprocess:
* Copy the input (bytewise) array into a wordwise array.
* Find the longest run of 0x00's in src[] for :: shorthanding.
*/
memset(words, '\0', sizeof(words));
for (i = 0; i < NS_IN6ADDRSZ; i++)
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
best.base = -1;
cur.base = -1;
best.len = 0;
cur.len = 0;
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++)
{
if (words[i] == 0)
{
if (cur.base == -1)
cur.base = i, cur.len = 1;
else
cur.len++;
}
else
{
if (cur.base != -1)
{
if (best.base == -1 || cur.len > best.len)
best = cur;
cur.base = -1;
}
}
}
if (cur.base != -1)
{
if (best.base == -1 || cur.len > best.len)
best = cur;
}
if (best.base != -1 && best.len < 2)
best.base = -1;
/*
* Format the result.
*/
tp = tmp;
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++)
{
/* Are we inside the best run of 0x00's? */
if (best.base != -1 && i >= best.base &&
i < (best.base + best.len))
{
if (i == best.base)
*tp++ = ':';
continue;
}
/* Are we following an initial run of 0x00s or any real hex? */
if (i != 0)
*tp++ = ':';
/* Is this address an encapsulated IPv4? */
if (i == 6 && best.base == 0 &&
(best.len == 6 || (best.len == 5 && words[5] == 0xffff)))
{
if (!inet_ntop4(src+12, tp, sizeof(tmp) - (tp - tmp)))
return (NULL);
tp += strlen(tp);
break;
}
tp += SPRINTF((tp, "%lx", words[i]));
}
/* Was it a trailing run of 0x00's? */
if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ))
*tp++ = ':';
*tp++ = '\0';
/*
* Check for overflow, copy, and we're done.
*/
if ((size_t)(tp - tmp) > size)
{
SET_ERRNO(ENOSPC);
return (NULL);
}
strcpy(dst, tmp);
return (dst);
}
cdef extern from "Python.h":
struct PyObject:
pass
ctypedef PyObject* PyObjectPtr "PyObject*"
void Py_INCREF(PyObjectPtr)
void Py_DECREF(PyObjectPtr)
void Py_XDECREF(PyObjectPtr)
int Py_ReprEnter(PyObjectPtr)
void Py_ReprLeave(PyObjectPtr)
int PyCallable_Check(PyObjectPtr)
cdef extern from "frameobject.h":
ctypedef struct PyThreadState:
PyObjectPtr exc_type
PyObjectPtr exc_value
PyObjectPtr exc_traceback
PyThreadState* PyThreadState_GET()
......@@ -2,7 +2,7 @@ import os
from _socket import getservbyname, getaddrinfo, gaierror, error
from gevent.hub import Waiter, get_hub
from gevent.socket import AF_UNSPEC, AF_INET, AF_INET6, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, AI_NUMERICHOST, EAI_SERVICE
from gevent.core import ares_channel
from gevent.ares import channel
__all__ = ['Resolver']
......@@ -10,7 +10,7 @@ __all__ = ['Resolver']
class Resolver(object):
ares_class = ares_channel
ares_class = channel
def __init__(self, hub=None, **kwargs):
if hub is None:
......
......@@ -5,53 +5,82 @@ import os
import re
import traceback
from os.path import join, abspath, basename, dirname
from glob import glob
try:
from setuptools import Extension, setup
except ImportError:
from distutils.core import Extension, setup
from distutils.command import build_ext
from distutils.command.build_ext import build_ext
from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError
ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError, IOError)
__version__ = re.search("__version__\s*=\s*'(.*)'", open('gevent/__init__.py').read(), re.M).group(1)
assert __version__
libev_embed = os.path.exists('libev')
cares_embed = os.path.exists('c-ares')
cython_output = 'gevent/core.c'
defines = []
include_dirs = []
ares_embed = os.path.exists('c-ares')
define_macros = []
libraries = []
gcc_options = []
cares_configure_command = './configure CONFIG_COMMANDS= CONFIG_FILES='
ares_configure_command = './configure CONFIG_COMMANDS= CONFIG_FILES='
if libev_embed:
defines += [('EV_STANDALONE', '1'),
('EV_COMMON', ''), # we don't use void* data
# libev watchers that we don't use currently:
('EV_STAT_ENABLE', '0'),
('EV_CHECK_ENABLE', '0'),
('EV_CLEANUP_ENABLE', '0'),
('EV_EMBED_ENABLE', '0'),
('EV_ASYNC_ENABLE', '0'),
("EV_PERIODIC_ENABLE", '0'),
("EV_CHILD_ENABLE", '0')]
include_dirs += ['libev']
gcc_options += ['-Wno-unused-variable', '-Wno-unused-result'] # disable warnings from ev.c
if sys.platform == 'win32':
libraries += ['ws2_32']
define_macros += [('FD_SETSIZE', '1024'), ('_WIN32', '1')]
if cares_embed:
include_dirs += ['c-ares']
defines += [('HAVE_CONFIG_H', '')]
CORE = Extension(name='gevent.core',
sources=['gevent/gevent.core.c'],
include_dirs=['libev'],
libraries=libraries,
define_macros=define_macros)
if sys.platform == 'win32':
libraries += ['ws2_32']
defines += [('FD_SETSIZE', '1024'), ('_WIN32', '1')]
ARES = Extension(name='gevent.ares',
sources=['gevent/gevent.ares.c'],
include_dirs=['c-ares'],
libraries=libraries,
define_macros=define_macros)
ARES.optional = True
if os.path.exists('libev'):
CORE.define_macros += [('EV_STANDALONE', '1'),
('EV_COMMON', ''), # we don't use void* data
# libev watchers that we don't use currently:
('EV_STAT_ENABLE', '0'),
('EV_CHECK_ENABLE', '0'),
('EV_CLEANUP_ENABLE', '0'),
('EV_EMBED_ENABLE', '0'),
('EV_ASYNC_ENABLE', '0'),
("EV_PERIODIC_ENABLE", '0'),
("EV_CHILD_ENABLE", '0')]
#CORE.gcc_options = ['-Wno-unused-variable', '-Wno-unused-result'] # disable warnings from ev.c
def need_configure_ares():
if 'Generated from ares_build.h.in by configure' not in read('c-ares/ares_build.h'):
return True
if not os.path.exists('c-ares/ares_config.h'):
return True
def configure_ares():
if need_configure_ares():
os.system('cd c-ares && %s' % ares_configure_command)
if ares_embed:
ARES.sources += sorted(glob('c-ares/*.c'))
if sys.platform == 'win32':
ARES.libraries += ['advapi32']
else:
ARES.configure = configure_ares
ARES.define_macros += [('HAVE_CONFIG_H', '')]
ARES.libraries += ['rt']
def has_changed(destination, *source):
from glob import glob
def need_update(destination, *source):
if not os.path.exists(destination):
sys.stderr.write('Creating %s\n' % destination)
return True
......@@ -69,45 +98,65 @@ def system(command):
return os.system(command)
def run_cython(cython_command='cython'):
if has_changed('gevent/core.pyx', 'gevent/core_.pyx', 'gevent/libev.pxd'):
def replace_in_file(filename, old, new, check=True):
olddata = open(filename).read()
newdata = olddata.replace(old, new)
if check:
assert olddata != newdata, 'replacement in %s failed' % filename
open(filename, 'w').write(newdata)
def run_cython_core(cython_command):
if need_update('gevent/core.pyx', 'gevent/core_.pyx'):
system('m4 gevent/core_.pyx > core.pyx && mv core.pyx gevent/')
if has_changed(cython_output, 'gevent/*.p*x*', 'gevent/*.h', 'gevent/*.c', 'libev/*.c', 'c-ares/*.c'):
if 0 == system('%s gevent/core.pyx -o core.c && mv core.* gevent/' % (cython_command, )):
data = open(cython_output).read()
data = data.replace('\n\n#endif /* Py_PYTHON_H */', '\n#include "callbacks.c"\n#endif /* Py_PYTHON_H */')
if need_update('gevent/gevent.core.c', 'gevent/core.p*x*'):
if 0 == system('%s gevent/core.pyx -o gevent.core.c && mv gevent.core.* gevent/' % (cython_command, )):
replace_in_file('gevent/gevent.core.c', '\n\n#endif /* Py_PYTHON_H */', '\n#include "callbacks.c"\n#endif /* Py_PYTHON_H */')
short_path = 'gevent/'
full_path = join(os.getcwd(), short_path)
data = data.replace(full_path, short_path)
open(cython_output, 'w').write(data)
cython_header = 'gevent/core.h'
data = open(cython_header).read().replace(full_path, short_path)
open(cython_header, 'w').write(data)
replace_in_file('gevent/gevent.core.c', short_path, full_path, check=False)
replace_in_file('gevent/gevent.core.h', short_path, full_path, check=False)
if need_update('gevent/gevent.core.c', 'gevent/callbacks.*', 'gevent/libev.h', 'libev/*.*'):
os.system('touch gevent/gevent.core.c')
def run_cython_ares(cython_command):
if need_update('gevent/gevent.ares.c', 'gevent/ares.pyx'):
system('%s gevent/ares.pyx -o gevent.ares.c && mv gevent.ares.* gevent/' % cython_command)
if need_update('gevent/gevent.ares.c', 'gevent/dnshelper.c', 'gevent/inet_ntop.c', 'c-ares/*.*'):
os.system('touch gevent/gevent.ares.c')
CORE.run_cython = run_cython_core
ARES.run_cython = run_cython_ares
class my_build_ext(build_ext.build_ext):
user_options = (build_ext.build_ext.user_options
class my_build_ext(build_ext):
user_options = (build_ext.user_options
+ [("cython=", None, "path to the cython executable")])
def initialize_options(self):
build_ext.build_ext.initialize_options(self)
build_ext.initialize_options(self)
self.cython = "cython"
def configure_cares(self):
if sys.platform != 'win32' and not os.path.exists('c-ares/ares_config.h'):
os.system('cd c-ares && %s' % cares_configure_command)
def gevent_prepare(self, ext):
if self.cython:
run_cython = getattr(ext, 'run_cython', None)
if run_cython:
run_cython(self.cython)
configure = getattr(ext, 'configure', None)
if configure:
configure()
def build_extension(self, ext):
if self.cython:
run_cython(self.cython)
if cares_embed:
self.configure_cares()
self.gevent_prepare(ext)
try:
if gcc_options and self.compiler.compiler[0] == 'gcc' and '-Wall' in self.compiler.compiler and not gevent_core.extra_compile_args:
gevent_core.extra_compile_args = gcc_options
except (IndexError, AttributeError):
pass
result = build_ext.build_ext.build_extension(self, ext)
result = build_ext.build_extension(self, ext)
except ext_errors:
if getattr(ext, 'optional', False):
raise BuildFailed
else:
raise
# hack: create a symlink from build/../core.so to gevent/core.so
# to prevent "ImportError: cannot import name core" failures
try:
......@@ -136,43 +185,77 @@ class my_build_ext(build_ext.build_ext):
return result
gevent_core = Extension(name='gevent.core',
sources=[cython_output],
include_dirs=include_dirs,
libraries=libraries,
define_macros=defines)
class BuildFailed(Exception):
pass
def read(name):
return open(join(dirname(__file__), name)).read()
try:
return open(join(dirname(__file__), name)).read()
except OSError:
return ''
ext_modules = [CORE, ARES]
if sys.platform == 'win32':
# XXX currently does not work
ext_modules.remove(ARES)
warnings = []
def warn(message):
message += '\n'
sys.stderr.write(message)
warnings.append(message)
ARES.disabled_why = None
for filename in ARES.sources:
if not os.path.exists(filename):
ARES.disabled_why = '%s does not exist' % filename
ext_modules.remove(ARES)
break
def run_setup(ext_modules):
setup(
name='gevent',
version=__version__,
description='Coroutine-based network library',
long_description=read('README.rst'),
author='Denis Bilenko',
author_email='denis.bilenko@gmail.com',
url='http://www.gevent.org/',
packages=['gevent'],
ext_modules=ext_modules,
cmdclass={'build_ext': my_build_ext},
install_requires=['greenlet'],
classifiers=[
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 2.4",
"Programming Language :: Python :: 2.5",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX",
"Operating System :: Microsoft :: Windows",
"Topic :: Internet",
"Topic :: Software Development :: Libraries :: Python Modules",
"Intended Audience :: Developers",
"Development Status :: 4 - Beta"])
if __name__ == '__main__':
if sys.argv[1:] == ['cython']:
run_cython()
CORE.run_cython('cython')
ARES.run_cython('cython')
else:
setup(
name='gevent',
version=__version__,
description='Coroutine-based network library',
long_description=read('README.rst'),
author='Denis Bilenko',
author_email='denis.bilenko@gmail.com',
url='http://www.gevent.org/',
packages=['gevent'],
ext_modules=[gevent_core],
cmdclass={'build_ext': my_build_ext},
install_requires=['greenlet'],
classifiers=[
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 2.4",
"Programming Language :: Python :: 2.5",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX",
"Operating System :: Microsoft :: Windows",
"Topic :: Internet",
"Topic :: Software Development :: Libraries :: Python Modules",
"Intended Audience :: Developers",
"Development Status :: 4 - Beta"])
try:
run_setup(ext_modules)
except BuildFailed:
if ARES not in ext_modules:
raise
ext_modules.remove(ARES)
ARES.disabled_why = 'failed to build'
run_setup(ext_modules)
if ARES.disabled_why:
sys.stderr.write('\nWARNING: The gevent.ares extension has been disabled because %s.\n' % ARES.disabled_why)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment