Runnable with both Python3 and Python2, with PyQt4 #2249

Open
kashikoibumi wants to merge 59 commits from kashikoibumi/py3 into v0.6
12 changed files with 70 additions and 38 deletions
Showing only changes of commit df2631c4ee - Show all commits

View File

@ -476,7 +476,7 @@ class MessageList_TimeWidget(BMTableWidgetItem):
msgid is available by QtCore.Qt.UserRole
"""
def __init__(self, label=None, unread=False, timestamp=None, msgid=''):
def __init__(self, label=None, unread=False, timestamp=None, msgid=b''):
super(MessageList_TimeWidget, self).__init__(label, unread)
self.setData(QtCore.Qt.UserRole, QtCore.QByteArray(msgid))
self.setData(TimestampRole, int(timestamp))

View File

@ -28,8 +28,6 @@ class Inventory:
# cheap inheritance copied from asyncore
def __getattr__(self, attr):
if attr == "__contains__":
self.numberOfInventoryLookupsPerformed += 1
try:
realRet = getattr(self._realInventory, attr)
except AttributeError:
@ -40,6 +38,10 @@ class Inventory:
else:
return realRet
def __contains__(self, key):
self.numberOfInventoryLookupsPerformed += 1
return key in self._realInventory
# hint for pylint: this is dictionary like object
def __getitem__(self, key):
return self._realInventory[key]

View File

@ -724,21 +724,6 @@ class dispatcher(object):
if why.args[0] not in (ENOTCONN, EBADF):
raise
# cheap inheritance, used to pass all other attribute
# references to the underlying socket object.
def __getattr__(self, attr):
try:
retattr = getattr(self.socket, attr)
except AttributeError:
raise AttributeError(
"%s instance has no attribute '%s'"
% (self.__class__.__name__, attr))
else:
msg = "%(me)s.%(attr)s is deprecated; use %(me)s.socket.%(attr)s"\
" instead" % {'me': self.__class__.__name__, 'attr': attr}
warnings.warn(msg, DeprecationWarning, stacklevel=2)
return retattr
# log and log_info may be overridden to provide more sophisticated
# logging and warning methods. In general, log is for 'hit' logging
# and 'log_info' is for informational, warning and error logging.

View File

@ -9,6 +9,7 @@ import re
import socket
import struct
import time
import six
# magic imports!
import addresses
@ -34,6 +35,18 @@ from .objectracker import ObjectTracker, missingObjects
logger = logging.getLogger('default')
def _hoststr(v):
if six.PY3:
return v
else: # assume six.PY2
return str(v)
def _restr(v):
if six.PY3:
return v.decode("utf-8", "replace")
else: # assume six.PY2
return v
class BMProtoError(ProxyError):
"""A Bitmessage Protocol Base Error"""
errorCodes = ("Protocol error")
@ -115,7 +128,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
if not self.invalid:
try:
retval = getattr(
self, "bm_command_" + str(self.command).lower())()
self, "bm_command_" + self.command.decode("utf-8", "replace").lower())()
except AttributeError:
# unimplemented command
logger.debug('unimplemented command %s', self.command)
@ -169,16 +182,16 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# protocol.checkIPAddress()
services, host, port = self.decode_payload_content("Q16sH")
if host[0:12] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
host = socket.inet_ntop(socket.AF_INET, str(host[12:16]))
host = socket.inet_ntop(socket.AF_INET, _hoststr(host[12:16]))
elif host[0:6] == b'\xfd\x87\xd8\x7e\xeb\x43':
# Onion, based on BMD/bitcoind
host = base64.b32encode(host[6:]).lower() + b".onion"
else:
host = socket.inet_ntop(socket.AF_INET6, str(host))
host = socket.inet_ntop(socket.AF_INET6, _hoststr(host))
if host == b"":
# This can happen on Windows systems which are not 64-bit
# compatible so let us drop the IPv6 address.
host = socket.inet_ntop(socket.AF_INET, str(host[12:16]))
host = socket.inet_ntop(socket.AF_INET, _hoststr(host[12:16]))
return Node(services, host, port)
@ -534,7 +547,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
self.append_write_buf(protocol.CreatePacket(b'verack'))
self.verackSent = True
ua_valid = re.match(
r'^/[a-zA-Z]+:[0-9]+\.?[\w\s\(\)\./:;-]*/$', self.userAgent)
r'^/[a-zA-Z]+:[0-9]+\.?[\w\s\(\)\./:;-]*/$', _restr(self.userAgent))
if not ua_valid:
self.userAgent = b'/INVALID:0/'
if not self.isOutbound:

View File

@ -14,10 +14,16 @@ from queues import queue, portCheckerQueue
logger = logging.getLogger('default')
def _ends_with(s, tail):
try:
return s.endswith(tail)
except:
return s.decode("utf-8", "replace").endswith(tail)
def getDiscoveredPeer():
"""Get a peer from the local peer discovery list"""
try:
peer = random.choice(state.discoveredPeers.keys()) # nosec B311
peer = random.choice(list(state.discoveredPeers.keys())) # nosec B311
except (IndexError, KeyError):
raise ValueError
try:
@ -45,7 +51,7 @@ def chooseConnection(stream):
return getDiscoveredPeer()
for _ in range(50):
peer = random.choice( # nosec B311
knownnodes.knownNodes[stream].keys())
list(knownnodes.knownNodes[stream].keys()))
try:
peer_info = knownnodes.knownNodes[stream][peer]
if peer_info.get('self'):
@ -57,10 +63,10 @@ def chooseConnection(stream):
if haveOnion:
# do not connect to raw IP addresses
# --keep all traffic within Tor overlay
if onionOnly and not peer.host.endswith('.onion'):
if onionOnly and not _ends_with(peer.host, '.onion'):
continue
# onion addresses have a higher priority when SOCKS
if peer.host.endswith('.onion') and rating > 0:
if _ends_with(peer.host, '.onion') and rating > 0:
rating = 1
# TODO: need better check
elif not peer.host.startswith('bootstrap'):

View File

@ -25,6 +25,12 @@ from .udp import UDPSocket
logger = logging.getLogger('default')
def _ends_with(s, tail):
try:
return s.endswith(tail)
except:
return s.decode("utf-8", "replace").endswith(tail)
class BMConnectionPool(object):
"""Pool of all existing connections"""
# pylint: disable=too-many-instance-attributes
@ -160,8 +166,8 @@ class BMConnectionPool(object):
@staticmethod
def getListeningIP():
"""What IP are we supposed to be listening on?"""
if config.safeGet(
"bitmessagesettings", "onionhostname", "").endswith(".onion"):
if _ends_with(config.safeGet(
"bitmessagesettings", "onionhostname", ""), ".onion"):
host = config.safeGet(
"bitmessagesettings", "onionbindip")
else:
@ -314,7 +320,7 @@ class BMConnectionPool(object):
continue
try:
if chosen.host.endswith(".onion") and Proxy.onion_proxy:
if _ends_with(chosen.host, ".onion") and Proxy.onion_proxy:
if onionsocksproxytype == "SOCKS5":
self.addConnection(Socks5BMConnection(chosen))
elif onionsocksproxytype == "SOCKS4a":

View File

@ -185,8 +185,8 @@ class Dandelion: # pylint: disable=old-style-class
try:
# random two connections
self.stem = sample(
connectionpool.BMConnectionPool(
).outboundConnections.values(), MAX_STEMS)
sorted(connectionpool.BMConnectionPool(
).outboundConnections.values()), MAX_STEMS)
# not enough stems available
except ValueError:
self.stem = connectionpool.BMConnectionPool(

View File

@ -106,6 +106,12 @@ def addKnownNode(stream, peer, lastseen=None, is_self=False):
Returns True if added a new node.
"""
# pylint: disable=too-many-branches
if not isinstance(peer.host, str):
try:
peer = Peer(peer.host.decode("ascii"), peer.port)
except UnicodeDecodeError as err:
logger.warning("Invalid host: {}".format(peer.host.decode("ascii", "backslashreplace")))
return
if isinstance(stream, Iterable):
with knownNodesLock:
for s in stream:
@ -151,7 +157,7 @@ def createDefaultKnownNodes():
def readKnownNodes():
"""Load knownnodes from filesystem"""
try:
with open(state.appdata + 'knownnodes.dat', 'rb') as source:
with open(state.appdata + 'knownnodes.dat', 'r') as source:
with knownNodesLock:
try:
json_deserialize_knownnodes(source)

