Skip to content

Commit

Permalink
Merge branch 'main' into pythongh-115999-contain-op
Browse files Browse the repository at this point in the history
  • Loading branch information
corona10 authored Nov 6, 2024
2 parents 728ed63 + a204c63 commit 0c188a4
Show file tree
Hide file tree
Showing 21 changed files with 103 additions and 78 deletions.
2 changes: 1 addition & 1 deletion .azure-pipelines/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jobs:
displayName: Pre-build checks

pool:
vmImage: ubuntu-22.04
vmImage: ubuntu-24.04

steps:
- template: ./prebuild-checks.yml
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ jobs:
name: 'Check if generated files are up to date'
# Don't use ubuntu-latest but a specific version to make the job
# reproducible: to get the same tools versions (autoconf, aclocal, ...)
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
timeout-minutes: 60
needs: check_source
if: needs.check_source.outputs.run_tests == 'true'
Expand Down Expand Up @@ -237,7 +237,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04]
os: [ubuntu-24.04]
openssl_ver: [3.0.15, 3.1.7, 3.2.3, 3.3.2]
env:
OPENSSL_VER: ${{ matrix.openssl_ver }}
Expand Down Expand Up @@ -297,7 +297,7 @@ jobs:

test_hypothesis:
name: "Hypothesis tests on Ubuntu"
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
timeout-minutes: 60
needs: check_source
if: needs.check_source.outputs.run_tests == 'true' && needs.check_source.outputs.run_hypothesis == 'true'
Expand Down Expand Up @@ -417,7 +417,7 @@ jobs:
if: needs.check_source.outputs.run_tests == 'true'
strategy:
matrix:
os: [ubuntu-22.04]
os: [ubuntu-24.04]
env:
OPENSSL_VER: 3.0.15
PYTHONSTRICTEXTENSIONBUILD: 1
Expand Down
11 changes: 6 additions & 5 deletions .github/workflows/jit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
- x86_64-pc-windows-msvc/msvc
- aarch64-pc-windows-msvc/msvc
- x86_64-apple-darwin/clang
- aarch64-apple-darwin/clang
# - aarch64-apple-darwin/clang
- x86_64-unknown-linux-gnu/gcc
- x86_64-unknown-linux-gnu/clang
- aarch64-unknown-linux-gnu/gcc
Expand All @@ -79,10 +79,11 @@ jobs:
architecture: x86_64
runner: macos-13
compiler: clang
- target: aarch64-apple-darwin/clang
architecture: aarch64
runner: macos-14
compiler: clang
# GH-126464: A recent change to either GHA or LLVM broke this job:
# - target: aarch64-apple-darwin/clang
# architecture: aarch64
# runner: macos-14
# compiler: clang
- target: x86_64-unknown-linux-gnu/gcc
architecture: x86_64
runner: ubuntu-22.04
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/posix-deps-apt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ apt-get -yq install \
libgdbm-dev \
libgdbm-compat-dev \
liblzma-dev \
libmpdec-dev \
libncurses5-dev \
libreadline6-dev \
libsqlite3-dev \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/reusable-tsan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ on:
jobs:
build_tsan_reusable:
name: 'Thread sanitizer'
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/reusable-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04]
os: [ubuntu-24.04]
env:
FORCE_COLOR: 1
OPENSSL_VER: 3.0.15
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/reusable-wasi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
build_wasi_reusable:
name: 'build and test'
timeout-minutes: 60
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
env:
WASMTIME_VERSION: 22.0.0
WASI_SDK_VERSION: 24
Expand Down
5 changes: 5 additions & 0 deletions Doc/library/pathlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1592,6 +1592,11 @@ Copying, moving and deleting
This argument has no effect when copying files on Windows (where
metadata is always preserved).

.. note::
Where supported by the operating system and file system, this method
performs a lightweight copy, where data blocks are only copied when
modified. This is known as copy-on-write.

.. versionadded:: 3.14


