Skip to content

Commit 09b1069

Browse files
authored
Merge branch 'main' into gh-106293
2 parents 00e094c + 04dfc6f commit 09b1069

12 files changed

+96
-45
lines changed

Doc/library/timeit.rst

+9-9
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ can be used to compare three different expressions:
2727

2828
.. code-block:: shell-session
2929
30-
$ python -m timeit '"-".join(str(n) for n in range(100))'
30+
$ python -m timeit "'-'.join(str(n) for n in range(100))"
3131
10000 loops, best of 5: 30.2 usec per loop
32-
$ python -m timeit '"-".join([str(n) for n in range(100)])'
32+
$ python -m timeit "'-'.join([str(n) for n in range(100)])"
3333
10000 loops, best of 5: 27.5 usec per loop
34-
$ python -m timeit '"-".join(map(str, range(100)))'
34+
$ python -m timeit "'-'.join(map(str, range(100)))"
3535
10000 loops, best of 5: 23.2 usec per loop
3636
3737
This can be achieved from the :ref:`python-interface` with::
@@ -277,9 +277,9 @@ It is possible to provide a setup statement that is executed only once at the be
277277

278278
.. code-block:: shell-session
279279
280-
$ python -m timeit -s 'text = "sample string"; char = "g"' 'char in text'
280+
$ python -m timeit -s "text = 'sample string'; char = 'g'" "char in text"
281281
5000000 loops, best of 5: 0.0877 usec per loop
282-
$ python -m timeit -s 'text = "sample string"; char = "g"' 'text.find(char)'
282+
$ python -m timeit -s "text = 'sample string'; char = 'g'" "text.find(char)"
283283
1000000 loops, best of 5: 0.342 usec per loop
284284
285285
In the output, there are three fields. The loop count, which tells you how many
@@ -313,14 +313,14 @@ to test for missing and present object attributes:
313313

314314
.. code-block:: shell-session
315315
316-
$ python -m timeit 'try:' ' str.__bool__' 'except AttributeError:' ' pass'
316+
$ python -m timeit "try:" " str.__bool__" "except AttributeError:" " pass"
317317
20000 loops, best of 5: 15.7 usec per loop
318-
$ python -m timeit 'if hasattr(str, "__bool__"): pass'
318+
$ python -m timeit "if hasattr(str, '__bool__'): pass"
319319
50000 loops, best of 5: 4.26 usec per loop
320320
321-
$ python -m timeit 'try:' ' int.__bool__' 'except AttributeError:' ' pass'
321+
$ python -m timeit "try:" " int.__bool__" "except AttributeError:" " pass"
322322
200000 loops, best of 5: 1.43 usec per loop
323-
$ python -m timeit 'if hasattr(int, "__bool__"): pass'
323+
$ python -m timeit "if hasattr(int, '__bool__'): pass"
324324
100000 loops, best of 5: 2.23 usec per loop
325325
326326
::

Doc/using/cmdline.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ source.
109109
Many standard library modules contain code that is invoked on their execution
110110
as a script. An example is the :mod:`timeit` module::
111111

112-
python -m timeit -s 'setup here' 'benchmarked code here'
112+
python -m timeit -s "setup here" "benchmarked code here"
113113
python -m timeit -h # for details
114114

115115
.. audit-event:: cpython.run_module module-name cmdoption-m

Lib/test/test_genericalias.py

+8
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ class MyList(list):
209209
def test_repr(self):
210210
class MyList(list):
211211
pass
212+
class MyGeneric:
213+
__class_getitem__ = classmethod(GenericAlias)
214+
212215
self.assertEqual(repr(list[str]), 'list[str]')
213216
self.assertEqual(repr(list[()]), 'list[()]')
214217
self.assertEqual(repr(tuple[int, ...]), 'tuple[int, ...]')
@@ -221,6 +224,11 @@ class MyList(list):
221224
self.assertTrue(repr(MyList[int]).endswith('.BaseTest.test_repr.<locals>.MyList[int]'))
222225
self.assertEqual(repr(list[str]()), '[]') # instances should keep their normal repr
223226

227+
# gh-105488
228+
self.assertTrue(repr(MyGeneric[int]).endswith('MyGeneric[int]'))
229+
self.assertTrue(repr(MyGeneric[[]]).endswith('MyGeneric[[]]'))
230+
self.assertTrue(repr(MyGeneric[[int, str]]).endswith('MyGeneric[[int, str]]'))
231+
224232
def test_exposed_type(self):
225233
import types
226234
a = types.GenericAlias(list, int)

