[Python-checkins] gh-121035: Improve logging flow diagram for dark/light modes. (GH-121254)

2024-07-02 Thread vsajip
https://github.com/python/cpython/commit/bfe0e4d7696647a546110328510bdb98146ad2f2
commit: bfe0e4d7696647a546110328510bdb98146ad2f2
branch: main
author: Vinay Sajip 
committer: vsajip 
date: 2024-07-02T09:13:37+01:00
summary:

gh-121035: Improve logging flow diagram for dark/light modes. (GH-121254)

files:
M Doc/howto/logging.rst
M Doc/howto/logging_flow.svg

diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst
index 316b16aa796af4..9c55233e910f17 100644
--- a/Doc/howto/logging.rst
+++ b/Doc/howto/logging.rst
@@ -385,6 +385,44 @@ following diagram.
 .. raw:: html
:file: logging_flow.svg
 
+.. raw:: html
+
+   
+   /*
+* This snippet is needed to handle the case where a light or dark theme is
+* chosen via the theme is selected in the page. We call the existing 
handler
+* and then add a dark-theme class to the body when the dark theme is 
selected.
+* The SVG styling (above) then does the rest.
+*
+* If the pydoc theme is updated to set the dark-theme class, this snippet
+* won't be needed any more.
+*/
+   (function() {
+ var oldActivateTheme = activateTheme;
+
+ function updateBody(theme) {
+let elem = document.body;
+
+if (theme === 'dark') {
+elem.classList.add('dark-theme');
+}
+else {
+elem.classList.remove('dark-theme');
+}
+ }
+
+ activateTheme = function(theme) {
+oldActivateTheme(theme);
+updateBody(theme);
+ };
+ /*
+  * If the page is refreshed, make sure we update the body - the overriding
+  * of activateTheme won't have taken effect yet.
+  */
+  updateBody(localStorage.getItem('currentTheme') || 'auto');
+   })();
+   
+
 Loggers
 ^^^
 
diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg
index a5f656b1df0b42..9807323b7190d6 100644
--- a/Doc/howto/logging_flow.svg
+++ b/Doc/howto/logging_flow.svg
@@ -1,9 +1,9 @@
 http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";>
   
   
-  svg {
-background-color: transparent !important;
-  }
+svg {
+  background-color: transparent !important;
+}
 line {
   stroke: #00;
   fill: none;
@@ -16,7 +16,7 @@
   stroke-opacity: 1;
 }
 polygon.filled {
-  fill: #ff;
+  fill: #00;
 }
 polyline {
   fill: none;
@@ -36,6 +36,9 @@
   polygon, rect, polyline, line {
 stroke: #ff;
   }
+  polygon.filled {
+fill: #ff;
+  }
   text {
 fill: #ff;
   }
@@ -43,6 +46,15 @@
 filter: invert(100%) hue-rotate(180deg) saturate(1.25);
   }
 }
+body.dark-theme polygon, body.dark-theme rect, body.dark-theme polyline, 
body.dark-theme line {
+  stroke: #ff;
+}
+body.dark-theme polygon.filled {
+  fill: #ff;
+}
+body.dark-theme text {
+  fill: #ff;
+}
   
 
   
@@ -57,7 +69,7 @@
   
   
 Create
-LogRecord
+LogRecord
   
 
 
@@ -100,7 +112,7 @@
 
   
   
-Pass to
+Pass record to
 handlers of
 current logger
   
@@ -135,16 +147,17 @@
 
 
   
-  
-Use lastResort
-handler
+  
+Use
+lastResort
+handler
   
 
 
   
   
 Handler enabled for
-level of LogRecord?
+level of record?
   
 
 
@@ -292,7 +305,7 @@
   Yes
 
 
-  LogRecord passed
+  Record passed
   to handler
 
 

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] Move get_signal_name() to test.support (#121251)

2024-07-02 Thread vstinner
https://github.com/python/cpython/commit/7435f053b4a54372a2c43dee7a15c4b973f09209
commit: 7435f053b4a54372a2c43dee7a15c4b973f09209
branch: main
author: Victor Stinner 
committer: vstinner 
date: 2024-07-02T10:34:13+02:00
summary:

Move get_signal_name() to test.support (#121251)

* Move get_signal_name() from test.libregrtest to test.support.
* Use get_signal_name() in support.script_helper.
* support.script_helper now decodes stdout and stderr from UTF-8,
  instead of ASCII, if a command failed.

files:
M Lib/test/libregrtest/run_workers.py
M Lib/test/libregrtest/utils.py
M Lib/test/support/__init__.py
M Lib/test/support/script_helper.py
M Lib/test/test_regrtest.py
M Lib/test/test_support.py

diff --git a/Lib/test/libregrtest/run_workers.py 
b/Lib/test/libregrtest/run_workers.py
index a71050e66db3bd..387ddf9614cf79 100644
--- a/Lib/test/libregrtest/run_workers.py
+++ b/Lib/test/libregrtest/run_workers.py
@@ -22,7 +22,7 @@
 from .single import PROGRESS_MIN_TIME
 from .utils import (
 StrPath, TestName,
-format_duration, print_warning, count, plural, get_signal_name)
+format_duration, print_warning, count, plural)
 from .worker import create_worker_process, USE_PROCESS_GROUP
 
 if MS_WINDOWS:
@@ -366,7 +366,7 @@ def _runtest(self, test_name: TestName) -> 
MultiprocessResult:
   err_msg=None,
   state=State.TIMEOUT)
 if retcode != 0:
