From 44aff4373eae9bbda499588e49edbacc441fb943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0pa=C4=8Dek?= Date: Fri, 31 Jul 2020 14:10:56 +0200 Subject: [PATCH] WIP: make pylint happy pylint does not see Enum members exposed from modules via globals().update(Algorithm.__members__) This leads to tons of complains from pylint in third-party code which uses trivial expressions like e.g. dns.flags.DO leads to error E1101: Module 'dns.flags' has no 'DO' member (no-member) This change exports Enum values statically so pylint can see them. New test code verifies that all Enums in specified modules actually export all its members so we omissions will be caught. It re-introduces some duplication but I believe it is good tradeoff as it allows static checkers to work again. --- dns/flags.py | 13 +++++++++++-- dns/opcode.py | 7 ++++++- dns/rcode.py | 24 +++++++++++++++++++++++- tests/test_flags.py | 5 +++++ tests/util.py | 18 ++++++++++++++++++ 5 files changed, 63 insertions(+), 4 deletions(-) diff --git a/dns/flags.py b/dns/flags.py index 4eb6d90cc..6c30591ca 100644 --- a/dns/flags.py +++ b/dns/flags.py @@ -37,7 +37,15 @@ class Flag(enum.IntFlag): #: Checking Disabled CD = 0x0010 -globals().update(Flag.__members__) + +# 1.16 compatibility +QR = Flag.QR +AA = Flag.AA +TC = Flag.TC +RD = Flag.RD +RA = Flag.RA +AD = Flag.AD +CD = Flag.CD # EDNS flags @@ -47,7 +55,8 @@ class EDNSFlag(enum.IntFlag): DO = 0x8000 -globals().update(EDNSFlag.__members__) +# 1.16 compatibility +DO = EDNSFlag.DO def _from_text(text, enum_class): diff --git a/dns/opcode.py b/dns/opcode.py index 5a76326a7..6e1e398a7 100644 --- a/dns/opcode.py +++ b/dns/opcode.py @@ -40,7 +40,12 @@ def _maximum(cls): def _unknown_exception_class(cls): return UnknownOpcode -globals().update(Opcode.__members__) +# 1.16 compatibility +QUERY = Opcode.QUERY +IQUERY = Opcode.IQUERY +STATUS = Opcode.STATUS +NOTIFY = Opcode.NOTIFY +UPDATE = Opcode.UPDATE class UnknownOpcode(dns.exception.DNSException): diff --git a/dns/rcode.py b/dns/rcode.py index 846bf6dcd..915307a51 100644 --- a/dns/rcode.py +++ b/dns/rcode.py @@ -72,7 +72,29 @@ def _maximum(cls): def _unknown_exception_class(cls): return UnknownRcode -globals().update(Rcode.__members__) +# 1.16 compatibility +NOERROR = Rcode.NOERROR +FORMERR = Rcode.FORMERR +SERVFAIL = Rcode.SERVFAIL +NXDOMAIN = Rcode.NXDOMAIN +NOTIMP = Rcode.NOTIMP +REFUSED = Rcode.REFUSED +YXDOMAIN = Rcode.YXDOMAIN +YXRRSET = Rcode.YXRRSET +NXRRSET = Rcode.NXRRSET +NOTAUTH = Rcode.NOTAUTH +NOTZONE = Rcode.NOTZONE +DSOTYPENI = Rcode.DSOTYPENI +BADVERS = Rcode.BADVERS +BADSIG = Rcode.BADSIG +BADKEY = Rcode.BADKEY +BADTIME = Rcode.BADTIME +BADMODE = Rcode.BADMODE +BADNAME = Rcode.BADNAME +BADALG = Rcode.BADALG +BADTRUNC = Rcode.BADTRUNC +BADCOOKIE = Rcode.BADCOOKIE + class UnknownRcode(dns.exception.DNSException): """A DNS rcode is unknown.""" diff --git a/tests/test_flags.py b/tests/test_flags.py index 3f5fc6961..459be3f41 100644 --- a/tests/test_flags.py +++ b/tests/test_flags.py @@ -20,6 +20,7 @@ import dns.flags import dns.rcode import dns.opcode +import tests.util class FlagsTestCase(unittest.TestCase): @@ -76,6 +77,10 @@ def test_unknown_rcode(self): with self.assertRaises(dns.rcode.UnknownRcode): dns.rcode.Rcode.make('BOGUS') + def test_1x_compatibility(self): + tests.util.check_enum_exports(dns.flags, self.assertEqual) + tests.util.check_enum_exports(dns.rcode, self.assertEqual) + tests.util.check_enum_exports(dns.opcode, self.assertEqual) if __name__ == '__main__': unittest.main() diff --git a/tests/util.py b/tests/util.py index df736df05..d884242c4 100644 --- a/tests/util.py +++ b/tests/util.py @@ -15,7 +15,25 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +import enum +import inspect import os.path + +def enumerate_module(module, super_class): + """Yield module attributes which are subclasses of given class""" + for attr_name in dir(module): + attr = getattr(module, attr_name) + if inspect.isclass(attr) and issubclass(attr, super_class): + yield attr + +def check_enum_exports(module, eq_callback): + """Make sure module exports all mnemonics from enums""" + for attr in enumerate_module(module, enum.Enum): + for flag, value in attr.__members__.items(): + print(module, flag, value) + eq_callback(getattr(module, flag), value) + + def here(filename): return os.path.join(os.path.dirname(__file__), filename)