Update some of changed libs and tests from 3.14.5 -> 3.14.6 (part 1) (#8084)

This commit is contained in:
Shahar Naveh authored and GitHub committed 2026-06-12 23:49:15 +09:00
1 parent 52c2d3fd61
commit a2fabfa064
37 files changed
+674 -374

No files matched your search

+17 -12
View File
@@ -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]
+56 -51
View File
@@ -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')
+69 -59
View File
@@ -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.
"""
+18 -19
View File
@@ -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.
+1 -1
View File
@@ -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",
+12 -9
View File
@@ -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."""
+7 -7
View File
@@ -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()
+9 -9
View File
@@ -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:
+1 -2
View File
@@ -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()
+19 -17
View File
@@ -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()
+2 -1
View File
@@ -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
+17 -14
View File
@@ -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:
+7 -7
View File
@@ -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.
+30 -27
View File
@@ -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(
+2 -1
View File
@@ -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()
+2 -1
View File
@@ -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:
+11 -10
View File
@@ -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):
+3
View File
@@ -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,
+22 -15
View File
@@ -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
+15 -13
View File
@@ -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.
+19 -18
View File
@@ -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
+10
View File
@@ -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)
+1 -1
View File
@@ -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.
-2
View File
@@ -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.
+8
View File
@@ -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
View File
@@ -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_)
+10 -1
View File
@@ -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.
+6 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()
-1
View File
@@ -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"""
+88
View File
@@ -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
View File
@@ -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')
+3 -9
View File
@@ -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.
+2 -2
View File
@@ -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:
+35 -1
View File
@@ -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 = {