View File

@ -14,6 +14,12 @@ from .node import Peer
logger = logging.getLogger('default')
def _ends_with(s, tail):
try:
return s.endswith(tail)
except:
return s.decode("utf-8", "replace").endswith(tail)
class ProxyError(Exception):
"""Base proxy exception class"""
errorCodes = ("Unknown error",)
@ -125,7 +131,7 @@ class Proxy(AdvancedDispatcher):
self.auth = None
self.connect(
self.onion_proxy
if address.host.endswith(".onion") and self.onion_proxy else
if _ends_with(address.host, ".onion") and self.onion_proxy else
self.proxy
)

View File

@ -40,6 +40,12 @@ maximumAgeOfNodesThatIAdvertiseToOthers = 10800 #: Equals three hours
maximumTimeOffsetWrongCount = 3 #: Connections with wrong time offset
def _ends_with(s, tail):
try:
return s.endswith(tail)
except:
return s.decode("utf-8", "replace").endswith(tail)
class TCPConnection(BMProto, TLSDispatcher):
# pylint: disable=too-many-instance-attributes
"""
@ -195,7 +201,7 @@ class TCPConnection(BMProto, TLSDispatcher):
(k, v) for k, v in six.iteritems(nodes)
if v["lastseen"] > int(time.time())
- maximumAgeOfNodesThatIAdvertiseToOthers
and v["rating"] >= 0 and not k.host.endswith('.onion')
and v["rating"] >= 0 and not _ends_with(k.host, '.onion')
]
# sent 250 only if the remote isn't interested in it
elemCount = min(

View File

@ -81,7 +81,7 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
return True
remoteport = False
for seenTime, stream, _, ip, port in addresses:
decodedIP = protocol.checkIPAddress(str(ip))
decodedIP = protocol.checkIPAddress(ip)
if stream not in network.connectionpool.pool.streams:
continue
if (seenTime < time.time() - protocol.MAX_TIME_OFFSET

View File

@ -170,7 +170,7 @@ def checkIPAddress(host, private=False):
otherwise returns False
"""
if host[0:12] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
hostStandardFormat = socket.inet_ntop(socket.AF_INET, host[12:])
hostStandardFormat = socket.inet_ntop(socket.AF_INET, bytes(host[12:]))
return checkIPv4Address(host[12:], hostStandardFormat, private)
elif host[0:6] == b'\xfd\x87\xd8\x7e\xeb\x43':
# Onion, based on BMD/bitcoind
@ -419,7 +419,7 @@ def assembleVersionMessage(
return CreatePacket(b'version', payload)
def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''):
def assembleErrorMessage(fatal=0, banTime=0, inventoryVector=b'', errorText=''):
"""
Construct the payload of an error message,
return the resulting bytes of running `CreatePacket` on it
@ -428,6 +428,8 @@ def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''):
payload += encodeVarint(banTime)
payload += encodeVarint(len(inventoryVector))
payload += inventoryVector
if isinstance(errorText, str):
errorText = errorText.encode("utf-8", "replace")
payload += encodeVarint(len(errorText))
payload += errorText
return CreatePacket(b'error', payload)