Skip to content

Commit 21f5335

Browse files
committed
pythongh-121657: merge upstream
2 parents 2abce33 + a640a60 commit 21f5335

22 files changed

+280
-58
lines changed

Doc/faq/programming.rst

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,11 +1741,31 @@ but effective way to define class private variables. Any identifier of the form
17411741
is textually replaced with ``_classname__spam``, where ``classname`` is the
17421742
current class name with any leading underscores stripped.
17431743

1744-
This doesn't guarantee privacy: an outside user can still deliberately access
1745-
the "_classname__spam" attribute, and private values are visible in the object's
1746-
``__dict__``. Many Python programmers never bother to use private variable
1747-
names at all.
1744+
The identifier can be used unchanged within the class, but to access it outside
1745+
the class, the mangled name must be used:
17481746

1747+
.. code-block:: python
1748+
1749+
class A:
1750+
def __one(self):
1751+
return 1
1752+
def two(self):
1753+
return 2 * self.__one()
1754+
1755+
class B(A):
1756+
def three(self):
1757+
return 3 * self._A__one()
1758+
1759+
four = 4 * A()._A__one()
1760+
1761+
In particular, this does not guarantee privacy since an outside user can still
1762+
deliberately access the private attribute; many Python programmers never bother
1763+
to use private variable names at all.
1764+
1765+
.. seealso::
1766+
1767+
The :ref:`private name mangling specifications <private-name-mangling>`
1768+
for details and special cases.
17491769

17501770
My class defines __del__ but it is not called when I delete the object.
17511771
-----------------------------------------------------------------------

Doc/library/configparser.rst

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -147,23 +147,28 @@ case-insensitive and stored in lowercase [1]_.
147147
It is possible to read several configurations into a single
148148
:class:`ConfigParser`, where the most recently added configuration has the
149149
highest priority. Any conflicting keys are taken from the more recent
150-
configuration while the previously existing keys are retained.
150+
configuration while the previously existing keys are retained. The example
151+
below reads in an ``override.ini`` file, which will override any conflicting
152+
keys from the ``example.ini`` file.
153+
154+
.. code-block:: ini
155+
156+
[DEFAULT]
157+
ServerAliveInterval = -1
151158
152159
.. doctest::
153160

154-
>>> another_config = configparser.ConfigParser()
155-
>>> another_config.read('example.ini')
156-
['example.ini']
157-
>>> another_config['topsecret.server.example']['Port']
158-
'50022'
159-
>>> another_config.read_string("[topsecret.server.example]\nPort=48484")
160-
>>> another_config['topsecret.server.example']['Port']
161-
'48484'
162-
>>> another_config.read_dict({"topsecret.server.example": {"Port": 21212}})
163-
>>> another_config['topsecret.server.example']['Port']
164-
'21212'
165-
>>> another_config['topsecret.server.example']['ForwardX11']
166-
'no'
161+
>>> config_override = configparser.ConfigParser()
162+
>>> config_override['DEFAULT'] = {'ServerAliveInterval': '-1'}
163+
>>> with open('override.ini', 'w') as configfile:
164+
... config_override.write(configfile)
165+
...
166+
>>> config_override = configparser.ConfigParser()
167+
>>> config_override.read(['example.ini', 'override.ini'])
168+
['example.ini', 'override.ini']
169+
>>> print(config_override.get('DEFAULT', 'ServerAliveInterval'))
170+
-1
171+
167172

168173
This behaviour is equivalent to a :meth:`ConfigParser.read` call with several
169174
files passed to the *filenames* parameter.
@@ -984,6 +989,31 @@ ConfigParser Objects
984989
converter gets its own corresponding :meth:`!get*()` method on the parser
985990
object and section proxies.
986991

992+
It is possible to read several configurations into a single
993+
:class:`ConfigParser`, where the most recently added configuration has the
994+
highest priority. Any conflicting keys are taken from the more recent
995+
configuration while the previously existing keys are retained. The example
996+
below reads in an ``override.ini`` file, which will override any conflicting
997+
keys from the ``example.ini`` file.
998+
999+
.. code-block:: ini
1000+
1001+
[DEFAULT]
1002+
ServerAliveInterval = -1
1003+
1004+
.. doctest::
1005+
1006+
>>> config_override = configparser.ConfigParser()
1007+
>>> config_override['DEFAULT'] = {'ServerAliveInterval': '-1'}
1008+
>>> with open('override.ini', 'w') as configfile:
1009+
... config_override.write(configfile)
1010+
...
1011+
>>> config_override = configparser.ConfigParser()
1012+
>>> config_override.read(['example.ini', 'override.ini'])
1013+
['example.ini', 'override.ini']
1014+
>>> print(config_override.get('DEFAULT', 'ServerAliveInterval'))
1015+
-1
1016+
9871017
.. versionchanged:: 3.1
9881018
The default *dict_type* is :class:`collections.OrderedDict`.
9891019

