Skip to content
Merged
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
1 change: 1 addition & 0 deletions Doc/deprecations/pending-removal-in-3.20.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Pending removal in Python 3.20
- :mod:`argparse`
- :mod:`csv`
- :mod:`!ctypes.macholib`
- :mod:`imaplib`
- :mod:`ipaddress`
- :mod:`json`
- :mod:`logging` (``__date__`` also deprecated)
Expand Down
31 changes: 29 additions & 2 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3451,7 +3451,10 @@ features:

.. attribute:: stx_mnt_id

Mount ID.
Mount identifier.

Equal to ``None`` if :data:`STATX_MNT_ID` is missing from
:attr:`~os.statx_result.stx_mask`.

.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
userspace API headers >= 5.8.
Expand All @@ -3460,19 +3463,28 @@ features:

Direct I/O memory buffer alignment requirement.

Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from
:attr:`~os.statx_result.stx_mask`.

.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
userspace API headers >= 6.1.

.. attribute:: stx_dio_offset_align

Direct I/O file offset alignment requirement.

Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from
:attr:`~os.statx_result.stx_mask`.

.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
userspace API headers >= 6.1.

.. attribute:: stx_subvol

Subvolume ID.
Subvolume identifier.

Equal to ``None`` if :data:`STATX_SUBVOL` is missing from
:attr:`~os.statx_result.stx_mask`.

.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
userspace API headers >= 6.10.
Expand All @@ -3481,34 +3493,49 @@ features:

Minimum size for direct I/O with torn-write protection.

Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from
:attr:`~os.statx_result.stx_mask`.

.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
userspace API headers >= 6.11.

.. attribute:: stx_atomic_write_unit_max

Maximum size for direct I/O with torn-write protection.

Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from
:attr:`~os.statx_result.stx_mask`.

.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
userspace API headers >= 6.11.

.. attribute:: stx_atomic_write_unit_max_opt

Maximum optimized size for direct I/O with torn-write protection.

Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from
:attr:`~os.statx_result.stx_mask`.

.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
userspace API headers >= 6.16.

.. attribute:: stx_atomic_write_segments_max

Maximum iovecs for direct I/O with torn-write protection.

Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from
:attr:`~os.statx_result.stx_mask`.

.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
userspace API headers >= 6.11.

.. attribute:: stx_dio_read_offset_align

Direct I/O file offset alignment requirement for reads.

Equal to ``None`` if :data:`STATX_DIO_READ_ALIGN` is missing from
:attr:`~os.statx_result.stx_mask`.

.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
userspace API headers >= 6.14.

Expand Down
16 changes: 10 additions & 6 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ Objects, values and types
single: data

:dfn:`Objects` are Python's abstraction for data. All data in a Python program
is represented by objects or by relations between objects. (In a sense, and in
conformance to Von Neumann's model of a "stored program computer", code is also
represented by objects.)
is represented by objects or by relations between objects. Even code is
represented by objects.

.. index::
pair: built-in function; id
Expand All @@ -29,9 +28,6 @@ represented by objects.)
single: mutable object
single: immutable object

.. XXX it *is* now possible in some cases to change an object's
type, under certain controlled conditions

Every object has an identity, a type and a value. An object's *identity* never
changes once it has been created; you may think of it as the object's address in
memory. The :keyword:`is` operator compares the identity of two objects; the
Expand Down Expand Up @@ -1185,6 +1181,7 @@ Special attributes
single: __module__ (class attribute)
single: __dict__ (class attribute)
single: __bases__ (class attribute)
single: __base__ (class attribute)
single: __doc__ (class attribute)
single: __annotations__ (class attribute)
single: __annotate__ (class attribute)
Expand Down Expand Up @@ -1219,6 +1216,13 @@ Special attributes
In most cases, for a class defined as ``class X(A, B, C)``,
``X.__bases__`` will be exactly equal to ``(A, B, C)``.

* - .. attribute:: type.__base__
- .. impl-detail::

The single base class in the inheritance chain that is responsible
for the memory layout of instances. This attribute corresponds to
:c:member:`~PyTypeObject.tp_base` at the C level.

* - .. attribute:: type.__doc__
- The class's documentation string, or ``None`` if undefined.
Not inherited by subclasses.
Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,8 @@ mimetypes
---------

* Add ``application/toml``. (Contributed by Gil Forcada in :gh:`139959`.)
* Rename ``application/x-texinfo`` to ``application/texinfo``.
(Contributed by Charlie Lin in :gh:`140165`)


