Hello community, here is the log from the commit of package python-thrift for openSUSE:Factory checked in at 2015-05-18 22:34:47 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-thrift (Old) and /work/SRC/openSUSE:Factory/.python-thrift.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-thrift" Changes: -------- --- /work/SRC/openSUSE:Factory/python-thrift/python-thrift.changes 2013-10-30 15:51:02.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.python-thrift.new/python-thrift.changes 2015-05-18 22:34:49.000000000 +0200 @@ -1,0 +2,7 @@ +Thu May 14 09:49:48 UTC 2015 - [email protected] + +- update to version 0.9.2: + * no changelog available +- remove README from package: not provided anymore + +------------------------------------------------------------------- Old: ---- thrift-0.9.1.tar.gz New: ---- thrift-0.9.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-thrift.spec ++++++ --- /var/tmp/diff_new_pack.M2cCJH/_old 2015-05-18 22:34:51.000000000 +0200 +++ /var/tmp/diff_new_pack.M2cCJH/_new 2015-05-18 22:34:51.000000000 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-thrift # -# Copyright (c) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: python-thrift -Version: 0.9.1 +Version: 0.9.2 Release: 0 Summary: Python bindings for the Apache Thrift RPC system License: Apache-2.0 @@ -53,7 +53,6 @@ %files %defattr(-,root,root,-) -%doc README %{python_sitearch}/thrift-%{version}-py%{py_ver}.egg-info %{python_sitearch}/thrift ++++++ thrift-0.9.1.tar.gz -> thrift-0.9.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/PKG-INFO new/thrift-0.9.2/PKG-INFO --- old/thrift-0.9.1/PKG-INFO 2013-08-18 22:52:09.000000000 +0200 +++ new/thrift-0.9.2/PKG-INFO 2014-11-18 04:29:47.000000000 +0100 @@ -1,10 +1,10 @@ Metadata-Version: 1.1 Name: thrift -Version: 0.9.1 +Version: 0.9.2 Summary: Python bindings for the Apache Thrift RPC system Home-page: http://thrift.apache.org -Author: ['Thrift Developers'] -Author-email: ['[email protected]'] +Author: Thrift Developers +Author-email: [email protected] License: Apache License 2.0 Description: UNKNOWN Platform: UNKNOWN diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/README new/thrift-0.9.2/README --- old/thrift-0.9.1/README 2013-08-15 16:04:29.000000000 +0200 +++ new/thrift-0.9.2/README 1970-01-01 01:00:00.000000000 +0100 @@ -1,35 +0,0 @@ -Thrift Python Software Library - -License -======= - -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. - -Using Thrift with Python -======================== - -Thrift is provided as a set of Python packages. The top level package is -thrift, and there are subpackages for the protocol, transport, and server -code. Each package contains modules using standard Thrift naming conventions -(i.e. TProtocol, TTransport) and implementations in corresponding modules -(i.e. TSocket). There is also a subpackage reflection, which contains -the generated code for the reflection structures. - -The Python libraries can be installed manually using the provided setup.py -file, or automatically using the install hook provided via autoconf/automake. -To use the latter, become superuser and do make install. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/setup.cfg new/thrift-0.9.2/setup.cfg --- old/thrift-0.9.1/setup.cfg 2013-08-18 22:52:09.000000000 +0200 +++ new/thrift-0.9.2/setup.cfg 2014-11-18 04:29:47.000000000 +0100 @@ -1,6 +1,9 @@ [install] optimize = 1 +[metadata] +description-file = README.md + [egg_info] tag_build = tag_date = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/setup.py new/thrift-0.9.2/setup.py --- old/thrift-0.9.1/setup.py 2013-08-18 17:40:36.000000000 +0200 +++ new/thrift-0.9.2/setup.py 2014-11-18 04:28:47.000000000 +0100 @@ -28,6 +28,11 @@ from distutils.command.build_ext import build_ext from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError +# Fix to build sdist under vagrant +import os +if 'vagrant' in str(os.environ): + del os.link + include_dirs = [] if sys.platform == 'win32': include_dirs.append('compat/win32') @@ -42,13 +47,13 @@ def run(self): try: build_ext.run(self) - except DistutilsPlatformError, x: + except DistutilsPlatformError as x: raise BuildFailed() def build_extension(self, ext): try: build_ext.build_extension(self, ext) - except ext_errors, x: + except ext_errors as x: raise BuildFailed() def run_setup(with_binary): @@ -66,10 +71,10 @@ extensions = dict() setup(name = 'thrift', - version = '0.9.1', + version = '0.9.2', description = 'Python bindings for the Apache Thrift RPC system', - author = ['Thrift Developers'], - author_email = ['[email protected]'], + author = 'Thrift Developers', + author_email = '[email protected]', url = 'http://thrift.apache.org', license = 'Apache License 2.0', packages = [ @@ -88,17 +93,18 @@ 'Topic :: Software Development :: Libraries', 'Topic :: System :: Networking' ], + use_2to3 = True, **extensions ) try: run_setup(True) except BuildFailed: - print - print '*' * 80 - print "An error occured while trying to compile with the C extension enabled" - print "Attempting to build without the extension now" - print '*' * 80 - print + print() + print('*' * 80) + print("An error occured while trying to compile with the C extension enabled") + print("Attempting to build without the extension now") + print('*' * 80) + print() run_setup(False) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/TMultiplexedProcessor.py new/thrift-0.9.2/src/TMultiplexedProcessor.py --- old/thrift-0.9.1/src/TMultiplexedProcessor.py 1970-01-01 01:00:00.000000000 +0100 +++ new/thrift-0.9.2/src/TMultiplexedProcessor.py 2014-11-05 02:50:31.000000000 +0100 @@ -0,0 +1,58 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from thrift.Thrift import TProcessor, TMessageType, TException +from thrift.protocol import TProtocolDecorator, TMultiplexedProtocol + +class TMultiplexedProcessor(TProcessor): + def __init__(self): + self.services = {} + + def registerProcessor(self, serviceName, processor): + self.services[serviceName] = processor + + def process(self, iprot, oprot): + (name, type, seqid) = iprot.readMessageBegin(); + if type != TMessageType.CALL & type != TMessageType.ONEWAY: + raise TException("TMultiplex protocol only supports CALL & ONEWAY") + + index = name.find(TMultiplexedProtocol.SEPARATOR) + if index < 0: + raise TException("Service name not found in message name: " + name + ". Did you forget to use TMultiplexProtocol in your client?") + + serviceName = name[0:index] + call = name[index+len(TMultiplexedProtocol.SEPARATOR):] + if not serviceName in self.services: + raise TException("Service name not found: " + serviceName + ". Did you forget to call registerProcessor()?") + + standardMessage = ( + call, + type, + seqid + ) + return self.services[serviceName].process(StoredMessageProtocol(iprot, standardMessage), oprot) + + +class StoredMessageProtocol(TProtocolDecorator.TProtocolDecorator): + def __init__(self, protocol, messageBegin): + TProtocolDecorator.TProtocolDecorator.__init__(self, protocol) + self.messageBegin = messageBegin + + def readMessageBegin(self): + return self.messageBegin diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/TTornado.py new/thrift-0.9.2/src/TTornado.py --- old/thrift-0.9.1/src/TTornado.py 2013-08-15 16:04:29.000000000 +0200 +++ new/thrift-0.9.2/src/TTornado.py 2014-11-05 02:50:31.000000000 +0100 @@ -17,58 +17,93 @@ # under the License. # -from cStringIO import StringIO -import logging +from __future__ import absolute_import import socket import struct -from thrift.transport import TTransport -from thrift.transport.TTransport import TTransportException +import logging +logger = logging.getLogger(__name__) + +from thrift.transport.TTransport import TTransportException, TTransportBase, TMemoryBuffer + +from io import BytesIO +from collections import deque +from contextlib import contextmanager +from tornado import gen, iostream, ioloop, tcpserver, concurrent + +__all__ = ['TTornadoServer', 'TTornadoStreamTransport'] + + +class _Lock(object): + def __init__(self): + self._waiters = deque() -from tornado import gen -from tornado import iostream -from tornado import netutil + def acquired(self): + return len(self._waiters) > 0 + @gen.coroutine + def acquire(self): + blocker = self._waiters[-1] if self.acquired() else None + future = concurrent.Future() + self._waiters.append(future) + if blocker: + yield blocker -class TTornadoStreamTransport(TTransport.TTransportBase): + raise gen.Return(self._lock_context()) + + def release(self): + assert self.acquired(), 'Lock not aquired' + future = self._waiters.popleft() + future.set_result(None) + + @contextmanager + def _lock_context(self): + try: + yield + finally: + self.release() + + +class TTornadoStreamTransport(TTransportBase): """a framed, buffered transport over a Tornado stream""" - def __init__(self, host, port, stream=None): + def __init__(self, host, port, stream=None, io_loop=None): self.host = host self.port = port - self.is_queuing_reads = False - self.read_queue = [] - self.__wbuf = StringIO() + self.io_loop = io_loop or ioloop.IOLoop.current() + self.__wbuf = BytesIO() + self._read_lock = _Lock() # servers provide a ready-to-go stream self.stream = stream - if self.stream is not None: - self._set_close_callback() - # not the same number of parameters as TTransportBase.open - def open(self, callback): - logging.debug('socket connecting') + def with_timeout(self, timeout, future): + return gen.with_timeout(timeout, future, self.io_loop) + + @gen.coroutine + def open(self, timeout=None): + logger.debug('socket connecting') sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) self.stream = iostream.IOStream(sock) - def on_close_in_connect(*_): - message = 'could not connect to {}:{}'.format(self.host, self.port) + try: + connect = self.stream.connect((self.host, self.port)) + if timeout is not None: + yield self.with_timeout(timeout, connect) + else: + yield connect + except (socket.error, IOError, ioloop.TimeoutError) as e: + message = 'could not connect to {}:{} ({})'.format(self.host, self.port, e) raise TTransportException( type=TTransportException.NOT_OPEN, message=message) - self.stream.set_close_callback(on_close_in_connect) - def finish(*_): - self._set_close_callback() - callback() + raise gen.Return(self) - self.stream.connect((self.host, self.port), callback=finish) - - def _set_close_callback(self): - def on_close(): - raise TTransportException( - type=TTransportException.END_OF_FILE, - message='socket closed') - self.stream.set_close_callback(self.close) + def set_close_callback(self, callback): + """ + Should be called only after open() returns + """ + self.stream.set_close_callback(callback) def close(self): # don't raise if we intend to close @@ -78,54 +113,46 @@ def read(self, _): # The generated code for Tornado shouldn't do individual reads -- only # frames at a time - assert "you're doing it wrong" is True + assert False, "you're doing it wrong" - @gen.engine - def readFrame(self, callback): - self.read_queue.append(callback) - logging.debug('read queue: %s', self.read_queue) - - if self.is_queuing_reads: - # If a read is already in flight, then the while loop below should - # pull it from self.read_queue - return - - self.is_queuing_reads = True - while self.read_queue: - next_callback = self.read_queue.pop() - result = yield gen.Task(self._readFrameFromStream) - next_callback(result) - self.is_queuing_reads = False - - @gen.engine - def _readFrameFromStream(self, callback): - logging.debug('_readFrameFromStream') - frame_header = yield gen.Task(self.stream.read_bytes, 4) - frame_length, = struct.unpack('!i', frame_header) - logging.debug('received frame header, frame length = %i', frame_length) - frame = yield gen.Task(self.stream.read_bytes, frame_length) - logging.debug('received frame payload') - callback(frame) + @contextmanager + def io_exception_context(self): + try: + yield + except (socket.error, IOError) as e: + raise TTransportException( + type=TTransportException.END_OF_FILE, + message=str(e)) + except iostream.StreamBufferFullError as e: + raise TTransportException( + type=TTransportException.UNKNOWN, + message=str(e)) + + @gen.coroutine + def readFrame(self): + # IOStream processes reads one at a time + with (yield self._read_lock.acquire()): + with self.io_exception_context(): + frame_header = yield self.stream.read_bytes(4) + if len(frame_header) == 0: + raise iostream.StreamClosedError('Read zero bytes from stream') + frame_length, = struct.unpack('!i', frame_header) + frame = yield self.stream.read_bytes(frame_length) + raise gen.Return(frame) def write(self, buf): self.__wbuf.write(buf) - def flush(self, callback=None): - wout = self.__wbuf.getvalue() - wsz = len(wout) + def flush(self): + frame = self.__wbuf.getvalue() # reset wbuf before write/flush to preserve state on underlying failure - self.__wbuf = StringIO() - # N.B.: Doing this string concatenation is WAY cheaper than making - # two separate calls to the underlying socket object. Socket writes in - # Python turn out to be REALLY expensive, but it seems to do a pretty - # good job of managing string buffer operations without excessive copies - buf = struct.pack("!i", wsz) + wout - - logging.debug('writing frame length = %i', wsz) - self.stream.write(buf, callback) + frame_length = struct.pack('!i', len(frame)) + self.__wbuf = BytesIO() + with self.io_exception_context(): + return self.stream.write(frame_length + frame) -class TTornadoServer(netutil.TCPServer): +class TTornadoServer(tcpserver.TCPServer): def __init__(self, processor, iprot_factory, oprot_factory=None, *args, **kwargs): super(TTornadoServer, self).__init__(*args, **kwargs) @@ -135,19 +162,21 @@ self._oprot_factory = (oprot_factory if oprot_factory is not None else iprot_factory) + @gen.coroutine def handle_stream(self, stream, address): - try: - host, port = address - trans = TTornadoStreamTransport(host=host, port=port, stream=stream) - oprot = self._oprot_factory.getProtocol(trans) - - def next_pass(): - if not trans.stream.closed(): - self._processor.process(trans, self._iprot_factory, oprot, - callback=next_pass) - - next_pass() + host, port = address + trans = TTornadoStreamTransport(host=host, port=port, stream=stream, + io_loop=self.io_loop) + oprot = self._oprot_factory.getProtocol(trans) + try: + while not trans.stream.closed(): + frame = yield trans.readFrame() + tr = TMemoryBuffer(frame) + iprot = self._iprot_factory.getProtocol(tr) + yield self._processor.process(iprot, oprot) except Exception: - logging.exception('thrift exception in handle_stream') + logger.exception('thrift exception in handle_stream') trans.close() + + logger.info('client disconnected %s:%d', host, port) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/protocol/TCompactProtocol.py new/thrift-0.9.2/src/protocol/TCompactProtocol.py --- old/thrift-0.9.1/src/protocol/TCompactProtocol.py 2013-08-15 16:04:29.000000000 +0200 +++ new/thrift-0.9.2/src/protocol/TCompactProtocol.py 2014-11-05 02:50:31.000000000 +0100 @@ -45,6 +45,7 @@ def makeZigZag(n, bits): + checkIntegerLimits(n, bits) return (n << 1) ^ (n >> (bits - 1)) @@ -120,6 +121,7 @@ VERSION = 1 VERSION_MASK = 0x1f TYPE_MASK = 0xe0 + TYPE_BITS = 0x07 TYPE_SHIFT_AMOUNT = 5 def __init__(self, trans): @@ -250,7 +252,7 @@ @writer def writeDouble(self, dub): - self.trans.write(pack('!d', dub)) + self.trans.write(pack('<d', dub)) def __writeString(self, s): self.__writeSize(len(s)) @@ -310,7 +312,7 @@ raise TProtocolException(TProtocolException.BAD_VERSION, 'Bad protocol id in the message: %d' % proto_id) ver_type = self.__readUByte() - type = (ver_type & self.TYPE_MASK) >> self.TYPE_SHIFT_AMOUNT + type = (ver_type >> self.TYPE_SHIFT_AMOUNT) & self.TYPE_BITS version = ver_type & self.VERSION_MASK if version != self.VERSION: raise TProtocolException(TProtocolException.BAD_VERSION, @@ -383,7 +385,7 @@ @reader def readDouble(self): buff = self.trans.readAll(8) - val, = unpack('!d', buff) + val, = unpack('<d', buff) return val def __readString(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/protocol/TJSONProtocol.py new/thrift-0.9.2/src/protocol/TJSONProtocol.py --- old/thrift-0.9.1/src/protocol/TJSONProtocol.py 2013-08-15 16:04:29.000000000 +0200 +++ new/thrift-0.9.2/src/protocol/TJSONProtocol.py 2014-11-05 02:50:31.000000000 +0100 @@ -17,7 +17,8 @@ # under the License. # -from TProtocol import TType, TProtocolBase, TProtocolException +from TProtocol import TType, TProtocolBase, TProtocolException, \ + checkIntegerLimits import base64 import json import math @@ -449,12 +450,21 @@ def writeBool(self, boolean): self.writeJSONNumber(1 if boolean is True else 0) - def writeInteger(self, integer): - self.writeJSONNumber(integer) - writeByte = writeInteger - writeI16 = writeInteger - writeI32 = writeInteger - writeI64 = writeInteger + def writeByte(self, byte): + checkIntegerLimits(byte, 8) + self.writeJSONNumber(byte) + + def writeI16(self, i16): + checkIntegerLimits(i16, 16) + self.writeJSONNumber(i16) + + def writeI32(self, i32): + checkIntegerLimits(i32, 32) + self.writeJSONNumber(i32) + + def writeI64(self, i64): + checkIntegerLimits(i64, 64) + self.writeJSONNumber(i64) def writeDouble(self, dbl): self.writeJSONNumber(dbl) @@ -524,12 +534,21 @@ writeSetBegin = _writeCollectionBegin writeSetEnd = _writeCollectionEnd - def writeInteger(self, integer): - self.writeJSONNumber(integer) - writeByte = writeInteger - writeI16 = writeInteger - writeI32 = writeInteger - writeI64 = writeInteger + def writeByte(self, byte): + checkIntegerLimits(byte, 8) + self.writeJSONNumber(byte) + + def writeI16(self, i16): + checkIntegerLimits(i16, 16) + self.writeJSONNumber(i16) + + def writeI32(self, i32): + checkIntegerLimits(i32, 32) + self.writeJSONNumber(i32) + + def writeI64(self, i64): + checkIntegerLimits(i64, 64) + self.writeJSONNumber(i64) def writeBool(self, boolean): self.writeJSONNumber(1 if boolean is True else 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/protocol/TMultiplexedProtocol.py new/thrift-0.9.2/src/protocol/TMultiplexedProtocol.py --- old/thrift-0.9.1/src/protocol/TMultiplexedProtocol.py 1970-01-01 01:00:00.000000000 +0100 +++ new/thrift-0.9.2/src/protocol/TMultiplexedProtocol.py 2014-11-05 02:50:31.000000000 +0100 @@ -0,0 +1,39 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from thrift.Thrift import TMessageType +from thrift.protocol import TProtocolDecorator + +SEPARATOR = ":" + +class TMultiplexedProtocol(TProtocolDecorator.TProtocolDecorator): + def __init__(self, protocol, serviceName): + TProtocolDecorator.TProtocolDecorator.__init__(self, protocol) + self.serviceName = serviceName + + def writeMessageBegin(self, name, type, seqid): + if (type == TMessageType.CALL or + type == TMessageType.ONEWAY): + self.protocol.writeMessageBegin( + self.serviceName + SEPARATOR + name, + type, + seqid + ) + else: + self.protocol.writeMessageBegin(name, type, seqid) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/protocol/TProtocol.py new/thrift-0.9.2/src/protocol/TProtocol.py --- old/thrift-0.9.1/src/protocol/TProtocol.py 2013-08-15 16:04:29.000000000 +0200 +++ new/thrift-0.9.2/src/protocol/TProtocol.py 2014-11-05 02:50:31.000000000 +0100 @@ -28,6 +28,8 @@ NEGATIVE_SIZE = 2 SIZE_LIMIT = 3 BAD_VERSION = 4 + NOT_IMPLEMENTED = 5 + DEPTH_LIMIT = 6 def __init__(self, type=UNKNOWN, message=None): TException.__init__(self, message) @@ -400,6 +402,19 @@ else: writer(val) +def checkIntegerLimits(i, bits): + if bits == 8 and (i < -128 or i > 127): + raise TProtocolException(TProtocolException.INVALID_DATA, + "i8 requires -128 <= number <= 127") + elif bits == 16 and (i < -32768 or i > 32767): + raise TProtocolException(TProtocolException.INVALID_DATA, + "i16 requires -32768 <= number <= 32767") + elif bits == 32 and (i < -2147483648 or i > 2147483647): + raise TProtocolException(TProtocolException.INVALID_DATA, + "i32 requires -2147483648 <= number <= 2147483647") + elif bits == 64 and (i < -9223372036854775808 or i > 9223372036854775807): + raise TProtocolException(TProtocolException.INVALID_DATA, + "i64 requires -9223372036854775808 <= number <= 9223372036854775807") class TProtocolFactory: def getProtocol(self, trans): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/protocol/TProtocolDecorator.py new/thrift-0.9.2/src/protocol/TProtocolDecorator.py --- old/thrift-0.9.1/src/protocol/TProtocolDecorator.py 1970-01-01 01:00:00.000000000 +0100 +++ new/thrift-0.9.2/src/protocol/TProtocolDecorator.py 2014-11-05 02:50:31.000000000 +0100 @@ -0,0 +1,42 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from thrift.protocol.TProtocol import TProtocolBase +from types import * + +class TProtocolDecorator(): + def __init__(self, protocol): + TProtocolBase(protocol) + self.protocol = protocol + + def __getattr__(self, name): + if hasattr(self.protocol, name): + member = getattr(self.protocol, name) + if type(member) in [MethodType, UnboundMethodType, FunctionType, LambdaType, BuiltinFunctionType, BuiltinMethodType]: + return lambda *args, **kwargs: self._wrap(member, args, kwargs) + else: + return member + raise AttributeError(name) + + def _wrap(self, func, args, kwargs): + if type(func) == MethodType: + result = func(*args, **kwargs) + else: + result = func(self.protocol, *args, **kwargs) + return result diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/server/TNonblockingServer.py new/thrift-0.9.2/src/server/TNonblockingServer.py --- old/thrift-0.9.1/src/server/TNonblockingServer.py 2013-08-15 16:04:29.000000000 +0200 +++ new/thrift-0.9.2/src/server/TNonblockingServer.py 2014-11-05 02:50:31.000000000 +0100 @@ -29,7 +29,9 @@ import Queue import select import struct + import logging +logger = logging.getLogger(__name__) from thrift.transport import TTransport from thrift.protocol.TBinaryProtocol import TBinaryProtocolFactory @@ -54,7 +56,7 @@ processor.process(iprot, oprot) callback(True, otrans.getvalue()) except Exception: - logging.exception("Exception while processing request") + logger.exception("Exception while processing request") callback(False, '') WAIT_LEN = 0 @@ -116,18 +118,18 @@ # if we read 0 bytes and self.message is empty, then # the client closed the connection if len(self.message) != 0: - logging.error("can't read frame size from socket") + logger.error("can't read frame size from socket") self.close() return self.message += read if len(self.message) == 4: self.len, = struct.unpack('!i', self.message) if self.len < 0: - logging.error("negative frame size, it seems client " + logger.error("negative frame size, it seems client " "doesn't use FramedTransport") self.close() elif self.len == 0: - logging.error("empty frame, it's really strange") + logger.error("empty frame, it's really strange") self.close() else: self.message = '' @@ -145,7 +147,7 @@ elif self.status == WAIT_MESSAGE: read = self.socket.recv(self.len - len(self.message)) if len(read) == 0: - logging.error("can't read frame from socket (get %d of " + logger.error("can't read frame from socket (get %d of " "%d bytes)" % (len(self.message), self.len)) self.close() return diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/server/TProcessPoolServer.py new/thrift-0.9.2/src/server/TProcessPoolServer.py --- old/thrift-0.9.1/src/server/TProcessPoolServer.py 2013-08-15 16:04:29.000000000 +0200 +++ new/thrift-0.9.2/src/server/TProcessPoolServer.py 2014-11-05 02:50:31.000000000 +0100 @@ -19,6 +19,8 @@ import logging +logger = logging.getLogger(__name__) + from multiprocessing import Process, Value, Condition, reduction from TServer import TServer @@ -56,11 +58,13 @@ while self.isRunning.value: try: client = self.serverTransport.accept() + if not client: + continue self.serveClient(client) except (KeyboardInterrupt, SystemExit): return 0 except Exception, x: - logging.exception(x) + logger.exception(x) def serveClient(self, client): """Process input/output from a client for as long as possible""" @@ -75,7 +79,7 @@ except TTransportException, tx: pass except Exception, x: - logging.exception(x) + logger.exception(x) itrans.close() otrans.close() @@ -96,7 +100,7 @@ w.start() self.workers.append(w) except Exception, x: - logging.exception(x) + logger.exception(x) # wait until the condition is set by stop() while True: @@ -107,7 +111,7 @@ except (SystemExit, KeyboardInterrupt): break except Exception, x: - logging.exception(x) + logger.exception(x) self.isRunning.value = False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/server/TServer.py new/thrift-0.9.2/src/server/TServer.py --- old/thrift-0.9.1/src/server/TServer.py 2013-08-15 16:04:29.000000000 +0200 +++ new/thrift-0.9.2/src/server/TServer.py 2014-11-05 02:50:31.000000000 +0100 @@ -18,12 +18,14 @@ # import Queue -import logging import os import sys import threading import traceback +import logging +logger = logging.getLogger(__name__) + from thrift.Thrift import TProcessor from thrift.protocol import TBinaryProtocol from thrift.transport import TTransport @@ -75,6 +77,8 @@ self.serverTransport.listen() while True: client = self.serverTransport.accept() + if not client: + continue itrans = self.inputTransportFactory.getTransport(client) otrans = self.outputTransportFactory.getTransport(client) iprot = self.inputProtocolFactory.getProtocol(itrans) @@ -85,7 +89,7 @@ except TTransport.TTransportException, tx: pass except Exception, x: - logging.exception(x) + logger.exception(x) itrans.close() otrans.close() @@ -103,13 +107,15 @@ while True: try: client = self.serverTransport.accept() + if not client: + continue t = threading.Thread(target=self.handle, args=(client,)) t.setDaemon(self.daemon) t.start() except KeyboardInterrupt: raise except Exception, x: - logging.exception(x) + logger.exception(x) def handle(self, client): itrans = self.inputTransportFactory.getTransport(client) @@ -122,7 +128,7 @@ except TTransport.TTransportException, tx: pass except Exception, x: - logging.exception(x) + logger.exception(x) itrans.close() otrans.close() @@ -148,7 +154,7 @@ client = self.clients.get() self.serveClient(client) except Exception, x: - logging.exception(x) + logger.exception(x) def serveClient(self, client): """Process input/output from a client for as long as possible""" @@ -162,7 +168,7 @@ except TTransport.TTransportException, tx: pass except Exception, x: - logging.exception(x) + logger.exception(x) itrans.close() otrans.close() @@ -175,16 +181,18 @@ t.setDaemon(self.daemon) t.start() except Exception, x: - logging.exception(x) + logger.exception(x) # Pump the socket for clients self.serverTransport.listen() while True: try: client = self.serverTransport.accept() + if not client: + continue self.clients.put(client) except Exception, x: - logging.exception(x) + logger.exception(x) class TForkingServer(TServer): @@ -209,11 +217,13 @@ try: file.close() except IOError, e: - logging.warning(e, exc_info=True) + logger.warning(e, exc_info=True) self.serverTransport.listen() while True: client = self.serverTransport.accept() + if not client: + continue try: pid = os.fork() @@ -243,7 +253,7 @@ except TTransport.TTransportException, tx: pass except Exception, e: - logging.exception(e) + logger.exception(e) ecode = 1 finally: try_close(itrans) @@ -254,7 +264,7 @@ except TTransport.TTransportException, tx: pass except Exception, x: - logging.exception(x) + logger.exception(x) def collect_children(self): while self.children: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/transport/TSocket.py new/thrift-0.9.2/src/transport/TSocket.py --- old/thrift-0.9.1/src/transport/TSocket.py 2013-08-15 16:04:29.000000000 +0200 +++ new/thrift-0.9.2/src/transport/TSocket.py 2014-11-05 02:50:31.000000000 +0100 @@ -33,7 +33,7 @@ else: return socket.getaddrinfo(self.host, self.port, - socket.AF_UNSPEC, + self._socket_family, socket.SOCK_STREAM, 0, socket.AI_PASSIVE | socket.AI_ADDRCONFIG) @@ -47,19 +47,21 @@ class TSocket(TSocketBase): """Socket implementation of TTransport base.""" - def __init__(self, host='localhost', port=9090, unix_socket=None): + def __init__(self, host='localhost', port=9090, unix_socket=None, socket_family=socket.AF_UNSPEC): """Initialize a TSocket @param host(str) The host to connect to. @param port(int) The (TCP) port to connect to. @param unix_socket(str) The filename of a unix socket to connect to. (host and port will be ignored.) + @param socket_family(int) The socket family to use with this socket. """ self.host = host self.port = port self.handle = None self._unix_socket = unix_socket self._timeout = None + self._socket_family = socket_family def setHandle(self, h): self.handle = h @@ -139,16 +141,18 @@ class TServerSocket(TSocketBase, TServerTransportBase): """Socket implementation of TServerTransport base.""" - def __init__(self, host=None, port=9090, unix_socket=None): + def __init__(self, host=None, port=9090, unix_socket=None, socket_family=socket.AF_UNSPEC): self.host = host self.port = port self._unix_socket = unix_socket + self._socket_family = socket_family self.handle = None def listen(self): res0 = self._resolveAddr() + socket_family = self._socket_family == socket.AF_UNSPEC and socket.AF_INET6 or self._socket_family for res in res0: - if res[0] is socket.AF_INET6 or res is res0[-1]: + if res[0] is socket_family or res is res0[-1]: break # We need remove the old unix socket if the file exists and diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/transport/TTransport.py new/thrift-0.9.2/src/transport/TTransport.py --- old/thrift-0.9.1/src/transport/TTransport.py 2013-08-15 16:04:29.000000000 +0200 +++ new/thrift-0.9.2/src/transport/TTransport.py 2014-11-05 02:50:31.000000000 +0100 @@ -328,3 +328,114 @@ def flush(self): self.fileobj.flush() + + +class TSaslClientTransport(TTransportBase, CReadableTransport): + """ + SASL transport + """ + + START = 1 + OK = 2 + BAD = 3 + ERROR = 4 + COMPLETE = 5 + + def __init__(self, transport, host, service, mechanism='GSSAPI', + **sasl_kwargs): + """ + transport: an underlying transport to use, typically just a TSocket + host: the name of the server, from a SASL perspective + service: the name of the server's service, from a SASL perspective + mechanism: the name of the preferred mechanism to use + + All other kwargs will be passed to the puresasl.client.SASLClient + constructor. + """ + + from puresasl.client import SASLClient + + self.transport = transport + self.sasl = SASLClient(host, service, mechanism, **sasl_kwargs) + + self.__wbuf = StringIO() + self.__rbuf = StringIO() + + def open(self): + if not self.transport.isOpen(): + self.transport.open() + + self.send_sasl_msg(self.START, self.sasl.mechanism) + self.send_sasl_msg(self.OK, self.sasl.process()) + + while True: + status, challenge = self.recv_sasl_msg() + if status == self.OK: + self.send_sasl_msg(self.OK, self.sasl.process(challenge)) + elif status == self.COMPLETE: + if not self.sasl.complete: + raise TTransportException("The server erroneously indicated " + "that SASL negotiation was complete") + else: + break + else: + raise TTransportException("Bad SASL negotiation status: %d (%s)" + % (status, challenge)) + + def send_sasl_msg(self, status, body): + header = pack(">BI", status, len(body)) + self.transport.write(header + body) + self.transport.flush() + + def recv_sasl_msg(self): + header = self.transport.readAll(5) + status, length = unpack(">BI", header) + if length > 0: + payload = self.transport.readAll(length) + else: + payload = "" + return status, payload + + def write(self, data): + self.__wbuf.write(data) + + def flush(self): + data = self.__wbuf.getvalue() + encoded = self.sasl.wrap(data) + self.transport.write(''.join((pack("!i", len(encoded)), encoded))) + self.transport.flush() + self.__wbuf = StringIO() + + def read(self, sz): + ret = self.__rbuf.read(sz) + if len(ret) != 0: + return ret + + self._read_frame() + return self.__rbuf.read(sz) + + def _read_frame(self): + header = self.transport.readAll(4) + length, = unpack('!i', header) + encoded = self.transport.readAll(length) + self.__rbuf = StringIO(self.sasl.unwrap(encoded)) + + def close(self): + self.sasl.dispose() + self.transport.close() + + # based on TFramedTransport + @property + def cstringio_buf(self): + return self.__rbuf + + def cstringio_refill(self, prefix, reqlen): + # self.__rbuf will already be empty here because fastbinary doesn't + # ask for a refill until the previous buffer is empty. Therefore, + # we can start reading new frames immediately. + while len(prefix) < reqlen: + self._read_frame() + prefix += self.__rbuf.getvalue() + self.__rbuf = StringIO(prefix) + return self.__rbuf + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/src/transport/TTwisted.py new/thrift-0.9.2/src/transport/TTwisted.py --- old/thrift-0.9.1/src/transport/TTwisted.py 2013-08-15 16:04:29.000000000 +0200 +++ new/thrift-0.9.2/src/transport/TTwisted.py 2014-11-05 02:50:31.000000000 +0100 @@ -17,14 +17,15 @@ # under the License. # +import struct from cStringIO import StringIO from zope.interface import implements, Interface, Attribute -from twisted.internet.protocol import Protocol, ServerFactory, ClientFactory, \ +from twisted.internet.protocol import ServerFactory, ClientFactory, \ connectionDone from twisted.internet import defer +from twisted.internet.threads import deferToThread from twisted.protocols import basic -from twisted.python import log from twisted.web import server, resource, http from thrift.transport import TTransport @@ -41,7 +42,7 @@ def flush(self): msg = self.__wbuf.getvalue() self.__wbuf = StringIO() - self.sendMessage(msg) + return self.sendMessage(msg) def sendMessage(self, message): raise NotImplementedError @@ -54,7 +55,7 @@ self.func = func def sendMessage(self, message): - self.func(message) + return self.func(message) class ThriftClientProtocol(basic.Int32StringReceiver): @@ -101,6 +102,108 @@ method(iprot, mtype, rseqid) +class ThriftSASLClientProtocol(ThriftClientProtocol): + + START = 1 + OK = 2 + BAD = 3 + ERROR = 4 + COMPLETE = 5 + + MAX_LENGTH = 2 ** 31 - 1 + + def __init__(self, client_class, iprot_factory, oprot_factory=None, + host=None, service=None, mechanism='GSSAPI', **sasl_kwargs): + """ + host: the name of the server, from a SASL perspective + service: the name of the server's service, from a SASL perspective + mechanism: the name of the preferred mechanism to use + + All other kwargs will be passed to the puresasl.client.SASLClient + constructor. + """ + + from puresasl.client import SASLClient + self.SASLCLient = SASLClient + + ThriftClientProtocol.__init__(self, client_class, iprot_factory, oprot_factory) + + self._sasl_negotiation_deferred = None + self._sasl_negotiation_status = None + self.client = None + + if host is not None: + self.createSASLClient(host, service, mechanism, **sasl_kwargs) + + def createSASLClient(self, host, service, mechanism, **kwargs): + self.sasl = self.SASLClient(host, service, mechanism, **kwargs) + + def dispatch(self, msg): + encoded = self.sasl.wrap(msg) + len_and_encoded = ''.join((struct.pack('!i', len(encoded)), encoded)) + ThriftClientProtocol.dispatch(self, len_and_encoded) + + @defer.inlineCallbacks + def connectionMade(self): + self._sendSASLMessage(self.START, self.sasl.mechanism) + initial_message = yield deferToThread(self.sasl.process) + self._sendSASLMessage(self.OK, initial_message) + + while True: + status, challenge = yield self._receiveSASLMessage() + if status == self.OK: + response = yield deferToThread(self.sasl.process, challenge) + self._sendSASLMessage(self.OK, response) + elif status == self.COMPLETE: + if not self.sasl.complete: + msg = "The server erroneously indicated that SASL " \ + "negotiation was complete" + raise TTransport.TTransportException(msg, message=msg) + else: + break + else: + msg = "Bad SASL negotiation status: %d (%s)" % (status, challenge) + raise TTransport.TTransportException(msg, message=msg) + + self._sasl_negotiation_deferred = None + ThriftClientProtocol.connectionMade(self) + + def _sendSASLMessage(self, status, body): + if body is None: + body = "" + header = struct.pack(">BI", status, len(body)) + self.transport.write(header + body) + + def _receiveSASLMessage(self): + self._sasl_negotiation_deferred = defer.Deferred() + self._sasl_negotiation_status = None + return self._sasl_negotiation_deferred + + def connectionLost(self, reason=connectionDone): + if self.client: + ThriftClientProtocol.connectionLost(self, reason) + + def dataReceived(self, data): + if self._sasl_negotiation_deferred: + # we got a sasl challenge in the format (status, length, challenge) + # save the status, let IntNStringReceiver piece the challenge data together + self._sasl_negotiation_status, = struct.unpack("B", data[0]) + ThriftClientProtocol.dataReceived(self, data[1:]) + else: + # normal frame, let IntNStringReceiver piece it together + ThriftClientProtocol.dataReceived(self, data) + + def stringReceived(self, frame): + if self._sasl_negotiation_deferred: + # the frame is just a SASL challenge + response = (self._sasl_negotiation_status, frame) + self._sasl_negotiation_deferred.callback(response) + else: + # there's a second 4 byte length prefix inside the frame + decoded_frame = self.sasl.unwrap(frame[4:]) + ThriftClientProtocol.stringReceived(self, decoded_frame) + + class ThriftServerProtocol(basic.Int32StringReceiver): MAX_LENGTH = 2 ** 31 - 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/thrift.egg-info/PKG-INFO new/thrift-0.9.2/thrift.egg-info/PKG-INFO --- old/thrift-0.9.1/thrift.egg-info/PKG-INFO 2013-08-18 22:52:09.000000000 +0200 +++ new/thrift-0.9.2/thrift.egg-info/PKG-INFO 2014-11-18 04:29:46.000000000 +0100 @@ -1,10 +1,10 @@ Metadata-Version: 1.1 Name: thrift -Version: 0.9.1 +Version: 0.9.2 Summary: Python bindings for the Apache Thrift RPC system Home-page: http://thrift.apache.org -Author: ['Thrift Developers'] -Author-email: ['[email protected]'] +Author: Thrift Developers +Author-email: [email protected] License: Apache License 2.0 Description: UNKNOWN Platform: UNKNOWN diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thrift-0.9.1/thrift.egg-info/SOURCES.txt new/thrift-0.9.2/thrift.egg-info/SOURCES.txt --- old/thrift-0.9.1/thrift.egg-info/SOURCES.txt 2013-08-18 22:52:09.000000000 +0200 +++ new/thrift-0.9.2/thrift.egg-info/SOURCES.txt 2014-11-18 04:29:46.000000000 +0100 @@ -1,6 +1,6 @@ -README setup.cfg setup.py +src/TMultiplexedProcessor.py src/TSCons.py src/TSerialization.py src/TTornado.py @@ -10,7 +10,9 @@ src/protocol/TBinaryProtocol.py src/protocol/TCompactProtocol.py src/protocol/TJSONProtocol.py +src/protocol/TMultiplexedProtocol.py src/protocol/TProtocol.py +src/protocol/TProtocolDecorator.py src/protocol/__init__.py src/protocol/fastbinary.c src/server/THttpServer.py
