Updated script that can be controled by Nodejs web app
This commit is contained in:
69
lib/python3.13/site-packages/eventlet/support/__init__.py
Normal file
69
lib/python3.13/site-packages/eventlet/support/__init__.py
Normal file
@ -0,0 +1,69 @@
|
||||
import inspect
|
||||
import functools
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from eventlet.support import greenlets
|
||||
|
||||
|
||||
_MISSING = object()
|
||||
|
||||
|
||||
def get_errno(exc):
|
||||
""" Get the error code out of socket.error objects.
|
||||
socket.error in <2.5 does not have errno attribute
|
||||
socket.error in 3.x does not allow indexing access
|
||||
e.args[0] works for all.
|
||||
There are cases when args[0] is not errno.
|
||||
i.e. http://bugs.python.org/issue6471
|
||||
Maybe there are cases when errno is set, but it is not the first argument?
|
||||
"""
|
||||
|
||||
try:
|
||||
if exc.errno is not None:
|
||||
return exc.errno
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
return exc.args[0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
def bytes_to_str(b, encoding='ascii'):
|
||||
return b
|
||||
else:
|
||||
def bytes_to_str(b, encoding='ascii'):
|
||||
return b.decode(encoding)
|
||||
|
||||
PY33 = sys.version_info[:2] == (3, 3)
|
||||
|
||||
|
||||
def wrap_deprecated(old, new):
|
||||
def _resolve(s):
|
||||
return 'eventlet.'+s if '.' not in s else s
|
||||
msg = '''\
|
||||
{old} is deprecated and will be removed in next version. Use {new} instead.
|
||||
Autoupgrade: fgrep -rl '{old}' . |xargs -t sed --in-place='' -e 's/{old}/{new}/'
|
||||
'''.format(old=_resolve(old), new=_resolve(new))
|
||||
|
||||
def wrapper(base):
|
||||
klass = None
|
||||
if inspect.isclass(base):
|
||||
class klass(base):
|
||||
pass
|
||||
klass.__name__ = base.__name__
|
||||
klass.__module__ = base.__module__
|
||||
|
||||
@functools.wraps(base)
|
||||
def wrapped(*a, **kw):
|
||||
warnings.warn(msg, DeprecationWarning, stacklevel=5)
|
||||
return base(*a, **kw)
|
||||
|
||||
if klass is not None:
|
||||
klass.__init__ = wrapped
|
||||
return klass
|
||||
|
||||
return wrapped
|
||||
return wrapper
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
959
lib/python3.13/site-packages/eventlet/support/greendns.py
Normal file
959
lib/python3.13/site-packages/eventlet/support/greendns.py
Normal file
@ -0,0 +1,959 @@
|
||||
'''greendns - non-blocking DNS support for Eventlet
|
||||
'''
|
||||
|
||||
# Portions of this code taken from the gogreen project:
|
||||
# http://github.com/slideinc/gogreen
|
||||
#
|
||||
# Copyright (c) 2005-2010 Slide, Inc.
|
||||
# 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.
|
||||
# * Neither the name of the author nor the names of other
|
||||
# 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.
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
|
||||
import eventlet
|
||||
from eventlet import patcher
|
||||
from eventlet.green import _socket_nodns
|
||||
from eventlet.green import os
|
||||
from eventlet.green import time
|
||||
from eventlet.green import select
|
||||
from eventlet.green import ssl
|
||||
|
||||
|
||||
def import_patched(module_name):
|
||||
# Import cycle note: it's crucial to use _socket_nodns here because
|
||||
# regular evenlet.green.socket imports *this* module and if we imported
|
||||
# it back we'd end with an import cycle (socket -> greendns -> socket).
|
||||
# We break this import cycle by providing a restricted socket module.
|
||||
modules = {
|
||||
'select': select,
|
||||
'time': time,
|
||||
'os': os,
|
||||
'socket': _socket_nodns,
|
||||
'ssl': ssl,
|
||||
}
|
||||
return patcher.import_patched(module_name, **modules)
|
||||
|
||||
|
||||
dns = import_patched('dns')
|
||||
|
||||
# Handle rdtypes separately; we need fully it available as we patch the rest
|
||||
dns.rdtypes = import_patched('dns.rdtypes')
|
||||
dns.rdtypes.__all__.extend(['dnskeybase', 'dsbase', 'txtbase'])
|
||||
for pkg in dns.rdtypes.__all__:
|
||||
setattr(dns.rdtypes, pkg, import_patched('dns.rdtypes.' + pkg))
|
||||
for pkg in dns.rdtypes.IN.__all__:
|
||||
setattr(dns.rdtypes.IN, pkg, import_patched('dns.rdtypes.IN.' + pkg))
|
||||
for pkg in dns.rdtypes.ANY.__all__:
|
||||
setattr(dns.rdtypes.ANY, pkg, import_patched('dns.rdtypes.ANY.' + pkg))
|
||||
|
||||
for pkg in dns.__all__:
|
||||
if pkg == 'rdtypes':
|
||||
continue
|
||||
setattr(dns, pkg, import_patched('dns.' + pkg))
|
||||
del import_patched
|
||||
|
||||
|
||||
socket = _socket_nodns
|
||||
|
||||
DNS_QUERY_TIMEOUT = 10.0
|
||||
HOSTS_TTL = 10.0
|
||||
|
||||
# NOTE(victor): do not use EAI_*_ERROR instances for raising errors in python3, which will cause a memory leak.
|
||||
EAI_EAGAIN_ERROR = socket.gaierror(socket.EAI_AGAIN, 'Lookup timed out')
|
||||
EAI_NONAME_ERROR = socket.gaierror(socket.EAI_NONAME, 'Name or service not known')
|
||||
# EAI_NODATA was removed from RFC3493, it's now replaced with EAI_NONAME
|
||||
# socket.EAI_NODATA is not defined on FreeBSD, probably on some other platforms too.
|
||||
# https://lists.freebsd.org/pipermail/freebsd-ports/2003-October/005757.html
|
||||
EAI_NODATA_ERROR = EAI_NONAME_ERROR
|
||||
if (os.environ.get('EVENTLET_DEPRECATED_EAI_NODATA', '').lower() in ('1', 'y', 'yes')
|
||||
and hasattr(socket, 'EAI_NODATA')):
|
||||
EAI_NODATA_ERROR = socket.gaierror(socket.EAI_NODATA, 'No address associated with hostname')
|
||||
|
||||
|
||||
def _raise_new_error(error_instance):
|
||||
raise error_instance.__class__(*error_instance.args)
|
||||
|
||||
|
||||
def is_ipv4_addr(host):
|
||||
"""Return True if host is a valid IPv4 address"""
|
||||
if not isinstance(host, str):
|
||||
return False
|
||||
try:
|
||||
dns.ipv4.inet_aton(host)
|
||||
except dns.exception.SyntaxError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def is_ipv6_addr(host):
|
||||
"""Return True if host is a valid IPv6 address"""
|
||||
if not isinstance(host, str):
|
||||
return False
|
||||
host = host.split('%', 1)[0]
|
||||
try:
|
||||
dns.ipv6.inet_aton(host)
|
||||
except dns.exception.SyntaxError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def is_ip_addr(host):
|
||||
"""Return True if host is a valid IPv4 or IPv6 address"""
|
||||
return is_ipv4_addr(host) or is_ipv6_addr(host)
|
||||
|
||||
|
||||
# NOTE(ralonsoh): in dnspython v2.0.0, "_compute_expiration" was replaced
|
||||
# by "_compute_times".
|
||||
if hasattr(dns.query, '_compute_expiration'):
|
||||
def compute_expiration(query, timeout):
|
||||
return query._compute_expiration(timeout)
|
||||
else:
|
||||
def compute_expiration(query, timeout):
|
||||
return query._compute_times(timeout)[1]
|
||||
|
||||
|
||||
class HostsAnswer(dns.resolver.Answer):
|
||||
"""Answer class for HostsResolver object"""
|
||||
|
||||
def __init__(self, qname, rdtype, rdclass, rrset, raise_on_no_answer=True):
|
||||
"""Create a new answer
|
||||
|
||||
:qname: A dns.name.Name instance of the query name
|
||||
:rdtype: The rdatatype of the query
|
||||
:rdclass: The rdataclass of the query
|
||||
:rrset: The dns.rrset.RRset with the response, must have ttl attribute
|
||||
:raise_on_no_answer: Whether to raise dns.resolver.NoAnswer if no
|
||||
answer.
|
||||
"""
|
||||
self.response = None
|
||||
self.qname = qname
|
||||
self.rdtype = rdtype
|
||||
self.rdclass = rdclass
|
||||
self.canonical_name = qname
|
||||
if not rrset and raise_on_no_answer:
|
||||
raise dns.resolver.NoAnswer()
|
||||
self.rrset = rrset
|
||||
self.expiration = (time.time() +
|
||||
rrset.ttl if hasattr(rrset, 'ttl') else 0)
|
||||
|
||||
|
||||
class HostsResolver:
|
||||
"""Class to parse the hosts file
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
||||
:fname: The filename of the hosts file in use.
|
||||
:interval: The time between checking for hosts file modification
|
||||
"""
|
||||
|
||||
LINES_RE = re.compile(r"""
|
||||
\s* # Leading space
|
||||
([^\r\n#]*?) # The actual match, non-greedy so as not to include trailing space
|
||||
\s* # Trailing space
|
||||
(?:[#][^\r\n]+)? # Comments
|
||||
(?:$|[\r\n]+) # EOF or newline
|
||||
""", re.VERBOSE)
|
||||
|
||||
def __init__(self, fname=None, interval=HOSTS_TTL):
|
||||
self._v4 = {} # name -> ipv4
|
||||
self._v6 = {} # name -> ipv6
|
||||
self._aliases = {} # name -> canonical_name
|
||||
self.interval = interval
|
||||
self.fname = fname
|
||||
if fname is None:
|
||||
if os.name == 'posix':
|
||||
self.fname = '/etc/hosts'
|
||||
elif os.name == 'nt':
|
||||
self.fname = os.path.expandvars(
|
||||
r'%SystemRoot%\system32\drivers\etc\hosts')
|
||||
self._last_load = 0
|
||||
if self.fname:
|
||||
self._load()
|
||||
|
||||
def _readlines(self):
|
||||
"""Read the contents of the hosts file
|
||||
|
||||
Return list of lines, comment lines and empty lines are
|
||||
excluded.
|
||||
|
||||
Note that this performs disk I/O so can be blocking.
|
||||
"""
|
||||
try:
|
||||
with open(self.fname, 'rb') as fp:
|
||||
fdata = fp.read()
|
||||
except OSError:
|
||||
return []
|
||||
|
||||
udata = fdata.decode(errors='ignore')
|
||||
|
||||
return filter(None, self.LINES_RE.findall(udata))
|
||||
|
||||
def _load(self):
|
||||
"""Load hosts file
|
||||
|
||||
This will unconditionally (re)load the data from the hosts
|
||||
file.
|
||||
"""
|
||||
lines = self._readlines()
|
||||
self._v4.clear()
|
||||
self._v6.clear()
|
||||
self._aliases.clear()
|
||||
for line in lines:
|
||||
parts = line.split()
|
||||
if len(parts) < 2:
|
||||
continue
|
||||
ip = parts.pop(0)
|
||||
if is_ipv4_addr(ip):
|
||||
ipmap = self._v4
|
||||
elif is_ipv6_addr(ip):
|
||||
if ip.startswith('fe80'):
|
||||
# Do not use link-local addresses, OSX stores these here
|
||||
continue
|
||||
ipmap = self._v6
|
||||
else:
|
||||
continue
|
||||
cname = parts.pop(0).lower()
|
||||
ipmap[cname] = ip
|
||||
for alias in parts:
|
||||
alias = alias.lower()
|
||||
ipmap[alias] = ip
|
||||
self._aliases[alias] = cname
|
||||
self._last_load = time.time()
|
||||
|
||||
def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
|
||||
tcp=False, source=None, raise_on_no_answer=True):
|
||||
"""Query the hosts file
|
||||
|
||||
The known rdtypes are dns.rdatatype.A, dns.rdatatype.AAAA and
|
||||
dns.rdatatype.CNAME.
|
||||
|
||||
The ``rdclass`` parameter must be dns.rdataclass.IN while the
|
||||
``tcp`` and ``source`` parameters are ignored.
|
||||
|
||||
Return a HostAnswer instance or raise a dns.resolver.NoAnswer
|
||||
exception.
|
||||
"""
|
||||
now = time.time()
|
||||
if self._last_load + self.interval < now:
|
||||
self._load()
|
||||
rdclass = dns.rdataclass.IN
|
||||
if isinstance(qname, str):
|
||||
name = qname
|
||||
qname = dns.name.from_text(qname)
|
||||
elif isinstance(qname, bytes):
|
||||
name = qname.decode("ascii")
|
||||
qname = dns.name.from_text(qname)
|
||||
else:
|
||||
name = str(qname)
|
||||
name = name.lower()
|
||||
rrset = dns.rrset.RRset(qname, rdclass, rdtype)
|
||||
rrset.ttl = self._last_load + self.interval - now
|
||||
if rdclass == dns.rdataclass.IN and rdtype == dns.rdatatype.A:
|
||||
addr = self._v4.get(name)
|
||||
if not addr and qname.is_absolute():
|
||||
addr = self._v4.get(name[:-1])
|
||||
if addr:
|
||||
rrset.add(dns.rdtypes.IN.A.A(rdclass, rdtype, addr))
|
||||
elif rdclass == dns.rdataclass.IN and rdtype == dns.rdatatype.AAAA:
|
||||
addr = self._v6.get(name)
|
||||
if not addr and qname.is_absolute():
|
||||
addr = self._v6.get(name[:-1])
|
||||
if addr:
|
||||
rrset.add(dns.rdtypes.IN.AAAA.AAAA(rdclass, rdtype, addr))
|
||||
elif rdclass == dns.rdataclass.IN and rdtype == dns.rdatatype.CNAME:
|
||||
cname = self._aliases.get(name)
|
||||
if not cname and qname.is_absolute():
|
||||
cname = self._aliases.get(name[:-1])
|
||||
if cname:
|
||||
rrset.add(dns.rdtypes.ANY.CNAME.CNAME(
|
||||
rdclass, rdtype, dns.name.from_text(cname)))
|
||||
return HostsAnswer(qname, rdtype, rdclass, rrset, raise_on_no_answer)
|
||||
|
||||
def getaliases(self, hostname):
|
||||
"""Return a list of all the aliases of a given cname"""
|
||||
# Due to the way store aliases this is a bit inefficient, this
|
||||
# clearly was an afterthought. But this is only used by
|
||||
# gethostbyname_ex so it's probably fine.
|
||||
aliases = []
|
||||
if hostname in self._aliases:
|
||||
cannon = self._aliases[hostname]
|
||||
else:
|
||||
cannon = hostname
|
||||
aliases.append(cannon)
|
||||
for alias, cname in self._aliases.items():
|
||||
if cannon == cname:
|
||||
aliases.append(alias)
|
||||
aliases.remove(hostname)
|
||||
return aliases
|
||||
|
||||
|
||||
class ResolverProxy:
|
||||
"""Resolver class which can also use /etc/hosts
|
||||
|
||||
Initialise with a HostsResolver instance in order for it to also
|
||||
use the hosts file.
|
||||
"""
|
||||
|
||||
def __init__(self, hosts_resolver=None, filename='/etc/resolv.conf'):
|
||||
"""Initialise the resolver proxy
|
||||
|
||||
:param hosts_resolver: An instance of HostsResolver to use.
|
||||
|
||||
:param filename: The filename containing the resolver
|
||||
configuration. The default value is correct for both UNIX
|
||||
and Windows, on Windows it will result in the configuration
|
||||
being read from the Windows registry.
|
||||
"""
|
||||
self._hosts = hosts_resolver
|
||||
self._filename = filename
|
||||
# NOTE(dtantsur): we cannot create a resolver here since this code is
|
||||
# executed on eventlet import. In an environment without DNS, creating
|
||||
# a Resolver will fail making eventlet unusable at all. See
|
||||
# https://github.com/eventlet/eventlet/issues/736 for details.
|
||||
self._cached_resolver = None
|
||||
|
||||
@property
|
||||
def _resolver(self):
|
||||
if self._cached_resolver is None:
|
||||
self.clear()
|
||||
return self._cached_resolver
|
||||
|
||||
@_resolver.setter
|
||||
def _resolver(self, value):
|
||||
self._cached_resolver = value
|
||||
|
||||
def clear(self):
|
||||
self._resolver = dns.resolver.Resolver(filename=self._filename)
|
||||
self._resolver.cache = dns.resolver.LRUCache()
|
||||
|
||||
def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
|
||||
tcp=False, source=None, raise_on_no_answer=True,
|
||||
_hosts_rdtypes=(dns.rdatatype.A, dns.rdatatype.AAAA),
|
||||
use_network=True):
|
||||
"""Query the resolver, using /etc/hosts if enabled.
|
||||
|
||||
Behavior:
|
||||
1. if hosts is enabled and contains answer, return it now
|
||||
2. query nameservers for qname if use_network is True
|
||||
3. if qname did not contain dots, pretend it was top-level domain,
|
||||
query "foobar." and append to previous result
|
||||
"""
|
||||
result = [None, None, 0]
|
||||
|
||||
if qname is None:
|
||||
qname = '0.0.0.0'
|
||||
if isinstance(qname, str) or isinstance(qname, bytes):
|
||||
qname = dns.name.from_text(qname, None)
|
||||
|
||||
def step(fun, *args, **kwargs):
|
||||
try:
|
||||
a = fun(*args, **kwargs)
|
||||
except Exception as e:
|
||||
result[1] = e
|
||||
return False
|
||||
if a.rrset is not None and len(a.rrset):
|
||||
if result[0] is None:
|
||||
result[0] = a
|
||||
else:
|
||||
result[0].rrset.union_update(a.rrset)
|
||||
result[2] += len(a.rrset)
|
||||
return True
|
||||
|
||||
def end():
|
||||
if result[0] is not None:
|
||||
if raise_on_no_answer and result[2] == 0:
|
||||
raise dns.resolver.NoAnswer
|
||||
return result[0]
|
||||
if result[1] is not None:
|
||||
if raise_on_no_answer or not isinstance(result[1], dns.resolver.NoAnswer):
|
||||
raise result[1]
|
||||
raise dns.resolver.NXDOMAIN(qnames=(qname,))
|
||||
|
||||
if (self._hosts and (rdclass == dns.rdataclass.IN) and (rdtype in _hosts_rdtypes)):
|
||||
if step(self._hosts.query, qname, rdtype, raise_on_no_answer=False):
|
||||
if (result[0] is not None) or (result[1] is not None) or (not use_network):
|
||||
return end()
|
||||
|
||||
# Main query
|
||||
step(self._resolver.query, qname, rdtype, rdclass, tcp, source, raise_on_no_answer=False)
|
||||
|
||||
# `resolv.conf` docs say unqualified names must resolve from search (or local) domain.
|
||||
# However, common OS `getaddrinfo()` implementations append trailing dot (e.g. `db -> db.`)
|
||||
# and ask nameservers, as if top-level domain was queried.
|
||||
# This step follows established practice.
|
||||
# https://github.com/nameko/nameko/issues/392
|
||||
# https://github.com/eventlet/eventlet/issues/363
|
||||
if len(qname) == 1:
|
||||
step(self._resolver.query, qname.concatenate(dns.name.root),
|
||||
rdtype, rdclass, tcp, source, raise_on_no_answer=False)
|
||||
|
||||
return end()
|
||||
|
||||
def getaliases(self, hostname):
|
||||
"""Return a list of all the aliases of a given hostname"""
|
||||
if self._hosts:
|
||||
aliases = self._hosts.getaliases(hostname)
|
||||
else:
|
||||
aliases = []
|
||||
while True:
|
||||
try:
|
||||
ans = self._resolver.query(hostname, dns.rdatatype.CNAME)
|
||||
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
|
||||
break
|
||||
else:
|
||||
aliases.extend(str(rr.target) for rr in ans.rrset)
|
||||
hostname = ans[0].target
|
||||
return aliases
|
||||
|
||||
|
||||
resolver = ResolverProxy(hosts_resolver=HostsResolver())
|
||||
|
||||
|
||||
def resolve(name, family=socket.AF_INET, raises=True, _proxy=None,
|
||||
use_network=True):
|
||||
"""Resolve a name for a given family using the global resolver proxy.
|
||||
|
||||
This method is called by the global getaddrinfo() function. If use_network
|
||||
is False, only resolution via hosts file will be performed.
|
||||
|
||||
Return a dns.resolver.Answer instance. If there is no answer it's
|
||||
rrset will be emtpy.
|
||||
"""
|
||||
if family == socket.AF_INET:
|
||||
rdtype = dns.rdatatype.A
|
||||
elif family == socket.AF_INET6:
|
||||
rdtype = dns.rdatatype.AAAA
|
||||
else:
|
||||
raise socket.gaierror(socket.EAI_FAMILY,
|
||||
'Address family not supported')
|
||||
|
||||
if _proxy is None:
|
||||
_proxy = resolver
|
||||
try:
|
||||
try:
|
||||
return _proxy.query(name, rdtype, raise_on_no_answer=raises,
|
||||
use_network=use_network)
|
||||
except dns.resolver.NXDOMAIN:
|
||||
if not raises:
|
||||
return HostsAnswer(dns.name.Name(name),
|
||||
rdtype, dns.rdataclass.IN, None, False)
|
||||
raise
|
||||
except dns.exception.Timeout:
|
||||
_raise_new_error(EAI_EAGAIN_ERROR)
|
||||
except dns.exception.DNSException:
|
||||
_raise_new_error(EAI_NODATA_ERROR)
|
||||
|
||||
|
||||
def resolve_cname(host):
|
||||
"""Return the canonical name of a hostname"""
|
||||
try:
|
||||
ans = resolver.query(host, dns.rdatatype.CNAME)
|
||||
except dns.resolver.NoAnswer:
|
||||
return host
|
||||
except dns.exception.Timeout:
|
||||
_raise_new_error(EAI_EAGAIN_ERROR)
|
||||
except dns.exception.DNSException:
|
||||
_raise_new_error(EAI_NODATA_ERROR)
|
||||
else:
|
||||
return str(ans[0].target)
|
||||
|
||||
|
||||
def getaliases(host):
|
||||
"""Return a list of for aliases for the given hostname
|
||||
|
||||
This method does translate the dnspython exceptions into
|
||||
socket.gaierror exceptions. If no aliases are available an empty
|
||||
list will be returned.
|
||||
"""
|
||||
try:
|
||||
return resolver.getaliases(host)
|
||||
except dns.exception.Timeout:
|
||||
_raise_new_error(EAI_EAGAIN_ERROR)
|
||||
except dns.exception.DNSException:
|
||||
_raise_new_error(EAI_NODATA_ERROR)
|
||||
|
||||
|
||||
def _getaddrinfo_lookup(host, family, flags):
|
||||
"""Resolve a hostname to a list of addresses
|
||||
|
||||
Helper function for getaddrinfo.
|
||||
"""
|
||||
if flags & socket.AI_NUMERICHOST:
|
||||
_raise_new_error(EAI_NONAME_ERROR)
|
||||
addrs = []
|
||||
if family == socket.AF_UNSPEC:
|
||||
err = None
|
||||
for use_network in [False, True]:
|
||||
for qfamily in [socket.AF_INET6, socket.AF_INET]:
|
||||
try:
|
||||
answer = resolve(host, qfamily, False, use_network=use_network)
|
||||
except socket.gaierror as e:
|
||||
if e.errno not in (socket.EAI_AGAIN, EAI_NONAME_ERROR.errno, EAI_NODATA_ERROR.errno):
|
||||
raise
|
||||
err = e
|
||||
else:
|
||||
if answer.rrset:
|
||||
addrs.extend(rr.address for rr in answer.rrset)
|
||||
if addrs:
|
||||
break
|
||||
if err is not None and not addrs:
|
||||
raise err
|
||||
elif family == socket.AF_INET6 and flags & socket.AI_V4MAPPED:
|
||||
answer = resolve(host, socket.AF_INET6, False)
|
||||
if answer.rrset:
|
||||
addrs = [rr.address for rr in answer.rrset]
|
||||
if not addrs or flags & socket.AI_ALL:
|
||||
answer = resolve(host, socket.AF_INET, False)
|
||||
if answer.rrset:
|
||||
addrs = ['::ffff:' + rr.address for rr in answer.rrset]
|
||||
else:
|
||||
answer = resolve(host, family, False)
|
||||
if answer.rrset:
|
||||
addrs = [rr.address for rr in answer.rrset]
|
||||
return str(answer.qname), addrs
|
||||
|
||||
|
||||
def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
|
||||
"""Replacement for Python's socket.getaddrinfo
|
||||
|
||||
This does the A and AAAA lookups asynchronously after which it
|
||||
calls the OS' getaddrinfo(3) using the AI_NUMERICHOST flag. This
|
||||
flag ensures getaddrinfo(3) does not use the network itself and
|
||||
allows us to respect all the other arguments like the native OS.
|
||||
"""
|
||||
if isinstance(host, str):
|
||||
host = host.encode('idna').decode('ascii')
|
||||
elif isinstance(host, bytes):
|
||||
host = host.decode("ascii")
|
||||
if host is not None and not is_ip_addr(host):
|
||||
qname, addrs = _getaddrinfo_lookup(host, family, flags)
|
||||
else:
|
||||
qname = host
|
||||
addrs = [host]
|
||||
aiflags = (flags | socket.AI_NUMERICHOST) & (0xffff ^ socket.AI_CANONNAME)
|
||||
res = []
|
||||
err = None
|
||||
for addr in addrs:
|
||||
try:
|
||||
ai = socket.getaddrinfo(addr, port, family,
|
||||
type, proto, aiflags)
|
||||
except OSError as e:
|
||||
if flags & socket.AI_ADDRCONFIG:
|
||||
err = e
|
||||
continue
|
||||
raise
|
||||
res.extend(ai)
|
||||
if not res:
|
||||
if err:
|
||||
raise err
|
||||
raise socket.gaierror(socket.EAI_NONAME, 'No address found')
|
||||
if flags & socket.AI_CANONNAME:
|
||||
if not is_ip_addr(qname):
|
||||
qname = resolve_cname(qname).encode('ascii').decode('idna')
|
||||
ai = res[0]
|
||||
res[0] = (ai[0], ai[1], ai[2], qname, ai[4])
|
||||
return res
|
||||
|
||||
|
||||
def gethostbyname(hostname):
|
||||
"""Replacement for Python's socket.gethostbyname"""
|
||||
if is_ipv4_addr(hostname):
|
||||
return hostname
|
||||
rrset = resolve(hostname)
|
||||
return rrset[0].address
|
||||
|
||||
|
||||
def gethostbyname_ex(hostname):
|
||||
"""Replacement for Python's socket.gethostbyname_ex"""
|
||||
if is_ipv4_addr(hostname):
|
||||
return (hostname, [], [hostname])
|
||||
ans = resolve(hostname)
|
||||
aliases = getaliases(hostname)
|
||||
addrs = [rr.address for rr in ans.rrset]
|
||||
qname = str(ans.qname)
|
||||
if qname[-1] == '.':
|
||||
qname = qname[:-1]
|
||||
return (qname, aliases, addrs)
|
||||
|
||||
|
||||
def getnameinfo(sockaddr, flags):
|
||||
"""Replacement for Python's socket.getnameinfo.
|
||||
|
||||
Currently only supports IPv4.
|
||||
"""
|
||||
try:
|
||||
host, port = sockaddr
|
||||
except (ValueError, TypeError):
|
||||
if not isinstance(sockaddr, tuple):
|
||||
del sockaddr # to pass a stdlib test that is
|
||||
# hyper-careful about reference counts
|
||||
raise TypeError('getnameinfo() argument 1 must be a tuple')
|
||||
else:
|
||||
# must be ipv6 sockaddr, pretending we don't know how to resolve it
|
||||
_raise_new_error(EAI_NONAME_ERROR)
|
||||
|
||||
if (flags & socket.NI_NAMEREQD) and (flags & socket.NI_NUMERICHOST):
|
||||
# Conflicting flags. Punt.
|
||||
_raise_new_error(EAI_NONAME_ERROR)
|
||||
|
||||
if is_ipv4_addr(host):
|
||||
try:
|
||||
rrset = resolver.query(
|
||||
dns.reversename.from_address(host), dns.rdatatype.PTR)
|
||||
if len(rrset) > 1:
|
||||
raise OSError('sockaddr resolved to multiple addresses')
|
||||
host = rrset[0].target.to_text(omit_final_dot=True)
|
||||
except dns.exception.Timeout:
|
||||
if flags & socket.NI_NAMEREQD:
|
||||
_raise_new_error(EAI_EAGAIN_ERROR)
|
||||
except dns.exception.DNSException:
|
||||
if flags & socket.NI_NAMEREQD:
|
||||
_raise_new_error(EAI_NONAME_ERROR)
|
||||
else:
|
||||
try:
|
||||
rrset = resolver.query(host)
|
||||
if len(rrset) > 1:
|
||||
raise OSError('sockaddr resolved to multiple addresses')
|
||||
if flags & socket.NI_NUMERICHOST:
|
||||
host = rrset[0].address
|
||||
except dns.exception.Timeout:
|
||||
_raise_new_error(EAI_EAGAIN_ERROR)
|
||||
except dns.exception.DNSException:
|
||||
raise socket.gaierror(
|
||||
(socket.EAI_NODATA, 'No address associated with hostname'))
|
||||
|
||||
if not (flags & socket.NI_NUMERICSERV):
|
||||
proto = (flags & socket.NI_DGRAM) and 'udp' or 'tcp'
|
||||
port = socket.getservbyport(port, proto)
|
||||
|
||||
return (host, port)
|
||||
|
||||
|
||||
def _net_read(sock, count, expiration):
|
||||
"""coro friendly replacement for dns.query._net_read
|
||||
Read the specified number of bytes from sock. Keep trying until we
|
||||
either get the desired amount, or we hit EOF.
|
||||
A Timeout exception will be raised if the operation is not completed
|
||||
by the expiration time.
|
||||
"""
|
||||
s = bytearray()
|
||||
while count > 0:
|
||||
try:
|
||||
n = sock.recv(count)
|
||||
except socket.timeout:
|
||||
# Q: Do we also need to catch coro.CoroutineSocketWake and pass?
|
||||
if expiration - time.time() <= 0.0:
|
||||
raise dns.exception.Timeout
|
||||
eventlet.sleep(0.01)
|
||||
continue
|
||||
if n == b'':
|
||||
raise EOFError
|
||||
count = count - len(n)
|
||||
s += n
|
||||
return s
|
||||
|
||||
|
||||
def _net_write(sock, data, expiration):
|
||||
"""coro friendly replacement for dns.query._net_write
|
||||
Write the specified data to the socket.
|
||||
A Timeout exception will be raised if the operation is not completed
|
||||
by the expiration time.
|
||||
"""
|
||||
current = 0
|
||||
l = len(data)
|
||||
while current < l:
|
||||
try:
|
||||
current += sock.send(data[current:])
|
||||
except socket.timeout:
|
||||
# Q: Do we also need to catch coro.CoroutineSocketWake and pass?
|
||||
if expiration - time.time() <= 0.0:
|
||||
raise dns.exception.Timeout
|
||||
|
||||
|
||||
# Test if raise_on_truncation is an argument we should handle.
|
||||
# It was newly added in dnspython 2.0
|
||||
try:
|
||||
dns.message.from_wire("", raise_on_truncation=True)
|
||||
except dns.message.ShortHeader:
|
||||
_handle_raise_on_truncation = True
|
||||
except TypeError:
|
||||
# Argument error, there is no argument "raise_on_truncation"
|
||||
_handle_raise_on_truncation = False
|
||||
|
||||
|
||||
def udp(q, where, timeout=DNS_QUERY_TIMEOUT, port=53,
|
||||
af=None, source=None, source_port=0, ignore_unexpected=False,
|
||||
one_rr_per_rrset=False, ignore_trailing=False,
|
||||
raise_on_truncation=False, sock=None, ignore_errors=False):
|
||||
"""coro friendly replacement for dns.query.udp
|
||||
Return the response obtained after sending a query via UDP.
|
||||
|
||||
@param q: the query
|
||||
@type q: dns.message.Message
|
||||
@param where: where to send the message
|
||||
@type where: string containing an IPv4 or IPv6 address
|
||||
@param timeout: The number of seconds to wait before the query times out.
|
||||
If None, the default, wait forever.
|
||||
@type timeout: float
|
||||
@param port: The port to which to send the message. The default is 53.
|
||||
@type port: int
|
||||
@param af: the address family to use. The default is None, which
|
||||
causes the address family to use to be inferred from the form of of where.
|
||||
If the inference attempt fails, AF_INET is used.
|
||||
@type af: int
|
||||
@rtype: dns.message.Message object
|
||||
@param source: source address. The default is the IPv4 wildcard address.
|
||||
@type source: string
|
||||
@param source_port: The port from which to send the message.
|
||||
The default is 0.
|
||||
@type source_port: int
|
||||
@param ignore_unexpected: If True, ignore responses from unexpected
|
||||
sources. The default is False.
|
||||
@type ignore_unexpected: bool
|
||||
@param one_rr_per_rrset: If True, put each RR into its own
|
||||
RRset.
|
||||
@type one_rr_per_rrset: bool
|
||||
@param ignore_trailing: If True, ignore trailing
|
||||
junk at end of the received message.
|
||||
@type ignore_trailing: bool
|
||||
@param raise_on_truncation: If True, raise an exception if
|
||||
the TC bit is set.
|
||||
@type raise_on_truncation: bool
|
||||
@param sock: the socket to use for the
|
||||
query. If None, the default, a socket is created. Note that
|
||||
if a socket is provided, it must be a nonblocking datagram socket,
|
||||
and the source and source_port are ignored.
|
||||
@type sock: socket.socket | None
|
||||
@param ignore_errors: if various format errors or response mismatches occur,
|
||||
continue listening.
|
||||
@type ignore_errors: bool"""
|
||||
|
||||
wire = q.to_wire()
|
||||
if af is None:
|
||||
try:
|
||||
af = dns.inet.af_for_address(where)
|
||||
except:
|
||||
af = dns.inet.AF_INET
|
||||
if af == dns.inet.AF_INET:
|
||||
destination = (where, port)
|
||||
if source is not None:
|
||||
source = (source, source_port)
|
||||
elif af == dns.inet.AF_INET6:
|
||||
# Purge any stray zeroes in source address. When doing the tuple comparison
|
||||
# below, we need to always ensure both our target and where we receive replies
|
||||
# from are compared with all zeroes removed so that we don't erroneously fail.
|
||||
# e.g. ('00::1', 53, 0, 0) != ('::1', 53, 0, 0)
|
||||
where_trunc = dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(where))
|
||||
destination = (where_trunc, port, 0, 0)
|
||||
if source is not None:
|
||||
source = (source, source_port, 0, 0)
|
||||
|
||||
if sock:
|
||||
s = sock
|
||||
else:
|
||||
s = socket.socket(af, socket.SOCK_DGRAM)
|
||||
s.settimeout(timeout)
|
||||
try:
|
||||
expiration = compute_expiration(dns.query, timeout)
|
||||
if source is not None:
|
||||
s.bind(source)
|
||||
while True:
|
||||
try:
|
||||
s.sendto(wire, destination)
|
||||
break
|
||||
except socket.timeout:
|
||||
# Q: Do we also need to catch coro.CoroutineSocketWake and pass?
|
||||
if expiration - time.time() <= 0.0:
|
||||
raise dns.exception.Timeout
|
||||
eventlet.sleep(0.01)
|
||||
continue
|
||||
|
||||
tried = False
|
||||
while True:
|
||||
# If we've tried to receive at least once, check to see if our
|
||||
# timer expired
|
||||
if tried and (expiration - time.time() <= 0.0):
|
||||
raise dns.exception.Timeout
|
||||
# Sleep if we are retrying the operation due to a bad source
|
||||
# address or a socket timeout.
|
||||
if tried:
|
||||
eventlet.sleep(0.01)
|
||||
tried = True
|
||||
|
||||
try:
|
||||
(wire, from_address) = s.recvfrom(65535)
|
||||
except socket.timeout:
|
||||
# Q: Do we also need to catch coro.CoroutineSocketWake and pass?
|
||||
continue
|
||||
if dns.inet.af_for_address(from_address[0]) == dns.inet.AF_INET6:
|
||||
# Purge all possible zeroes for ipv6 to match above logic
|
||||
addr = from_address[0]
|
||||
addr = dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(addr))
|
||||
from_address = (addr, from_address[1], from_address[2], from_address[3])
|
||||
if from_address != destination:
|
||||
if ignore_unexpected:
|
||||
continue
|
||||
else:
|
||||
raise dns.query.UnexpectedSource(
|
||||
'got a response from %s instead of %s'
|
||||
% (from_address, destination))
|
||||
try:
|
||||
if _handle_raise_on_truncation:
|
||||
r = dns.message.from_wire(wire,
|
||||
keyring=q.keyring,
|
||||
request_mac=q.mac,
|
||||
one_rr_per_rrset=one_rr_per_rrset,
|
||||
ignore_trailing=ignore_trailing,
|
||||
raise_on_truncation=raise_on_truncation)
|
||||
else:
|
||||
r = dns.message.from_wire(wire,
|
||||
keyring=q.keyring,
|
||||
request_mac=q.mac,
|
||||
one_rr_per_rrset=one_rr_per_rrset,
|
||||
ignore_trailing=ignore_trailing)
|
||||
if not q.is_response(r):
|
||||
raise dns.query.BadResponse()
|
||||
break
|
||||
except dns.message.Truncated as e:
|
||||
if ignore_errors and not q.is_response(e.message()):
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
except Exception:
|
||||
if ignore_errors:
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
finally:
|
||||
s.close()
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def tcp(q, where, timeout=DNS_QUERY_TIMEOUT, port=53,
|
||||
af=None, source=None, source_port=0,
|
||||
one_rr_per_rrset=False, ignore_trailing=False, sock=None):
|
||||
"""coro friendly replacement for dns.query.tcp
|
||||
Return the response obtained after sending a query via TCP.
|
||||
|
||||
@param q: the query
|
||||
@type q: dns.message.Message object
|
||||
@param where: where to send the message
|
||||
@type where: string containing an IPv4 or IPv6 address
|
||||
@param timeout: The number of seconds to wait before the query times out.
|
||||
If None, the default, wait forever.
|
||||
@type timeout: float
|
||||
@param port: The port to which to send the message. The default is 53.
|
||||
@type port: int
|
||||
@param af: the address family to use. The default is None, which
|
||||
causes the address family to use to be inferred from the form of of where.
|
||||
If the inference attempt fails, AF_INET is used.
|
||||
@type af: int
|
||||
@rtype: dns.message.Message object
|
||||
@param source: source address. The default is the IPv4 wildcard address.
|
||||
@type source: string
|
||||
@param source_port: The port from which to send the message.
|
||||
The default is 0.
|
||||
@type source_port: int
|
||||
@type ignore_unexpected: bool
|
||||
@param one_rr_per_rrset: If True, put each RR into its own
|
||||
RRset.
|
||||
@type one_rr_per_rrset: bool
|
||||
@param ignore_trailing: If True, ignore trailing
|
||||
junk at end of the received message.
|
||||
@type ignore_trailing: bool
|
||||
@param sock: the socket to use for the
|
||||
query. If None, the default, a socket is created. Note that
|
||||
if a socket is provided, it must be a nonblocking datagram socket,
|
||||
and the source and source_port are ignored.
|
||||
@type sock: socket.socket | None"""
|
||||
|
||||
wire = q.to_wire()
|
||||
if af is None:
|
||||
try:
|
||||
af = dns.inet.af_for_address(where)
|
||||
except:
|
||||
af = dns.inet.AF_INET
|
||||
if af == dns.inet.AF_INET:
|
||||
destination = (where, port)
|
||||
if source is not None:
|
||||
source = (source, source_port)
|
||||
elif af == dns.inet.AF_INET6:
|
||||
destination = (where, port, 0, 0)
|
||||
if source is not None:
|
||||
source = (source, source_port, 0, 0)
|
||||
if sock:
|
||||
s = sock
|
||||
else:
|
||||
s = socket.socket(af, socket.SOCK_STREAM)
|
||||
s.settimeout(timeout)
|
||||
try:
|
||||
expiration = compute_expiration(dns.query, timeout)
|
||||
if source is not None:
|
||||
s.bind(source)
|
||||
while True:
|
||||
try:
|
||||
s.connect(destination)
|
||||
break
|
||||
except socket.timeout:
|
||||
# Q: Do we also need to catch coro.CoroutineSocketWake and pass?
|
||||
if expiration - time.time() <= 0.0:
|
||||
raise dns.exception.Timeout
|
||||
eventlet.sleep(0.01)
|
||||
continue
|
||||
|
||||
l = len(wire)
|
||||
# copying the wire into tcpmsg is inefficient, but lets us
|
||||
# avoid writev() or doing a short write that would get pushed
|
||||
# onto the net
|
||||
tcpmsg = struct.pack("!H", l) + wire
|
||||
_net_write(s, tcpmsg, expiration)
|
||||
ldata = _net_read(s, 2, expiration)
|
||||
(l,) = struct.unpack("!H", ldata)
|
||||
wire = bytes(_net_read(s, l, expiration))
|
||||
finally:
|
||||
s.close()
|
||||
r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
|
||||
one_rr_per_rrset=one_rr_per_rrset,
|
||||
ignore_trailing=ignore_trailing)
|
||||
if not q.is_response(r):
|
||||
raise dns.query.BadResponse()
|
||||
return r
|
||||
|
||||
|
||||
def reset():
|
||||
resolver.clear()
|
||||
|
||||
|
||||
# Install our coro-friendly replacements for the tcp and udp query methods.
|
||||
dns.query.tcp = tcp
|
||||
dns.query.udp = udp
|
@ -0,0 +1,4 @@
|
||||
import greenlet
|
||||
getcurrent = greenlet.greenlet.getcurrent
|
||||
GreenletExit = greenlet.greenlet.GreenletExit
|
||||
greenlet = greenlet.greenlet
|
@ -0,0 +1,55 @@
|
||||
"""A wait callback to allow psycopg2 cooperation with eventlet.
|
||||
|
||||
Use `make_psycopg_green()` to enable eventlet support in Psycopg.
|
||||
"""
|
||||
|
||||
# Copyright (C) 2010 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||
# and licensed under the MIT license:
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
import psycopg2
|
||||
from psycopg2 import extensions
|
||||
|
||||
import eventlet.hubs
|
||||
|
||||
|
||||
def make_psycopg_green():
|
||||
"""Configure Psycopg to be used with eventlet in non-blocking way."""
|
||||
if not hasattr(extensions, 'set_wait_callback'):
|
||||
raise ImportError(
|
||||
"support for coroutines not available in this Psycopg version (%s)"
|
||||
% psycopg2.__version__)
|
||||
|
||||
extensions.set_wait_callback(eventlet_wait_callback)
|
||||
|
||||
|
||||
def eventlet_wait_callback(conn, timeout=-1):
|
||||
"""A wait callback useful to allow eventlet to work with Psycopg."""
|
||||
while 1:
|
||||
state = conn.poll()
|
||||
if state == extensions.POLL_OK:
|
||||
break
|
||||
elif state == extensions.POLL_READ:
|
||||
eventlet.hubs.trampoline(conn.fileno(), read=True)
|
||||
elif state == extensions.POLL_WRITE:
|
||||
eventlet.hubs.trampoline(conn.fileno(), write=True)
|
||||
else:
|
||||
raise psycopg2.OperationalError(
|
||||
"Bad result from poll: %r" % state)
|
12
lib/python3.13/site-packages/eventlet/support/pylib.py
Normal file
12
lib/python3.13/site-packages/eventlet/support/pylib.py
Normal file
@ -0,0 +1,12 @@
|
||||
from py.magic import greenlet
|
||||
|
||||
import sys
|
||||
import types
|
||||
|
||||
|
||||
def emulate():
|
||||
module = types.ModuleType('greenlet')
|
||||
sys.modules['greenlet'] = module
|
||||
module.greenlet = greenlet
|
||||
module.getcurrent = greenlet.getcurrent
|
||||
module.GreenletExit = greenlet.GreenletExit
|
@ -0,0 +1,12 @@
|
||||
from stackless import greenlet
|
||||
|
||||
import sys
|
||||
import types
|
||||
|
||||
|
||||
def emulate():
|
||||
module = types.ModuleType('greenlet')
|
||||
sys.modules['greenlet'] = module
|
||||
module.greenlet = greenlet
|
||||
module.getcurrent = greenlet.getcurrent
|
||||
module.GreenletExit = greenlet.GreenletExit
|
84
lib/python3.13/site-packages/eventlet/support/stacklesss.py
Normal file
84
lib/python3.13/site-packages/eventlet/support/stacklesss.py
Normal file
@ -0,0 +1,84 @@
|
||||
"""
|
||||
Support for using stackless python. Broken and riddled with print statements
|
||||
at the moment. Please fix it!
|
||||
"""
|
||||
|
||||
import sys
|
||||
import types
|
||||
|
||||
import stackless
|
||||
|
||||
caller = None
|
||||
coro_args = {}
|
||||
tasklet_to_greenlet = {}
|
||||
|
||||
|
||||
def getcurrent():
|
||||
return tasklet_to_greenlet[stackless.getcurrent()]
|
||||
|
||||
|
||||
class FirstSwitch:
|
||||
def __init__(self, gr):
|
||||
self.gr = gr
|
||||
|
||||
def __call__(self, *args, **kw):
|
||||
# print("first call", args, kw)
|
||||
gr = self.gr
|
||||
del gr.switch
|
||||
run, gr.run = gr.run, None
|
||||
t = stackless.tasklet(run)
|
||||
gr.t = t
|
||||
tasklet_to_greenlet[t] = gr
|
||||
t.setup(*args, **kw)
|
||||
t.run()
|
||||
|
||||
|
||||
class greenlet:
|
||||
def __init__(self, run=None, parent=None):
|
||||
self.dead = False
|
||||
if parent is None:
|
||||
parent = getcurrent()
|
||||
|
||||
self.parent = parent
|
||||
if run is not None:
|
||||
self.run = run
|
||||
|
||||
self.switch = FirstSwitch(self)
|
||||
|
||||
def switch(self, *args):
|
||||
# print("switch", args)
|
||||
global caller
|
||||
caller = stackless.getcurrent()
|
||||
coro_args[self] = args
|
||||
self.t.insert()
|
||||
stackless.schedule()
|
||||
if caller is not self.t:
|
||||
caller.remove()
|
||||
rval = coro_args[self]
|
||||
return rval
|
||||
|
||||
def run(self):
|
||||
pass
|
||||
|
||||
def __bool__(self):
|
||||
return self.run is None and not self.dead
|
||||
|
||||
|
||||
class GreenletExit(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def emulate():
|
||||
module = types.ModuleType('greenlet')
|
||||
sys.modules['greenlet'] = module
|
||||
module.greenlet = greenlet
|
||||
module.getcurrent = getcurrent
|
||||
module.GreenletExit = GreenletExit
|
||||
|
||||
caller = stackless.getcurrent()
|
||||
tasklet_to_greenlet[caller] = None
|
||||
main_coro = greenlet()
|
||||
tasklet_to_greenlet[caller] = main_coro
|
||||
main_coro.t = caller
|
||||
del main_coro.switch # It's already running
|
||||
coro_args[main_coro] = None
|
Reference in New Issue
Block a user