zeroconf 0.147.4__tar.gz → 0.149.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of zeroconf might be problematic. Click here for more details.

Files changed (98) hide show
  1. {zeroconf-0.147.4 → zeroconf-0.149.0}/CHANGELOG.md +14 -0
  2. {zeroconf-0.147.4 → zeroconf-0.149.0}/PKG-INFO +4 -5
  3. {zeroconf-0.147.4 → zeroconf-0.149.0}/README.rst +2 -2
  4. {zeroconf-0.147.4 → zeroconf-0.149.0}/build_ext.py +2 -0
  5. {zeroconf-0.147.4 → zeroconf-0.149.0}/pyproject.toml +4 -4
  6. {zeroconf-0.147.4 → zeroconf-0.149.0}/setup.py +3 -3
  7. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/__init__.py +1 -1
  8. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_cache.py +2 -2
  9. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_core.py +14 -1
  10. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_services/__init__.py +2 -1
  11. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_services/browser.py +1 -2
  12. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_utils/net.py +2 -2
  13. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/asyncio.py +1 -2
  14. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_core.py +30 -0
  15. {zeroconf-0.147.4 → zeroconf-0.149.0}/COPYING +0 -0
  16. {zeroconf-0.147.4 → zeroconf-0.149.0}/docs/Makefile +0 -0
  17. {zeroconf-0.147.4 → zeroconf-0.149.0}/docs/_ext/zeroconfautodocfix.py +0 -0
  18. {zeroconf-0.147.4 → zeroconf-0.149.0}/docs/api.rst +0 -0
  19. {zeroconf-0.147.4 → zeroconf-0.149.0}/docs/conf.py +0 -0
  20. {zeroconf-0.147.4 → zeroconf-0.149.0}/docs/index.rst +0 -0
  21. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_cache.pxd +0 -0
  22. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_dns.pxd +0 -0
  23. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_dns.py +0 -0
  24. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_engine.py +0 -0
  25. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_exceptions.py +0 -0
  26. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_handlers/__init__.py +0 -0
  27. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_handlers/answers.pxd +0 -0
  28. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_handlers/answers.py +0 -0
  29. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_handlers/multicast_outgoing_queue.pxd +0 -0
  30. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_handlers/multicast_outgoing_queue.py +0 -0
  31. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_handlers/query_handler.pxd +0 -0
  32. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_handlers/query_handler.py +0 -0
  33. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_handlers/record_manager.pxd +0 -0
  34. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_handlers/record_manager.py +0 -0
  35. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_history.pxd +0 -0
  36. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_history.py +0 -0
  37. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_listener.pxd +0 -0
  38. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_listener.py +0 -0
  39. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_logger.py +0 -0
  40. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_protocol/__init__.py +0 -0
  41. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_protocol/incoming.pxd +0 -0
  42. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_protocol/incoming.py +0 -0
  43. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_protocol/outgoing.pxd +0 -0
  44. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_protocol/outgoing.py +0 -0
  45. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_record_update.pxd +0 -0
  46. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_record_update.py +0 -0
  47. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_services/__init__.pxd +0 -0
  48. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_services/browser.pxd +0 -0
  49. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_services/info.pxd +0 -0
  50. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_services/info.py +0 -0
  51. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_services/registry.pxd +0 -0
  52. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_services/registry.py +0 -0
  53. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_services/types.py +0 -0
  54. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_transport.py +0 -0
  55. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_updates.pxd +0 -0
  56. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_updates.py +0 -0
  57. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_utils/__init__.py +0 -0
  58. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_utils/asyncio.py +0 -0
  59. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_utils/ipaddress.pxd +0 -0
  60. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_utils/ipaddress.py +0 -0
  61. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_utils/name.py +0 -0
  62. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_utils/time.pxd +0 -0
  63. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/_utils/time.py +0 -0
  64. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/const.py +0 -0
  65. {zeroconf-0.147.4 → zeroconf-0.149.0}/src/zeroconf/py.typed +0 -0
  66. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/__init__.py +0 -0
  67. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/benchmarks/__init__.py +0 -0
  68. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/benchmarks/helpers.py +0 -0
  69. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/benchmarks/test_cache.py +0 -0
  70. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/benchmarks/test_incoming.py +0 -0
  71. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/benchmarks/test_outgoing.py +0 -0
  72. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/benchmarks/test_send.py +0 -0
  73. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/benchmarks/test_txt_properties.py +0 -0
  74. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/conftest.py +0 -0
  75. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/services/__init__.py +0 -0
  76. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/services/test_browser.py +0 -0
  77. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/services/test_info.py +0 -0
  78. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/services/test_registry.py +0 -0
  79. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/services/test_types.py +0 -0
  80. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_asyncio.py +0 -0
  81. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_cache.py +0 -0
  82. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_circular_imports.py +0 -0
  83. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_dns.py +0 -0
  84. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_engine.py +0 -0
  85. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_exceptions.py +0 -0
  86. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_handlers.py +0 -0
  87. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_history.py +0 -0
  88. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_init.py +0 -0
  89. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_listener.py +0 -0
  90. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_logger.py +0 -0
  91. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_protocol.py +0 -0
  92. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_services.py +0 -0
  93. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/test_updates.py +0 -0
  94. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/utils/__init__.py +0 -0
  95. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/utils/test_asyncio.py +0 -0
  96. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/utils/test_ipaddress.py +0 -0
  97. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/utils/test_name.py +0 -0
  98. {zeroconf-0.147.4 → zeroconf-0.149.0}/tests/utils/test_net.py +0 -0