Expand Down
14 changes: 9 additions & 5 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -1507,11 +1507,15 @@ def getclosurevars(func):
global_vars = {}
builtin_vars = {}
unbound_names = set()
for name in code.co_names:
if name in ("None", "True", "False"):
# Because these used to be builtins instead of keywords, they
# may still show up as name references. We ignore them.
continue
global_names = set()
for instruction in dis.get_instructions(code):
opname = instruction.opname
name = instruction.argval
if opname == "LOAD_ATTR":
unbound_names.add(name)
elif opname == "LOAD_GLOBAL":
global_names.add(name)
for name in global_names:
try:
global_vars[name] = global_ns[name]
except KeyError:
Expand Down
59 changes: 37 additions & 22 deletions Lib/pathlib/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class PathGlobber(_GlobberBase):
@staticmethod
def concat_path(path, text):
"""Appends text to the given path."""
return path.with_segments(path._raw_path + text)
return path.with_segments(str(path) + text)


class PurePathBase:
Expand All @@ -112,9 +112,9 @@ class PurePathBase:
"""

__slots__ = (
# The `_raw_path` slot store a joined string path. This is set in the
# `__init__()` method.
'_raw_path',
# The `_raw_paths` slot stores unjoined string paths. This is set in
# the `__init__()` method.
'_raw_paths',

# The '_resolving' slot stores a boolean indicating whether the path
# is being processed by `PathBase.resolve()`. This prevents duplicate
Expand All @@ -124,11 +124,14 @@ class PurePathBase:
parser = ParserBase()
_globber = PathGlobber

def __init__(self, path, *paths):
self._raw_path = self.parser.join(path, *paths) if paths else path
if not isinstance(self._raw_path, str):
raise TypeError(
f"path should be a str, not {type(self._raw_path).__name__!r}")
def __init__(self, arg, *args):
paths = [arg]
paths.extend(args)
for path in paths:
if not isinstance(path, str):
raise TypeError(
f"path should be a str, not {type(path).__name__!r}")
self._raw_paths = paths
self._resolving = False

def with_segments(self, *pathsegments):
Expand All @@ -141,7 +144,19 @@ def with_segments(self, *pathsegments):
def __str__(self):
"""Return the string representation of the path, suitable for
passing to system calls."""
return self._raw_path
paths = self._raw_paths
if len(paths) == 1:
return paths[0]
elif paths:
# Join path segments from the initializer.
path = self.parser.join(*paths)
# Cache the joined path.
paths.clear()
paths.append(path)
return path
else:
paths.append('')
return ''

def as_posix(self):
"""Return the string representation of the path with forward (/)
Expand All @@ -166,7 +181,7 @@ def anchor(self):
@property
def name(self):
"""The final path component, if any."""
return self.parser.split(self._raw_path)[1]
return self.parser.split(str(self))[1]

@property
def suffix(self):
Expand Down Expand Up @@ -202,7 +217,7 @@ def with_name(self, name):
split = self.parser.split
if split(name)[0]:
raise ValueError(f"Invalid name {name!r}")
return self.with_segments(split(self._raw_path)[0], name)
return self.with_segments(split(str(self))[0], name)

def with_stem(self, stem):
"""Return a new path with the stem changed."""
Expand Down Expand Up @@ -242,17 +257,17 @@ def relative_to(self, other, *, walk_up=False):
anchor0, parts0 = self._stack
anchor1, parts1 = other._stack
if anchor0 != anchor1:
raise ValueError(f"{self._raw_path!r} and {other._raw_path!r} have different anchors")
raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
while parts0 and parts1 and parts0[-1] == parts1[-1]:
parts0.pop()
parts1.pop()
for part in parts1:
if not part or part == '.':
pass
elif not walk_up:
raise ValueError(f"{self._raw_path!r} is not in the subpath of {other._raw_path!r}")
raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}")
elif part == '..':
raise ValueError(f"'..' segment in {other._raw_path!r} cannot be walked")
raise ValueError(f"'..' segment in {str(other)!r} cannot be walked")
else:
parts0.append('..')
return self.with_segments('', *reversed(parts0))
Expand Down Expand Up @@ -289,17 +304,17 @@ def joinpath(self, *pathsegments):
paths) or a totally different path (if one of the arguments is
anchored).
"""
return self.with_segments(self._raw_path, *pathsegments)
return self.with_segments(*self._raw_paths, *pathsegments)

def __truediv__(self, key):
try:
return self.with_segments(self._raw_path, key)
return self.with_segments(*self._raw_paths, key)
except TypeError:
return NotImplemented

def __rtruediv__(self, key):
try:
return self.with_segments(key, self._raw_path)
return self.with_segments(key, *self._raw_paths)
except TypeError:
return NotImplemented

Expand All @@ -311,7 +326,7 @@ def _stack(self):
*parts* is a reversed list of parts following the anchor.
"""
split = self.parser.split
path = self._raw_path
path = str(self)
parent, name = split(path)
names = []
while path != parent:
Expand All @@ -323,7 +338,7 @@ def _stack(self):
@property
def parent(self):
"""The logical parent of the path."""
path = self._raw_path
path = str(self)
parent = self.parser.split(path)[0]
if path != parent:
parent = self.with_segments(parent)
Expand All @@ -335,7 +350,7 @@ def parent(self):
def parents(self):
"""A sequence of this path's logical parents."""
split = self.parser.split
path = self._raw_path
path = str(self)
parent = split(path)[0]
parents = []
while path != parent:
Expand All @@ -347,7 +362,7 @@ def parents(self):
def is_absolute(self):
"""True if the path is absolute (has both a root and, if applicable,
a drive)."""
return self.parser.isabs(self._raw_path)
return self.parser.isabs(str(self))