pathlib
Expand Down Expand Up @@ -825,6 +827,7 @@ New deprecations
- :mod:`argparse`
- :mod:`csv`
- :mod:`!ctypes.macholib`
- :mod:`imaplib`
- :mod:`ipaddress`
- :mod:`json`
- :mod:`logging` (``__date__`` also deprecated)
Expand Down
12 changes: 9 additions & 3 deletions Lib/imaplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
# GET/SETANNOTATION contributed by Tomas Lindroos <skitta@abo.fi> June 2005.
# IDLE contributed by Forest <forestix@nom.one> August 2024.

__version__ = "2.60"

import binascii, errno, random, re, socket, subprocess, sys, time, calendar
from datetime import datetime, timezone, timedelta
from io import DEFAULT_BUFFER_SIZE
Expand Down Expand Up @@ -247,7 +245,6 @@ def _connect(self):
self._cmd_log_idx = 0
self._cmd_log = {} # Last '_cmd_log_len' interactions
if self.debug >= 1:
self._mesg('imaplib version %s' % __version__)
self._mesg('new IMAP4 connection, tag=%s' % self.tagpre)

self.welcome = self._get_response()
Expand Down Expand Up @@ -1965,3 +1962,12 @@ def run(cmd, args):
''' % sys.argv[0])

raise


def __getattr__(name):
if name == "__version__":
from warnings import _deprecated

_deprecated("__version__", remove=(3, 20))
return "2.60" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
4 changes: 2 additions & 2 deletions Lib/mimetypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ def _default_mime_types():
'.ps' : 'application/postscript',
'.ai' : 'application/postscript',
'.eps' : 'application/postscript',
'.texi' : 'application/texinfo',
'.texinfo': 'application/texinfo',
'.toml' : 'application/toml',
'.trig' : 'application/trig',
'.m3u' : 'application/vnd.apple.mpegurl',
Expand Down Expand Up @@ -549,8 +551,6 @@ def _default_mime_types():
'.tar' : 'application/x-tar',
'.tcl' : 'application/x-tcl',
'.tex' : 'application/x-tex',
'.texi' : 'application/x-texinfo',
'.texinfo': 'application/x-texinfo',
'.roff' : 'application/x-troff',
'.t' : 'application/x-troff',
'.tr' : 'application/x-troff',
Expand Down
10 changes: 10 additions & 0 deletions Lib/test/test_imaplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1117,5 +1117,15 @@ def test_ssl_verified(self):
client.shutdown()


class TestModule(unittest.TestCase):
def test_deprecated__version__(self):
with self.assertWarnsRegex(
DeprecationWarning,
"'__version__' is deprecated and slated for removal in Python 3.20",
) as cm:
getattr(imaplib, "__version__")
self.assertEqual(cm.filename, __file__)


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion Lib/test/test_mimetypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ def check_extensions():
("application/gzip", ".gz"),
("application/ogg", ".ogx"),
("application/postscript", ".ps"),
("application/texinfo", ".texi"),
("application/toml", ".toml"),
("application/vnd.apple.mpegurl", ".m3u"),
("application/vnd.ms-excel", ".xls"),
Expand All @@ -247,7 +248,6 @@ def check_extensions():
("application/x-debian-package", ".deb"),
("application/x-httpd-php", ".php"),
("application/x-rpm", ".rpm"),
("application/x-texinfo", ".texi"),
("application/x-troff", ".roff"),
("application/xml", ".xsl"),
("application/yaml", ".yaml"),
Expand Down
77 changes: 77 additions & 0 deletions Lib/test/test_tokenize.py
Original file line number Diff line number Diff line change
Expand Up @@ -1495,6 +1495,61 @@ def test_cookie_second_line_noncommented_first_line(self):
expected = [b"print('\xc2\xa3')\n"]
self.assertEqual(consumed_lines, expected)

def test_first_non_utf8_coding_line(self):
lines = (
b'#coding:iso-8859-15 \xa4\n',
b'print(something)\n'
)
encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines))
self.assertEqual(encoding, 'iso-8859-15')
self.assertEqual(consumed_lines, list(lines[:1]))

def test_first_utf8_coding_line_error(self):
lines = (
b'#coding:ascii \xc3\xa4\n',
b'print(something)\n'
)
with self.assertRaises(SyntaxError):
tokenize.detect_encoding(self.get_readline(lines))

def test_second_non_utf8_coding_line(self):
lines = (
b'#!/usr/bin/python\n',
b'#coding:iso-8859-15 \xa4\n',
b'print(something)\n'
)
encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines))
self.assertEqual(encoding, 'iso-8859-15')
self.assertEqual(consumed_lines, list(lines[:2]))

def test_second_utf8_coding_line_error(self):
lines = (
b'#!/usr/bin/python\n',
b'#coding:ascii \xc3\xa4\n',
b'print(something)\n'
)
with self.assertRaises(SyntaxError):
tokenize.detect_encoding(self.get_readline(lines))

def test_non_utf8_shebang(self):
lines = (
b'#!/home/\xa4/bin/python\n',
b'#coding:iso-8859-15\n',
b'print(something)\n'
)
encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines))
self.assertEqual(encoding, 'iso-8859-15')
self.assertEqual(consumed_lines, list(lines[:2]))

def test_utf8_shebang_error(self):
lines = (
b'#!/home/\xc3\xa4/bin/python\n',
b'#coding:ascii\n',
b'print(something)\n'
)
with self.assertRaises(SyntaxError):
tokenize.detect_encoding(self.get_readline(lines))

def test_cookie_second_line_empty_first_line(self):
lines = (
b'\n',
Expand Down Expand Up @@ -1548,6 +1603,28 @@ def test_double_coding_utf8(self):
self.assertEqual(encoding, 'utf-8')
self.assertEqual(consumed_lines, list(lines[:1]))

def test_nul_in_first_coding_line(self):
lines = (
b'#coding:iso8859-15\x00\n',
b'\n',
b'\n',
b'print(something)\n'
)
with self.assertRaisesRegex(SyntaxError,
"source code cannot contain null bytes"):
tokenize.detect_encoding(self.get_readline(lines))

def test_nul_in_second_coding_line(self):
lines = (
b'#!/usr/bin/python\n',
b'#coding:iso8859-15\x00\n',
b'\n',
b'print(something)\n'
)
with self.assertRaisesRegex(SyntaxError,
"source code cannot contain null bytes"):
tokenize.detect_encoding(self.get_readline(lines))

def test_latin1_normalization(self):
# See get_normal_name() in Parser/tokenizer/helpers.c.
encodings = ("latin-1", "iso-8859-1", "iso-latin-1", "latin-1-unix",
Expand Down
22 changes: 14 additions & 8 deletions Lib/tokenize.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from token import EXACT_TOKEN_TYPES
import _tokenize

cookie_re = re.compile(r'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII)
cookie_re = re.compile(br'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII)
blank_re = re.compile(br'^[ \t\f]*(?:[#\r\n]|$)', re.ASCII)

import token
Expand Down Expand Up @@ -385,22 +385,23 @@ def read_or_stop():
except StopIteration:
return b''

def find_cookie(line):
def check(line, encoding):
# Check if the line matches the encoding.
if 0 in line:
raise SyntaxError("source code cannot contain null bytes")
try:
# Decode as UTF-8. Either the line is an encoding declaration,
# in which case it should be pure ASCII, or it must be UTF-8
# per default encoding.
line_string = line.decode('utf-8')
line.decode(encoding)
except UnicodeDecodeError:
msg = "invalid or missing encoding declaration"
if filename is not None:
msg = '{} for {!r}'.format(msg, filename)
raise SyntaxError(msg)

match = cookie_re.match(line_string)
def find_cookie(line):
match = cookie_re.match(line)
if not match:
return None
encoding = _get_normal_name(match.group(1))
encoding = _get_normal_name(match.group(1).decode())
try:
codec = lookup(encoding)
except LookupError:
Expand Down Expand Up @@ -433,18 +434,23 @@ def find_cookie(line):

encoding = find_cookie(first)
if encoding:
check(first, encoding)
return encoding, [first]
if not blank_re.match(first):
check(first, default)
return default, [first]

second = read_or_stop()
if not second:
check(first, default)
return default, [first]

encoding = find_cookie(second)
if encoding:
check(first + second, encoding)
return encoding, [first, second]

check(first + second, default)
return default, [first, second]


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :func:`tokenize.detect_encoding`. Support non-UTF-8 shebang and comments
if non-UTF-8 encoding is specified. Detect decoding error for non-UTF-8
encoding. Detect null bytes in source code.
Loading
Loading