From 8518ee52f6f5685a79abb01ba06594eaef08408c Mon Sep 17 00:00:00 2001 From: Bob Halley Date: Sat, 5 Mar 2022 13:32:36 -0800 Subject: [PATCH] Remove 3.6-specific code. We still have to add AsyncExitStacks, and this requires making the associated socket wrappers async context managers. --- dns/_immutable_attr.py | 84 ----------------------------------------- dns/asyncbackend.py | 6 --- dns/asyncquery.py | 5 +-- dns/immutable.py | 16 ++------ dns/query.py | 5 +-- dns/set.py | 16 ++------ tests/test_async.py | 20 +--------- tests/test_doh.py | 5 +-- tests/test_immutable.py | 19 +--------- tests/test_xfr.py | 10 +---- 10 files changed, 16 insertions(+), 170 deletions(-) delete mode 100644 dns/_immutable_attr.py diff --git a/dns/_immutable_attr.py b/dns/_immutable_attr.py deleted file mode 100644 index 4d89be90..00000000 --- a/dns/_immutable_attr.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# This implementation of the immutable decorator is for python 3.6, -# which doesn't have Context Variables. This implementation is somewhat -# costly for classes with slots, as it adds a __dict__ to them. - - -import inspect - - -class _Immutable: - """Immutable mixin class""" - - # Note we MUST NOT have __slots__ as that causes - # - # TypeError: multiple bases have instance lay-out conflict - # - # when we get mixed in with another class with slots. When we - # get mixed into something with slots, it effectively adds __dict__ to - # the slots of the other class, which allows attribute setting to work, - # albeit at the cost of the dictionary. - - def __setattr__(self, name, value): - if not hasattr(self, '_immutable_init') or \ - self._immutable_init is not self: - raise TypeError("object doesn't support attribute assignment") - else: - super().__setattr__(name, value) - - def __delattr__(self, name): - if not hasattr(self, '_immutable_init') or \ - self._immutable_init is not self: - raise TypeError("object doesn't support attribute assignment") - else: - super().__delattr__(name) - - -def _immutable_init(f): - def nf(*args, **kwargs): - try: - # Are we already initializing an immutable class? - previous = args[0]._immutable_init - except AttributeError: - # We are the first! - previous = None - object.__setattr__(args[0], '_immutable_init', args[0]) - try: - # call the actual __init__ - f(*args, **kwargs) - finally: - if not previous: - # If we started the initialization, establish immutability - # by removing the attribute that allows mutation - object.__delattr__(args[0], '_immutable_init') - nf.__signature__ = inspect.signature(f) - return nf - - -def immutable(cls): - if _Immutable in cls.__mro__: - # Some ancestor already has the mixin, so just make sure we keep - # following the __init__ protocol. - cls.__init__ = _immutable_init(cls.__init__) - if hasattr(cls, '__setstate__'): - cls.__setstate__ = _immutable_init(cls.__setstate__) - ncls = cls - else: - # Mixin the Immutable class and follow the __init__ protocol. - class ncls(_Immutable, cls): - - @_immutable_init - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - if hasattr(cls, '__setstate__'): - @_immutable_init - def __setstate__(self, *args, **kwargs): - super().__setstate__(*args, **kwargs) - - # make ncls have the same name and module as cls - ncls.__name__ = cls.__name__ - ncls.__qualname__ = cls.__qualname__ - ncls.__module__ = cls.__module__ - return ncls diff --git a/dns/asyncbackend.py b/dns/asyncbackend.py index a8f794ac..0c6fc0ae 100644 --- a/dns/asyncbackend.py +++ b/dns/asyncbackend.py @@ -72,12 +72,6 @@ def sniff() -> str: return 'asyncio' except RuntimeError: raise AsyncLibraryNotFoundError('no async library detected') - except AttributeError: # pragma: no cover - # we have to check current_task on 3.6; we ignore for mypy - # purposes at it is otherwise unhappy on >= 3.7 - if not asyncio.Task.current_task(): # type: ignore - raise AsyncLibraryNotFoundError('no async library detected') - return 'asyncio' def get_default_backend() -> Backend: diff --git a/dns/asyncquery.py b/dns/asyncquery.py index 8c35d1aa..3ac48dc3 100644 --- a/dns/asyncquery.py +++ b/dns/asyncquery.py @@ -356,10 +356,7 @@ async def tls(q: dns.message.Message, where: str, timeout: Optional[float]=None, if ssl_context is None: # See the comment about ssl.create_default_context() in query.py ssl_context = ssl.create_default_context() # lgtm[py/insecure-protocol] - if sys.version_info >= (3, 7): - ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 - else: - ssl_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 + ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 if server_hostname is None: ssl_context.check_hostname = False else: diff --git a/dns/immutable.py b/dns/immutable.py index 672be598..17515b1f 100644 --- a/dns/immutable.py +++ b/dns/immutable.py @@ -3,15 +3,7 @@ import collections.abc import sys -# pylint: disable=unused-import -if sys.version_info >= (3, 7): - odict = dict - from dns._immutable_ctx import immutable -else: - # pragma: no cover - from collections import OrderedDict as odict - from dns._immutable_attr import immutable # noqa -# pylint: enable=unused-import +from dns._immutable_ctx import immutable @immutable @@ -23,10 +15,10 @@ class Dict(collections.abc.Mapping): # lgtm[py/missing-equals] of copied. Only set this if you are sure there will be no external references to the dictionary. """ - if no_copy and isinstance(dictionary, odict): + if no_copy and isinstance(dictionary, dict): self._odict = dictionary else: - self._odict = odict(dictionary) + self._odict = dict(dictionary) self._hash = None def __getitem__(self, key): @@ -63,7 +55,7 @@ def constify(o): if isinstance(o, list): return tuple(constify(elt) for elt in o) if isinstance(o, dict): - cdict = odict() + cdict = dict() for k, v in o.items(): cdict[k] = constify(v) return Dict(cdict, True) diff --git a/dns/query.py b/dns/query.py index e2dca20d..9588621b 100644 --- a/dns/query.py +++ b/dns/query.py @@ -892,10 +892,7 @@ def tls(q: dns.message.Message, where: str, timeout: Optional[float]=None, # can, even though this might require a future dnspython release if that # version becomes deprecated. ssl_context = ssl.create_default_context() # lgtm[py/insecure-protocol] - if sys.version_info >= (3, 7): - ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 - else: - ssl_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 + ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 if server_hostname is None: ssl_context.check_hostname = False diff --git a/dns/set.py b/dns/set.py index 32b50a73..40583549 100644 --- a/dns/set.py +++ b/dns/set.py @@ -18,10 +18,6 @@ import itertools import sys -if sys.version_info >= (3, 7): - odict = dict -else: - from collections import OrderedDict as odict # pragma: no cover class Set: @@ -41,7 +37,7 @@ class Set: *items*, an iterable or ``None``, the initial set of items. """ - self.items = odict() + self.items = dict() if items is not None: for item in items: # This is safe for how we use set, but if other code @@ -96,7 +92,7 @@ class Set: else: cls = self.__class__ obj = cls.__new__(cls) - obj.items = odict() + obj.items = dict() obj.items.update(self.items) return obj @@ -259,13 +255,7 @@ class Set: self.items.clear() def __eq__(self, other): - if odict == dict: - return self.items == other.items - else: - # We don't want an ordered comparison. - if len(self.items) != len(other.items): - return False - return all(elt in other.items for elt in self.items) + return self.items == other.items def __ne__(self, other): return not self.__eq__(other) diff --git a/tests/test_async.py b/tests/test_async.py index 4759b029..ce0caa14 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -85,15 +85,7 @@ class AsyncDetectionTests(unittest.TestCase): sniff_result = 'asyncio' def async_run(self, afunc): - try: - runner = asyncio.run - except AttributeError: - # this is only needed for 3.6 - def old_runner(awaitable): - loop = asyncio.get_event_loop() - return loop.run_until_complete(awaitable) - runner = old_runner - return runner(afunc()) + return asyncio.run(afunc()) def test_sniff(self): dns.asyncbackend._default_backend = None @@ -177,15 +169,7 @@ class AsyncTests(unittest.TestCase): self.backend = dns.asyncbackend.set_default_backend('asyncio') def async_run(self, afunc): - try: - runner = asyncio.run - except AttributeError: - # this is only needed for 3.6 - def old_runner(awaitable): - loop = asyncio.get_event_loop() - return loop.run_until_complete(awaitable) - runner = old_runner - return runner(afunc()) + return asyncio.run(afunc()) def testResolve(self): async def run(): diff --git a/tests/test_doh.py b/tests/test_doh.py index 6be7d211..bc02e952 100644 --- a/tests/test_doh.py +++ b/tests/test_doh.py @@ -211,9 +211,8 @@ class DNSOverHTTPSTestCaseHttpx(unittest.TestCase): valid_tls_url = 'https://doh.cleanbrowsing.org/doh/family-filter/' q = dns.message.make_query('example.com.', dns.rdatatype.A) # make sure CleanBrowsing's IP address will fail TLS certificate - # check. On 3.6 we get ssl.CertificateError instead of - # httpx.ConnectError. - with self.assertRaises((httpx.ConnectError, ssl.CertificateError)): + # check. + with self.assertRaises(httpx.ConnectError): dns.query.https(q, invalid_tls_url, session=self.session, timeout=4) # We can't do the Host header and SNI magic with httpx, but diff --git a/tests/test_immutable.py b/tests/test_immutable.py index 1a70e3d4..8ab145ea 100644 --- a/tests/test_immutable.py +++ b/tests/test_immutable.py @@ -3,16 +3,7 @@ import unittest import dns.immutable -import dns._immutable_attr - -try: - import dns._immutable_ctx as immutable_ctx - _have_contextvars = True -except ImportError: - _have_contextvars = False - - class immutable_ctx: - pass +import dns._immutable_ctx class ImmutableTestCase(unittest.TestCase): @@ -52,7 +43,7 @@ class ImmutableTestCase(unittest.TestCase): class DecoratorTestCase(unittest.TestCase): - immutable_module = dns._immutable_attr + immutable_module = dns._immutable_ctx def make_classes(self): class A: @@ -152,9 +143,3 @@ class DecoratorTestCase(unittest.TestCase): with self.assertRaises(TypeError): B(a, 20) self.assertEqual(a.a, 10) - - -@unittest.skipIf(not _have_contextvars, "contextvars not available") -class CtxDecoratorTestCase(DecoratorTestCase): - - immutable_module = immutable_ctx diff --git a/tests/test_xfr.py b/tests/test_xfr.py index 462e3a05..3cf4c913 100644 --- a/tests/test_xfr.py +++ b/tests/test_xfr.py @@ -676,15 +676,7 @@ def test_asyncio_inbound_xfr(): dns.asyncbackend.set_default_backend('asyncio') async def run(): await async_inbound_xfr() - try: - runner = asyncio.run - except AttributeError: - # this is only needed for 3.6 - def old_runner(awaitable): - loop = asyncio.get_event_loop() - return loop.run_until_complete(awaitable) - runner = old_runner - runner(run()) + asyncio.run(run()) # # We don't need to do this as it's all generic code, but -- 2.47.3