Doc/library/ftplib.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ FTP objects
243243
Retrieve a file in binary transfer mode.
244244

245245
:param str cmd:
246-
An appropriate ``STOR`` command: :samp:`"STOR {filename}"`.
246+
An appropriate ``RETR`` command: :samp:`"RETR {filename}"`.
247247

248248
:param callback:
249249
A single parameter callable that is called

Doc/reference/expressions.rst

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,47 @@ exception.
8383
pair: name; mangling
8484
pair: private; names
8585

86-
**Private name mangling:** When an identifier that textually occurs in a class
87-
definition begins with two or more underscore characters and does not end in two
88-
or more underscores, it is considered a :dfn:`private name` of that class.
89-
Private names are transformed to a longer form before code is generated for
90-
them. The transformation inserts the class name, with leading underscores
91-
removed and a single underscore inserted, in front of the name. For example,
92-
the identifier ``__spam`` occurring in a class named ``Ham`` will be transformed
93-
to ``_Ham__spam``. This transformation is independent of the syntactical
94-
context in which the identifier is used. If the transformed name is extremely
95-
long (longer than 255 characters), implementation defined truncation may happen.
96-
If the class name consists only of underscores, no transformation is done.
86+
Private name mangling
87+
^^^^^^^^^^^^^^^^^^^^^
9788

89+
When an identifier that textually occurs in a class definition begins with two
90+
or more underscore characters and does not end in two or more underscores, it
91+
is considered a :dfn:`private name` of that class.
92+
93+
.. seealso::
94+
95+
The :ref:`class specifications <class>`.
96+
97+
More precisely, private names are transformed to a longer form before code is
98+
generated for them. If the transformed name is longer than 255 characters,
99+
implementation-defined truncation may happen.
100+
101+
The transformation is independent of the syntactical context in which the
102+
identifier is used but only the following private identifiers are mangled:
103+
104+
- Any name used as the name of a variable that is assigned or read or any
105+
name of an attribute being accessed.
106+
107+
The ``__name__`` attribute of nested functions, classes, and type aliases
108+
is however not mangled.
109+
110+
- The name of imported modules, e.g., ``__spam`` in ``import __spam``.
111+
If the module is part of a package (i.e., its name contains a dot),
112+
the name is *not* mangled, e.g., the ``__foo`` in ``import __foo.bar``
113+
is not mangled.
114+
115+
- The name of an imported member, e.g., ``__f`` in ``from spam import __f``.
116+
117+
The transformation rule is defined as follows:
118+
119+
- The class name, with leading underscores removed and a single leading
120+
underscore inserted, is inserted in front of the identifier, e.g., the
121+
identifier ``__spam`` occurring in a class named ``Foo``, ``_Foo`` or
122+
``__Foo`` is transformed to ``_Foo__spam``.
123+
124+
- If the class name consists only of underscores, the transformation is the
125+
identity, e.g., the identifier ``__spam`` occurring in a class named ``_``
126+
or ``__`` is left as is.
98127

99128
.. _atom-literals:
100129

Doc/tutorial/classes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,11 @@ current class name with leading underscore(s) stripped. This mangling is done
688688
without regard to the syntactic position of the identifier, as long as it
689689
occurs within the definition of a class.
690690

691+
.. seealso::
692+
693+
The :ref:`private name mangling specifications <private-name-mangling>`
694+
for details and special cases.
695+
691696
Name mangling is helpful for letting subclasses override methods without
692697
breaking intraclass method calls. For example::
693698

Lib/_pyrepl/historical_reader.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ def select_item(self, i: int) -> None:
264264
self.historyi = i
265265
self.pos = len(self.buffer)
266266
self.dirty = True
267+
self.last_refresh_cache.invalidated = True
267268

268269
def get_item(self, i: int) -> str:
269270
if i != len(self.history):

Lib/_pyrepl/reader.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ def disp_str(buffer: str) -> tuple[str, list[int]]:
5858
elif unicodedata.category(c).startswith("C"):
5959
c = r"\u%04x" % ord(c)
6060
s.append(c)
61-
b.append(str_width(c))
6261
b.extend([0] * (len(c) - 1))
6362
else:
6463
s.append(c)
@@ -254,6 +253,7 @@ class RefreshCache:
254253
pos: int = field(init=False)
255254
cxy: tuple[int, int] = field(init=False)
256255
dimensions: tuple[int, int] = field(init=False)
256+
invalidated: bool = False
257257

