8000 Sync with main · python/cpython@007ccb5 · GitHub
[go: up one dir, main page]

Skip to content

Commit 007ccb5

Browse files
Sync with main
2 parents 5b0d0f4 + b53bad6 commit 007ccb5

17 files changed

+488
-49
lines changed

Doc/library/ctypes.rst

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,14 @@ integer, string, bytes, a :mod:`ctypes` instance, or an object with an
466466
Return types
467467
^^^^^^^^^^^^
468468

469+
.. testsetup::
470+
471+
from ctypes import CDLL, c_char, c_char_p
472+
from ctypes.util import find_library
473+
libc = CDLL(find_library('c'))
474+
strchr = libc.strchr
475+
476+
469477
By default functions are assumed to return the C :c:expr:`int` type. Other
470478
return types can be specified by setting the :attr:`restype` attribute of the
471479
function object.
@@ -502,18 +510,19 @@ If you want to avoid the ``ord("x")`` calls above, you can set the
502510
:attr:`argtypes` attribute, and the second argument will be converted from a
503511
single character Python bytes object into a C char::
504512

513+
.. doctest::
514+
505515
>>> strchr.restype = c_char_p
506516
>>> strchr.argtypes = [c_char_p, c_char]
507517
>>> strchr(b"abcdef", b"d")
508-
'def'
518+
b'def'
509519
>>> strchr(b"abcdef", b"def")
510520
Traceback (most recent call last):
511-
File "<stdin>", line 1, in <module>
512-
ArgumentError: argument 2: TypeError: one character string expected
521+
ctypes.ArgumentError: argument 2: TypeError: one character bytes, bytearray or integer expected
513522
>>> print(strchr(b"abcdef", b"x"))
514523
None
515524
>>> strchr(b"abcdef", b"d")
516-
'def'
525+
b'def'
517526
>>>
518527

519528
You can also use a callable Python object (a function or a class for example) as

Doc/library/uuid.rst

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,46 @@ of the :attr:`variant` attribute:
261261
internal format of UUIDs, and methods of generating UUIDs.
262262

263263

264+
.. _uuid-cli:
265+
266+
Command-Line Usage
267+
------------------
268+
269+
.. versionadded:: 3.12
270+
271+
The :mod:`uuid` module can be executed as a script from the command line.
272+
273+
.. code-block:: sh
274+
275+
python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-ns NAMESPACE] [-n NAME]
276+
277+
The following options are accepted:
278+
279+
.. program:: uuid
280+
281+
.. cmdoption:: -h, --help
282+
283+
Show the help message and exit.
284+
285+
.. cmdoption:: -u <uuid>
286+
--uuid <uuid>
287+
288+
Specify the function name to use to generate the uuid. By default :func:`uuid4`
289+
is used.
290+
291+
.. cmdoption:: -ns <namespace>
292+
--namespace <namespace>
293+
294+
The namespace used as part of generating the uuid. Only required for
295+
:func:`uuid3` / :func:`uuid5` functions.
296+
297+
.. cmdoption:: -n <name>
298+
--name <name>
299+
300+
The name used as part of generating the uuid. Only required for
301+
:func:`uuid3` / :func:`uuid5` functions.
302+
303+
264304
.. _uuid-example:
265305

266306
Example
@@ -301,3 +341,22 @@ Here are some examples of typical usage of the :mod:`uuid` module::
301341
>>> uuid.UUID(bytes=x.bytes)
302342
UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
303343

344+
345+
.. _uuid-cli-example:
346+
347+
Command-Line Example
348+
--------------------
349+
350+
Here are some examples of typical usage of the :mod:`uuid` command line interface:
351+
352+
.. code-block:: shell
353+
354+
# generate a random uuid - by default uuid4() is used
355+
$ python -m uuid
356+
357+
# generate a uuid using uuid1()
358+
$ python -m uuid -u uuid1
359+
360+
# generate a uuid using uuid5
361+
$ python -m uuid -u uuid5 -ns NAMESPACE_URL -n example.com
362+

Doc/whatsnew/3.12.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,12 @@ unicodedata
327327
* The Unicode database has been updated to version 15.0.0. (Contributed by
328328
Benjamin Peterson in :gh:`96734`).
329329

330+
uuid
331+
----
332+
333+
* Add a :ref:`command-line interface <uuid-cli>`.
334+
(Contributed by Adam Chhina in :gh:`88597`.)
335+
330336
tempfile
331337
--------
332338

