Skip to content

Commit faa85f5

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents 2f13a4e + 0aeda29 commit faa85f5

23 files changed

+199
-39
lines changed

.github/workflows/require-pr-label.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ on:
44
pull_request:
55
types: [opened, reopened, labeled, unlabeled, synchronize]
66

7+
permissions:
8+
issues: read
9+
pull-requests: read
10+
711
jobs:
812
label:
913
name: DO-NOT-MERGE / unresolved review

Doc/whatsnew/3.12.rst

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ asyncio
282282
writing to sockets and uses :meth:`~socket.socket.sendmsg` if the platform
283283
supports it. (Contributed by Kumar Aditya in :gh:`91166`.)
284284

285+
* Added :func:`asyncio.eager_task_factory` and :func:`asyncio.create_eager_task_factory`
286+
functions to allow opting an event loop in to eager task execution,
287+
making some use-cases 2x to 5x faster.
288+
(Contributed by Jacob Bower & Itamar O in :gh:`102853`, :gh:`104140`, and :gh:`104138`)
289+
285290
* On Linux, :mod:`asyncio` uses :class:`~asyncio.PidfdChildWatcher` by default
286291
if :func:`os.pidfd_open` is available and functional instead of
287292
:class:`~asyncio.ThreadedChildWatcher`.
@@ -644,11 +649,6 @@ Optimizations
644649
* Speed up :class:`asyncio.Task` creation by deferring expensive string formatting.
645650
(Contributed by Itamar O in :gh:`103793`.)
646651

647-
* Added :func:`asyncio.eager_task_factory` and :func:`asyncio.create_eager_task_factory`
648-
functions to allow opting an event loop in to eager task execution,
649-
speeding up some use-cases by up to 50%.
650-
(Contributed by Jacob Bower & Itamar O in :gh:`102853`)
651-
652652

653653
CPython bytecode changes
654654
========================
@@ -1169,6 +1169,14 @@ Build Changes
11691169
optimization levels (0, 1, 2) at once.
11701170
(Contributed by Victor Stinner in :gh:`99289`.)
11711171

1172+
* Add platform triplets for 64-bit LoongArch:
1173+
1174+
* loongarch64-linux-gnusf
1175+
* loongarch64-linux-gnuf32
1176+
* loongarch64-linux-gnu
1177+
1178+
(Contributed by Zhang Na in :gh:`90656`.)
1179+
11721180

11731181
C API Changes
11741182
=============

Lib/asyncio/tasks.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -942,18 +942,31 @@ def callback():
942942

943943

944944
def create_eager_task_factory(custom_task_constructor):
945+
"""Create a function suitable for use as a task factory on an event-loop.
945946
946-
if "eager_start" not in inspect.signature(custom_task_constructor).parameters:
947-
raise TypeError(
948-
"Provided constructor does not support eager task execution")
947+
Example usage:
948+
949+
loop.set_task_factory(
950+
asyncio.create_eager_task_factory(my_task_constructor))
951+
952+
Now, tasks created will be started immediately (rather than being first
953+
scheduled to an event loop). The constructor argument can be any callable
954+
that returns a Task-compatible object and has a signature compatible
955+
with `Task.__init__`; it must have the `eager_start` keyword argument.
956+
957+
Most applications will use `Task` for `custom_task_constructor` and in
958+
this case there's no need to call `create_eager_task_factory()`
959+
directly. Instead the global `eager_task_factory` instance can be
960+
used. E.g. `loop.set_task_factory(asyncio.eager_task_factory)`.
961+
"""
949962

950963
def factory(loop, coro, *, name=None, context=None):
951964
return custom_task_constructor(
952965
coro, loop=loop, name=name, context=context, eager_start=True)
953966

954-
955967
return factory
956968

969+
957970
eager_task_factory = create_eager_task_factory(Task)
958971

959972

Lib/test/libregrtest/utils.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,13 @@ def clear_caches():
210210
else:
211211
fractions._hash_algorithm.cache_clear()
212212

213+
try:
214+
inspect = sys.modules['inspect']
215+
except KeyError:
216+
pass
217+
else:
218+
inspect._shadowed_dict_from_mro_tuple.cache_clear()
219+
213220

214221
def get_build_info():
215222
# Get most important configure and build options as a list of strings.

Lib/test/support/bytecode_helper.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def complete_insts_info(self, insts):
124124
class CodegenTestCase(CompilationStepTestCase):
125125

126126
def generate_code(self, ast):
127-
insts = compiler_codegen(ast, "my_file.py", 0)
127+
insts, _ = compiler_codegen(ast, "my_file.py", 0)
128128
return insts
129129

130130

Lib/test/test_compiler_assemble.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def test_simple_expr(self):
5252
'filename' : 'avg.py',
5353
'name' : 'avg',
5454
'qualname' : 'stats.avg',
55-
'consts' : [2],
55+
'consts' : {2 : 0},
5656
'argcount' : 2,
5757
'varnames' : {'x' : 0, 'y' : 1},
5858
}

Lib/test/test_urlparse.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,20 @@ class UrlParseTestCase(unittest.TestCase):
7272

7373
def checkRoundtrips(self, url, parsed, split):
7474
result = urllib.parse.urlparse(url)
75-
self.assertEqual(result, parsed)
75+
self.assertSequenceEqual(result, parsed)
7676
t = (result.scheme, result.netloc, result.path,
7777
result.params, result.query, result.fragment)
78-
self.assertEqual(t, parsed)
78+
self.assertSequenceEqual(t, parsed)
7979
# put it back together and it should be the same
8080
result2 = urllib.parse.urlunparse(result)
81-
self.assertEqual(result2, url)
82-
self.assertEqual(result2, result.geturl())
81+
self.assertSequenceEqual(result2, url)
82+
self.assertSequenceEqual(result2, result.geturl())
8383