258258
def update_cache(self,
259259
reader: Reader,
@@ -266,14 +266,19 @@ def update_cache(self,
266266
self.pos = reader.pos
267267
self.cxy = reader.cxy
268268
self.dimensions = reader.console.width, reader.console.height
269+
self.invalidated = False
269270

270271
def valid(self, reader: Reader) -> bool:
272+
if self.invalidated:
273+
return False
271274
dimensions = reader.console.width, reader.console.height
272275
dimensions_changed = dimensions != self.dimensions
273276
paste_changed = reader.in_bracketed_paste != self.in_bracketed_paste
274277
return not (dimensions_changed or paste_changed)
275278

276279
def get_cached_location(self, reader: Reader) -> tuple[int, int]:
280+
if self.invalidated:
281+
raise ValueError("Cache is invalidated")
277282
offset = 0
278283
earliest_common_pos = min(reader.pos, self.pos)
279284
num_common_lines = len(self.line_end_offsets)

Lib/_pyrepl/unix_console.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,8 @@ def get_event(self, block: bool = True) -> Event | None:
383383
Returns:
384384
- Event: Event object from the event queue.
385385
"""
386+
if not block and not self.wait(timeout=0):
387+
return None
386388
while self.event_queue.empty():
387389
while True:
388390
try:
@@ -397,8 +399,6 @@ def get_event(self, block: bool = True) -> Event | None:
397399
raise
398400
else:
399401
break
400-
if not block:
401-
break
402402
return self.event_queue.get()
403403

404404
def wait(self, timeout: float | None = None) -> bool:

Lib/asyncio/__main__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ def run(self):
106106
if os.getenv("PYTHON_BASIC_REPL"):
107107
raise RuntimeError("user environment requested basic REPL")
108108
if not os.isatty(sys.stdin.fileno()):
109-
raise OSError(errno.ENOTTY, "tty required", "stdin")
109+
return_code = errno.ENOTTY
110+
raise OSError(return_code, "tty required", "stdin")
110111

111112
# This import will fail on operating systems with no termios.
112113
from _pyrepl.simple_interact import (

Lib/test/libregrtest/logger.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ def log(self, line: str = '') -> None:
4343

4444
def get_load_avg(self) -> float | None:
4545
if hasattr(os, 'getloadavg'):
46-
return os.getloadavg()[0]
46+
try:
47+
return os.getloadavg()[0]
48+
except OSError:
49+
pass
4750
if self.win_load_tracker is not None:
4851
return self.win_load_tracker.getloadavg()
4952
return None

Lib/test/test_ast.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1821,6 +1821,12 @@ def test_get_docstring(self):
18211821
node = ast.parse('async def foo():\n """spam\n ham"""')
18221822
self.assertEqual(ast.get_docstring(node.body[0]), 'spam\nham')
18231823

1824+
node = ast.parse('async def foo():\n """spam\n ham"""')
1825+
self.assertEqual(ast.get_docstring(node.body[0], clean=False), 'spam\n ham')
1826+
1827+
node = ast.parse('x')
1828+
self.assertRaises(TypeError, ast.get_docstring, node.body[0])
1829+
18241830
def test_get_docstring_none(self):
18251831
self.assertIsNone(ast.get_docstring(ast.parse('')))
18261832
node = ast.parse('x = "not docstring"')
@@ -1845,6 +1851,9 @@ def test_get_docstring_none(self):
18451851
node = ast.parse('async def foo():\n x = "not docstring"')
18461852
self.assertIsNone(ast.get_docstring(node.body[0]))
18471853

1854+
node = ast.parse('async def foo():\n 42')
1855+
self.assertIsNone(ast.get_docstring(node.body[0]))
1856+
18481857
def test_multi_line_docstring_col_offset_and_lineno_issue16806(self):
18491858
node = ast.parse(
18501859
'"""line one\nline two"""\n\n'

Lib/test/test_pyrepl/support.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ def code_to_events(code: str):
3838
yield Event(evt="key", data=c, raw=bytearray(c.encode("utf-8")))
3939

4040

41+
def clean_screen(screen: Iterable[str]):
42+
"""Cleans color and console characters out of a screen output.
43+
44+
This is useful for screen testing, it increases the test readability since
45+
it strips out all the unreadable side of the screen.
46+
"""
47+
output = []
48+
for line in screen:
49+
if line.startswith(">>>") or line.startswith("..."):
50+
line = line[3:]
51+
output.append(line)
52+
return "\n".join(output).strip()
53+
54+
4155
def prepare_reader(console: Console, **kwargs):
4256
config = ReadlineConfig(readline_completer=kwargs.pop("readline_completer", None))
4357
reader = ReadlineAlikeReader(console=console, config=config)

0 commit comments

Comments
 (0)