diff --git a/src/calibre/srv/bonjour.py b/src/calibre/srv/bonjour.py index e652ae84b7..55c5ce3af9 100644 --- a/src/calibre/srv/bonjour.py +++ b/src/calibre/srv/bonjour.py @@ -27,14 +27,8 @@ class BonJour(object): # {{{ ip_address, port = loop.bound_address[:2] self.zeroconf_ip_address = zipa = verify_ipV4_address(ip_address) or get_external_ip() prefix = loop.opts.url_prefix or '' - # The Zeroconf module requires everything to be bytestrings - - def enc(x): - if not isinstance(x, bytes): - x = x.encode('ascii') - return x mdns_services = ( - (enc(self.service_name), enc(self.service_type), port, {b'path':enc(prefix + self.path)}), + (self.service_name, self.service_type, port, {'path':prefix + self.path}), ) if self.shutdown.is_set(): return diff --git a/src/calibre/srv/tests/loop.py b/src/calibre/srv/tests/loop.py index 897721cc40..30c153d777 100644 --- a/src/calibre/srv/tests/loop.py +++ b/src/calibre/srv/tests/loop.py @@ -12,6 +12,7 @@ from unittest import skipIf from glob import glob from threading import Event +from calibre.constants import ispy3 from calibre.srv.pre_activated import has_preactivated_support from calibre.srv.tests.base import BaseTest, TestServer from calibre.ptempfile import TemporaryDirectory @@ -114,15 +115,18 @@ class LoopTest(BaseTest): def test_bonjour(self): 'Test advertising via BonJour' from calibre.srv.bonjour import BonJour - from calibre.utils.Zeroconf import Zeroconf + if ispy3: + from zeroconf import Zeroconf + else: + from calibre.utils.Zeroconf import Zeroconf b = BonJour() with TestServer(lambda data:(data.path[0] + data.read()), plugins=(b,), shutdown_timeout=5) as server: self.assertTrue(b.started.wait(5), 'BonJour not started') self.ae(b.advertised_port, server.address[1]) service = b.services[0] - self.ae(service.type, b'_calibre._tcp.local.') + self.ae(service.type, '_calibre._tcp.local.') r = Zeroconf() - info = r.getServiceInfo(service.type, service.name) + info = r.get_service_info(service.type, service.name) self.assertIsNotNone(info) self.ae(info.text, b'\npath=/opds') diff --git a/src/calibre/test_build.py b/src/calibre/test_build.py index b96d0f6a8a..f5e5208708 100644 --- a/src/calibre/test_build.py +++ b/src/calibre/test_build.py @@ -13,7 +13,7 @@ Test a binary calibre build to ensure that all needed binary images/libraries ha import os, ctypes, sys, unittest, time -from calibre.constants import plugins, iswindows, islinux, isosx +from calibre.constants import plugins, iswindows, islinux, isosx, ispy3 from polyglot.builtins import iteritems, map, unicode_type is_ci = os.environ.get('CI', '').lower() == 'true' @@ -77,6 +77,15 @@ class BuildTest(unittest.TestCase): import soupsieve, bs4 del soupsieve, bs4 + def test_zeroconf(self): + if ispy3: + import zeroconf as z, ifaddr + else: + import calibre.utils.Zeroconf as z + ifaddr = None + del z + del ifaddr + def test_plugins(self): exclusions = set() if is_ci: diff --git a/src/calibre/utils/Zeroconf.py b/src/calibre/utils/Zeroconf.py index 88a5db3081..ee7157c076 100755 --- a/src/calibre/utils/Zeroconf.py +++ b/src/calibre/utils/Zeroconf.py @@ -1384,6 +1384,7 @@ class Zeroconf(object): if info.request(self, timeout): return info return None + get_service_info = getServiceInfo def addServiceListener(self, type, listener): """Adds a listener for a particular service type. This object @@ -1424,6 +1425,7 @@ class Zeroconf(object): self.send(out) i += 1 nextTime += _REGISTER_TIME + register_service = registerService def unregisterService(self, info): """Unregister a service.""" @@ -1452,6 +1454,7 @@ class Zeroconf(object): self.send(out) i += 1 nextTime += _UNREGISTER_TIME + unregister_service = unregisterService def unregisterAllServices(self): """Unregister all registered services.""" diff --git a/src/calibre/utils/mdns.py b/src/calibre/utils/mdns.py index 294253ae7f..41db4713d5 100644 --- a/src/calibre/utils/mdns.py +++ b/src/calibre/utils/mdns.py @@ -1,4 +1,4 @@ -from __future__ import with_statement +from __future__ import absolute_import, division, print_function, unicode_literals __license__ = 'GPL 3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' @@ -9,10 +9,12 @@ from threading import Thread from calibre.utils.filenames import ascii_text from calibre import force_unicode +from calibre.constants import ispy3 +from polyglot.builtins import iteritems, unicode_type _server = None -_all_ip_addresses = dict() +_all_ip_addresses = {} class AllIpAddressesGetter(Thread): @@ -50,9 +52,9 @@ def get_all_ips(reinitialize=False): global _all_ip_addresses, _ip_address_getter_thread if not _ip_address_getter_thread or (reinitialize and not _ip_address_getter_thread.is_alive()): - _all_ip_addresses = dict() + _all_ip_addresses = {} _ip_address_getter_thread = AllIpAddressesGetter() - _ip_address_getter_thread.setDaemon(True) + _ip_address_getter_thread.daemon = True _ip_address_getter_thread.start() return _all_ip_addresses @@ -61,7 +63,7 @@ def _get_external_ip(): 'Get IP address of interface used to connect to the outside world' try: ipaddr = socket.gethostbyname(socket.gethostname()) - except: + except Exception: ipaddr = '127.0.0.1' if ipaddr.startswith('127.'): for addr in ('192.0.2.0', '198.51.100.0', 'google.com'): @@ -85,7 +87,7 @@ def verify_ipV4_address(ip_address): socket.inet_aton(ip_address) if len(ip_address.split('.')) == 4: result = ip_address - except socket.error: + except (socket.error, OSError): # Not legal ip address pass return result @@ -108,7 +110,10 @@ def get_external_ip(): def start_server(): global _server if _server is None: - from calibre.utils.Zeroconf import Zeroconf + if ispy3: + from zeroconf import Zeroconf + else: + from calibre.utils.Zeroconf import Zeroconf try: _server = Zeroconf() except Exception: @@ -120,7 +125,7 @@ def start_server(): return _server -def create_service(desc, type, port, properties, add_hostname, use_ip_address=None): +def create_service(desc, service_type, port, properties, add_hostname, use_ip_address=None): port = int(port) try: hostname = ascii_text(force_unicode(socket.gethostname())).partition('.')[0] @@ -142,42 +147,60 @@ def create_service(desc, type, port, properties, add_hostname, use_ip_address=No local_ip = get_external_ip() if not local_ip: raise ValueError('Failed to determine local IP address to advertise via BonJour') - type = type+'.local.' - from calibre.utils.Zeroconf import ServiceInfo - return ServiceInfo(type, desc+'.'+type, - address=socket.inet_aton(local_ip), - port=port, - properties=properties, - server=hostname+'.local.') + service_type = service_type+'.local.' + service_name = desc + '.' + service_type + server_name = hostname+'.local.' + if ispy3: + from zeroconf import ServiceInfo + else: + from calibre.utils.Zeroconf import ServiceInfo + + def enc(x): + if isinstance(x, unicode_type): + x = x.encode('ascii') + return x + + service_type = enc(service_type) + service_name = enc(service_name) + server_name = enc(server_name) + if properties: + properties = {enc(k): enc(v) for k, v in iteritems(properties)} + + return ServiceInfo( + service_type, service_name, + address=socket.inet_aton(local_ip), + port=port, + properties=properties, + server=server_name) -def publish(desc, type, port, properties=None, add_hostname=True, use_ip_address=None): +def publish(desc, service_type, port, properties=None, add_hostname=True, use_ip_address=None): ''' Publish a service. :param desc: Description of service - :param type: Name and type of service. For example _stanza._tcp + :param service_type: Name and type of service. For example _stanza._tcp :param port: Port the service listens on :param properties: An optional dictionary whose keys and values will be put into the TXT record. ''' server = start_server() - service = create_service(desc, type, port, properties, add_hostname, + service = create_service(desc, service_type, port, properties, add_hostname, use_ip_address) - server.registerService(service) + server.register_service(service) return service -def unpublish(desc, type, port, properties=None, add_hostname=True): +def unpublish(desc, service_type, port, properties=None, add_hostname=True): ''' Unpublish a service. The parameters must be the same as used in the corresponding call to publish ''' server = start_server() - service = create_service(desc, type, port, properties, add_hostname) - server.unregisterService(service) - if server.countRegisteredServices() == 0: + service = create_service(desc, service_type, port, properties, add_hostname) + server.unregister_service(service) + if len(server.services) == 0: stop_server()