diff --git a/examples/browser.py b/examples/browser.py index 17a778ac..c851be67 100755 --- a/examples/browser.py +++ b/examples/browser.py @@ -12,7 +12,7 @@ def on_service_state_change( - zeroconf: Zeroconf, service_type: str, name: str, state_change: ServiceStateChange, + zeroconf: Zeroconf, service_type: str, name: str, state_change: ServiceStateChange ) -> None: print("Service %s of type %s state changed: %s" % (name, service_type, state_change)) diff --git a/examples/registration.py b/examples/registration.py index ad707c22..7829acc9 100755 --- a/examples/registration.py +++ b/examples/registration.py @@ -17,10 +17,16 @@ desc = {'path': '/~paulsm/'} - info = ServiceInfo("_http._tcp.local.", - "Paul's Test Web Site._http._tcp.local.", - socket.inet_aton("127.0.0.1"), 80, 0, 0, - desc, "ash-2.local.") + info = ServiceInfo( + "_http._tcp.local.", + "Paul's Test Web Site._http._tcp.local.", + socket.inet_aton("127.0.0.1"), + 80, + 0, + 0, + desc, + "ash-2.local.", + ) zeroconf = Zeroconf() print("Registration of a service, press Ctrl-C to exit...") diff --git a/examples/self_test.py b/examples/self_test.py index c5b56ecb..6667d13e 100755 --- a/examples/self_test.py +++ b/examples/self_test.py @@ -18,15 +18,20 @@ r = Zeroconf() print("1. Testing registration of a service...") desc = {'version': '0.10', 'a': 'test value', 'b': 'another value'} - info = ServiceInfo("_http._tcp.local.", - "My Service Name._http._tcp.local.", - socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc) + info = ServiceInfo( + "_http._tcp.local.", + "My Service Name._http._tcp.local.", + socket.inet_aton("127.0.0.1"), + 1234, + 0, + 0, + desc, + ) print(" Registering service...") r.register_service(info) print(" Registration done.") print("2. Testing query of service information...") - print(" Getting ZOE service: %s" % ( - r.get_service_info("_http._tcp.local.", "ZOE._http._tcp.local."))) + print(" Getting ZOE service: %s" % (r.get_service_info("_http._tcp.local.", "ZOE._http._tcp.local."))) print(" Query done.") print("3. Testing query of own service...") queried_info = r.get_service_info("_http._tcp.local.", "My Service Name._http._tcp.local.") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..a5d30b54 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,4 @@ +[tool.black] +line-length = 110 +target_version = ['py35', 'py36', 'py37'] +skip_string_normalization = true diff --git a/setup.cfg b/setup.cfg index d39bf999..dd4764ed 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,6 +2,7 @@ show-source = 1 application-import-names=zeroconf max-line-length=110 +ignore=E203,W503 [mypy] ignore_missing_imports = true diff --git a/setup.py b/setup.py index ecfff3e6..b306619d 100755 --- a/setup.py +++ b/setup.py @@ -11,14 +11,14 @@ version = ( [l for l in open(join(PROJECT_ROOT, 'zeroconf.py')) if '__version__' in l][0] .split('=')[-1] - .strip().strip('\'"') + .strip() + .strip('\'"') ) setup( name='zeroconf', version=version, - description='Pure Python Multicast DNS Service Discovery Library ' - '(Bonjour/Avahi compatible)', + description='Pure Python Multicast DNS Service Discovery Library ' '(Bonjour/Avahi compatible)', long_description=readme, author='Paul Scott-Murphy, William McBrine, Jakub Stasiak', url='https://github.com/jstasiak/python-zeroconf', @@ -41,12 +41,6 @@ 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], - keywords=[ - 'Bonjour', 'Avahi', 'Zeroconf', 'Multicast DNS', 'Service Discovery', - 'mDNS', - ], - install_requires=[ - 'ifaddr', - 'typing;python_version<"3.5"' - ], + keywords=['Bonjour', 'Avahi', 'Zeroconf', 'Multicast DNS', 'Service Discovery', 'mDNS'], + install_requires=['ifaddr', 'typing;python_version<"3.5"'], ) diff --git a/test_zeroconf.py b/test_zeroconf.py index 525fdc55..9737d38f 100644 --- a/test_zeroconf.py +++ b/test_zeroconf.py @@ -42,7 +42,6 @@ def teardown_module(): class TestDunder(unittest.TestCase): - def test_dns_text_repr(self): # There was an issue on Python 3 that prevented DNSText's repr # from working when the text was longer than 10 bytes @@ -58,8 +57,7 @@ def test_dns_hinfo_repr_eq(self): repr(hinfo) def test_dns_pointer_repr(self): - pointer = r.DNSPointer( - 'irrelevant', r._TYPE_PTR, r._CLASS_IN, r._DNS_OTHER_TTL, '123') + pointer = r.DNSPointer('irrelevant', r._TYPE_PTR, r._CLASS_IN, r._DNS_OTHER_TTL, '123') repr(pointer) def test_dns_address_repr(self): @@ -67,14 +65,12 @@ def test_dns_address_repr(self): repr(address) def test_dns_question_repr(self): - question = r.DNSQuestion( - 'irrelevant', r._TYPE_SRV, r._CLASS_IN | r._CLASS_UNIQUE) + question = r.DNSQuestion('irrelevant', r._TYPE_SRV, r._CLASS_IN | r._CLASS_UNIQUE) repr(question) assert not question != question def test_dns_service_repr(self): - service = r.DNSService( - 'irrelevant', r._TYPE_SRV, r._CLASS_IN, r._DNS_HOST_TTL, 0, 0, 80, b'a') + service = r.DNSService('irrelevant', r._TYPE_SRV, r._CLASS_IN, r._DNS_HOST_TTL, 0, 0, 80, b'a') repr(service) def test_dns_record_abc(self): @@ -87,9 +83,8 @@ def test_service_info_dunder(self): name = "xxxyyy" registration_name = "%s.%s" % (name, type_) info = ServiceInfo( - type_, registration_name, - socket.inet_aton("10.0.1.2"), 80, 0, 0, - None, "ash-2.local.") + type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, None, "ash-2.local." + ) assert not info != info repr(info) @@ -99,9 +94,12 @@ def test_service_info_text_properties_not_given(self): name = "xxxyyy" registration_name = "%s.%s" % (name, type_) info = ServiceInfo( - type_=type_, name=registration_name, + type_=type_, + name=registration_name, address=socket.inet_aton("10.0.1.2"), - port=80, server="ash-2.local.") + port=80, + server="ash-2.local.", + ) assert isinstance(info.text, bytes) repr(info) @@ -112,7 +110,6 @@ def test_dns_outgoing_repr(self): class PacketGeneration(unittest.TestCase): - def test_parse_own_packet_simple(self): generated = r.DNSOutgoing(0) r.DNSIncoming(generated.packet()) @@ -127,14 +124,14 @@ def test_parse_own_packet_flags(self): def test_parse_own_packet_question(self): generated = r.DNSOutgoing(r._FLAGS_QR_QUERY) - generated.add_question(r.DNSQuestion("testname.local.", r._TYPE_SRV, - r._CLASS_IN)) + generated.add_question(r.DNSQuestion("testname.local.", r._TYPE_SRV, r._CLASS_IN)) r.DNSIncoming(generated.packet()) def test_parse_own_packet_response(self): generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) - generated.add_answer_at_time(r.DNSService( - "æøå.local.", r._TYPE_SRV, r._CLASS_IN, r._DNS_HOST_TTL, 0, 0, 80, "foo.local."), 0) + generated.add_answer_at_time( + r.DNSService("æøå.local.", r._TYPE_SRV, r._CLASS_IN, r._DNS_HOST_TTL, 0, 0, 80, "foo.local."), 0 + ) parsed = r.DNSIncoming(generated.packet()) self.assertEqual(len(generated.answers), 1) self.assertEqual(len(generated.answers), len(parsed.answers)) @@ -153,11 +150,14 @@ def test_suppress_answer(self): question = r.DNSQuestion("testname.local.", r._TYPE_SRV, r._CLASS_IN) query_generated.add_question(question) answer1 = r.DNSService( - "testname1.local.", r._TYPE_SRV, r._CLASS_IN, r._DNS_HOST_TTL, 0, 0, 80, "foo.local.") + "testname1.local.", r._TYPE_SRV, r._CLASS_IN, r._DNS_HOST_TTL, 0, 0, 80, "foo.local." + ) staleanswer2 = r.DNSService( - "testname2.local.", r._TYPE_SRV, r._CLASS_IN, r._DNS_HOST_TTL/2, 0, 0, 80, "foo.local.") + "testname2.local.", r._TYPE_SRV, r._CLASS_IN, r._DNS_HOST_TTL / 2, 0, 0, 80, "foo.local." + ) answer2 = r.DNSService( - "testname2.local.", r._TYPE_SRV, r._CLASS_IN, r._DNS_HOST_TTL, 0, 0, 80, "foo.local.") + "testname2.local.", r._TYPE_SRV, r._CLASS_IN, r._DNS_HOST_TTL, 0, 0, 80, "foo.local." + ) query_generated.add_answer_at_time(answer1, 0) query_generated.add_answer_at_time(staleanswer2, 0) query = r.DNSIncoming(query_generated.packet()) @@ -193,21 +193,18 @@ def test_suppress_answer(self): def test_dns_hinfo(self): generated = r.DNSOutgoing(0) - generated.add_additional_answer( - DNSHinfo('irrelevant', r._TYPE_HINFO, 0, 0, 'cpu', 'os')) + generated.add_additional_answer(DNSHinfo('irrelevant', r._TYPE_HINFO, 0, 0, 'cpu', 'os')) parsed = r.DNSIncoming(generated.packet()) answer = cast(r.DNSHinfo, parsed.answers[0]) self.assertEqual(answer.cpu, u'cpu') self.assertEqual(answer.os, u'os') generated = r.DNSOutgoing(0) - generated.add_additional_answer( - DNSHinfo('irrelevant', r._TYPE_HINFO, 0, 0, 'cpu', 'x' * 257)) + generated.add_additional_answer(DNSHinfo('irrelevant', r._TYPE_HINFO, 0, 0, 'cpu', 'x' * 257)) self.assertRaises(r.NamePartTooLongException, generated.packet) class PacketForm(unittest.TestCase): - def test_transaction_id(self): """ID must be zero in a DNS-SD packet""" generated = r.DNSOutgoing(r._FLAGS_QR_QUERY) @@ -230,8 +227,7 @@ def test_response_header_bits(self): def test_numbers(self): generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) bytes = generated.packet() - (num_questions, num_answers, num_authorities, - num_additionals) = struct.unpack('!4H', bytes[4:12]) + (num_questions, num_answers, num_authorities, num_additionals) = struct.unpack('!4H', bytes[4:12]) self.assertEqual(num_questions, 0) self.assertEqual(num_answers, 0) self.assertEqual(num_authorities, 0) @@ -243,8 +239,7 @@ def test_numbers_questions(self): for i in range(10): generated.add_question(question) bytes = generated.packet() - (num_questions, num_answers, num_authorities, - num_additionals) = struct.unpack('!4H', bytes[4:12]) + (num_questions, num_answers, num_authorities, num_additionals) = struct.unpack('!4H', bytes[4:12]) self.assertEqual(num_questions, 10) self.assertEqual(num_answers, 0) self.assertEqual(num_authorities, 0) @@ -252,11 +247,11 @@ def test_numbers_questions(self): class Names(unittest.TestCase): - def test_long_name(self): generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE) - question = r.DNSQuestion("this.is.a.very.long.name.with.lots.of.parts.in.it.local.", - r._TYPE_SRV, r._CLASS_IN) + question = r.DNSQuestion( + "this.is.a.very.long.name.with.lots.of.parts.in.it.local.", r._TYPE_SRV, r._CLASS_IN + ) generated.add_question(question) r.DNSIncoming(generated.packet()) @@ -323,8 +318,7 @@ def on_service_state_change(zeroconf, service_type, state_change, name): # wait until the browse request packet has maxed out in size sleep_count = 0 - while sleep_count < 100 and \ - longest_packet_len < r._MAX_MSG_ABSOLUTE - 100: + while sleep_count < 100 and longest_packet_len < r._MAX_MSG_ABSOLUTE - 100: sleep_count += 1 time.sleep(0.1) @@ -332,8 +326,8 @@ def on_service_state_change(zeroconf, service_type, state_change, name): time.sleep(0.5) import zeroconf - zeroconf.log.debug('sleep_count %d, sized %d', - sleep_count, longest_packet_len) + + zeroconf.log.debug('sleep_count %d, sized %d', sleep_count, longest_packet_len) # now the browser has sent at least one request, verify the size assert longest_packet_len <= r._MAX_MSG_ABSOLUTE @@ -341,6 +335,7 @@ def on_service_state_change(zeroconf, service_type, state_change, name): # mock zeroconf's logger warning() and debug() from unittest.mock import patch + patch_warn = patch('zeroconf.log.warning') patch_debug = patch('zeroconf.log.debug') mocked_log_warn = patch_warn.start() @@ -372,10 +367,9 @@ def on_service_state_change(zeroconf, service_type, state_change, name): s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT)) s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT)) time.sleep(2.0) - zeroconf.log.debug('warn %d debug %d was %s', - mocked_log_warn.call_count, - mocked_log_debug.call_count, - call_counts) + zeroconf.log.debug( + 'warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts + ) assert mocked_log_debug.call_count > call_counts[0] # close our zeroconf which will close the sockets @@ -389,17 +383,15 @@ def on_service_state_change(zeroconf, service_type, state_change, name): call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count # send on a closed socket (force a socket error) zc.send(out) - zeroconf.log.debug('warn %d debug %d was %s', - mocked_log_warn.call_count, - mocked_log_debug.call_count, - call_counts) + zeroconf.log.debug( + 'warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts + ) assert mocked_log_warn.call_count > call_counts[0] assert mocked_log_debug.call_count > call_counts[0] zc.send(out) - zeroconf.log.debug('warn %d debug %d was %s', - mocked_log_warn.call_count, - mocked_log_debug.call_count, - call_counts) + zeroconf.log.debug( + 'warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts + ) assert mocked_log_debug.call_count > call_counts[0] + 2 mocked_log_warn.stop() @@ -408,17 +400,14 @@ def on_service_state_change(zeroconf, service_type, state_change, name): def verify_name_change(self, zc, type_, name, number_hosts): desc = {'path': '/~paulsm/'} info_service = ServiceInfo( - type_, '%s.%s' % (name, type_), socket.inet_aton("10.0.1.2"), - 80, 0, 0, desc, "ash-2.local.") + type_, '%s.%s' % (name, type_), socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local." + ) # verify name conflict - self.assertRaises( - r.NonUniqueNameException, - zc.register_service, info_service) + self.assertRaises(r.NonUniqueNameException, zc.register_service, info_service) zc.register_service(info_service, allow_name_change=True) - assert info_service.name.split('.')[0] == '%s-%d' % ( - name, number_hosts + 1) + assert info_service.name.split('.')[0] == '%s-%d' % (name, number_hosts + 1) def generate_many_hosts(self, zc, type_, name, number_hosts): records_per_server = 2 @@ -429,9 +418,7 @@ def generate_many_hosts(self, zc, type_, name, number_hosts): self.generate_host(zc, next_name, type_) if i % block_size == 0: sleep_count = 0 - while sleep_count < 40 and \ - i * records_per_server > len( - zc.cache.entries_with_name(type_)): + while sleep_count < 40 and i * records_per_server > len(zc.cache.entries_with_name(type_)): sleep_count += 1 time.sleep(0.05) @@ -439,18 +426,14 @@ def generate_many_hosts(self, zc, type_, name, number_hosts): def generate_host(zc, host_name, type_): name = '.'.join((host_name, type_)) out = r.DNSOutgoing(r._FLAGS_QR_RESPONSE | r._FLAGS_AA) + out.add_answer_at_time(r.DNSPointer(type_, r._TYPE_PTR, r._CLASS_IN, r._DNS_OTHER_TTL, name), 0) out.add_answer_at_time( - r.DNSPointer(type_, r._TYPE_PTR, r._CLASS_IN, - r._DNS_OTHER_TTL, name), 0) - out.add_answer_at_time( - r.DNSService(type_, r._TYPE_SRV, r._CLASS_IN, - r._DNS_HOST_TTL, 0, 0, 80, - name), 0) + r.DNSService(type_, r._TYPE_SRV, r._CLASS_IN, r._DNS_HOST_TTL, 0, 0, 80, name), 0 + ) zc.send(out) class Framework(unittest.TestCase): - def test_launch_and_close(self): rv = r.Zeroconf(interfaces=r.InterfaceChoice.All) rv.close() @@ -472,9 +455,7 @@ def tearDownClass(cls): cls.browser = None def test_bad_service_info_name(self): - self.assertRaises( - r.BadTypeInNameException, - self.browser.get_service_info, "type", "type_not") + self.assertRaises(r.BadTypeInNameException, self.browser.get_service_info, "type", "type_not") def test_bad_service_names(self): bad_names_to_try = ( @@ -494,15 +475,13 @@ def test_bad_service_names(self): '\x00._x._udp.local.', ) for name in bad_names_to_try: - self.assertRaises( - r.BadTypeInNameException, - self.browser.get_service_info, name, 'x.' + name) + self.assertRaises(r.BadTypeInNameException, self.browser.get_service_info, name, 'x.' + name) def test_good_instance_names(self): good_names_to_try = ( '.._x._tcp.local.', 'x.sub._http._tcp.local.', - '6d86f882b90facee9170ad3439d72a4d6ee9f511._zget._http._tcp.local.' + '6d86f882b90facee9170ad3439d72a4d6ee9f511._zget._http._tcp.local.', ) for name in good_names_to_try: r.service_type_name(name) @@ -514,8 +493,7 @@ def test_bad_types(self): 'a' * 62 + u'â._sub._http._tcp.local.', ) for name in bad_names_to_try: - self.assertRaises( - r.BadTypeInNameException, r.service_type_name, name) + self.assertRaises(r.BadTypeInNameException, r.service_type_name, name) def test_bad_sub_types(self): bad_names_to_try = ( @@ -525,8 +503,7 @@ def test_bad_sub_types(self): '\x1f._sub._http._tcp.local.', ) for name in bad_names_to_try: - self.assertRaises( - r.BadTypeInNameException, r.service_type_name, name) + self.assertRaises(r.BadTypeInNameException, r.service_type_name, name) def test_good_service_names(self): good_names_to_try = ( @@ -544,7 +521,6 @@ def test_good_service_names(self): class TestDnsIncoming(unittest.TestCase): - def test_incoming_exception_handling(self): generated = r.DNSOutgoing(0) packet = generated.packet() @@ -569,7 +545,6 @@ def test_incoming_ipv6(self): class TestRegistrar(unittest.TestCase): - def test_ttl(self): # instantiate a zeroconf instance @@ -582,9 +557,8 @@ def test_ttl(self): desc = {'path': '/~paulsm/'} info = ServiceInfo( - type_, registration_name, - socket.inet_aton("10.0.1.2"), 80, 0, 0, - desc, "ash-2.local.") + type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local." + ) # we are going to monkey patch the zeroconf send to check packet sizes old_send = zc.send @@ -664,7 +638,6 @@ def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT): class TestDNSCache(unittest.TestCase): - def test_order(self): record1 = r.DNSAddress('a', r._TYPE_SOA, r._CLASS_IN, 1, b'a') record2 = r.DNSAddress('a', r._TYPE_SOA, r._CLASS_IN, 1, b'b') @@ -677,7 +650,6 @@ def test_order(self): class ServiceTypesQuery(unittest.TestCase): - def test_integration_with_listener(self): type_ = "_test-srvc-type._tcp.local." @@ -687,17 +659,14 @@ def test_integration_with_listener(self): zeroconf_registrar = Zeroconf(interfaces=['127.0.0.1']) desc = {'path': '/~paulsm/'} info = ServiceInfo( - type_, registration_name, - socket.inet_aton("10.0.1.2"), 80, 0, 0, - desc, "ash-2.local.") + type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local." + ) zeroconf_registrar.register_service(info) try: - service_types = ZeroconfServiceTypes.find( - interfaces=['127.0.0.1'], timeout=0.5) + service_types = ZeroconfServiceTypes.find(interfaces=['127.0.0.1'], timeout=0.5) assert type_ in service_types - service_types = ZeroconfServiceTypes.find( - zc=zeroconf_registrar, timeout=0.5) + service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=0.5) assert type_ in service_types finally: @@ -714,17 +683,14 @@ def test_integration_with_subtype_and_listener(self): zeroconf_registrar = Zeroconf(interfaces=['127.0.0.1']) desc = {'path': '/~paulsm/'} info = ServiceInfo( - discovery_type, registration_name, - socket.inet_aton("10.0.1.2"), 80, 0, 0, - desc, "ash-2.local.") + discovery_type, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local." + ) zeroconf_registrar.register_service(info) try: - service_types = ZeroconfServiceTypes.find( - interfaces=['127.0.0.1'], timeout=0.5) + service_types = ZeroconfServiceTypes.find(interfaces=['127.0.0.1'], timeout=0.5) assert discovery_type in service_types - service_types = ZeroconfServiceTypes.find( - zc=zeroconf_registrar, timeout=0.5) + service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar, timeout=0.5) assert discovery_type in service_types finally: @@ -732,7 +698,6 @@ def test_integration_with_subtype_and_listener(self): class ListenerTest(unittest.TestCase): - def test_integration_with_listener_class(self): service_added = Event() @@ -780,9 +745,8 @@ def update_service(self, zeroconf, type, name): desc = {'path': '/~paulsm/'} # type: r.ServicePropertiesType desc.update(properties) info_service = ServiceInfo( - subtype, registration_name, - socket.inet_aton("10.0.1.2"), 80, 0, 0, - desc, "ash-2.local.") + subtype, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local." + ) zeroconf_registrar.register_service(info_service) try: @@ -816,9 +780,8 @@ def update_service(self, zeroconf, type, name): properties['prop_blank'] = b'an updated string' desc.update(properties) info_service = ServiceInfo( - subtype, registration_name, - socket.inet_aton("10.0.1.2"), 80, 0, 0, - desc, "ash-2.local.") + subtype, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local." + ) zeroconf_registrar.update_service(info_service) service_updated.wait(1) assert service_updated.is_set() @@ -895,7 +858,7 @@ def on_service_state_change(zeroconf, service_type, state_change, name): next_query_interval = initial_query_interval expected_query_time = initial_query_interval else: - next_query_interval = min(2*next_query_interval, r._BROWSER_BACKOFF_LIMIT) + next_query_interval = min(2 * next_query_interval, r._BROWSER_BACKOFF_LIMIT) expected_query_time += next_query_interval else: assert not got_query.is_set() @@ -965,10 +928,7 @@ def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT): zeroconf_registrar = Zeroconf(interfaces=['127.0.0.1']) desc = {'path': '/~paulsm/'} - info = ServiceInfo( - type_, registration_name, - socket.inet_aton("10.0.1.2"), 80, 0, 0, - desc, "ash-2.local.") + info = ServiceInfo(type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") zeroconf_registrar.register_service(info) try: diff --git a/zeroconf.py b/zeroconf.py index 26400dd0..b8c66473 100644 --- a/zeroconf.py +++ b/zeroconf.py @@ -44,15 +44,21 @@ __all__ = [ "__version__", - "Zeroconf", "ServiceInfo", "ServiceBrowser", - "Error", "InterfaceChoice", "ServiceStateChange", + "Zeroconf", + "ServiceInfo", + "ServiceBrowser", + "Error", + "InterfaceChoice", + "ServiceStateChange", ] if sys.version_info <= (3, 3): - raise ImportError(''' + raise ImportError( + ''' Python version > 3.3 required for python-zeroconf. If you need support for Python 2 or Python 3.3 please use version 19.1 - ''') + ''' + ) log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) @@ -124,32 +130,36 @@ # Mapping constants to names -_CLASSES = {_CLASS_IN: "in", - _CLASS_CS: "cs", - _CLASS_CH: "ch", - _CLASS_HS: "hs", - _CLASS_NONE: "none", - _CLASS_ANY: "any"} - -_TYPES = {_TYPE_A: "a", - _TYPE_NS: "ns", - _TYPE_MD: "md", - _TYPE_MF: "mf", - _TYPE_CNAME: "cname", - _TYPE_SOA: "soa", - _TYPE_MB: "mb", - _TYPE_MG: "mg", - _TYPE_MR: "mr", - _TYPE_NULL: "null", - _TYPE_WKS: "wks", - _TYPE_PTR: "ptr", - _TYPE_HINFO: "hinfo", - _TYPE_MINFO: "minfo", - _TYPE_MX: "mx", - _TYPE_TXT: "txt", - _TYPE_AAAA: "quada", - _TYPE_SRV: "srv", - _TYPE_ANY: "any"} +_CLASSES = { + _CLASS_IN: "in", + _CLASS_CS: "cs", + _CLASS_CH: "ch", + _CLASS_HS: "hs", + _CLASS_NONE: "none", + _CLASS_ANY: "any", +} + +_TYPES = { + _TYPE_A: "a", + _TYPE_NS: "ns", + _TYPE_MD: "md", + _TYPE_MF: "mf", + _TYPE_CNAME: "cname", + _TYPE_SOA: "soa", + _TYPE_MB: "mb", + _TYPE_MG: "mg", + _TYPE_MR: "mr", + _TYPE_NULL: "null", + _TYPE_WKS: "wks", + _TYPE_PTR: "ptr", + _TYPE_HINFO: "hinfo", + _TYPE_MINFO: "minfo", + _TYPE_MX: "mx", + _TYPE_TXT: "txt", + _TYPE_AAAA: "quada", + _TYPE_SRV: "srv", + _TYPE_ANY: "any", +} _HAS_A_TO_Z = re.compile(r'[A-Za-z]') _HAS_ONLY_A_TO_Z_NUM_HYPHEN = re.compile(r'^[A-Za-z0-9\-]+$') @@ -171,6 +181,7 @@ class ServiceStateChange(enum.Enum): Removed = 2 Updated = 3 + # utility functions @@ -220,58 +231,48 @@ def service_type_name(type_, *, allow_underscores: bool = False): :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_) + raise BadTypeInNameException("Type '%s' must end with '._tcp.local.' or '._udp.local.'" % type_) - remaining = type_[:-len('._tcp.local.')].split('.') + remaining = type_[: -len('._tcp.local.')].split('.') name = remaining.pop() if not 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_) + raise BadTypeInNameException("Type '%s' must not start with '.'" % type_) if name[0] != '_': - raise BadTypeInNameException( - "Service name (%s) must start with '_'" % name) + 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) + raise BadTypeInNameException("Service name (%s) must be <= 15 bytes" % name) if '--' in name: - raise BadTypeInNameException( - "Service name (%s) must not contain '--'" % 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) + 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) + 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 + _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( "Service name (%s) must contain only these characters: " - "A-Z, a-z, 0-9, hyphen ('-')%s" % (name, ", underscore ('_')" if allow_underscores else "")) + "A-Z, a-z, 0-9, hyphen ('-')%s" % (name, ", underscore ('_')" if allow_underscores else "") + ) if remaining and remaining[-1] == '_sub': remaining.pop() if len(remaining) == 0 or len(remaining[0]) == 0: - raise BadTypeInNameException( - "_sub requires a subtype name") + raise BadTypeInNameException("_sub requires a subtype name") if len(remaining) > 1: remaining = ['.'.join(remaining)] @@ -283,10 +284,10 @@ def service_type_name(type_, *, allow_underscores: bool = False): if _HAS_ASCII_CONTROL_CHARS.search(remaining[0]): raise BadTypeInNameException( - "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 '_' + name + type_[-len('._tcp.local.') :] # Exceptions @@ -361,10 +362,12 @@ def __init__(self, name: str, type_: int, class_): def __eq__(self, other): """Equality test on name, type, and class""" - return (isinstance(other, DNSEntry) and - self.name == other.name and - self.type == other.type and - self.class_ == other.class_) + return ( + isinstance(other, DNSEntry) + and self.name == other.name + and self.type == other.type + and self.class_ == other.class_ + ) def __ne__(self, other): """Non-equality test""" @@ -382,8 +385,7 @@ def get_type(t): def to_string(self, hdr, other) -> str: """String representation with additional information""" - result = "%s[%s,%s" % (hdr, self.get_type(self.type), - self.get_class_(self.class_)) + result = "%s[%s,%s" % (hdr, self.get_type(self.type), self.get_class_(self.class_)) if self.unique: result += "-unique," else: @@ -405,9 +407,11 @@ def __init__(self, name: str, type_: int, class_: int) -> None: def answered_by(self, rec: 'DNSRecord') -> bool: """Returns true if the question is answered by the record""" - return (self.class_ == rec.class_ and - (self.type == rec.type or self.type == _TYPE_ANY) and - self.name == rec.name) + return ( + self.class_ == rec.class_ + and (self.type == rec.type or self.type == _TYPE_ANY) + and self.name == rec.name + ) def __repr__(self) -> str: """String representation""" @@ -473,8 +477,7 @@ def write(self, out): def to_string(self, other): """String representation with additional information""" - arg = "%s/%s,%s" % ( - self.ttl, self.get_remaining_ttl(current_time_millis()), other) + arg = "%s/%s,%s" % (self.ttl, self.get_remaining_ttl(current_time_millis()), other) return DNSEntry.to_string(self, "record", arg) @@ -492,8 +495,9 @@ def write(self, out): def __eq__(self, other): """Tests equality on address""" - return (isinstance(other, DNSAddress) and DNSEntry.__eq__(self, other) and - self.address == other.address) + return ( + isinstance(other, DNSAddress) and DNSEntry.__eq__(self, other) and self.address == other.address + ) def __ne__(self, other): """Non-equality test""" @@ -529,8 +533,12 @@ def write(self, out): def __eq__(self, other): """Tests equality on cpu and os""" - return (isinstance(other, DNSHinfo) and DNSEntry.__eq__(self, other) and - self.cpu == other.cpu and self.os == other.os) + return ( + isinstance(other, DNSHinfo) + and DNSEntry.__eq__(self, other) + and self.cpu == other.cpu + and self.os == other.os + ) def __ne__(self, other): """Non-equality test""" @@ -555,8 +563,7 @@ def write(self, out): def __eq__(self, other): """Tests equality on alias""" - return (isinstance(other, DNSPointer) and DNSEntry.__eq__(self, other) and - self.alias == other.alias) + return isinstance(other, DNSPointer) and DNSEntry.__eq__(self, other) and self.alias == other.alias def __ne__(self, other): """Non-equality test""" @@ -582,8 +589,7 @@ def write(self, out): def __eq__(self, other): """Tests equality on text""" - return (isinstance(other, DNSText) and DNSEntry.__eq__(self, other) and - self.text == other.text) + return isinstance(other, DNSText) and DNSEntry.__eq__(self, other) and self.text == other.text def __ne__(self, other): """Non-equality test""" @@ -601,8 +607,7 @@ class DNSService(DNSRecord): """A DNS service record""" - def __init__(self, name, type_, class_, ttl, - priority, weight, port, server): + def __init__(self, name, type_, class_, ttl, priority, weight, port, server): DNSRecord.__init__(self, name, type_, class_, ttl) self.priority = priority self.weight = weight @@ -618,12 +623,14 @@ def write(self, out): def __eq__(self, other): """Tests equality on priority, weight, port and server""" - return (isinstance(other, DNSService) and - DNSEntry.__eq__(self, other) and - self.priority == other.priority and - self.weight == other.weight and - self.port == other.port and - self.server == other.server) + return ( + isinstance(other, DNSService) + and DNSEntry.__eq__(self, other) + and self.priority == other.priority + and self.weight == other.weight + and self.port == other.port + and self.server == other.server + ) def __ne__(self, other): """Non-equality test""" @@ -659,20 +666,24 @@ def __init__(self, data): self.valid = True except (IndexError, struct.error, IncomingDecodeError): - self.log_exception_warning(( - 'Choked at offset %d while unpacking %r', self.offset, data)) + self.log_exception_warning(('Choked at offset %d while unpacking %r', self.offset, data)) def unpack(self, format_): length = struct.calcsize(format_) - info = struct.unpack( - format_, self.data[self.offset:self.offset + length]) + info = struct.unpack(format_, self.data[self.offset : self.offset + length]) self.offset += length return info def read_header(self): """Reads header portion of packet""" - (self.id, self.flags, self.num_questions, self.num_answers, - self.num_authorities, self.num_additionals) = self.unpack(b'!6H') + ( + self.id, + self.flags, + self.num_questions, + self.num_answers, + self.num_authorities, + self.num_additionals, + ) = self.unpack(b'!6H') def read_questions(self): """Reads questions section of packet""" @@ -695,7 +706,7 @@ def read_character_string(self): def read_string(self, length): """Reads a string of a given length from the packet""" - info = self.data[self.offset:self.offset + length] + info = self.data[self.offset : self.offset + length] self.offset += length return info @@ -713,26 +724,28 @@ def read_others(self): rec = None # type: Optional[DNSRecord] if type_ == _TYPE_A: - rec = DNSAddress( - domain, type_, class_, ttl, self.read_string(4)) + rec = DNSAddress(domain, type_, class_, ttl, self.read_string(4)) elif type_ == _TYPE_CNAME or type_ == _TYPE_PTR: - rec = DNSPointer( - domain, type_, class_, ttl, self.read_name()) + rec = DNSPointer(domain, type_, class_, ttl, self.read_name()) elif type_ == _TYPE_TXT: - rec = DNSText( - domain, type_, class_, ttl, self.read_string(length)) + rec = DNSText(domain, type_, class_, ttl, self.read_string(length)) elif type_ == _TYPE_SRV: rec = DNSService( - domain, type_, class_, ttl, - self.read_unsigned_short(), self.read_unsigned_short(), - self.read_unsigned_short(), self.read_name()) + domain, + type_, + class_, + ttl, + self.read_unsigned_short(), + self.read_unsigned_short(), + self.read_unsigned_short(), + self.read_name(), + ) elif type_ == _TYPE_HINFO: rec = DNSHinfo( - domain, type_, class_, ttl, - self.read_character_string(), self.read_character_string()) + domain, type_, class_, ttl, self.read_character_string(), self.read_character_string() + ) elif type_ == _TYPE_AAAA: - rec = DNSAddress( - domain, type_, class_, ttl, self.read_string(16)) + rec = DNSAddress(domain, type_, class_, ttl, self.read_string(16)) else: # Try to ignore types we don't know about # Skip the payload for the resource record so the next @@ -752,7 +765,7 @@ def is_response(self): def read_utf(self, offset, length): """Reads a UTF-8 string of a given length from the packet""" - return str(self.data[offset:offset + length], 'utf-8', 'replace') + return str(self.data[offset : offset + length], 'utf-8', 'replace') def read_name(self): """Reads a domain name from the packet""" @@ -775,8 +788,7 @@ def read_name(self): next_ = off + 1 off = ((length & 0x3F) << 8) | self.data[off] if off >= first: - raise IncomingDecodeError( - "Bad domain name (circular) at %s" % (off,)) + raise IncomingDecodeError("Bad domain name (circular) at %s" % (off,)) first = off else: raise IncomingDecodeError("Bad domain name at %s" % (off,)) @@ -809,14 +821,16 @@ def __init__(self, flags, multicast=True): self.additionals = [] # type: List[DNSAddress] def __repr__(self): - return '' % ', '.join([ - 'multicast=%s' % self.multicast, - 'flags=%s' % self.flags, - 'questions=%s' % self.questions, - 'answers=%s' % self.answers, - 'authorities=%s' % self.authorities, - 'additionals=%s' % self.additionals, - ]) + return '' % ', '.join( + [ + 'multicast=%s' % self.multicast, + 'flags=%s' % self.flags, + 'questions=%s' % self.questions, + 'answers=%s' % self.answers, + 'authorities=%s' % self.authorities, + 'additionals=%s' % self.additionals, + ] + ) class State(enum.Enum): init = 0 @@ -1091,9 +1105,7 @@ def entries_with_name(self, name): def current_entry_with_name_and_alias(self, name, alias): now = current_time_millis() for record in self.entries_with_name(name): - if (record.type == _TYPE_PTR and - not record.is_expired(now) and - record.alias == alias): + if record.type == _TYPE_PTR and not record.is_expired(now) and record.alias == alias: return record def entries(self): @@ -1242,7 +1254,6 @@ def registration_interface(self) -> 'SignalRegistrationInterface': class SignalRegistrationInterface: - def __init__(self, handlers): self._handlers = handlers @@ -1279,20 +1290,27 @@ class ServiceBrowser(RecordUpdateListener, threading.Thread): remove_service() methods called when this browser discovers changes in the services availability.""" - def __init__(self, zc: 'Zeroconf', type_: str, handlers=None, listener=None, - addr: str = _MDNS_ADDR, port: int = _MDNS_PORT, delay: int = _BROWSER_TIME) -> None: + def __init__( + self, + zc: 'Zeroconf', + type_: str, + handlers=None, + listener=None, + addr: str = _MDNS_ADDR, + port: int = _MDNS_PORT, + delay: int = _BROWSER_TIME, + ) -> None: """Creates a browser for a specific type""" assert handlers or listener, 'You need to specify at least one handler' if not type_.endswith(service_type_name(type_, allow_underscores=True)): raise BadTypeInNameException - threading.Thread.__init__( - self, name='zeroconf-ServiceBrowser_' + type_) + threading.Thread.__init__(self, name='zeroconf-ServiceBrowser_' + type_) self.daemon = True self.zc = zc self.type = type_ self.addr = addr self.port = port - self.multicast = (self.addr == _MDNS_ADDR) + self.multicast = self.addr == _MDNS_ADDR self.services = {} # type: Dict[str, DNSRecord] self.next_time = current_time_millis() self.delay = delay @@ -1309,6 +1327,7 @@ def __init__(self, zc: 'Zeroconf', type_: str, handlers=None, listener=None, handlers = handlers or [] if listener: + def on_change(zeroconf, service_type, name, state_change): args = (zeroconf, service_type, name) if state_change is ServiceStateChange.Added: @@ -1320,6 +1339,7 @@ def on_change(zeroconf, service_type, name, state_change): listener.update_service(*args) else: raise NotImplementedError(state_change) + handlers.append(on_change) for h in handlers: @@ -1339,11 +1359,9 @@ def update_record(self, zc: 'Zeroconf', now: float, record: DNSRecord) -> None: def enqueue_callback(state_change: ServiceStateChange, name: str) -> None: self._handlers_to_call.append( lambda zeroconf: self._service_state_changed.fire( - zeroconf=zeroconf, - service_type=self.type, - name=name, - state_change=state_change, - )) + zeroconf=zeroconf, service_type=self.type, name=name, state_change=state_change + ) + ) if record.type == _TYPE_PTR and record.name == self.type: assert isinstance(record, DNSPointer) @@ -1411,9 +1429,19 @@ class ServiceInfo(RecordUpdateListener): """Service information""" - def __init__(self, type_: str, name: str, address: Optional[bytes] = None, port: Optional[int] = None, - weight: int = 0, priority: int = 0, properties=b'', server: Optional[str] = None, - host_ttl: int = _DNS_HOST_TTL, other_ttl: int = _DNS_OTHER_TTL) -> None: + def __init__( + self, + type_: str, + name: str, + address: Optional[bytes] = None, + port: Optional[int] = None, + weight: int = 0, + priority: int = 0, + properties=b'', + server: Optional[str] = None, + host_ttl: int = _DNS_HOST_TTL, + other_ttl: int = _DNS_OTHER_TTL, + ) -> None: """Create a service description. type_: fully qualified service type name @@ -1489,7 +1517,7 @@ def _set_text(self, text): while index < end: length = text[index] index += 1 - strs.append(text[index:index + length]) + strs.append(text[index : index + length]) index += length for s in strs: @@ -1515,7 +1543,7 @@ def _set_text(self, text): def get_name(self): """Name accessor""" if self.type is not None and self.name.endswith("." + self.type): - return self.name[:len(self.name) - len(self.type) - 1] + return self.name[: len(self.name) - len(self.type) - 1] return self.name def update_record(self, zc: 'Zeroconf', now: float, record: DNSRecord) -> None: @@ -1534,9 +1562,7 @@ def update_record(self, zc: 'Zeroconf', now: float, record: DNSRecord) -> None: self.weight = record.weight self.priority = record.priority # self.address = None - self.update_record( - zc, now, zc.cache.get_by_details( - self.server, _TYPE_A, _CLASS_IN)) + self.update_record(zc, now, zc.cache.get_by_details(self.server, _TYPE_A, _CLASS_IN)) elif record.type == _TYPE_TXT: assert isinstance(record, DNSText) if record.name == self.name: @@ -1551,10 +1577,7 @@ def request(self, zc: 'Zeroconf', timeout: float) -> bool: next_ = now + delay last = now + timeout - record_types_for_check_cache = [ - (_TYPE_SRV, _CLASS_IN), - (_TYPE_TXT, _CLASS_IN), - ] + record_types_for_check_cache = [(_TYPE_SRV, _CLASS_IN), (_TYPE_TXT, _CLASS_IN)] if self.server is not None: record_types_for_check_cache.append((_TYPE_A, _CLASS_IN)) for record_type in record_types_for_check_cache: @@ -1572,24 +1595,15 @@ def request(self, zc: 'Zeroconf', timeout: float) -> bool: return False if next_ <= now: out = DNSOutgoing(_FLAGS_QR_QUERY) - out.add_question( - DNSQuestion(self.name, _TYPE_SRV, _CLASS_IN)) - out.add_answer_at_time( - zc.cache.get_by_details( - self.name, _TYPE_SRV, _CLASS_IN), now) + out.add_question(DNSQuestion(self.name, _TYPE_SRV, _CLASS_IN)) + out.add_answer_at_time(zc.cache.get_by_details(self.name, _TYPE_SRV, _CLASS_IN), now) - out.add_question( - DNSQuestion(self.name, _TYPE_TXT, _CLASS_IN)) - out.add_answer_at_time( - zc.cache.get_by_details( - self.name, _TYPE_TXT, _CLASS_IN), now) + out.add_question(DNSQuestion(self.name, _TYPE_TXT, _CLASS_IN)) + out.add_answer_at_time(zc.cache.get_by_details(self.name, _TYPE_TXT, _CLASS_IN), now) if self.server is not None: - out.add_question( - DNSQuestion(self.server, _TYPE_A, _CLASS_IN)) - out.add_answer_at_time( - zc.cache.get_by_details( - self.server, _TYPE_A, _CLASS_IN), now) + out.add_question(DNSQuestion(self.server, _TYPE_A, _CLASS_IN)) + out.add_answer_at_time(zc.cache.get_by_details(self.server, _TYPE_A, _CLASS_IN), now) zc.send(out) next_ = now + delay delay *= 2 @@ -1615,11 +1629,8 @@ def __repr__(self) -> str: type(self).__name__, ', '.join( '%s=%r' % (name, getattr(self, name)) - for name in ( - 'type', 'name', 'address', 'port', 'weight', 'priority', - 'server', 'properties', - ) - ) + for name in ('type', 'name', 'address', 'port', 'weight', 'priority', 'server', 'properties') + ), ) @@ -1627,6 +1638,7 @@ class ZeroconfServiceTypes(ServiceListener): """ Return all of the advertised services on any local networks """ + def __init__(self): self.found_services = set() # type: Set[str] @@ -1648,8 +1660,7 @@ def find(cls, zc=None, timeout=5, interfaces=InterfaceChoice.All): """ local_zc = zc or Zeroconf(interfaces=interfaces) listener = cls() - browser = ServiceBrowser( - local_zc, '_services._dns-sd._udp.local.', listener=listener) + browser = ServiceBrowser(local_zc, '_services._dns-sd._udp.local.', listener=listener) # wait for responses time.sleep(timeout) @@ -1664,12 +1675,14 @@ def find(cls, zc=None, timeout=5, interfaces=InterfaceChoice.All): def get_all_addresses() -> List[str]: - return list(set( - addr.ip - for iface in ifaddr.get_adapters() - for addr in iface.ips - if addr.is_IPv4 and addr.network_prefix != 32 # Host only netmask 255.255.255.255 - )) + return list( + set( + addr.ip + for iface in ifaddr.get_adapters() + for addr in iface.ips + if addr.is_IPv4 and addr.network_prefix != 32 # Host only netmask 255.255.255.255 + ) + ) def normalize_interface_choice(choice: Union[List[str], InterfaceChoice]) -> List[str]: @@ -1730,9 +1743,7 @@ class Zeroconf(QuietLogger): """ def __init__( - self, - interfaces: Union[List[str], InterfaceChoice] = InterfaceChoice.All, - unicast: bool = False + self, interfaces: Union[List[str], InterfaceChoice] = InterfaceChoice.All, unicast: bool = False ) -> None: """Creates an instance of the Zeroconf class, establishing multicast communications, listening and reaping threads. @@ -1754,34 +1765,31 @@ def __init__( log.debug('Adding %r to multicast group', i) try: _value = socket.inet_aton(_MDNS_ADDR) + socket.inet_aton(i) - self._listen_socket.setsockopt( - socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, _value) + self._listen_socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, _value) except socket.error as e: _errno = get_errno(e) if _errno == errno.EADDRINUSE: log.info( 'Address in use when adding %s to multicast group, ' - 'it is expected to happen on some systems', i, + 'it is expected to happen on some systems', + i, ) elif _errno == errno.EADDRNOTAVAIL: log.info( 'Address not available when adding %s to multicast ' - 'group, it is expected to happen on some systems', i, + 'group, it is expected to happen on some systems', + i, ) continue elif _errno == errno.EINVAL: - log.info( - 'Interface of %s does not support multicast, ' - 'it is expected in WSL', i - ) + log.info('Interface of %s does not support multicast, ' 'it is expected in WSL', i) continue else: raise respond_socket = new_socket() - respond_socket.setsockopt( - socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(i)) + respond_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(i)) else: respond_socket = new_socket(port=0) @@ -1850,7 +1858,7 @@ def remove_all_service_listeners(self) -> None: self.remove_service_listener(listener) def register_service( - self, info: ServiceInfo, ttl: Optional[int] = None, allow_name_change: bool = False, + self, info: ServiceInfo, ttl: Optional[int] = None, allow_name_change: bool = False ) -> None: """Registers service information to the network with a default TTL. Zeroconf will then respond to requests for information for that @@ -1870,9 +1878,7 @@ def register_service( self._broadcast_service(info) - def update_service( - self, info: ServiceInfo - ) -> None: + def update_service(self, info: ServiceInfo) -> None: """Registers service information to the network with a default TTL. Zeroconf will then respond to requests for information for that service.""" @@ -1894,19 +1900,26 @@ def _broadcast_service(self, info: ServiceInfo) -> None: now = current_time_millis() continue out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) + out.add_answer_at_time(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, info.other_ttl, info.name), 0) out.add_answer_at_time( - DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, info.other_ttl, info.name), 0) - out.add_answer_at_time( - DNSService(info.name, _TYPE_SRV, _CLASS_IN, - info.host_ttl, info.priority, info.weight, info.port, - info.server), 0) + DNSService( + info.name, + _TYPE_SRV, + _CLASS_IN, + info.host_ttl, + info.priority, + info.weight, + info.port, + info.server, + ), + 0, + ) - out.add_answer_at_time( - DNSText(info.name, _TYPE_TXT, _CLASS_IN, info.other_ttl, info.text), 0) + out.add_answer_at_time(DNSText(info.name, _TYPE_TXT, _CLASS_IN, info.other_ttl, info.text), 0) if info.address: out.add_answer_at_time( - DNSAddress(info.server, _TYPE_A, _CLASS_IN, - info.host_ttl, info.address), 0) + DNSAddress(info.server, _TYPE_A, _CLASS_IN, info.host_ttl, info.address), 0 + ) self.send(out) i += 1 next_time += _REGISTER_TIME @@ -1930,18 +1943,17 @@ def unregister_service(self, info: ServiceInfo) -> None: now = current_time_millis() continue out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) + out.add_answer_at_time(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) out.add_answer_at_time( - DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) - out.add_answer_at_time( - DNSService(info.name, _TYPE_SRV, _CLASS_IN, 0, - info.priority, info.weight, info.port, info.name), 0) - out.add_answer_at_time( - DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) + DNSService( + info.name, _TYPE_SRV, _CLASS_IN, 0, info.priority, info.weight, info.port, info.name + ), + 0, + ) + out.add_answer_at_time(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) if info.address: - out.add_answer_at_time( - DNSAddress(info.server, _TYPE_A, _CLASS_IN, 0, - info.address), 0) + out.add_answer_at_time(DNSAddress(info.server, _TYPE_A, _CLASS_IN, 0, info.address), 0) self.send(out) i += 1 next_time += _UNREGISTER_TIME @@ -1959,17 +1971,25 @@ def unregister_all_services(self) -> None: continue out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) for info in self.services.values(): - out.add_answer_at_time(DNSPointer( - info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) - out.add_answer_at_time(DNSService( - info.name, _TYPE_SRV, _CLASS_IN, 0, - info.priority, info.weight, info.port, info.server), 0) - out.add_answer_at_time(DNSText( - info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) + out.add_answer_at_time(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) + out.add_answer_at_time( + DNSService( + info.name, + _TYPE_SRV, + _CLASS_IN, + 0, + info.priority, + info.weight, + info.port, + info.server, + ), + 0, + ) + out.add_answer_at_time(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) if info.address: - out.add_answer_at_time(DNSAddress( - info.server, _TYPE_A, _CLASS_IN, 0, - info.address), 0) + out.add_answer_at_time( + DNSAddress(info.server, _TYPE_A, _CLASS_IN, 0, info.address), 0 + ) self.send(out) i += 1 next_time += _UNREGISTER_TIME @@ -1984,7 +2004,7 @@ def check_service(self, info: ServiceInfo, allow_name_change: bool) -> None: if not info.type.endswith(service_name): raise BadTypeInNameException - instance_name = info.name[:-len(service_name) - 1] + instance_name = info.name[: -len(service_name) - 1] next_instance_number = 2 now = current_time_millis() @@ -1992,14 +2012,12 @@ def check_service(self, info: ServiceInfo, allow_name_change: bool) -> None: i = 0 while i < 3: # check for a name conflict - while self.cache.current_entry_with_name_and_alias( - info.type, info.name): + while self.cache.current_entry_with_name_and_alias(info.type, info.name): if not allow_name_change: raise NonUniqueNameException # change the name and look for a conflict - info.name = '%s-%s.%s' % ( - instance_name, next_instance_number, info.type) + info.name = '%s-%s.%s' % (instance_name, next_instance_number, info.type) next_instance_number += 1 service_type_name(info.name) next_time = now @@ -2013,8 +2031,7 @@ def check_service(self, info: ServiceInfo, allow_name_change: bool) -> None: out = DNSOutgoing(_FLAGS_QR_QUERY | _FLAGS_AA) self.debug = out out.add_question(DNSQuestion(info.type, _TYPE_PTR, _CLASS_IN)) - out.add_authorative_answer(DNSPointer( - info.type, _TYPE_PTR, _CLASS_IN, info.other_ttl, info.name)) + out.add_authorative_answer(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, info.other_ttl, info.name)) self.send(out) i += 1 next_time += _CHECK_TIME @@ -2086,16 +2103,20 @@ def handle_query(self, msg: DNSIncoming, addr: str, port: int) -> None: for stype in self.servicetypes.keys(): if out is None: out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - out.add_answer(msg, DNSPointer( - "_services._dns-sd._udp.local.", _TYPE_PTR, - _CLASS_IN, _DNS_OTHER_TTL, stype)) + out.add_answer( + msg, + DNSPointer( + "_services._dns-sd._udp.local.", _TYPE_PTR, _CLASS_IN, _DNS_OTHER_TTL, stype + ), + ) for service in self.services.values(): if question.name == service.type: if out is None: out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - out.add_answer(msg, DNSPointer( - service.type, _TYPE_PTR, - _CLASS_IN, service.other_ttl, service.name)) + out.add_answer( + msg, + DNSPointer(service.type, _TYPE_PTR, _CLASS_IN, service.other_ttl, service.name), + ) else: try: if out is None: @@ -2105,10 +2126,16 @@ def handle_query(self, msg: DNSIncoming, addr: str, port: int) -> None: if question.type in (_TYPE_A, _TYPE_ANY): for service in self.services.values(): if service.server == question.name.lower(): - out.add_answer(msg, DNSAddress( - question.name, _TYPE_A, - _CLASS_IN | _CLASS_UNIQUE, - service.host_ttl, service.address)) + out.add_answer( + msg, + DNSAddress( + question.name, + _TYPE_A, + _CLASS_IN | _CLASS_UNIQUE, + service.host_ttl, + service.address, + ), + ) name_to_find = question.name.lower() if name_to_find not in self.services: @@ -2116,18 +2143,40 @@ def handle_query(self, msg: DNSIncoming, addr: str, port: int) -> None: service = self.services[name_to_find] if question.type in (_TYPE_SRV, _TYPE_ANY): - out.add_answer(msg, DNSService( - question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, - service.host_ttl, service.priority, service.weight, - service.port, service.server)) + out.add_answer( + msg, + DNSService( + question.name, + _TYPE_SRV, + _CLASS_IN | _CLASS_UNIQUE, + service.host_ttl, + service.priority, + service.weight, + service.port, + service.server, + ), + ) if question.type in (_TYPE_TXT, _TYPE_ANY): - out.add_answer(msg, DNSText( - question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, - service.other_ttl, service.text)) + out.add_answer( + msg, + DNSText( + question.name, + _TYPE_TXT, + _CLASS_IN | _CLASS_UNIQUE, + service.other_ttl, + service.text, + ), + ) if question.type == _TYPE_SRV: - out.add_additional_answer(DNSAddress( - service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, - service.host_ttl, service.address)) + out.add_additional_answer( + DNSAddress( + service.server, + _TYPE_A, + _CLASS_IN | _CLASS_UNIQUE, + service.host_ttl, + service.address, + ) + ) except Exception: # TODO stop catching all Exceptions self.log_exception_warning() @@ -2139,8 +2188,7 @@ def send(self, out: DNSOutgoing, addr: str = _MDNS_ADDR, port: int = _MDNS_PORT) """Sends an outgoing packet.""" packet = out.packet() if len(packet) > _MAX_MSG_ABSOLUTE: - self.log_warning_once("Dropping %r over-sized packet (%d bytes) %r", - out, len(packet), packet) + self.log_warning_once("Dropping %r over-sized packet (%d bytes) %r", out, len(packet), packet) return log.debug('Sending %r (%d bytes) as %r...', out, len(packet), packet) for s in self._respond_sockets: @@ -2148,14 +2196,12 @@ def send(self, out: DNSOutgoing, addr: str = _MDNS_ADDR, port: int = _MDNS_PORT) return try: bytes_sent = s.sendto(packet, 0, (addr, port)) - except Exception: # TODO stop catching all Exceptions + except Exception: # TODO stop catching all Exceptions # on send errors, log the exception and keep going self.log_exception_warning() else: if bytes_sent != len(packet): - self.log_warning_once( - '!!! sent %d out of %d bytes to %r' % ( - bytes_sent, len(packet), s)) + self.log_warning_once('!!! sent %d out of %d bytes to %r' % (bytes_sent, len(packet), s)) def close(self) -> None: """Ends the background threads, and prevent this instance from