Lib/test/support/ast_helper.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import ast
2+
3+
class ASTTestMixin:
4+
"""Test mixing to have basic assertions for AST nodes."""
5+
6+
def assertASTEqual(self, ast1, ast2):
7+
# Ensure the comparisons start at an AST node
8+
self.assertIsInstance(ast1, ast.AST)
9+
self.assertIsInstance(ast2, ast.AST)
10+
11+
# An AST comparison routine modeled after ast.dump(), but
12+
# instead of string building, it traverses the two trees
13+
# in lock-step.
14+
def traverse_compare(a, b, missing=object()):
15+
if type(a) is not type(b):
16+
self.fail(f"{type(a)!r} is not {type(b)!r}")
17+
if isinstance(a, ast.AST):
18+
for field in a._fields:
19+
value1 = getattr(a, field, missing)
20+
value2 = getattr(b, field, missing)
21+
# Singletons are equal by definition, so further
22+
# testing can be skipped.
23+
if value1 is not value2:
24+
traverse_compare(value1, value2)
25+
elif isinstance(a, list):
26+
try:
27+
for node1, node2 in zip(a, b, strict=True):
28+
traverse_compare(node1, node2)
29+
except ValueError:
30+
# Attempt a "pretty" error ala assertSequenceEqual()
31+
len1 = len(a)
32+
len2 = len(b)
33+
if len1 > len2:
34+
what = "First"
35+
diff = len1 - len2
36+
else:
37+
what = "Second"
38+
diff = len2 - len1
39+
msg = f"{what} list contains {diff} additional elements."
40+
raise self.failureException(msg) from None
41+
elif a != b:
42+
self.fail(f"{a!r} != {b!r}")
43+
traverse_compare(ast1, ast2)

Lib/test/test_ast.py

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from textwrap import dedent
1212

1313
from test import support
14+
from test.support.ast_helper import ASTTestMixin
1415

1516
def to_tuple(t):
1617
if t is None or isinstance(t, (str, int, complex)) or t is Ellipsis:
@@ -2290,9 +2291,10 @@ def test_source_segment_missing_info(self):
22902291
self.assertIsNone(ast.get_source_segment(s, x))
22912292
self.assertIsNone(ast.get_source_segment(s, y))
22922293

2293-
class NodeVisitorTests(unittest.TestCase):
2294+
class BaseNodeVisitorCases:
2295+
# Both `NodeVisitor` and `NodeTranformer` must raise these warnings:
22942296
def test_old_constant_nodes(self):
2295-
class Visitor(ast.NodeVisitor):
2297+
class Visitor(self.visitor_class):
22962298
def visit_Num(self, node):
22972299
log.append((node.lineno, 'Num', node.n))
22982300
def visit_Str(self, node):
@@ -2340,6 +2342,128 @@ def visit_Ellipsis(self, node):
23402342
])
23412343

23422344

2345+
class NodeVisitorTests(BaseNodeVisitorCases, unittest.TestCase):
2346+
visitor_class = ast.NodeVisitor
2347+
2348+
2349+
class NodeTransformerTests(ASTTestMixin, BaseNodeVisitorCases, unittest.TestCase):
2350+
visitor_class = ast.NodeTransformer
2351+
2352+
def assertASTTransformation(self, tranformer_class,
2353+
initial_code, expected_code):
2354+
initial_ast = ast.parse(dedent(initial_code))
2355+
expected_ast = ast.parse(dedent(expected_code))
2356+
2357+
tranformer = tranformer_class()
2358+
result_ast = ast.fix_missing_locations(tranformer.visit(initial_ast))
2359+
2360+
self.assertASTEqual(result_ast, expected_ast)
2361+
2362+
def test_node_remove_single(self):
2363+
code = 'def func(arg) -> SomeType: ...'
2364+
expected = 'def func(arg): ...'
2365+
2366+
# Since `FunctionDef.returns` is defined as a single value, we test
2367+
# the `if isinstance(old_value, AST):` branch here.
2368+
class SomeTypeRemover(ast.NodeTransformer):
2369+
def visit_Name(self, node: ast.Name):
2370+
self.generic_visit(node)
2371+
if node.id == 'SomeType':
2372+
return None
2373+
return node
2374+
2375+
self.assertASTTransformation(SomeTypeRemover, code, expected)
2376+
2377+
def test_node_remove_from_list(self):
2378+
code = """
2379+
def func(arg):
2380+
print(arg)
2381+
yield arg
2382+
"""
2383+
expected = """
2384+
def func(arg):
2385+
print(arg)
2386+
"""
2387+
2388+
# Since `FunctionDef.body` is defined as a list, we test
2389+
# the `if isinstance(old_value, list):` branch here.
2390+
class YieldRemover(ast.NodeTransformer):
2391+
def visit_Expr(self, node: ast.Expr):
2392+
self.generic_visit(node)
2393+
if isinstance(node.value, ast.Yield):
2394+
return None # Remove `yield` from a function
2395+
return node
2396+
2397+
self.assertASTTransformation(YieldRemover, code, expected)
2398+
2399+
def test_node_return_list(self):
2400+
code = """
2401+
class DSL(Base, kw1=True): ...
2402+
"""
2403+
expected = """
2404+
class DSL(Base, kw1=True, kw2=True, kw3=False): ...
2405+
"""
2406+
2407+
class ExtendKeywords(ast.NodeTransformer):
2408+
def visit_keyword(self, node: ast.keyword):
2409+
self.generic_visit(node)
2410+
if node.arg == 'kw1':
2411+
return [
2412+
node,
2413+
ast.keyword('kw2', ast.Constant(True)),
2414+
ast.keyword('kw3', ast.Constant(False)),
2415+
]
2416+
return node
2417+
2418+
self.assertASTTransformation(ExtendKeywords, code, expected)
2419+
2420+
def test_node_mutate(self):
2421+
code = """
2422+
def func(arg):
2423+
print(arg)
2424+
"""
2425+
expected = """
2426+
def func(arg):
2427+
log(arg)
2428+
"""
2429+
2430+
class PrintToLog(ast.NodeTransformer):
2431+
def visit_Call(self, node: ast.Call):
2432+
self.generic_visit(node)
2433+
if isinstance(node.func, ast.Name) and node.func.id == 'print':
2434+
node.func.id = 'log'
2435+
return node
2436+
2437+
self.assertASTTransformation(PrintToLog, code, expected)
2438+
2439+
def test_node_replace(self):
2440+
code = """
2441+
def func(arg):
2442+
print(arg)
2443+
"""
2444+
expected = """
2445+
def func(arg):
2446+
logger.log(arg, debug=True)
2447+
"""
2448+
2449+
class PrintToLog(ast.NodeTransformer):
2450+
def visit_Call(self, node: ast.Call):
2451+
self.generic_visit(node)
2452+
if isinstance(node.func, ast.Name) and node.func.id == 'print':
2453+
return ast.Call(
2454+
func=ast.Attribute(
2455+
ast.Name('logger', ctx=ast.Load()),
2456+
attr='log',
2457+
ctx=ast.Load(),
2458+
),
2459+
args=node.args,
2460+
keywords=[ast.keyword('debug', ast.Constant(True))],
2461+
)
2462+
return node
2463+
2464+
self.assertASTTransformation(PrintToLog, code, expected)
2465+
2466+
23432467
@support.cpython_only
23442468
class ModuleStateTests(unittest.TestCase):
23452469
# bpo-41194, bpo-41261, bpo-41631: The _ast module uses a global state.