-name = get_signal_name(retcode)
+name = support.get_signal_name(retcode)
 if name:
 retcode = f"{retcode} ({name})"
 raise WorkerError(self.test_name, f"Exit code {retcode}", 
stdout,
diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py
index 0167742d388a2c..0197e50125d96e 100644
--- a/Lib/test/libregrtest/utils.py
+++ b/Lib/test/libregrtest/utils.py
@@ -685,35 +685,6 @@ def cleanup_temp_dir(tmp_dir: StrPath):
 print("Remove file: %s" % name)
 os_helper.unlink(name)
 
-WINDOWS_STATUS = {
-0xC005: "STATUS_ACCESS_VIOLATION",
-0xC0FD: "STATUS_STACK_OVERFLOW",
-0xC13A: "STATUS_CONTROL_C_EXIT",
-}
-
-def get_signal_name(exitcode):
-if exitcode < 0:
-signum = -exitcode
-try:
-return signal.Signals(signum).name
-except ValueError:
-pass
-
-# Shell exit code (ex: WASI build)
-if 128 < exitcode < 256:
-signum = exitcode - 128
-try:
-return signal.Signals(signum).name
-except ValueError:
-pass
-
-try:
-return WINDOWS_STATUS[exitcode]
-except KeyError:
-pass
-
-return None
-
 
 ILLEGAL_XML_CHARS_RE = re.compile(
 '['
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index dbea070929be9b..18455bb6e0ff52 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -2632,3 +2632,35 @@ def initialized_with_pyrepl():
 """Detect whether PyREPL was used during Python initialization."""
 # If the main module has a __file__ attribute it's a Python module, which 
means PyREPL.
 return hasattr(sys.modules["__main__"], "__file__")
+
+
+WINDOWS_STATUS = {
+0xC005: "STATUS_ACCESS_VIOLATION",
+0xC0FD: "STATUS_STACK_OVERFLOW",
+0xC13A: "STATUS_CONTROL_C_EXIT",
+}
+
+def get_signal_name(exitcode):
+import signal
+
+if exitcode < 0:
+signum = -exitcode
+try:
+return signal.Signals(signum).name
+except ValueError:
+pass
+
+# Shell exit code (ex: WASI build)
+if 128 < exitcode < 256:
+signum = exitcode - 128
+try:
+return signal.Signals(signum).name
+except ValueError:
+pass
+
+try:
+return WINDOWS_STATUS[exitcode]
+except KeyError:
+pass
+
+return None
diff --git a/Lib/test/support/script_helper.py 
b/Lib/test/support/script_helper.py
index 65e0bc199e7f0b..d0be3179b0efa3 100644
--- a/Lib/test/support/script_helper.py
+++ b/Lib/test/support/script_helper.py
@@ -70,23 +70,25 @@ def fail(self, cmd_line):
 out = b'(... truncated stdout ...)' + out[-maxlen:]
 if len(err) > maxlen:
 err = b'(... truncated stderr ...)' + err[-maxlen:]
-out = out.decode('ascii', 'replace').rstrip()
-err = err.decode('ascii', 'replace').rstrip()
-raise AssertionError("Process return code is %d\n"
- "command line: %r\n"
- "\n"
- "stdout:\n"
- "---\n"
- "%s\n"
- "---\n"
- "\n"
- "stderr:\n"
- "---\n"
- "%s\n"
- "---"
- % (self.rc, cmd_line,
-

[Python-checkins] gh-121245: Amend d611c4c8e9 (correct import) (#121255)

2024-07-02 Thread ambv
https://github.com/python/cpython/commit/7a807c3efaa83f1e4fb9b791579b47a0a1fd47de
commit: 7a807c3efaa83f1e4fb9b791579b47a0a1fd47de
branch: main
author: Sergey B Kirpichev 
committer: ambv 
date: 2024-07-02T09:40:01Z
summary:

gh-121245: Amend d611c4c8e9 (correct import) (#121255)

Co-authored-by: Miro Hrončok 

files:
A Misc/NEWS.d/next/Library/2024-07-02-11-34-06.gh-issue-121245.sSkDAr.rst
M Lib/site.py

diff --git a/Lib/site.py b/Lib/site.py
index 9381f6f510eb46..daa56e158949db 100644
--- a/Lib/site.py
+++ b/Lib/site.py
@@ -526,8 +526,7 @@ def register_readline():
 
 def write_history():
 try:
-# _pyrepl.__main__ is executed as the __main__ module
-from __main__ import CAN_USE_PYREPL
+from _pyrepl.main import CAN_USE_PYREPL
 except ImportError:
 CAN_USE_PYREPL = False
 
diff --git 
a/Misc/NEWS.d/next/Library/2024-07-02-11-34-06.gh-issue-121245.sSkDAr.rst 
b/Misc/NEWS.d/next/Library/2024-07-02-11-34-06.gh-issue-121245.sSkDAr.rst
new file mode 100644
index 00..6e9dec2545166f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-07-02-11-34-06.gh-issue-121245.sSkDAr.rst
@@ -0,0 +1,2 @@
+Fix a bug in the handling of the command history of the new :term:`REPL` that 
caused
+the history file to be wiped at REPL exit.

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] gh-121210: handle nodes with missing attributes/fields in `ast.compare` (#121211)

2024-07-02 Thread kumaraditya303
https://github.com/python/cpython/commit/15232a0819a2f7e0f448f28f2e6081912d10e7cb
commit: 15232a0819a2f7e0f448f28f2e6081912d10e7cb
branch: main
author: Bénédikt Tran <10796600+picn...@users.noreply.github.com>
committer: kumaraditya303 
date: 2024-07-02T16:23:17+05:30
summary:

gh-121210: handle nodes with missing attributes/fields in `ast.compare` 
(#121211)

files:
A Misc/NEWS.d/next/Library/2024-07-01-11-23-18.gh-issue-121210.cD0zfn.rst
M Lib/ast.py
M Lib/test/test_ast.py

diff --git a/Lib/ast.py b/Lib/ast.py
index fb4d21b87d8bd0..a954d4a97d3c22 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -422,6 +422,8 @@ def compare(
 might differ in whitespace or similar details.
 """
 
+sentinel = object()  # handle the possibility of a missing attribute/field
+
 def _compare(a, b):
 # Compare two fields on an AST object, which may themselves be
 # AST objects, lists of AST objects, or primitive ASDL types
@@ -449,8 +451,14 @@ def _compare_fields(a, b):
 if a._fields != b._fields:
 return False
 for field in a._fields:
-a_field = getattr(a, field)
-b_field = getattr(b, field)
+a_field = getattr(a, field, sentinel)
+b_field = getattr(b, field, sentinel)
+if a_field is sentinel and b_field is sentinel:
+# both nodes are missing a field at runtime
+continue
+if a_field is sentinel or b_field is sentinel:
+# one of the node is missing a field
+return False
 if not _compare(a_field, b_field):
 return False
 else:
@@ -461,8 +469,11 @@ def _compare_attributes(a, b):
 return False
 # Attributes are always ints.
 for attr in a._attributes:
-a_attr = getattr(a, attr)
-b_attr = getattr(b, attr)
+a_attr = getattr(a, attr, sentinel)
+b_attr = getattr(b, attr, sentinel)
+if a_attr is sentinel and b_attr is sentinel:
+# both nodes are missing an attribute at runtime
+continue
 if a_attr != b_attr:
 return False
 else:
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index 5d71d524516df2..fbd19620311159 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -948,6 +948,15 @@ def test_compare_fieldless(self):
 self.assertTrue(ast.compare(ast.Add(), ast.Add()))
 self.assertFalse(ast.compare(ast.Sub(), ast.Add()))
 
+# test that missing runtime fields is handled in ast.compare()
+a1, a2 = ast.Name('a'), ast.Name('a')
+self.assertTrue(ast.compare(a1, a2))
+self.assertTrue(ast.compare(a1, a2))
+del a1.id
+self.assertFalse(ast.compare(a1, a2))
+del a2.id
+self.assertTrue(ast.compare(a1, a2))
+
 def test_compare_modes(self):
 for mode, sources in (
 ("exec", exec_tests),
@@ -970,6 +979,16 @@ def parse(a, b):
 self.assertTrue(ast.compare(a, b, compare_attributes=False))
 self.assertFalse(ast.compare(a, b, compare_attributes=True))
 
+def test_compare_attributes_option_missing_attribute(self):
+# test that missing runtime attributes is handled in ast.compare()
+a1, a2 = ast.Name('a', lineno=1), ast.Name('a', lineno=1)
+self.assertTrue(ast.compare(a1, a2))
+self.assertTrue(ast.compare(a1, a2, compare_attributes=True))
+del a1.lineno
+self.assertFalse(ast.compare(a1, a2, compare_attributes=True))
+del a2.lineno
+self.assertTrue(ast.compare(a1, a2, compare_attributes=True))
+
 def test_positional_only_feature_version(self):
 ast.parse('def foo(x, /): ...', feature_version=(3, 8))
 ast.parse('def bar(x=1, /): ...', feature_version=(3, 8))
diff --git 
a/Misc/NEWS.d/next/Library/2024-07-01-11-23-18.gh-issue-121210.cD0zfn.rst 
b/Misc/NEWS.d/next/Library/2024-07-01-11-23-18.gh-issue-121210.cD0zfn.rst
new file mode 100644
index 00..55d5b221bf0765
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-07-01-11-23-18.gh-issue-121210.cD0zfn.rst
@@ -0,0 +1,2 @@
+Handle AST nodes with missing runtime fields or attributes in
+:func:`ast.compare`. Patch by Bénédikt Tran.

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] gh-121165: protect macro expansion of `ADJUST_INDICES` with do-while(0) (#121166)

2024-07-02 Thread kumaraditya303
https://github.com/python/cpython/commit/6343486eb60ac5a9e15402a592298259c5afdee1
commit: 6343486eb60ac5a9e15402a592298259c5afdee1
branch: main
author: Bénédikt Tran <10796600+picn...@users.noreply.github.com>
committer: kumaraditya303 
date: 2024-07-02T16:27:51+05:30
summary:

gh-121165: protect macro expansion of `ADJUST_INDICES` with do-while(0) 
(#121166)

files:
M Objects/bytes_methods.c
M Objects/unicodeobject.c

diff --git a/Objects/bytes_methods.c b/Objects/bytes_methods.c
index 55252406578774..c239ae18a593e3 100644
--- a/Objects/bytes_methods.c
+++ b/Objects/bytes_methods.c
@@ -432,19 +432,24 @@ parse_args_finds_byte(const char *function_name, PyObject 
**subobj, char *byte)
 }
 
 /* helper macro to fixup start/end slice values */
-#define ADJUST_INDICES(start, end, len) \
-if (end > len)  \
-end = len;  \
-else if (end < 0) { \
-end += len; \
-if (end < 0)\
-end = 0;\
-}   \
-if (start < 0) {\
-start += len;   \
-if (start < 0)  \
-start = 0;  \
-}
+#define ADJUST_INDICES(start, end, len) \
+do {\
+if (end > len) {\
+end = len;  \
+}   \
+else if (end < 0) { \
+end += len; \
+if (end < 0) {  \
+end = 0;\
+}   \
+}   \
+if (start < 0) {\
+start += len;   \
+if (start < 0) {\
+start = 0;  \
+}   \
+}   \
+} while (0)
 
 Py_LOCAL_INLINE(Py_ssize_t)
 find_internal(const char *str, Py_ssize_t len,
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 9738442ab962b0..394ea888fc9231 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -9315,19 +9315,24 @@ _PyUnicode_TransformDecimalAndSpaceToASCII(PyObject 
*unicode)
 /* --- Helpers  */
 
 /* helper macro to fixup start/end slice values */
-#define ADJUST_INDICES(start, end, len) \
-if (end > len)  \
-end = len;  \
-else if (end < 0) { \
-end += len; \
-if (end < 0)\
-end = 0;\
-}   \
-if (start < 0) {\
-start += len;   \
-if (start < 0)  \
-start = 0;  \
-}
+#define ADJUST_INDICES(start, end, len) \
+do {\
+if (end > len) {\
+end = len;  \
+}   \
+else if (end < 0) { \
+end += len; \
+if (end < 0) {  \
+end = 0;\
+}   \
+}   \
+if (start < 0) {\
+start += len;   \
+if (start < 0) {\
+start = 0;  \
+}   \
+}   \
+} while (0)
 
 static Py_ssize_t
 any_find_slice(PyObject* s1, PyObject* s2,

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] [3.13] gh-121245: Correct pyrepl import in site.py (GH-121255) (#121261)

2024-07-02 Thread lysnikolaou
https://github.com/python/cpython/commit/78e96bdf2417414c3d8aadc9a5f75702b94968fc
commit: 78e96bdf2417414c3d8aadc9a5f75702b94968fc
branch: 3.13
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: lysnikolaou 
date: 2024-07-02T13:40:05+02:00
summary:

[3.13] gh-121245: Correct pyrepl import in site.py (GH-121255) (#121261)

(cherry picked from commit 7a807c3efaa83f1e4fb9b791579b47a0a1fd47de)

Co-authored-by: Sergey B Kirpichev 
Co-authored-by: Miro Hrončok 

files:
A Misc/NEWS.d/next/Library/2024-07-02-11-34-06.gh-issue-121245.sSkDAr.rst
M Lib/site.py

diff --git a/Lib/site.py b/Lib/site.py
index 7eace190f5ab21..95c7ebf2fdf146 100644
--- a/Lib/site.py
+++ b/Lib/site.py
@@ -526,8 +526,7 @@ def register_readline():
 
 def write_history():
 try:
-# _pyrepl.__main__ is executed as the __main__ module
-from __main__ import CAN_USE_PYREPL
+from _pyrepl.main import CAN_USE_PYREPL
 except ImportError:
 CAN_USE_PYREPL = False
 
diff --git 
a/Misc/NEWS.d/next/Library/2024-07-02-11-34-06.gh-issue-121245.sSkDAr.rst 
b/Misc/NEWS.d/next/Library/2024-07-02-11-34-06.gh-issue-121245.sSkDAr.rst
new file mode 100644
index 00..6e9dec2545166f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-07-02-11-34-06.gh-issue-121245.sSkDAr.rst
@@ -0,0 +1,2 @@
+Fix a bug in the handling of the command history of the new :term:`REPL` that 
caused
+the history file to be wiped at REPL exit.

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] gh-121272: move __future__ import validation from compiler to symtable (#121273)

2024-07-02 Thread iritkatriel
https://github.com/python/cpython/commit/1ac273224a85126c4356e355f7445206fadde7ec
commit: 1ac273224a85126c4356e355f7445206fadde7ec
branch: main
author: Irit Katriel <1055913+iritkatr...@users.noreply.github.com>
committer: iritkatriel <1055913+iritkatr...@users.noreply.github.com>
date: 2024-07-02T16:22:08Z
summary:

gh-121272: move __future__ import validation from compiler to symtable (#121273)

files:
M Python/compile.c
M Python/symtable.c

diff --git a/Python/compile.c b/Python/compile.c
index 69de0ec2996e00..d33db69f425361 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -77,14 +77,6 @@ typedef struct _PyCfgBuilder cfg_builder;
 #define LOCATION(LNO, END_LNO, COL, END_COL) \
 ((const _Py_SourceLocation){(LNO), (END_LNO), (COL), (END_COL)})
 
-/* Return true if loc1 starts after loc2 ends. */
-static inline bool
-location_is_after(location loc1, location loc2) {
-return (loc1.lineno > loc2.end_lineno) ||
-((loc1.lineno == loc2.end_lineno) &&
- (loc1.col_offset > loc2.end_col_offset));
-}
-
 #define LOC(x) SRC_LOCATION_FROM_AST(x)
 
 typedef _PyJumpTargetLabel jump_target_label;
@@ -3847,14 +3839,6 @@ compiler_from_import(struct compiler *c, stmt_ty s)
 PyTuple_SET_ITEM(names, i, Py_NewRef(alias->name));
 }
 
-if (location_is_after(LOC(s), c->c_future.ff_location) &&
-s->v.ImportFrom.module && s->v.ImportFrom.level == 0 &&
-_PyUnicode_EqualToASCIIString(s->v.ImportFrom.module, "__future__"))
-{
-Py_DECREF(names);
-return compiler_error(c, LOC(s), "from __future__ imports must occur "
-  "at the beginning of the file");
-}
 ADDOP_LOAD_CONST_NEW(c, LOC(s), names);
 
 if (s->v.ImportFrom.module) {
diff --git a/Python/symtable.c b/Python/symtable.c
index 2e56ea6e830846..61fa5c6fdf923c 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -1660,6 +1660,27 @@ has_kwonlydefaults(asdl_arg_seq *kwonlyargs, 
asdl_expr_seq *kw_defaults)
 return 0;
 }
 
+static int
+check_import_from(struct symtable *st, stmt_ty s)
+{
+assert(s->kind == ImportFrom_kind);
+_Py_SourceLocation fut = st->st_future->ff_location;
+if (s->v.ImportFrom.module && s->v.ImportFrom.level == 0 &&
+_PyUnicode_EqualToASCIIString(s->v.ImportFrom.module, "__future__") &&
+((s->lineno > fut.lineno) ||
+ ((s->lineno == fut.end_lineno) && (s->col_offset > 
fut.end_col_offset
+{
+PyErr_SetString(PyExc_SyntaxError,
+"from __future__ imports must occur "
+"at the beginning of the file");
+PyErr_RangedSyntaxLocationObject(st->st_filename,
+ s->lineno, s->col_offset + 1,
+ s->end_lineno, s->end_col_offset + 1);
+return 0;
+}
+return 1;
+}
+
 static int
 symtable_visit_stmt(struct symtable *st, stmt_ty s)
 {
@@ -1914,6 +1935,9 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
 break;
 case ImportFrom_kind:
 VISIT_SEQ(st, alias, s->v.ImportFrom.names);
+if (!check_import_from(st, s)) {
+VISIT_QUIT(st, 0);
+}
 break;
 case Global_kind: {
 Py_ssize_t i;

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] gh-117139: Add _PyTuple_FromStackRefSteal and use it (#121244)

2024-07-02 Thread colesbury
https://github.com/python/cpython/commit/8e8d202f552c993f40913b628139a39a5abe6a03
commit: 8e8d202f552c993f40913b628139a39a5abe6a03
branch: main
author: Sam Gross 
committer: colesbury 
date: 2024-07-02T12:30:14-04:00
summary:

gh-117139: Add _PyTuple_FromStackRefSteal and use it (#121244)

Avoids the extra conversion from stack refs to PyObjects.

files:
M Include/internal/pycore_stackref.h
M Include/internal/pycore_tuple.h
M Objects/tupleobject.c
M Python/bytecodes.c
M Python/ceval.c
M Python/executor_cases.c.h
M Python/generated_cases.c.h
M Tools/cases_generator/analyzer.py

diff --git a/Include/internal/pycore_stackref.h 
b/Include/internal/pycore_stackref.h
index 32e445dd96f9a1..4301c6a7cb40b0 100644
--- a/Include/internal/pycore_stackref.h
+++ b/Include/internal/pycore_stackref.h
@@ -48,7 +48,7 @@ extern "C" {
CPython refcounting operations on it!
 */
 
-typedef union {
+typedef union _PyStackRef {
 uintptr_t bits;
 } _PyStackRef;
 
diff --git a/Include/internal/pycore_tuple.h b/Include/internal/pycore_tuple.h
index 14a9e42c3a324c..dfbbd6fd0c7de5 100644
--- a/Include/internal/pycore_tuple.h
+++ b/Include/internal/pycore_tuple.h
@@ -21,6 +21,7 @@ extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState 
*);
 #define _PyTuple_ITEMS(op) _Py_RVALUE(_PyTuple_CAST(op)->ob_item)
 
 extern PyObject *_PyTuple_FromArray(PyObject *const *, Py_ssize_t);
+PyAPI_FUNC(PyObject *)_PyTuple_FromStackRefSteal(const union _PyStackRef *, 
Py_ssize_t);
 PyAPI_FUNC(PyObject *)_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t);
 
 typedef struct {
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
index 5ae1ee9a89af84..994258f20b495d 100644
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -390,6 +390,27 @@ _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
 return (PyObject *)tuple;
 }
 
+PyObject *
+_PyTuple_FromStackRefSteal(const _PyStackRef *src, Py_ssize_t n)
+{
+if (n == 0) {
+return tuple_get_empty();
+}
+PyTupleObject *tuple = tuple_alloc(n);
+if (tuple == NULL) {
+for (Py_ssize_t i = 0; i < n; i++) {
+PyStackRef_CLOSE(src[i]);
+}
+return NULL;
+}
+PyObject **dst = tuple->ob_item;
+for (Py_ssize_t i = 0; i < n; i++) {
+dst[i] = PyStackRef_AsPyObjectSteal(src[i]);
+}
+_PyObject_GC_TRACK(tuple);
+return (PyObject *)tuple;
+}
+
 PyObject *
 _PyTuple_FromArraySteal(PyObject *const *src, Py_ssize_t n)
 {
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 343481e9313de4..76587a4f0dc695 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1780,13 +1780,7 @@ dummy_func(
 }
 
 inst(BUILD_TUPLE, (values[oparg] -- tup)) {
-STACKREFS_TO_PYOBJECTS(values, oparg, values_o);
-if (CONVERSION_FAILED(values_o)) {
-DECREF_INPUTS();
-ERROR_IF(true, error);
-}
-PyObject *tup_o = _PyTuple_FromArraySteal(values_o, oparg);
-STACKREFS_TO_PYOBJECTS_CLEANUP(values_o);
+PyObject *tup_o = _PyTuple_FromStackRefSteal(values, oparg);
 ERROR_IF(tup_o == NULL, error);
 tup = PyStackRef_FromPyObjectSteal(tup_o);
 }
diff --git a/Python/ceval.c b/Python/ceval.c
index a71244676f3029..a240ed4321f7ee 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1500,13 +1500,7 @@ initialize_locals(PyThreadState *tstate, 
PyFunctionObject *func,
 u = (PyObject *)&_Py_SINGLETON(tuple_empty);
 }
 else {
-assert(args != NULL);
-STACKREFS_TO_PYOBJECTS((_PyStackRef *)args, argcount, args_o);
-if (args_o == NULL) {
-goto fail_pre_positional;
-}
-u = _PyTuple_FromArraySteal((args_o + n), argcount - n);
-STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
+u = _PyTuple_FromStackRefSteal(args + n, argcount - n);
 }
 if (u == NULL) {
 goto fail_post_positional;
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index d70a57a9a8ffbe..3b999465aac815 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -1816,15 +1816,7 @@
 _PyStackRef tup;
 oparg = CURRENT_OPARG();
 values = &stack_pointer[-oparg];
-STACKREFS_TO_PYOBJECTS(values, oparg, values_o);
-if (CONVERSION_FAILED(values_o)) {
-for (int _i = oparg; --_i >= 0;) {
-PyStackRef_CLOSE(values[_i]);
-}
-if (true) JUMP_TO_ERROR();
-}
-PyObject *tup_o = _PyTuple_FromArraySteal(values_o, oparg);
-STACKREFS_TO_PYOBJECTS_CLEANUP(values_o);
+PyObject *tup_o = _PyTuple_FromStackRefSteal(values, oparg);
 if (tup_o == NULL) JUMP_TO_ERROR();
 tup = PyStackRef_FromPyObjectSteal(tup_o);
 stack_pointer[-oparg] = tup;
diff --git a/Python/generated_cases.c.h b/P

[Python-checkins] gh-115773: Add sizes to debug offset structure (#120112)

2024-07-02 Thread pablogsal
https://github.com/python/cpython/commit/b180788d4a927d23af54f4b4702ccaf254f64854
commit: b180788d4a927d23af54f4b4702ccaf254f64854
branch: main
author: Pablo Galindo Salgado 
committer: pablogsal 
date: 2024-07-02T17:54:33Z
summary:

gh-115773: Add sizes to debug offset structure (#120112)

files:
M Include/internal/pycore_runtime.h
M Include/internal/pycore_runtime_init.h

diff --git a/Include/internal/pycore_runtime.h 
b/Include/internal/pycore_runtime.h
index f58eccf729cb2a..341fe29a68af16 100644
--- a/Include/internal/pycore_runtime.h
+++ b/Include/internal/pycore_runtime.h
@@ -55,12 +55,14 @@ typedef struct _Py_DebugOffsets {
 uint64_t version;
 // Runtime state offset;
 struct _runtime_state {
+uint64_t size;
 uint64_t finalizing;
 uint64_t interpreters_head;
 } runtime_state;
 
 // Interpreter state offset;
 struct _interpreter_state {
+uint64_t size;
 uint64_t next;
 uint64_t threads_head;
 uint64_t gc;
@@ -74,6 +76,7 @@ typedef struct _Py_DebugOffsets {
 
 // Thread state offset;
 struct _thread_state{
+uint64_t size;
 uint64_t prev;
 uint64_t next;
 uint64_t interp;
@@ -84,6 +87,7 @@ typedef struct _Py_DebugOffsets {
 
 // InterpreterFrame offset;
 struct _interpreter_frame {
+uint64_t size;
 uint64_t previous;
 uint64_t executable;
 uint64_t instr_ptr;
@@ -93,12 +97,14 @@ typedef struct _Py_DebugOffsets {
 
 // CFrame offset;
 struct _cframe {
+uint64_t size;
 uint64_t current_frame;
 uint64_t previous;
 } cframe;
 
 // Code object offset;
 struct _code_object {
+uint64_t size;
 uint64_t filename;
 uint64_t name;
 uint64_t linetable;
@@ -111,21 +117,25 @@ typedef struct _Py_DebugOffsets {
 
 // PyObject offset;
 struct _pyobject {
+uint64_t size;
 uint64_t ob_type;
 } pyobject;
 
 // PyTypeObject object offset;
 struct _type_object {
+uint64_t size;
 uint64_t tp_name;
 } type_object;
 
 // PyTuple object offset;
 struct _tuple_object {
+uint64_t size;
 uint64_t ob_item;
 } tuple_object;
 
 // Unicode object offset;
 struct _unicode_object {
+uint64_t size;
 uint64_t state;
 uint64_t length;
 size_t asciiobject_size;
diff --git a/Include/internal/pycore_runtime_init.h 
b/Include/internal/pycore_runtime_init.h
index 98920dbb7c7a92..33e39c2edbe541 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -35,10 +35,12 @@ extern PyTypeObject _PyExc_MemoryError;
 .cookie = "xdebugpy", \
 .version = PY_VERSION_HEX, \
 .runtime_state = { \
+.size = sizeof(_PyRuntimeState), \
 .finalizing = offsetof(_PyRuntimeState, _finalizing), \
 .interpreters_head = offsetof(_PyRuntimeState, 
interpreters.head), \
 }, \
 .interpreter_state = { \
+.size = sizeof(PyInterpreterState), \
 .next = offsetof(PyInterpreterState, next), \
 .threads_head = offsetof(PyInterpreterState, threads.head), \
 .gc = offsetof(PyInterpreterState, gc), \
@@ -50,6 +52,7 @@ extern PyTypeObject _PyExc_MemoryError;
 .gil_runtime_state_holder = offsetof(PyInterpreterState, 
_gil.last_holder), \
 }, \
 .thread_state = { \
+.size = sizeof(PyThreadState), \
 .prev = offsetof(PyThreadState, prev), \
 .next = offsetof(PyThreadState, next), \
 .interp = offsetof(PyThreadState, interp), \
@@ -58,6 +61,7 @@ extern PyTypeObject _PyExc_MemoryError;
 .native_thread_id = offsetof(PyThreadState, native_thread_id), 
\
 }, \
 .interpreter_frame = { \
+.size = sizeof(_PyInterpreterFrame), \
 .previous = offsetof(_PyInterpreterFrame, previous), \
 .executable = offsetof(_PyInterpreterFrame, f_executable), \
 .instr_ptr = offsetof(_PyInterpreterFrame, instr_ptr), \
@@ -65,6 +69,7 @@ extern PyTypeObject _PyExc_MemoryError;
 .owner = offsetof(_PyInterpreterFrame, owner), \
 }, \
 .code_object = { \
+.size = sizeof(PyCodeObject), \
 .filename = offsetof(PyCodeObject, co_filename), \
 .name = offsetof(PyCodeObject, co_name), \
 .linetable = offsetof(PyCodeObject, co_linetable), \
@@ -75,15 +80,19 @@ extern PyTypeObject _PyExc_MemoryError;
 .co_code_adaptive = offsetof(PyCodeObject, co_code_adaptive), \
 }, \
 .pyobject = { \
+.size = sizeof(PyObject), \
 .ob_type = offsetof(PyObject, ob_type), \
 }, \
  

[Python-checkins] gh-121035: Further improve logging flow diagram with respect to dark/light modes. (GH-121265)

2024-07-02 Thread vsajip
https://github.com/python/cpython/commit/089835469d5efbea4793cd611b43cb8387f2e7e5
commit: 089835469d5efbea4793cd611b43cb8387f2e7e5
branch: main
author: Vinay Sajip 
committer: vsajip 
date: 2024-07-02T18:57:34+01:00
summary:

gh-121035: Further improve logging flow diagram with respect to dark/light 
modes. (GH-121265)

files:
M Doc/howto/logging.rst
M Doc/howto/logging_flow.svg

diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst
index 9c55233e910f17..cbfe93319ddaa4 100644
--- a/Doc/howto/logging.rst
+++ b/Doc/howto/logging.rst
@@ -403,11 +403,13 @@ following diagram.
  function updateBody(theme) {
 let elem = document.body;
 
+elem.classList.remove('dark-theme');
+elem.classList.remove('light-theme');
 if (theme === 'dark') {
 elem.classList.add('dark-theme');
 }
-else {
-elem.classList.remove('dark-theme');
+else if (theme === 'light') {
+elem.classList.add('light-theme');
 }
  }
 
diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg
index 9807323b7190d6..4974994ac6b400 100644
--- a/Doc/howto/logging_flow.svg
+++ b/Doc/howto/logging_flow.svg
@@ -46,6 +46,7 @@
 filter: invert(100%) hue-rotate(180deg) saturate(1.25);
   }
 }
+/* These rules are for when the theme selector is used, perhaps in 
contrast to the browser theme. */
 body.dark-theme polygon, body.dark-theme rect, body.dark-theme polyline, 
body.dark-theme line {
   stroke: #ff;
 }
@@ -55,6 +56,15 @@
 body.dark-theme text {
   fill: #ff;
 }
+body.light-theme polygon, body.light-theme rect, body.light-theme 
polyline, body.light-theme line {
+  stroke: #00;
+}
+body.light-theme polygon.filled {
+  fill: #00;
+}
+body.light-theme text {
+  fill: #00;
+}
   
 
   

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] [3.13] gh-115773: Add sizes to debug offset structure (GH-120112) (#121283)

2024-07-02 Thread pablogsal
https://github.com/python/cpython/commit/89bf33732fd53fbb954aa088cfb34f13264746ad
commit: 89bf33732fd53fbb954aa088cfb34f13264746ad
branch: 3.13
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: pablogsal 
date: 2024-07-02T18:19:26Z
summary:

[3.13] gh-115773: Add sizes to debug offset structure (GH-120112) (#121283)

gh-115773: Add sizes to debug offset structure (GH-120112)
(cherry picked from commit b180788d4a927d23af54f4b4702ccaf254f64854)

Co-authored-by: Pablo Galindo Salgado 

files:
M Include/internal/pycore_runtime.h
M Include/internal/pycore_runtime_init.h

diff --git a/Include/internal/pycore_runtime.h 
b/Include/internal/pycore_runtime.h
index f58eccf729cb2a..341fe29a68af16 100644
--- a/Include/internal/pycore_runtime.h
+++ b/Include/internal/pycore_runtime.h
@@ -55,12 +55,14 @@ typedef struct _Py_DebugOffsets {
 uint64_t version;
 // Runtime state offset;
 struct _runtime_state {
+uint64_t size;
 uint64_t finalizing;
 uint64_t interpreters_head;
 } runtime_state;
 
 // Interpreter state offset;
 struct _interpreter_state {
+uint64_t size;
 uint64_t next;
 uint64_t threads_head;
 uint64_t gc;
@@ -74,6 +76,7 @@ typedef struct _Py_DebugOffsets {
 
 // Thread state offset;
 struct _thread_state{
+uint64_t size;
 uint64_t prev;
 uint64_t next;
 uint64_t interp;
@@ -84,6 +87,7 @@ typedef struct _Py_DebugOffsets {
 
 // InterpreterFrame offset;
 struct _interpreter_frame {
+uint64_t size;
 uint64_t previous;
 uint64_t executable;
 uint64_t instr_ptr;
@@ -93,12 +97,14 @@ typedef struct _Py_DebugOffsets {
 
 // CFrame offset;
 struct _cframe {
+uint64_t size;
 uint64_t current_frame;
 uint64_t previous;
 } cframe;
 
 // Code object offset;
 struct _code_object {
+uint64_t size;
 uint64_t filename;
 uint64_t name;
 uint64_t linetable;
@@ -111,21 +117,25 @@ typedef struct _Py_DebugOffsets {
 
 // PyObject offset;
 struct _pyobject {
+uint64_t size;
 uint64_t ob_type;
 } pyobject;
 
 // PyTypeObject object offset;
 struct _type_object {
+uint64_t size;
 uint64_t tp_name;
 } type_object;
 
 // PyTuple object offset;
 struct _tuple_object {
+uint64_t size;
 uint64_t ob_item;
 } tuple_object;
 
 // Unicode object offset;
 struct _unicode_object {
+uint64_t size;
 uint64_t state;
 uint64_t length;
 size_t asciiobject_size;
diff --git a/Include/internal/pycore_runtime_init.h 
b/Include/internal/pycore_runtime_init.h
index 98920dbb7c7a92..33e39c2edbe541 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -35,10 +35,12 @@ extern PyTypeObject _PyExc_MemoryError;
 .cookie = "xdebugpy", \
 .version = PY_VERSION_HEX, \
 .runtime_state = { \
+.size = sizeof(_PyRuntimeState), \
 .finalizing = offsetof(_PyRuntimeState, _finalizing), \
 .interpreters_head = offsetof(_PyRuntimeState, 
interpreters.head), \
 }, \
 .interpreter_state = { \
+.size = sizeof(PyInterpreterState), \
 .next = offsetof(PyInterpreterState, next), \
 .threads_head = offsetof(PyInterpreterState, threads.head), \
 .gc = offsetof(PyInterpreterState, gc), \
@@ -50,6 +52,7 @@ extern PyTypeObject _PyExc_MemoryError;
 .gil_runtime_state_holder = offsetof(PyInterpreterState, 
_gil.last_holder), \
 }, \
 .thread_state = { \
+.size = sizeof(PyThreadState), \
 .prev = offsetof(PyThreadState, prev), \
 .next = offsetof(PyThreadState, next), \
 .interp = offsetof(PyThreadState, interp), \
@@ -58,6 +61,7 @@ extern PyTypeObject _PyExc_MemoryError;
 .native_thread_id = offsetof(PyThreadState, native_thread_id), 
\
 }, \
 .interpreter_frame = { \
+.size = sizeof(_PyInterpreterFrame), \
 .previous = offsetof(_PyInterpreterFrame, previous), \
 .executable = offsetof(_PyInterpreterFrame, f_executable), \
 .instr_ptr = offsetof(_PyInterpreterFrame, instr_ptr), \
@@ -65,6 +69,7 @@ extern PyTypeObject _PyExc_MemoryError;
 .owner = offsetof(_PyInterpreterFrame, owner), \
 }, \
 .code_object = { \
+.size = sizeof(PyCodeObject), \
 .filename = offsetof(PyCodeObject, co_filename), \
 .name = offsetof(PyCodeObject, co_name), \
 .linetable = offsetof(PyCodeObject, co_linetable), \
@@ -75,15 +80,19 @@ extern PyTypeObject _PyExc_MemoryError;
 .co_co

[Python-checkins] GH-73991: Support copying directory symlinks on older Windows (#120807)

2024-07-02 Thread barneygale
https://github.com/python/cpython/commit/f09d184821efd9438d092643881e28bdf8de4de5
commit: f09d184821efd9438d092643881e28bdf8de4de5
branch: main
author: Barney Gale 
committer: barneygale 
date: 2024-07-03T04:30:29+01:00
summary:

GH-73991: Support copying directory symlinks on older Windows (#120807)

Check for `ERROR_INVALID_PARAMETER` when calling `_winapi.CopyFile2()` and
raise `UnsupportedOperation`. In `Path.copy()`, handle this exception and
fall back to the `PathBase.copy()` implementation.

files:
M Doc/library/pathlib.rst
M Lib/pathlib/__init__.py
M Lib/pathlib/_abc.py
M Lib/pathlib/_local.py
M Lib/pathlib/_os.py
M Lib/test/test_pathlib/test_pathlib_abc.py

diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst
index 0918bbb47e9ea6..d7fd56f4c4ff7f 100644
--- a/Doc/library/pathlib.rst
+++ b/Doc/library/pathlib.rst
@@ -1554,11 +1554,6 @@ Copying, renaming and deleting
   permissions. After the copy is complete, users may wish to call
   :meth:`Path.chmod` to set the permissions of the target file.
 
-   .. warning::
-  On old builds of Windows (before Windows 10 build 19041), this method
-  raises :exc:`OSError` when a symlink to a directory is encountered and
-  *follow_symlinks* is false.
-
.. versionadded:: 3.14
 
 
diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py
index 4b3edf535a61aa..2298a249529460 100644
--- a/Lib/pathlib/__init__.py
+++ b/Lib/pathlib/__init__.py
@@ -5,8 +5,8 @@
 operating systems.
 """
 
-from ._abc import *
+from ._os import *
 from ._local import *
 
-__all__ = (_abc.__all__ +
+__all__ = (_os.__all__ +
_local.__all__)
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index 71973913921169..b5f903ec1f03ce 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -16,10 +16,7 @@
 import posixpath
 from glob import _GlobberBase, _no_recurse_symlinks
 from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, 
S_ISFIFO
-from ._os import copyfileobj
-
-
-__all__ = ["UnsupportedOperation"]
+from ._os import UnsupportedOperation, copyfileobj
 
 
 @functools.cache
@@ -27,12 +24,6 @@ def _is_case_sensitive(parser):
 return parser.normcase('Aa') == 'Aa'
 
 
-class UnsupportedOperation(NotImplementedError):
-"""An exception that is raised when an unsupported operation is called on
-a path object.
-"""
-pass
-
 
 class ParserBase:
 """Base class for path parsers, which do low-level path manipulation.
diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py
index 0105ea3042422e..acb57214b81865 100644
--- a/Lib/pathlib/_local.py
+++ b/Lib/pathlib/_local.py
@@ -17,8 +17,8 @@
 except ImportError:
 grp = None
 
-from ._abc import UnsupportedOperation, PurePathBase, PathBase
-from ._os import copyfile
+from ._os import UnsupportedOperation, copyfile
+from ._abc import PurePathBase, PathBase
 
 
 __all__ = [
@@ -791,12 +791,15 @@ def copy(self, target, follow_symlinks=True):
 try:
 target = os.fspath(target)
 except TypeError:
-if isinstance(target, PathBase):
-# Target is an instance of PathBase but not os.PathLike.
-# Use generic implementation from PathBase.
-return PathBase.copy(self, target, 
follow_symlinks=follow_symlinks)
-raise
-copyfile(os.fspath(self), target, follow_symlinks)
+if not isinstance(target, PathBase):
+raise
+else:
+try:
+copyfile(os.fspath(self), target, follow_symlinks)
+return
+except UnsupportedOperation:
+pass  # Fall through to generic code.
+PathBase.copy(self, target, follow_symlinks=follow_symlinks)
 
 def chmod(self, mode, *, follow_symlinks=True):
 """
diff --git a/Lib/pathlib/_os.py b/Lib/pathlib/_os.py
index bbb019b6534503..61923b5e410b5c 100644
--- a/Lib/pathlib/_os.py
+++ b/Lib/pathlib/_os.py
@@ -20,6 +20,15 @@
 _winapi = None
 
 
+__all__ = ["UnsupportedOperation"]
+
+
+class UnsupportedOperation(NotImplementedError):
+"""An exception that is raised when an unsupported operation is attempted.
+"""
+pass
+
+
 def get_copy_blocksize(infd):
 """Determine blocksize for fastcopying on Linux.
 Hopefully the whole file will be copied in a single call.
@@ -106,18 +115,30 @@ def copyfile(source, target, follow_symlinks):
 Copy from one file to another using CopyFile2 (Windows only).
 """
 if follow_symlinks:
-flags = 0
+_winapi.CopyFile2(source, target, 0)
 else:
+# Use COPY_FILE_COPY_SYMLINK to copy a file symlink.
 flags = _winapi.COPY_FILE_COPY_SYMLINK
 try:
 _winapi.CopyFile2(source, target, flags)
 return
 except OSError as err:
 # Check for ERROR_ACCESS_DENIED
- 

[Python-checkins] gh-121027: Make the functools.partial object a method descriptor (GH-121089)

2024-07-02 Thread serhiy-storchaka
https://github.com/python/cpython/commit/ff5806c78edda1feed61254ac55193772dc9ec48
commit: ff5806c78edda1feed61254ac55193772dc9ec48
branch: main
author: Serhiy Storchaka 
committer: serhiy-storchaka 
date: 2024-07-03T09:02:15+03:00
summary:

gh-121027: Make the functools.partial object a method descriptor (GH-121089)

Co-authored-by: d.grigonis 

files:
A Misc/NEWS.d/next/Library/2024-06-27-12-27-52.gh-issue-121027.D4K1OX.rst
M Doc/whatsnew/3.14.rst
M Lib/functools.py
M Lib/test/test_functools.py
M Lib/test/test_inspect/test_inspect.py
M Modules/_functoolsmodule.c

diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 6ebadd75092fac..9578ba0c9c9657 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -305,6 +305,12 @@ Porting to Python 3.14
 This section lists previously described changes and other bugfixes
 that may require changes to your code.
 
+Changes in the Python API
+-
+
+* :class:`functools.partial` is now a method descriptor.
+  Wrap it in :func:`staticmethod` if you want to preserve the old behavior.
+  (Contributed by Serhiy Storchaka and Dominykas Grigonis in :gh:`121027`.)
 
 Build Changes
 =
diff --git a/Lib/functools.py b/Lib/functools.py
index d04957c555295e..a10493f0e25360 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -18,6 +18,7 @@
 from collections import namedtuple
 # import types, weakref  # Deferred to single_dispatch()
 from reprlib import recursive_repr
+from types import MethodType
 from _thread import RLock
 
 # Avoid importing types, so we can speedup import time
@@ -314,12 +315,7 @@ def __repr__(self):
 def __get__(self, obj, objtype=None):
 if obj is None:
 return self
-import warnings
-warnings.warn('functools.partial will be a method descriptor in '
-  'future Python versions; wrap it in staticmethod() '
-  'if you want to preserve the old behavior',
-  FutureWarning, 2)
-return self
+return MethodType(self, obj)
 
 def __reduce__(self):
 return type(self), (self.func,), (self.func, self.args,
@@ -402,7 +398,7 @@ def _method(cls_or_self, /, *args, **keywords):
 def __get__(self, obj, cls=None):
 get = getattr(self.func, "__get__", None)
 result = None
-if get is not None and not isinstance(self.func, partial):
+if get is not None:
 new_func = get(obj, cls)
 if new_func is not self.func:
 # Assume __get__ returning something new indicates the
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index 1ce0f4d0aea6ee..492a16a8c7ff45 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -405,9 +405,7 @@ class A:
 self.assertEqual(A.meth(3, b=4), ((1, 3), {'a': 2, 'b': 4}))
 self.assertEqual(A.cmeth(3, b=4), ((1, A, 3), {'a': 2, 'b': 4}))
 self.assertEqual(A.smeth(3, b=4), ((1, 3), {'a': 2, 'b': 4}))
-with self.assertWarns(FutureWarning) as w:
-self.assertEqual(a.meth(3, b=4), ((1, 3), {'a': 2, 'b': 4}))
-self.assertEqual(w.filename, __file__)
+self.assertEqual(a.meth(3, b=4), ((1, a, 3), {'a': 2, 'b': 4}))
 self.assertEqual(a.cmeth(3, b=4), ((1, A, 3), {'a': 2, 'b': 4}))
 self.assertEqual(a.smeth(3, b=4), ((1, 3), {'a': 2, 'b': 4}))
 
diff --git a/Lib/test/test_inspect/test_inspect.py 
b/Lib/test/test_inspect/test_inspect.py
index 308c09874fe2ac..d39c3ccdc847bd 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -3868,17 +3868,15 @@ def __init__(self, b):
 
 with self.subTest('partial'):
 class CM(type):
-__call__ = functools.partial(lambda x, a: (x, a), 2)
+__call__ = functools.partial(lambda x, a, b: (x, a, b), 2)
 class C(metaclass=CM):
-def __init__(self, b):
+def __init__(self, c):
 pass
 
-with self.assertWarns(FutureWarning):
-self.assertEqual(C(1), (2, 1))
-with self.assertWarns(FutureWarning):
-self.assertEqual(self.signature(C),
-((('a', ..., ..., "positional_or_keyword"),),
-...))
+self.assertEqual(C(1), (2, C, 1))
+self.assertEqual(self.signature(C),
+((('b', ..., ..., "positional_or_keyword"),),
+...))
 
 with self.subTest('partialmethod'):
 class CM(type):
@@ -4024,14 +4022,12 @@ class C:
 
 with self.subTest('partial'):
 class C:
-__init__ = functools.partial(lambda x, a: None, 2)
+__init__ = functools.partial(lambda x, a, b: None, 2)
 
-with self.assertWarns(FutureWarning):
-C(1)  # does not raise
-with self.assertWar