mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Update some of changed libs and tests from 3.14.5 -> 3.14.6 (part 1) (#8084)
This commit is contained in:
1 parent
d18d35faba
commit
404ee2a1f6
37 files changed
+674
-374
No files matched your search
Vendored
+17
-12
@@ -461,8 +461,8 @@ class Buffer(metaclass=ABCMeta):
|
||||
class _CallableGenericAlias(GenericAlias):
|
||||
""" Represent `Callable[argtypes, resulttype]`.
|
||||
|
||||
This sets ``__args__`` to a tuple containing the flattened ``argtypes``
|
||||
followed by ``resulttype``.
|
||||
This sets ``__args__`` to a tuple containing the flattened
|
||||
``argtypes`` followed by ``resulttype``.
|
||||
|
||||
Example: ``Callable[[int, str], float]`` sets ``__args__`` to
|
||||
``(int, str, float)``.
|
||||
@@ -927,8 +927,9 @@ class MutableMapping(Mapping):
|
||||
__marker = object()
|
||||
|
||||
def pop(self, key, default=__marker):
|
||||
'''D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
|
||||
If key is not found, d is returned if given, otherwise KeyError is raised.
|
||||
'''D.pop(k[,d]) -> v, remove specified key and return the corresponding
|
||||
value. If key is not found, d is returned if given, otherwise
|
||||
KeyError is raised.
|
||||
'''
|
||||
try:
|
||||
value = self[key]
|
||||
@@ -962,9 +963,12 @@ class MutableMapping(Mapping):
|
||||
|
||||
def update(self, other=(), /, **kwds):
|
||||
''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F.
|
||||
If E present and has a .keys() method, does: for k in E.keys(): D[k] = E[k]
|
||||
If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
|
||||
In either case, this is followed by: for k, v in F.items(): D[k] = v
|
||||
If E present and has a .keys() method, does:
|
||||
for k in E.keys(): D[k] = E[k]
|
||||
If E present and lacks .keys() method, does:
|
||||
for (k, v) in E: D[k] = v
|
||||
In either case, this is followed by:
|
||||
for k, v in F.items(): D[k] = v
|
||||
'''
|
||||
if isinstance(other, Mapping):
|
||||
for key in other:
|
||||
@@ -1029,8 +1033,8 @@ class Sequence(Reversible, Collection):
|
||||
yield self[i]
|
||||
|
||||
def index(self, value, start=0, stop=None):
|
||||
'''S.index(value, [start, [stop]]) -> integer -- return first index of value.
|
||||
Raises ValueError if the value is not present.
|
||||
'''S.index(value, [start, [stop]]) -> integer -- return first index of
|
||||
value. Raises ValueError if the value is not present.
|
||||
|
||||
Supporting start and stop arguments is optional, but
|
||||
recommended.
|
||||
@@ -1138,15 +1142,16 @@ class MutableSequence(Sequence):
|
||||
self[i], self[n-i-1] = self[n-i-1], self[i]
|
||||
|
||||
def extend(self, values):
|
||||
'S.extend(iterable) -- extend sequence by appending elements from the iterable'
|
||||
"""S.extend(iterable) -- extend sequence by appending elements from the
|
||||
iterable"""
|
||||
if values is self:
|
||||
values = list(values)
|
||||
for v in values:
|
||||
self.append(v)
|
||||
|
||||
def pop(self, index=-1):
|
||||
'''S.pop([index]) -> item -- remove and return item at index (default last).
|
||||
Raise IndexError if list is empty or index is out of range.
|
||||
'''S.pop([index]) -> item -- remove and return item at index (default
|
||||
last). Raise IndexError if list is empty or index is out of range.
|
||||
'''
|
||||
v = self[index]
|
||||
del self[index]
|
||||
|
||||
Vendored
+56
-51
@@ -104,8 +104,8 @@ class DecimalException(ArithmeticError):
|
||||
anything, though.
|
||||
|
||||
handle -- Called when context._raise_error is called and the
|
||||
trap_enabler is not set. First argument is self, second is the
|
||||
context. More arguments can be given, those being after
|
||||
trap_enabler is not set. First argument is self, second is
|
||||
the context. More arguments can be given, those being after
|
||||
the explanation in _raise_error (For example,
|
||||
context._raise_error(NewError, '(-x)!', self._sign) would
|
||||
call NewError().handle(context, self._sign).)
|
||||
@@ -222,11 +222,12 @@ class InvalidContext(InvalidOperation):
|
||||
"""Invalid context. Unknown rounding, for example.
|
||||
|
||||
This occurs and signals invalid-operation if an invalid context was
|
||||
detected during an operation. This can occur if contexts are not checked
|
||||
on creation and either the precision exceeds the capability of the
|
||||
underlying concrete representation or an unknown or unsupported rounding
|
||||
was specified. These aspects of the context need only be checked when
|
||||
the values are required to be used. The result is [0,qNaN].
|
||||
detected during an operation. This can occur if contexts are not
|
||||
checked on creation and either the precision exceeds the capability of
|
||||
the underlying concrete representation or an unknown or unsupported
|
||||
rounding was specified. These aspects of the context need only be
|
||||
checked when the values are required to be used. The result is
|
||||
[0,qNaN].
|
||||
"""
|
||||
|
||||
def handle(self, context, *args):
|
||||
@@ -319,8 +320,9 @@ class FloatOperation(DecimalException, TypeError):
|
||||
Decimal.from_float() or context.create_decimal_from_float() do not
|
||||
set the flag.
|
||||
|
||||
Otherwise (the signal is trapped), only equality comparisons and explicit
|
||||
conversions are silent. All other mixed operations raise FloatOperation.
|
||||
Otherwise (the signal is trapped), only equality comparisons and
|
||||
explicit conversions are silent. All other mixed operations raise
|
||||
FloatOperation.
|
||||
"""
|
||||
|
||||
# List of public traps and flags
|
||||
@@ -2898,8 +2900,8 @@ class Decimal(object):
|
||||
"""Compares self to other using the abstract representations.
|
||||
|
||||
This is not like the standard compare, which use their numerical
|
||||
value. Note that a total ordering is defined for all possible abstract
|
||||
representations.
|
||||
value. Note that a total ordering is defined for all possible
|
||||
abstract representations.
|
||||
"""
|
||||
other = _convert_other(other, raiseit=True)
|
||||
|
||||
@@ -2970,7 +2972,8 @@ class Decimal(object):
|
||||
def compare_total_mag(self, other, context=None):
|
||||
"""Compares self to other using abstract repr., ignoring sign.
|
||||
|
||||
Like compare_total, but with operand's sign ignored and assumed to be 0.
|
||||
Like compare_total, but with operand's sign ignored and assumed to
|
||||
be 0.
|
||||
"""
|
||||
other = _convert_other(other, raiseit=True)
|
||||
|
||||
@@ -4107,9 +4110,9 @@ class Context(object):
|
||||
def abs(self, a):
|
||||
"""Returns the absolute value of the operand.
|
||||
|
||||
If the operand is negative, the result is the same as using the minus
|
||||
operation on the operand. Otherwise, the result is the same as using
|
||||
the plus operation on the operand.
|
||||
If the operand is negative, the result is the same as using the
|
||||
minus operation on the operand. Otherwise, the result is the same
|
||||
as using the plus operation on the operand.
|
||||
|
||||
>>> ExtendedContext.abs(Decimal('2.1'))
|
||||
Decimal('2.1')
|
||||
@@ -4165,16 +4168,17 @@ class Context(object):
|
||||
def compare(self, a, b):
|
||||
"""Compares values numerically.
|
||||
|
||||
If the signs of the operands differ, a value representing each operand
|
||||
('-1' if the operand is less than zero, '0' if the operand is zero or
|
||||
negative zero, or '1' if the operand is greater than zero) is used in
|
||||
place of that operand for the comparison instead of the actual
|
||||
operand.
|
||||
If the signs of the operands differ, a value representing each
|
||||
operand ('-1' if the operand is less than zero, '0' if the operand
|
||||
is zero or negative zero, or '1' if the operand is greater than
|
||||
zero) is used in place of that operand for the comparison instead of
|
||||
the actual operand.
|
||||
|
||||
The comparison is then effected by subtracting the second operand from
|
||||
the first and then returning a value according to the result of the
|
||||
subtraction: '-1' if the result is less than zero, '0' if the result is
|
||||
zero or negative zero, or '1' if the result is greater than zero.
|
||||
The comparison is then effected by subtracting the second operand
|
||||
from the first and then returning a value according to the result of
|
||||
the subtraction: '-1' if the result is less than zero, '0' if the
|
||||
result is zero or negative zero, or '1' if the result is greater
|
||||
than zero.
|
||||
|
||||
>>> ExtendedContext.compare(Decimal('2.1'), Decimal('3'))
|
||||
Decimal('-1')
|
||||
@@ -4237,8 +4241,8 @@ class Context(object):
|
||||
"""Compares two operands using their abstract representation.
|
||||
|
||||
This is not like the standard compare, which use their numerical
|
||||
value. Note that a total ordering is defined for all possible abstract
|
||||
representations.
|
||||
value. Note that a total ordering is defined for all possible
|
||||
abstract representations.
|
||||
|
||||
>>> ExtendedContext.compare_total(Decimal('12.73'), Decimal('127.9'))
|
||||
Decimal('-1')
|
||||
@@ -4265,7 +4269,8 @@ class Context(object):
|
||||
def compare_total_mag(self, a, b):
|
||||
"""Compares two operands using their abstract representation ignoring sign.
|
||||
|
||||
Like compare_total, but with operand's sign ignored and assumed to be 0.
|
||||
Like compare_total, but with operand's sign ignored and assumed to
|
||||
be 0.
|
||||
"""
|
||||
a = _convert_other(a, raiseit=True)
|
||||
return a.compare_total_mag(b)
|
||||
@@ -4923,8 +4928,8 @@ class Context(object):
|
||||
|
||||
If either operand is a special value then the general rules apply.
|
||||
Otherwise, the operands are multiplied together
|
||||
('long multiplication'), resulting in a number which may be as long as
|
||||
the sum of the lengths of the two operands.
|
||||
('long multiplication'), resulting in a number which may be as long
|
||||
as the sum of the lengths of the two operands.
|
||||
|
||||
>>> ExtendedContext.multiply(Decimal('1.20'), Decimal('3'))
|
||||
Decimal('3.60')
|
||||
@@ -5200,19 +5205,19 @@ class Context(object):
|
||||
"""Returns a value equal to 'a' (rounded), having the exponent of 'b'.
|
||||
|
||||
The coefficient of the result is derived from that of the left-hand
|
||||
operand. It may be rounded using the current rounding setting (if the
|
||||
exponent is being increased), multiplied by a positive power of ten (if
|
||||
the exponent is being decreased), or is unchanged (if the exponent is
|
||||
already equal to that of the right-hand operand).
|
||||
operand. It may be rounded using the current rounding setting (if
|
||||
the exponent is being increased), multiplied by a positive power of
|
||||
ten (if the exponent is being decreased), or is unchanged (if the
|
||||
exponent is already equal to that of the right-hand operand).
|
||||
|
||||
Unlike other operations, if the length of the coefficient after the
|
||||
quantize operation would be greater than precision then an Invalid
|
||||
operation condition is raised. This guarantees that, unless there is
|
||||
an error condition, the exponent of the result of a quantize is always
|
||||
equal to that of the right-hand operand.
|
||||
operation condition is raised. This guarantees that, unless there
|
||||
is an error condition, the exponent of the result of a quantize is
|
||||
always equal to that of the right-hand operand.
|
||||
|
||||
Also unlike other operations, quantize will never raise Underflow, even
|
||||
if the result is subnormal and inexact.
|
||||
Also unlike other operations, quantize will never raise Underflow,
|
||||
even if the result is subnormal and inexact.
|
||||
|
||||
>>> ExtendedContext.quantize(Decimal('2.17'), Decimal('0.001'))
|
||||
Decimal('2.170')
|
||||
@@ -5266,13 +5271,13 @@ class Context(object):
|
||||
"""Returns the remainder from integer division.
|
||||
|
||||
The result is the residue of the dividend after the operation of
|
||||
calculating integer division as described for divide-integer, rounded
|
||||
to precision digits if necessary. The sign of the result, if
|
||||
non-zero, is the same as that of the original dividend.
|
||||
calculating integer division as described for divide-integer,
|
||||
rounded to precision digits if necessary. The sign of the result,
|
||||
if non-zero, is the same as that of the original dividend.
|
||||
|
||||
This operation will fail under the same conditions as integer division
|
||||
(that is, if integer division on the same two operands would fail, the
|
||||
remainder cannot be calculated).
|
||||
This operation will fail under the same conditions as integer
|
||||
division (that is, if integer division on the same two operands
|
||||
would fail, the remainder cannot be calculated).
|
||||
|
||||
>>> ExtendedContext.remainder(Decimal('2.1'), Decimal('3'))
|
||||
Decimal('2.1')
|
||||
@@ -5306,9 +5311,9 @@ class Context(object):
|
||||
is chosen). If the result is equal to 0 then its sign will be the
|
||||
sign of a.
|
||||
|
||||
This operation will fail under the same conditions as integer division
|
||||
(that is, if integer division on the same two operands would fail, the
|
||||
remainder cannot be calculated).
|
||||
This operation will fail under the same conditions as integer
|
||||
division (that is, if integer division on the same two operands
|
||||
would fail, the remainder cannot be calculated).
|
||||
|
||||
>>> ExtendedContext.remainder_near(Decimal('2.1'), Decimal('3'))
|
||||
Decimal('-0.9')
|
||||
@@ -5366,8 +5371,8 @@ class Context(object):
|
||||
def same_quantum(self, a, b):
|
||||
"""Returns True if the two operands have the same exponent.
|
||||
|
||||
The result is never affected by either the sign or the coefficient of
|
||||
either operand.
|
||||
The result is never affected by either the sign or the coefficient
|
||||
of either operand.
|
||||
|
||||
>>> ExtendedContext.same_quantum(Decimal('2.17'), Decimal('0.001'))
|
||||
False
|
||||
@@ -5439,8 +5444,8 @@ class Context(object):
|
||||
def sqrt(self, a):
|
||||
"""Square root of a non-negative number to context precision.
|
||||
|
||||
If the result must be inexact, it is rounded using the round-half-even
|
||||
algorithm.
|
||||
If the result must be inexact, it is rounded using the
|
||||
round-half-even algorithm.
|
||||
|
||||
>>> ExtendedContext.sqrt(Decimal('0'))
|
||||
Decimal('0')
|
||||
|
||||
Vendored
+69
-59
@@ -83,27 +83,28 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
||||
wrapped. (If a file descriptor is given, it is closed when the
|
||||
returned I/O object is closed, unless closefd is set to False.)
|
||||
|
||||
mode is an optional string that specifies the mode in which the file is
|
||||
opened. It defaults to 'r' which means open for reading in text mode. Other
|
||||
common values are 'w' for writing (truncating the file if it already
|
||||
exists), 'x' for exclusive creation of a new file, and 'a' for appending
|
||||
(which on some Unix systems, means that all writes append to the end of the
|
||||
file regardless of the current seek position). In text mode, if encoding is
|
||||
not specified the encoding used is platform dependent. (For reading and
|
||||
writing raw bytes use binary mode and leave encoding unspecified.) The
|
||||
available modes are:
|
||||
mode is an optional string that specifies the mode in which the file
|
||||
is opened. It defaults to 'r' which means open for reading in text
|
||||
mode. Other common values are 'w' for writing (truncating the file if
|
||||
it already exists), 'x' for exclusive creation of a new file, and
|
||||
'a' for appending (which on some Unix systems, means that all writes
|
||||
append to the end of the file regardless of the current seek position).
|
||||
In text mode, if encoding is not specified the encoding used is platform
|
||||
dependent. (For reading and writing raw bytes use binary mode and leave
|
||||
encoding unspecified.) The available modes are:
|
||||
|
||||
========= ===============================================================
|
||||
========= ==========================================================
|
||||
Character Meaning
|
||||
--------- ---------------------------------------------------------------
|
||||
--------- ----------------------------------------------------------
|
||||
'r' open for reading (default)
|
||||
'w' open for writing, truncating the file first
|
||||
'x' create a new file and open it for writing
|
||||
'a' open for writing, appending to the end of the file if it exists
|
||||
'a' open for writing, appending to the end of the file if it
|
||||
exists
|
||||
'b' binary mode
|
||||
't' text mode (default)
|
||||
'+' open a disk file for updating (reading and writing)
|
||||
========= ===============================================================
|
||||
========= ==========================================================
|
||||
|
||||
The default mode is 'rt' (open for reading text). For binary random
|
||||
access, the mode 'w+b' opens and truncates the file to 0 bytes, while
|
||||
@@ -111,23 +112,23 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
||||
raises an `FileExistsError` if the file already exists.
|
||||
|
||||
Python distinguishes between files opened in binary and text modes,
|
||||
even when the underlying operating system doesn't. Files opened in
|
||||
even when the underlying operating system doesn't. Files opened in
|
||||
binary mode (appending 'b' to the mode argument) return contents as
|
||||
bytes objects without any decoding. In text mode (the default, or when
|
||||
bytes objects without any decoding. In text mode (the default, or when
|
||||
't' is appended to the mode argument), the contents of the file are
|
||||
returned as strings, the bytes having been first decoded using a
|
||||
platform-dependent encoding or using the specified encoding if given.
|
||||
|
||||
buffering is an optional integer used to set the buffering policy.
|
||||
Pass 0 to switch buffering off (only allowed in binary mode), 1 to select
|
||||
line buffering (only usable in text mode), and an integer > 1 to indicate
|
||||
the size of a fixed-size chunk buffer. When no buffering argument is
|
||||
given, the default buffering policy works as follows:
|
||||
Pass 0 to switch buffering off (only allowed in binary mode), 1 to
|
||||
select line buffering (only usable in text mode), and an integer > 1 to
|
||||
indicate the size of a fixed-size chunk buffer. When no buffering
|
||||
argument is given, the default buffering policy works as follows:
|
||||
|
||||
* Binary files are buffered in fixed-size chunks; the size of the buffer
|
||||
is max(min(blocksize, 8 MiB), DEFAULT_BUFFER_SIZE)
|
||||
when the device block size is available.
|
||||
On most systems, the buffer will typically be 128 kilobytes long.
|
||||
* Binary files are buffered in fixed-size chunks; the size of the buffer
|
||||
is max(min(blocksize, 8 MiB), DEFAULT_BUFFER_SIZE) when the device
|
||||
block size is available.
|
||||
On most systems, the buffer will typically be 128 kilobytes long.
|
||||
|
||||
* "Interactive" text files (files for which isatty() returns True)
|
||||
use line buffering. Other text files use the policy described above
|
||||
@@ -147,8 +148,8 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
||||
encoding error strings.
|
||||
|
||||
newline is a string controlling how universal newlines works (it only
|
||||
applies to text mode). It can be None, '', '\n', '\r', and '\r\n'. It works
|
||||
as follows:
|
||||
applies to text mode). It can be None, '', '\n', '\r', and '\r\n'. It
|
||||
works as follows:
|
||||
|
||||
* On input, if newline is None, universal newlines mode is
|
||||
enabled. Lines in the input can end in '\n', '\r', or '\r\n', and
|
||||
@@ -164,17 +165,17 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
||||
other legal values, any '\n' characters written are translated to
|
||||
the given string.
|
||||
|
||||
closedfd is a bool. If closefd is False, the underlying file descriptor will
|
||||
be kept open when the file is closed. This does not work when a file name is
|
||||
given and must be True in that case.
|
||||
closedfd is a bool. If closefd is False, the underlying file descriptor
|
||||
will be kept open when the file is closed. This does not work when
|
||||
a file name is given and must be True in that case.
|
||||
|
||||
The newly created file is non-inheritable.
|
||||
|
||||
A custom opener can be used by passing a callable as *opener*. The
|
||||
underlying file descriptor for the file object is then obtained by calling
|
||||
*opener* with (*file*, *flags*). *opener* must return an open file
|
||||
descriptor (passing os.open as *opener* results in functionality similar to
|
||||
passing None).
|
||||
underlying file descriptor for the file object is then obtained by
|
||||
calling *opener* with (*file*, *flags*). *opener* must return an open
|
||||
file descriptor (passing os.open as *opener* results in functionality
|
||||
similar to passing None).
|
||||
|
||||
open() returns a file object whose type depends on the mode, and
|
||||
through which the standard file operations such as reading and writing
|
||||
@@ -351,10 +352,12 @@ class IOBase(metaclass=abc.ABCMeta):
|
||||
interpreted relative to the position indicated by whence. Values
|
||||
for whence are ints:
|
||||
|
||||
* 0 -- start of stream (the default); offset should be zero or positive
|
||||
* 0 -- start of stream (the default); offset should be zero or
|
||||
positive
|
||||
* 1 -- current stream position; offset may be negative
|
||||
* 2 -- end of stream; offset is usually negative
|
||||
Some operating systems / file systems could provide additional values.
|
||||
Some operating systems / file systems could provide additional
|
||||
values.
|
||||
|
||||
Return an int indicating the new absolute position.
|
||||
"""
|
||||
@@ -367,8 +370,8 @@ class IOBase(metaclass=abc.ABCMeta):
|
||||
def truncate(self, pos=None):
|
||||
"""Truncate file to size bytes.
|
||||
|
||||
Size defaults to the current IO position as reported by tell(). Return
|
||||
the new size.
|
||||
Size defaults to the current IO position as reported by tell().
|
||||
Return the new size.
|
||||
"""
|
||||
self._unsupported("truncate")
|
||||
|
||||
@@ -492,7 +495,8 @@ class IOBase(metaclass=abc.ABCMeta):
|
||||
def fileno(self):
|
||||
"""Returns underlying file descriptor (an int) if one exists.
|
||||
|
||||
An OSError is raised if the IO object does not use a file descriptor.
|
||||
An OSError is raised if the IO object does not use a file
|
||||
descriptor.
|
||||
"""
|
||||
self._unsupported("fileno")
|
||||
|
||||
@@ -1485,17 +1489,22 @@ class FileIO(RawIOBase):
|
||||
_closefd = True
|
||||
|
||||
def __init__(self, file, mode='r', closefd=True, opener=None):
|
||||
"""Open a file. The mode can be 'r' (default), 'w', 'x' or 'a' for reading,
|
||||
writing, exclusive creation or appending. The file will be created if it
|
||||
doesn't exist when opened for writing or appending; it will be truncated
|
||||
when opened for writing. A FileExistsError will be raised if it already
|
||||
exists when opened for creating. Opening a file for creating implies
|
||||
writing so this mode behaves in a similar way to 'w'. Add a '+' to the mode
|
||||
to allow simultaneous reading and writing. A custom opener can be used by
|
||||
passing a callable as *opener*. The underlying file descriptor for the file
|
||||
object is then obtained by calling opener with (*name*, *flags*).
|
||||
*opener* must return an open file descriptor (passing os.open as *opener*
|
||||
results in functionality similar to passing None).
|
||||
"""Open a file.
|
||||
|
||||
The mode can be 'r' (default), 'w', 'x' or 'a' for reading,
|
||||
writing, exclusive creation or appending. The file will be created
|
||||
if it doesn't exist when opened for writing or appending; it will be
|
||||
truncated when opened for writing. A FileExistsError will be raised
|
||||
if it already exists when opened for creating. Opening a file for
|
||||
creating implies writing so this mode behaves in a similar way to
|
||||
'w'. Add a '+' to the mode to allow simultaneous reading and
|
||||
writing.
|
||||
|
||||
A custom opener can be used by passing a callable as *opener*.
|
||||
The underlying file descriptor for the file object is then obtained
|
||||
by calling opener with (*name*, *flags*). *opener* must return
|
||||
an open file descriptor (passing os.open as *opener* results in
|
||||
functionality similar to passing None).
|
||||
"""
|
||||
if self._fd >= 0:
|
||||
# Have to close the existing file first.
|
||||
@@ -1733,8 +1742,8 @@ class FileIO(RawIOBase):
|
||||
"""Write bytes b to file, return number written.
|
||||
|
||||
Only makes one system call, so not all of the data may be written.
|
||||
The number of bytes actually written is returned. In non-blocking mode,
|
||||
returns None if the write would block.
|
||||
The number of bytes actually written is returned. In non-blocking
|
||||
mode, returns None if the write would block.
|
||||
"""
|
||||
self._checkClosed()
|
||||
self._checkWritable()
|
||||
@@ -1746,11 +1755,12 @@ class FileIO(RawIOBase):
|
||||
def seek(self, pos, whence=SEEK_SET):
|
||||
"""Move to new file position.
|
||||
|
||||
Argument offset is a byte count. Optional argument whence defaults to
|
||||
SEEK_SET or 0 (offset from start of file, offset should be >= 0); other values
|
||||
are SEEK_CUR or 1 (move relative to current position, positive or negative),
|
||||
and SEEK_END or 2 (move relative to end of file, usually negative, although
|
||||
many platforms allow seeking beyond the end of a file).
|
||||
Argument offset is a byte count. Optional argument whence defaults
|
||||
to SEEK_SET or 0 (offset from start of file, offset should be >= 0);
|
||||
other values are SEEK_CUR or 1 (move relative to current position,
|
||||
positive or negative), and SEEK_END or 2 (move relative to end of
|
||||
file, usually negative, although many platforms allow seeking beyond
|
||||
the end of a file).
|
||||
|
||||
Note that not all file objects are seekable.
|
||||
"""
|
||||
@@ -1783,8 +1793,8 @@ class FileIO(RawIOBase):
|
||||
def close(self):
|
||||
"""Close the file.
|
||||
|
||||
A closed file cannot be used for further I/O operations. close() may be
|
||||
called more than once without error.
|
||||
A closed file cannot be used for further I/O operations.
|
||||
close() may be called more than once without error.
|
||||
"""
|
||||
if not self.closed:
|
||||
self._stat_atopen = None
|
||||
@@ -1879,8 +1889,8 @@ class TextIOBase(IOBase):
|
||||
def read(self, size=-1):
|
||||
"""Read at most size characters from stream, where size is an int.
|
||||
|
||||
Read from underlying buffer until we have size characters or we hit EOF.
|
||||
If size is negative or omitted, read until EOF.
|
||||
Read from underlying buffer until we have size characters or we hit
|
||||
EOF. If size is negative or omitted, read until EOF.
|
||||
|
||||
Returns a string.
|
||||
"""
|
||||
|
||||
Vendored
+18
-19
@@ -483,10 +483,10 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||
If factory is None the default task factory will be set.
|
||||
|
||||
If factory is a callable, it should have a signature matching
|
||||
'(loop, coro, **kwargs)', where 'loop' will be a reference to the active
|
||||
event loop, 'coro' will be a coroutine object, and **kwargs will be
|
||||
arbitrary keyword arguments that should be passed on to Task.
|
||||
The callable must return a Task.
|
||||
'(loop, coro, **kwargs)', where 'loop' will be a reference to the
|
||||
active event loop, 'coro' will be a coroutine object, and **kwargs
|
||||
will be arbitrary keyword arguments that should be passed on to
|
||||
Task. The callable must return a Task.
|
||||
"""
|
||||
if factory is not None and not callable(factory):
|
||||
raise TypeError('task factory must be a callable or None')
|
||||
@@ -721,8 +721,8 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||
def stop(self):
|
||||
"""Stop running the event loop.
|
||||
|
||||
Every callback already scheduled will still run. This simply informs
|
||||
run_forever to stop looping after a complete iteration.
|
||||
Every callback already scheduled will still run. This simply
|
||||
informs run_forever to stop looping after a complete iteration.
|
||||
"""
|
||||
self._stopping = True
|
||||
|
||||
@@ -963,7 +963,7 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||
f"and file {file!r} combination")
|
||||
|
||||
async def _sock_sendfile_fallback(self, sock, file, offset, count):
|
||||
if offset:
|
||||
if hasattr(file, 'seek'):
|
||||
file.seek(offset)
|
||||
blocksize = (
|
||||
min(count, constants.SENDFILE_FALLBACK_READBUFFER_SIZE)
|
||||
@@ -1070,12 +1070,12 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||
|
||||
Create a streaming transport connection to a given internet host and
|
||||
port: socket family AF_INET or socket.AF_INET6 depending on host (or
|
||||
family if specified), socket type SOCK_STREAM. protocol_factory must be
|
||||
a callable returning a protocol instance.
|
||||
family if specified), socket type SOCK_STREAM. protocol_factory must
|
||||
be a callable returning a protocol instance.
|
||||
|
||||
This method is a coroutine which will try to establish the connection
|
||||
in the background. When successful, the coroutine returns a
|
||||
(transport, protocol) pair.
|
||||
This method is a coroutine which will try to establish the
|
||||
connection in the background. When successful, the coroutine
|
||||
returns a (transport, protocol) pair.
|
||||
"""
|
||||
if server_hostname is not None and not ssl:
|
||||
raise ValueError('server_hostname is only meaningful with ssl')
|
||||
@@ -1278,7 +1278,6 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||
raise RuntimeError(
|
||||
f"fallback is disabled and native sendfile is not "
|
||||
f"supported for transport {transport!r}")
|
||||
|
||||
return await self._sendfile_fallback(transport, file,
|
||||
offset, count)
|
||||
|
||||
@@ -1287,7 +1286,7 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||
"sendfile syscall is not supported")
|
||||
|
||||
async def _sendfile_fallback(self, transp, file, offset, count):
|
||||
if offset:
|
||||
if hasattr(file, 'seek'):
|
||||
file.seek(offset)
|
||||
blocksize = min(count, 16384) if count else 16384
|
||||
buf = bytearray(blocksize)
|
||||
@@ -1543,11 +1542,11 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||
The host parameter can be a string, in that case the TCP server is
|
||||
bound to host and port.
|
||||
|
||||
The host parameter can also be a sequence of strings and in that case
|
||||
the TCP server is bound to all hosts of the sequence. If a host
|
||||
appears multiple times (possibly indirectly e.g. when hostnames
|
||||
resolve to the same IP address), the server is only bound once to that
|
||||
host.
|
||||
The host parameter can also be a sequence of strings and in that
|
||||
case the TCP server is bound to all hosts of the sequence. If
|
||||
a host appears multiple times (possibly indirectly e.g. when
|
||||
hostnames resolve to the same IP address), the server is only bound
|
||||
once to that host.
|
||||
|
||||
Return a Server object which can be used to stop the service.
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -18,8 +18,8 @@ _is_coroutine = object()
|
||||
|
||||
|
||||
def iscoroutinefunction(func):
|
||||
import warnings
|
||||
"""Return True if func is a decorated coroutine function."""
|
||||
import warnings
|
||||
warnings._deprecated("asyncio.iscoroutinefunction",
|
||||
f"{warnings._DEPRECATED_MSG}; "
|
||||
"use inspect.iscoroutinefunction() instead",
|
||||
|
||||
Vendored
+12
-9
@@ -374,8 +374,8 @@ class AbstractEventLoop:
|
||||
|
||||
If host is an empty string or None all interfaces are assumed
|
||||
and a list of multiple sockets will be returned (most likely
|
||||
one for IPv4 and another one for IPv6). The host parameter can also be
|
||||
a sequence (e.g. list) of hosts to bind to.
|
||||
one for IPv4 and another one for IPv6). The host parameter can also
|
||||
be a sequence (e.g. list) of hosts to bind to.
|
||||
|
||||
family can be set to either AF_INET or AF_INET6 to force the
|
||||
socket to use IPv4 or IPv6. If not set it will be determined
|
||||
@@ -415,8 +415,9 @@ class AbstractEventLoop:
|
||||
|
||||
start_serving set to True (default) causes the created server
|
||||
to start accepting connections immediately. When set to False,
|
||||
the user should await Server.start_serving() or Server.serve_forever()
|
||||
to make the server to start accepting connections.
|
||||
the user should await Server.start_serving() or
|
||||
Server.serve_forever() to make the server to start accepting
|
||||
connections.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -479,8 +480,9 @@ class AbstractEventLoop:
|
||||
|
||||
start_serving set to True (default) causes the created server
|
||||
to start accepting connections immediately. When set to False,
|
||||
the user should await Server.start_serving() or Server.serve_forever()
|
||||
to make the server to start accepting connections.
|
||||
the user should await Server.start_serving() or
|
||||
Server.serve_forever() to make the server to start accepting
|
||||
connections.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -511,8 +513,8 @@ class AbstractEventLoop:
|
||||
|
||||
protocol_factory must be a callable returning a protocol instance.
|
||||
|
||||
socket family AF_INET, socket.AF_INET6 or socket.AF_UNIX depending on
|
||||
host (or family if specified), socket type SOCK_DGRAM.
|
||||
socket family AF_INET, socket.AF_INET6 or socket.AF_UNIX depending
|
||||
on host (or family if specified), socket type SOCK_DGRAM.
|
||||
|
||||
reuse_address tells the kernel to reuse a local socket in
|
||||
TIME_WAIT state, without waiting for its natural timeout to
|
||||
@@ -552,7 +554,8 @@ class AbstractEventLoop:
|
||||
async def connect_write_pipe(self, protocol_factory, pipe):
|
||||
"""Register write pipe in event loop.
|
||||
|
||||
protocol_factory should instantiate object with BaseProtocol interface.
|
||||
protocol_factory should instantiate object with BaseProtocol
|
||||
interface.
|
||||
Pipe is file-like object already switched to nonblocking.
|
||||
Return pair (transport, protocol), where transport support
|
||||
WriteTransport interface."""
|
||||
|
||||
Vendored
+7
-7
@@ -112,13 +112,13 @@ def capture_call_graph(
|
||||
optional keyword-only 'depth' argument can be used to skip the specified
|
||||
number of frames from top of the stack.
|
||||
|
||||
If the optional keyword-only 'limit' argument is provided, each call stack
|
||||
in the resulting graph is truncated to include at most ``abs(limit)``
|
||||
entries. If 'limit' is positive, the entries left are the closest to
|
||||
the invocation point. If 'limit' is negative, the topmost entries are
|
||||
left. If 'limit' is omitted or None, all entries are present.
|
||||
If 'limit' is 0, the call stack is not captured at all, only
|
||||
"awaited by" information is present.
|
||||
If the optional keyword-only 'limit' argument is provided, each call
|
||||
stack in the resulting graph is truncated to include at most
|
||||
``abs(limit)`` entries. If 'limit' is positive, the entries left are
|
||||
the closest to the invocation point. If 'limit' is negative, the
|
||||
topmost entries are left. If 'limit' is omitted or None, all entries
|
||||
are present. If 'limit' is 0, the call stack is not captured at all,
|
||||
only "awaited by" information is present.
|
||||
"""
|
||||
|
||||
loop = events._get_running_loop()
|
||||
|
||||
Vendored
+9
-9
@@ -158,10 +158,10 @@ class Lock(_ContextManagerMixin, mixins._LoopBoundMixin):
|
||||
class Event(mixins._LoopBoundMixin):
|
||||
"""Asynchronous equivalent to threading.Event.
|
||||
|
||||
Class implementing event objects. An event manages a flag that can be set
|
||||
to true with the set() method and reset to false with the clear() method.
|
||||
The wait() method blocks until the flag is true. The flag is initially
|
||||
false.
|
||||
Class implementing event objects. An event manages a flag that can be
|
||||
set to true with the set() method and reset to false with the clear()
|
||||
method. The wait() method blocks until the flag is true. The flag is
|
||||
initially false.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@@ -353,9 +353,9 @@ class Semaphore(_ContextManagerMixin, mixins._LoopBoundMixin):
|
||||
"""A Semaphore implementation.
|
||||
|
||||
A semaphore manages an internal counter which is decremented by each
|
||||
acquire() call and incremented by each release() call. The counter
|
||||
can never go below zero; when acquire() finds that it is zero, it blocks,
|
||||
waiting until some other thread calls release().
|
||||
acquire() call and incremented by each release() call. The counter
|
||||
can never go below zero; when acquire() finds that it is zero, it
|
||||
blocks, waiting until some other thread calls release().
|
||||
|
||||
Semaphores also support the context management protocol.
|
||||
|
||||
@@ -511,8 +511,8 @@ class Barrier(mixins._LoopBoundMixin):
|
||||
async def wait(self):
|
||||
"""Wait for the barrier.
|
||||
|
||||
When the specified number of tasks have started waiting, they are all
|
||||
simultaneously awoken.
|
||||
When the specified number of tasks have started waiting, they are
|
||||
all simultaneously awoken.
|
||||
Returns an unique and individual index number from 0 to 'parties-1'.
|
||||
"""
|
||||
async with self._cond:
|
||||
|
||||
Vendored
+1
-2
@@ -756,8 +756,7 @@ class BaseProactorEventLoop(base_events.BaseEventLoop):
|
||||
offset += blocksize
|
||||
total_sent += blocksize
|
||||
finally:
|
||||
if total_sent > 0:
|
||||
file.seek(offset)
|
||||
file.seek(offset)
|
||||
|
||||
async def _sendfile_native(self, transp, file, offset, count):
|
||||
resume_reading = transp.is_reading()
|
||||
|
||||
Vendored
+19
-17
@@ -33,9 +33,9 @@ class QueueShutDown(Exception):
|
||||
class Queue(mixins._LoopBoundMixin):
|
||||
"""A queue, useful for coordinating producer and consumer coroutines.
|
||||
|
||||
If maxsize is less than or equal to zero, the queue size is infinite. If it
|
||||
is an integer greater than 0, then "await put()" will block when the
|
||||
queue reaches maxsize, until an item is removed by get().
|
||||
If maxsize is less than or equal to zero, the queue size is infinite.
|
||||
If it is an integer greater than 0, then "await put()" will block when
|
||||
the queue reaches maxsize, until an item is removed by get().
|
||||
|
||||
Unlike queue.Queue, you can reliably know this Queue's size
|
||||
with qsize(), since your single-threaded asyncio application won't be
|
||||
@@ -174,8 +174,8 @@ class Queue(mixins._LoopBoundMixin):
|
||||
|
||||
If queue is empty, wait until an item is available.
|
||||
|
||||
Raises QueueShutDown if the queue has been shut down and is empty, or
|
||||
if the queue has been shut down immediately.
|
||||
Raises QueueShutDown if the queue has been shut down and is empty,
|
||||
or if the queue has been shut down immediately.
|
||||
"""
|
||||
while self.empty():
|
||||
if self._is_shutdown and self.empty():
|
||||
@@ -203,10 +203,11 @@ class Queue(mixins._LoopBoundMixin):
|
||||
def get_nowait(self):
|
||||
"""Remove and return an item from the queue.
|
||||
|
||||
Return an item if one is immediately available, else raise QueueEmpty.
|
||||
Return an item if one is immediately available, else raise
|
||||
QueueEmpty.
|
||||
|
||||
Raises QueueShutDown if the queue has been shut down and is empty, or
|
||||
if the queue has been shut down immediately.
|
||||
Raises QueueShutDown if the queue has been shut down and is empty,
|
||||
or if the queue has been shut down immediately.
|
||||
"""
|
||||
if self.empty():
|
||||
if self._is_shutdown:
|
||||
@@ -223,12 +224,12 @@ class Queue(mixins._LoopBoundMixin):
|
||||
a subsequent call to task_done() tells the queue that the processing
|
||||
on the task is complete.
|
||||
|
||||
If a join() is currently blocking, it will resume when all items have
|
||||
been processed (meaning that a task_done() call was received for every
|
||||
item that had been put() into the queue).
|
||||
If a join() is currently blocking, it will resume when all items
|
||||
have been processed (meaning that a task_done() call was received
|
||||
for every item that had been put() into the queue).
|
||||
|
||||
Raises ValueError if called more times than there were items placed in
|
||||
the queue.
|
||||
Raises ValueError if called more times than there were items placed
|
||||
in the queue.
|
||||
"""
|
||||
if self._unfinished_tasks <= 0:
|
||||
raise ValueError('task_done() called too many times')
|
||||
@@ -239,10 +240,11 @@ class Queue(mixins._LoopBoundMixin):
|
||||
async def join(self):
|
||||
"""Block until all items in the queue have been gotten and processed.
|
||||
|
||||
The count of unfinished tasks goes up whenever an item is added to the
|
||||
queue. The count goes down whenever a consumer calls task_done() to
|
||||
indicate that the item was retrieved and all work on it is complete.
|
||||
When the count of unfinished tasks drops to zero, join() unblocks.
|
||||
The count of unfinished tasks goes up whenever an item is added to
|
||||
the queue. The count goes down whenever a consumer calls
|
||||
task_done() to indicate that the item was retrieved and all work on
|
||||
it is complete. When the count of unfinished tasks drops to zero,
|
||||
join() unblocks.
|
||||
"""
|
||||
if self._unfinished_tasks > 0:
|
||||
await self._finished.wait()
|
||||
|
||||
Vendored
+2
-1
@@ -35,7 +35,8 @@ class Runner:
|
||||
with asyncio.Runner(debug=True) as runner:
|
||||
runner.run(main())
|
||||
|
||||
The run() method can be called multiple times within the runner's context.
|
||||
The run() method can be called multiple times within the runner's
|
||||
context.
|
||||
|
||||
This can be useful for interactive console (e.g. IPython),
|
||||
unittest runners, console tools, -- everywhere when async code
|
||||
|
||||
Vendored
+17
-14
@@ -530,11 +530,12 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
|
||||
async def sock_sendall(self, sock, data):
|
||||
"""Send data to the socket.
|
||||
|
||||
The socket must be connected to a remote socket. This method continues
|
||||
to send data from data until either all data has been sent or an
|
||||
error occurs. None is returned on success. On error, an exception is
|
||||
raised, and there is no way to determine how much data, if any, was
|
||||
successfully processed by the receiving end of the connection.
|
||||
The socket must be connected to a remote socket. This method
|
||||
continues to send data from data until either all data has been
|
||||
sent or an error occurs. None is returned on success. On error,
|
||||
an exception is raised, and there is no way to determine how much
|
||||
data, if any, was successfully processed by the receiving end of
|
||||
the connection.
|
||||
"""
|
||||
base_events._check_ssl_socket(sock)
|
||||
if self._debug and sock.gettimeout() != 0:
|
||||
@@ -583,11 +584,12 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
|
||||
async def sock_sendto(self, sock, data, address):
|
||||
"""Send data to the socket.
|
||||
|
||||
The socket must be connected to a remote socket. This method continues
|
||||
to send data from data until either all data has been sent or an
|
||||
error occurs. None is returned on success. On error, an exception is
|
||||
raised, and there is no way to determine how much data, if any, was
|
||||
successfully processed by the receiving end of the connection.
|
||||
The socket must be connected to a remote socket. This method
|
||||
continues to send data from data until either all data has been
|
||||
sent or an error occurs. None is returned on success. On error,
|
||||
an exception is raised, and there is no way to determine how much
|
||||
data, if any, was successfully processed by the receiving end of
|
||||
the connection.
|
||||
"""
|
||||
base_events._check_ssl_socket(sock)
|
||||
if self._debug and sock.gettimeout() != 0:
|
||||
@@ -698,10 +700,11 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
|
||||
async def sock_accept(self, sock):
|
||||
"""Accept a connection.
|
||||
|
||||
The socket must be bound to an address and listening for connections.
|
||||
The return value is a pair (conn, address) where conn is a new socket
|
||||
object usable to send and receive data on the connection, and address
|
||||
is the address bound to the socket on the other end of the connection.
|
||||
The socket must be bound to an address and listening for
|
||||
connections. The return value is a pair (conn, address) where
|
||||
conn is a new socket object usable to send and receive data on the
|
||||
connection, and address is the address bound to the socket on the
|
||||
other end of the connection.
|
||||
"""
|
||||
base_events._check_ssl_socket(sock)
|
||||
if self._debug and sock.gettimeout() != 0:
|
||||
|
||||
Vendored
+7
-7
@@ -541,17 +541,17 @@ class StreamReader:
|
||||
self._waiter = None
|
||||
|
||||
async def readline(self):
|
||||
"""Read chunk of data from the stream until newline (b'\n') is found.
|
||||
r"""Read chunk of data from the stream until newline (b'\n') is found.
|
||||
|
||||
On success, return chunk that ends with newline. If only partial
|
||||
On success, return chunk that ends with newline. If only partial
|
||||
line can be read due to EOF, return incomplete line without
|
||||
terminating newline. When EOF was reached while no bytes read, empty
|
||||
bytes object is returned.
|
||||
terminating newline. When EOF was reached while no bytes read,
|
||||
empty bytes object is returned.
|
||||
|
||||
If limit is reached, ValueError will be raised. In that case, if
|
||||
If limit is reached, ValueError will be raised. In that case, if
|
||||
newline was found, complete line including newline will be removed
|
||||
from internal buffer. Else, internal buffer will be cleared. Limit is
|
||||
compared against part of the line without newline.
|
||||
from internal buffer. Else, internal buffer will be cleared.
|
||||
Limit is compared against part of the line without newline.
|
||||
|
||||
If stream was paused, this function will automatically resume it if
|
||||
needed.
|
||||
|
||||
Vendored
+30
-27
@@ -624,10 +624,11 @@ def as_completed(fs, *, timeout=None):
|
||||
Run the supplied awaitables concurrently. The returned object can be
|
||||
iterated to obtain the results of the awaitables as they finish.
|
||||
|
||||
The object returned can be iterated as an asynchronous iterator or a plain
|
||||
iterator. When asynchronous iteration is used, the originally-supplied
|
||||
awaitables are yielded if they are tasks or futures. This makes it easy to
|
||||
correlate previously-scheduled tasks with their results:
|
||||
The object returned can be iterated as an asynchronous iterator or
|
||||
a plain iterator. When asynchronous iteration is used, the
|
||||
originally-supplied awaitables are yielded if they are tasks or
|
||||
futures. This makes it easy to correlate previously-scheduled tasks
|
||||
with their results:
|
||||
|
||||
ipv4_connect = create_task(open_connection("127.0.0.1", 80))
|
||||
ipv6_connect = create_task(open_connection("::1", 80))
|
||||
@@ -643,26 +644,27 @@ def as_completed(fs, *, timeout=None):
|
||||
else:
|
||||
print("IPv4 connection established.")
|
||||
|
||||
During asynchronous iteration, implicitly-created tasks will be yielded for
|
||||
supplied awaitables that aren't tasks or futures.
|
||||
During asynchronous iteration, implicitly-created tasks will be
|
||||
yielded for supplied awaitables that aren't tasks or futures.
|
||||
|
||||
When used as a plain iterator, each iteration yields a new coroutine that
|
||||
returns the result or raises the exception of the next completed awaitable.
|
||||
This pattern is compatible with Python versions older than 3.13:
|
||||
When used as a plain iterator, each iteration yields a new coroutine
|
||||
that returns the result or raises the exception of the next completed
|
||||
awaitable. This pattern is compatible with Python versions older than
|
||||
3.13:
|
||||
|
||||
ipv4_connect = create_task(open_connection("127.0.0.1", 80))
|
||||
ipv6_connect = create_task(open_connection("::1", 80))
|
||||
tasks = [ipv4_connect, ipv6_connect]
|
||||
|
||||
for next_connect in as_completed(tasks):
|
||||
# next_connect is not one of the original task objects. It must be
|
||||
# awaited to obtain the result value or raise the exception of the
|
||||
# awaitable that finishes next.
|
||||
# next_connect is not one of the original task objects. It must
|
||||
# be awaited to obtain the result value or raise the exception
|
||||
# of the awaitable that finishes next.
|
||||
reader, writer = await next_connect
|
||||
|
||||
A TimeoutError is raised if the timeout occurs before all awaitables are
|
||||
done. This is raised by the async for loop during asynchronous iteration or
|
||||
by the coroutines yielded during plain iteration.
|
||||
A TimeoutError is raised if the timeout occurs before all awaitables
|
||||
are done. This is raised by the async for loop during asynchronous
|
||||
iteration or by the coroutines yielded during plain iteration.
|
||||
"""
|
||||
if inspect.isawaitable(fs):
|
||||
raise TypeError(
|
||||
@@ -1030,21 +1032,22 @@ def run_coroutine_threadsafe(coro, loop):
|
||||
def create_eager_task_factory(custom_task_constructor):
|
||||
"""Create a function suitable for use as a task factory on an event-loop.
|
||||
|
||||
Example usage:
|
||||
Example usage:
|
||||
|
||||
loop.set_task_factory(
|
||||
asyncio.create_eager_task_factory(my_task_constructor))
|
||||
loop.set_task_factory(
|
||||
asyncio.create_eager_task_factory(my_task_constructor))
|
||||
|
||||
Now, tasks created will be started immediately (rather than being first
|
||||
scheduled to an event loop). The constructor argument can be any callable
|
||||
that returns a Task-compatible object and has a signature compatible
|
||||
with `Task.__init__`; it must have the `eager_start` keyword argument.
|
||||
Now, tasks created will be started immediately (rather than being first
|
||||
scheduled to an event loop). The constructor argument can be any
|
||||
callable that returns a Task-compatible object and has a signature
|
||||
compatible with `Task.__init__`; it must have the `eager_start`
|
||||
keyword argument.
|
||||
|
||||
Most applications will use `Task` for `custom_task_constructor` and in
|
||||
this case there's no need to call `create_eager_task_factory()`
|
||||
directly. Instead the global `eager_task_factory` instance can be
|
||||
used. E.g. `loop.set_task_factory(asyncio.eager_task_factory)`.
|
||||
"""
|
||||
Most applications will use `Task` for `custom_task_constructor` and in
|
||||
this case there's no need to call `create_eager_task_factory()`
|
||||
directly. Instead the global `eager_task_factory` instance can be
|
||||
used. E.g. `loop.set_task_factory(asyncio.eager_task_factory)`.
|
||||
"""
|
||||
|
||||
def factory(loop, coro, *, eager_start=True, **kwargs):
|
||||
return custom_task_constructor(
|
||||
|
||||
Vendored
+2
-1
@@ -17,7 +17,8 @@ async def to_thread(func, /, *args, **kwargs):
|
||||
allowing context variables from the main thread to be accessed in the
|
||||
separate thread.
|
||||
|
||||
Return a coroutine that can be awaited to get the eventual result of *func*.
|
||||
Return a coroutine that can be awaited to get the eventual result of
|
||||
*func*.
|
||||
"""
|
||||
loop = events.get_running_loop()
|
||||
ctx = contextvars.copy_context()
|
||||
|
||||
Vendored
+2
-1
@@ -25,7 +25,8 @@ class _State(enum.Enum):
|
||||
class Timeout:
|
||||
"""Asynchronous context manager for cancelling overdue coroutines.
|
||||
|
||||
Use `timeout()` or `timeout_at()` rather than instantiating this class directly.
|
||||
Use `timeout()` or `timeout_at()` rather than instantiating this class
|
||||
directly.
|
||||
"""
|
||||
|
||||
def __init__(self, when: float | None) -> None:
|
||||
|
||||
Vendored
+11
-10
@@ -54,7 +54,8 @@ def waitstatus_to_exitcode(status):
|
||||
class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
|
||||
"""Unix event loop.
|
||||
|
||||
Adds signal handling and UNIX Domain Socket support to SelectorEventLoop.
|
||||
Adds signal handling and UNIX Domain Socket support to
|
||||
SelectorEventLoop.
|
||||
"""
|
||||
|
||||
def __init__(self, selector=None):
|
||||
@@ -384,12 +385,12 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
|
||||
# order to simplify the common case.
|
||||
self.remove_writer(registered_fd)
|
||||
if fut.cancelled():
|
||||
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
|
||||
self._sock_sendfile_update_filepos(fileno, offset)
|
||||
return
|
||||
if count:
|
||||
blocksize = count - total_sent
|
||||
if blocksize <= 0:
|
||||
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
|
||||
self._sock_sendfile_update_filepos(fileno, offset)
|
||||
fut.set_result(total_sent)
|
||||
return
|
||||
|
||||
@@ -423,20 +424,20 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
|
||||
# plain send().
|
||||
err = exceptions.SendfileNotAvailableError(
|
||||
"os.sendfile call failed")
|
||||
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
|
||||
self._sock_sendfile_update_filepos(fileno, offset)
|
||||
fut.set_exception(err)
|
||||
else:
|
||||
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
|
||||
self._sock_sendfile_update_filepos(fileno, offset)
|
||||
fut.set_exception(exc)
|
||||
except (SystemExit, KeyboardInterrupt):
|
||||
raise
|
||||
except BaseException as exc:
|
||||
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
|
||||
self._sock_sendfile_update_filepos(fileno, offset)
|
||||
fut.set_exception(exc)
|
||||
else:
|
||||
if sent == 0:
|
||||
# EOF
|
||||
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
|
||||
self._sock_sendfile_update_filepos(fileno, offset)
|
||||
fut.set_result(total_sent)
|
||||
else:
|
||||
offset += sent
|
||||
@@ -447,9 +448,9 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
|
||||
fd, sock, fileno,
|
||||
offset, count, blocksize, total_sent)
|
||||
|
||||
def _sock_sendfile_update_filepos(self, fileno, offset, total_sent):
|
||||
if total_sent > 0:
|
||||
os.lseek(fileno, offset, os.SEEK_SET)
|
||||
def _sock_sendfile_update_filepos(self, fileno, offset):
|
||||
# After this helper runs, the source fd's lseek pointer is at offset."
|
||||
os.lseek(fileno, offset, os.SEEK_SET)
|
||||
|
||||
def _sock_add_cancellation_callback(self, fut, sock):
|
||||
def cb(fut):
|
||||
|
||||
Vendored
+3
@@ -610,6 +610,9 @@ class IocpProactor:
|
||||
ov = _overlapped.Overlapped(NULL)
|
||||
offset_low = offset & 0xffff_ffff
|
||||
offset_high = (offset >> 32) & 0xffff_ffff
|
||||
# TransmitFile ignores OVERLAPPED.Offset for handles not opened with
|
||||
# FILE_FLAG_OVERLAPPED, so seek the CRT file pointer to match.
|
||||
file.seek(offset)
|
||||
ov.TransmitFile(sock.fileno(),
|
||||
msvcrt.get_osfhandle(file.fileno()),
|
||||
offset_low, offset_high,
|
||||
|
||||
Vendored
+22
-15
@@ -325,17 +325,20 @@ def a85encode(b, *, foldspaces=False, wrapcol=0, pad=False, adobe=False):
|
||||
|
||||
foldspaces is an optional flag that uses the special short sequence 'y'
|
||||
instead of 4 consecutive spaces (ASCII 0x20) as supported by 'btoa'. This
|
||||
feature is not supported by the "standard" Adobe encoding.
|
||||
feature is not supported by the standard encoding used in PDF.
|
||||
|
||||
wrapcol controls whether the output should have newline (b'\\n') characters
|
||||
added to it. If this is non-zero, each output line will be at most this
|
||||
many characters long, excluding the trailing newline.
|
||||
If wrapcol is non-zero, insert a newline (b'\\n') character after at most
|
||||
every wrapcol characters.
|
||||
|
||||
pad controls whether the input is padded to a multiple of 4 before
|
||||
encoding. Note that the btoa implementation always pads.
|
||||
pad controls whether zero-padding applied to the end of the input
|
||||
is fully retained in the output encoding, as done by btoa,
|
||||
producing an exact multiple of 5 bytes of output.
|
||||
|
||||
adobe controls whether the encoded byte sequence is framed with <~
|
||||
and ~>, as in a PostScript base-85 string literal. Note that
|
||||
while ASCII85Decode streams in PDF documents must be terminated
|
||||
with ~>, they must not use a leading <~.
|
||||
|
||||
adobe controls whether the encoded byte sequence is framed with <~ and ~>,
|
||||
which is used by the Adobe implementation.
|
||||
"""
|
||||
global _a85chars, _a85chars2
|
||||
# Delay the initialization of tables to not waste memory
|
||||
@@ -364,12 +367,14 @@ def a85encode(b, *, foldspaces=False, wrapcol=0, pad=False, adobe=False):
|
||||
def a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\v'):
|
||||
"""Decode the Ascii85 encoded bytes-like object or ASCII string b.
|
||||
|
||||
foldspaces is a flag that specifies whether the 'y' short sequence should be
|
||||
accepted as shorthand for 4 consecutive spaces (ASCII 0x20). This feature is
|
||||
not supported by the "standard" Adobe encoding.
|
||||
foldspaces is a flag that specifies whether the 'y' short sequence
|
||||
should be accepted as shorthand for 4 consecutive spaces (ASCII
|
||||
0x20). This feature is not supported by the standard Ascii85
|
||||
encoding used in PDF and PostScript.
|
||||
|
||||
adobe controls whether the input sequence is in Adobe Ascii85 format (i.e.
|
||||
is framed with <~ and ~>).
|
||||
adobe controls whether the <~ and ~> markers are present. While
|
||||
the leading <~ is not required, the input must end with ~>, or a
|
||||
ValueError is raised.
|
||||
|
||||
ignorechars should be a byte string containing characters to ignore from the
|
||||
input. This should only contain whitespace characters, and by default
|
||||
@@ -442,8 +447,10 @@ _b85dec = None
|
||||
def b85encode(b, pad=False):
|
||||
"""Encode bytes-like object b in base85 format and return a bytes object.
|
||||
|
||||
If pad is true, the input is padded with b'\\0' so its length is a multiple of
|
||||
4 bytes before encoding.
|
||||
The input is padded with b'\0' so its length is a multiple of 4
|
||||
bytes before encoding. If pad is true, all the resulting
|
||||
characters are retained in the output, which will always be a
|
||||
multiple of 5 bytes.
|
||||
"""
|
||||
global _b85chars, _b85chars2
|
||||
# Delay the initialization of tables to not waste memory
|
||||
|
||||
Vendored
+15
-13
@@ -61,8 +61,9 @@ class FrameInfo:
|
||||
def get_frame_info(frame_buffer):
|
||||
"""Get Zstandard frame information from a frame header.
|
||||
|
||||
*frame_buffer* is a bytes-like object. It should start from the beginning
|
||||
of a frame, and needs to include at least the frame header (6 to 18 bytes).
|
||||
*frame_buffer* is a bytes-like object. It should start from the
|
||||
beginning of a frame, and needs to include at least the frame header
|
||||
(6 to 18 bytes).
|
||||
|
||||
The returned FrameInfo object has two attributes.
|
||||
'decompressed_size' is the size in bytes of the data in the frame when
|
||||
@@ -103,16 +104,17 @@ def finalize_dict(zstd_dict, /, samples, dict_size, level):
|
||||
finalize *zstd_dict* by adding headers and statistics according to the
|
||||
Zstandard dictionary format.
|
||||
|
||||
You may compose an effective dictionary content by hand, which is used as
|
||||
basis dictionary, and use some samples to finalize a dictionary. The basis
|
||||
dictionary may be a "raw content" dictionary. See *is_raw* in ZstdDict.
|
||||
You may compose an effective dictionary content by hand, which is used
|
||||
as basis dictionary, and use some samples to finalize a dictionary. The
|
||||
basis dictionary may be a "raw content" dictionary. See *is_raw* in
|
||||
ZstdDict.
|
||||
|
||||
*samples* is an iterable of samples, where a sample is a bytes-like object
|
||||
representing a file.
|
||||
*samples* is an iterable of samples, where a sample is a bytes-like
|
||||
object representing a file.
|
||||
*dict_size* is the dictionary's maximum size, in bytes.
|
||||
*level* is the expected compression level. The statistics for each
|
||||
compression level differ, so tuning the dictionary to the compression level
|
||||
can provide improvements.
|
||||
compression level differ, so tuning the dictionary to the compression
|
||||
level can provide improvements.
|
||||
"""
|
||||
|
||||
if not isinstance(zstd_dict, ZstdDict):
|
||||
@@ -140,8 +142,8 @@ def compress(data, level=None, options=None, zstd_dict=None):
|
||||
COMPRESSION_LEVEL_DEFAULT ('3').
|
||||
*options* is a dict object that contains advanced compression
|
||||
parameters. See CompressionParameter for more on options.
|
||||
*zstd_dict* is a ZstdDict object, a pre-trained Zstandard dictionary. See
|
||||
the function train_dict for how to train a ZstdDict on sample data.
|
||||
*zstd_dict* is a ZstdDict object, a pre-trained Zstandard dictionary.
|
||||
See the function train_dict for how to train a ZstdDict on sample data.
|
||||
|
||||
For incremental compression, use a ZstdCompressor instead.
|
||||
"""
|
||||
@@ -152,8 +154,8 @@ def compress(data, level=None, options=None, zstd_dict=None):
|
||||
def decompress(data, zstd_dict=None, options=None):
|
||||
"""Decompress one or more frames of Zstandard compressed *data*.
|
||||
|
||||
*zstd_dict* is a ZstdDict object, a pre-trained Zstandard dictionary. See
|
||||
the function train_dict for how to train a ZstdDict on sample data.
|
||||
*zstd_dict* is a ZstdDict object, a pre-trained Zstandard dictionary.
|
||||
See the function train_dict for how to train a ZstdDict on sample data.
|
||||
*options* is a dict object that contains advanced compression
|
||||
parameters. See DecompressionParameter for more on options.
|
||||
|
||||
|
||||
Vendored
+19
-18
@@ -36,9 +36,9 @@ class ZstdFile(_streams.BaseStream):
|
||||
|
||||
*file* can be either an file-like object, or a file name to open.
|
||||
|
||||
*mode* can be 'r' for reading (default), 'w' for (over)writing, 'x' for
|
||||
creating exclusively, or 'a' for appending. These can equivalently be
|
||||
given as 'rb', 'wb', 'xb' and 'ab' respectively.
|
||||
*mode* can be 'r' for reading (default), 'w' for (over)writing, 'x'
|
||||
for creating exclusively, or 'a' for appending. These can
|
||||
equivalently be given as 'rb', 'wb', 'xb' and 'ab' respectively.
|
||||
|
||||
*level* is an optional int specifying the compression level to use,
|
||||
or COMPRESSION_LEVEL_DEFAULT if not given.
|
||||
@@ -296,26 +296,27 @@ def open(file, /, mode='rb', *, level=None, options=None, zstd_dict=None,
|
||||
encoding=None, errors=None, newline=None):
|
||||
"""Open a Zstandard compressed file in binary or text mode.
|
||||
|
||||
file can be either a file name (given as a str, bytes, or PathLike object),
|
||||
in which case the named file is opened, or it can be an existing file object
|
||||
to read from or write to.
|
||||
file can be either a file name (given as a str, bytes, or PathLike
|
||||
object), in which case the named file is opened, or it can be
|
||||
an existing file object to read from or write to.
|
||||
|
||||
The mode parameter can be 'r', 'rb' (default), 'w', 'wb', 'x', 'xb', 'a',
|
||||
'ab' for binary mode, or 'rt', 'wt', 'xt', 'at' for text mode.
|
||||
The mode parameter can be 'r', 'rb' (default), 'w', 'wb', 'x', 'xb',
|
||||
'a', 'ab' for binary mode, or 'rt', 'wt', 'xt', 'at' for text mode.
|
||||
|
||||
The level, options, and zstd_dict parameters specify the settings the same
|
||||
as ZstdFile.
|
||||
The level, options, and zstd_dict parameters specify the settings the
|
||||
same as ZstdFile.
|
||||
|
||||
When using read mode (decompression), the options parameter is a dict
|
||||
representing advanced decompression options. The level parameter is not
|
||||
supported in this case. When using write mode (compression), only one of
|
||||
level, an int representing the compression level, or options, a dict
|
||||
representing advanced compression options, may be passed. In both modes,
|
||||
zstd_dict is a ZstdDict instance containing a trained Zstandard dictionary.
|
||||
representing advanced decompression options. The level parameter is not
|
||||
supported in this case. When using write mode (compression), only one
|
||||
of level, an int representing the compression level, or options, a dict
|
||||
representing advanced compression options, may be passed. In both
|
||||
modes, zstd_dict is a ZstdDict instance containing a trained Zstandard
|
||||
dictionary.
|
||||
|
||||
For binary mode, this function is equivalent to the ZstdFile constructor:
|
||||
ZstdFile(filename, mode, ...). In this case, the encoding, errors and
|
||||
newline parameters must not be provided.
|
||||
For binary mode, this function is equivalent to the ZstdFile
|
||||
constructor: ZstdFile(filename, mode, ...). In this case, the encoding,
|
||||
errors and newline parameters must not be provided.
|
||||
|
||||
For text mode, an ZstdFile object is created, and wrapped in an
|
||||
io.TextIOWrapper instance with the specified encoding, error handling
|
||||
|
||||
Vendored
+10
@@ -1463,6 +1463,16 @@ def get_phrase(value):
|
||||
else:
|
||||
try:
|
||||
token, value = get_word(value)
|
||||
if (token[0].token_type == 'encoded-word'
|
||||
and phrase
|
||||
and phrase[-1].token_type == 'atom'
|
||||
and len(phrase[-1]) > 1
|
||||
and phrase[-1][-2].token_type == 'encoded-word'
|
||||
and phrase[-1][-1].token_type == 'cfws'
|
||||
and not phrase[-1][-1].comments
|
||||
):
|
||||
# linear ws between ews needs special handing...
|
||||
phrase[-1][-1] = EWWhiteSpaceTerminal(phrase[-1], 'fws')
|
||||
except errors.HeaderParseError:
|
||||
if value[0] in CFWS_LEADER:
|
||||
token, value = get_cfws(value)
|
||||
|
||||
Vendored
+1
-1
@@ -59,7 +59,7 @@ def _parsedate_tz(data):
|
||||
|
||||
The last (additional) element is the time zone offset in seconds, except if
|
||||
the timezone was specified as -0000. In that case the last element is
|
||||
None. This indicates a UTC timestamp that explicitly declaims knowledge of
|
||||
None. This indicates a UTC timestamp that explicitly disclaims knowledge of
|
||||
the source timezone, as opposed to a +0000 timestamp that indicates the
|
||||
source timezone really was UTC.
|
||||
|
||||
|
||||
Vendored
-2
@@ -93,8 +93,6 @@ ALIASES = {
|
||||
|
||||
# Map charsets to their Unicode codec strings.
|
||||
CODEC_MAP = {
|
||||
'gb2312': 'eucgb2312_cn',
|
||||
'big5': 'big5_tw',
|
||||
# Hack: We don't want *any* conversion for stuff marked us-ascii, as all
|
||||
# sorts of garbage might be sent to us in the guise of 7-bit us-ascii.
|
||||
# Let that stuff pass through without conversion to/from Unicode.
|
||||
|
||||
Vendored
+8
@@ -71,6 +71,10 @@ aliases = {
|
||||
|
||||
# cp1140 codec
|
||||
'1140' : 'cp1140',
|
||||
'cp01140' : 'cp1140',
|
||||
'csibm01140' : 'cp1140',
|
||||
'ebcdic_us_37_euro' : 'cp1140',
|
||||
'ibm01140' : 'cp1140',
|
||||
'ibm1140' : 'cp1140',
|
||||
|
||||
# cp1250 codec
|
||||
@@ -159,8 +163,12 @@ aliases = {
|
||||
|
||||
# cp858 codec
|
||||
'858' : 'cp858',
|
||||
'cp00858' : 'cp858',
|
||||
'csibm00858' : 'cp858',
|
||||
'csibm858' : 'cp858',
|
||||
'ibm00858' : 'cp858',
|
||||
'ibm858' : 'cp858',
|
||||
'pc_multilingual_850_euro' : 'cp858',
|
||||
|
||||
# cp860 codec
|
||||
'860' : 'cp860',
|
||||
|
||||
Vendored
+13
-12
@@ -678,9 +678,9 @@ class EnumType(type):
|
||||
"""
|
||||
Either returns an existing member, or creates a new enum class.
|
||||
|
||||
This method is used both when an enum class is given a value to match
|
||||
to an enumeration member (i.e. Color(3)) and for the functional API
|
||||
(i.e. Color = Enum('Color', names='RED GREEN BLUE')).
|
||||
This method is used both when an enum class is given a value to
|
||||
match to an enumeration member (i.e. Color(3)) and for the
|
||||
functional API (i.e. Color = Enum('Color', names='RED GREEN BLUE')).
|
||||
|
||||
The value lookup branch is chosen if the enum is final.
|
||||
|
||||
@@ -688,16 +688,17 @@ class EnumType(type):
|
||||
|
||||
`value` will be the name of the new class.
|
||||
|
||||
`names` should be either a string of white-space/comma delimited names
|
||||
(values will start at `start`), or an iterator/mapping of name, value pairs.
|
||||
`names` should be either a string of white-space/comma delimited
|
||||
names (values will start at `start`), or an iterator/mapping of
|
||||
name, value pairs.
|
||||
|
||||
`module` should be set to the module this class is being created in;
|
||||
if it is not set, an attempt to find that module will be made, but if
|
||||
it fails the class will not be picklable.
|
||||
if it is not set, an attempt to find that module will be made, but
|
||||
if it fails the class will not be picklable.
|
||||
|
||||
`qualname` should be set to the actual location this class can be found
|
||||
at in its module; by default it is set to the global scope. If this is
|
||||
not correct, unpickling will fail in some circumstances.
|
||||
`qualname` should be set to the actual location this class can be
|
||||
found at in its module; by default it is set to the global scope.
|
||||
If this is not correct, unpickling will fail in some circumstances.
|
||||
|
||||
`type`, if set, will be mixed in as the first base class.
|
||||
"""
|
||||
@@ -791,8 +792,8 @@ class EnumType(type):
|
||||
"""
|
||||
Returns a mapping of member name->value.
|
||||
|
||||
This mapping lists all enum members, including aliases. Note that this
|
||||
is a read-only view of the internal mapping.
|
||||
This mapping lists all enum members, including aliases. Note that
|
||||
this is a read-only view of the internal mapping.
|
||||
"""
|
||||
return MappingProxyType(cls._member_map_)
|
||||
|
||||
|
||||
Vendored
+10
-1
@@ -883,7 +883,16 @@ def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
|
||||
type = 'TYPE ' + type
|
||||
source.voidcmd(type)
|
||||
target.voidcmd(type)
|
||||
sourcehost, sourceport = parse227(source.sendcmd('PASV'))
|
||||
# Don't trust the IPv4 address the source server advertises in its PASV
|
||||
# reply: a malicious source could otherwise point the target's data
|
||||
# connection at an arbitrary host (SSRF). A caller that needs the old
|
||||
# behavior can set trust_server_pasv_ipv4_address on the source FTP
|
||||
# object. See FTP.makepasv(), which applies the same rule.
|
||||
untrusted_host, sourceport = parse227(source.sendcmd('PASV'))
|
||||
if source.trust_server_pasv_ipv4_address:
|
||||
sourcehost = untrusted_host
|
||||
else:
|
||||
sourcehost = source.sock.getpeername()[0]
|
||||
target.sendport(sourcehost, sourceport)
|
||||
# RFC 959: the user must "listen" [...] BEFORE sending the
|
||||
# transfer request.
|
||||
|
||||
Vendored
+6
-6
@@ -556,16 +556,16 @@ def lru_cache(maxsize=128, typed=False):
|
||||
If *maxsize* is set to None, the LRU features are disabled and the cache
|
||||
can grow without bound.
|
||||
|
||||
If *typed* is True, arguments of different types will be cached separately.
|
||||
For example, f(decimal.Decimal("3.0")) and f(3.0) will be treated as
|
||||
distinct calls with distinct results. Some types such as str and int may
|
||||
be cached separately even when typed is false.
|
||||
If *typed* is True, arguments of different types will be cached
|
||||
separately. For example, f(decimal.Decimal("3.0")) and f(3.0) will be
|
||||
treated as distinct calls with distinct results. Some types such as
|
||||
str and int may be cached separately even when typed is false.
|
||||
|
||||
Arguments to the cached function must be hashable.
|
||||
|
||||
View the cache statistics named tuple (hits, misses, maxsize, currsize)
|
||||
with f.cache_info(). Clear the cache and statistics with f.cache_clear().
|
||||
Access the underlying function with f.__wrapped__.
|
||||
with f.cache_info(). Clear the cache and statistics with
|
||||
f.cache_clear(). Access the underlying function with f.__wrapped__.
|
||||
|
||||
See: https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)
|
||||
|
||||
|
||||
Vendored
+17
-17
@@ -25,17 +25,17 @@ def glob(pathname, *, root_dir=None, dir_fd=None, recursive=False,
|
||||
The order of the returned list is undefined. Sort it if you need a
|
||||
particular order.
|
||||
|
||||
If `root_dir` is not None, it should be a path-like object specifying the
|
||||
root directory for searching. It has the same effect as changing the
|
||||
current directory before calling it (without actually
|
||||
changing it). If pathname is relative, the result will contain
|
||||
paths relative to `root_dir`.
|
||||
If `root_dir` is not None, it should be a path-like object specifying
|
||||
the root directory for searching. It has the same effect as changing
|
||||
the current directory before calling it (without actually changing it).
|
||||
If pathname is relative, the result will contain paths relative to
|
||||
`root_dir`.
|
||||
|
||||
If `dir_fd` is not None, it should be a file descriptor referring to a
|
||||
directory, and paths will then be relative to that directory.
|
||||
|
||||
If `include_hidden` is true, the patterns '*', '?', '**' will match hidden
|
||||
directories.
|
||||
If `include_hidden` is true, the patterns '*', '?', '**' will match
|
||||
hidden directories.
|
||||
|
||||
If `recursive` is true, the pattern '**' will match any files and
|
||||
zero or more directories and subdirectories.
|
||||
@@ -56,16 +56,16 @@ def iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False,
|
||||
particular order.
|
||||
|
||||
If `root_dir` is not None, it should be a path-like object specifying
|
||||
the root directory for searching. It has the same effect as changing
|
||||
the current directory before calling it (without actually
|
||||
changing it). If pathname is relative, the result will contain
|
||||
paths relative to `root_dir`.
|
||||
the root directory for searching. It has the same effect as changing
|
||||
the current directory before calling it (without actually changing it).
|
||||
If pathname is relative, the result will contain paths relative to
|
||||
`root_dir`.
|
||||
|
||||
If `dir_fd` is not None, it should be a file descriptor referring to a
|
||||
directory, and paths will then be relative to that directory.
|
||||
|
||||
If `include_hidden` is true, the patterns '*', '?', '**' will match hidden
|
||||
directories.
|
||||
If `include_hidden` is true, the patterns '*', '?', '**' will match
|
||||
hidden directories.
|
||||
|
||||
If `recursive` is true, the pattern '**' will match any files and
|
||||
zero or more directories and subdirectories.
|
||||
@@ -294,15 +294,15 @@ _no_recurse_symlinks = object()
|
||||
def translate(pat, *, recursive=False, include_hidden=False, seps=None):
|
||||
"""Translate a pathname with shell wildcards to a regular expression.
|
||||
|
||||
If `recursive` is true, the pattern segment '**' will match any number of
|
||||
path segments.
|
||||
If `recursive` is true, the pattern segment '**' will match any number
|
||||
of path segments.
|
||||
|
||||
If `include_hidden` is true, wildcards can match path segments beginning
|
||||
with a dot ('.').
|
||||
|
||||
If a sequence of separator characters is given to `seps`, they will be
|
||||
used to split the pattern into segments and match path separators. If not
|
||||
given, os.path.sep and os.path.altsep (where available) are used.
|
||||
used to split the pattern into segments and match path separators. If
|
||||
not given, os.path.sep and os.path.altsep (where available) are used.
|
||||
"""
|
||||
if not seps:
|
||||
if os.path.altsep:
|
||||
|
||||
Vendored
+31
-29
@@ -34,16 +34,16 @@ def open(filename, mode="rb", compresslevel=_COMPRESS_LEVEL_BEST,
|
||||
encoding=None, errors=None, newline=None):
|
||||
"""Open a gzip-compressed file in binary or text mode.
|
||||
|
||||
The filename argument can be an actual filename (a str or bytes object), or
|
||||
an existing file object to read from or write to.
|
||||
The filename argument can be an actual filename (a str or bytes object),
|
||||
or an existing file object to read from or write to.
|
||||
|
||||
The mode argument can be "r", "rb", "w", "wb", "x", "xb", "a" or "ab" for
|
||||
binary mode, or "rt", "wt", "xt" or "at" for text mode. The default mode is
|
||||
"rb", and the default compresslevel is 9.
|
||||
The mode argument can be "r", "rb", "w", "wb", "x", "xb", "a" or "ab"
|
||||
for binary mode, or "rt", "wt", "xt" or "at" for text mode. The default
|
||||
mode is "rb", and the default compresslevel is 9.
|
||||
|
||||
For binary mode, this function is equivalent to the GzipFile constructor:
|
||||
GzipFile(filename, mode, compresslevel). In this case, the encoding, errors
|
||||
and newline arguments must not be provided.
|
||||
For binary mode, this function is equivalent to the GzipFile
|
||||
constructor: GzipFile(filename, mode, compresslevel). In this case,
|
||||
the encoding, errors and newline arguments must not be provided.
|
||||
|
||||
For text mode, a GzipFile object is created, and wrapped in an
|
||||
io.TextIOWrapper instance with the specified encoding, error handling
|
||||
@@ -148,8 +148,8 @@ class GzipFile(_streams.BaseStream):
|
||||
"""The GzipFile class simulates most of the methods of a file object with
|
||||
the exception of the truncate() method.
|
||||
|
||||
This class only supports opening files in binary mode. If you need to open a
|
||||
compressed file in text mode, use the gzip.open() function.
|
||||
This class only supports opening files in binary mode. If you need to
|
||||
open a compressed file in text mode, use the gzip.open() function.
|
||||
|
||||
"""
|
||||
|
||||
@@ -165,31 +165,33 @@ class GzipFile(_streams.BaseStream):
|
||||
non-trivial value.
|
||||
|
||||
The new class instance is based on fileobj, which can be a regular
|
||||
file, an io.BytesIO object, or any other object which simulates a file.
|
||||
It defaults to None, in which case filename is opened to provide
|
||||
a file object.
|
||||
file, an io.BytesIO object, or any other object which simulates
|
||||
a file. It defaults to None, in which case filename is opened to
|
||||
provide a file object.
|
||||
|
||||
When fileobj is not None, the filename argument is only used to be
|
||||
included in the gzip file header, which may include the original
|
||||
filename of the uncompressed file. It defaults to the filename of
|
||||
fileobj, if discernible; otherwise, it defaults to the empty string,
|
||||
and in this case the original filename is not included in the header.
|
||||
and in this case the original filename is not included in the
|
||||
header.
|
||||
|
||||
The mode argument can be any of 'r', 'rb', 'a', 'ab', 'w', 'wb', 'x', or
|
||||
'xb' depending on whether the file will be read or written. The default
|
||||
is the mode of fileobj if discernible; otherwise, the default is 'rb'.
|
||||
A mode of 'r' is equivalent to one of 'rb', and similarly for 'w' and
|
||||
'wb', 'a' and 'ab', and 'x' and 'xb'.
|
||||
The mode argument can be any of 'r', 'rb', 'a', 'ab', 'w', 'wb',
|
||||
'x', or 'xb' depending on whether the file will be read or written.
|
||||
The default is the mode of fileobj if discernible; otherwise, the
|
||||
default is 'rb'. A mode of 'r' is equivalent to one of 'rb', and
|
||||
similarly for 'w' and 'wb', 'a' and 'ab', and 'x' and 'xb'.
|
||||
|
||||
The compresslevel argument is an integer from 0 to 9 controlling the
|
||||
level of compression; 1 is fastest and produces the least compression,
|
||||
and 9 is slowest and produces the most compression. 0 is no compression
|
||||
at all. The default is 9.
|
||||
The compresslevel argument is an integer from 0 to 9 controlling
|
||||
the level of compression; 1 is fastest and produces the least
|
||||
compression, and 9 is slowest and produces the most compression.
|
||||
0 is no compression at all. The default is 9.
|
||||
|
||||
The optional mtime argument is the timestamp requested by gzip. The time
|
||||
is in Unix format, i.e., seconds since 00:00:00 UTC, January 1, 1970.
|
||||
If mtime is omitted or None, the current time is used. Use mtime = 0
|
||||
to generate a compressed stream that does not depend on creation time.
|
||||
The optional mtime argument is the timestamp requested by gzip.
|
||||
The time is in Unix format, i.e., seconds since 00:00:00 UTC,
|
||||
January 1, 1970. If mtime is omitted or None, the current time
|
||||
is used. Use mtime = 0 to generate a compressed stream that does
|
||||
not depend on creation time.
|
||||
|
||||
"""
|
||||
|
||||
@@ -575,10 +577,10 @@ class _GzipReader(_streams.DecompressReader):
|
||||
# Read a chunk of data from the file
|
||||
if self._decompressor.needs_input:
|
||||
buf = self._fp.read(READ_BUFFER_SIZE)
|
||||
uncompress = self._decompressor.decompress(buf, size)
|
||||
else:
|
||||
uncompress = self._decompressor.decompress(b"", size)
|
||||
buf = b""
|
||||
|
||||
uncompress = self._decompressor.decompress(buf, size)
|
||||
if self._decompressor.unused_data != b"":
|
||||
# Prepend the already read bytes to the fileobj so they can
|
||||
# be seen by _read_eof() and _read_gzip_header()
|
||||
|
||||
+55
@@ -228,6 +228,61 @@ class SockSendfileMixin(SendfileBase):
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(self.file.tell(), 0)
|
||||
|
||||
def check_sock_sendfile_offset(self, data, offset, force_fallback=False):
|
||||
sock, proto = self.prepare_socksendfile()
|
||||
with tempfile.TemporaryFile() as f:
|
||||
f.write(data)
|
||||
f.flush()
|
||||
self.assertEqual(f.tell(), len(data))
|
||||
|
||||
if force_fallback:
|
||||
async def _sock_sendfile_fail(sock, file, offset, count):
|
||||
raise asyncio.exceptions.SendfileNotAvailableError()
|
||||
with support.swap_attr(self.loop, '_sock_sendfile_native', _sock_sendfile_fail):
|
||||
ret = self.run_loop(self.loop.sock_sendfile(sock, f, offset, None))
|
||||
else:
|
||||
ret = self.run_loop(self.loop.sock_sendfile(sock, f, offset, None))
|
||||
|
||||
self.assertEqual(f.tell(), len(data))
|
||||
|
||||
sock.close()
|
||||
self.run_loop(proto.wait_closed())
|
||||
|
||||
self.assertEqual(ret, len(data) - offset)
|
||||
|
||||
|
||||
def test_sock_sendfile_offset(self):
|
||||
data = b'abcdef'
|
||||
for offset in (0, len(data) // 2, len(data)):
|
||||
for force_fallback in (False, True):
|
||||
with self.subTest(offset=offset, force_fallback=force_fallback):
|
||||
self.check_sock_sendfile_offset(data, offset, force_fallback)
|
||||
|
||||
def check_sendfile_offset(self, offset, fallback):
|
||||
srv_proto, cli_proto = self.prepare_sendfile()
|
||||
self.file.seek(123)
|
||||
coro = self.loop.sendfile(cli_proto.transport, self.file, offset, fallback=fallback)
|
||||
try:
|
||||
ret = self.run_loop(coro)
|
||||
except asyncio.SendfileNotAvailableError:
|
||||
if fallback:
|
||||
raise
|
||||
cli_proto.transport.close()
|
||||
self.run_loop(srv_proto.done)
|
||||
return
|
||||
cli_proto.transport.close()
|
||||
self.run_loop(srv_proto.done)
|
||||
self.assertEqual(ret, len(self.DATA) - offset)
|
||||
self.assertEqual(srv_proto.nbytes, len(self.DATA) - offset)
|
||||
self.assertEqual(srv_proto.data, self.DATA[offset:])
|
||||
self.assertEqual(self.file.tell(), len(self.DATA))
|
||||
|
||||
def test_sendfile_offset(self):
|
||||
for offset in (0, len(self.DATA) // 2, len(self.DATA)):
|
||||
for fallback in (False, True):
|
||||
with self.subTest(offset=offset, fallback=fallback):
|
||||
self.check_sendfile_offset(offset, fallback)
|
||||
|
||||
def test_sock_sendfile_mix_with_regular_send(self):
|
||||
buf = b"mix_regular_send" * (4 * 1024) # 64 KiB
|
||||
sock, proto = self.prepare_socksendfile()
|
||||
|
||||
Vendored
-1
@@ -4775,7 +4775,6 @@ class PyCoverage(Coverage, unittest.TestCase):
|
||||
sys.set_int_max_str_digits(self._previous_int_limit)
|
||||
super().tearDown()
|
||||
|
||||
|
||||
class PyFunctionality(unittest.TestCase):
|
||||
"""Extra functionality in decimal.py"""
|
||||
|
||||
|
||||
@@ -1060,6 +1060,78 @@ class TestParser(TestParserMixin, TestEmailBase):
|
||||
with self.assertRaises(errors.HeaderParseError):
|
||||
parser.get_phrase(' (foo) ')
|
||||
|
||||
def test_get_phrase_adjacent_ew(self):
|
||||
# "'linear-white-space' that separates a pair of adjacent
|
||||
# 'encoded-word's is ignored" (rfc2047 section 6.2)
|
||||
self._test_get_x(parser.get_phrase, '=?ascii?q?Joi?= \t =?ascii?q?ned?=', 'Joined', 'Joined', [], '')
|
||||
|
||||
def test_get_phrase_adjacent_ew_different_encodings(self):
|
||||
self._test_get_x(
|
||||
parser.get_phrase,
|
||||
'=?utf-8?q?B=C3=A9r?= =?iso-8859-1?q?=E9nice?=', 'Bérénice', 'Bérénice', [], ''
|
||||
)
|
||||
|
||||
def test_get_phrase_adjacent_ew_encoded_spaces(self):
|
||||
self._test_get_x(
|
||||
parser.get_phrase,
|
||||
'=?ascii?q?Encoded?= =?ascii?q?_spaces_?= =?ascii?q?preserved?=',
|
||||
'Encoded spaces preserved',
|
||||
'Encoded spaces preserved',
|
||||
[],
|
||||
''
|
||||
)
|
||||
|
||||
def test_get_phrase_adjacent_ew_comment_is_not_linear_white_space(self):
|
||||
self._test_get_x(
|
||||
parser.get_phrase,
|
||||
'=?ascii?q?Comment?= (is not) =?ascii?q?linear-white-space?=',
|
||||
'Comment (is not) linear-white-space',
|
||||
'Comment linear-white-space',
|
||||
[],
|
||||
'',
|
||||
comments=['is not'],
|
||||
)
|
||||
|
||||
def test_get_phrase_adjacent_ew_no_error_on_defects(self):
|
||||
self._test_get_x(
|
||||
parser.get_phrase,
|
||||
'=?ascii?q?Def?= =?ascii?q?ect still joins?=',
|
||||
'Defect still joins',
|
||||
'Defect still joins',
|
||||
[errors.InvalidHeaderDefect], # whitespace inside encoded word
|
||||
''
|
||||
)
|
||||
|
||||
def test_get_phrase_adjacent_ew_ignore_non_ew(self):
|
||||
self._test_get_x(
|
||||
parser.get_phrase,
|
||||
'=?ascii?q?No?= =?join?= for non-ew',
|
||||
'No =?join?= for non-ew',
|
||||
'No =?join?= for non-ew',
|
||||
[],
|
||||
''
|
||||
)
|
||||
|
||||
def test_get_phrase_adjacent_ew_ignore_invalid_ew(self):
|
||||
self._test_get_x(
|
||||
parser.get_phrase,
|
||||
'=?ascii?q?No?= =?ascii?rot13?wbva= for invalid ew',
|
||||
'No =?ascii?rot13?wbva= for invalid ew',
|
||||
'No =?ascii?rot13?wbva= for invalid ew',
|
||||
[],
|
||||
''
|
||||
)
|
||||
|
||||
def test_get_phrase_adjacent_ew_missing_space(self):
|
||||
self._test_get_x(
|
||||
parser.get_phrase,
|
||||
'=?ascii?q?Joi?==?ascii?q?ned?=',
|
||||
'Joined',
|
||||
'Joined',
|
||||
[errors.InvalidHeaderDefect], # missing trailing whitespace
|
||||
''
|
||||
)
|
||||
|
||||
# get_local_part
|
||||
|
||||
def test_get_local_part_simple(self):
|
||||
@@ -2398,6 +2470,22 @@ class TestParser(TestParserMixin, TestEmailBase):
|
||||
self.assertEqual(address[0].token_type,
|
||||
'mailbox')
|
||||
|
||||
def test_get_address_rfc2047_display_name_adjacent_ews(self):
|
||||
address = self._test_get_x(parser.get_address,
|
||||
'=?utf-8?q?B=C3=A9r?= =?utf-8?q?=C3=A9nice?= <foo@example.com>',
|
||||
'Bérénice <foo@example.com>',
|
||||
'Bérénice <foo@example.com>',
|
||||
[],
|
||||
'')
|
||||
self.assertEqual(address.token_type, 'address')
|
||||
self.assertEqual(len(address.mailboxes), 1)
|
||||
self.assertEqual(address.mailboxes,
|
||||
address.all_mailboxes)
|
||||
self.assertEqual(address.mailboxes[0].display_name,
|
||||
'Bérénice')
|
||||
self.assertEqual(address[0].token_type,
|
||||
'mailbox')
|
||||
|
||||
def test_get_address_empty_group(self):
|
||||
address = self._test_get_x(parser.get_address,
|
||||
'Monty Python:;',
|
||||
|
||||
+56
@@ -58,6 +58,62 @@ Hello World! =?iso-2022-jp?b?GyRCJU8lbSE8JW8hPCVrJUkhKhsoQg==?=
|
||||
# TK: full decode comparison
|
||||
eq(str(h).encode(jcode), subject_bytes)
|
||||
|
||||
h = Header("Japanese")
|
||||
s = '\u65e5\u672c\u8a9e' # 日本語
|
||||
h.append(s, Charset('euc-jp'))
|
||||
h.append(s, Charset('iso-2022-jp'))
|
||||
h.append(s, Charset('shift_jis'))
|
||||
eq(h.encode(), """\
|
||||
Japanese =?iso-2022-jp?b?GyRCRnxLXDhsGyhC?= =?iso-2022-jp?b?GyRCRnxLXDhsGyhC?=
|
||||
=?iso-2022-jp?b?GyRCRnxLXDhsGyhC?=""")
|
||||
eq(decode_header(h.encode()),
|
||||
[(b'Japanese ', None),
|
||||
(b'\x1b$BF|K\\8l\x1b(B\x1b$BF|K\\8l\x1b(B\x1b$BF|K\\8l\x1b(B', 'iso-2022-jp'),
|
||||
])
|
||||
|
||||
def test_chinese_codecs(self):
|
||||
eq = self.ndiffAssertEqual
|
||||
h = Header("Chinese")
|
||||
s = '\u4e2d\u6587' # 中文
|
||||
h.append(s, Charset('gb2312'))
|
||||
h.append(s, Charset('gbk'))
|
||||
h.append(s, Charset('gb18030'))
|
||||
h.append(s, Charset('hz'))
|
||||
h.append(s, Charset('big5'))
|
||||
h.append(s, Charset('big5hkscs'))
|
||||
eq(h.encode(), """\
|
||||
Chinese =?gb2312?b?1tDOxA==?= =?gbk?b?1tDOxA==?= =?gb18030?b?1tDOxA==?=
|
||||
=?hz?b?fntWUE5Efn0=?= =?big5?b?pKSk5Q==?= =?big5hkscs?b?pKSk5Q==?=""")
|
||||
eq(decode_header(h.encode()),
|
||||
[(b'Chinese ', None),
|
||||
(b'\xd6\xd0\xce\xc4', 'gb2312'),
|
||||
(b'\xd6\xd0\xce\xc4', 'gbk'),
|
||||
(b'\xd6\xd0\xce\xc4', 'gb18030'),
|
||||
(b'~{VPND~}', 'hz'),
|
||||
(b'\xa4\xa4\xa4\xe5', 'big5'),
|
||||
(b'\xa4\xa4\xa4\xe5', 'big5hkscs'),
|
||||
])
|
||||
|
||||
def test_korean_codecs(self):
|
||||
eq = self.ndiffAssertEqual
|
||||
h = Header("Korean")
|
||||
s = '\ud55c\uad6d\uc5b4' # 한국어
|
||||
h.append(s, Charset('euc-kr'))
|
||||
h.append(s, Charset('ks_c_5601-1987'))
|
||||
h.append(s, Charset('cp949'))
|
||||
h.append(s, Charset('iso-2022-kr'))
|
||||
h.append(s, Charset('johab'))
|
||||
eq(h.encode(), """\
|
||||
Korean =?euc-kr?b?x9Gxub7u?= =?ks_c_5601-1987?b?x9Gxub7uIMfRsbm+7g==?=
|
||||
=?iso-2022-kr?b?GyQpQw5HUTE5Pm4P?= =?johab?b?0GWKgrTh?=""")
|
||||
eq(decode_header(h.encode()),
|
||||
[(b'Korean ', None),
|
||||
(b'\xc7\xd1\xb1\xb9\xbe\xee', 'euc-kr'),
|
||||
(b'\xc7\xd1\xb1\xb9\xbe\xee \xc7\xd1\xb1\xb9\xbe\xee', 'ks_c_5601-1987'),
|
||||
(b'\x1b$)C\x0eGQ19>n\x0f', 'iso-2022-kr'),
|
||||
(b'\xd0e\x8a\x82\xb4\xe1', 'johab'),
|
||||
])
|
||||
|
||||
def test_payload_encoding_utf8(self):
|
||||
jhello = str(b'\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc'
|
||||
b'\xa5\xeb\xa5\xc9\xa1\xaa', 'euc-jp')
|
||||
|
||||
Vendored
+3
-9
@@ -4988,6 +4988,7 @@ class TestCharset(unittest.TestCase):
|
||||
c = Charset('utf-8')
|
||||
eq(c.header_encode(s), '=?utf-8?b?wqTCosKkwqTCpMKmwqTCqMKkwqo=?=')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; LookupError: unknown encoding: iso_2022_jp
|
||||
def test_body_encode(self):
|
||||
eq = self.assertEqual
|
||||
# Try a charset with QP body encoding
|
||||
@@ -5002,15 +5003,8 @@ class TestCharset(unittest.TestCase):
|
||||
# Try the convert argument, where input codec != output codec
|
||||
c = Charset('euc-jp')
|
||||
# With apologies to Tokio Kikuchi ;)
|
||||
# XXX FIXME
|
||||
## try:
|
||||
## eq('\x1b$B5FCO;~IW\x1b(B',
|
||||
## c.body_encode('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7'))
|
||||
## eq('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7',
|
||||
## c.body_encode('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7', False))
|
||||
## except LookupError:
|
||||
## # We probably don't have the Japanese codecs installed
|
||||
## pass
|
||||
eq('\x1b$B5FCO;~IW\x1b(B',
|
||||
c.body_encode('\u83ca\u5730\u6642\u592b'))
|
||||
# Testing SF bug #625509, which we have to fake, since there are no
|
||||
# built-in encodings where the header encoding is QP but the body
|
||||
# encoding is not.
|
||||
|
||||
Vendored
+2
-2
@@ -4988,8 +4988,8 @@ class Color(enum.Enum)
|
||||
| __members__
|
||||
| Returns a mapping of member name->value.
|
||||
|
|
||||
| This mapping lists all enum members, including aliases. Note that this
|
||||
| is a read-only view of the internal mapping."""
|
||||
| This mapping lists all enum members, including aliases. Note that
|
||||
| this is a read-only view of the internal mapping."""
|
||||
|
||||
expected_help_output_without_docs = """\
|
||||
Help on class Color in module %s:
|
||||
|
||||
Vendored
+35
-1
@@ -16,7 +16,7 @@ try:
|
||||
except ImportError:
|
||||
ssl = None
|
||||
|
||||
from unittest import TestCase, skipUnless
|
||||
from unittest import mock, TestCase, skipUnless
|
||||
from test import support
|
||||
from test.support import requires_subprocess
|
||||
from test.support import threading_helper
|
||||
@@ -1145,6 +1145,40 @@ class TestTimeouts(TestCase):
|
||||
ftp.close()
|
||||
|
||||
|
||||
class TestFtpcpSecurity(TestCase):
|
||||
"""ftpcp() must not trust the host a source server advertises in PASV.
|
||||
|
||||
A malicious source server can otherwise redirect the target server's
|
||||
data connection to an arbitrary host:port (SSRF), so ftpcp() uses the
|
||||
source server's actual peer address instead, the same as FTP.makepasv().
|
||||
"""
|
||||
|
||||
def _make_pair(self, *, advertised_host, real_host, trust=False):
|
||||
source = mock.Mock(spec=ftplib.FTP)
|
||||
source.trust_server_pasv_ipv4_address = trust
|
||||
source.sock.getpeername.return_value = (real_host, 21)
|
||||
# PASV replies give the host as comma-separated octets, not dotted.
|
||||
advertised = advertised_host.replace('.', ',')
|
||||
source.sendcmd.side_effect = lambda cmd: (
|
||||
f'227 Entering Passive Mode ({advertised},1,2).'
|
||||
if cmd == 'PASV' else '150 ok')
|
||||
target = mock.Mock(spec=ftplib.FTP)
|
||||
target.sendcmd.return_value = '150 ok'
|
||||
return source, target
|
||||
|
||||
def test_ftpcp_ignores_untrusted_pasv_host(self):
|
||||
source, target = self._make_pair(advertised_host='10.0.0.5',
|
||||
real_host='198.51.100.7')
|
||||
ftplib.ftpcp(source, 'a', target, 'b')
|
||||
target.sendport.assert_called_once_with('198.51.100.7', 258)
|
||||
|
||||
def test_ftpcp_trust_server_pasv_ipv4_address(self):
|
||||
source, target = self._make_pair(advertised_host='10.0.0.5',
|
||||
real_host='198.51.100.7', trust=True)
|
||||
ftplib.ftpcp(source, 'a', target, 'b')
|
||||
target.sendport.assert_called_once_with('10.0.0.5', 258)
|
||||
|
||||
|
||||
class MiscTestCase(TestCase):
|
||||
def test__all__(self):
|
||||
not_exported = {
|
||||
|
||||
Reference in new issue
Block a user