Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,35 @@ repos:
args: [--enable=default-role]
files: ^Doc/|^Misc/NEWS.d/

- repo: https://github.com/oxipng/oxipng
rev: v9.1.5
hooks:
- id: oxipng
args: [ "-o", "2", "--strip", "safe", "--alpha" ]
exclude: |
(?x)^(
PC/icons/*.png|
Android/testbed/app/src/main/res/drawable-xxhdpi/ic_launcher.png|
Objects/object_layout_*.png|
InternalDocs/images/python-cyclic-gc-*-new-page.png|
Doc/howto/logging_flow.png|
Doc/_static/og-image.png|
Doc/library/pathlib-inheritance.png|
Doc/library/tk_msg.png|
Doc/library/hashlib-blake2-tree.png|
Doc/library/turtle-star.png|
Doc/library/kde_example.png|
Doc/using/mac_installer_09_custom_install_free_threaded.png|
Doc/using/win_install_freethreaded.png|
Doc/using/win_installer.png|
Doc/using/mac_installer_*.png|
Tools/msi/bundle/SideBar.png|
Lib/profiling/sampling/_assets/python-logo-only.png|
Lib/test/tkinterdata/python.png|
Lib/test/test_email/data/python.png|
Lib/idlelib/Icons/idle_*.png|
)$

- repo: local
hooks:
- id: blurb-no-space-c-api
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions Doc/c-api/init_config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,11 @@ PyConfig

Default: ``0``.

.. deprecated-removed:: 3.15 3.17

The :option:`-b` and :option:`!-bb` options will become no-op in 3.17.
:c:member:`~PyConfig.bytes_warning` member will be removed in 3.17.

.. c:member:: int warn_default_encoding

If non-zero, emit a :exc:`EncodingWarning` warning when :class:`io.TextIOWrapper`
Expand Down
Binary file removed Doc/faq/python-video-icon.png
Binary file not shown.
Binary file modified Doc/howto/logging_flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Doc/library/hashlib-blake2-tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Doc/library/kde_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Doc/library/tk_msg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Doc/library/turtle-star.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions Doc/using/cmdline.rst
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,15 @@ Miscellaneous options
.. versionchanged:: 3.5
Affects also comparisons of :class:`bytes` with :class:`int`.

.. deprecated:: 3.15

Deprecate :option:`-b` and :option:`!-bb` command line options
and schedule them to become no-op in Python 3.17.
These were primarily helpers for the Python 2 -> 3 transition.
Starting with Python 3.17, no :exc:`BytesWarning` will be raised
for these cases; use a type checker instead.


.. option:: -B

If given, Python won't try to write ``.pyc`` files on the
Expand Down
Binary file modified Doc/using/mac_installer_02_readme.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Doc/using/mac_installer_07_applications.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Doc/using/mac_installer_09_custom_install_free_threaded.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Doc/using/win_install_freethreaded.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Doc/using/win_installer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,17 @@ module_name
Deprecated
==========

CLI
---

* Deprecate :option:`-b` and :option:`!-bb` command line options
and schedule them to become no-op in Python 3.17.
These were primarily helpers for the Python 2 -> 3 transition.
Starting with Python 3.17, no :exc:`BytesWarning` will be raised
for these cases; use a type checker instead.

(Contributed by Nikita Sobolev in :gh:`136355`.)

hashlib
-------

Expand Down Expand Up @@ -775,6 +786,10 @@ Deprecated C APIs
:c:func:`_Py_c_abs` are :term:`soft deprecated`.
(Contributed by Sergey B Kirpichev in :gh:`128813`.)

* :c:member:`~PyConfig.bytes_warning` is deprecated
since 3.15 and will be removed in 3.17.
(Contributed by Nikita Sobolev in :gh:`136355`.)


.. Add C API deprecations above alphabetically, not here at the end.

Expand Down
Binary file modified InternalDocs/images/python-cyclic-gc-1-new-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified InternalDocs/images/python-cyclic-gc-2-new-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified InternalDocs/images/python-cyclic-gc-3-new-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified InternalDocs/images/python-cyclic-gc-4-new-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified InternalDocs/images/python-cyclic-gc-5-new-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Lib/idlelib/Icons/idle_16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Lib/idlelib/Icons/idle_256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Lib/idlelib/Icons/idle_32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Lib/idlelib/Icons/idle_48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions Lib/pathlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

from pathlib._os import (
PathInfo, DirEntryInfo,
magic_open, vfspath,
vfsopen, vfspath,
ensure_different_files, ensure_distinct_paths,
copyfile2, copyfileobj, copy_info,
)
Expand Down Expand Up @@ -1129,7 +1129,7 @@ def _copy_from(self, source, follow_symlinks=True, preserve_metadata=False):

def _copy_from_file(self, source, preserve_metadata=False):
ensure_different_files(source, self)
with magic_open(source, 'rb') as source_f:
with vfsopen(source, 'rb') as source_f:
with open(self, 'wb') as target_f:
copyfileobj(source_f, target_f)
if preserve_metadata:
Expand Down
94 changes: 66 additions & 28 deletions Lib/pathlib/_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,48 +166,86 @@ def copyfileobj(source_f, target_f):
write_target(buf)


def magic_open(path, mode='r', buffering=-1, encoding=None, errors=None,
newline=None):
def _open_reader(obj):
cls = type(obj)
try:
open_reader = cls.__open_reader__
except AttributeError:
cls_name = cls.__name__
raise TypeError(f"{cls_name} can't be opened for reading") from None
else:
return open_reader(obj)


def _open_writer(obj, mode):
cls = type(obj)
try:
open_writer = cls.__open_writer__
except AttributeError:
cls_name = cls.__name__
raise TypeError(f"{cls_name} can't be opened for writing") from None
else:
return open_writer(obj, mode)


def _open_updater(obj, mode):
cls = type(obj)
try:
open_updater = cls.__open_updater__
except AttributeError:
cls_name = cls.__name__
raise TypeError(f"{cls_name} can't be opened for updating") from None
else:
return open_updater(obj, mode)


def vfsopen(obj, mode='r', buffering=-1, encoding=None, errors=None,
newline=None):
"""
Open the file pointed to by this path and return a file object, as
the built-in open() function does.

Unlike the built-in open() function, this function additionally accepts
'openable' objects, which are objects with any of these special methods:

__open_reader__()
__open_writer__(mode)
__open_updater__(mode)

'__open_reader__' is called for 'r' mode; '__open_writer__' for 'a', 'w'
and 'x' modes; and '__open_updater__' for 'r+' and 'w+' modes. If text
mode is requested, the result is wrapped in an io.TextIOWrapper object.
"""
if buffering != -1:
raise ValueError("buffer size can't be customized")
text = 'b' not in mode
if text:
# Call io.text_encoding() here to ensure any warning is raised at an
# appropriate stack level.
encoding = text_encoding(encoding)
try:
return open(path, mode, buffering, encoding, errors, newline)
return open(obj, mode, buffering, encoding, errors, newline)
except TypeError:
pass
cls = type(path)
if not text:
if encoding is not None:
raise ValueError("binary mode doesn't take an encoding argument")
if errors is not None:
raise ValueError("binary mode doesn't take an errors argument")
if newline is not None:
raise ValueError("binary mode doesn't take a newline argument")
mode = ''.join(sorted(c for c in mode if c not in 'bt'))
if text:
try:
attr = getattr(cls, f'__open_{mode}__')
except AttributeError:
pass
else:
return attr(path, buffering, encoding, errors, newline)
elif encoding is not None:
raise ValueError("binary mode doesn't take an encoding argument")
elif errors is not None:
raise ValueError("binary mode doesn't take an errors argument")
elif newline is not None:
raise ValueError("binary mode doesn't take a newline argument")

try:
attr = getattr(cls, f'__open_{mode}b__')
except AttributeError:
pass
if mode == 'r':
stream = _open_reader(obj)
elif mode in ('a', 'w', 'x'):
stream = _open_writer(obj, mode)
elif mode in ('+r', '+w'):
stream = _open_updater(obj, mode[1])
else:
stream = attr(path, buffering)
if text:
stream = TextIOWrapper(stream, encoding, errors, newline)
return stream

raise TypeError(f"{cls.__name__} can't be opened with mode {mode!r}")
raise ValueError(f'invalid mode: {mode}')
if text:
stream = TextIOWrapper(stream, encoding, errors, newline)
return stream


def vfspath(obj):
Expand Down
22 changes: 11 additions & 11 deletions Lib/pathlib/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from abc import ABC, abstractmethod
from glob import _GlobberBase
from io import text_encoding
from pathlib._os import (magic_open, vfspath, ensure_distinct_paths,
from pathlib._os import (vfsopen, vfspath, ensure_distinct_paths,
ensure_different_files, copyfileobj)
from pathlib import PurePath, Path
from typing import Optional, Protocol, runtime_checkable
Expand Down Expand Up @@ -264,18 +264,18 @@ def info(self):
raise NotImplementedError

@abstractmethod
def __open_rb__(self, buffering=-1):
def __open_reader__(self):
"""
Open the file pointed to by this path for reading in binary mode and
return a file object, like open(mode='rb').
return a file object.
"""
raise NotImplementedError

def read_bytes(self):
"""
Open the file in bytes mode, read it, and close the file.
"""
with magic_open(self, mode='rb', buffering=0) as f:
with vfsopen(self, mode='rb') as f:
return f.read()

def read_text(self, encoding=None, errors=None, newline=None):
Expand All @@ -285,7 +285,7 @@ def read_text(self, encoding=None, errors=None, newline=None):
# Call io.text_encoding() here to ensure any warning is raised at an
# appropriate stack level.
encoding = text_encoding(encoding)
with magic_open(self, mode='r', encoding=encoding, errors=errors, newline=newline) as f:
with vfsopen(self, mode='r', encoding=encoding, errors=errors, newline=newline) as f:
return f.read()

@abstractmethod
Expand Down Expand Up @@ -394,10 +394,10 @@ def mkdir(self):
raise NotImplementedError

@abstractmethod
def __open_wb__(self, buffering=-1):
def __open_writer__(self, mode):
"""
Open the file pointed to by this path for writing in binary mode and
return a file object, like open(mode='wb').
return a file object.
"""
raise NotImplementedError

Expand All @@ -407,7 +407,7 @@ def write_bytes(self, data):
"""
# type-check for the buffer interface before truncating the file
view = memoryview(data)
with magic_open(self, mode='wb') as f:
with vfsopen(self, mode='wb') as f:
return f.write(view)

def write_text(self, data, encoding=None, errors=None, newline=None):
Expand All @@ -420,7 +420,7 @@ def write_text(self, data, encoding=None, errors=None, newline=None):
if not isinstance(data, str):
raise TypeError('data must be str, not %s' %
data.__class__.__name__)
with magic_open(self, mode='w', encoding=encoding, errors=errors, newline=newline) as f:
with vfsopen(self, mode='w', encoding=encoding, errors=errors, newline=newline) as f:
return f.write(data)

def _copy_from(self, source, follow_symlinks=True):
Expand All @@ -439,8 +439,8 @@ def _copy_from(self, source, follow_symlinks=True):
stack.append((child, dst.joinpath(child.name)))
else:
ensure_different_files(src, dst)
with magic_open(src, 'rb') as source_f:
with magic_open(dst, 'wb') as target_f:
with vfsopen(src, 'rb') as source_f:
with vfsopen(dst, 'wb') as target_f:
copyfileobj(source_f, target_f)


Expand Down
Binary file modified Lib/profiling/sampling/_assets/python-logo-only.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions Lib/shlex.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,9 @@ def quote(s):
if not s:
return "''"

if not isinstance(s, str):
raise TypeError(f"expected string object, got {type(s).__name__!r}")

# Use bytes.translate() for performance
safe_chars = (b'%+,-./0123456789:=@'
b'ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
Expand Down
Binary file modified Lib/test/test_email/data/python.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions Lib/test/test_pathlib/support/local_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def __init__(self, *pathsegments):
super().__init__(*pathsegments)
self.info = LocalPathInfo(self)

def __open_rb__(self, buffering=-1):
def __open_reader__(self):
return open(self, 'rb')

def iterdir(self):
Expand All @@ -163,8 +163,8 @@ class WritableLocalPath(_WritablePath, LexicalPath):
__slots__ = ()
__fspath__ = LexicalPath.__vfspath__

def __open_wb__(self, buffering=-1):
return open(self, 'wb')
def __open_writer__(self, mode):
return open(self, f'{mode}b')

def mkdir(self, mode=0o777):
os.mkdir(self, mode)
Expand Down
8 changes: 4 additions & 4 deletions Lib/test/test_pathlib/support/zip_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,13 @@ def info(self):
tree = self.zip_file.filelist.tree
return tree.resolve(vfspath(self), follow_symlinks=False)

def __open_rb__(self, buffering=-1):
def __open_reader__(self):
info = self.info.resolve()
if not info.exists():
raise FileNotFoundError(errno.ENOENT, "File not found", self)
elif info.is_dir():
raise IsADirectoryError(errno.EISDIR, "Is a directory", self)
return self.zip_file.open(info.zip_info, 'r')
return self.zip_file.open(info.zip_info)

def iterdir(self):
info = self.info.resolve()
Expand Down Expand Up @@ -320,8 +320,8 @@ def __repr__(self):
def with_segments(self, *pathsegments):
return type(self)(*pathsegments, zip_file=self.zip_file)

def __open_wb__(self, buffering=-1):
return self.zip_file.open(vfspath(self), 'w')
def __open_writer__(self, mode):
return self.zip_file.open(vfspath(self), mode)

def mkdir(self, mode=0o777):
zinfo = zipfile.ZipInfo(vfspath(self) + '/')
Expand Down
Loading