Lib/test/test_ctypes/test_functions.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,23 @@ class X(object, _SimpleCData):
5454
class X(object, Structure):
5555
_fields_ = []
5656

57+
def test_c_char_parm(self):
58+
proto = CFUNCTYPE(c_int, c_char)
59+
def callback(*args):
60+
return 0
61+
62+
callback = proto(callback)
63+
64+
self.assertEqual(callback(b"a"), 0)
65+
66+
with self.assertRaises(ArgumentError) as cm:
67+
callback(b"abc")
68+
69+
self.assertEqual(str(cm.exception),
70+
"argument 1: TypeError: one character bytes, "
71+
"bytearray or integer expected")
72+
73+
5774
@need_symbol('c_wchar')
5875
def test_wchar_parm(self):
5976
f = dll._testfunc_i_bhilfd
@@ -62,6 +79,18 @@ def test_wchar_parm(self):
6279
self.assertEqual(result, 139)
6380
self.assertEqual(type(result), int)
6481

82+
with self.assertRaises(ArgumentError) as cm:
83+
f(1, 2, 3, 4, 5.0, 6.0)
84+
self.assertEqual(str(cm.exception),
85+
"argument 2: TypeError: unicode string expected "
86+
"instead of int instance")
87+
88+
with self.assertRaises(ArgumentError) as cm:
89+
f(1, "abc", 3, 4, 5.0, 6.0)
90+
self.assertEqual(str(cm.exception),
91+
"argument 2: TypeError: one character unicode string "
92+
"expected")
93+
6594
@need_symbol('c_wchar')
6695
def test_wchar_result(self):
6796
f = dll._testfunc_i_bhilfd

Lib/test/test_ctypes/test_parameters.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,29 @@ def test_cw_strings(self):
7878
pa = c_wchar_p.from_param(c_wchar_p("123"))
7979
self.assertEqual(type(pa), c_wchar_p)
8080

81+
def test_c_char(self):
82+
from ctypes import c_char
83+
84+
with self.assertRaises(TypeError) as cm:
85+
c_char.from_param(b"abc")
86+
self.assertEqual(str(cm.exception),
87+
"one character bytes, bytearray or integer expected")
88+
89+
@need_symbol('c_wchar')
90+
def test_c_wchar(self):
91+
from ctypes import c_wchar
92+
93+
with self.assertRaises(TypeError) as cm:
94+
c_wchar.from_param("abc")
95+
self.assertEqual(str(cm.exception),
96+
"one character unicode string expected")
97+
98+
99+
with self.assertRaises(TypeError) as cm:
100+
c_wchar.from_param(123)
101+
self.assertEqual(str(cm.exception),
102+
"unicode string expected instead of int instance")
103+
81104
def test_int_pointers(self):
82105
from ctypes import c_short, c_uint, c_int, c_long, POINTER, pointer
83106
LPINT = POINTER(c_int)

Lib/test/test_range.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ def test_range_iterators(self):
542542
for start in limits
543543
for end in limits
544544
for step in (-2**63, -2**31, -2, -1, 1, 2)]
545+
test_ranges += [(-2**63, 2**63-2, 1)] # regression test for gh-100810
545546

546547
for start, end, step in test_ranges:
547548
iter1 = range(start, end, step)

0 commit comments

Comments
 (0)
0