Lib/test/test_pathlib.py

+12-20
Original file line numberDiff line numberDiff line change
@@ -1620,21 +1620,13 @@ def cleanup():
16201620
# Relative symlinks.
16211621
os.symlink('fileA', join('linkA'))
16221622
os.symlink('non-existing', join('brokenLink'))
1623-
self.dirlink('dirB', join('linkB'))
1624-
self.dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC'))
1623+
os.symlink('dirB', join('linkB'), target_is_directory=True)
1624+
os.symlink(os.path.join('..', 'dirB'), join('dirA', 'linkC'), target_is_directory=True)
16251625
# This one goes upwards, creating a loop.
1626-
self.dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD'))
1626+
os.symlink(os.path.join('..', 'dirB'), join('dirB', 'linkD'), target_is_directory=True)
16271627
# Broken symlink (pointing to itself).
16281628
os.symlink('brokenLinkLoop', join('brokenLinkLoop'))
16291629

1630-
if os.name == 'nt':
1631-
# Workaround for http://bugs.python.org/issue13772.
1632-
def dirlink(self, src, dest):
1633-
os.symlink(src, dest, target_is_directory=True)
1634-
else:
1635-
def dirlink(self, src, dest):
1636-
os.symlink(src, dest)
1637-
16381630
def assertSame(self, path_a, path_b):
16391631
self.assertTrue(os.path.samefile(str(path_a), str(path_b)),
16401632
"%r and %r don't point to the same file" %
@@ -2118,8 +2110,8 @@ def test_resolve_common(self):
21182110
d = os_helper._longpath(tempfile.mkdtemp(suffix='-dirD',
21192111
dir=os.getcwd()))
21202112
self.addCleanup(os_helper.rmtree, d)
2121-
os.symlink(os.path.join(d), join('dirA', 'linkX'))
2122-
os.symlink(join('dirB'), os.path.join(d, 'linkY'))
2113+
P(BASE, 'dirA', 'linkX').symlink_to(d)
2114+
P(BASE, str(d), 'linkY').symlink_to(join('dirB'))
21232115
p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB')
21242116
self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB'))
21252117
# Non-strict
@@ -2140,9 +2132,9 @@ def test_resolve_common(self):
21402132
def test_resolve_dot(self):
21412133
# See http://web.archive.org/web/20200623062557/https://bitbucket.org/pitrou/pathlib/issues/9/
21422134
p = self.cls(BASE)
2143-
self.dirlink('.', join('0'))
2144-
self.dirlink(os.path.join('0', '0'), join('1'))
2145-
self.dirlink(os.path.join('1', '1'), join('2'))
2135+
p.joinpath('0').symlink_to('.', target_is_directory=True)
2136+
p.joinpath('1').symlink_to(os.path.join('0', '0'), target_is_directory=True)
2137+
p.joinpath('2').symlink_to(os.path.join('1', '1'), target_is_directory=True)
21462138
q = p / '2'
21472139
self.assertEqual(q.resolve(strict=True), p)
21482140
r = q / '3' / '4'
@@ -2320,10 +2312,10 @@ def test_parts_interning(self):
23202312
def _check_complex_symlinks(self, link0_target):
23212313
# Test solving a non-looping chain of symlinks (issue #19887).
23222314
P = self.cls(BASE)
2323-
self.dirlink(os.path.join('link0', 'link0'), join('link1'))
2324-
self.dirlink(os.path.join('link1', 'link1'), join('link2'))
2325-
self.dirlink(os.path.join('link2', 'link2'), join('link3'))
2326-
self.dirlink(link0_target, join('link0'))
2315+
P.joinpath('link1').symlink_to(os.path.join('link0', 'link0'), target_is_directory=True)
2316+
P.joinpath('link2').symlink_to(os.path.join('link1', 'link1'), target_is_directory=True)
2317+
P.joinpath('link3').symlink_to(os.path.join('link2', 'link2'), target_is_directory=True)
2318+
P.joinpath('link0').symlink_to(link0_target, target_is_directory=True)
23272319

23282320
# Resolve absolute paths.
23292321
p = (P / 'link0').resolve()

Lib/test/test_type_aliases.py

+16
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,16 @@ def test_subscripting(self):
142142

143143
def test_repr(self):
144144
type Simple = int
145+
type VeryGeneric[T, *Ts, **P] = Callable[P, tuple[T, *Ts]]
146+
145147
self.assertEqual(repr(Simple), "Simple")
148+
self.assertEqual(repr(VeryGeneric), "VeryGeneric")
149+
self.assertEqual(repr(VeryGeneric[int, bytes, str, [float, object]]),
150+
"VeryGeneric[int, bytes, str, [float, object]]")
151+
self.assertEqual(repr(VeryGeneric[int, []]),
152+
"VeryGeneric[int, []]")
153+
self.assertEqual(repr(VeryGeneric[int, [VeryGeneric[int], list[str]]]),
154+
"VeryGeneric[int, [VeryGeneric[int], list[str]]]")
146155

147156
def test_recursive_repr(self):
148157
type Recursive = Recursive
@@ -151,6 +160,13 @@ def test_recursive_repr(self):
151160
type X = list[Y]
152161
type Y = list[X]
153162
self.assertEqual(repr(X), "X")
163+
self.assertEqual(repr(Y), "Y")
164+
165+
type GenericRecursive[X] = list[X | GenericRecursive[X]]
166+
self.assertEqual(repr(GenericRecursive), "GenericRecursive")
167+
self.assertEqual(repr(GenericRecursive[int]), "GenericRecursive[int]")
168+
self.assertEqual(repr(GenericRecursive[GenericRecursive[int]]),
169+
"GenericRecursive[GenericRecursive[int]]")
154170

155171

156172
class TypeAliasConstructorTest(unittest.TestCase):

Lib/test/test_unparse.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ class DirectoryTestCase(ASTTestCase):
705705
test_directories = (lib_dir, lib_dir / "test")
706706
run_always_files = {"test_grammar.py", "test_syntax.py", "test_compile.py",
707707
"test_ast.py", "test_asdl_parser.py", "test_fstring.py",
708-
"test_patma.py"}
708+
"test_patma.py", "test_type_alias.py", "test_type_params.py"}
709709