@@ -2,6 +2,20 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v0.149.0 (2026-05-16)
6
+
7
+ ### Features
8
+
9
+ - Drop Python 3.9 support ([#1688](https://github.com/python-zeroconf/python-zeroconf/pull/1688),
10
+ [`327b93d`](https://github.com/python-zeroconf/python-zeroconf/commit/327b93dc602707e25d53788f0fb14e142f4558b3))
11
+
12
+ ### Performance Improvements
13
+
14
+ - **build**: Parallelize cython extension compilation
15
+ ([#1689](https://github.com/python-zeroconf/python-zeroconf/pull/1689),
16
+ [`1ea6b94`](https://github.com/python-zeroconf/python-zeroconf/commit/1ea6b940ecbfd7e7654ad022cf7f4f888cf1daa5))
17
+
18
+
5
19
  ## v0.147.4 (2026-05-16)
6
20
 
7
21
  ### Bug Fixes
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zeroconf
3
- Version: 0.147.4
3
+ Version: 0.149.0
4
4
  Summary: A pure python implementation of multicast DNS service discovery
5
5
  License-Expression: LGPL-2.1-or-later
6
6
  License-File: COPYING
7
7
  Author: Paul Scott-Murphy
8
- Requires-Python: >=3.9
8
+ Requires-Python: >=3.10
9
9
  Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Intended Audience :: System Administrators
@@ -13,7 +13,6 @@ Classifier: Operating System :: MacOS :: MacOS X
13
13
  Classifier: Operating System :: POSIX
14
14
  Classifier: Operating System :: POSIX :: Linux
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.9
17
16
  Classifier: Programming Language :: Python :: 3.10
18
17
  Classifier: Programming Language :: Python :: 3.11
19
18
  Classifier: Programming Language :: Python :: 3.12
@@ -84,8 +83,8 @@ Compared to some other Zeroconf/Bonjour/Avahi Python packages, python-zeroconf:
84
83
  Python compatibility
85
84
  --------------------
86
85
 
87
- * CPython 3.9+
88
- * PyPy 3.9+
86
+ * CPython 3.10+
87
+ * PyPy 3.10+
89
88
 
90
89
  Versioning
91
90
  ----------
@@ -53,8 +53,8 @@ Compared to some other Zeroconf/Bonjour/Avahi Python packages, python-zeroconf:
53
53
  Python compatibility
54
54
  --------------------
55
55
 
56
- * CPython 3.9+
57
- * PyPy 3.9+
56
+ * CPython 3.10+
57
+ * PyPy 3.10+
58
58
 
59
59
  Versioning
60
60
  ----------
@@ -46,6 +46,8 @@ EXTENSIONS = [
46
46
 
47
47
  class BuildExt(build_ext):
48
48
  def build_extensions(self) -> None:
49
+ if self.parallel is None: # type: ignore[has-type]
50
+ self.parallel = os.cpu_count() or 1
49
51
  try:
50
52
  super().build_extensions()
51
53
  except Exception:
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [project]
6
6
  name = "zeroconf"
7
- version = "0.147.4"
7
+ version = "0.149.0"
8
8
  license = "LGPL-2.1-or-later"
9
9
  description = "A pure python implementation of multicast DNS service discovery"
10
10
  readme = "README.rst"
@@ -14,7 +14,7 @@ authors = [
14
14
  { name = "Jakub Stasiak" },
15
15
  { name = "J. Nick Koston" },
16
16
  ]
17
- requires-python = ">=3.9"
17
+ requires-python = ">=3.10"
18
18
 
19
19
  [project.urls]
20
20
  "Repository" = "https://github.com/python-zeroconf/python-zeroconf"
@@ -80,7 +80,7 @@ match = "(?!(master|release-0.x)$)"
80
80
  prerelease = true
81
81
 
82
82
  [tool.poetry.dependencies]
83
- python = "^3.9"
83
+ python = "^3.10"
84
84
  ifaddr = ">=0.1.7"
85
85
 
86
86
  [tool.poetry.group.dev.dependencies]
@@ -97,7 +97,7 @@ sphinx = "^7.4.7 || ^8.1.3"
97
97
  sphinx-rtd-theme = "^3.1.0"
98
98
 
99
99
  [tool.ruff]
100
- target-version = "py39"
100
+ target-version = "py310"
101
101
  line-length = 110
102
102
 
103
103
  [tool.ruff.lint]
@@ -19,9 +19,9 @@ install_requires = \
19
19
 
20
20
  setup_kwargs = {
21
21
  'name': 'zeroconf',
22
- 'version': '0.147.4',
22
+ 'version': '0.149.0',
23
23
  'description': 'A pure python implementation of multicast DNS service discovery',
24
- 'long_description': 'python-zeroconf\n===============\n\n.. image:: https://github.com/python-zeroconf/python-zeroconf/workflows/CI/badge.svg\n :target: https://github.com/python-zeroconf/python-zeroconf?query=workflow%3ACI+branch%3Amaster\n\n.. image:: https://img.shields.io/pypi/v/zeroconf.svg\n :target: https://pypi.python.org/pypi/zeroconf\n\n.. image:: https://codecov.io/gh/python-zeroconf/python-zeroconf/branch/master/graph/badge.svg\n :target: https://codecov.io/gh/python-zeroconf/python-zeroconf\n\n.. image:: https://img.shields.io/endpoint?url=https://codspeed.io/badge.json\n :target: https://codspeed.io/python-zeroconf/python-zeroconf\n :alt: Codspeed.io status for python-zeroconf\n\n.. image:: https://readthedocs.org/projects/python-zeroconf/badge/?version=latest\n :target: https://python-zeroconf.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\n`Documentation <https://python-zeroconf.readthedocs.io/en/latest/>`_.\n\nThis is fork of pyzeroconf, Multicast DNS Service Discovery for Python,\noriginally by Paul Scott-Murphy (https://github.com/paulsm/pyzeroconf),\nmodified by William McBrine (https://github.com/wmcbrine/pyzeroconf).\n\nThe original William McBrine\'s fork note::\n\n This fork is used in all of my TiVo-related projects: HME for Python\n (and therefore HME/VLC), Network Remote, Remote Proxy, and pyTivo.\n Before this, I was tracking the changes for zeroconf.py in three\n separate repos. I figured I should have an authoritative source.\n\n Although I make changes based on my experience with TiVos, I expect that\n they\'re generally applicable. This version also includes patches found\n on the now-defunct (?) Launchpad repo of pyzeroconf, and elsewhere\n around the net -- not always well-documented, sorry.\n\nCompatible with:\n\n* Bonjour\n* Avahi\n\nCompared to some other Zeroconf/Bonjour/Avahi Python packages, python-zeroconf:\n\n* isn\'t tied to Bonjour or Avahi\n* doesn\'t use D-Bus\n* doesn\'t force you to use particular event loop or Twisted (asyncio is used under the hood but not required)\n* is pip-installable\n* has PyPI distribution\n* has an optional cython extension for performance (pure python is supported as well)\n\nPython compatibility\n--------------------\n\n* CPython 3.9+\n* PyPy 3.9+\n\nVersioning\n----------\n\nThis project uses semantic versioning.\n\nStatus\n------\n\nThis project is actively maintained.\n\nTraffic Reduction\n-----------------\n\nBefore version 0.32, most traffic reduction techniques described in https://datatracker.ietf.org/doc/html/rfc6762#section-7\nwhere not implemented which could lead to excessive network traffic. It is highly recommended that version 0.32 or later\nis used if this is a concern.\n\nIPv6 support\n------------\n\nIPv6 support is relatively new and currently limited, specifically:\n\n* `InterfaceChoice.All` is an alias for `InterfaceChoice.Default` on non-POSIX\n systems.\n* Dual-stack IPv6 sockets are used, which may not be supported everywhere (some\n BSD variants do not have them).\n* Listening on localhost (`::1`) does not work. Help with understanding why is\n appreciated.\n\nHow to get python-zeroconf?\n===========================\n\n* PyPI page https://pypi.org/project/zeroconf/\n* GitHub project https://github.com/python-zeroconf/python-zeroconf\n\nThe easiest way to install python-zeroconf is using pip::\n\n pip install zeroconf\n\n\n\nHow do I use it?\n================\n\nHere\'s an example of browsing for a service:\n\n.. code-block:: python\n\n from zeroconf import ServiceBrowser, ServiceListener, Zeroconf\n\n\n class MyListener(ServiceListener):\n\n def update_service(self, zc: Zeroconf, type_: str, name: str) -> None:\n print(f"Service {name} updated")\n\n def remove_service(self, zc: Zeroconf, type_: str, name: str) -> None:\n print(f"Service {name} removed")\n\n def add_service(self, zc: Zeroconf, type_: str, name: str) -> None:\n info = zc.get_service_info(type_, name)\n print(f"Service {name} added, service info: {info}")\n\n\n zeroconf = Zeroconf()\n listener = MyListener()\n browser = ServiceBrowser(zeroconf, "_http._tcp.local.", listener)\n try:\n input("Press enter to exit...\\n\\n")\n finally:\n zeroconf.close()\n\n.. note::\n\n Discovery and service registration use *all* available network interfaces by default.\n If you want to customize that you need to specify ``interfaces`` argument when\n constructing ``Zeroconf`` object (see the code for details).\n\nIf you don\'t know the name of the service you need to browse for, try:\n\n.. code-block:: python\n\n from zeroconf import ZeroconfServiceTypes\n print(\'\\n\'.join(ZeroconfServiceTypes.find()))\n\nSee examples directory for more.\n\nChangelog\n=========\n\n`Changelog <CHANGELOG.md>`_\n\nLicense\n=======\n\nLGPL, see COPYING file for details.\n',
24
+ 'long_description': 'python-zeroconf\n===============\n\n.. image:: https://github.com/python-zeroconf/python-zeroconf/workflows/CI/badge.svg\n :target: https://github.com/python-zeroconf/python-zeroconf?query=workflow%3ACI+branch%3Amaster\n\n.. image:: https://img.shields.io/pypi/v/zeroconf.svg\n :target: https://pypi.python.org/pypi/zeroconf\n\n.. image:: https://codecov.io/gh/python-zeroconf/python-zeroconf/branch/master/graph/badge.svg\n :target: https://codecov.io/gh/python-zeroconf/python-zeroconf\n\n.. image:: https://img.shields.io/endpoint?url=https://codspeed.io/badge.json\n :target: https://codspeed.io/python-zeroconf/python-zeroconf\n :alt: Codspeed.io status for python-zeroconf\n\n.. image:: https://readthedocs.org/projects/python-zeroconf/badge/?version=latest\n :target: https://python-zeroconf.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\n`Documentation <https://python-zeroconf.readthedocs.io/en/latest/>`_.\n\nThis is fork of pyzeroconf, Multicast DNS Service Discovery for Python,\noriginally by Paul Scott-Murphy (https://github.com/paulsm/pyzeroconf),\nmodified by William McBrine (https://github.com/wmcbrine/pyzeroconf).\n\nThe original William McBrine\'s fork note::\n\n This fork is used in all of my TiVo-related projects: HME for Python\n (and therefore HME/VLC), Network Remote, Remote Proxy, and pyTivo.\n Before this, I was tracking the changes for zeroconf.py in three\n separate repos. I figured I should have an authoritative source.\n\n Although I make changes based on my experience with TiVos, I expect that\n they\'re generally applicable. This version also includes patches found\n on the now-defunct (?) Launchpad repo of pyzeroconf, and elsewhere\n around the net -- not always well-documented, sorry.\n\nCompatible with:\n\n* Bonjour\n* Avahi\n\nCompared to some other Zeroconf/Bonjour/Avahi Python packages, python-zeroconf:\n\n* isn\'t tied to Bonjour or Avahi\n* doesn\'t use D-Bus\n* doesn\'t force you to use particular event loop or Twisted (asyncio is used under the hood but not required)\n* is pip-installable\n* has PyPI distribution\n* has an optional cython extension for performance (pure python is supported as well)\n\nPython compatibility\n--------------------\n\n* CPython 3.10+\n* PyPy 3.10+\n\nVersioning\n----------\n\nThis project uses semantic versioning.\n\nStatus\n------\n\nThis project is actively maintained.\n\nTraffic Reduction\n-----------------\n\nBefore version 0.32, most traffic reduction techniques described in https://datatracker.ietf.org/doc/html/rfc6762#section-7\nwhere not implemented which could lead to excessive network traffic. It is highly recommended that version 0.32 or later\nis used if this is a concern.\n\nIPv6 support\n------------\n\nIPv6 support is relatively new and currently limited, specifically:\n\n* `InterfaceChoice.All` is an alias for `InterfaceChoice.Default` on non-POSIX\n systems.\n* Dual-stack IPv6 sockets are used, which may not be supported everywhere (some\n BSD variants do not have them).\n* Listening on localhost (`::1`) does not work. Help with understanding why is\n appreciated.\n\nHow to get python-zeroconf?\n===========================\n\n* PyPI page https://pypi.org/project/zeroconf/\n* GitHub project https://github.com/python-zeroconf/python-zeroconf\n\nThe easiest way to install python-zeroconf is using pip::\n\n pip install zeroconf\n\n\n\nHow do I use it?\n================\n\nHere\'s an example of browsing for a service:\n\n.. code-block:: python\n\n from zeroconf import ServiceBrowser, ServiceListener, Zeroconf\n\n\n class MyListener(ServiceListener):\n\n def update_service(self, zc: Zeroconf, type_: str, name: str) -> None:\n print(f"Service {name} updated")\n\n def remove_service(self, zc: Zeroconf, type_: str, name: str) -> None:\n print(f"Service {name} removed")\n\n def add_service(self, zc: Zeroconf, type_: str, name: str) -> None:\n info = zc.get_service_info(type_, name)\n print(f"Service {name} added, service info: {info}")\n\n\n zeroconf = Zeroconf()\n listener = MyListener()\n browser = ServiceBrowser(zeroconf, "_http._tcp.local.", listener)\n try:\n input("Press enter to exit...\\n\\n")\n finally:\n zeroconf.close()\n\n.. note::\n\n Discovery and service registration use *all* available network interfaces by default.\n If you want to customize that you need to specify ``interfaces`` argument when\n constructing ``Zeroconf`` object (see the code for details).\n\nIf you don\'t know the name of the service you need to browse for, try:\n\n.. code-block:: python\n\n from zeroconf import ZeroconfServiceTypes\n print(\'\\n\'.join(ZeroconfServiceTypes.find()))\n\nSee examples directory for more.\n\nChangelog\n=========\n\n`Changelog <CHANGELOG.md>`_\n\nLicense\n=======\n\nLGPL, see COPYING file for details.\n',
25
25
  'author': 'Paul Scott-Murphy',
26
26
  'author_email': 'None',
27
27
  'maintainer': 'None',
@@ -31,7 +31,7 @@ setup_kwargs = {
31
31
  'packages': packages,
32
32
  'package_data': package_data,
33
33
  'install_requires': install_requires,
34
- 'python_requires': '>=3.9',
34
+ 'python_requires': '>=3.10',
35
35
  }
36
36
  from build_ext import *
37
37
  build(setup_kwargs)
@@ -88,7 +88,7 @@ from ._utils.time import ( # noqa # import needed for backwards compat
88
88
 
89
89
  __author__ = "Paul Scott-Murphy, William McBrine"
90
90
  __maintainer__ = "Jakub Stasiak <jakub@stasiak.at>"
91
- __version__ = "0.147.4"
91
+ __version__ = "0.149.0"
92
92
  __license__ = "LGPL"
93
93
 
94
94
 
@@ -24,7 +24,7 @@ from __future__ import annotations
24
24
 
25
25
  from collections.abc import Iterable
26
26
  from heapq import heapify, heappop, heappush
27
- from typing import Union, cast
27
+ from typing import cast
28
28
 
29
29
  from ._dns import (
30
30
  DNSAddress,
@@ -40,7 +40,7 @@ from ._utils.time import current_time_millis
40
40
  from .const import _ONE_SECOND, _TYPE_PTR
41
41
 
42
42
  _UNIQUE_RECORD_TYPES = (DNSAddress, DNSHinfo, DNSPointer, DNSText, DNSService)
43
- _UniqueRecordsType = Union[DNSAddress, DNSHinfo, DNSPointer, DNSText, DNSService]
43
+ _UniqueRecordsType = DNSAddress | DNSHinfo | DNSPointer | DNSText | DNSService
44
44
  _DNSRecordCacheType = dict[str, dict[DNSRecord, DNSRecord]]
45
45
  _DNSRecord = DNSRecord
46
46
  _str = str
@@ -154,6 +154,7 @@ class Zeroconf(QuietLogger):
154
154
  unicast: bool = False,
155
155
  ip_version: IPVersion | None = None,
156
156
  apple_p2p: bool = False,
157
+ use_asyncio: bool | None = None,
157
158
  ) -> None:
158
159
  """Creates an instance of the Zeroconf class, establishing
159
160
  multicast communications, listening and reaping threads.
@@ -169,6 +170,14 @@ class Zeroconf(QuietLogger):
169
170
  :param ip_version: IP versions to support. If `choice` is a list, the default is detected
170
171
  from it. Otherwise defaults to V4 only for backward compatibility.
171
172
  :param apple_p2p: use AWDL interface (only macOS)
173
+ :param use_asyncio: explicitly control whether to attach to the running
174
+ asyncio event loop (``True``) or run an internal thread with its
175
+ own loop (``False``). ``None`` (default) keeps the historic
176
+ behavior: attach if an event loop is running, otherwise start a
177
+ thread. Set to ``False`` when running inside an environment that
178
+ already has an event loop (e.g. Jupyter) but you want blocking
179
+ semantics. ``True`` raises :class:`RuntimeError` immediately if no
180
+ running event loop is found, instead of falling back to the thread.
172
181
  """
173
182
  if ip_version is None:
174
183
  ip_version = autodetect_ip_version(interfaces)
@@ -178,7 +187,11 @@ class Zeroconf(QuietLogger):
178
187
  if apple_p2p and sys.platform != "darwin":
179
188
  raise RuntimeError("Option `apple_p2p` is not supported on non-Apple platforms.")
180
189
 
190
+ if use_asyncio is True and get_running_loop() is None:
191
+ raise RuntimeError("use_asyncio=True requires a running asyncio event loop")
192
+
181
193
  self.unicast = unicast
194
+ self._use_asyncio = use_asyncio
182
195
  listen_socket, respond_sockets = create_sockets(interfaces, unicast, ip_version, apple_p2p=apple_p2p)
183
196
  log.debug("Listen socket %s, respond sockets %s", listen_socket, respond_sockets)
184
197
 
@@ -216,7 +229,7 @@ class Zeroconf(QuietLogger):
216
229
 
217
230
  def start(self) -> None:
218
231
  """Start Zeroconf."""
219
- self.loop = get_running_loop()
232
+ self.loop = None if self._use_asyncio is False else get_running_loop()
220
233
  if self.loop:
221
234
  self.engine.setup(self.loop, None)
222
235
  return
@@ -23,7 +23,8 @@ USA
23
23
  from __future__ import annotations
24
24
 
25
25
  import enum
26
- from typing import TYPE_CHECKING, Any, Callable
26
+ from collections.abc import Callable
27
+ from typing import TYPE_CHECKING, Any
27
28
 
28
29
  if TYPE_CHECKING:
29
30
  from .._core import Zeroconf
@@ -30,13 +30,12 @@ import random
30
30
  import threading
31
31
  import time
32
32
  import warnings
33
- from collections.abc import Iterable
33
+ from collections.abc import Callable, Iterable
34
34
  from functools import partial
35
35
  from types import TracebackType # used in type hints
36
36
  from typing import (
37
37
  TYPE_CHECKING,
38
38
  Any,
39
- Callable,
40
39
  cast,
41
40
  )
42
41
 
@@ -30,7 +30,7 @@ import struct
30
30
  import sys
31
31
  import warnings
32
32
  from collections.abc import Iterable, Sequence
33
- from typing import Any, Union, cast
33
+ from typing import Any, cast
34
34
 
35
35
  import ifaddr
36
36
 
@@ -44,7 +44,7 @@ class InterfaceChoice(enum.Enum):
44
44
  All = 2
45
45
 
46
46
 
47
- InterfacesType = Union[Sequence[Union[str, int, tuple[tuple[str, int, int], int]]], InterfaceChoice]
47
+ InterfacesType = Sequence[str | int | tuple[tuple[str, int, int], int]] | InterfaceChoice
48
48
 
49
49
 
50
50
  @enum.unique
@@ -24,9 +24,8 @@ from __future__ import annotations
24
24
 
25
25
  import asyncio
26
26
  import contextlib
27
- from collections.abc import Awaitable
27
+ from collections.abc import Awaitable, Callable
28
28
  from types import TracebackType # used in type hints
29
- from typing import Callable
30
29
 
31
30
  from ._core import Zeroconf
32
31
  from ._dns import DNSQuestionType
@@ -154,6 +154,36 @@ class Framework(unittest.TestCase):
154
154
  rv = r.Zeroconf(apple_p2p=True)
155
155
  rv.close()
156
156
 
157
+ def test_use_asyncio_false_forces_thread_when_loop_running(self):
158
+ """use_asyncio=False starts a thread even with a running event loop."""
159
+
160
+ async def run() -> r.Zeroconf:
161
+ return r.Zeroconf(interfaces=["127.0.0.1"], use_asyncio=False)
162
+
163
+ loop = asyncio.new_event_loop()
164
+ zc: r.Zeroconf | None = None
165
+ try:
166
+ zc = loop.run_until_complete(run())
167
+ assert zc._loop_thread is not None
168
+ assert zc.loop is not loop
169
+ finally:
170
+ if zc is not None:
171
+ zc.close()
172
+ loop.close()
173
+
174
+ def test_use_asyncio_true_requires_running_loop(self):
175
+ """use_asyncio=True without a running loop raises RuntimeError."""
176
+ with pytest.raises(RuntimeError, match="requires a running asyncio event loop"):
177
+ r.Zeroconf(interfaces=["127.0.0.1"], use_asyncio=True)
178
+
179
+ def test_use_asyncio_default_starts_thread_without_loop(self):
180
+ """use_asyncio=None (default) keeps the historic auto-detect behavior."""
181
+ zc = r.Zeroconf(interfaces=["127.0.0.1"])
182
+ try:
183
+ assert zc._loop_thread is not None
184
+ finally:
185
+ zc.close()
186
+
157
187
  def test_async_updates_from_response(self):
158
188
  def mock_incoming_msg(
159
189
  service_state_change: r.ServiceStateChange,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes