Update bundled zeroconf

It's internal structures that calibre uses/monkeypatches have changed,
neccessitating that the calibre code change as well.
This commit is contained in:
Kovid Goyal 2021-06-27 09:44:45 +05:30
parent b3e723e8ca
commit 1ec75da6e3
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 100 additions and 64 deletions

View File

@ -809,10 +809,9 @@
{ {
"name": "zeroconf", "name": "zeroconf",
"python": 3,
"unix": { "unix": {
"filename": "zeroconf-0.28.1.tar.gz", "filename": "zeroconf-0.31.0.tar.gz",
"hash": "sha256:902e6c3ca4cc752577d650d05a3e7102a897b647fe76da7c0d322cd493cbd1a3", "hash": "sha256:53a180248471c6f81bd1fffcbce03ed93d7d8eaf10905c9121ac1ea996d19844",
"urls": ["pypi"] "urls": ["pypi"]
} }
}, },

View File

@ -6,19 +6,28 @@ Created on 29 Jun 2012
@author: charles @author: charles
''' '''
import socket, select, json, os, traceback, time, sys, random import hashlib
import json
import os
import posixpath import posixpath
import random
import select
import socket
import sys
import threading
import time
import traceback
from collections import defaultdict from collections import defaultdict
import hashlib, threading
from functools import wraps
from errno import EAGAIN, EINTR from errno import EAGAIN, EINTR
from functools import wraps
from threading import Thread from threading import Thread
from calibre import prints from calibre import prints
from calibre.constants import numeric_version, DEBUG, cache_dir from calibre.constants import DEBUG, cache_dir, numeric_version
from calibre.devices.errors import (OpenFailed, OpenFeedback, ControlError, TimeoutError, from calibre.devices.errors import (
InitialConnectionError, PacketError, UserFeedback) ControlError, InitialConnectionError, OpenFailed, OpenFeedback, PacketError,
TimeoutError, UserFeedback
)
from calibre.devices.interface import DevicePlugin, currently_connected_device from calibre.devices.interface import DevicePlugin, currently_connected_device
from calibre.devices.usbms.books import Book, CollectionsBookList from calibre.devices.usbms.books import Book, CollectionsBookList
from calibre.devices.usbms.deviceconfig import DeviceConfig from calibre.devices.usbms.deviceconfig import DeviceConfig
@ -30,14 +39,15 @@ from calibre.ebooks.metadata.book.base import Metadata
from calibre.ebooks.metadata.book.json_codec import JsonCodec from calibre.ebooks.metadata.book.json_codec import JsonCodec
from calibre.library import current_library_name from calibre.library import current_library_name
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.ipc import eintr_retry_call
from calibre.utils.config_base import tweaks from calibre.utils.config_base import tweaks
from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to
from calibre.utils.mdns import (publish as publish_zeroconf, unpublish as from calibre.utils.ipc import eintr_retry_call
unpublish_zeroconf, get_all_ips) from calibre.utils.mdns import (
get_all_ips, publish as publish_zeroconf, unpublish as unpublish_zeroconf
)
from calibre.utils.socket_inheritance import set_socket_inherit from calibre.utils.socket_inheritance import set_socket_inherit
from polyglot.builtins import as_bytes, unicode_type, iteritems, itervalues
from polyglot import queue from polyglot import queue
from polyglot.builtins import as_bytes, iteritems, itervalues, unicode_type
def synchronous(tlockname): def synchronous(tlockname):
@ -423,8 +433,9 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
# copied from USBMS. Perhaps this could be a classmethod in usbms? # copied from USBMS. Perhaps this could be a classmethod in usbms?
def _update_driveinfo_record(self, dinfo, prefix, location_code, name=None): def _update_driveinfo_record(self, dinfo, prefix, location_code, name=None):
from calibre.utils.date import isoformat, now
import uuid import uuid
from calibre.utils.date import isoformat, now
if not isinstance(dinfo, dict): if not isinstance(dinfo, dict):
dinfo = {} dinfo = {}
if dinfo.get('device_store_uuid', None) is None: if dinfo.get('device_store_uuid', None) is None:
@ -485,8 +496,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
template = "{title}_%d-%d-%d" % date template = "{title}_%d-%d-%d" % date
use_subdirs = self.SUPPORTS_SUB_DIRS and settings.use_subdirs use_subdirs = self.SUPPORTS_SUB_DIRS and settings.use_subdirs
from calibre.library.save_to_disk import get_components from calibre.library.save_to_disk import config, get_components
from calibre.library.save_to_disk import config
opts = config().parse() opts = config().parse()
if not isinstance(template, unicode_type): if not isinstance(template, unicode_type):
template = template.decode('utf-8') template = template.decode('utf-8')
@ -947,8 +957,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
self.broadcast_socket = None self.broadcast_socket = None
def _read_file_metadata(self, temp_file_name): def _read_file_metadata(self, temp_file_name):
from calibre.ebooks.metadata.meta import get_metadata
from calibre.customize.ui import quick_metadata from calibre.customize.ui import quick_metadata
from calibre.ebooks.metadata.meta import get_metadata
ext = temp_file_name.rpartition('.')[-1].lower() ext = temp_file_name.rpartition('.')[-1].lower()
with lopen(temp_file_name, 'rb') as stream: with lopen(temp_file_name, 'rb') as stream:
with quick_metadata: with quick_metadata:
@ -1635,7 +1645,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
if not self.will_ask_for_update_books: if not self.will_ask_for_update_books:
return (None, False) return (None, False)
from calibre.utils.date import parse_date, isoformat from calibre.utils.date import isoformat, parse_date
try: try:
if not hasattr(book, '_format_mtime_'): if not hasattr(book, '_format_mtime_'):
return (None, False) return (None, False)
@ -1664,7 +1674,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
@synchronous('sync_lock') @synchronous('sync_lock')
def synchronize_with_db(self, db, id_, book, first_call): def synchronize_with_db(self, db, id_, book, first_call):
from calibre.utils.date import parse_date, is_date_undefined, now from calibre.utils.date import is_date_undefined, now, parse_date
if first_call: if first_call:
self.have_sent_future_dated_book_message = False self.have_sent_future_dated_book_message = False
@ -2041,13 +2051,15 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
# Copied from https://github.com/jstasiak/python-zeroconf version 0.28.1 # Copied from https://github.com/jstasiak/python-zeroconf version 0.28.1
from zeroconf import (BadTypeInNameException, _HAS_A_TO_Z, from zeroconf import (
_HAS_ONLY_A_TO_Z_NUM_HYPHEN_UNDERSCORE, _HAS_A_TO_Z, _HAS_ASCII_CONTROL_CHARS, _HAS_ONLY_A_TO_Z_NUM_HYPHEN,
_HAS_ASCII_CONTROL_CHARS, _HAS_ONLY_A_TO_Z_NUM_HYPHEN_UNDERSCORE, _LOCAL_TRAILER,
_HAS_ONLY_A_TO_Z_NUM_HYPHEN) _NONTCP_PROTOCOL_LOCAL_TRAILER, _TCP_PROTOCOL_LOCAL_TRAILER,
BadTypeInNameException
)
def service_type_name(type_: str, *, allow_underscores: bool = False) -> str: def service_type_name(type_: str, *, strict: bool = True) -> str:
""" """
Validate a fully qualified service name, instance or subtype. [rfc6763] Validate a fully qualified service name, instance or subtype. [rfc6763]
@ -2064,9 +2076,11 @@ def service_type_name(type_: str, *, allow_underscores: bool = False) -> str:
This is true because we are implementing mDNS and since the 'm' means This is true because we are implementing mDNS and since the 'm' means
multi-cast, the 'local.' domain is mandatory. multi-cast, the 'local.' domain is mandatory.
2) local is preceded with either '_udp.' or '_tcp.' 2) local is preceded with either '_udp.' or '_tcp.' unless
strict is False
3) service name <sn> precedes <_tcp|_udp> 3) service name <sn> precedes <_tcp|_udp> unless
strict is False
The rules for Service Names [RFC6335] state that they may be no more The rules for Service Names [RFC6335] state that they may be no more
than fifteen characters long (not counting the mandatory underscore), than fifteen characters long (not counting the mandatory underscore),
@ -2087,44 +2101,63 @@ def service_type_name(type_: str, *, allow_underscores: bool = False) -> str:
:param type_: Type, SubType or service name to validate :param type_: Type, SubType or service name to validate
:return: fully qualified service name (eg: _http._tcp.local.) :return: fully qualified service name (eg: _http._tcp.local.)
""" """
if not (type_.endswith('._tcp.local.') or type_.endswith('._udp.local.')):
raise BadTypeInNameException("Type '%s' must end with '._tcp.local.' or '._udp.local.'" % type_)
remaining = type_[: -len('._tcp.local.')].split('.') if type_.endswith(_TCP_PROTOCOL_LOCAL_TRAILER) or type_.endswith(_NONTCP_PROTOCOL_LOCAL_TRAILER):
name = remaining.pop() remaining = type_[: -len(_TCP_PROTOCOL_LOCAL_TRAILER)].split('.')
if not name: trailer = type_[-len(_TCP_PROTOCOL_LOCAL_TRAILER) :]
raise BadTypeInNameException("No Service name found") has_protocol = True
elif strict:
if len(remaining) == 1 and len(remaining[0]) == 0:
raise BadTypeInNameException("Type '%s' must not start with '.'" % type_)
if name[0] != '_':
raise BadTypeInNameException("Service name (%s) must start with '_'" % name)
# remove leading underscore
name = name[1:]
# if len(name) > 15:
# raise BadTypeInNameException("Service name (%s) must be <= 15 bytes" % name)
if '--' in name:
raise BadTypeInNameException("Service name (%s) must not contain '--'" % name)
if '-' in (name[0], name[-1]):
raise BadTypeInNameException("Service name (%s) may not start or end with '-'" % name)
if not _HAS_A_TO_Z.search(name):
raise BadTypeInNameException("Service name (%s) must contain at least one letter (eg: 'A-Z')" % name)
allowed_characters_re = (
_HAS_ONLY_A_TO_Z_NUM_HYPHEN_UNDERSCORE if allow_underscores else _HAS_ONLY_A_TO_Z_NUM_HYPHEN
)
if not allowed_characters_re.search(name):
raise BadTypeInNameException( raise BadTypeInNameException(
"Service name (%s) must contain only these characters: " "Type '%s' must end with '%s' or '%s'"
"A-Z, a-z, 0-9, hyphen ('-')%s" % (name, ", underscore ('_')" if allow_underscores else "") % (type_, _TCP_PROTOCOL_LOCAL_TRAILER, _NONTCP_PROTOCOL_LOCAL_TRAILER)
) )
elif type_.endswith(_LOCAL_TRAILER):
remaining = type_[: -len(_LOCAL_TRAILER)].split('.')
trailer = type_[-len(_LOCAL_TRAILER) + 1 :]
has_protocol = False
else:
raise BadTypeInNameException("Type '%s' must end with '%s'" % (type_, _LOCAL_TRAILER))
if strict or has_protocol:
service_name = remaining.pop()
if not service_name:
raise BadTypeInNameException("No Service name found")
if len(remaining) == 1 and len(remaining[0]) == 0:
raise BadTypeInNameException("Type '%s' must not start with '.'" % type_)
if service_name[0] != '_':
raise BadTypeInNameException("Service name (%s) must start with '_'" % service_name)
test_service_name = service_name[1:]
# if len(test_service_name) > 15:
# raise BadTypeInNameException("Service name (%s) must be <= 15 bytes" % test_service_name)
if '--' in test_service_name:
raise BadTypeInNameException("Service name (%s) must not contain '--'" % test_service_name)
if '-' in (test_service_name[0], test_service_name[-1]):
raise BadTypeInNameException(
"Service name (%s) may not start or end with '-'" % test_service_name
)
if not _HAS_A_TO_Z.search(test_service_name):
raise BadTypeInNameException(
"Service name (%s) must contain at least one letter (eg: 'A-Z')" % test_service_name
)
allowed_characters_re = (
_HAS_ONLY_A_TO_Z_NUM_HYPHEN if strict else _HAS_ONLY_A_TO_Z_NUM_HYPHEN_UNDERSCORE
)
if not allowed_characters_re.search(test_service_name):
raise BadTypeInNameException(
"Service name (%s) must contain only these characters: "
"A-Z, a-z, 0-9, hyphen ('-')%s" % (test_service_name, "" if strict else ", underscore ('_')")
)
else:
service_name = ''
if remaining and remaining[-1] == '_sub': if remaining and remaining[-1] == '_sub':
remaining.pop() remaining.pop()
@ -2144,4 +2177,4 @@ def service_type_name(type_: str, *, allow_underscores: bool = False) -> str:
"Ascii control character 0x00-0x1F and 0x7F illegal in '%s'" % remaining[0] "Ascii control character 0x00-0x1F and 0x7F illegal in '%s'" % remaining[0]
) )
return '_' + name + type_[-len('._tcp.local.') :] return service_name + trailer

View File

@ -180,7 +180,11 @@ def unpublish(desc, service_type, port, properties=None, add_hostname=True, wait
server = start_server() server = start_server()
service = create_service(desc, service_type, port, properties, add_hostname) service = create_service(desc, service_type, port, properties, add_hostname)
server.unregister_service(service) server.unregister_service(service)
if len(server.services) == 0: try:
no_services = len(server.registry.services) == 0
except AttributeError:
no_services = len(server.services) == 0
if no_services:
stop_server(wait_for_stop=wait_for_stop) stop_server(wait_for_stop=wait_for_stop)