mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
Use black with -l 120 param.
This commit is contained in:
1 parent
f928738bd5
commit
c021e21309
194 files changed
+6034
-4564
No files matched your search
@@ -60,10 +60,10 @@ def parse_version(version):
|
||||
"""
|
||||
|
||||
release_type_found = False
|
||||
version_infos = re.split(r'(\.|[a-z]+)', version)
|
||||
version_infos = re.split(r"(\.|[a-z]+)", version)
|
||||
version = []
|
||||
for info in version_infos:
|
||||
if info == '.' or len(info) == 0:
|
||||
if info == "." or len(info) == 0:
|
||||
continue
|
||||
try:
|
||||
info = int(info)
|
||||
@@ -77,8 +77,8 @@ def parse_version(version):
|
||||
if len(version) == 2:
|
||||
version.append("000000")
|
||||
# We want rc to be at lower level than dev version
|
||||
if info == 'rc':
|
||||
info = 'c'
|
||||
if info == "rc":
|
||||
info = "c"
|
||||
version.append(info)
|
||||
release_type_found = True
|
||||
if release_type_found is False:
|
||||
@@ -97,6 +97,6 @@ def shlex_quote(s):
|
||||
"""
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
return s if re.match(r'^[-_\w./]+$', s) else '"%s"' % s.replace('"', '\\"')
|
||||
return s if re.match(r"^[-_\w./]+$", s) else '"%s"' % s.replace('"', '\\"')
|
||||
else:
|
||||
return shlex.quote(s)
|
||||
@@ -17,6 +17,7 @@
|
||||
from gns3server.controller.controller_error import ControllerError
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -43,4 +44,6 @@ def get_next_application_id(projects, computes):
|
||||
application_id = (pool - used).pop()
|
||||
return application_id
|
||||
except KeyError:
|
||||
raise ControllerError("Cannot create a new IOU node (limit of 512 nodes across all opened projects using the same computes)")
|
||||
raise ControllerError(
|
||||
"Cannot create a new IOU node (limit of 512 nodes across all opened projects using the same computes)"
|
||||
)
|
||||
@@ -50,7 +50,7 @@ async def cancellable_wait_run_in_executor(func, *args, **kwargs):
|
||||
:returns: Return the result of the function
|
||||
"""
|
||||
stopped_event = threading.Event()
|
||||
kwargs['stopped_event'] = stopped_event
|
||||
kwargs["stopped_event"] = stopped_event
|
||||
try:
|
||||
await wait_run_in_executor(func, *args, **kwargs)
|
||||
except asyncio.CancelledError:
|
||||
@@ -81,6 +81,7 @@ async def subprocess_check_output(*args, cwd=None, env=None, stderr=False):
|
||||
# and the code of VPCS, dynamips... Will detect it's not the correct binary
|
||||
return output.decode("utf-8", errors="ignore")
|
||||
|
||||
|
||||
async def wait_for_process_termination(process, timeout=10):
|
||||
"""
|
||||
Wait for a process terminate, and raise asyncio.TimeoutError in case of
|
||||
@@ -152,7 +153,6 @@ async def wait_for_named_pipe_creation(pipe_path, timeout=60):
|
||||
|
||||
|
||||
def locking(f):
|
||||
|
||||
@functools.wraps(f)
|
||||
async def wrapper(oself, *args, **kwargs):
|
||||
lock_name = "__" + f.__name__ + "_lock"
|
||||
@@ -160,4 +160,5 @@ def locking(f):
|
||||
setattr(oself, lock_name, asyncio.Lock())
|
||||
async with getattr(oself, lock_name):
|
||||
return await f(oself, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
@@ -32,10 +32,18 @@ import asyncio
|
||||
import aiofiles
|
||||
from concurrent import futures
|
||||
|
||||
from zipfile import (structCentralDir, structEndArchive64, structEndArchive, structEndArchive64Locator,
|
||||
stringCentralDir, stringEndArchive64, stringEndArchive, stringEndArchive64Locator)
|
||||
from zipfile import (
|
||||
structCentralDir,
|
||||
structEndArchive64,
|
||||
structEndArchive,
|
||||
structEndArchive64Locator,
|
||||
stringCentralDir,
|
||||
stringEndArchive64,
|
||||
stringEndArchive,
|
||||
stringEndArchive64Locator,
|
||||
)
|
||||
|
||||
stringDataDescriptor = b'PK\x07\x08' # magic number for data descriptor
|
||||
stringDataDescriptor = b"PK\x07\x08" # magic number for data descriptor
|
||||
|
||||
|
||||
def _get_compressor(compress_type):
|
||||
@@ -45,21 +53,23 @@ def _get_compressor(compress_type):
|
||||
|
||||
if compress_type == zipfile.ZIP_DEFLATED:
|
||||
from zipfile import zlib
|
||||
|
||||
return zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15)
|
||||
elif compress_type == zipfile.ZIP_BZIP2:
|
||||
from zipfile import bz2
|
||||
|
||||
return bz2.BZ2Compressor()
|
||||
elif compress_type == zipfile.ZIP_LZMA:
|
||||
from zipfile import LZMACompressor
|
||||
|
||||
return LZMACompressor()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class PointerIO:
|
||||
|
||||
def __init__(self, mode='wb'):
|
||||
if mode not in ('wb', ):
|
||||
def __init__(self, mode="wb"):
|
||||
if mode not in ("wb",):
|
||||
raise RuntimeError('zipstream.ZipFile() requires mode "wb"')
|
||||
self.data_pointer = 0
|
||||
self.__mode = mode
|
||||
@@ -90,18 +100,17 @@ class PointerIO:
|
||||
|
||||
def write(self, data):
|
||||
if self.closed:
|
||||
raise ValueError('I/O operation on closed file')
|
||||
raise ValueError("I/O operation on closed file")
|
||||
|
||||
if isinstance(data, str):
|
||||
data = data.encode('utf-8')
|
||||
data = data.encode("utf-8")
|
||||
if not isinstance(data, bytes):
|
||||
raise TypeError('expected bytes')
|
||||
raise TypeError("expected bytes")
|
||||
self.data_pointer += len(data)
|
||||
return data
|
||||
|
||||
|
||||
class ZipInfo(zipfile.ZipInfo):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
zipfile.ZipInfo.__init__(self, *args, **kwargs)
|
||||
|
||||
@@ -113,23 +122,22 @@ class ZipInfo(zipfile.ZipInfo):
|
||||
"""
|
||||
|
||||
if self.compress_size > zipfile.ZIP64_LIMIT or self.file_size > zipfile.ZIP64_LIMIT:
|
||||
fmt = b'<4sLQQ'
|
||||
fmt = b"<4sLQQ"
|
||||
else:
|
||||
fmt = b'<4sLLL'
|
||||
fmt = b"<4sLLL"
|
||||
return struct.pack(fmt, stringDataDescriptor, self.CRC, self.compress_size, self.file_size)
|
||||
|
||||
|
||||
class ZipFile(zipfile.ZipFile):
|
||||
|
||||
def __init__(self, fileobj=None, mode='w', compression=zipfile.ZIP_STORED, allowZip64=True, chunksize=32768):
|
||||
def __init__(self, fileobj=None, mode="w", compression=zipfile.ZIP_STORED, allowZip64=True, chunksize=32768):
|
||||
"""Open the ZIP file with mode write "w"."""
|
||||
|
||||
if mode not in ('w', ):
|
||||
if mode not in ("w",):
|
||||
raise RuntimeError('aiozipstream.ZipFile() requires mode "w"')
|
||||
if fileobj is None:
|
||||
fileobj = PointerIO()
|
||||
|
||||
self._comment = b''
|
||||
self._comment = b""
|
||||
zipfile.ZipFile.__init__(self, fileobj, mode=mode, compression=compression, allowZip64=allowZip64)
|
||||
self._chunksize = chunksize
|
||||
self.paths_to_write = []
|
||||
@@ -156,8 +164,8 @@ class ZipFile(zipfile.ZipFile):
|
||||
# check for valid comment length
|
||||
if len(comment) >= zipfile.ZIP_MAX_COMMENT:
|
||||
if self.debug:
|
||||
print('Archive comment is too long; truncating to %d bytes' % zipfile.ZIP_MAX_COMMENT)
|
||||
comment = comment[:zipfile.ZIP_MAX_COMMENT]
|
||||
print("Archive comment is too long; truncating to %d bytes" % zipfile.ZIP_MAX_COMMENT)
|
||||
comment = comment[: zipfile.ZIP_MAX_COMMENT]
|
||||
self._comment = comment
|
||||
self._didModify = True
|
||||
|
||||
@@ -192,7 +200,7 @@ class ZipFile(zipfile.ZipFile):
|
||||
Write a file to the archive under the name `arcname`.
|
||||
"""
|
||||
|
||||
kwargs = {'filename': filename, 'arcname': arcname, 'compress_type': compress_type}
|
||||
kwargs = {"filename": filename, "arcname": arcname, "compress_type": compress_type}
|
||||
self.paths_to_write.append(kwargs)
|
||||
|
||||
def write_iter(self, arcname, iterable, compress_type=None):
|
||||
@@ -200,7 +208,7 @@ class ZipFile(zipfile.ZipFile):
|
||||
Write the bytes iterable `iterable` to the archive under the name `arcname`.
|
||||
"""
|
||||
|
||||
kwargs = {'arcname': arcname, 'iterable': iterable, 'compress_type': compress_type}
|
||||
kwargs = {"arcname": arcname, "iterable": iterable, "compress_type": compress_type}
|
||||
self.paths_to_write.append(kwargs)
|
||||
|
||||
def writestr(self, arcname, data, compress_type=None):
|
||||
@@ -210,6 +218,7 @@ class ZipFile(zipfile.ZipFile):
|
||||
|
||||
def _iterable():
|
||||
yield data
|
||||
|
||||
return self.write_iter(arcname, _iterable(), compress_type=compress_type)
|
||||
|
||||
async def _write(self, filename=None, iterable=None, arcname=None, compress_type=None):
|
||||
@@ -218,8 +227,7 @@ class ZipFile(zipfile.ZipFile):
|
||||
"""
|
||||
|
||||
if not self.fp:
|
||||
raise RuntimeError(
|
||||
"Attempt to write to ZIP archive that was already closed")
|
||||
raise RuntimeError("Attempt to write to ZIP archive that was already closed")
|
||||
if (filename is None and iterable is None) or (filename is not None and iterable is not None):
|
||||
raise ValueError("either (exclusively) filename or iterable shall be not None")
|
||||
|
||||
@@ -237,12 +245,12 @@ class ZipFile(zipfile.ZipFile):
|
||||
while arcname[0] in (os.sep, os.altsep):
|
||||
arcname = arcname[1:]
|
||||
if isdir:
|
||||
arcname += '/'
|
||||
arcname += "/"
|
||||
zinfo = ZipInfo(arcname, date_time)
|
||||
if st:
|
||||
zinfo.external_attr = (st[0] & 0xFFFF) << 16 # Unix attributes
|
||||
zinfo.external_attr = (st[0] & 0xFFFF) << 16 # Unix attributes
|
||||
else:
|
||||
zinfo.external_attr = 0o600 << 16 # ?rw-------
|
||||
zinfo.external_attr = 0o600 << 16 # ?rw-------
|
||||
if compress_type is None:
|
||||
zinfo.compress_type = self.compression
|
||||
else:
|
||||
@@ -253,8 +261,8 @@ class ZipFile(zipfile.ZipFile):
|
||||
else:
|
||||
zinfo.file_size = 0
|
||||
zinfo.flag_bits = 0x00
|
||||
zinfo.flag_bits |= 0x08 # ZIP flag bits, bit 3 indicates presence of data descriptor
|
||||
zinfo.header_offset = self.fp.tell() # Start of header bytes
|
||||
zinfo.flag_bits |= 0x08 # ZIP flag bits, bit 3 indicates presence of data descriptor
|
||||
zinfo.header_offset = self.fp.tell() # Start of header bytes
|
||||
if zinfo.compress_type == zipfile.ZIP_LZMA:
|
||||
# Compressed data includes an end-of-stream (EOS) marker
|
||||
zinfo.flag_bits |= 0x02
|
||||
@@ -284,15 +292,15 @@ class ZipFile(zipfile.ZipFile):
|
||||
if filename:
|
||||
async for buf in self.data_generator(filename):
|
||||
file_size = file_size + len(buf)
|
||||
CRC = zipfile.crc32(buf, CRC) & 0xffffffff
|
||||
CRC = zipfile.crc32(buf, CRC) & 0xFFFFFFFF
|
||||
if cmpr:
|
||||
buf = await self._run_in_executor(cmpr.compress, buf)
|
||||
compress_size = compress_size + len(buf)
|
||||
yield self.fp.write(buf)
|
||||
else: # we have an iterable
|
||||
else: # we have an iterable
|
||||
for buf in iterable:
|
||||
file_size = file_size + len(buf)
|
||||
CRC = zipfile.crc32(buf, CRC) & 0xffffffff
|
||||
CRC = zipfile.crc32(buf, CRC) & 0xFFFFFFFF
|
||||
if cmpr:
|
||||
buf = await self._run_in_executor(cmpr.compress, buf)
|
||||
compress_size = compress_size + len(buf)
|
||||
@@ -309,9 +317,9 @@ class ZipFile(zipfile.ZipFile):
|
||||
zinfo.file_size = file_size
|
||||
if not zip64 and self._allowZip64:
|
||||
if file_size > zipfile.ZIP64_LIMIT:
|
||||
raise RuntimeError('File size has increased during compressing')
|
||||
raise RuntimeError("File size has increased during compressing")
|
||||
if compress_size > zipfile.ZIP64_LIMIT:
|
||||
raise RuntimeError('Compressed size larger than uncompressed size')
|
||||
raise RuntimeError("Compressed size larger than uncompressed size")
|
||||
|
||||
yield self.fp.write(zinfo.DataDescriptor())
|
||||
self.filelist.append(zinfo)
|
||||
@@ -326,10 +334,10 @@ class ZipFile(zipfile.ZipFile):
|
||||
return
|
||||
|
||||
try:
|
||||
if self.mode in ('w', 'a') and self._didModify: # write ending records
|
||||
if self.mode in ("w", "a") and self._didModify: # write ending records
|
||||
count = 0
|
||||
pos1 = self.fp.tell()
|
||||
for zinfo in self.filelist: # write central directory
|
||||
for zinfo in self.filelist: # write central directory
|
||||
count = count + 1
|
||||
dt = zinfo.date_time
|
||||
dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
|
||||
@@ -338,15 +346,15 @@ class ZipFile(zipfile.ZipFile):
|
||||
if zinfo.file_size > zipfile.ZIP64_LIMIT or zinfo.compress_size > zipfile.ZIP64_LIMIT:
|
||||
extra.append(zinfo.file_size)
|
||||
extra.append(zinfo.compress_size)
|
||||
file_size = 0xffffffff
|
||||
compress_size = 0xffffffff
|
||||
file_size = 0xFFFFFFFF
|
||||
compress_size = 0xFFFFFFFF
|
||||
else:
|
||||
file_size = zinfo.file_size
|
||||
compress_size = zinfo.compress_size
|
||||
|
||||
if zinfo.header_offset > zipfile.ZIP64_LIMIT:
|
||||
extra.append(zinfo.header_offset)
|
||||
header_offset = 0xffffffff
|
||||
header_offset = 0xFFFFFFFF
|
||||
else:
|
||||
header_offset = zinfo.header_offset
|
||||
|
||||
@@ -354,9 +362,7 @@ class ZipFile(zipfile.ZipFile):
|
||||
min_version = 0
|
||||
if extra:
|
||||
# Append a ZIP64 field to the extra's
|
||||
extra_data = struct.pack(
|
||||
b'<HH' + b'Q'*len(extra),
|
||||
1, 8*len(extra), *extra) + extra_data
|
||||
extra_data = struct.pack(b"<HH" + b"Q" * len(extra), 1, 8 * len(extra), *extra) + extra_data
|
||||
min_version = zipfile.ZIP64_VERSION
|
||||
|
||||
if zinfo.compress_type == zipfile.ZIP_BZIP2:
|
||||
@@ -368,22 +374,54 @@ class ZipFile(zipfile.ZipFile):
|
||||
create_version = max(min_version, zinfo.create_version)
|
||||
try:
|
||||
filename, flag_bits = zinfo._encodeFilenameFlags()
|
||||
centdir = struct.pack(structCentralDir,
|
||||
stringCentralDir, create_version,
|
||||
zinfo.create_system, extract_version, zinfo.reserved,
|
||||
flag_bits, zinfo.compress_type, dostime, dosdate,
|
||||
zinfo.CRC, compress_size, file_size,
|
||||
len(filename), len(extra_data), len(zinfo.comment),
|
||||
0, zinfo.internal_attr, zinfo.external_attr,
|
||||
header_offset)
|
||||
centdir = struct.pack(
|
||||
structCentralDir,
|
||||
stringCentralDir,
|
||||
create_version,
|
||||
zinfo.create_system,
|
||||
extract_version,
|
||||
zinfo.reserved,
|
||||
flag_bits,
|
||||
zinfo.compress_type,
|
||||
dostime,
|
||||
dosdate,
|
||||
zinfo.CRC,
|
||||
compress_size,
|
||||
file_size,
|
||||
len(filename),
|
||||
len(extra_data),
|
||||
len(zinfo.comment),
|
||||
0,
|
||||
zinfo.internal_attr,
|
||||
zinfo.external_attr,
|
||||
header_offset,
|
||||
)
|
||||
except DeprecationWarning:
|
||||
print((structCentralDir, stringCentralDir, create_version,
|
||||
zinfo.create_system, extract_version, zinfo.reserved,
|
||||
zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
|
||||
zinfo.CRC, compress_size, file_size,
|
||||
len(zinfo.filename), len(extra_data), len(zinfo.comment),
|
||||
0, zinfo.internal_attr, zinfo.external_attr,
|
||||
header_offset), file=sys.stderr)
|
||||
print(
|
||||
(
|
||||
structCentralDir,
|
||||
stringCentralDir,
|
||||
create_version,
|
||||
zinfo.create_system,
|
||||
extract_version,
|
||||
zinfo.reserved,
|
||||
zinfo.flag_bits,
|
||||
zinfo.compress_type,
|
||||
dostime,
|
||||
dosdate,
|
||||
zinfo.CRC,
|
||||
compress_size,
|
||||
file_size,
|
||||
len(zinfo.filename),
|
||||
len(extra_data),
|
||||
len(zinfo.comment),
|
||||
0,
|
||||
zinfo.internal_attr,
|
||||
zinfo.external_attr,
|
||||
header_offset,
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
raise
|
||||
yield self.fp.write(centdir)
|
||||
yield self.fp.write(filename)
|
||||
@@ -395,27 +433,44 @@ class ZipFile(zipfile.ZipFile):
|
||||
centDirCount = count
|
||||
centDirSize = pos2 - pos1
|
||||
centDirOffset = pos1
|
||||
if (centDirCount >= zipfile.ZIP_FILECOUNT_LIMIT or
|
||||
centDirOffset > zipfile.ZIP64_LIMIT or
|
||||
centDirSize > zipfile.ZIP64_LIMIT):
|
||||
if (
|
||||
centDirCount >= zipfile.ZIP_FILECOUNT_LIMIT
|
||||
or centDirOffset > zipfile.ZIP64_LIMIT
|
||||
or centDirSize > zipfile.ZIP64_LIMIT
|
||||
):
|
||||
# Need to write the ZIP64 end-of-archive records
|
||||
zip64endrec = struct.pack(
|
||||
structEndArchive64, stringEndArchive64,
|
||||
44, 45, 45, 0, 0, centDirCount, centDirCount,
|
||||
centDirSize, centDirOffset)
|
||||
structEndArchive64,
|
||||
stringEndArchive64,
|
||||
44,
|
||||
45,
|
||||
45,
|
||||
0,
|
||||
0,
|
||||
centDirCount,
|
||||
centDirCount,
|
||||
centDirSize,
|
||||
centDirOffset,
|
||||
)
|
||||
yield self.fp.write(zip64endrec)
|
||||
|
||||
zip64locrec = struct.pack(
|
||||
structEndArchive64Locator,
|
||||
stringEndArchive64Locator, 0, pos2, 1)
|
||||
zip64locrec = struct.pack(structEndArchive64Locator, stringEndArchive64Locator, 0, pos2, 1)
|
||||
yield self.fp.write(zip64locrec)
|
||||
centDirCount = min(centDirCount, 0xFFFF)
|
||||
centDirSize = min(centDirSize, 0xFFFFFFFF)
|
||||
centDirOffset = min(centDirOffset, 0xFFFFFFFF)
|
||||
|
||||
endrec = struct.pack(structEndArchive, stringEndArchive,
|
||||
0, 0, centDirCount, centDirCount,
|
||||
centDirSize, centDirOffset, len(self._comment))
|
||||
endrec = struct.pack(
|
||||
structEndArchive,
|
||||
stringEndArchive,
|
||||
0,
|
||||
0,
|
||||
centDirCount,
|
||||
centDirCount,
|
||||
centDirSize,
|
||||
centDirOffset,
|
||||
len(self._comment),
|
||||
)
|
||||
yield self.fp.write(endrec)
|
||||
yield self.fp.write(self._comment)
|
||||
self.fp.flush()
|
||||
|
||||
@@ -46,7 +46,7 @@ class EmbedShell:
|
||||
self._loop = loop
|
||||
self._reader = reader
|
||||
self._writer = writer
|
||||
self._prompt = '> '
|
||||
self._prompt = "> "
|
||||
self._welcome_message = welcome_message
|
||||
|
||||
@property
|
||||
@@ -85,30 +85,30 @@ class EmbedShell:
|
||||
"""
|
||||
Show help
|
||||
"""
|
||||
res = ''
|
||||
res = ""
|
||||
if len(args) == 0:
|
||||
res = 'Help:\n'
|
||||
res = "Help:\n"
|
||||
for name, value in inspect.getmembers(self):
|
||||
if not inspect.isgeneratorfunction(value):
|
||||
continue
|
||||
if name.startswith('_') or (len(args) and name != args[0]) or name == 'run':
|
||||
if name.startswith("_") or (len(args) and name != args[0]) or name == "run":
|
||||
continue
|
||||
doc = inspect.getdoc(value)
|
||||
res += name
|
||||
if len(args) and doc:
|
||||
res += ': ' + doc
|
||||
res += ": " + doc
|
||||
elif doc:
|
||||
res += ': ' + doc.split('\n')[0]
|
||||
res += '\n'
|
||||
res += ": " + doc.split("\n")[0]
|
||||
res += "\n"
|
||||
if len(args) == 0:
|
||||
res += '\nhelp command for details about a command\n'
|
||||
res += "\nhelp command for details about a command\n"
|
||||
return res
|
||||
|
||||
async def _parse_command(self, text):
|
||||
cmd = text.split(' ')
|
||||
cmd = text.split(" ")
|
||||
found = False
|
||||
if cmd[0] == '?':
|
||||
cmd[0] = 'help'
|
||||
if cmd[0] == "?":
|
||||
cmd[0] = "help"
|
||||
|
||||
# when there is no command specified just return empty result
|
||||
if not cmd[0].strip():
|
||||
@@ -121,7 +121,7 @@ class EmbedShell:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
res = (f'Command not found {cmd[0]}\n' + (await self.help()))
|
||||
res = f"Command not found {cmd[0]}\n" + (await self.help())
|
||||
return res
|
||||
|
||||
async def run(self):
|
||||
@@ -130,7 +130,7 @@ class EmbedShell:
|
||||
while True:
|
||||
self._writer.feed_data(self._prompt.encode())
|
||||
result = await self._reader.readline()
|
||||
result = result.decode().strip('\n')
|
||||
result = result.decode().strip("\n")
|
||||
res = await self._parse_command(result)
|
||||
self._writer.feed_data(res.encode())
|
||||
|
||||
@@ -143,7 +143,7 @@ class EmbedShell:
|
||||
for name, value in inspect.getmembers(self):
|
||||
if not inspect.isgeneratorfunction(value):
|
||||
continue
|
||||
if name.startswith('_') or name == 'run':
|
||||
if name.startswith("_") or name == "run":
|
||||
continue
|
||||
doc = inspect.getdoc(value)
|
||||
commands.append((name, doc))
|
||||
@@ -156,22 +156,23 @@ class PatchedStdinInput(StdinInput):
|
||||
Fixes issue when PyCharm runs own terminal without emulation.
|
||||
https://github.com/GNS3/gns3-server/issues/1172
|
||||
"""
|
||||
|
||||
def __init__(self, stdin=None):
|
||||
self.stdin = stdin or sys.stdin
|
||||
try:
|
||||
self.stdin.fileno()
|
||||
except io.UnsupportedOperation:
|
||||
if 'idlelib.run' in sys.modules:
|
||||
raise io.UnsupportedOperation(
|
||||
'Stdin is not a terminal. Running from Idle is not supported.')
|
||||
if "idlelib.run" in sys.modules:
|
||||
raise io.UnsupportedOperation("Stdin is not a terminal. Running from Idle is not supported.")
|
||||
else:
|
||||
raise io.UnsupportedOperation('Stdin is not a terminal.')
|
||||
raise io.UnsupportedOperation("Stdin is not a terminal.")
|
||||
|
||||
|
||||
class UnstoppableEventLoop(EventLoop):
|
||||
"""
|
||||
Partially fake event loop which cannot be stopped by CommandLineInterface
|
||||
"""
|
||||
|
||||
def __init__(self, loop):
|
||||
self._loop = loop
|
||||
|
||||
@@ -202,13 +203,13 @@ class ShellConnection(TelnetConnection):
|
||||
self._cli = None
|
||||
self._cb = None
|
||||
self._size = Size(rows=40, columns=79)
|
||||
self.encoding = 'utf-8'
|
||||
|
||||
self.encoding = "utf-8"
|
||||
|
||||
async def connected(self):
|
||||
# prompt_toolkit internally checks if it's on windows during output rendering but
|
||||
# we need to force that we use Vt100_Output not Win32_Output
|
||||
from prompt_toolkit import renderer
|
||||
|
||||
renderer.is_windows = lambda: False
|
||||
|
||||
def get_size():
|
||||
@@ -218,7 +219,8 @@ class ShellConnection(TelnetConnection):
|
||||
application=create_prompt_application(self._shell.prompt),
|
||||
eventloop=UnstoppableEventLoop(create_asyncio_eventloop(self._loop)),
|
||||
input=PatchedStdinInput(sys.stdin),
|
||||
output=Vt100_Output(self, get_size))
|
||||
output=Vt100_Output(self, get_size),
|
||||
)
|
||||
|
||||
self._cb = self._cli.create_eventloop_callbacks()
|
||||
self._inputstream = InputStream(self._cb.feed_key)
|
||||
@@ -302,19 +304,21 @@ def create_stdin_shell(shell, loop=None):
|
||||
:param loop: The event loop
|
||||
:returns: Telnet server
|
||||
"""
|
||||
|
||||
async def feed_stdin(loop, reader, shell):
|
||||
history = InMemoryHistory()
|
||||
completer = WordCompleter([name for name, _ in shell.get_commands()], ignore_case=True)
|
||||
while True:
|
||||
line = await prompt(
|
||||
">", patch_stdout=True, return_asyncio_coroutine=True, history=history, completer=completer)
|
||||
line += '\n'
|
||||
">", patch_stdout=True, return_asyncio_coroutine=True, history=history, completer=completer
|
||||
)
|
||||
line += "\n"
|
||||
reader.feed_data(line.encode())
|
||||
|
||||
async def read_stdout(writer):
|
||||
while True:
|
||||
c = await writer.read(1)
|
||||
print(c.decode(), end='')
|
||||
print(c.decode(), end="")
|
||||
sys.stdout.flush()
|
||||
|
||||
reader = asyncio.StreamReader()
|
||||
@@ -329,30 +333,31 @@ def create_stdin_shell(shell, loop=None):
|
||||
shell_task = loop.create_task(shell.run())
|
||||
return asyncio.gather(shell_task, writer_task, reader_task)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
class Demo(EmbedShell):
|
||||
|
||||
async def hello(self, *args):
|
||||
"""
|
||||
Hello world
|
||||
|
||||
This command accept arguments: hello tutu will display tutu
|
||||
"""
|
||||
|
||||
async def world():
|
||||
await asyncio.sleep(2)
|
||||
if len(args):
|
||||
return ' '.join(args)
|
||||
return " ".join(args)
|
||||
else:
|
||||
return 'world\n'
|
||||
return "world\n"
|
||||
|
||||
return await world()
|
||||
|
||||
# Demo using telnet
|
||||
shell = Demo(welcome_message="Welcome!\n")
|
||||
server = create_telnet_shell(shell, loop=loop)
|
||||
coro = asyncio.start_server(server.run, '127.0.0.1', 4444, loop=loop)
|
||||
coro = asyncio.start_server(server.run, "127.0.0.1", 4444, loop=loop)
|
||||
s = loop.run_until_complete(coro)
|
||||
try:
|
||||
loop.run_forever()
|
||||
|
||||
@@ -19,160 +19,150 @@ from prompt_toolkit.key_binding.input_processor import KeyPress
|
||||
# )
|
||||
|
||||
_DEBUG_RENDERER_INPUT = False
|
||||
_DEBUG_RENDERER_INPUT_FILENAME = 'prompt-toolkit-render-input.log'
|
||||
_DEBUG_RENDERER_INPUT_FILENAME = "prompt-toolkit-render-input.log"
|
||||
|
||||
|
||||
# Regex matching any CPR response
|
||||
# (Note that we use '\Z' instead of '$', because '$' could include a trailing
|
||||
# newline.)
|
||||
_cpr_response_re = re.compile('^' + re.escape('\x1b[') + r'\d+;\d+R\Z')
|
||||
_cpr_response_re = re.compile("^" + re.escape("\x1b[") + r"\d+;\d+R\Z")
|
||||
|
||||
# Mouse events:
|
||||
# Typical: "Esc[MaB*" Urxvt: "Esc[96;14;13M" and for Xterm SGR: "Esc[<64;85;12M"
|
||||
_mouse_event_re = re.compile('^' + re.escape('\x1b[') + r'(<?[\d;]+[mM]|M...)\Z')
|
||||
_mouse_event_re = re.compile("^" + re.escape("\x1b[") + r"(<?[\d;]+[mM]|M...)\Z")
|
||||
|
||||
# Regex matching any valid prefix of a CPR response.
|
||||
# (Note that it doesn't contain the last character, the 'R'. The prefix has to
|
||||
# be shorter.)
|
||||
_cpr_response_prefix_re = re.compile('^' + re.escape('\x1b[') + r'[\d;]*\Z')
|
||||
_cpr_response_prefix_re = re.compile("^" + re.escape("\x1b[") + r"[\d;]*\Z")
|
||||
|
||||
_mouse_event_prefix_re = re.compile('^' + re.escape('\x1b[') + r'(<?[\d;]*|M.{0,2})\Z')
|
||||
_mouse_event_prefix_re = re.compile("^" + re.escape("\x1b[") + r"(<?[\d;]*|M.{0,2})\Z")
|
||||
|
||||
|
||||
class _Flush:
|
||||
""" Helper object to indicate flush operation to the parser. """
|
||||
|
||||
pass
|
||||
|
||||
|
||||
# Mapping of vt100 escape codes to Keys.
|
||||
ANSI_SEQUENCES = {
|
||||
'\x1b': Keys.Escape,
|
||||
|
||||
'\x00': Keys.ControlSpace, # Control-Space (Also for Ctrl-@)
|
||||
'\x01': Keys.ControlA, # Control-A (home)
|
||||
'\x02': Keys.ControlB, # Control-B (emacs cursor left)
|
||||
'\x03': Keys.ControlC, # Control-C (interrupt)
|
||||
'\x04': Keys.ControlD, # Control-D (exit)
|
||||
'\x05': Keys.ControlE, # Contrel-E (end)
|
||||
'\x06': Keys.ControlF, # Control-F (cursor forward)
|
||||
'\x07': Keys.ControlG, # Control-G
|
||||
'\x08': Keys.ControlH, # Control-H (8) (Identical to '\b')
|
||||
'\x09': Keys.ControlI, # Control-I (9) (Identical to '\t')
|
||||
'\x0a': Keys.ControlJ, # Control-J (10) (Identical to '\n')
|
||||
'\x0b': Keys.ControlK, # Control-K (delete until end of line; vertical tab)
|
||||
'\x0c': Keys.ControlL, # Control-L (clear; form feed)
|
||||
'\x0d': Keys.ControlM, # Control-M (13) (Identical to '\r')
|
||||
'\x0e': Keys.ControlN, # Control-N (14) (history forward)
|
||||
'\x0f': Keys.ControlO, # Control-O (15)
|
||||
'\x10': Keys.ControlP, # Control-P (16) (history back)
|
||||
'\x11': Keys.ControlQ, # Control-Q
|
||||
'\x12': Keys.ControlR, # Control-R (18) (reverse search)
|
||||
'\x13': Keys.ControlS, # Control-S (19) (forward search)
|
||||
'\x14': Keys.ControlT, # Control-T
|
||||
'\x15': Keys.ControlU, # Control-U
|
||||
'\x16': Keys.ControlV, # Control-V
|
||||
'\x17': Keys.ControlW, # Control-W
|
||||
'\x18': Keys.ControlX, # Control-X
|
||||
'\x19': Keys.ControlY, # Control-Y (25)
|
||||
'\x1a': Keys.ControlZ, # Control-Z
|
||||
|
||||
'\x1c': Keys.ControlBackslash, # Both Control-\ and Ctrl-|
|
||||
'\x1d': Keys.ControlSquareClose, # Control-]
|
||||
'\x1e': Keys.ControlCircumflex, # Control-^
|
||||
'\x1f': Keys.ControlUnderscore, # Control-underscore (Also for Ctrl-hypen.)
|
||||
'\x7f': Keys.Backspace, # (127) Backspace
|
||||
'\x1b[A': Keys.Up,
|
||||
'\x1b[B': Keys.Down,
|
||||
'\x1b[C': Keys.Right,
|
||||
'\x1b[D': Keys.Left,
|
||||
'\x1b[H': Keys.Home,
|
||||
'\x1bOH': Keys.Home,
|
||||
'\x1b[F': Keys.End,
|
||||
'\x1bOF': Keys.End,
|
||||
'\x1b[3~': Keys.Delete,
|
||||
'\x1b[3;2~': Keys.ShiftDelete, # xterm, gnome-terminal.
|
||||
'\x1b[3;5~': Keys.ControlDelete, # xterm, gnome-terminal.
|
||||
'\x1b[1~': Keys.Home, # tmux
|
||||
'\x1b[4~': Keys.End, # tmux
|
||||
'\x1b[5~': Keys.PageUp,
|
||||
'\x1b[6~': Keys.PageDown,
|
||||
'\x1b[7~': Keys.Home, # xrvt
|
||||
'\x1b[8~': Keys.End, # xrvt
|
||||
'\x1b[Z': Keys.BackTab, # shift + tab
|
||||
'\x1b[2~': Keys.Insert,
|
||||
|
||||
'\x1bOP': Keys.F1,
|
||||
'\x1bOQ': Keys.F2,
|
||||
'\x1bOR': Keys.F3,
|
||||
'\x1bOS': Keys.F4,
|
||||
'\x1b[[A': Keys.F1, # Linux console.
|
||||
'\x1b[[B': Keys.F2, # Linux console.
|
||||
'\x1b[[C': Keys.F3, # Linux console.
|
||||
'\x1b[[D': Keys.F4, # Linux console.
|
||||
'\x1b[[E': Keys.F5, # Linux console.
|
||||
'\x1b[11~': Keys.F1, # rxvt-unicode
|
||||
'\x1b[12~': Keys.F2, # rxvt-unicode
|
||||
'\x1b[13~': Keys.F3, # rxvt-unicode
|
||||
'\x1b[14~': Keys.F4, # rxvt-unicode
|
||||
'\x1b[15~': Keys.F5,
|
||||
'\x1b[17~': Keys.F6,
|
||||
'\x1b[18~': Keys.F7,
|
||||
'\x1b[19~': Keys.F8,
|
||||
'\x1b[20~': Keys.F9,
|
||||
'\x1b[21~': Keys.F10,
|
||||
'\x1b[23~': Keys.F11,
|
||||
'\x1b[24~': Keys.F12,
|
||||
'\x1b[25~': Keys.F13,
|
||||
'\x1b[26~': Keys.F14,
|
||||
'\x1b[28~': Keys.F15,
|
||||
'\x1b[29~': Keys.F16,
|
||||
'\x1b[31~': Keys.F17,
|
||||
'\x1b[32~': Keys.F18,
|
||||
'\x1b[33~': Keys.F19,
|
||||
'\x1b[34~': Keys.F20,
|
||||
|
||||
"\x1b": Keys.Escape,
|
||||
"\x00": Keys.ControlSpace, # Control-Space (Also for Ctrl-@)
|
||||
"\x01": Keys.ControlA, # Control-A (home)
|
||||
"\x02": Keys.ControlB, # Control-B (emacs cursor left)
|
||||
"\x03": Keys.ControlC, # Control-C (interrupt)
|
||||
"\x04": Keys.ControlD, # Control-D (exit)
|
||||
"\x05": Keys.ControlE, # Contrel-E (end)
|
||||
"\x06": Keys.ControlF, # Control-F (cursor forward)
|
||||
"\x07": Keys.ControlG, # Control-G
|
||||
"\x08": Keys.ControlH, # Control-H (8) (Identical to '\b')
|
||||
"\x09": Keys.ControlI, # Control-I (9) (Identical to '\t')
|
||||
"\x0a": Keys.ControlJ, # Control-J (10) (Identical to '\n')
|
||||
"\x0b": Keys.ControlK, # Control-K (delete until end of line; vertical tab)
|
||||
"\x0c": Keys.ControlL, # Control-L (clear; form feed)
|
||||
"\x0d": Keys.ControlM, # Control-M (13) (Identical to '\r')
|
||||
"\x0e": Keys.ControlN, # Control-N (14) (history forward)
|
||||
"\x0f": Keys.ControlO, # Control-O (15)
|
||||
"\x10": Keys.ControlP, # Control-P (16) (history back)
|
||||
"\x11": Keys.ControlQ, # Control-Q
|
||||
"\x12": Keys.ControlR, # Control-R (18) (reverse search)
|
||||
"\x13": Keys.ControlS, # Control-S (19) (forward search)
|
||||
"\x14": Keys.ControlT, # Control-T
|
||||
"\x15": Keys.ControlU, # Control-U
|
||||
"\x16": Keys.ControlV, # Control-V
|
||||
"\x17": Keys.ControlW, # Control-W
|
||||
"\x18": Keys.ControlX, # Control-X
|
||||
"\x19": Keys.ControlY, # Control-Y (25)
|
||||
"\x1a": Keys.ControlZ, # Control-Z
|
||||
"\x1c": Keys.ControlBackslash, # Both Control-\ and Ctrl-|
|
||||
"\x1d": Keys.ControlSquareClose, # Control-]
|
||||
"\x1e": Keys.ControlCircumflex, # Control-^
|
||||
"\x1f": Keys.ControlUnderscore, # Control-underscore (Also for Ctrl-hypen.)
|
||||
"\x7f": Keys.Backspace, # (127) Backspace
|
||||
"\x1b[A": Keys.Up,
|
||||
"\x1b[B": Keys.Down,
|
||||
"\x1b[C": Keys.Right,
|
||||
"\x1b[D": Keys.Left,
|
||||
"\x1b[H": Keys.Home,
|
||||
"\x1bOH": Keys.Home,
|
||||
"\x1b[F": Keys.End,
|
||||
"\x1bOF": Keys.End,
|
||||
"\x1b[3~": Keys.Delete,
|
||||
"\x1b[3;2~": Keys.ShiftDelete, # xterm, gnome-terminal.
|
||||
"\x1b[3;5~": Keys.ControlDelete, # xterm, gnome-terminal.
|
||||
"\x1b[1~": Keys.Home, # tmux
|
||||
"\x1b[4~": Keys.End, # tmux
|
||||
"\x1b[5~": Keys.PageUp,
|
||||
"\x1b[6~": Keys.PageDown,
|
||||
"\x1b[7~": Keys.Home, # xrvt
|
||||
"\x1b[8~": Keys.End, # xrvt
|
||||
"\x1b[Z": Keys.BackTab, # shift + tab
|
||||
"\x1b[2~": Keys.Insert,
|
||||
"\x1bOP": Keys.F1,
|
||||
"\x1bOQ": Keys.F2,
|
||||
"\x1bOR": Keys.F3,
|
||||
"\x1bOS": Keys.F4,
|
||||
"\x1b[[A": Keys.F1, # Linux console.
|
||||
"\x1b[[B": Keys.F2, # Linux console.
|
||||
"\x1b[[C": Keys.F3, # Linux console.
|
||||
"\x1b[[D": Keys.F4, # Linux console.
|
||||
"\x1b[[E": Keys.F5, # Linux console.
|
||||
"\x1b[11~": Keys.F1, # rxvt-unicode
|
||||
"\x1b[12~": Keys.F2, # rxvt-unicode
|
||||
"\x1b[13~": Keys.F3, # rxvt-unicode
|
||||
"\x1b[14~": Keys.F4, # rxvt-unicode
|
||||
"\x1b[15~": Keys.F5,
|
||||
"\x1b[17~": Keys.F6,
|
||||
"\x1b[18~": Keys.F7,
|
||||
"\x1b[19~": Keys.F8,
|
||||
"\x1b[20~": Keys.F9,
|
||||
"\x1b[21~": Keys.F10,
|
||||
"\x1b[23~": Keys.F11,
|
||||
"\x1b[24~": Keys.F12,
|
||||
"\x1b[25~": Keys.F13,
|
||||
"\x1b[26~": Keys.F14,
|
||||
"\x1b[28~": Keys.F15,
|
||||
"\x1b[29~": Keys.F16,
|
||||
"\x1b[31~": Keys.F17,
|
||||
"\x1b[32~": Keys.F18,
|
||||
"\x1b[33~": Keys.F19,
|
||||
"\x1b[34~": Keys.F20,
|
||||
# Xterm
|
||||
'\x1b[1;2P': Keys.F13,
|
||||
'\x1b[1;2Q': Keys.F14,
|
||||
"\x1b[1;2P": Keys.F13,
|
||||
"\x1b[1;2Q": Keys.F14,
|
||||
# '\x1b[1;2R': Keys.F15, # Conflicts with CPR response.
|
||||
'\x1b[1;2S': Keys.F16,
|
||||
'\x1b[15;2~': Keys.F17,
|
||||
'\x1b[17;2~': Keys.F18,
|
||||
'\x1b[18;2~': Keys.F19,
|
||||
'\x1b[19;2~': Keys.F20,
|
||||
'\x1b[20;2~': Keys.F21,
|
||||
'\x1b[21;2~': Keys.F22,
|
||||
'\x1b[23;2~': Keys.F23,
|
||||
'\x1b[24;2~': Keys.F24,
|
||||
|
||||
'\x1b[1;5A': Keys.ControlUp, # Cursor Mode
|
||||
'\x1b[1;5B': Keys.ControlDown, # Cursor Mode
|
||||
'\x1b[1;5C': Keys.ControlRight, # Cursor Mode
|
||||
'\x1b[1;5D': Keys.ControlLeft, # Cursor Mode
|
||||
|
||||
'\x1b[1;2A': Keys.ShiftUp,
|
||||
'\x1b[1;2B': Keys.ShiftDown,
|
||||
'\x1b[1;2C': Keys.ShiftRight,
|
||||
'\x1b[1;2D': Keys.ShiftLeft,
|
||||
|
||||
"\x1b[1;2S": Keys.F16,
|
||||
"\x1b[15;2~": Keys.F17,
|
||||
"\x1b[17;2~": Keys.F18,
|
||||
"\x1b[18;2~": Keys.F19,
|
||||
"\x1b[19;2~": Keys.F20,
|
||||
"\x1b[20;2~": Keys.F21,
|
||||
"\x1b[21;2~": Keys.F22,
|
||||
"\x1b[23;2~": Keys.F23,
|
||||
"\x1b[24;2~": Keys.F24,
|
||||
"\x1b[1;5A": Keys.ControlUp, # Cursor Mode
|
||||
"\x1b[1;5B": Keys.ControlDown, # Cursor Mode
|
||||
"\x1b[1;5C": Keys.ControlRight, # Cursor Mode
|
||||
"\x1b[1;5D": Keys.ControlLeft, # Cursor Mode
|
||||
"\x1b[1;2A": Keys.ShiftUp,
|
||||
"\x1b[1;2B": Keys.ShiftDown,
|
||||
"\x1b[1;2C": Keys.ShiftRight,
|
||||
"\x1b[1;2D": Keys.ShiftLeft,
|
||||
# Tmux sends following keystrokes when control+arrow is pressed, but for
|
||||
# Emacs ansi-term sends the same sequences for normal arrow keys. Consider
|
||||
# it a normal arrow press, because that's more important.
|
||||
'\x1bOA': Keys.Up,
|
||||
'\x1bOB': Keys.Down,
|
||||
'\x1bOC': Keys.Right,
|
||||
'\x1bOD': Keys.Left,
|
||||
|
||||
'\x1b[5A': Keys.ControlUp,
|
||||
'\x1b[5B': Keys.ControlDown,
|
||||
'\x1b[5C': Keys.ControlRight,
|
||||
'\x1b[5D': Keys.ControlLeft,
|
||||
|
||||
'\x1bOc': Keys.ControlRight, # rxvt
|
||||
'\x1bOd': Keys.ControlLeft, # rxvt
|
||||
|
||||
'\x1b[200~': Keys.BracketedPaste, # Start of bracketed paste.
|
||||
|
||||
"\x1bOA": Keys.Up,
|
||||
"\x1bOB": Keys.Down,
|
||||
"\x1bOC": Keys.Right,
|
||||
"\x1bOD": Keys.Left,
|
||||
"\x1b[5A": Keys.ControlUp,
|
||||
"\x1b[5B": Keys.ControlDown,
|
||||
"\x1b[5C": Keys.ControlRight,
|
||||
"\x1b[5D": Keys.ControlLeft,
|
||||
"\x1bOc": Keys.ControlRight, # rxvt
|
||||
"\x1bOd": Keys.ControlLeft, # rxvt
|
||||
"\x1b[200~": Keys.BracketedPaste, # Start of bracketed paste.
|
||||
# Meta + arrow keys. Several terminals handle this differently.
|
||||
# The following sequences are for xterm and gnome-terminal.
|
||||
# (Iterm sends ESC followed by the normal arrow_up/down/left/right
|
||||
@@ -182,15 +172,14 @@ ANSI_SEQUENCES = {
|
||||
# pressing ESC (to go to Vi navigation mode), followed by just the
|
||||
# 'b' or 'f' key. These combinations are handled in
|
||||
# the input processor.)
|
||||
'\x1b[1;3D': (Keys.Escape, Keys.Left),
|
||||
'\x1b[1;3C': (Keys.Escape, Keys.Right),
|
||||
'\x1b[1;3A': (Keys.Escape, Keys.Up),
|
||||
'\x1b[1;3B': (Keys.Escape, Keys.Down),
|
||||
|
||||
"\x1b[1;3D": (Keys.Escape, Keys.Left),
|
||||
"\x1b[1;3C": (Keys.Escape, Keys.Right),
|
||||
"\x1b[1;3A": (Keys.Escape, Keys.Up),
|
||||
"\x1b[1;3B": (Keys.Escape, Keys.Down),
|
||||
# Sequences generated by numpad 5. Not sure what it means. (It doesn't
|
||||
# appear in 'infocmp'. Just ignore.
|
||||
'\x1b[E': Keys.Ignore, # Xterm.
|
||||
'\x1b[G': Keys.Ignore, # Linux console.
|
||||
"\x1b[E": Keys.Ignore, # Xterm.
|
||||
"\x1b[G": Keys.Ignore, # Linux console.
|
||||
}
|
||||
|
||||
|
||||
@@ -199,10 +188,11 @@ class _IsPrefixOfLongerMatchCache(dict):
|
||||
Dictiory that maps input sequences to a boolean indicating whether there is
|
||||
any key that start with this characters.
|
||||
"""
|
||||
|
||||
def __missing__(self, prefix):
|
||||
# (hard coded) If this could be a prefix of a CPR response, return
|
||||
# True.
|
||||
if (_cpr_response_prefix_re.match(prefix) or _mouse_event_prefix_re.match(prefix)):
|
||||
if _cpr_response_prefix_re.match(prefix) or _mouse_event_prefix_re.match(prefix):
|
||||
result = True
|
||||
else:
|
||||
# If this could be a prefix of anything else, also return True.
|
||||
@@ -231,6 +221,7 @@ class InputStream:
|
||||
|
||||
:attr input_processor: :class:`~prompt_toolkit.key_binding.InputProcessor` instance.
|
||||
"""
|
||||
|
||||
# Lookup table of ANSI escape sequences for a VT100 terminal
|
||||
# Hint: in order to know what sequences your terminal writes to stdin, run
|
||||
# "od -c" and start typing.
|
||||
@@ -241,7 +232,7 @@ class InputStream:
|
||||
self.reset()
|
||||
|
||||
if _DEBUG_RENDERER_INPUT:
|
||||
self.LOG = open(_DEBUG_RENDERER_INPUT_FILENAME, 'ab')
|
||||
self.LOG = open(_DEBUG_RENDERER_INPUT_FILENAME, "ab")
|
||||
|
||||
def reset(self, request=False):
|
||||
self._in_bracketed_paste = False
|
||||
@@ -277,7 +268,7 @@ class InputStream:
|
||||
"""
|
||||
Coroutine (state machine) for the input parser.
|
||||
"""
|
||||
prefix = ''
|
||||
prefix = ""
|
||||
retry = False
|
||||
flush = False
|
||||
|
||||
@@ -303,7 +294,7 @@ class InputStream:
|
||||
# Exact matches found, call handlers..
|
||||
if (flush or not is_prefix_of_longer_match) and match:
|
||||
self._call_handler(match, prefix)
|
||||
prefix = ''
|
||||
prefix = ""
|
||||
|
||||
# No exact match found.
|
||||
elif (flush or not is_prefix_of_longer_match) and not match:
|
||||
@@ -313,7 +304,7 @@ class InputStream:
|
||||
# Loop over the input, try the longest match first and
|
||||
# shift.
|
||||
for i in range(len(prefix), 0, -1):
|
||||
match= self._get_match(prefix[:i])
|
||||
match = self._get_match(prefix[:i])
|
||||
if match:
|
||||
self._call_handler(match, prefix[:i])
|
||||
prefix = prefix[i:]
|
||||
@@ -333,7 +324,7 @@ class InputStream:
|
||||
else:
|
||||
if key == Keys.BracketedPaste:
|
||||
self._in_bracketed_paste = True
|
||||
self._paste_buffer = ''
|
||||
self._paste_buffer = ""
|
||||
else:
|
||||
self.feed_key_callback(KeyPress(key, insert_text))
|
||||
|
||||
@@ -346,7 +337,7 @@ class InputStream:
|
||||
assert isinstance(data, str)
|
||||
|
||||
if _DEBUG_RENDERER_INPUT:
|
||||
self.LOG.write(repr(data).encode('utf-8') + b'\n')
|
||||
self.LOG.write(repr(data).encode("utf-8") + b"\n")
|
||||
self.LOG.flush()
|
||||
|
||||
# Handle bracketed paste. (We bypass the parser that matches all other
|
||||
@@ -354,7 +345,7 @@ class InputStream:
|
||||
# This is much faster then parsing character by character.
|
||||
if self._in_bracketed_paste:
|
||||
self._paste_buffer += data
|
||||
end_mark = '\x1b[201~'
|
||||
end_mark = "\x1b[201~"
|
||||
|
||||
if end_mark in self._paste_buffer:
|
||||
end_index = self._paste_buffer.index(end_mark)
|
||||
@@ -365,8 +356,8 @@ class InputStream:
|
||||
|
||||
# Quit bracketed paste mode and handle remaining input.
|
||||
self._in_bracketed_paste = False
|
||||
remaining = self._paste_buffer[end_index + len(end_mark):]
|
||||
self._paste_buffer = ''
|
||||
remaining = self._paste_buffer[end_index + len(end_mark) :]
|
||||
self._paste_buffer = ""
|
||||
|
||||
self.feed(remaining)
|
||||
|
||||
@@ -391,8 +382,8 @@ class InputStream:
|
||||
|
||||
# When this is removed, replace Enter=ControlJ by
|
||||
# Enter=ControlM in keys.py.
|
||||
if c == '\r':
|
||||
c = '\n'
|
||||
if c == "\r":
|
||||
c = "\n"
|
||||
self._input_parser.send(c)
|
||||
|
||||
def flush(self):
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
import asyncio
|
||||
|
||||
|
||||
class Pool():
|
||||
class Pool:
|
||||
"""
|
||||
Limit concurrency for running parallel tasks
|
||||
"""
|
||||
@@ -60,5 +60,5 @@ def main():
|
||||
loop.run_until_complete(pool.join())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -20,6 +20,7 @@ import asyncio
|
||||
import asyncio.subprocess
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
READ_SIZE = 4096
|
||||
@@ -43,10 +44,12 @@ class AsyncioRawCommandServer:
|
||||
|
||||
async def run(self, network_reader, network_writer):
|
||||
await self._lock.acquire()
|
||||
process = await asyncio.subprocess.create_subprocess_exec(*self._command,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
stdin=asyncio.subprocess.PIPE)
|
||||
process = await asyncio.subprocess.create_subprocess_exec(
|
||||
*self._command,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
stdin=asyncio.subprocess.PIPE
|
||||
)
|
||||
try:
|
||||
await self._process(network_reader, network_writer, process.stdout, process.stdin)
|
||||
except ConnectionResetError:
|
||||
@@ -61,10 +64,20 @@ class AsyncioRawCommandServer:
|
||||
# Server host from the client point of view
|
||||
host = network_writer.transport.get_extra_info("sockname")[0]
|
||||
for replace in self._replaces:
|
||||
if b'{{HOST}}' in replace[1]:
|
||||
replaces.append((replace[0], replace[1].replace(b'{{HOST}}', host.encode()), ))
|
||||
if b"{{HOST}}" in replace[1]:
|
||||
replaces.append(
|
||||
(
|
||||
replace[0],
|
||||
replace[1].replace(b"{{HOST}}", host.encode()),
|
||||
)
|
||||
)
|
||||
else:
|
||||
replaces.append((replace[0], replace[1], ))
|
||||
replaces.append(
|
||||
(
|
||||
replace[0],
|
||||
replace[1],
|
||||
)
|
||||
)
|
||||
|
||||
network_read = asyncio.ensure_future(network_reader.read(READ_SIZE))
|
||||
reader_read = asyncio.ensure_future(process_reader.read(READ_SIZE))
|
||||
@@ -72,12 +85,8 @@ class AsyncioRawCommandServer:
|
||||
|
||||
while True:
|
||||
done, pending = await asyncio.wait(
|
||||
[
|
||||
network_read,
|
||||
reader_read
|
||||
],
|
||||
timeout=timeout,
|
||||
return_when=asyncio.FIRST_COMPLETED)
|
||||
[network_read, reader_read], timeout=timeout, return_when=asyncio.FIRST_COMPLETED
|
||||
)
|
||||
if len(done) == 0:
|
||||
raise ConnectionResetError()
|
||||
for coro in done:
|
||||
@@ -104,13 +113,21 @@ class AsyncioRawCommandServer:
|
||||
await network_writer.drain()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
command = ["nc", "localhost", "80"]
|
||||
server = AsyncioRawCommandServer(command, replaces=[(b"work", b"{{HOST}}", )])
|
||||
coro = asyncio.start_server(server.run, '0.0.0.0', 4444, loop=loop)
|
||||
server = AsyncioRawCommandServer(
|
||||
command,
|
||||
replaces=[
|
||||
(
|
||||
b"work",
|
||||
b"{{HOST}}",
|
||||
)
|
||||
],
|
||||
)
|
||||
coro = asyncio.start_server(server.run, "0.0.0.0", 4444, loop=loop)
|
||||
s = loop.run_until_complete(coro)
|
||||
|
||||
try:
|
||||
|
||||
@@ -31,7 +31,6 @@ if sys.platform.startswith("win"):
|
||||
|
||||
|
||||
class SerialReaderWriterProtocol(asyncio.Protocol):
|
||||
|
||||
def __init__(self):
|
||||
self._output = asyncio.StreamReader()
|
||||
self._closed = False
|
||||
@@ -135,6 +134,6 @@ async def asyncio_open_serial(path):
|
||||
"""
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
return (await _asyncio_open_serial_windows(path))
|
||||
return await _asyncio_open_serial_windows(path)
|
||||
else:
|
||||
return (await _asyncio_open_serial_unix(path))
|
||||
return await _asyncio_open_serial_unix(path)
|
||||
@@ -20,45 +20,47 @@ import asyncio.subprocess
|
||||
import struct
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Mostly from https://code.google.com/p/miniboa/source/browse/trunk/miniboa/telnet.py
|
||||
|
||||
# Telnet Commands
|
||||
SE = 240 # End of sub-negotiation parameters
|
||||
NOP = 241 # No operation
|
||||
DATMK = 242 # Data stream portion of a sync.
|
||||
BREAK = 243 # NVT Character BRK
|
||||
IP = 244 # Interrupt Process
|
||||
AO = 245 # Abort Output
|
||||
AYT = 246 # Are you there
|
||||
EC = 247 # Erase Character
|
||||
EL = 248 # Erase Line
|
||||
GA = 249 # The Go Ahead Signal
|
||||
SB = 250 # Sub-option to follow
|
||||
WILL = 251 # Will; request or confirm option begin
|
||||
WONT = 252 # Wont; deny option request
|
||||
DO = 253 # Do = Request or confirm remote option
|
||||
DONT = 254 # Don't = Demand or confirm option halt
|
||||
IAC = 255 # Interpret as Command
|
||||
SEND = 1 # Sub-process negotiation SEND command
|
||||
IS = 0 # Sub-process negotiation IS command
|
||||
SE = 240 # End of sub-negotiation parameters
|
||||
NOP = 241 # No operation
|
||||
DATMK = 242 # Data stream portion of a sync.
|
||||
BREAK = 243 # NVT Character BRK
|
||||
IP = 244 # Interrupt Process
|
||||
AO = 245 # Abort Output
|
||||
AYT = 246 # Are you there
|
||||
EC = 247 # Erase Character
|
||||
EL = 248 # Erase Line
|
||||
GA = 249 # The Go Ahead Signal
|
||||
SB = 250 # Sub-option to follow
|
||||
WILL = 251 # Will; request or confirm option begin
|
||||
WONT = 252 # Wont; deny option request
|
||||
DO = 253 # Do = Request or confirm remote option
|
||||
DONT = 254 # Don't = Demand or confirm option halt
|
||||
IAC = 255 # Interpret as Command
|
||||
SEND = 1 # Sub-process negotiation SEND command
|
||||
IS = 0 # Sub-process negotiation IS command
|
||||
|
||||
# Telnet Options
|
||||
BINARY = 0 # Transmit Binary
|
||||
ECHO = 1 # Echo characters back to sender
|
||||
RECON = 2 # Reconnection
|
||||
SGA = 3 # Suppress Go-Ahead
|
||||
TMARK = 6 # Timing Mark
|
||||
TTYPE = 24 # Terminal Type
|
||||
NAWS = 31 # Negotiate About Window Size
|
||||
LINEMO = 34 # Line Mode
|
||||
BINARY = 0 # Transmit Binary
|
||||
ECHO = 1 # Echo characters back to sender
|
||||
RECON = 2 # Reconnection
|
||||
SGA = 3 # Suppress Go-Ahead
|
||||
TMARK = 6 # Timing Mark
|
||||
TTYPE = 24 # Terminal Type
|
||||
NAWS = 31 # Negotiate About Window Size
|
||||
LINEMO = 34 # Line Mode
|
||||
|
||||
READ_SIZE = 1024
|
||||
|
||||
|
||||
class TelnetConnection:
|
||||
"""Default implementation of telnet connection which may but may not be used."""
|
||||
|
||||
def __init__(self, reader, writer, window_size_changed_callback=None):
|
||||
self.is_closing = False
|
||||
self._reader = reader
|
||||
@@ -83,7 +85,7 @@ class TelnetConnection:
|
||||
|
||||
async def window_size_changed(self, columns, rows):
|
||||
"""Method called when window size changed, only can occur when
|
||||
`naws` flag is enable in server configuration."""
|
||||
`naws` flag is enable in server configuration."""
|
||||
|
||||
if self._window_size_changed_callback:
|
||||
await self._window_size_changed_callback(columns, rows)
|
||||
@@ -113,14 +115,24 @@ class TelnetConnection:
|
||||
class AsyncioTelnetServer:
|
||||
MAX_NEGOTIATION_READ = 10
|
||||
|
||||
def __init__(self, reader=None, writer=None, binary=True, echo=False, naws=False, window_size_changed_callback=None, connection_factory=None):
|
||||
def __init__(
|
||||
self,
|
||||
reader=None,
|
||||
writer=None,
|
||||
binary=True,
|
||||
echo=False,
|
||||
naws=False,
|
||||
window_size_changed_callback=None,
|
||||
connection_factory=None,
|
||||
):
|
||||
"""
|
||||
Initializes telnet server
|
||||
:param naws when True make a window size negotiation
|
||||
:param connection_factory: when set it's possible to inject own implementation of connection
|
||||
"""
|
||||
assert connection_factory is None or (connection_factory is not None and reader is None and writer is None), \
|
||||
"Please use either reader and writer either connection_factory, otherwise duplicate data may be produced."
|
||||
assert connection_factory is None or (
|
||||
connection_factory is not None and reader is None and writer is None
|
||||
), "Please use either reader and writer either connection_factory, otherwise duplicate data may be produced."
|
||||
|
||||
self._reader = reader
|
||||
self._writer = writer
|
||||
@@ -151,9 +163,7 @@ class AsyncioTelnetServer:
|
||||
if echo:
|
||||
writer.write(bytes([IAC, WILL, ECHO]))
|
||||
else:
|
||||
writer.write(bytes([
|
||||
IAC, WONT, ECHO,
|
||||
IAC, DONT, ECHO]))
|
||||
writer.write(bytes([IAC, WONT, ECHO, IAC, DONT, ECHO]))
|
||||
await writer.drain()
|
||||
|
||||
async def _write_intro(self, writer, binary=False, echo=False, naws=False):
|
||||
@@ -161,26 +171,15 @@ class AsyncioTelnetServer:
|
||||
if echo:
|
||||
writer.write(bytes([IAC, WILL, ECHO]))
|
||||
else:
|
||||
writer.write(bytes([
|
||||
IAC, WONT, ECHO,
|
||||
IAC, DONT, ECHO]))
|
||||
writer.write(bytes([IAC, WONT, ECHO, IAC, DONT, ECHO]))
|
||||
|
||||
if binary:
|
||||
writer.write(bytes([
|
||||
IAC, WILL, SGA,
|
||||
IAC, WILL, BINARY,
|
||||
IAC, DO, BINARY]))
|
||||
writer.write(bytes([IAC, WILL, SGA, IAC, WILL, BINARY, IAC, DO, BINARY]))
|
||||
else:
|
||||
writer.write(bytes([
|
||||
IAC, WONT, SGA,
|
||||
IAC, DONT, SGA,
|
||||
IAC, WONT, BINARY,
|
||||
IAC, DONT, BINARY]))
|
||||
writer.write(bytes([IAC, WONT, SGA, IAC, DONT, SGA, IAC, WONT, BINARY, IAC, DONT, BINARY]))
|
||||
|
||||
if naws:
|
||||
writer.write(bytes([
|
||||
IAC, DO, NAWS
|
||||
]))
|
||||
writer.write(bytes([IAC, DO, NAWS]))
|
||||
await writer.drain()
|
||||
|
||||
async def run(self, network_reader, network_writer):
|
||||
@@ -188,7 +187,7 @@ class AsyncioTelnetServer:
|
||||
sock = network_writer.get_extra_info("socket")
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
#log.debug("New connection from {}".format(sock.getpeername()))
|
||||
# log.debug("New connection from {}".format(sock.getpeername()))
|
||||
|
||||
# Keep track of connected clients
|
||||
connection = self._connection_factory(network_reader, network_writer, self._window_size_changed_callback)
|
||||
@@ -247,14 +246,10 @@ class AsyncioTelnetServer:
|
||||
network_read,
|
||||
],
|
||||
timeout=1,
|
||||
return_when=asyncio.FIRST_COMPLETED)
|
||||
return_when=asyncio.FIRST_COMPLETED,
|
||||
)
|
||||
else:
|
||||
done, pending = await asyncio.wait(
|
||||
[
|
||||
network_read,
|
||||
reader_read
|
||||
],
|
||||
return_when=asyncio.FIRST_COMPLETED)
|
||||
done, pending = await asyncio.wait([network_read, reader_read], return_when=asyncio.FIRST_COMPLETED)
|
||||
for coro in done:
|
||||
data = coro.result()
|
||||
if coro == network_read:
|
||||
@@ -309,10 +304,10 @@ class AsyncioTelnetServer:
|
||||
command, payload = data[0], data[1:]
|
||||
if command == NAWS:
|
||||
if len(payload) == 4:
|
||||
columns, rows = struct.unpack('!HH', bytes(payload))
|
||||
columns, rows = struct.unpack("!HH", bytes(payload))
|
||||
await connection.window_size_changed(columns, rows)
|
||||
else:
|
||||
log.warning('Wrong number of NAWS bytes')
|
||||
log.warning("Wrong number of NAWS bytes")
|
||||
else:
|
||||
log.debug("Not supported negotiation sequence, received {} bytes", len(data))
|
||||
|
||||
@@ -324,7 +319,6 @@ class AsyncioTelnetServer:
|
||||
:returns: buffer minus Telnet commands
|
||||
"""
|
||||
|
||||
|
||||
skip_to = 0
|
||||
while True:
|
||||
# Locate an IAC to process
|
||||
@@ -345,7 +339,7 @@ class AsyncioTelnetServer:
|
||||
if iac_cmd[1] not in [WILL, WONT, DO, DONT, SB]:
|
||||
if iac_cmd[1] == AYT:
|
||||
log.debug("Telnet server received Are-You-There (AYT)")
|
||||
network_writer.write(b'\r\nYour Are-You-There received. I am here.\r\n')
|
||||
network_writer.write(b"\r\nYour Are-You-There received. I am here.\r\n")
|
||||
elif iac_cmd[1] == IAC:
|
||||
# It's data, not an IAC
|
||||
iac_cmd.pop()
|
||||
@@ -356,8 +350,7 @@ class AsyncioTelnetServer:
|
||||
elif iac_cmd[1] == NOP:
|
||||
pass
|
||||
else:
|
||||
log.debug("Unhandled telnet command: "
|
||||
"{:#x} {:#x}".format(*iac_cmd))
|
||||
log.debug("Unhandled telnet command: " "{:#x} {:#x}".format(*iac_cmd))
|
||||
elif iac_cmd[1] == SB: # starts negotiation commands
|
||||
negotiation = []
|
||||
for pos in range(2, self.MAX_NEGOTIATION_READ):
|
||||
@@ -392,38 +385,42 @@ class AsyncioTelnetServer:
|
||||
log.debug(f"Telnet WON'T {iac_cmd[2]:#x}")
|
||||
|
||||
elif iac_cmd[1] == DONT:
|
||||
log.debug("Unhandled DONT telnet command: "
|
||||
"{:#x} {:#x} {:#x}".format(*iac_cmd))
|
||||
log.debug("Unhandled DONT telnet command: " "{:#x} {:#x} {:#x}".format(*iac_cmd))
|
||||
elif iac_cmd[1] == WILL:
|
||||
if iac_cmd[2] not in [BINARY, NAWS]:
|
||||
log.debug("Unhandled WILL telnet command: "
|
||||
"{:#x} {:#x} {:#x}".format(*iac_cmd))
|
||||
log.debug("Unhandled WILL telnet command: " "{:#x} {:#x} {:#x}".format(*iac_cmd))
|
||||
elif iac_cmd[1] == WONT:
|
||||
log.debug("Unhandled WONT telnet command: "
|
||||
"{:#x} {:#x} {:#x}".format(*iac_cmd))
|
||||
log.debug("Unhandled WONT telnet command: " "{:#x} {:#x} {:#x}".format(*iac_cmd))
|
||||
else:
|
||||
log.debug("Unhandled telnet command: "
|
||||
"{:#x} {:#x} {:#x}".format(*iac_cmd))
|
||||
log.debug("Unhandled telnet command: " "{:#x} {:#x} {:#x}".format(*iac_cmd))
|
||||
|
||||
# Remove the entire TELNET command from the buffer
|
||||
buf = buf.replace(iac_cmd, b'', 1)
|
||||
buf = buf.replace(iac_cmd, b"", 1)
|
||||
|
||||
await network_writer.drain()
|
||||
|
||||
# Return the new copy of the buffer, minus telnet commands
|
||||
return buf
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
process = loop.run_until_complete(asyncio.ensure_future(asyncio.subprocess.create_subprocess_exec("/bin/sh", "-i",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
stdin=asyncio.subprocess.PIPE)))
|
||||
process = loop.run_until_complete(
|
||||
asyncio.ensure_future(
|
||||
asyncio.subprocess.create_subprocess_exec(
|
||||
"/bin/sh",
|
||||
"-i",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
)
|
||||
)
|
||||
)
|
||||
server = AsyncioTelnetServer(reader=process.stdout, writer=process.stdin, binary=False, echo=False)
|
||||
|
||||
coro = asyncio.start_server(server.run, '127.0.0.1', 4444, loop=loop)
|
||||
coro = asyncio.start_server(server.run, "127.0.0.1", 4444, loop=loop)
|
||||
s = loop.run_until_complete(coro)
|
||||
|
||||
try:
|
||||
|
||||
@@ -23,8 +23,8 @@ class CpuPercent:
|
||||
Ensures a minumum interval between two cpu_percent() calls
|
||||
"""
|
||||
|
||||
_last_measurement = None # time of last measurement
|
||||
_last_cpu_percent = 0.0 # last cpu_percent
|
||||
_last_measurement = None # time of last measurement
|
||||
_last_cpu_percent = 0.0 # last cpu_percent
|
||||
|
||||
@classmethod
|
||||
def get(cls, interval=None):
|
||||
@@ -39,8 +39,7 @@ class CpuPercent:
|
||||
cls._last_measurement = time.monotonic()
|
||||
else:
|
||||
cur_time = time.monotonic()
|
||||
if cls._last_measurement is None or \
|
||||
(cur_time - cls._last_measurement) >= 1.9:
|
||||
if cls._last_measurement is None or (cur_time - cls._last_measurement) >= 1.9:
|
||||
cls._last_cpu_percent = psutil.cpu_percent(interval=None)
|
||||
cls._last_measurement = cur_time
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class FileWatcher:
|
||||
:param strategy: File change strategy (mtime: modification time, hash: hash compute)
|
||||
"""
|
||||
|
||||
def __init__(self, paths, callback, delay=1, strategy='mtime'):
|
||||
def __init__(self, paths, callback, delay=1, strategy="mtime"):
|
||||
self._paths = []
|
||||
if not isinstance(paths, list):
|
||||
paths = [paths]
|
||||
@@ -43,7 +43,7 @@ class FileWatcher:
|
||||
self._closed = False
|
||||
self._strategy = strategy
|
||||
|
||||
if self._strategy == 'mtime':
|
||||
if self._strategy == "mtime":
|
||||
# Store modification time
|
||||
self._mtime = {}
|
||||
for path in self._paths:
|
||||
@@ -57,7 +57,7 @@ class FileWatcher:
|
||||
for path in self._paths:
|
||||
try:
|
||||
# Alder32 is a fast but insecure hash algorithm
|
||||
self._hashed[path] = zlib.adler32(open(path, 'rb').read())
|
||||
self._hashed[path] = zlib.adler32(open(path, "rb").read())
|
||||
except OSError:
|
||||
self._hashed[path] = None
|
||||
asyncio.get_event_loop().call_later(self._delay, self._check_config_file_change)
|
||||
@@ -74,7 +74,7 @@ class FileWatcher:
|
||||
changed = False
|
||||
|
||||
for path in self._paths:
|
||||
if self._strategy == 'mtime':
|
||||
if self._strategy == "mtime":
|
||||
try:
|
||||
mtime = os.stat(path).st_mtime_ns
|
||||
if mtime != self._mtime[path]:
|
||||
@@ -84,7 +84,7 @@ class FileWatcher:
|
||||
self._mtime[path] = None
|
||||
else:
|
||||
try:
|
||||
hashc = zlib.adler32(open(path, 'rb').read())
|
||||
hashc = zlib.adler32(open(path, "rb").read())
|
||||
if hashc != self._hashed[path]:
|
||||
changed = True
|
||||
self._hashed[path] = hashc
|
||||
|
||||
@@ -35,6 +35,7 @@ except ValueError:
|
||||
def clean_egg_cache():
|
||||
try:
|
||||
import shutil
|
||||
|
||||
log.debug("Clean egg cache %s", egg_cache_dir)
|
||||
shutil.rmtree(egg_cache_dir)
|
||||
except Exception:
|
||||
|
||||
@@ -19,6 +19,7 @@ import aiohttp
|
||||
import socket
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
+22
-13
@@ -22,6 +22,7 @@ from . import force_unix_path
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -56,9 +57,11 @@ def list_images(type):
|
||||
if filename not in files:
|
||||
if filename.endswith(".md5sum") or filename.startswith("."):
|
||||
continue
|
||||
elif ((filename.endswith(".image") or filename.endswith(".bin")) and type == "dynamips") \
|
||||
or ((filename.endswith(".bin") or filename.startswith("i86bi")) and type == "iou") \
|
||||
or (not filename.endswith(".bin") and not filename.endswith(".image") and type == "qemu"):
|
||||
elif (
|
||||
((filename.endswith(".image") or filename.endswith(".bin")) and type == "dynamips")
|
||||
or ((filename.endswith(".bin") or filename.startswith("i86bi")) and type == "iou")
|
||||
or (not filename.endswith(".bin") and not filename.endswith(".image") and type == "qemu")
|
||||
):
|
||||
files.add(filename)
|
||||
|
||||
# It the image is located in the standard directory the path is relative
|
||||
@@ -73,14 +76,20 @@ def list_images(type):
|
||||
# read the first 7 bytes of the file.
|
||||
elf_header_start = f.read(7)
|
||||
# valid IOS images must start with the ELF magic number, be 32-bit, big endian and have an ELF version of 1
|
||||
if not elf_header_start == b'\x7fELF\x01\x02\x01' and not elf_header_start == b'\x7fELF\x01\x01\x01':
|
||||
if (
|
||||
not elf_header_start == b"\x7fELF\x01\x02\x01"
|
||||
and not elf_header_start == b"\x7fELF\x01\x01\x01"
|
||||
):
|
||||
continue
|
||||
|
||||
images.append({
|
||||
"filename": filename,
|
||||
"path": force_unix_path(path),
|
||||
"md5sum": md5sum(os.path.join(root, filename)),
|
||||
"filesize": os.stat(os.path.join(root, filename)).st_size})
|
||||
images.append(
|
||||
{
|
||||
"filename": filename,
|
||||
"path": force_unix_path(path),
|
||||
"md5sum": md5sum(os.path.join(root, filename)),
|
||||
"filesize": os.stat(os.path.join(root, filename)).st_size,
|
||||
}
|
||||
)
|
||||
except OSError as e:
|
||||
log.warning(f"Can't add image {path}: {str(e)}")
|
||||
return images
|
||||
@@ -156,7 +165,7 @@ def md5sum(path, stopped_event=None):
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(path + '.md5sum') as f:
|
||||
with open(path + ".md5sum") as f:
|
||||
md5 = f.read().strip()
|
||||
if len(md5) == 32:
|
||||
return md5
|
||||
@@ -166,7 +175,7 @@ def md5sum(path, stopped_event=None):
|
||||
|
||||
try:
|
||||
m = hashlib.md5()
|
||||
with open(path, 'rb') as f:
|
||||
with open(path, "rb") as f:
|
||||
while True:
|
||||
if stopped_event is not None and stopped_event.is_set():
|
||||
log.error(f"MD5 sum calculation of `{path}` has stopped due to cancellation")
|
||||
@@ -181,7 +190,7 @@ def md5sum(path, stopped_event=None):
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(f'{path}.md5sum', 'w+') as f:
|
||||
with open(f"{path}.md5sum", "w+") as f:
|
||||
f.write(digest)
|
||||
except OSError as e:
|
||||
log.error("Can't write digest of %s: %s", path, str(e))
|
||||
@@ -194,6 +203,6 @@ def remove_checksum(path):
|
||||
Remove the checksum of an image from cache if exists
|
||||
"""
|
||||
|
||||
path = f'{path}.md5sum'
|
||||
path = f"{path}.md5sum"
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
@@ -26,9 +26,12 @@ from gns3server.compute.compute_error import ComputeError
|
||||
from gns3server.config import Config
|
||||
|
||||
if psutil.version_info < (3, 0, 0):
|
||||
raise Exception("psutil version should >= 3.0.0. If you are under Ubuntu/Debian install gns3 via apt instead of pip")
|
||||
raise Exception(
|
||||
"psutil version should >= 3.0.0. If you are under Ubuntu/Debian install gns3 via apt instead of pip"
|
||||
)
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -45,7 +48,10 @@ def _get_windows_interfaces_from_registry():
|
||||
hkeycard = winreg.OpenKey(hkey, network_card_id)
|
||||
guid, _ = winreg.QueryValueEx(hkeycard, "ServiceName")
|
||||
netcard, _ = winreg.QueryValueEx(hkeycard, "Description")
|
||||
connection = r"SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}" + fr"\{guid}\Connection"
|
||||
connection = (
|
||||
r"SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}"
|
||||
+ fr"\{guid}\Connection"
|
||||
)
|
||||
hkeycon = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, connection)
|
||||
name, _ = winreg.QueryValueEx(hkeycon, "Name")
|
||||
interface = fr"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{guid}"
|
||||
@@ -61,13 +67,17 @@ def _get_windows_interfaces_from_registry():
|
||||
# get the first IPv4 address only
|
||||
ip_address = ip_address[0]
|
||||
npf_interface = "\\Device\\NPF_{guid}".format(guid=guid)
|
||||
interfaces.append({"id": npf_interface,
|
||||
"name": name,
|
||||
"ip_address": ip_address,
|
||||
"mac_address": "", # TODO: find MAC address in registry
|
||||
"netcard": netcard,
|
||||
"netmask": netmask,
|
||||
"type": "ethernet"})
|
||||
interfaces.append(
|
||||
{
|
||||
"id": npf_interface,
|
||||
"name": name,
|
||||
"ip_address": ip_address,
|
||||
"mac_address": "", # TODO: find MAC address in registry
|
||||
"netcard": netcard,
|
||||
"netmask": netmask,
|
||||
"type": "ethernet",
|
||||
}
|
||||
)
|
||||
winreg.CloseKey(hkeyinterface)
|
||||
winreg.CloseKey(hkeycon)
|
||||
winreg.CloseKey(hkeycard)
|
||||
@@ -107,13 +117,17 @@ def get_windows_interfaces():
|
||||
netmask = network_config.IPSubnet[0]
|
||||
break
|
||||
npf_interface = "\\Device\\NPF_{guid}".format(guid=adapter.GUID)
|
||||
interfaces.append({"id": npf_interface,
|
||||
"name": adapter.NetConnectionID,
|
||||
"ip_address": ip_address,
|
||||
"mac_address": adapter.MACAddress,
|
||||
"netcard": adapter.name,
|
||||
"netmask": netmask,
|
||||
"type": "ethernet"})
|
||||
interfaces.append(
|
||||
{
|
||||
"id": npf_interface,
|
||||
"name": adapter.NetConnectionID,
|
||||
"ip_address": ip_address,
|
||||
"mac_address": adapter.MACAddress,
|
||||
"netcard": adapter.name,
|
||||
"netmask": netmask,
|
||||
"type": "ethernet",
|
||||
}
|
||||
)
|
||||
except (AttributeError, pywintypes.com_error):
|
||||
log.warning("Could not use the COM service to retrieve interface info, trying using the registry...")
|
||||
return _get_windows_interfaces_from_registry()
|
||||
@@ -152,11 +166,12 @@ def is_interface_up(interface):
|
||||
return False
|
||||
|
||||
import fcntl
|
||||
|
||||
SIOCGIFFLAGS = 0x8913
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
||||
result = fcntl.ioctl(s.fileno(), SIOCGIFFLAGS, interface + '\0' * 256)
|
||||
flags, = struct.unpack('H', result[16:18])
|
||||
result = fcntl.ioctl(s.fileno(), SIOCGIFFLAGS, interface + "\0" * 256)
|
||||
(flags,) = struct.unpack("H", result[16:18])
|
||||
if flags & 1: # check if the up bit is set
|
||||
return True
|
||||
return False
|
||||
@@ -203,12 +218,16 @@ def interfaces():
|
||||
if interface.startswith("tap"):
|
||||
# found no way to reliably detect a TAP interface
|
||||
interface_type = "tap"
|
||||
results.append({"id": interface,
|
||||
"name": interface,
|
||||
"ip_address": ip_address,
|
||||
"netmask": netmask,
|
||||
"mac_address": mac_address,
|
||||
"type": interface_type})
|
||||
results.append(
|
||||
{
|
||||
"id": interface,
|
||||
"name": interface,
|
||||
"ip_address": ip_address,
|
||||
"netmask": netmask,
|
||||
"mac_address": mac_address,
|
||||
"type": interface_type,
|
||||
}
|
||||
)
|
||||
else:
|
||||
try:
|
||||
service_installed = True
|
||||
@@ -217,7 +236,9 @@ def interfaces():
|
||||
else:
|
||||
results = get_windows_interfaces()
|
||||
except ImportError:
|
||||
message = "pywin32 module is not installed, please install it on the server to get the available interface names"
|
||||
message = (
|
||||
"pywin32 module is not installed, please install it on the server to get the available interface names"
|
||||
)
|
||||
raise ComputeError(message)
|
||||
except Exception as e:
|
||||
log.error(f"uncaught exception {type(e)}", exc_info=1)
|
||||
@@ -229,12 +250,25 @@ def interfaces():
|
||||
# This interface have special behavior
|
||||
for result in results:
|
||||
result["special"] = False
|
||||
for special_interface in ("lo", "vmnet", "vboxnet", "docker", "lxcbr",
|
||||
"virbr", "ovs-system", "veth", "fw", "p2p",
|
||||
"bridge", "vmware", "virtualbox", "gns3"):
|
||||
for special_interface in (
|
||||
"lo",
|
||||
"vmnet",
|
||||
"vboxnet",
|
||||
"docker",
|
||||
"lxcbr",
|
||||
"virbr",
|
||||
"ovs-system",
|
||||
"veth",
|
||||
"fw",
|
||||
"p2p",
|
||||
"bridge",
|
||||
"vmware",
|
||||
"virtualbox",
|
||||
"gns3",
|
||||
):
|
||||
if result["name"].lower().startswith(special_interface):
|
||||
result["special"] = True
|
||||
for special_interface in ("-nic"):
|
||||
for special_interface in "-nic":
|
||||
if result["name"].lower().endswith(special_interface):
|
||||
result["special"] = True
|
||||
return results
|
||||
@@ -23,6 +23,7 @@ from gns3server.utils.cpu_percent import CpuPercent
|
||||
from gns3server.utils.path import get_default_project_directory
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -55,9 +56,7 @@ class NotificationQueue(asyncio.Queue):
|
||||
"""
|
||||
Return the content of the ping notification
|
||||
"""
|
||||
msg = {"cpu_usage_percent": 0,
|
||||
"memory_usage_percent": 0,
|
||||
"disk_usage_percent": 0}
|
||||
msg = {"cpu_usage_percent": 0, "memory_usage_percent": 0, "disk_usage_percent": 0}
|
||||
# Non blocking call in order to get cpu usage. First call will return 0
|
||||
try:
|
||||
msg["cpu_usage_percent"] = CpuPercent.get(interval=None)
|
||||
|
||||
+11
-18
@@ -47,7 +47,7 @@ def get_size(data, default_width=0, default_height=0):
|
||||
|
||||
size = len(data)
|
||||
# handle GIFs
|
||||
if size >= 10 and data[:6] in (b'GIF87a', b'GIF89a'):
|
||||
if size >= 10 and data[:6] in (b"GIF87a", b"GIF89a"):
|
||||
# Check to see if content_type is correct
|
||||
try:
|
||||
width, height = struct.unpack("<hh", data[6:10])
|
||||
@@ -55,14 +55,14 @@ def get_size(data, default_width=0, default_height=0):
|
||||
except struct.error:
|
||||
raise ValueError("Invalid GIF file")
|
||||
# see png edition spec bytes are below chunk length then and finally the
|
||||
elif size >= 24 and data.startswith(b'\211PNG\r\n\032\n') and data[12:16] == b'IHDR':
|
||||
elif size >= 24 and data.startswith(b"\211PNG\r\n\032\n") and data[12:16] == b"IHDR":
|
||||
try:
|
||||
width, height = struct.unpack(">LL", data[16:24])
|
||||
filetype = "png"
|
||||
except struct.error:
|
||||
raise ValueError("Invalid PNG file")
|
||||
# Maybe this is for an older PNG version.
|
||||
elif size >= 16 and data.startswith(b'\211PNG\r\n\032\n'):
|
||||
elif size >= 16 and data.startswith(b"\211PNG\r\n\032\n"):
|
||||
# Check to see if we have the right content type
|
||||
try:
|
||||
width, height = struct.unpack(">LL", data[8:16])
|
||||
@@ -70,29 +70,29 @@ def get_size(data, default_width=0, default_height=0):
|
||||
except struct.error:
|
||||
raise ValueError("Invalid PNG file")
|
||||
# handle JPEGs
|
||||
elif size >= 2 and data.startswith(b'\377\330'):
|
||||
elif size >= 2 and data.startswith(b"\377\330"):
|
||||
try:
|
||||
# Not very efficient to copy data to a buffer
|
||||
fhandle = io.BytesIO(data)
|
||||
size = 2
|
||||
ftype = 0
|
||||
while not 0xc0 <= ftype <= 0xcf:
|
||||
while not 0xC0 <= ftype <= 0xCF:
|
||||
fhandle.seek(size, 1)
|
||||
byte = fhandle.read(1)
|
||||
while ord(byte) == 0xff:
|
||||
while ord(byte) == 0xFF:
|
||||
byte = fhandle.read(1)
|
||||
ftype = ord(byte)
|
||||
size = struct.unpack('>H', fhandle.read(2))[0] - 2
|
||||
size = struct.unpack(">H", fhandle.read(2))[0] - 2
|
||||
# We are at a SOFn block
|
||||
fhandle.seek(1, 1) # Skip `precision' byte.
|
||||
height, width = struct.unpack('>HH', fhandle.read(4))
|
||||
height, width = struct.unpack(">HH", fhandle.read(4))
|
||||
filetype = "jpg"
|
||||
except struct.error:
|
||||
raise ValueError("Invalid JPEG file")
|
||||
# End of https://github.com/shibukawa/imagesize_py
|
||||
|
||||
# handle SVG
|
||||
elif size >= 10 and (data.startswith(b'<?xml') or data.startswith(b'<svg')):
|
||||
elif size >= 10 and (data.startswith(b"<?xml") or data.startswith(b"<svg")):
|
||||
filetype = "svg"
|
||||
fhandle = io.BytesIO(data)
|
||||
tree = ElementTree()
|
||||
@@ -111,7 +111,7 @@ def get_size(data, default_width=0, default_height=0):
|
||||
viewbox = root.attrib.get("viewBox")
|
||||
if not viewbox:
|
||||
raise ValueError("Invalid SVG file: missing viewBox attribute")
|
||||
_, _, viewbox_width, viewbox_height = re.split(r'[\s,]+', viewbox)
|
||||
_, _, viewbox_width, viewbox_height = re.split(r"[\s,]+", viewbox)
|
||||
if width_attr.endswith("%"):
|
||||
width = _svg_convert_size(viewbox_width, width_attr)
|
||||
else:
|
||||
@@ -135,14 +135,7 @@ def _svg_convert_size(size, percent=None):
|
||||
"""
|
||||
|
||||
# https://www.w3.org/TR/SVG/coords.html#Units
|
||||
conversion_table = {
|
||||
"pt": 1.25,
|
||||
"pc": 15,
|
||||
"mm": 3.543307,
|
||||
"cm": 35.43307,
|
||||
"in": 90,
|
||||
"px": 1
|
||||
}
|
||||
conversion_table = {"pt": 1.25, "pc": 15, "mm": 3.543307, "cm": 35.43307, "in": 90, "px": 1}
|
||||
factor = 1.0
|
||||
if len(size) >= 3:
|
||||
if size[-2:] in conversion_table:
|
||||
|
||||
@@ -47,7 +47,7 @@ def parse_networking_file():
|
||||
version = f.readline()
|
||||
for line in f.read().splitlines():
|
||||
try:
|
||||
_, key, value = line.split(' ', 3)
|
||||
_, key, value = line.split(" ", 3)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
pairs[key] = value
|
||||
@@ -92,19 +92,20 @@ def parse_vmnet_range(start, end):
|
||||
"""
|
||||
|
||||
class Range(argparse.Action):
|
||||
|
||||
def __call__(self, parser, args, values, option_string=None):
|
||||
if len(values) != 2:
|
||||
raise argparse.ArgumentTypeError("vmnet range must consist of 2 numbers")
|
||||
if not start <= values[0] or not values[1] <= end:
|
||||
raise argparse.ArgumentTypeError(f"vmnet range must be between {start} and {end}")
|
||||
setattr(args, self.dest, values)
|
||||
|
||||
return Range
|
||||
|
||||
|
||||
def find_vnetlib_registry(regkey):
|
||||
|
||||
import winreg
|
||||
|
||||
try:
|
||||
# default path not used, let's look in the registry
|
||||
hkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, regkey)
|
||||
@@ -162,6 +163,7 @@ def vmnet_windows(args, vmnet_range_start, vmnet_range_end):
|
||||
if not vnetlib_path:
|
||||
raise SystemExit("VMware is not installed, could not find vnetlib.exe")
|
||||
from win32com.shell import shell
|
||||
|
||||
if not shell.IsUserAnAdmin():
|
||||
raise SystemExit("You must run this script as an administrator")
|
||||
|
||||
@@ -243,9 +245,15 @@ def main():
|
||||
Entry point for the VMNET tool.
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(description='%(prog)s add/remove vmnet interfaces')
|
||||
parser.add_argument('-r', "--range", nargs='+', action=parse_vmnet_range(1, 255),
|
||||
type=int, help=f"vmnet range to add (default is {DEFAULT_RANGE[0]} {DEFAULT_RANGE[1]})")
|
||||
parser = argparse.ArgumentParser(description="%(prog)s add/remove vmnet interfaces")
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--range",
|
||||
nargs="+",
|
||||
action=parse_vmnet_range(1, 255),
|
||||
type=int,
|
||||
help=f"vmnet range to add (default is {DEFAULT_RANGE[0]} {DEFAULT_RANGE[1]})",
|
||||
)
|
||||
parser.add_argument("-C", "--clean", action="store_true", help="remove all vmnets excepting vmnet1 and vmnet8")
|
||||
parser.add_argument("-l", "--list", action="store_true", help="list all existing vmnets (UNIX only)")
|
||||
|
||||
@@ -264,5 +272,6 @@ def main():
|
||||
else:
|
||||
vmnet_unix(args, vmnet_range[0], vmnet_range[1])
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -33,7 +33,6 @@ def parse_add_loopback():
|
||||
"""
|
||||
|
||||
class Add(argparse.Action):
|
||||
|
||||
def __call__(self, parser, args, values, option_string=None):
|
||||
try:
|
||||
ipaddress.IPv4Interface(f"{values[1]}/{values[2]}")
|
||||
@@ -42,6 +41,7 @@ def parse_add_loopback():
|
||||
except ipaddress.NetmaskValueError as e:
|
||||
raise argparse.ArgumentTypeError(f"Invalid subnet mask: {e}")
|
||||
setattr(args, self.dest, values)
|
||||
|
||||
return Add
|
||||
|
||||
|
||||
@@ -70,8 +70,8 @@ def add_loopback(devcon_path, name, ip_address, netmask):
|
||||
elif retcode != 0:
|
||||
print('Error while configuring IP/Subnet mask on "{}"')
|
||||
|
||||
#FIXME: support gateway?
|
||||
#network_config.SetGateways(DefaultIPGateway=[""])
|
||||
# FIXME: support gateway?
|
||||
# network_config.SetGateways(DefaultIPGateway=[""])
|
||||
break
|
||||
|
||||
# restart winpcap/npcap services to take the new adapter into account
|
||||
@@ -106,8 +106,8 @@ def main():
|
||||
Entry point for the Windows loopback tool.
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(description='%(prog)s add/remove Windows loopback adapters')
|
||||
parser.add_argument('-a', "--add", nargs=3, action=parse_add_loopback(), help="add a Windows loopback adapter")
|
||||
parser = argparse.ArgumentParser(description="%(prog)s add/remove Windows loopback adapters")
|
||||
parser.add_argument("-a", "--add", nargs=3, action=parse_add_loopback(), help="add a Windows loopback adapter")
|
||||
parser.add_argument("-r", "--remove", action="store", help="remove a Windows loopback adapter")
|
||||
try:
|
||||
args = parser.parse_args()
|
||||
@@ -120,6 +120,7 @@ def main():
|
||||
raise SystemExit("Could not find devcon.exe")
|
||||
|
||||
from win32com.shell import shell
|
||||
|
||||
if not shell.IsUserAnAdmin():
|
||||
raise SystemExit("You must run this script as an administrator")
|
||||
|
||||
@@ -132,5 +133,6 @@ def main():
|
||||
print(e)
|
||||
os.system("pause")
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user