710710
_files_to_test = None
711711

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Change the repr of ``ParamSpec`` list of args in ``types.GenericAlias``.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Make ``end_lineno`` and ``end_col_offset`` required on ``type_param`` ast
2+
nodes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Make timeit doc command lines compatible with Windows by using double quotes
2+
for arguments. This works on linux and macOS also.

Objects/genericaliasobject.c

+37-1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,36 @@ ga_repr_item(_PyUnicodeWriter *writer, PyObject *p)
121121
return err;
122122
}
123123

124+
static int
125+
ga_repr_items_list(_PyUnicodeWriter *writer, PyObject *p)
126+
{
127+
assert(PyList_CheckExact(p));
128+
129+
Py_ssize_t len = PyList_GET_SIZE(p);
130+
131+
if (_PyUnicodeWriter_WriteASCIIString(writer, "[", 1) < 0) {
132+
return -1;
133+
}
134+
135+
for (Py_ssize_t i = 0; i < len; i++) {
136+
if (i > 0) {
137+
if (_PyUnicodeWriter_WriteASCIIString(writer, ", ", 2) < 0) {
138+
return -1;
139+
}
140+
}
141+
PyObject *item = PyList_GET_ITEM(p, i);
142+
if (ga_repr_item(writer, item) < 0) {
143+
return -1;
144+
}
145+
}
146+
147+
if (_PyUnicodeWriter_WriteASCIIString(writer, "]", 1) < 0) {
148+
return -1;
149+
}
150+
151+
return 0;
152+
}
153+
124154
static PyObject *
125155
ga_repr(PyObject *self)
126156
{
@@ -148,7 +178,13 @@ ga_repr(PyObject *self)
148178
}
149179
}
150180
PyObject *p = PyTuple_GET_ITEM(alias->args, i);
151-
if (ga_repr_item(&writer, p) < 0) {
181+
if (PyList_CheckExact(p)) {
182+
// Looks like we are working with ParamSpec's list of type args:
183+
if (ga_repr_items_list(&writer, p) < 0) {
184+
goto error;
185+
}
186+
}
187+
else if (ga_repr_item(&writer, p) < 0) {
152188
goto error;
153189
}
154190
}

Parser/Python.asdl

+1-1
Original file line numberDiff line numberDiff line change
@@ -148,5 +148,5 @@ module Python
148148
type_param = TypeVar(identifier name, expr? bound)
149149
| ParamSpec(identifier name)
150150
| TypeVarTuple(identifier name)
151-
attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)
151+
attributes (int lineno, int col_offset, int end_lineno, int end_col_offset)
152152
}

Python/Python-ast.c

+6-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)