From 2cea05f127499f42179b3699866b8e1444761b9f Mon Sep 17 00:00:00 2001
From: Andrew Bauer <zonexpertconsulting@outlook.com>
Date: Wed, 17 Jun 2020 10:25:14 -0500
Subject: [PATCH] unbundle pysignals
---
ouimeaux/discovery.py | 2 +-
ouimeaux/pysignals/LICENSE.txt | 68 -----
ouimeaux/pysignals/__init__.py | 9 -
ouimeaux/pysignals/dispatcher.py | 385 ------------------------
ouimeaux/pysignals/inspect.py | 131 --------
ouimeaux/pysignals/license.python.txt | 254 ----------------
ouimeaux/pysignals/weakref_backports.py | 68 -----
ouimeaux/signals.py | 2 +-
requirements.txt | 1 +
9 files changed, 3 insertions(+), 917 deletions(-)
delete mode 100644 ouimeaux/pysignals/LICENSE.txt
delete mode 100644 ouimeaux/pysignals/__init__.py
delete mode 100644 ouimeaux/pysignals/dispatcher.py
delete mode 100644 ouimeaux/pysignals/inspect.py
delete mode 100644 ouimeaux/pysignals/license.python.txt
delete mode 100644 ouimeaux/pysignals/weakref_backports.py
diff --git a/ouimeaux/discovery.py b/ouimeaux/discovery.py
index 21bb2e7..a0c65a1 100644
--- a/ouimeaux/discovery.py
+++ b/ouimeaux/discovery.py
@@ -5,7 +5,7 @@
from gevent.server import DatagramServer
from ouimeaux.utils import get_ip_address
-from ouimeaux.pysignals import receiver
+from pysignals import receiver
from ouimeaux.signals import discovered
diff --git a/ouimeaux/pysignals/LICENSE.txt b/ouimeaux/pysignals/LICENSE.txt
deleted file mode 100644
index 77121f5..0000000
--- a/ouimeaux/pysignals/LICENSE.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-pysignals was originally forked from django.dispatch.
-
-Copyright (c) Django Software Foundation and individual contributors.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- 3. Neither the name of Django nor the names of its contributors may be used
- to endorse or promote products derived from this software without
- specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-
-django.dispatch was originally forked from PyDispatcher.
-
-PyDispatcher License:
-
- Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials
- provided with the distribution.
-
- The name of Patrick K. O'Brien, or the name of any Contributor,
- may not be used to endorse or promote products derived from this
- software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- OF THE POSSIBILITY OF SUCH DAMAGE.
-
diff --git a/ouimeaux/pysignals/__init__.py b/ouimeaux/pysignals/__init__.py
deleted file mode 100644
index 18b284c..0000000
--- a/ouimeaux/pysignals/__init__.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""Multi-consumer multi-producer dispatching mechanism
-
-Originally based on pydispatch (BSD) http://pypi.python.org/pypi/PyDispatcher/2.0.1
-See license.txt for original license.
-
-Heavily modified for Django's purposes.
-"""
-
-from .dispatcher import Signal, StateChange, receiver # NOQA
diff --git a/ouimeaux/pysignals/dispatcher.py b/ouimeaux/pysignals/dispatcher.py
deleted file mode 100644
index 4baf101..0000000
--- a/ouimeaux/pysignals/dispatcher.py
+++ /dev/null
@@ -1,385 +0,0 @@
-from __future__ import absolute_import
-
-import sys
-import threading
-import weakref
-
-import logging
-
-from future.builtins import range
-import six
-from .inspect import func_accepts_kwargs
-
-if six.PY2:
- from .weakref_backports import WeakMethod
-else:
- from weakref import WeakMethod
-
-
-pysignals_debug = False
-
-
-def set_debug( val ):
- pysignals_debug = val
-
-
-def _make_id(target):
- if hasattr(target, '__func__'):
- return (id(target.__self__), id(target.__func__))
- return id(target)
-NONE_ID = _make_id(None)
-
-# A marker for caching
-NO_RECEIVERS = object()
-
-
-class Signal(object):
- """
- Base class for all signals
-
- Internal attributes:
-
- receivers
- { receiverkey (id) : weakref(receiver) }
- """
- def __init__(self, providing_args=None, use_caching=False):
- """
- Create a new signal.
-
- providing_args
- A list of the arguments this signal can pass along in a send() call.
- """
- self.receivers = []
- if providing_args is None:
- providing_args = []
- self.providing_args = set(providing_args)
- self.lock = threading.Lock()
- self.use_caching = use_caching
- # For convenience we create empty caches even if they are not used.
- # A note about caching: if use_caching is defined, then for each
- # distinct sender we cache the receivers that sender has in
- # 'sender_receivers_cache'. The cache is cleaned when .connect() or
- # .disconnect() is called and populated on send().
- self.sender_receivers_cache = weakref.WeakKeyDictionary() if use_caching else {}
- self._dead_receivers = False
-
- def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
- """
- Connect receiver to sender for signal.
-
- Arguments:
-
- receiver
- A function or an instance method which is to receive signals.
- Receivers must be hashable objects.
-
- If weak is True, then receiver must be weak referenceable.
-
- Receivers must be able to accept keyword arguments.
-
- If a receiver is connected with a dispatch_uid argument, it
- will not be added if another receiver was already connected
- with that dispatch_uid.
-
- sender
- The sender to which the receiver should respond. Must either be
- of type Signal, or None to receive events from any sender.
-
- weak
- Whether to use weak references to the receiver. By default, the
- module will attempt to use weak references to the receiver
- objects. If this parameter is false, then strong references will
- be used.
-
- dispatch_uid
- An identifier used to uniquely identify a particular instance of
- a receiver. This will usually be a string, though it may be
- anything hashable.
- """
- #from django.conf import settings
-
- # If DEBUG is on, check that we got a good receiver
- if pysignals_debug:
- import inspect
- assert callable(receiver), "Signal receivers must be callable."
-
- # Check for **kwargs
- if not func_accepts_kwargs(receiver):
- raise ValueError("Signal receivers must accept keyword arguments (**kwargs).")
-
- if dispatch_uid:
- lookup_key = (dispatch_uid, _make_id(sender))
- else:
- lookup_key = (_make_id(receiver), _make_id(sender))
-
- if weak:
- ref = weakref.ref
- receiver_object = receiver
- # Check for bound methods
- if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'):
- ref = WeakMethod
- receiver_object = receiver.__self__
- if six.PY3:
- receiver = ref(receiver)
- weakref.finalize(receiver_object, self._remove_receiver)
- else:
- receiver = ref(receiver, self._remove_receiver)
-
- with self.lock:
- self._clear_dead_receivers()
- for r_key, _ in self.receivers:
- if r_key == lookup_key:
- break
- else:
- self.receivers.append((lookup_key, receiver))
- self.sender_receivers_cache.clear()
-
- def disconnect(self, receiver=None, sender=None, weak=None, dispatch_uid=None):
- """
- Disconnect receiver from sender for signal.
-
- If weak references are used, disconnect need not be called. The receiver
- will be remove from dispatch automatically.
-
- Arguments:
-
- receiver
- The registered receiver to disconnect. May be none if
- dispatch_uid is specified.
-
- sender
- The registered sender to disconnect
-
- dispatch_uid
- the unique identifier of the receiver to disconnect
- """
- if weak is not None:
- logging.WARNING("Passing `weak` to disconnect has no effect.")
- if dispatch_uid:
- lookup_key = (dispatch_uid, _make_id(sender))
- else:
- lookup_key = (_make_id(receiver), _make_id(sender))
-
- disconnected = False
- with self.lock:
- self._clear_dead_receivers()
- for index in range(len(self.receivers)):
- (r_key, _) = self.receivers[index]
- if r_key == lookup_key:
- disconnected = True
- del self.receivers[index]
- break
- self.sender_receivers_cache.clear()
- return disconnected
-
- def has_listeners(self, sender=None):
- return bool(self._live_receivers(sender))
-
- def send(self, sender, **named):
- """
- Send signal from sender to all connected receivers.
-
- If any receiver raises an error, the error propagates back through send,
- terminating the dispatch loop. So it's possible that all receivers
- won't be called if an error is raised.
-
- Arguments:
-
- sender
- The sender of the signal. Either a specific object or None.
-
- named
- Named arguments which will be passed to receivers.
-
- Returns a list of tuple pairs [(receiver, response), ... ].
- """
- responses = []
- if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
- return responses
-
- for receiver in self._live_receivers(sender):
- response = receiver(signal=self, sender=sender, **named)
- responses.append((receiver, response))
- return responses
-
- def send_robust(self, sender, **named):
- """
- Send signal from sender to all connected receivers catching errors.
-
- Arguments:
-
- sender
- The sender of the signal. Can be any python object (normally one
- registered with a connect if you actually want something to
- occur).
-
- named
- Named arguments which will be passed to receivers. These
- arguments must be a subset of the argument names defined in
- providing_args.
-
- Return a list of tuple pairs [(receiver, response), ... ]. May raise
- DispatcherKeyError.
-
- If any receiver raises an error (specifically any subclass of
- Exception), the error instance is returned as the result for that
- receiver. The traceback is always attached to the error at
- ``__traceback__``.
- """
- responses = []
- if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
- return responses
-
- # Call each receiver with whatever arguments it can accept.
- # Return a list of tuple pairs [(receiver, response), ... ].
- for receiver in self._live_receivers(sender):
- try:
- response = receiver(signal=self, sender=sender, **named)
- except Exception as err:
- if not hasattr(err, '__traceback__'):
- err.__traceback__ = sys.exc_info()[2]
- responses.append((receiver, err))
- else:
- responses.append((receiver, response))
- return responses
-
- def _clear_dead_receivers(self):
- # Note: caller is assumed to hold self.lock.
- if self._dead_receivers:
- self._dead_receivers = False
- new_receivers = []
- for r in self.receivers:
- if isinstance(r[1], weakref.ReferenceType) and r[1]() is None:
- continue
- new_receivers.append(r)
- self.receivers = new_receivers
-
- def _live_receivers(self, sender):
- """
- Filter sequence of receivers to get resolved, live receivers.
-
- This checks for weak references and resolves them, then returning only
- live receivers.
- """
- receivers = None
- if self.use_caching and not self._dead_receivers:
- receivers = self.sender_receivers_cache.get(sender)
- # We could end up here with NO_RECEIVERS even if we do check this case in
- # .send() prior to calling _live_receivers() due to concurrent .send() call.
- if receivers is NO_RECEIVERS:
- return []
- if receivers is None:
- with self.lock:
- self._clear_dead_receivers()
- senderkey = _make_id(sender)
- receivers = []
- for (receiverkey, r_senderkey), receiver in self.receivers:
- if r_senderkey == NONE_ID or r_senderkey == senderkey:
- receivers.append(receiver)
- if self.use_caching:
- if not receivers:
- self.sender_receivers_cache[sender] = NO_RECEIVERS
- else:
- # Note, we must cache the weakref versions.
- self.sender_receivers_cache[sender] = receivers
- non_weak_receivers = []
- for receiver in receivers:
- if isinstance(receiver, weakref.ReferenceType):
- # Dereference the weak reference.
- receiver = receiver()
- if receiver is not None:
- non_weak_receivers.append(receiver)
- else:
- non_weak_receivers.append(receiver)
- return non_weak_receivers
-
- def _remove_receiver(self, receiver=None):
- # Mark that the self.receivers list has dead weakrefs. If so, we will
- # clean those up in connect, disconnect and _live_receivers while
- # holding self.lock. Note that doing the cleanup here isn't a good
- # idea, _remove_receiver() will be called as side effect of garbage
- # collection, and so the call can happen while we are already holding
- # self.lock.
- self._dead_receivers = True
-
- def receive(self, **kwargs):
- """
- A decorator for connecting receivers to this signal. Used by passing in the
- keyword arguments to connect::
-
- @post_save.receive(sender=MyModel)
- def signal_receiver(sender, **kwargs):
- ...
-
- """
- def _decorator(func):
- self.connect(func, **kwargs)
- return func
- return _decorator
-
-
-class StateChange( Signal ):
-
- def __init__(self, providing_args=None):
- super(StateChange, self).__init__(providing_args)
- self.sender_status = {}
-
- def send(self, sender, **named):
- """
- Send signal from sender to all connected receivers *only if* the signal's
- contents has changed.
-
- If any receiver raises an error, the error propagates back through send,
- terminating the dispatch loop, so it is quite possible to not have all
- receivers called if a raises an error.
-
- Arguments:
-
- sender
- The sender of the signal Either a specific object or None.
-
- named
- Named arguments which will be passed to receivers.
-
- Returns a list of tuple pairs [(receiver, response), ... ].
- """
- responses = []
- if not self.receivers:
- return responses
-
- sender_id = _make_id(sender)
- if sender_id not in self.sender_status:
- self.sender_status[sender_id] = {}
-
- if self.sender_status[sender_id] == named:
- return responses
-
- self.sender_status[sender_id] = named
-
- for receiver in self._live_receivers(sender_id):
- response = receiver(signal=self, sender=sender, **named)
- responses.append((receiver, response))
- return responses
-
-
-def receiver(signal, **kwargs):
- """
- A decorator for connecting receivers to signals. Used by passing in the
- signal (or list of signals) and keyword arguments to connect::
-
- @receiver(post_save, sender=MyModel)
- def signal_receiver(sender, **kwargs):
- ...
-
- @receiver([post_save, post_delete], sender=MyModel)
- def signals_receiver(sender, **kwargs):
- ...
- """
- def _decorator(func):
- if isinstance(signal, (list, tuple)):
- for s in signal:
- s.connect(func, **kwargs)
- else:
- signal.connect(func, **kwargs)
- return func
- return _decorator
diff --git a/ouimeaux/pysignals/inspect.py b/ouimeaux/pysignals/inspect.py
deleted file mode 100644
index 0171931..0000000
--- a/ouimeaux/pysignals/inspect.py
+++ /dev/null
@@ -1,131 +0,0 @@
-from __future__ import absolute_import
-
-import inspect
-
-import six
-
-
-def getargspec(func):
- if six.PY2:
- return inspect.getargspec(func)
-
- sig = inspect.signature(func)
- args = [
- p.name for p in sig.parameters.values()
- if p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
- ]
- varargs = [
- p.name for p in sig.parameters.values()
- if p.kind == inspect.Parameter.VAR_POSITIONAL
- ]
- varargs = varargs[0] if varargs else None
- varkw = [
- p.name for p in sig.parameters.values()
- if p.kind == inspect.Parameter.VAR_KEYWORD
- ]
- varkw = varkw[0] if varkw else None
- defaults = [
- p.default for p in sig.parameters.values()
- if p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD and p.default is not p.empty
- ] or None
- return args, varargs, varkw, defaults
-
-
-def get_func_args(func):
- if six.PY2:
- argspec = inspect.getargspec(func)
- return argspec.args[1:] # ignore 'self'
-
- sig = inspect.signature(func)
- return [
- arg_name for arg_name, param in sig.parameters.items()
- if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
- ]
-
-
-def get_func_full_args(func):
- """
- Return a list of (argument name, default value) tuples. If the argument
- does not have a default value, omit it in the tuple. Arguments such as
- *args and **kwargs are also included.
- """
- if six.PY2:
- argspec = inspect.getargspec(func)
- args = argspec.args[1:] # ignore 'self'
- defaults = argspec.defaults or []
- # Split args into two lists depending on whether they have default value
- no_default = args[:len(args) - len(defaults)]
- with_default = args[len(args) - len(defaults):]
- # Join the two lists and combine it with default values
- args = [(arg,) for arg in no_default] + zip(with_default, defaults)
- # Add possible *args and **kwargs and prepend them with '*' or '**'
- varargs = [('*' + argspec.varargs,)] if argspec.varargs else []
- kwargs = [('**' + argspec.keywords,)] if argspec.keywords else []
- return args + varargs + kwargs
-
- sig = inspect.signature(func)
- args = []
- for arg_name, param in sig.parameters.items():
- name = arg_name
- # Ignore 'self'
- if name == 'self':
- continue
- if param.kind == inspect.Parameter.VAR_POSITIONAL:
- name = '*' + name
- elif param.kind == inspect.Parameter.VAR_KEYWORD:
- name = '**' + name
- if param.default != inspect.Parameter.empty:
- args.append((name, param.default))
- else:
- args.append((name,))
- return args
-
-
-def func_accepts_kwargs(func):
- if six.PY2:
- # Not all callables are inspectable with getargspec, so we'll
- # try a couple different ways but in the end fall back on assuming
- # it is -- we don't want to prevent registration of valid but weird
- # callables.
- try:
- argspec = inspect.getargspec(func)
- except TypeError:
- try:
- argspec = inspect.getargspec(func.__call__)
- except (TypeError, AttributeError):
- argspec = None
- return not argspec or argspec[2] is not None
-
- return any(
- p for p in inspect.signature(func).parameters.values()
- if p.kind == p.VAR_KEYWORD
- )
-
-
-def func_accepts_var_args(func):
- """
- Return True if function 'func' accepts positional arguments *args.
- """
- if six.PY2:
- return inspect.getargspec(func)[1] is not None
-
- return any(
- p for p in inspect.signature(func).parameters.values()
- if p.kind == p.VAR_POSITIONAL
- )
-
-
-def func_has_no_args(func):
- args = inspect.getargspec(func)[0] if six.PY2 else [
- p for p in inspect.signature(func).parameters.values()
- if p.kind == p.POSITIONAL_OR_KEYWORD
- ]
- return len(args) == 1
-
-
-def func_supports_parameter(func, parameter):
- if six.PY3:
- return parameter in inspect.signature(func).parameters
- else:
- args, varargs, varkw, defaults = inspect.getargspec(func)
- return parameter in args
diff --git a/ouimeaux/pysignals/license.python.txt b/ouimeaux/pysignals/license.python.txt
deleted file mode 100644
index f9ca2c9..0000000
--- a/ouimeaux/pysignals/license.python.txt
+++ /dev/null
@@ -1,254 +0,0 @@
-A. HISTORY OF THE SOFTWARE
-==========================
-
-Python was created in the early 1990s by Guido van Rossum at Stichting
-Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
-as a successor of a language called ABC. Guido remains Python's
-principal author, although it includes many contributions from others.
-
-In 1995, Guido continued his work on Python at the Corporation for
-National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
-in Reston, Virginia where he released several versions of the
-software.
-
-In May 2000, Guido and the Python core development team moved to
-BeOpen.com to form the BeOpen PythonLabs team. In October of the same
-year, the PythonLabs team moved to Digital Creations (now Zope
-Corporation, see http://www.zope.com). In 2001, the Python Software
-Foundation (PSF, see http://www.python.org/psf/) was formed, a
-non-profit organization created specifically to own Python-related
-Intellectual Property. Zope Corporation is a sponsoring member of
-the PSF.
-
-All Python releases are Open Source (see http://www.opensource.org for
-the Open Source Definition). Historically, most, but not all, Python
-releases have also been GPL-compatible; the table below summarizes
-the various releases.
-
- Release Derived Year Owner GPL-
- from compatible? (1)
-
- 0.9.0 thru 1.2 1991-1995 CWI yes
- 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
- 1.6 1.5.2 2000 CNRI no
- 2.0 1.6 2000 BeOpen.com no
- 1.6.1 1.6 2001 CNRI yes (2)
- 2.1 2.0+1.6.1 2001 PSF no
- 2.0.1 2.0+1.6.1 2001 PSF yes
- 2.1.1 2.1+2.0.1 2001 PSF yes
- 2.1.2 2.1.1 2002 PSF yes
- 2.1.3 2.1.2 2002 PSF yes
- 2.2 and above 2.1.1 2001-now PSF yes
-
-Footnotes:
-
-(1) GPL-compatible doesn't mean that we're distributing Python under
- the GPL. All Python licenses, unlike the GPL, let you distribute
- a modified version without making your changes open source. The
- GPL-compatible licenses make it possible to combine Python with
- other software that is released under the GPL; the others don't.
-
-(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
- because its license has a choice of law clause. According to
- CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
- is "not incompatible" with the GPL.
-
-Thanks to the many outside volunteers who have worked under Guido's
-direction to make these releases possible.
-
-
-B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
-===============================================================
-
-PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
---------------------------------------------
-
-1. This LICENSE AGREEMENT is between the Python Software Foundation
-("PSF"), and the Individual or Organization ("Licensee") accessing and
-otherwise using this software ("Python") in source or binary form and
-its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, PSF hereby
-grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
-analyze, test, perform and/or display publicly, prepare derivative works,
-distribute, and otherwise use Python alone or in any derivative version,
-provided, however, that PSF's License Agreement and PSF's notice of copyright,
-i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-2011, 2012, 2013, 2014 Python Software Foundation; All Rights Reserved" are retained
-in Python alone or in any derivative version prepared by Licensee.
-
-3. In the event Licensee prepares a derivative work that is based on
-or incorporates Python or any part thereof, and wants to make
-the derivative work available to others as provided herein, then
-Licensee hereby agrees to include in any such work a brief summary of
-the changes made to Python.
-
-4. PSF is making Python available to Licensee on an "AS IS"
-basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
-OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-7. Nothing in this License Agreement shall be deemed to create any
-relationship of agency, partnership, or joint venture between PSF and
-Licensee. This License Agreement does not grant permission to use PSF
-trademarks or trade name in a trademark sense to endorse or promote
-products or services of Licensee, or any third party.
-
-8. By copying, installing or otherwise using Python, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
-
-
-BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
--------------------------------------------
-
-BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
-
-1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
-office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
-Individual or Organization ("Licensee") accessing and otherwise using
-this software in source or binary form and its associated
-documentation ("the Software").
-
-2. Subject to the terms and conditions of this BeOpen Python License
-Agreement, BeOpen hereby grants Licensee a non-exclusive,
-royalty-free, world-wide license to reproduce, analyze, test, perform
-and/or display publicly, prepare derivative works, distribute, and
-otherwise use the Software alone or in any derivative version,
-provided, however, that the BeOpen Python License is retained in the
-Software, alone or in any derivative version prepared by Licensee.
-
-3. BeOpen is making the Software available to Licensee on an "AS IS"
-basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
-SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
-AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
-DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-5. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-6. This License Agreement shall be governed by and interpreted in all
-respects by the law of the State of California, excluding conflict of
-law provisions. Nothing in this License Agreement shall be deemed to
-create any relationship of agency, partnership, or joint venture
-between BeOpen and Licensee. This License Agreement does not grant
-permission to use BeOpen trademarks or trade names in a trademark
-sense to endorse or promote products or services of Licensee, or any
-third party. As an exception, the "BeOpen Python" logos available at
-http://www.pythonlabs.com/logos.html may be used according to the
-permissions granted on that web page.
-
-7. By copying, installing or otherwise using the software, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
-
-
-CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
----------------------------------------
-
-1. This LICENSE AGREEMENT is between the Corporation for National
-Research Initiatives, having an office at 1895 Preston White Drive,
-Reston, VA 20191 ("CNRI"), and the Individual or Organization
-("Licensee") accessing and otherwise using Python 1.6.1 software in
-source or binary form and its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, CNRI
-hereby grants Licensee a nonexclusive, royalty-free, world-wide
-license to reproduce, analyze, test, perform and/or display publicly,
-prepare derivative works, distribute, and otherwise use Python 1.6.1
-alone or in any derivative version, provided, however, that CNRI's
-License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
-1995-2001 Corporation for National Research Initiatives; All Rights
-Reserved" are retained in Python 1.6.1 alone or in any derivative
-version prepared by Licensee. Alternately, in lieu of CNRI's License
-Agreement, Licensee may substitute the following text (omitting the
-quotes): "Python 1.6.1 is made available subject to the terms and
-conditions in CNRI's License Agreement. This Agreement together with
-Python 1.6.1 may be located on the Internet using the following
-unique, persistent identifier (known as a handle): 1895.22/1013. This
-Agreement may also be obtained from a proxy server on the Internet
-using the following URL: http://hdl.handle.net/1895.22/1013".
-
-3. In the event Licensee prepares a derivative work that is based on
-or incorporates Python 1.6.1 or any part thereof, and wants to make
-the derivative work available to others as provided herein, then
-Licensee hereby agrees to include in any such work a brief summary of
-the changes made to Python 1.6.1.
-
-4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
-basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
-OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-7. This License Agreement shall be governed by the federal
-intellectual property law of the United States, including without
-limitation the federal copyright law, and, to the extent such
-U.S. federal law does not apply, by the law of the Commonwealth of
-Virginia, excluding Virginia's conflict of law provisions.
-Notwithstanding the foregoing, with regard to derivative works based
-on Python 1.6.1 that incorporate non-separable material that was
-previously distributed under the GNU General Public License (GPL), the
-law of the Commonwealth of Virginia shall govern this License
-Agreement only as to issues arising under or with respect to
-Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
-License Agreement shall be deemed to create any relationship of
-agency, partnership, or joint venture between CNRI and Licensee. This
-License Agreement does not grant permission to use CNRI trademarks or
-trade name in a trademark sense to endorse or promote products or
-services of Licensee, or any third party.
-
-8. By clicking on the "ACCEPT" button where indicated, or by copying,
-installing or otherwise using Python 1.6.1, Licensee agrees to be
-bound by the terms and conditions of this License Agreement.
-
- ACCEPT
-
-
-CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
---------------------------------------------------
-
-Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
-The Netherlands. All rights reserved.
-
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-provided that the above copyright notice appear in all copies and that
-both that copyright notice and this permission notice appear in
-supporting documentation, and that the name of Stichting Mathematisch
-Centrum or CWI not be used in advertising or publicity pertaining to
-distribution of the software without specific, written prior
-permission.
-
-STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
-THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
-FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/ouimeaux/pysignals/weakref_backports.py b/ouimeaux/pysignals/weakref_backports.py
deleted file mode 100644
index edc8693..0000000
--- a/ouimeaux/pysignals/weakref_backports.py
+++ /dev/null
@@ -1,68 +0,0 @@
-"""
-weakref_backports is a partial backport of the weakref module for python
-versions below 3.4.
-
-Copyright (C) 2013 Python Software Foundation, see license.python.txt for
-details.
-
-The following changes were made to the original sources during backporting:
-
- * Added `self` to `super` calls.
- * Removed `from None` when raising exceptions.
-
-"""
-from weakref import ref
-
-
-class WeakMethod(ref):
- """
- A custom `weakref.ref` subclass which simulates a weak reference to
- a bound method, working around the lifetime problem of bound methods.
- """
-
- __slots__ = "_func_ref", "_meth_type", "_alive", "__weakref__"
-
- def __new__(cls, meth, callback=None):
- try:
- obj = meth.__self__
- func = meth.__func__
- except AttributeError:
- raise TypeError("argument should be a bound method, not {}"
- .format(type(meth)))
- def _cb(arg):
- # The self-weakref trick is needed to avoid creating a reference
- # cycle.
- self = self_wr()
- if self._alive:
- self._alive = False
- if callback is not None:
- callback(self)
- self = ref.__new__(cls, obj, _cb)
- self._func_ref = ref(func, _cb)
- self._meth_type = type(meth)
- self._alive = True
- self_wr = ref(self)
- return self
-
- def __call__(self):
- obj = super(WeakMethod, self).__call__()
- func = self._func_ref()
- if obj is None or func is None:
- return None
- return self._meth_type(func, obj)
-
- def __eq__(self, other):
- if isinstance(other, WeakMethod):
- if not self._alive or not other._alive:
- return self is other
- return ref.__eq__(self, other) and self._func_ref == other._func_ref
- return False
-
- def __ne__(self, other):
- if isinstance(other, WeakMethod):
- if not self._alive or not other._alive:
- return self is not other
- return ref.__ne__(self, other) or self._func_ref != other._func_ref
- return True
-
- __hash__ = ref.__hash__
diff --git a/ouimeaux/signals.py b/ouimeaux/signals.py
index b421aab..9b61c25 100644
--- a/ouimeaux/signals.py
+++ b/ouimeaux/signals.py
@@ -1,4 +1,4 @@
-from ouimeaux.pysignals import Signal, StateChange, receiver
+from pysignals import Signal, StateChange, receiver
# Work around a bug in pysignals when in the interactive interpreter
import sys
diff --git a/requirements.txt b/requirements.txt
index efba2a3..e146907 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,3 +3,4 @@ requests >= 2.3.0
pyyaml
six
future
+pysignals