diff --git a/secop/lib/py35compat.py b/secop/lib/py35compat.py new file mode 100644 index 0000000..fa612ea --- /dev/null +++ b/secop/lib/py35compat.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# ***************************************************************************** +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Module authors: +# Markus Zolliker +# +# ***************************************************************************** +"""workaround for python versions older than 3.6 + +``Object`` must be inherited for classes needing support for +__init_subclass__ and __set_name__ +""" + + +if hasattr(object, '__init_subclass__'): + class Object: + pass +else: + class PEP487Metaclass(type): + # support for __set_name__ and __init_subclass__ for older python versions + # slightly modified from PEP487 doc + def __new__(cls, *args, **kwargs): + if len(args) != 3: + return super().__new__(cls, *args) + name, bases, ns = args + init = ns.get('__init_subclass__') + if callable(init): + ns['__init_subclass__'] = classmethod(init) + newtype = super().__new__(cls, name, bases, ns) + for k, v in newtype.__dict__.items(): + func = getattr(v, '__set_name__', None) + if func is not None: + func(newtype, k) + if bases: + super(newtype, newtype).__init_subclass__(**kwargs) # pylint: disable=bad-super-call + return newtype + + def __init__(cls, name, bases, ns, **kwargs): + super().__init__(name, bases, ns) + + class Object(metaclass=PEP487Metaclass): + @classmethod + def __init_subclass__(cls, *args, **kwargs): + pass diff --git a/secop/properties.py b/secop/properties.py index 97a6c45..047c147 100644 --- a/secop/properties.py +++ b/secop/properties.py @@ -24,24 +24,12 @@ import inspect -import sys from secop.errors import BadValueError, ConfigError, ProgrammingError +from secop.lib.py35compat import Object -class HasDescriptorMeta(type): - def __new__(cls, name, bases, attrs): - newtype = type.__new__(cls, name, bases, attrs) - if sys.version_info < (3, 6): - # support older python versions - for key, attr in attrs.items(): - if hasattr(attr, '__set_name__'): - attr.__set_name__(newtype, key) - newtype.__init_subclass__() - return newtype - - -class HasDescriptors(metaclass=HasDescriptorMeta): +class HasDescriptors(Object): @classmethod def __init_subclass__(cls): # when migrating old style declarations, sometimes the trailing comma is not removed @@ -142,12 +130,6 @@ class HasProperties(HasDescriptors): @classmethod def __init_subclass__(cls): super().__init_subclass__() - # raise an error when an attribute is a tuple with one single descriptor as element - # when migrating old style declarations, sometimes the trailing comma is not removed - bad = [k for k, v in cls.__dict__.items() - if isinstance(v, tuple) and len(v) == 1 and hasattr(v[0], '__set_name__')] - if bad: - raise ProgrammingError('misplaced trailing comma after %s.%s' % (cls.__name__, '/'.join(bad))) properties = {} # using cls.__bases__ and base.propertyDict for this would fail on some multiple inheritance cases for base in reversed(cls.__mro__): diff --git a/secop/protocol/interface/tcp.py b/secop/protocol/interface/tcp.py index 417db97..a41f4b4 100644 --- a/secop/protocol/interface/tcp.py +++ b/secop/protocol/interface/tcp.py @@ -202,3 +202,11 @@ class TCPServer(socketserver.ThreadingTCPServer): if ntry: self.log.warning('tried again %d times after "Address already in use"' % ntry) self.log.info("TCPServer initiated") + + # py35 compatibility + if not hasattr(socketserver.ThreadingTCPServer, '__exit__'): + def __enter__(self): + return self + + def __exit__(self, *args): + self.server_close()