@property
def _pattern_str(self):
Expand Down
25 changes: 6 additions & 19 deletions Lib/pathlib/_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ class PurePath(PurePathBase):
"""

__slots__ = (
# The `_raw_paths` slot stores unnormalized string paths. This is set
# in the `__init__()` method.
'_raw_paths',

# The `_drv`, `_root` and `_tail_cached` slots store parsed and
# normalized parts of the path. They are set when any of the `drive`,
# `root` or `_tail` properties are accessed for the first time. The
Expand Down Expand Up @@ -299,25 +295,14 @@ def _parse_pattern(cls, pattern):
parts.append('')
return parts

@property
def _raw_path(self):
"""The joined but unnormalized path."""
paths = self._raw_paths
if len(paths) == 0:
path = ''
elif len(paths) == 1:
path = paths[0]
else:
path = self.parser.join(*paths)
return path

@property
def drive(self):
"""The drive prefix (letter or UNC path), if any."""
try:
return self._drv
except AttributeError:
self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path)
raw_path = PurePathBase.__str__(self)
self._drv, self._root, self._tail_cached = self._parse_path(raw_path)
return self._drv

@property
Expand All @@ -326,15 +311,17 @@ def root(self):
try:
return self._root
except AttributeError:
self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path)
raw_path = PurePathBase.__str__(self)
self._drv, self._root, self._tail_cached = self._parse_path(raw_path)
return self._root

@property
def _tail(self):
try:
return self._tail_cached
except AttributeError:
self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path)
raw_path = PurePathBase.__str__(self)
self._drv, self._root, self._tail_cached = self._parse_path(raw_path)
return self._tail_cached

@property
Expand Down
13 changes: 13 additions & 0 deletions Lib/test/test_inspect/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -1960,6 +1960,19 @@ def g(local_ref):
builtin_vars, unbound_names)
self.assertEqual(inspect.getclosurevars(C().f(_arg)), expected)

def test_attribute_same_name_as_global_var(self):
class C:
_global_ref = object()
def f():
print(C._global_ref, _global_ref)
nonlocal_vars = {"C": C}
global_vars = {"_global_ref": _global_ref}
builtin_vars = {"print": print}
unbound_names = {"_global_ref"}
expected = inspect.ClosureVars(nonlocal_vars, global_vars,
builtin_vars, unbound_names)
self.assertEqual(inspect.getclosurevars(f), expected)

def test_nonlocal_vars(self):
# More complex tests of nonlocal resolution
def _nonlocal_vars(f):
Expand Down
5 changes: 0 additions & 5 deletions Lib/test/test_pathlib/test_pathlib_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,6 @@ def test_unsupported_operation_pure(self):
p.suffix
with self.assertRaises(e):
p.suffixes
with self.assertRaises(e):
p / 'bar'
with self.assertRaises(e):
'bar' / p
self.assertRaises(e, p.joinpath, 'bar')
self.assertRaises(e, p.with_name, 'bar')
self.assertRaises(e, p.with_stem, 'bar')
self.assertRaises(e, p.with_suffix, '.txt')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed an issue where :func:`inspect.getclosurevars` would incorrectly classify an attribute name as a global variable when the name exists both as an attribute name and a global variable.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Removed unnecessary DLLs from Windows embeddable package
Loading

0 comments on commit 0c188a4

Please sign in to comment.