8484
# the result of geturl() is a fixpoint; we can always parse it
8585
# again to get the same result:
8686
result3 = urllib.parse.urlparse(result.geturl())
8787
self.assertEqual(result3.geturl(), result.geturl())
88-
self.assertEqual(result3, result)
88+
self.assertSequenceEqual(result3, result)
8989
self.assertEqual(result3.scheme, result.scheme)
9090
self.assertEqual(result3.netloc, result.netloc)
9191
self.assertEqual(result3.path, result.path)
@@ -99,18 +99,18 @@ def checkRoundtrips(self, url, parsed, split):
9999

100100
# check the roundtrip using urlsplit() as well
101101
result = urllib.parse.urlsplit(url)
102-
self.assertEqual(result, split)
102+
self.assertSequenceEqual(result, split)
103103
t = (result.scheme, result.netloc, result.path,
104104
result.query, result.fragment)
105-
self.assertEqual(t, split)
105+
self.assertSequenceEqual(t, split)
106106
result2 = urllib.parse.urlunsplit(result)
107-
self.assertEqual(result2, url)
108-
self.assertEqual(result2, result.geturl())
107+
self.assertSequenceEqual(result2, url)
108+
self.assertSequenceEqual(result2, result.geturl())
109109

110110
# check the fixpoint property of re-parsing the result of geturl()
111111
result3 = urllib.parse.urlsplit(result.geturl())
112112
self.assertEqual(result3.geturl(), result.geturl())
113-
self.assertEqual(result3, result)
113+
self.assertSequenceEqual(result3, result)
114114
self.assertEqual(result3.scheme, result.scheme)
115115
self.assertEqual(result3.netloc, result.netloc)
116116
self.assertEqual(result3.path, result.path)
@@ -162,10 +162,15 @@ def test_roundtrips(self):
162162
('svn+ssh', 'svn.zope.org', '/repos/main/ZConfig/trunk/',
163163
'', '')),
164164
('git+ssh://[email protected]/user/project.git',
165-
('git+ssh', '[email protected]','/user/project.git',
166-
'','',''),
167-
('git+ssh', '[email protected]','/user/project.git',
168-
'', '')),
165+
('git+ssh', '[email protected]','/user/project.git',
166+
'','',''),
167+
('git+ssh', '[email protected]','/user/project.git',
168+
'', '')),
169+
('itms-services://?action=download-manifest&url=https://example.com/app',
170+
('itms-services', '', '', '',
171+
'action=download-manifest&url=https://example.com/app', ''),
172+
('itms-services', '', '',
173+
'action=download-manifest&url=https://example.com/app', '')),
169174
]
170175
def _encode(t):
171176
return (t[0].encode('ascii'),

Lib/test/test_uu.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,34 @@ def test_newlines_escaped(self):
147147
uu.encode(inp, out, filename)
148148
self.assertIn(safefilename, out.getvalue())
149149

150+
def test_no_directory_traversal(self):
151+
relative_bad = b"""\
152+
begin 644 ../../../../../../../../tmp/test1
153+
$86)C"@``
154+
`
155+
end
156+
"""
157+
with self.assertRaisesRegex(uu.Error, 'directory'):
158+
uu.decode(io.BytesIO(relative_bad))
159+
if os.altsep:
160+
relative_bad_bs = relative_bad.replace(b'/', b'\\')
161+
with self.assertRaisesRegex(uu.Error, 'directory'):
162+
uu.decode(io.BytesIO(relative_bad_bs))
163+
164+
absolute_bad = b"""\
165+
begin 644 /tmp/test2
166+
$86)C"@``
167+
`
168+
end
169+
"""
170+
with self.assertRaisesRegex(uu.Error, 'directory'):
171+
uu.decode(io.BytesIO(absolute_bad))
172+
if os.altsep:
173+
absolute_bad_bs = absolute_bad.replace(b'/', b'\\')
174+
with self.assertRaisesRegex(uu.Error, 'directory'):
175+
uu.decode(io.BytesIO(absolute_bad_bs))
176+
177+
150178
class UUStdIOTest(unittest.TestCase):
151179

152180
def setUp(self):

Lib/urllib/parse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
'imap', 'wais', 'file', 'mms', 'https', 'shttp',
5555
'snews', 'prospero', 'rtsp', 'rtspu', 'rsync',
5656
'svn', 'svn+ssh', 'sftp', 'nfs', 'git', 'git+ssh',
57-
'ws', 'wss']
57+
'ws', 'wss', 'itms-services']
5858

5959
uses_params = ['', 'ftp', 'hdl', 'prospero', 'http', 'imap',
6060
'https', 'shttp', 'rtsp', 'rtspu', 'sip', 'sips',

Lib/uu.py

100755100644
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,14 @@ def decode(in_file, out_file=None, mode=None, quiet=False):
133133
# If the filename isn't ASCII, what's up with that?!?
134134
out_file = hdrfields[2].rstrip(b' \t\r\n\f').decode("ascii")
135135
if os.path.exists(out_file):
136-
raise Error('Cannot overwrite existing file: %s' % out_file)
136+
raise Error(f'Cannot overwrite existing file: {out_file}')
137+
if (out_file.startswith(os.sep) or
138+
f'..{os.sep}' in out_file or (
139+
os.altsep and
140+
(out_file.startswith(os.altsep) or
141+
f'..{os.altsep}' in out_file))
142+
):
143+
raise Error(f'Refusing to write to {out_file} due to directory traversal')
137144
if mode is None:
138145
mode = int(hdrfields[1], 8)
139146
#

0 commit comments

Comments
 (0)