[gentoo-commits] proj/portage:master commit in: lib/portage/sync/modules/git/

2024-05-12 Thread Sam James
commit: 95b4337d376a146db8fda7717393366175cbd285
Author: Alex Xu (Hello71)  yahoo  ca>
AuthorDate: Sun May  5 20:24:52 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Sun May 12 16:20:02 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=95b4337d

sync: git: add safe.directory for get head commit

Closes: https://bugs.gentoo.org/930992
Fixes: 1339a02103 ("sync: git: include signing key and git revision in log 
output")
Signed-off-by: Alex Xu (Hello71)  yahoo.ca>
Signed-off-by: Sam James  gentoo.org>

 lib/portage/sync/modules/git/git.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lib/portage/sync/modules/git/git.py 
b/lib/portage/sync/modules/git/git.py
index 8fdbf97de0..a2830280fb 100644
--- a/lib/portage/sync/modules/git/git.py
+++ b/lib/portage/sync/modules/git/git.py
@@ -606,6 +606,7 @@ class GitSync(NewBase):
 if self.bin_command is None:
 # return quietly so that we don't pollute emerge --info output
 return (1, False)
+self.add_safe_directory()
 rev_cmd = [self.bin_command, "rev-list", "--max-count=1", "HEAD"]
 try:
 ret = (



[gentoo-commits] proj/portage:master commit in: lib/portage/sync/modules/zipfile/

2024-04-27 Thread Sam James
commit: 8c6e5d06afbf6fca1893cff5ed777e44f93b7a5d
Author: Alexey Gladkov  kernel  org>
AuthorDate: Sun Mar  3 16:41:08 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Sun Apr 28 00:04:08 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=8c6e5d06

sync/zipfile: Handle ETag header

Most services add an ETag header and determine whether the locally
cached version of the URL has expired. So we can add ETag processing to
avoid unnecessary downloading and unpacking of the zip archive.

Signed-off-by: Alexey Gladkov  kernel.org>
Signed-off-by: Sam James  gentoo.org>

 lib/portage/sync/modules/zipfile/zipfile.py | 36 +++--
 1 file changed, 29 insertions(+), 7 deletions(-)

diff --git a/lib/portage/sync/modules/zipfile/zipfile.py 
b/lib/portage/sync/modules/zipfile/zipfile.py
index 1762d2c8f1..bb78b39243 100644
--- a/lib/portage/sync/modules/zipfile/zipfile.py
+++ b/lib/portage/sync/modules/zipfile/zipfile.py
@@ -10,7 +10,7 @@ import tempfile
 import urllib.request
 
 import portage
-from portage.util import writemsg_level
+from portage.util import writemsg_level, writemsg_stdout
 from portage.sync.syncbase import SyncBase
 
 
@@ -31,13 +31,31 @@ class ZipFile(SyncBase):
 if kwargs:
 self._kwargs(kwargs)
 
-# initial checkout
-zip_uri = self.repo.sync_uri
+req = urllib.request.Request(url=self.repo.sync_uri)
 
-with urllib.request.urlopen(zip_uri) as response:
-with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
-shutil.copyfileobj(response, tmp_file)
-zip_file = tmp_file.name
+info = portage.grabdict(os.path.join(self.repo.location, ".info"))
+if "etag" in info:
+req.add_header("If-None-Match", info["etag"][0])
+
+try:
+with urllib.request.urlopen(req) as response:
+with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
+shutil.copyfileobj(response, tmp_file)
+
+zip_file = tmp_file.name
+etag = response.headers.get("etag")
+
+except urllib.error.HTTPError as resp:
+if resp.code == 304:
+writemsg_stdout(">>> The repository has not changed.\n", 
noiselevel=-1)
+return (os.EX_OK, False)
+
+writemsg_level(
+f"!!! Unable to obtain zip archive: {resp}\n",
+noiselevel=-1,
+level=logging.ERROR,
+)
+return (1, False)
 
 if not zipfile.is_zipfile(zip_file):
 msg = "!!! file is not a zip archive."
@@ -77,6 +95,10 @@ class ZipFile(SyncBase):
 with open(dstpath, "wb") as dstfile:
 shutil.copyfileobj(srcfile, dstfile)
 
+with open(os.path.join(self.repo.location, ".info"), "w") as infofile:
+if etag:
+infofile.write(f"etag {etag}\n")
+
 os.unlink(zip_file)
 
 return (os.EX_OK, True)



[gentoo-commits] proj/portage:master commit in: lib/portage/sync/modules/zipfile/

2024-04-27 Thread Sam James
commit: 7e93192fda22594b9e9d223c54a39b4bad0554f9
Author: Alexey Gladkov  kernel  org>
AuthorDate: Mon Mar 11 00:25:07 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Sun Apr 28 00:04:08 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=7e93192f

sync/zipfile: Add retrieve_head to return archive checksum

Since we have an ETag, we can return the checksum of the archive. It
will be a replacement for the head commit of the repository.

Suggested-by: Zac Medico  gentoo.org>
Signed-off-by: Alexey Gladkov  kernel.org>
Signed-off-by: Sam James  gentoo.org>

 lib/portage/sync/modules/zipfile/__init__.py | 3 ++-
 lib/portage/sync/modules/zipfile/zipfile.py  | 9 +
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/lib/portage/sync/modules/zipfile/__init__.py 
b/lib/portage/sync/modules/zipfile/__init__.py
index 19fe3af412..e44833088c 100644
--- a/lib/portage/sync/modules/zipfile/__init__.py
+++ b/lib/portage/sync/modules/zipfile/__init__.py
@@ -21,10 +21,11 @@ module_spec = {
 "sourcefile": "zipfile",
 "class": "ZipFile",
 "description": doc,
-"functions": ["sync"],
+"functions": ["sync", "retrieve_head"],
 "func_desc": {
 "sync": "Performs an archived http download of the "
 + "repository, then unpacks it.",
+"retrieve_head": "Returns the checksum of the unpacked 
archive.",
 },
 "validate_config": CheckSyncConfig,
 "module_specific_options": (),

diff --git a/lib/portage/sync/modules/zipfile/zipfile.py 
b/lib/portage/sync/modules/zipfile/zipfile.py
index bb78b39243..3cd210a64b 100644
--- a/lib/portage/sync/modules/zipfile/zipfile.py
+++ b/lib/portage/sync/modules/zipfile/zipfile.py
@@ -26,6 +26,15 @@ class ZipFile(SyncBase):
 def __init__(self):
 SyncBase.__init__(self, "emerge", ">=sys-apps/portage-2.3")
 
+def retrieve_head(self, **kwargs):
+"""Get information about the checksum of the unpacked archive"""
+if kwargs:
+self._kwargs(kwargs)
+info = portage.grabdict(os.path.join(self.repo.location, ".info"))
+if "etag" in info:
+return (os.EX_OK, info["etag"][0])
+return (1, False)
+
 def sync(self, **kwargs):
 """Sync the repository"""
 if kwargs:



[gentoo-commits] proj/portage:master commit in: lib/portage/sync/modules/zipfile/

2024-04-27 Thread Sam James
commit: ced2e6d4f4ac95b8e17cf7dae964a64037a85bf0
Author: Alexey Gladkov  kernel  org>
AuthorDate: Mon Mar 11 17:09:05 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Sun Apr 28 00:04:09 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=ced2e6d4

sync/zipfile: Recycle files that have not changed

We can check whether the content of files from the archive differs from
the current revision. This will give us several advantages:

* This will give us some meaning to the mtime of files, since it will
prevent the timestamps of unmodified files from being changed.

* This will also get rid of recreatiing self.repo.location, which will
allow sync with FEATURES=usersync because self.repo.location is reused.

Suggested-by: Zac Medico  gentoo.org>
Signed-off-by: Alexey Gladkov  kernel.org>
Signed-off-by: Sam James  gentoo.org>

 lib/portage/sync/modules/zipfile/zipfile.py | 32 -
 1 file changed, 31 insertions(+), 1 deletion(-)

diff --git a/lib/portage/sync/modules/zipfile/zipfile.py 
b/lib/portage/sync/modules/zipfile/zipfile.py
index 3cd210a64b..edfb5aa681 100644
--- a/lib/portage/sync/modules/zipfile/zipfile.py
+++ b/lib/portage/sync/modules/zipfile/zipfile.py
@@ -35,6 +35,16 @@ class ZipFile(SyncBase):
 return (os.EX_OK, info["etag"][0])
 return (1, False)
 
+def _do_cmp(self, f1, f2):
+bufsize = 8 * 1024
+while True:
+b1 = f1.read(bufsize)
+b2 = f2.read(bufsize)
+if b1 != b2:
+return False
+if not b1:
+return True
+
 def sync(self, **kwargs):
 """Sync the repository"""
 if kwargs:
@@ -76,7 +86,15 @@ class ZipFile(SyncBase):
 return (1, False)
 
 # Drop previous tree
-shutil.rmtree(self.repo.location)
+tempdir = tempfile.mkdtemp(prefix=".temp", dir=self.repo.location)
+tmpname = os.path.basename(tempdir)
+
+for name in os.listdir(self.repo.location):
+if name != tmpname:
+os.rename(
+os.path.join(self.repo.location, name),
+os.path.join(tempdir, name),
+)
 
 with zipfile.ZipFile(zip_file) as archive:
 strip_comp = 0
@@ -101,9 +119,21 @@ class ZipFile(SyncBase):
 continue
 
 with archive.open(n) as srcfile:
+prvpath = os.path.join(tempdir, *parts[strip_comp:])
+
+if os.path.exists(prvpath):
+with open(prvpath, "rb") as prvfile:
+if self._do_cmp(prvfile, srcfile):
+os.rename(prvpath, dstpath)
+continue
+srcfile.seek(0)
+
 with open(dstpath, "wb") as dstfile:
 shutil.copyfileobj(srcfile, dstfile)
 
+# Drop previous tree
+shutil.rmtree(tempdir)
+
 with open(os.path.join(self.repo.location, ".info"), "w") as infofile:
 if etag:
 infofile.write(f"etag {etag}\n")



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/sync/

2024-04-27 Thread Sam James
commit: b01cd4208a17a141311d490788aff11537312575
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Mar 12 10:20:56 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Sun Apr 28 00:04:09 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=b01cd420

sync/zipfile: Add testcase for etag

Signed-off-by: Zac Medico  gentoo.org>
Signed-off-by: Sam James  gentoo.org>

 lib/portage/tests/sync/meson.build  |  1 +
 lib/portage/tests/sync/test_sync_zipfile.py | 99 +
 2 files changed, 100 insertions(+)

diff --git a/lib/portage/tests/sync/meson.build 
b/lib/portage/tests/sync/meson.build
index b78583021f..8c566080e3 100644
--- a/lib/portage/tests/sync/meson.build
+++ b/lib/portage/tests/sync/meson.build
@@ -1,6 +1,7 @@
 py.install_sources(
 [
 'test_sync_local.py',
+'test_sync_zipfile.py',
 '__init__.py',
 '__test__.py',
 ],

diff --git a/lib/portage/tests/sync/test_sync_zipfile.py 
b/lib/portage/tests/sync/test_sync_zipfile.py
new file mode 100644
index 00..4fbde8a351
--- /dev/null
+++ b/lib/portage/tests/sync/test_sync_zipfile.py
@@ -0,0 +1,99 @@
+# Copyright 2024 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+import http.server
+import os
+import shutil
+import socketserver
+import subprocess
+import tempfile
+import textwrap
+import threading
+from functools import partial
+
+import portage
+from portage.tests import TestCase
+from portage.tests.resolver.ResolverPlayground import ResolverPlayground
+
+
+class test_sync_zipfile_case(TestCase):
+def test_sync_zipfile(self):
+cpv = "dev-libs/A-0"
+ebuilds = {
+cpv: {"EAPI": "8"},
+}
+etag = "foo"
+
+server = None
+playground = None
+tmpdir = tempfile.mkdtemp()
+try:
+
+class Handler(http.server.SimpleHTTPRequestHandler):
+def end_headers(self):
+self.send_header("etag", etag)
+super().end_headers()
+
+server = socketserver.TCPServer(
+("127.0.0.1", 0),
+partial(Handler, directory=tmpdir),
+)
+threading.Thread(target=server.serve_forever, daemon=True).start()
+
+playground = ResolverPlayground(
+ebuilds=ebuilds,
+)
+settings = playground.settings
+
+env = settings.environ()
+
+repos_conf = textwrap.dedent(
+"""
+[test_repo]
+location = %(location)s
+sync-type = zipfile
+sync-uri = %(sync-uri)s
+auto-sync = true
+"""
+)
+
+repo_location = f"{playground.eprefix}/var/repositories/test_repo"
+
+env["PORTAGE_REPOSITORIES"] = repos_conf % {
+"location": repo_location,
+"sync-uri": 
"http://{}:{}/test_repo.zip".format(*server.server_address),
+}
+
+shutil.make_archive(os.path.join(tmpdir, "test_repo"), "zip", 
repo_location)
+
+ebuild = 
playground.trees[playground.eroot]["porttree"].dbapi.findname(cpv)
+self.assertTrue(os.path.exists(ebuild))
+shutil.rmtree(repo_location)
+self.assertFalse(os.path.exists(ebuild))
+
+result = subprocess.run(
+[
+"emerge",
+"--sync",
+],
+env=env,
+stdout=subprocess.PIPE,
+stderr=subprocess.STDOUT,
+)
+output = result.stdout.decode(errors="replace")
+try:
+self.assertEqual(result.returncode, os.EX_OK)
+except Exception:
+print(output)
+raise
+
+repo = settings.repositories["test_repo"]
+sync_mod = portage.sync.module_controller.get_class("zipfile")
+status, repo_revision = sync_mod().retrieve_head(options={"repo": 
repo})
+self.assertEqual(status, os.EX_OK)
+self.assertEqual(repo_revision, etag)
+finally:
+if server is not None:
+server.shutdown()
+shutil.rmtree(tmpdir)
+playground.cleanup()



[gentoo-commits] proj/portage:master commit in: lib/portage/sync/modules/zipfile/

2024-04-27 Thread Sam James
commit: 80445d9b00bfcd1eb4955cf3ecb397b4c02663ba
Author: Alexey Gladkov  kernel  org>
AuthorDate: Mon Feb 12 13:59:40 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Sun Apr 28 00:04:07 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=80445d9b

sync: Add method to download zip archives

Add a simple method for synchronizing repository as a snapshot in a zip
archive. The implementation does not require external utilities to
download and unpack archive. This makes the method very cheap.

The main usecase being considered is obtaining snapshots of github
repositories, but many other web interfaces for git also support
receiving snapshots in zip format.

For example, to get a snapshot of the master branch:

  https://github.com/gentoo/portage/archive/refs/heads/master.zip
  https://gitweb.gentoo.org/proj/portage.git/snapshot/portage-master.zip

or a link to a snapshot of the tag:

  https://github.com/gentoo/portage/archive/refs/tags/portage-3.0.61.zip

Signed-off-by: Alexey Gladkov  kernel.org>
Signed-off-by: Sam James  gentoo.org>

 lib/portage/sync/modules/zipfile/__init__.py | 33 +++
 lib/portage/sync/modules/zipfile/zipfile.py  | 82 
 2 files changed, 115 insertions(+)

diff --git a/lib/portage/sync/modules/zipfile/__init__.py 
b/lib/portage/sync/modules/zipfile/__init__.py
new file mode 100644
index 00..19fe3af412
--- /dev/null
+++ b/lib/portage/sync/modules/zipfile/__init__.py
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2024  Alexey Gladkov 
+
+doc = """Zipfile plug-in module for portage.
+Performs a http download of a portage snapshot and unpacks it to the repo
+location."""
+__doc__ = doc[:]
+
+
+import os
+
+from portage.sync.config_checks import CheckSyncConfig
+
+
+module_spec = {
+"name": "zipfile",
+"description": doc,
+"provides": {
+"zipfile-module": {
+"name": "zipfile",
+"sourcefile": "zipfile",
+"class": "ZipFile",
+"description": doc,
+"functions": ["sync"],
+"func_desc": {
+"sync": "Performs an archived http download of the "
++ "repository, then unpacks it.",
+},
+"validate_config": CheckSyncConfig,
+"module_specific_options": (),
+},
+},
+}

diff --git a/lib/portage/sync/modules/zipfile/zipfile.py 
b/lib/portage/sync/modules/zipfile/zipfile.py
new file mode 100644
index 00..1762d2c8f1
--- /dev/null
+++ b/lib/portage/sync/modules/zipfile/zipfile.py
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2024  Alexey Gladkov 
+
+import os
+import os.path
+import logging
+import zipfile
+import shutil
+import tempfile
+import urllib.request
+
+import portage
+from portage.util import writemsg_level
+from portage.sync.syncbase import SyncBase
+
+
+class ZipFile(SyncBase):
+"""ZipFile sync module"""
+
+short_desc = "Perform sync operations on GitHub repositories"
+
+@staticmethod
+def name():
+return "ZipFile"
+
+def __init__(self):
+SyncBase.__init__(self, "emerge", ">=sys-apps/portage-2.3")
+
+def sync(self, **kwargs):
+"""Sync the repository"""
+if kwargs:
+self._kwargs(kwargs)
+
+# initial checkout
+zip_uri = self.repo.sync_uri
+
+with urllib.request.urlopen(zip_uri) as response:
+with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
+shutil.copyfileobj(response, tmp_file)
+zip_file = tmp_file.name
+
+if not zipfile.is_zipfile(zip_file):
+msg = "!!! file is not a zip archive."
+self.logger(self.xterm_titles, msg)
+writemsg_level(msg + "\n", noiselevel=-1, level=logging.ERROR)
+
+os.unlink(zip_file)
+
+return (1, False)
+
+# Drop previous tree
+shutil.rmtree(self.repo.location)
+
+with zipfile.ZipFile(zip_file) as archive:
+strip_comp = 0
+
+for f in archive.namelist():
+f = os.path.normpath(f)
+if os.path.basename(f) == "profiles":
+strip_comp = f.count("/")
+break
+
+for n in archive.infolist():
+p = os.path.normpath(n.filename)
+
+if os.path.isabs(p):
+continue
+
+parts = p.split("/")
+dstpath = os.path.join(self.repo.location, *parts[strip_comp:])
+
+if n.is_dir():
+os.makedirs(dstpath, mode=0o755, exist_ok=True)
+continue
+
+with archive.open(n) as srcfile:
+with open(dstpath, "wb") as dstfile:
+shutil.copyfileobj(srcfile, dstfile)
+
+os.unlink(zip_file)
+
+return (os.EX_OK, True)



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-04-26 Thread Sam James
commit: 36235596e061bf8cf4729b3915f9aaa6ec80baa3
Author: Alfred Wingate  protonmail  com>
AuthorDate: Sat Apr  6 08:28:43 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Fri Apr 26 22:05:48 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=36235596

lib: adhere to python package version conventions

* Commit metadata isn't valid version that python tooling is expected to
  parse. Follow python ecosystem conventions and make it a local
  version.

https://packaging.python.org/en/latest/specifications/version-specifiers/#local-version-segments

Example:
* Old: 3.0.63-g08a2bc380
* New: 3.0.63+g08a2bc380

Bug: https://bugs.gentoo.org/926966
Signed-off-by: Alfred Wingate  protonmail.com>
Closes: https://github.com/gentoo/portage/pull/1314
Signed-off-by: Sam James  gentoo.org>

 lib/portage/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/portage/__init__.py b/lib/portage/__init__.py
index a468eeaff3..21bf993170 100644
--- a/lib/portage/__init__.py
+++ b/lib/portage/__init__.py
@@ -732,7 +732,7 @@ if installation.TYPE == installation.TYPES.SOURCE:
 output = _unicode_decode(proc.communicate()[0], 
encoding=encoding)
 status = proc.wait()
 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == os.EX_OK:
-VERSION = output.lstrip('portage-').strip()
+VERSION = output.lstrip("portage-").strip().replace("-g", 
"+g")
 else:
 VERSION = "HEAD"
 return VERSION



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-04-26 Thread Sam James
commit: 381fad5e3554ec94ec5626e8c17874f32b30b752
Author: Sam James  gentoo  org>
AuthorDate: Tue Aug 29 07:26:36 2023 +
Commit: Sam James  gentoo  org>
CommitDate: Fri Apr 26 22:05:48 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=381fad5e

lib: use more pure git-describe output for --version

Use `git describe --dirty` output rather than mangling git-describe and 
reinventing
--dirty by manually checking for changes post-commit.

We no longer mangle the 7th commit post-tag into _p7, but instead do: 
${tag}-7-${last_commit}.

This is similar to gnulib's git-version-gen (which we may still want to import,
not sure, this seems enough for now) and is familiar output for developers.

Example:
* Old: 3.0.51_p7
* New: 3.0.51-7-g098b30548

Bug: https://bugs.gentoo.org/912209
Signed-off-by: Sam James  gentoo.org>

 lib/portage/__init__.py | 35 ---
 1 file changed, 4 insertions(+), 31 deletions(-)

diff --git a/lib/portage/__init__.py b/lib/portage/__init__.py
index aa81bdb4c2..a468eeaff3 100644
--- a/lib/portage/__init__.py
+++ b/lib/portage/__init__.py
@@ -720,10 +720,7 @@ if installation.TYPE == installation.TYPES.SOURCE:
 BASH_BINARY,
 "-c",
 (
-f"cd {_shell_quote(PORTAGE_BASE_PATH)} ; git describe 
--match 'portage-*' || exit $? ; "
-'if [ -n "`git diff-index --name-only --diff-filter=M 
HEAD`" ] ; '
-"then echo modified ; git rev-list --format=%%ct -n 1 
HEAD ; fi ; "
-"exit 0"
+f"cd {_shell_quote(PORTAGE_BASE_PATH)} ; git describe 
--dirty --match 'portage-*' || exit $? ; "
 ),
 ]
 cmd = [
@@ -735,33 +732,9 @@ if installation.TYPE == installation.TYPES.SOURCE:
 output = _unicode_decode(proc.communicate()[0], 
encoding=encoding)
 status = proc.wait()
 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == os.EX_OK:
-output_lines = output.splitlines()
-if output_lines:
-version_split = output_lines[0].split("-")
-if len(version_split) > 1:
-VERSION = version_split[1]
-patchlevel = False
-if len(version_split) > 2:
-patchlevel = True
-VERSION = f"{VERSION}_p{version_split[2]}"
-if len(output_lines) > 1 and output_lines[1] == 
"modified":
-head_timestamp = None
-if len(output_lines) > 3:
-try:
-head_timestamp = int(output_lines[3])
-except ValueError:
-pass
-timestamp = int(time.time())
-if (
-head_timestamp is not None
-and timestamp > head_timestamp
-):
-timestamp = timestamp - head_timestamp
-if not patchlevel:
-VERSION = f"{VERSION}_p0"
-VERSION = f"{VERSION}_p{timestamp}"
-return VERSION
-VERSION = "HEAD"
+VERSION = output.lstrip('portage-').strip()
+else:
+VERSION = "HEAD"
 return VERSION
 
 VERSION = _LazyVersion()



[gentoo-commits] proj/portage:master commit in: lib/portage/package/ebuild/

2024-03-24 Thread Zac Medico
commit: 72f41d07b5396195a98691fafeb3e5b207a76564
Author: Raul E Rangel  chromium  org>
AuthorDate: Thu Mar 21 20:29:59 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Mar 24 22:18:34 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=72f41d07

Reapply "config: Don't directly modify FEATURES"

This reverts commit b150419d28bd7afb98404a829c639584d34efc03.

It turns out we need to keep the open coded version to avoid creating
a persistent setting.
See https://github.com/gentoo/portage/pull/1098#issuecomment-1761638611

This change just adds a sorted() so we get deterministic ordering.

Bug: https://bugs.gentoo.org/914441
Signed-off-by: Raul E Rangel  chromium.org>
Closes: https://github.com/gentoo/portage/pull/1312
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/package/ebuild/config.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/portage/package/ebuild/config.py 
b/lib/portage/package/ebuild/config.py
index bafdc55a08..67fd1bb18d 100644
--- a/lib/portage/package/ebuild/config.py
+++ b/lib/portage/package/ebuild/config.py
@@ -2206,7 +2206,9 @@ class config:
 # "test" is in IUSE and USE=test is masked, so execution
 # of src_test() probably is not reliable. Therefore,
 # temporarily disable FEATURES=test just for this package.
-self["FEATURES"] = " ".join(x for x in self.features if x != 
"test")
+self["FEATURES"] = " ".join(
+x for x in sorted(self.features) if x != "test"
+)
 
 # Allow _* flags from USE_EXPAND wildcards to pass through here.
 use.difference_update(



[gentoo-commits] proj/portage:master commit in: lib/portage/util/, lib/portage/util/file_copy/, src/

2024-03-15 Thread Mike Gilbert
commit: 23529ee81964665107400e87fc3d49c256e915c0
Author: Mike Gilbert  gentoo  org>
AuthorDate: Fri Mar  1 15:45:58 2024 +
Commit: Mike Gilbert  gentoo  org>
CommitDate: Fri Mar 15 20:05:34 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=23529ee8

Replace linux_reflink extension module

Python 3.8 added support for copy_file_range in the os module,
so we can just call that directly.

Also, we can use the FICLONE ioctl for fast file clones on supported
filesystems (btrfs).

Signed-off-by: Mike Gilbert  gentoo.org>

 lib/portage/util/file_copy.py  | 137 ++
 lib/portage/util/file_copy/__init__.py |  36 ---
 lib/portage/util/file_copy/meson.build |   7 -
 lib/portage/util/meson.build   |   2 +-
 src/meson.build|  20 --
 src/portage_util_file_copy_reflink_linux.c | 396 -
 6 files changed, 138 insertions(+), 460 deletions(-)

diff --git a/lib/portage/util/file_copy.py b/lib/portage/util/file_copy.py
new file mode 100644
index 00..e3926d8ef6
--- /dev/null
+++ b/lib/portage/util/file_copy.py
@@ -0,0 +1,137 @@
+# Copyright 2024 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+import errno
+import fcntl
+import logging
+import os
+import platform
+import shutil
+import sys
+
+
+logger = logging.getLogger(__name__)
+
+# Added in Python 3.12
+FICLONE = getattr(fcntl, "FICLONE", 0x40049409)
+
+# Unavailable in PyPy
+SEEK_DATA = getattr(os, "SEEK_DATA", 3)
+SEEK_HOLE = getattr(os, "SEEK_HOLE", 4)
+
+
+def _get_chunks(src):
+try:
+offset_hole = 0
+while True:
+try:
+# Find the next bit of data
+offset_data = os.lseek(src, offset_hole, SEEK_DATA)
+except OSError as e:
+# Re-raise for unexpected errno values
+if e.errno not in (errno.EINVAL, errno.ENXIO):
+raise
+
+offset_end = os.lseek(src, 0, os.SEEK_END)
+
+if e.errno == errno.ENXIO:
+# End of file
+if offset_end > offset_hole:
+# Hole at end of file
+yield (offset_end, 0)
+else:
+# SEEK_DATA failed with EINVAL, return the whole file
+yield (0, offset_end)
+
+break
+else:
+offset_hole = os.lseek(src, offset_data, SEEK_HOLE)
+yield (offset_data, offset_hole - offset_data)
+
+except OSError:
+logger.warning("_get_chunks failed unexpectedly", 
exc_info=sys.exc_info())
+raise
+
+
+def _do_copy_file_range(src, dst, offset, count):
+while count > 0:
+# count must fit in ssize_t
+c = min(count, sys.maxsize)
+written = os.copy_file_range(src, dst, c, offset, offset)
+if written == 0:
+# https://bugs.gentoo.org/828844
+raise OSError(errno.EOPNOTSUPP, os.strerror(errno.EOPNOTSUPP))
+offset += written
+count -= written
+
+
+def _do_sendfile(src, dst, offset, count):
+os.lseek(dst, offset, os.SEEK_SET)
+while count > 0:
+# count must fit in ssize_t
+c = min(count, sys.maxsize)
+written = os.sendfile(dst, src, offset, c)
+offset += written
+count -= written
+
+
+def _fastcopy(src, dst):
+with (
+open(src, "rb", buffering=0) as srcf,
+open(dst, "wb", buffering=0) as dstf,
+):
+srcfd = srcf.fileno()
+dstfd = dstf.fileno()
+
+if platform.system() == "Linux":
+try:
+fcntl.ioctl(dstfd, FICLONE, srcfd)
+return
+except OSError:
+pass
+
+try_cfr = hasattr(os, "copy_file_range")
+
+for offset, count in _get_chunks(srcfd):
+if count == 0:
+os.ftruncate(dstfd, offset)
+else:
+if try_cfr:
+try:
+_do_copy_file_range(srcfd, dstfd, offset, count)
+continue
+except OSError as e:
+try_cfr = False
+if e.errno not in (errno.EXDEV, errno.ENOSYS, 
errno.EOPNOTSUPP):
+logger.warning(
+"_do_copy_file_range failed unexpectedly",
+exc_info=sys.exc_info(),
+)
+try:
+_do_sendfile(srcfd, dstfd, offset, count)
+except OSError:
+logger.warning(
+"_do_sendfile failed unexpectedly", 
exc_info=sys.exc_info()
+)
+raise
+
+
+def copyfile(src, dst):
+"""
+Copy the contents (no metadata) of the file named src to a file
+named dst.
+
+If possible, 

[gentoo-commits] proj/portage:master commit in: lib/portage/tests/util/file_copy/

2024-03-15 Thread Mike Gilbert
commit: 0cc3f9e269b26173c2f81d731c5fe208b758270b
Author: Mike Gilbert  gentoo  org>
AuthorDate: Sun Mar  3 02:28:42 2024 +
Commit: Mike Gilbert  gentoo  org>
CommitDate: Fri Mar 15 20:05:34 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=0cc3f9e2

Improve testCopyFileSparse

Actually create sparse blocks at the start and end.
Check file size before/after copying.
Ensure sparse output when _fastcopy succeeds.

Signed-off-by: Mike Gilbert  gentoo.org>

 lib/portage/tests/util/file_copy/test_copyfile.py | 35 ---
 1 file changed, 24 insertions(+), 11 deletions(-)

diff --git a/lib/portage/tests/util/file_copy/test_copyfile.py 
b/lib/portage/tests/util/file_copy/test_copyfile.py
index e91a47bed8..e114e6ae35 100644
--- a/lib/portage/tests/util/file_copy/test_copyfile.py
+++ b/lib/portage/tests/util/file_copy/test_copyfile.py
@@ -3,13 +3,14 @@
 
 import shutil
 import tempfile
+from unittest.mock import patch
 
 import pytest
 
 from portage import os
 from portage.tests import TestCase
 from portage.checksum import perform_md5
-from portage.util.file_copy import copyfile
+from portage.util.file_copy import copyfile, _fastcopy
 
 
 class CopyFileTestCase(TestCase):
@@ -42,25 +43,37 @@ class CopyFileSparseTestCase(TestCase):
 # files too big, in case the filesystem doesn't support
 # sparse files.
 with open(src_path, "wb") as f:
+f.seek(2**16, os.SEEK_SET)
 f.write(content)
-f.seek(2**17, 1)
-f.write(content)
-f.seek(2**18, 1)
+f.seek(2**17, os.SEEK_SET)
 f.write(content)
 # Test that sparse blocks are handled correctly at
-# the end of the file (involves seek and truncate).
-f.seek(2**17, 1)
+# the end of the file.
+f.truncate(2**18)
 
-copyfile(src_path, dest_path)
+fastcopy_success = False
+
+def mock_fastcopy(src, dst):
+nonlocal fastcopy_success
+_fastcopy(src, dst)
+fastcopy_success = True
+
+with patch("portage.util.file_copy._fastcopy", new=mock_fastcopy):
+copyfile(src_path, dest_path)
 
 self.assertEqual(perform_md5(src_path), perform_md5(dest_path))
 
-# This last part of the test is expected to fail when sparse
-# copy is not implemented, so mark it xfail:
-pytest.xfail(reason="sparse copy is not implemented")
+src_stat = os.stat(src_path)
+dest_stat = os.stat(dest_path)
+
+self.assertEqual(src_stat.st_size, dest_stat.st_size)
 
 # If sparse blocks were preserved, then both files should
 # consume the same number of blocks.
-self.assertEqual(os.stat(src_path).st_blocks, 
os.stat(dest_path).st_blocks)
+# This is expected to fail when sparse copy is not implemented.
+if src_stat.st_blocks != dest_stat.st_blocks:
+if fastcopy_success:
+pytest.fail(reason="sparse copy failed with _fastcopy")
+pytest.xfail(reason="sparse copy is not implemented")
 finally:
 shutil.rmtree(tempdir)



[gentoo-commits] proj/portage:master commit in: lib/portage/util/

2024-03-07 Thread Mike Gilbert
commit: b7e89f866a9a1d73ab72670d74e2292b05893849
Author: Mike Gilbert  gentoo  org>
AuthorDate: Wed Mar  6 04:01:27 2024 +
Commit: Mike Gilbert  gentoo  org>
CommitDate: Wed Mar  6 18:19:31 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=b7e89f86

util: set a timeout for urlopen calls

A hung urlopen call can cause emerge to produce no output when fetching
binhost data.

Bug: https://bugs.gentoo.org/926221
Signed-off-by: Mike Gilbert  gentoo.org>

 lib/portage/util/_urlopen.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/portage/util/_urlopen.py b/lib/portage/util/_urlopen.py
index 22f0e08df0..d451a94a89 100644
--- a/lib/portage/util/_urlopen.py
+++ b/lib/portage/util/_urlopen.py
@@ -26,10 +26,10 @@ def have_pep_476():
 return hasattr(__import__("ssl"), "_create_unverified_context")
 
 
-def urlopen(url, if_modified_since=None, headers={}, proxies=None):
+def urlopen(url, timeout=10, if_modified_since=None, headers={}, proxies=None):
 parse_result = urllib_parse.urlparse(url)
 if parse_result.scheme not in ("http", "https"):
-return _urlopen(url)
+return _urlopen(url, timeout=timeout)
 
 netloc = parse_result.netloc.rpartition("@")[-1]
 url = urllib_parse.urlunparse(
@@ -59,7 +59,7 @@ def urlopen(url, if_modified_since=None, headers={}, 
proxies=None):
 handlers.append(urllib_request.ProxyHandler(proxies))
 opener = urllib_request.build_opener(*handlers)
 
-hdl = opener.open(request)
+hdl = opener.open(request, timeout=timeout)
 if hdl.headers.get("last-modified", ""):
 try:
 add_header = hdl.headers.add_header



[gentoo-commits] proj/portage:master commit in: lib/portage/util/_eventloop/, lib/portage/util/, lib/portage/tests/util/

2024-03-03 Thread Zac Medico
commit: 11c65496bd951c3b7778a3c1ea240e347b1f4c38
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Mar  3 21:06:13 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Mar  3 21:10:49 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=11c65496

socks5: Use run_coroutine_exitfuncs()

Since commit c3ebdbb42e72 the atexit_register(proxy.stop) call in
the get_socks5_proxy function can accept a coroutine function to
execute in run_coroutine_exitfuncs(), so convert the ProxyManager
stop method to a coroutine and use run_coroutine_exitfuncs().

Bug: https://bugs.gentoo.org/925240
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/util/test_socks5.py |  5 ++-
 lib/portage/util/_eventloop/asyncio_event_loop.py | 12 ---
 lib/portage/util/socks5.py| 39 +++
 3 files changed, 7 insertions(+), 49 deletions(-)

diff --git a/lib/portage/tests/util/test_socks5.py 
b/lib/portage/tests/util/test_socks5.py
index 4a6d08169d..a8cd0c46c4 100644
--- a/lib/portage/tests/util/test_socks5.py
+++ b/lib/portage/tests/util/test_socks5.py
@@ -216,10 +216,9 @@ class Socks5ServerTestCase(TestCase):
 self.assertEqual(result, content)
 finally:
 try:
-# Also run_exitfuncs to test atexit hook cleanup.
-await socks5.proxy.stop()
+# Also run_coroutine_exitfuncs to test atexit hook cleanup.
 self.assertNotEqual(portage.process._exithandlers, [])
-portage.process.run_exitfuncs()
+await portage.process.run_coroutine_exitfuncs()
 self.assertEqual(portage.process._exithandlers, [])
 finally:
 portage.process._exithandlers = previous_exithandlers

diff --git a/lib/portage/util/_eventloop/asyncio_event_loop.py 
b/lib/portage/util/_eventloop/asyncio_event_loop.py
index a598b1b516..821cc7f102 100644
--- a/lib/portage/util/_eventloop/asyncio_event_loop.py
+++ b/lib/portage/util/_eventloop/asyncio_event_loop.py
@@ -15,7 +15,6 @@ except ImportError:
 PidfdChildWatcher = None
 
 import portage
-from portage.util import socks5
 
 
 class AsyncioEventLoop(_AbstractEventLoop):
@@ -75,17 +74,6 @@ class AsyncioEventLoop(_AbstractEventLoop):
 self._closing = False
 
 async def _close_main(self):
-# Even though this has an exit hook, invoke it here so that
-# we can properly wait for it and avoid messages like this:
-# [ERROR] Task was destroyed but it is pending!
-if socks5.proxy.is_running():
-# TODO: Convert socks5.proxy.stop() to a regular coroutine
-# function so that it doesn't need to be wrapped like this.
-async def stop_socks5_proxy():
-await socks5.proxy.stop()
-
-portage.process.atexit_register(stop_socks5_proxy)
-
 await portage.process.run_coroutine_exitfuncs()
 portage.process.run_exitfuncs()
 

diff --git a/lib/portage/util/socks5.py b/lib/portage/util/socks5.py
index f8fcdf9fca..c32ba77674 100644
--- a/lib/portage/util/socks5.py
+++ b/lib/portage/util/socks5.py
@@ -6,15 +6,8 @@ import asyncio
 import errno
 import os
 import socket
-from typing import Union
 
 import portage
-
-portage.proxy.lazyimport.lazyimport(
-globals(),
-"portage.util._eventloop.global_event_loop:global_event_loop",
-)
-
 import portage.data
 from portage import _python_interpreter
 from portage.data import portage_gid, portage_uid, userpriv_groups
@@ -65,41 +58,19 @@ class ProxyManager:
 **spawn_kwargs,
 )
 
-def stop(self) -> Union[None, asyncio.Future]:
+async def stop(self):
 """
-Stop the SOCKSv5 server.
-
-If there is a running asyncio event loop then asyncio.Future is
-returned which should be used to wait for the server process
-to exit.
+Stop the SOCKSv5 server. This method is a coroutine.
 """
-future = None
-try:
-loop = asyncio.get_running_loop()
-except RuntimeError:
-loop = None
 if self._proc is not None:
 self._proc.terminate()
-if loop is None:
-# In this case spawn internals would have used
-# portage's global loop when attaching a waiter to
-# self._proc, so we are obligated to use that.
-global_event_loop().run_until_complete(self._proc.wait())
-else:
-if self._proc_waiter is None:
-self._proc_waiter = asyncio.ensure_future(
-self._proc.wait(), loop=loop
-)
-future = asyncio.shield(self._proc_waiter)
-
-if loop is not None and future is None:
-future = loop.create_future()
-future.set_result(None)
+if self._proc_waiter is None:
+self._proc_waiter 

[gentoo-commits] proj/portage:master commit in: lib/portage/tests/ebuild/, lib/portage/package/ebuild/, ...

2024-03-03 Thread Zac Medico
commit: 6ce2be8d454f95c508d9f547d13487f9de863bdd
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Mar  3 19:53:11 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Mar  3 19:53:11 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=6ce2be8d

_validate_deps: Discard configdict["pkg"]["USE"]

Since configdict["pkg"]["USE"] may contain package.use settings
from config.setcpv, it is inappropriate to use here (bug 675748),
so discard it. This is only an issue because configdict["pkg"] is
a sub-optimal place to extract metadata from. This issue does not
necessarily indicate a flaw in the Package constructor, since
passing in precalculated USE can be valid for things like
autounmask USE changes.

Bug: https://bugs.gentoo.org/675748
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/package/ebuild/doebuild.py | 12 
 lib/portage/tests/ebuild/test_doebuild_fd_pipes.py | 19 ++-
 lib/portage/tests/resolver/ResolverPlayground.py   |  1 +
 3 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/lib/portage/package/ebuild/doebuild.py 
b/lib/portage/package/ebuild/doebuild.py
index 942fa90101..6691db4e97 100644
--- a/lib/portage/package/ebuild/doebuild.py
+++ b/lib/portage/package/ebuild/doebuild.py
@@ -1813,6 +1813,14 @@ def _validate_deps(mysettings, myroot, mydo, mydbapi):
 invalid_dep_exempt_phases = {"clean", "cleanrm", "help", "prerm", "postrm"}
 all_keys = set(Package.metadata_keys)
 all_keys.add("SRC_URI")
+# Since configdict["pkg"]["USE"] may contain package.use settings
+# from config.setcpv, it is inappropriate to use here (bug 675748),
+# so discard it. This is only an issue because configdict["pkg"] is
+# a sub-optimal place to extract metadata from. This issue does not
+# necessarily indicate a flaw in the Package constructor, since
+# passing in precalculated USE can be valid for things like
+# autounmask USE changes.
+all_keys.discard("USE")
 all_keys = tuple(all_keys)
 metadata = mysettings.configdict["pkg"]
 if all(k in metadata for k in ("PORTAGE_REPO_NAME", "SRC_URI")):
@@ -1838,6 +1846,10 @@ def _validate_deps(mysettings, myroot, mydo, mydbapi):
 
 root_config = RootConfig(mysettings, {"porttree": FakeTree(mydbapi)}, None)
 
+# A USE calculation from setcpv should always be available here because
+# mysettings.mycpv is not None, so use it to prevent redundant setcpv 
calls.
+metadata["USE"] = mysettings["PORTAGE_USE"]
+
 pkg = Package(
 built=False,
 cpv=mysettings.mycpv,

diff --git a/lib/portage/tests/ebuild/test_doebuild_fd_pipes.py 
b/lib/portage/tests/ebuild/test_doebuild_fd_pipes.py
index b38605bb90..445fcf6c4e 100644
--- a/lib/portage/tests/ebuild/test_doebuild_fd_pipes.py
+++ b/lib/portage/tests/ebuild/test_doebuild_fd_pipes.py
@@ -51,10 +51,23 @@ class DoebuildFdPipesTestCase(TestCase):
 ebuilds = {
 "app-misct/foo-1": {
 "EAPI": "8",
+"IUSE": "+foo +bar",
+"REQUIRED_USE": "|| ( foo bar )",
 "MISC_CONTENT": ebuild_body,
 }
 }
 
+# Populate configdict["pkg"]["USE"] with something arbitrary in order
+# to try and trigger bug 675748 in doebuild _validate_deps.
+arbitrary_package_use = "baz"
+
+user_config = {
+# In order to trigger bug 675748, package.env must be non-empty,
+# but the referenced env file can be empty.
+"package.env": (f"app-misct/foo {os.devnull}",),
+"package.use": (f"app-misct/foo {arbitrary_package_use}",),
+}
+
 # Override things that may be unavailable, or may have portability
 # issues when running tests in exotic environments.
 #   prepstrip - bug #447810 (bash read builtin EINTR problem)
@@ -63,7 +76,7 @@ class DoebuildFdPipesTestCase(TestCase):
 self.assertEqual(true_binary is None, False, "true command not found")
 
 dev_null = open(os.devnull, "wb")
-playground = ResolverPlayground(ebuilds=ebuilds)
+playground = ResolverPlayground(ebuilds=ebuilds, 
user_config=user_config)
 try:
 QueryCommand._db = playground.trees
 root_config = playground.trees[playground.eroot]["root_config"]
@@ -106,6 +119,10 @@ class DoebuildFdPipesTestCase(TestCase):
 )
 settings.setcpv(pkg)
 
+# Demonstrate that settings.configdict["pkg"]["USE"] contains our 
arbitrary
+# package.use setting in order to trigger bug 675748.
+self.assertEqual(settings.configdict["pkg"]["USE"], 
arbitrary_package_use)
+
 # Try to trigger the config.environ() split_LC_ALL assertion for 
bug 925863.
 settings["LC_ALL"] = "C"
 

diff --git a/lib/portage/tests/resolver/ResolverPlayground.py 
b/lib/portage/tests/resolver/ResolverPlayground.py
index c0455415a1..f52a98f8db 

[gentoo-commits] proj/portage:master commit in: lib/portage/elog/, lib/portage/, lib/portage/util/_eventloop/

2024-03-02 Thread Zac Medico
commit: c3ebdbb42e72335ca65335c855a82b99537c7606
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Mar  3 06:30:50 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Mar  3 06:30:50 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=c3ebdbb4

elog/mod_custom: Spawn processes in background

Since elog_process is typically called while the event loop is
running, hold references to spawned processes and wait for them
asynchronously, ultimately waiting for them if necessary when
the AsyncioEventLoop _close_main method calls _async_finalize
via portage.process.run_coroutine_exitfuncs().

ConfigProtectTestCase is useful for exercising this code, and
this little make.globals patch can be used to test failure
during finalize with this error message:

!!! PORTAGE_ELOG_COMMAND failed with exitcode 1

--- a/cnf/make.globals
+++ b/cnf/make.globals
@@ -144 +144,2 @@ PORTAGE_ELOG_CLASSES="log warn error"
-PORTAGE_ELOG_SYSTEM="save_summary:log,warn,error,qa echo"
+PORTAGE_ELOG_SYSTEM="save_summary:log,warn,error,qa echo custom"
+PORTAGE_ELOG_COMMAND="/bin/false"

Bug: https://bugs.gentoo.org/925907
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/elog/mod_custom.py| 73 +--
 lib/portage/process.py| 29 +
 lib/portage/util/_eventloop/asyncio_event_loop.py |  8 ++-
 3 files changed, 105 insertions(+), 5 deletions(-)

diff --git a/lib/portage/elog/mod_custom.py b/lib/portage/elog/mod_custom.py
index e0ae77e100..a3e199bcb7 100644
--- a/lib/portage/elog/mod_custom.py
+++ b/lib/portage/elog/mod_custom.py
@@ -1,10 +1,33 @@
 # elog/mod_custom.py - elog dispatch module
-# Copyright 2006-2020 Gentoo Authors
+# Copyright 2006-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
+import types
+
+import portage
 import portage.elog.mod_save
 import portage.exception
 import portage.process
+from portage.util.futures import asyncio
+
+# Since elog_process is typically called while the event loop is
+# running, hold references to spawned processes and wait for them
+# asynchronously, ultimately waiting for them if necessary when
+# the AsyncioEventLoop _close_main method calls _async_finalize
+# via portage.process.run_coroutine_exitfuncs().
+_proc_refs = None
+
+
+def _get_procs() -> list[tuple[portage.process.MultiprocessingProcess, 
asyncio.Future]]:
+"""
+Return list of (proc, asyncio.ensure_future(proc.wait())) which is not
+inherited from the parent after fork.
+"""
+global _proc_refs
+if _proc_refs is None or _proc_refs.pid != portage.getpid():
+_proc_refs = types.SimpleNamespace(pid=portage.getpid(), procs=[])
+portage.process.atexit_register(_async_finalize)
+return _proc_refs.procs
 
 
 def process(mysettings, key, logentries, fulltext):
@@ -18,8 +41,50 @@ def process(mysettings, key, logentries, fulltext):
 mylogcmd = mysettings["PORTAGE_ELOG_COMMAND"]
 mylogcmd = mylogcmd.replace("${LOGFILE}", elogfilename)
 mylogcmd = mylogcmd.replace("${PACKAGE}", key)
-retval = portage.process.spawn_bash(mylogcmd)
-if retval != 0:
+loop = asyncio.get_event_loop()
+proc = portage.process.spawn_bash(mylogcmd, returnproc=True)
+procs = _get_procs()
+procs.append((proc, asyncio.ensure_future(proc.wait(), loop=loop)))
+for index, (proc, waiter) in reversed(list(enumerate(procs))):
+if not waiter.done():
+continue
+del procs[index]
+if waiter.result() != 0:
+raise portage.exception.PortageException(
+f"!!! PORTAGE_ELOG_COMMAND failed with exitcode 
{waiter.result()}"
+)
+
+
+async def _async_finalize():
+"""
+Async finalize is preferred, since we can wait for process exit status.
+"""
+procs = _get_procs()
+while procs:
+proc, waiter = procs.pop()
+if (await waiter) != 0:
+raise portage.exception.PortageException(
+f"!!! PORTAGE_ELOG_COMMAND failed with exitcode 
{waiter.result()}"
+)
+
+
+def finalize():
+"""
+NOTE: This raises PortageException if there are any processes
+still running, so it's better to use _async_finalize instead
+(invoked via portage.process.run_coroutine_exitfuncs() in
+the AsyncioEventLoop _close_main method).
+"""
+procs = _get_procs()
+while procs:
+proc, waiter = procs.pop()
+if not waiter.done():
+waiter.cancel()
+proc.terminate()
+raise portage.exception.PortageException(
+f"!!! PORTAGE_ELOG_COMMAND was killed after it was found 
running in the background (pid {proc.pid})"
+)
+elif waiter.result() != 0:
 raise portage.exception.PortageException(
-"!!! PORTAGE_ELOG_COMMAND failed with exitcode %d" % retval
+

[gentoo-commits] proj/portage:master commit in: lib/portage/dbapi/

2024-03-02 Thread Zac Medico
commit: 62ee9bf8c680b2a18713da5bd453e3a771257409
Author: Zac Medico  gentoo  org>
AuthorDate: Sat Mar  2 22:48:54 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sat Mar  2 22:50:54 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=62ee9bf8

binarytree._populate_remote: Fix UnboundLocalError for binpkg-request-signature

If an InvalidBinaryPackageFormat exception was raised from
get_binpkg_format for binpkg-request-signature then it
triggered an UnboundLocalError here.

Fixes: 445f10f4214c ("Use binpkg extensions and header to get format")
Bug: https://bugs.gentoo.org/926048
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/dbapi/bintree.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/portage/dbapi/bintree.py b/lib/portage/dbapi/bintree.py
index f4251b47d6..4ba1407cda 100644
--- a/lib/portage/dbapi/bintree.py
+++ b/lib/portage/dbapi/bintree.py
@@ -1624,7 +1624,7 @@ class binarytree:
 binpkg_format = get_binpkg_format(
 d.get("PATH"), remote=True
 )
-except InvalidBinaryPackageFormat:
+except InvalidBinaryPackageFormat as e:
 writemsg(
 colorize(
 "WARN",



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/ebuild/, lib/portage/package/ebuild/

2024-03-01 Thread Zac Medico
commit: fe510e099bc9a8055c3ee50fced47fc3dc7ba166
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Mar  1 17:09:56 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Mar  1 18:08:51 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=fe510e09

doebuild: Call _setup_locale

Call _setup_locale in order to prevent an AssertionError from
config.environ() for the config phase (or any other phase for
that matter).

For returnproc or returnpid assume that the event loop is running
so we can't run the event loop to call _setup_locale in this case
and we have to assume the caller took care of it (otherwise
config.environ() will raise AssertionError).

Update DoebuildFdPipesTestCase to use EAPI 8 and test the
pkg_config function with an ebuild located in /var/db/pkg just
like emerge --config does. Set LC_ALL=C just before doebuild
calls in order to try and trigger the config.environ()
split_LC_ALL assertion.

Bug: https://bugs.gentoo.org/925863
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/package/ebuild/doebuild.py |  8 +++
 lib/portage/tests/ebuild/test_doebuild_fd_pipes.py | 77 --
 2 files changed, 52 insertions(+), 33 deletions(-)

diff --git a/lib/portage/package/ebuild/doebuild.py 
b/lib/portage/package/ebuild/doebuild.py
index bc51fdff2d..942fa90101 100644
--- a/lib/portage/package/ebuild/doebuild.py
+++ b/lib/portage/package/ebuild/doebuild.py
@@ -43,6 +43,7 @@ portage.proxy.lazyimport.lazyimport(
 "portage.util._async.SchedulerInterface:SchedulerInterface",
 "portage.util._eventloop.global_event_loop:global_event_loop",
 "portage.util.ExtractKernelVersion:ExtractKernelVersion",
+"_emerge.EbuildPhase:_setup_locale",
 )
 
 from portage import (
@@ -1034,6 +1035,13 @@ def doebuild(
 myebuild, mydo, myroot, mysettings, debug, use_cache, mydbapi
 )
 
+# For returnproc or returnpid assume that the event loop is running
+# so we can't run the event loop to call _setup_locale in this case
+# and we have to assume the caller took care of it (otherwise
+# config.environ() will raise AssertionError).
+if not (returnproc or returnpid):
+asyncio.run(_setup_locale(mysettings))
+
 if mydo in clean_phases:
 builddir_lock = None
 if not returnpid and "PORTAGE_BUILDDIR_LOCKED" not in mysettings:

diff --git a/lib/portage/tests/ebuild/test_doebuild_fd_pipes.py 
b/lib/portage/tests/ebuild/test_doebuild_fd_pipes.py
index 678486ed16..b38605bb90 100644
--- a/lib/portage/tests/ebuild/test_doebuild_fd_pipes.py
+++ b/lib/portage/tests/ebuild/test_doebuild_fd_pipes.py
@@ -1,4 +1,4 @@
-# Copyright 2013-2023 Gentoo Authors
+# Copyright 2013-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import multiprocessing
@@ -27,20 +27,22 @@ class DoebuildFdPipesTestCase(TestCase):
 
 output_fd = self.output_fd
 ebuild_body = ["S=${WORKDIR}"]
-for phase_func in (
-"pkg_info",
-"pkg_nofetch",
-"pkg_pretend",
-"pkg_setup",
-"src_unpack",
-"src_prepare",
-"src_configure",
-"src_compile",
-"src_test",
-"src_install",
+for phase_func, default in (
+("pkg_info", False),
+("pkg_nofetch", False),
+("pkg_pretend", False),
+("pkg_setup", False),
+("pkg_config", False),
+("src_unpack", False),
+("src_prepare", True),
+("src_configure", False),
+("src_compile", False),
+("src_test", False),
+("src_install", False),
 ):
 ebuild_body.append(
-("%s() { echo ${EBUILD_PHASE}" " 1>&%s; }") % (phase_func, 
output_fd)
+("%s() { %secho ${EBUILD_PHASE}" " 1>&%s; }")
+% (phase_func, "default; " if default else "", output_fd)
 )
 
 ebuild_body.append("")
@@ -48,7 +50,7 @@ class DoebuildFdPipesTestCase(TestCase):
 
 ebuilds = {
 "app-misct/foo-1": {
-"EAPI": "5",
+"EAPI": "8",
 "MISC_CONTENT": ebuild_body,
 }
 }
@@ -103,24 +105,33 @@ class DoebuildFdPipesTestCase(TestCase):
 type_name="ebuild",
 )
 settings.setcpv(pkg)
-ebuildpath = portdb.findname(cpv)
-self.assertNotEqual(ebuildpath, None)
-
-for phase in (
-"info",
-"nofetch",
-"pretend",
-"setup",
-"unpack",
-"prepare",
-"configure",
-"compile",
-"test",
-"install",
-"qmerge",
-"clean",
-"merge",
+
+# Try to trigger the 

[gentoo-commits] proj/portage:master commit in: lib/portage/util/

2024-03-01 Thread Mike Gilbert
commit: d8089d1af39c80e1edfb1669ae92fef7ab30e08a
Author: Mike Gilbert  gentoo  org>
AuthorDate: Fri Mar  1 15:49:26 2024 +
Commit: Mike Gilbert  gentoo  org>
CommitDate: Fri Mar  1 16:19:55 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=d8089d1a

Improve whitespace handling when parsing /proc/self/mountinfo

Only break lines on "\n" (line feed).
Only split lines on " " (space).

Bug: https://bugs.gentoo.org/925888
Signed-off-by: Mike Gilbert  gentoo.org>

 lib/portage/util/writeable_check.py | 7 ---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/lib/portage/util/writeable_check.py 
b/lib/portage/util/writeable_check.py
index 3427315cf7..ad1d9edff0 100644
--- a/lib/portage/util/writeable_check.py
+++ b/lib/portage/util/writeable_check.py
@@ -47,6 +47,7 @@ def linux_ro_checker(dir_list):
 "/proc/self/mountinfo",
 encoding=_encodings["content"],
 errors="replace",
+newline="\n",
 ) as f:
 for line in f:
 # we're interested in dir and both attr fields which always
@@ -58,7 +59,7 @@ def linux_ro_checker(dir_list):
 # to the left of the ' - ', after the attr's, so split it there
 mount = line.split(" - ", 1)
 try:
-_dir, attr1 = mount[0].split()[4:6]
+_dir, attr1 = mount[0].split(" ")[4:6]
 except ValueError:
 # If it raises ValueError we can simply ignore the line.
 invalids.append(line)
@@ -68,10 +69,10 @@ def linux_ro_checker(dir_list):
 # for example: 16 1 0:16 / /root rw,noatime - lxfs  rw
 if len(mount) > 1:
 try:
-attr2 = mount[1].split()[2]
+attr2 = mount[1].split(" ")[2]
 except IndexError:
 try:
-attr2 = mount[1].split()[1]
+attr2 = mount[1].split(" ")[1]
 except IndexError:
 invalids.append(line)
 continue



[gentoo-commits] proj/portage:master commit in: lib/portage/util/, src/

2024-03-01 Thread Mike Gilbert
commit: a860484b6e8c8d107255f2105796ae7f58812e9f
Author: Mike Gilbert  gentoo  org>
AuthorDate: Thu Feb 29 19:38:20 2024 +
Commit: Mike Gilbert  gentoo  org>
CommitDate: Thu Feb 29 19:38:20 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=a860484b

Drop portage.util.libc extension module

This was originally added as a workaround for musl, where
ctypes.util.find_library fails.

Instead, we now try to load "libc.so" as a fallback and can just rely on
ctypes to call tolower() and toupper().

Signed-off-by: Mike Gilbert  gentoo.org>

 lib/portage/util/locale.py |  9 +++
 src/meson.build|  9 ---
 src/portage_util_libc.c| 67 --
 3 files changed, 3 insertions(+), 82 deletions(-)

diff --git a/lib/portage/util/locale.py b/lib/portage/util/locale.py
index d0edeb4afe..f45d761760 100644
--- a/lib/portage/util/locale.py
+++ b/lib/portage/util/locale.py
@@ -43,12 +43,9 @@ def _check_locale(silent):
 """
 The inner locale check function.
 """
-try:
-from portage.util import libc
-except ImportError:
-(libc, _) = load_libc()
-if libc is None:
-return None
+(libc, _) = load_libc()
+if libc is None:
+return None
 
 lc = list(range(ord("a"), ord("z") + 1))
 uc = list(range(ord("A"), ord("Z") + 1))

diff --git a/src/meson.build b/src/meson.build
index cbc7aa611c..6a36724ceb 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -2,14 +2,6 @@
 # and for development. Meson does not allow you to build in-place and Python
 # cannot create a single namespace from two identically-named paths.
 
-libc_ext = py.extension_module(
-'libc',
-'portage_util_libc.c',
-dependencies : py.dependency(),
-subdir : 'portage' / 'util',
-install : true
-)
-
 whirlpool_ext = py.extension_module(
 '_whirlpool',
 'portage_util__whirlpool.c',
@@ -21,7 +13,6 @@ whirlpool_ext = py.extension_module(
 run_command(
 [
 'ln', '-srnf',
-libc_ext.full_path(),
 whirlpool_ext.full_path(),
 meson.project_source_root() / 'lib' / 'portage' / 'util/'
 ],

diff --git a/src/portage_util_libc.c b/src/portage_util_libc.c
deleted file mode 100644
index 12b2440c72..00
--- a/src/portage_util_libc.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/* Copyright 2005-2020 Gentoo Authors
- * Distributed under the terms of the GNU General Public License v2
- */
-
-#include 
-#include 
-#include 
-
-static PyObject * _libc_tolower(PyObject *, PyObject *);
-static PyObject * _libc_toupper(PyObject *, PyObject *);
-
-static PyMethodDef LibcMethods[] = {
-   {
-   .ml_name = "tolower",
-   .ml_meth = _libc_tolower,
-   .ml_flags = METH_VARARGS,
-   .ml_doc = "Convert to lower case using system locale."
-
-   },
-   {
-   .ml_name = "toupper",
-   .ml_meth = _libc_toupper,
-   .ml_flags = METH_VARARGS,
-   .ml_doc = "Convert to upper case using system locale."
-   },
-   {NULL, NULL, 0, NULL}
-};
-
-static struct PyModuleDef moduledef = {
-   PyModuleDef_HEAD_INIT,
-   .m_name = "libc",
-   .m_doc = "Module for converting case using the system locale",
-   .m_size = -1,
-   .m_methods = LibcMethods,
-};
-
-PyMODINIT_FUNC
-PyInit_libc(void)
-{
-   PyObject *m;
-   m = PyModule_Create();
-   return m;
-}
-
-
-static PyObject *
-_libc_tolower(PyObject *self, PyObject *args)
-{
-   int c;
-
-   if (!PyArg_ParseTuple(args, "i", ))
-   return NULL;
-
-   return Py_BuildValue("i", tolower(c));
-}
-
-
-static PyObject *
-_libc_toupper(PyObject *self, PyObject *args)
-{
-   int c;
-
-   if (!PyArg_ParseTuple(args, "i", ))
-   return NULL;
-
-   return Py_BuildValue("i", toupper(c));
-}



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/util/futures/, lib/portage/util/futures/, ...

2024-02-28 Thread Zac Medico
commit: b2d8226af4589d95f44d20d441056f645a523039
Author: Zac Medico  gentoo  org>
AuthorDate: Thu Feb 29 04:26:02 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Thu Feb 29 04:37:21 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=b2d8226a

Delete compat_coroutine module

The compat_coroutine module has been unused since the migration
to PEP 492 async and await syntax in 2021, which began in commit
b3b9acc13c43 and was completed in commit bcda30d0a6fa.

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/util/futures/meson.build |   1 -
 .../tests/util/futures/test_compat_coroutine.py| 210 -
 lib/portage/util/futures/_asyncio/__init__.py  |  15 +-
 lib/portage/util/futures/compat_coroutine.py   | 142 --
 lib/portage/util/futures/meson.build   |   1 -
 5 files changed, 1 insertion(+), 368 deletions(-)

diff --git a/lib/portage/tests/util/futures/meson.build 
b/lib/portage/tests/util/futures/meson.build
index 877acc27cd..cb78314844 100644
--- a/lib/portage/tests/util/futures/meson.build
+++ b/lib/portage/tests/util/futures/meson.build
@@ -1,6 +1,5 @@
 py.install_sources(
 [
-'test_compat_coroutine.py',
 'test_done_callback.py',
 'test_done_callback_after_exit.py',
 'test_iter_completed.py',

diff --git a/lib/portage/tests/util/futures/test_compat_coroutine.py 
b/lib/portage/tests/util/futures/test_compat_coroutine.py
deleted file mode 100644
index b25708886c..00
--- a/lib/portage/tests/util/futures/test_compat_coroutine.py
+++ /dev/null
@@ -1,210 +0,0 @@
-# Copyright 2018 Gentoo Foundation
-# Distributed under the terms of the GNU General Public License v2
-
-from portage.util.futures import asyncio
-from portage.util.futures.compat_coroutine import (
-coroutine,
-coroutine_return,
-)
-from portage.util.futures._sync_decorator import _sync_decorator, _sync_methods
-from portage.tests import TestCase
-
-
-class CompatCoroutineTestCase(TestCase):
-def test_returning_coroutine(self):
-@coroutine
-def returning_coroutine(loop=None):
-yield asyncio.sleep(0, loop=loop)
-coroutine_return("success")
-
-loop = asyncio.get_event_loop()
-self.assertEqual(
-"success",
-
asyncio.get_event_loop().run_until_complete(returning_coroutine(loop=loop)),
-)
-
-def test_raising_coroutine(self):
-class TestException(Exception):
-pass
-
-@coroutine
-def raising_coroutine(loop=None):
-yield asyncio.sleep(0, loop=loop)
-raise TestException("exception")
-
-loop = asyncio.get_event_loop()
-self.assertRaises(
-TestException, loop.run_until_complete, 
raising_coroutine(loop=loop)
-)
-
-def test_catching_coroutine(self):
-class TestException(Exception):
-pass
-
-@coroutine
-def catching_coroutine(loop=None):
-loop = asyncio._wrap_loop(loop)
-future = loop.create_future()
-loop.call_soon(future.set_exception, TestException("exception"))
-try:
-yield future
-except TestException:
-self.assertTrue(True)
-else:
-self.assertTrue(False)
-coroutine_return("success")
-
-loop = asyncio.get_event_loop()
-self.assertEqual(
-"success", loop.run_until_complete(catching_coroutine(loop=loop))
-)
-
-def test_cancelled_coroutine(self):
-"""
-Verify that a coroutine can handle (and reraise) asyncio.CancelledError
-in order to perform any necessary cleanup. Note that the
-asyncio.CancelledError will only be thrown in the coroutine if there's
-an opportunity (yield) before the generator raises StopIteration.
-"""
-loop = asyncio.get_event_loop()
-ready_for_exception = loop.create_future()
-exception_in_coroutine = loop.create_future()
-
-@coroutine
-def cancelled_coroutine(loop=None):
-loop = asyncio._wrap_loop(loop)
-while True:
-task = loop.create_future()
-try:
-ready_for_exception.set_result(None)
-yield task
-except BaseException as e:
-# Since python3.8, asyncio.CancelledError inherits
-# from BaseException.
-task.done() or task.cancel()
-exception_in_coroutine.set_exception(e)
-raise
-else:
-exception_in_coroutine.set_result(None)
-
-future = cancelled_coroutine(loop=loop)
-loop.run_until_complete(ready_for_exception)
-future.cancel()
-
-self.assertRaises(asyncio.CancelledError, loop.run_until_complete, 
future)
-
-

[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-28 Thread Sam James
commit: fa7fb4c5119aa077a0731e1f8892ea0791a4968b
Author: Sam James  gentoo  org>
AuthorDate: Wed Feb 28 16:00:57 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Wed Feb 28 16:01:04 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=fa7fb4c5

gpkg: placate black

Fixes: 957902f84edece635210689f46e20741e76d9dba
Signed-off-by: Sam James  gentoo.org>

 lib/portage/gpkg.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/portage/gpkg.py b/lib/portage/gpkg.py
index 5cd1f2394e..2b957d58c4 100644
--- a/lib/portage/gpkg.py
+++ b/lib/portage/gpkg.py
@@ -656,7 +656,9 @@ class tar_safe_extract:
 raise ValueError("Path traversal detected.")
 if member.isdev():
 writemsg(
-colorize("BAD", f"Danger: device file detected: 
{member.name}\n")
+colorize(
+"BAD", f"Danger: device file detected: 
{member.name}\n"
+)
 )
 raise ValueError("Device file detected.")
 if member.islnk() and (member.linkname not in self.file_list):



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-28 Thread Sam James
commit: 957902f84edece635210689f46e20741e76d9dba
Author: Sam James  gentoo  org>
AuthorDate: Wed Feb 28 15:51:15 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Wed Feb 28 15:51:15 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=957902f8

gpkg: add missing new lines to error messages

Signed-off-by: Sam James  gentoo.org>

 lib/portage/gpkg.py | 18 +-
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/lib/portage/gpkg.py b/lib/portage/gpkg.py
index edb0e43fbf..5cd1f2394e 100644
--- a/lib/portage/gpkg.py
+++ b/lib/portage/gpkg.py
@@ -382,7 +382,7 @@ class tar_stream_reader:
 try:
 if self.proc.wait() != os.EX_OK:
 if not self.killed:
-writemsg(colorize("BAD", f"GPKG external program 
failed."))
+writemsg(colorize("BAD", f"GPKG external program 
failed.\n"))
 raise CompressorOperationFailed("decompression failed")
 finally:
 self.proc.stdout.close()
@@ -418,7 +418,7 @@ class checksum_helper:
 else:
 self.uid = pwd.getpwnam(drop_user).pw_uid
 except KeyError:
-writemsg(colorize("BAD", f"!!! Failed to find user 
{drop_user}."))
+writemsg(colorize("BAD", f"!!! Failed to find user 
{drop_user}.\n"))
 raise
 
 try:
@@ -428,7 +428,7 @@ class checksum_helper:
 else:
 self.gid = grp.getgrnam(drop_group).gr_gid
 except KeyError:
-writemsg(colorize("BAD", f"!!! Failed to find group 
{drop_group}."))
+writemsg(colorize("BAD", f"!!! Failed to find group 
{drop_group}.\n"))
 raise
 else:
 self.uid = None
@@ -636,33 +636,33 @@ class tar_safe_extract:
 ):
 writemsg(
 colorize(
-"BAD", f"Danger: duplicate files detected: 
{member.name}"
+"BAD", f"Danger: duplicate files detected: 
{member.name}\n"
 )
 )
 raise ValueError("Duplicate files detected.")
 if member.name.startswith("/"):
 writemsg(
 colorize(
-"BAD", f"Danger: absolute path detected: 
{member.name}"
+"BAD", f"Danger: absolute path detected: 
{member.name}\n"
 )
 )
 raise ValueError("Absolute path detected.")
 if member.name.startswith("../") or ("/../" in member.name):
 writemsg(
 colorize(
-"BAD", f"Danger: path traversal detected: 
{member.name}"
+"BAD", f"Danger: path traversal detected: 
{member.name}\n"
 )
 )
 raise ValueError("Path traversal detected.")
 if member.isdev():
 writemsg(
-colorize("BAD", f"Danger: device file detected: 
{member.name}")
+colorize("BAD", f"Danger: device file detected: 
{member.name}\n")
 )
 raise ValueError("Device file detected.")
 if member.islnk() and (member.linkname not in self.file_list):
 writemsg(
 colorize(
-"BAD", f"Danger: hardlink escape detected: 
{member.name}"
+"BAD", f"Danger: hardlink escape detected: 
{member.name}\n"
 )
 )
 raise ValueError("Hardlink escape detected.")
@@ -995,7 +995,7 @@ class gpkg:
 image_safe.extractall(decompress_dir)
 image_tar.close()
 except Exception as ex:
-writemsg(colorize("BAD", "!!!Extract failed."))
+writemsg(colorize("BAD", "!!!Extract failed.\n"))
 raise
 finally:
 if not image_tar.closed:



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-28 Thread Sam James
commit: f070275fe05d5053c3756ebb5d0a602db8ba515d
Author: Sam James  gentoo  org>
AuthorDate: Wed Feb 28 15:48:48 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Wed Feb 28 15:49:16 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=f070275f

binpkg: add another missing newline to error message

```
Error reading binpkg 
'/var/cache/binpkgs/dev-perl/SGMLSpm/SGMLSpm-1.1-r2-7.gpkg.tar': [Errno 22] 
Invalid argument!!! Invalid binary package: 
'/var/cache/binpkgs/dev-perl/SGMLSpm/SGMLSpm-1.1-r2-7.gpkg.tar', Error reading 
binpkg '/var/cache/binpkgs/dev-perl/SGMLSpm/SGMLSpm-1.1-r2-7.gpkg.tar': [Errno 
22] Invalid argument
```

Bug: https://bugs.gentoo.org/925714
Signed-off-by: Sam James  gentoo.org>

 lib/portage/binpkg.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/portage/binpkg.py b/lib/portage/binpkg.py
index 9ecd52cf3c..a48e09bdb8 100644
--- a/lib/portage/binpkg.py
+++ b/lib/portage/binpkg.py
@@ -54,7 +54,7 @@ def get_binpkg_format(binpkg_path, check_file=False, 
remote=False):
 # We got many different exceptions here, so have to catch all of them.
 file_format = None
 writemsg(
-colorize("ERR", f"Error reading binpkg '{binpkg_path}': {err}"),
+colorize("ERR", f"Error reading binpkg '{binpkg_path}': {err}\n"),
 )
 raise InvalidBinaryPackageFormat(f"Error reading binpkg 
'{binpkg_path}': {err}")
 



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/process/

2024-02-27 Thread Zac Medico
commit: e882b1e956d50808a0143875a8ca35f2fc21f368
Author: Zac Medico  gentoo  org>
AuthorDate: Wed Feb 28 06:25:45 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb 28 06:25:45 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=e882b1e9

UnshareNetTestCase: Initialize ABILITY_TO_UNSHARE in setUp

Initialize ABILITY_TO_UNSHARE in setUp so that _unshare_validate
uses the correct PORTAGE_MULTIPROCESSING_START_METHOD setup
from super().setUp(), eliminating messages like this from CI
runs for the multiprocessing start method spawn:


/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/multiprocessing/popen_fork.py:66:
DeprecationWarning: This process (pid=2886) is multi-threaded, use of 
fork() may lead to deadlocks in the child.

The cause of these messages can be traced by patching python's
multiprocessing popen_fork.py like this:

--- /usr/lib/python3.12/multiprocessing/popen_fork.py
+++ /usr/lib/python3.12/multiprocessing/popen_fork.py
@@ -2,2 +2,3 @@
 import signal
+import traceback

@@ -62,2 +63,3 @@
 def _launch(self, process_obj):
+traceback.print_stack()
 code = 1

Fixes: 3110ec376cbc ("actions: Fix interaction between start-method and 
pytest-xdist")
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/process/test_unshare_net.py | 18 +-
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/lib/portage/tests/process/test_unshare_net.py 
b/lib/portage/tests/process/test_unshare_net.py
index dabf15585f..ad3b288ef4 100644
--- a/lib/portage/tests/process/test_unshare_net.py
+++ b/lib/portage/tests/process/test_unshare_net.py
@@ -20,19 +20,27 @@ ping -c 1 -W 1 10.0.0.1 || exit 1
 ping -c 1 -W 1 ::1 || exit 1
 ping -c 1 -W 1 fd::1 || exit 1
 """
-ABILITY_TO_UNSHARE = portage.process._unshare_validate(CLONE_NEWNET)
 
 
 class UnshareNetTestCase(TestCase):
-@pytest.mark.skipif(
-ABILITY_TO_UNSHARE != 0,
-reason=f"Unable to unshare: {errno.errorcode.get(ABILITY_TO_UNSHARE, 
'?')}",
-)
+def setUp(self):
+"""
+Initialize ABILITY_TO_UNSHARE in setUp so that _unshare_validate
+uses the correct PORTAGE_MULTIPROCESSING_START_METHOD setup
+from super().setUp().
+"""
+super().setUp()
+self.ABILITY_TO_UNSHARE = 
portage.process._unshare_validate(CLONE_NEWNET)
+
 @pytest.mark.skipif(
 portage.process.find_binary("ping") is None, reason="ping not found"
 )
 @pytest.mark.skipif(platform.system() != "Linux", reason="not Linux")
 def testUnshareNet(self):
+if self.ABILITY_TO_UNSHARE != 0:
+pytest.skip(
+f"Unable to unshare: 
{errno.errorcode.get(self.ABILITY_TO_UNSHARE, '?')}"
+)
 env = os.environ.copy()
 env["IPV6"] = "1" if portage.process._has_ipv6() else ""
 self.assertEqual(



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/dbapi/

2024-02-26 Thread Zac Medico
commit: e9bdb7342b3048ab3236bff9d94ce733bb877d8e
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Feb 27 04:02:28 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Tue Feb 27 04:03:07 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=e9bdb734

BinarytreeTestCase: Use temporary TMPDIR

Create a temporary TMPDIR which prevents test methods of this
class from leaving behind an empty /tmp/Packages file if TMPDIR
is initially unset.

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/dbapi/test_bintree.py | 22 +-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/lib/portage/tests/dbapi/test_bintree.py 
b/lib/portage/tests/dbapi/test_bintree.py
index 018f1cf9bd..91ac338a05 100644
--- a/lib/portage/tests/dbapi/test_bintree.py
+++ b/lib/portage/tests/dbapi/test_bintree.py
@@ -1,4 +1,4 @@
-# Copyright 2022 Gentoo Authors
+# Copyright 2022-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from unittest.mock import MagicMock, patch, call
@@ -13,6 +13,26 @@ from portage.const import BINREPOS_CONF_FILE
 
 
 class BinarytreeTestCase(TestCase):
+@classmethod
+def setUpClass(cls):
+"""
+Create a temporary TMPDIR which prevents test
+methods of this class from leaving behind an empty
+/tmp/Packages file if TMPDIR is initially unset.
+"""
+cls._orig_tmpdir = os.environ.get("TMPDIR")
+cls._tmpdir = tempfile.TemporaryDirectory()
+os.environ["TMPDIR"] = cls._tmpdir.name
+
+@classmethod
+def tearDownClass(cls):
+cls._tmpdir.cleanup()
+if cls._orig_tmpdir is None:
+os.environ.pop("TMPDIR", None)
+else:
+os.environ["TMPDIR"] = cls._orig_tmpdir
+del cls._orig_tmpdir, cls._tmpdir
+
 def test_required_init_params(self):
 with self.assertRaises(TypeError) as cm:
 binarytree()



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/resolver/, cnf/sets/, lib/portage/tests/sets/base/

2024-02-26 Thread Zac Medico
commit: 1d3e3843f2a51c581d344540c5c6ee266afa30d2
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb 25 22:57:43 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Mon Feb 26 23:17:55 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=1d3e3843

cnf: sets: Migrate @golang-rebuild to dev-lang/go

Bug: https://bugs.gentoo.org/919751
Signed-off-by: Zac Medico  gentoo.org>

 cnf/sets/portage.conf|  6 --
 lib/portage/tests/resolver/ResolverPlayground.py | 10 +-
 lib/portage/tests/sets/base/test_variable_set.py |  8 ++--
 3 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/cnf/sets/portage.conf b/cnf/sets/portage.conf
index 2e02f91f97..c272f98db1 100644
--- a/cnf/sets/portage.conf
+++ b/cnf/sets/portage.conf
@@ -110,12 +110,6 @@ class = portage.sets.dbapi.UnavailableBinaries
 [changed-deps]
 class = portage.sets.dbapi.ChangedDepsSet
 
-# Installed packages for which vdb *DEPEND includes dev-lang/go.
-[golang-rebuild]
-class = portage.sets.dbapi.VariableSet
-variable = BDEPEND
-includes = dev-lang/go
-
 # Installed packages for which vdb *DEPEND includes virtual/rust
 [rust-rebuild]
 class = portage.sets.dbapi.VariableSet

diff --git a/lib/portage/tests/resolver/ResolverPlayground.py 
b/lib/portage/tests/resolver/ResolverPlayground.py
index 75c86b615c..c0455415a1 100644
--- a/lib/portage/tests/resolver/ResolverPlayground.py
+++ b/lib/portage/tests/resolver/ResolverPlayground.py
@@ -28,7 +28,7 @@ from portage.exception import InvalidBinaryPackageFormat
 from portage.gpg import GPG
 
 import _emerge
-from _emerge.actions import _calc_depclean
+from _emerge.actions import _calc_depclean, expand_set_arguments
 from _emerge.Blocker import Blocker
 from _emerge.create_depgraph_params import create_depgraph_params
 from _emerge.DependencyArg import DependencyArg
@@ -747,6 +747,14 @@ class ResolverPlayground:
 self.settings, self.trees, options, params, None
 )
 
+atoms, retval = expand_set_arguments(
+atoms, action, self.trees[self.eroot]["root_config"]
+)
+if retval != os.EX_OK:
+raise AssertionError(
+f"expand_set_arguments failed with retval {retval}"
+)
+
 if params_action == "remove":
 depclean_result = _calc_depclean(
 self.settings,

diff --git a/lib/portage/tests/sets/base/test_variable_set.py 
b/lib/portage/tests/sets/base/test_variable_set.py
index 9e90ee6dd7..60c43a5b83 100644
--- a/lib/portage/tests/sets/base/test_variable_set.py
+++ b/lib/portage/tests/sets/base/test_variable_set.py
@@ -1,4 +1,4 @@
-# Copyright 2022 Gentoo Authors
+# Copyright 2022-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from portage.tests import TestCase
@@ -10,6 +10,10 @@ from portage.tests.resolver.ResolverPlayground import (
 
 class VariableSetTestCase(TestCase):
 def testVariableSetEmerge(self):
+
+# Using local set definition because @golang-rebuild migrated to 
dev-lang/go since bug 919751.
+golang_rebuild = 
"{class=portage.sets.dbapi.VariableSet,variable=BDEPEND,includes=dev-lang/go}"
+
 ebuilds = {
 "dev-go/go-pkg-1": {"BDEPEND": "dev-lang/go"},
 "www-client/firefox-1": {
@@ -21,7 +25,7 @@ class VariableSetTestCase(TestCase):
 
 test_cases = (
 ResolverPlaygroundTestCase(
-["@golang-rebuild"],
+[f"@golang-rebuild{golang_rebuild}"],
 mergelist=["dev-go/go-pkg-1"],
 success=True,
 ),



[gentoo-commits] proj/portage:master commit in: lib/portage/util/, lib/portage/, lib/_emerge/

2024-02-26 Thread Zac Medico
commit: 0ff7a3b28e0ec63d68d32e01145db8962d53774d
Author: Zac Medico  gentoo  org>
AuthorDate: Mon Feb 26 05:09:21 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Mon Feb 26 05:09:21 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=0ff7a3b2

SpawnProcess: Wait for async set_term_size

Use the SpawnProcess _unregister method to handle async
set_term_size results, avoiding possible messages like
bug 925456 triggered:

[ERROR] Task was destroyed but it is pending!

Also update _BinpkgFetcherProcess and _EbuildFetcherProcess
which inherit the _pty_ready attribute from SpawnProcess.

Fixes: f97e414ce980 ("set_term_size: Wait asynchronously if event loop is 
running")
Bug: https://bugs.gentoo.org/923750
Bug: https://bugs.gentoo.org/925456
Signed-off-by: Zac Medico  gentoo.org>

 lib/_emerge/BinpkgFetcher.py |  6 --
 lib/_emerge/EbuildFetcher.py |  6 --
 lib/_emerge/SpawnProcess.py  | 14 +-
 lib/portage/output.py| 13 +
 lib/portage/util/_pty.py | 23 ---
 5 files changed, 46 insertions(+), 16 deletions(-)

diff --git a/lib/_emerge/BinpkgFetcher.py b/lib/_emerge/BinpkgFetcher.py
index a1524dc009..587e4a57a3 100644
--- a/lib/_emerge/BinpkgFetcher.py
+++ b/lib/_emerge/BinpkgFetcher.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2023 Gentoo Authors
+# Copyright 1999-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from _emerge.AsynchronousLock import AsynchronousLock
@@ -233,7 +233,9 @@ class _BinpkgFetcherProcess(SpawnProcess):
 stdout_pipe = None
 if not self.background:
 stdout_pipe = fd_pipes.get(1)
-got_pty, master_fd, slave_fd = 
_create_pty_or_pipe(copy_term_size=stdout_pipe)
+self._pty_ready, master_fd, slave_fd = _create_pty_or_pipe(
+copy_term_size=stdout_pipe
+)
 return (master_fd, slave_fd)
 
 def sync_timestamp(self):

diff --git a/lib/_emerge/EbuildFetcher.py b/lib/_emerge/EbuildFetcher.py
index 7a45d95172..81d4b1054b 100644
--- a/lib/_emerge/EbuildFetcher.py
+++ b/lib/_emerge/EbuildFetcher.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2023 Gentoo Authors
+# Copyright 1999-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import copy
@@ -394,7 +394,9 @@ class _EbuildFetcherProcess(ForkProcess):
 stdout_pipe = None
 if not self.background:
 stdout_pipe = fd_pipes.get(1)
-got_pty, master_fd, slave_fd = 
_create_pty_or_pipe(copy_term_size=stdout_pipe)
+self._pty_ready, master_fd, slave_fd = _create_pty_or_pipe(
+copy_term_size=stdout_pipe
+)
 return (master_fd, slave_fd)
 
 def _eerror(self, lines):

diff --git a/lib/_emerge/SpawnProcess.py b/lib/_emerge/SpawnProcess.py
index b63afae01c..9fc12c42e5 100644
--- a/lib/_emerge/SpawnProcess.py
+++ b/lib/_emerge/SpawnProcess.py
@@ -46,6 +46,7 @@ class SpawnProcess(SubProcess):
 + (
 "_main_task",
 "_main_task_cancel",
+"_pty_ready",
 "_selinux_type",
 )
 )
@@ -193,6 +194,9 @@ class SpawnProcess(SubProcess):
 self._main_task.add_done_callback(self._main_exit)
 
 async def _main(self, build_logger, pipe_logger, loop=None):
+if isinstance(self._pty_ready, asyncio.Future):
+await self._pty_ready
+self._pty_ready = None
 try:
 if pipe_logger.poll() is None:
 await pipe_logger.async_wait()
@@ -238,7 +242,9 @@ class SpawnProcess(SubProcess):
 stdout_pipe = None
 if not self.background:
 stdout_pipe = fd_pipes.get(1)
-got_pty, master_fd, slave_fd = 
_create_pty_or_pipe(copy_term_size=stdout_pipe)
+self._pty_ready, master_fd, slave_fd = _create_pty_or_pipe(
+copy_term_size=stdout_pipe
+)
 return (master_fd, slave_fd)
 
 def _spawn(
@@ -258,6 +264,12 @@ class SpawnProcess(SubProcess):
 SubProcess._unregister(self)
 if self._main_task is not None:
 self._main_task.done() or self._main_task.cancel()
+if isinstance(self._pty_ready, asyncio.Future):
+(
+self._pty_ready.done()
+and (self._pty_ready.cancelled() or self._pty_ready.result() 
or True)
+) or self._pty_ready.cancel()
+self._pty_ready = None
 
 def _cancel(self):
 if self._main_task is not None:

diff --git a/lib/portage/output.py b/lib/portage/output.py
index 7d3a6278f3..4408705c45 100644
--- a/lib/portage/output.py
+++ b/lib/portage/output.py
@@ -8,6 +8,7 @@ import itertools
 import re
 import subprocess
 import sys
+from typing import Optional
 
 import portage
 
@@ -554,10 +555,16 @@ def get_term_size(fd=None):
 return (0, 0)
 
 
-def set_term_size(lines, columns, fd):
+def set_term_size(lines: int, columns: int, fd: int) -> 
Optional[asyncio.Future]:

[gentoo-commits] proj/portage:master commit in: lib/portage/dbapi/, lib/portage/util/, /, lib/portage/

2024-02-25 Thread Mike Gilbert
commit: 19e27e0415fd321c39104f7d687bcdc4f4132e24
Author: Mike Gilbert  gentoo  org>
AuthorDate: Sun Feb 25 18:10:15 2024 +
Commit: Mike Gilbert  gentoo  org>
CommitDate: Mon Feb 26 04:10:32 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=19e27e04

Add workaround for loading libc on musl

musl libc has no soname, which causes ctypes.util.find_library to fail.
As a fallback, try to load "libc.so".

Signed-off-by: Mike Gilbert  gentoo.org>

 NEWS|  7 +++
 lib/portage/dbapi/_MergeProcess.py  |  4 ++--
 lib/portage/dbapi/_SyncfsProcess.py | 14 --
 lib/portage/process.py  | 16 +---
 lib/portage/util/_ctypes.py | 15 +++
 lib/portage/util/locale.py  |  7 ++-
 6 files changed, 35 insertions(+), 28 deletions(-)

diff --git a/NEWS b/NEWS
index eb84651b53..258d800373 100644
--- a/NEWS
+++ b/NEWS
@@ -6,8 +6,15 @@ Release notes take the form of the following optional 
categories:
 * Bug fixes
 * Cleanups
 
+portage-3.0.64 (UNRELEASED)
+--
+
+Bug fixes:
+
+
 portage-3.0.63 (2024-02-25)
 --
+* ctypes: Add workaround for loading libc on musl
 
 Bug fixes:
 * emerge: Skip installed packages with emptytree in depgraph selection (bug 
#651018).

diff --git a/lib/portage/dbapi/_MergeProcess.py 
b/lib/portage/dbapi/_MergeProcess.py
index dd5ad71cf8..d9ab2b47aa 100644
--- a/lib/portage/dbapi/_MergeProcess.py
+++ b/lib/portage/dbapi/_MergeProcess.py
@@ -10,7 +10,7 @@ import fcntl
 import portage
 from portage import os, _unicode_decode
 from portage.package.ebuild._ipc.QueryCommand import QueryCommand
-from portage.util._ctypes import find_library
+from portage.util._ctypes import load_libc
 import portage.elog.messages
 from portage.util._async.ForkProcess import ForkProcess
 from portage.util import no_color
@@ -64,7 +64,7 @@ class MergeProcess(ForkProcess):
 # process, so that it's only done once rather than
 # for each child process.
 if platform.system() == "Linux" and "merge-sync" in settings.features:
-find_library("c")
+load_libc()
 
 # Inherit stdin by default, so that the pdb SIGUSR1
 # handler is usable for the subprocess.

diff --git a/lib/portage/dbapi/_SyncfsProcess.py 
b/lib/portage/dbapi/_SyncfsProcess.py
index ddc2240071..300ae53985 100644
--- a/lib/portage/dbapi/_SyncfsProcess.py
+++ b/lib/portage/dbapi/_SyncfsProcess.py
@@ -4,7 +4,7 @@
 import functools
 
 from portage import os
-from portage.util._ctypes import find_library, LoadLibrary
+from portage.util._ctypes import load_libc
 from portage.util._async.ForkProcess import ForkProcess
 
 
@@ -24,15 +24,9 @@ class SyncfsProcess(ForkProcess):
 
 @staticmethod
 def _get_syncfs():
-filename = find_library("c")
-if filename is not None:
-library = LoadLibrary(filename)
-if library is not None:
-try:
-return library.syncfs
-except AttributeError:
-pass
-
+(libc, _) = load_libc()
+if libc is not None:
+return getattr(libc, "syncfs", None)
 return None
 
 @staticmethod

diff --git a/lib/portage/process.py b/lib/portage/process.py
index cc9ed7bf78..b652e32942 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -37,7 +37,7 @@ portage.proxy.lazyimport.lazyimport(
 from portage.const import BASH_BINARY, SANDBOX_BINARY, FAKEROOT_BINARY
 from portage.exception import CommandNotFound
 from portage.proxy.objectproxy import ObjectProxy
-from portage.util._ctypes import find_library, LoadLibrary, ctypes
+from portage.util._ctypes import load_libc, LoadLibrary, ctypes
 
 try:
 from portage.util.netlink import RtNetlink
@@ -960,11 +960,9 @@ def _exec(
 have_unshare = False
 libc = None
 if unshare_net or unshare_ipc or unshare_mount or unshare_pid:
-filename = find_library("c")
-if filename is not None:
-libc = LoadLibrary(filename)
-if libc is not None:
-have_unshare = hasattr(libc, "unshare")
+(libc, _) = load_libc()
+if libc is not None:
+have_unshare = hasattr(libc, "unshare")
 
 if not have_unshare:
 # unshare() may not be supported by libc
@@ -1212,11 +1210,7 @@ class _unshare_validator:
 """
 # This ctypes library lookup caches the result for use in the
 # subprocess when the multiprocessing start method is fork.
-filename = find_library("c")
-if filename is None:
-return errno.ENOTSUP
-
-libc = LoadLibrary(filename)
+(libc, filename) = load_libc()
 if libc is None:
 return errno.ENOTSUP
 

diff --git a/lib/portage/util/_ctypes.py b/lib/portage/util/_ctypes.py
index e6d1e327cb..04e965ba92 100644
--- a/lib/portage/util/_ctypes.py
+++ b/lib/portage/util/_ctypes.py
@@ 

[gentoo-commits] proj/portage:master commit in: lib/portage/dbapi/

2024-02-25 Thread Sam James
commit: 0855821572f32e81b031a250f7491f34a2fd4078
Author: Zac Medico  gentoo  org>
AuthorDate: Sat Feb 24 23:29:29 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Sun Feb 25 08:24:55 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=08558215

dbapi: Fix TypeError when passing Exception to warnings.warn

If an Exception is passed as a message to warnings.warn then
it triggers this TypeError:

if metadata_updates:
try:
aux_update(cpv, metadata_updates)
except (InvalidBinaryPackageFormat, CorruptionKeyError) as e:
>   warnings.warn(e)
E   TypeError: expected string or bytes-like object, got 
'CorruptionKeyError'

Fixes: fb1d0a22f657 ("dbapi: KeyError tolerance during package moves")
Bug: https://bugs.gentoo.org/922935
Signed-off-by: Zac Medico  gentoo.org>
Closes: https://github.com/gentoo/portage/pull/1282
Signed-off-by: Sam James  gentoo.org>

 lib/portage/dbapi/__init__.py | 11 +++
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/lib/portage/dbapi/__init__.py b/lib/portage/dbapi/__init__.py
index 6f95b93a21..9105227c77 100644
--- a/lib/portage/dbapi/__init__.py
+++ b/lib/portage/dbapi/__init__.py
@@ -1,11 +1,12 @@
-# Copyright 1998-2023 Gentoo Authors
+# Copyright 1998-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 __all__ = ["dbapi"]
 
 import functools
+import logging
 import re
-import warnings
+import sys
 from typing import Any, Dict, List, Optional, Tuple
 from collections.abc import Sequence
 
@@ -429,7 +430,9 @@ class dbapi:
 try:
 aux_update(cpv, metadata_updates)
 except (InvalidBinaryPackageFormat, CorruptionKeyError) as e:
-warnings.warn(e)
+logging.warning(
+f"{e.__class__.__name__}: {e}", exc_info=sys.exc_info()
+)
 if onUpdate:
 onUpdate(maxval, i + 1)
 if onProgress:
@@ -477,6 +480,6 @@ class dbapi:
 try:
 self.aux_update(mycpv, mydata)
 except CorruptionKeyError as e:
-warnings.warn(e)
+logging.warning(f"{e.__class__.__name__}: {e}", 
exc_info=sys.exc_info())
 continue
 return moves



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/emerge/

2024-02-25 Thread Sam James
commit: 8a2f1d14788d107ec54dc53c9ef1cf00ee310d51
Author: Gábor Oszkár Dénes  protonmail  com>
AuthorDate: Sat Feb 24 20:48:05 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Sun Feb 25 08:25:06 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=8a2f1d14

test_baseline: Improve robustness with cleanup

The baseline tests need to cleanup the ResolverPlayground
after each testcase, with each different parametrization.
This is ensured by making the scope of the playground
fixture the function instead of the module. With module
the cleanup only happens before/after the switch from/to
xpak and gpkg.

Signed-off-by: Gábor Oszkár Dénes  protonmail.com>
Closes: https://github.com/gentoo/portage/pull/1281
Signed-off-by: Sam James  gentoo.org>

 lib/portage/tests/emerge/conftest.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/portage/tests/emerge/conftest.py 
b/lib/portage/tests/emerge/conftest.py
index 580d1e09ab..356e09879c 100644
--- a/lib/portage/tests/emerge/conftest.py
+++ b/lib/portage/tests/emerge/conftest.py
@@ -406,7 +406,7 @@ def async_loop():
 yield asyncio._wrap_loop()
 
 
-@pytest.fixture(params=SUPPORTED_GENTOO_BINPKG_FORMATS, scope="module")
+@pytest.fixture(params=SUPPORTED_GENTOO_BINPKG_FORMATS, scope="function")
 def playground(request, tmp_path_factory):
 """Fixture that provides instances of ``ResolverPlayground``
 each one with one supported value for ``BINPKG_FORMAT``."""



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-25 Thread Sam James
commit: 92615cd37d7a1efce923c474e455f59fe61a589c
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb 25 05:09:48 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Sun Feb 25 08:24:59 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=92615cd3

_start_proc: Prevent premature ForkProcess garbage collection

In order to prevent the constructed ForkProcess instance from
being prematurely garbage collected, return a reference to the
caller inside of a _GCProtector object, preventing messages
reported in bug 925456 like this:

[ERROR] Task was destroyed but it is pending!

Fixes: a69c1b853a47 ("process.spawn: Use multiprocessing.Process for 
returnproc")
Bug: https://bugs.gentoo.org/925456
Signed-off-by: Zac Medico  gentoo.org>
Closes: https://github.com/gentoo/portage/pull/1284
Signed-off-by: Sam James  gentoo.org>

 lib/portage/process.py | 41 +++--
 1 file changed, 39 insertions(+), 2 deletions(-)

diff --git a/lib/portage/process.py b/lib/portage/process.py
index d16262e75a..cc9ed7bf78 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -36,6 +36,7 @@ portage.proxy.lazyimport.lazyimport(
 
 from portage.const import BASH_BINARY, SANDBOX_BINARY, FAKEROOT_BINARY
 from portage.exception import CommandNotFound
+from portage.proxy.objectproxy import ObjectProxy
 from portage.util._ctypes import find_library, LoadLibrary, ctypes
 
 try:
@@ -1493,8 +1494,44 @@ def _start_proc(
 proc.start()
 
 # ForkProcess conveniently holds a MultiprocessingProcess
-# instance that is suitable to return here.
-return proc._proc
+# instance that is suitable to return here, but use _GCProtector
+# to protect the ForkProcess instance from being garbage collected
+# and triggering messages like this (bug 925456):
+# [ERROR] Task was destroyed but it is pending!
+return _GCProtector(proc._proc, proc.async_wait)
+
+
+class _GCProtector(ObjectProxy):
+"""
+Proxy a target object, and also hold a reference to something
+extra in order to protect it from garbage collection. Override
+the wait method to first call target's wait method and then
+wait for extra (a coroutine function) before returning the result.
+"""
+
+__slots__ = ("_extra", "_target")
+
+def __init__(self, target, extra):
+super().__init__()
+object.__setattr__(self, "_target", target)
+object.__setattr__(self, "_extra", extra)
+
+def _get_target(self):
+return object.__getattribute__(self, "_target")
+
+def __getattribute__(self, attr):
+if attr == "wait":
+return object.__getattribute__(self, attr)
+return getattr(object.__getattribute__(self, "_target"), attr)
+
+async def wait(self):
+"""
+Wrap the target's wait method to also wait for an extra
+coroutine function.
+"""
+result = await object.__getattribute__(self, "_target").wait()
+await object.__getattribute__(self, "_extra")()
+return result
 
 
 def find_binary(binary):



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/process/

2024-02-24 Thread Zac Medico
commit: 9e84ef57ba747766c9147c1ac1b247faa1f05956
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb 25 00:31:13 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Feb 25 00:42:46 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=9e84ef57

testSpawnReturnProcTerminate: Fix integer in spawn command argument

The invalid integer in the spawn command argument intermittently
triggered this error when SIGTERM did not arrive until after the
exec call:

- Captured stderr call -
Process ForkProcess-23:
Traceback (most recent call last):
  File "/home/runner/work/portage/portage/lib/portage/process.py", line 818, in 
_exec_wrapper
_exec(
  File "/home/runner/work/portage/portage/lib/portage/process.py", line 1068, 
in _exec
_exec2(
  File "/home/runner/work/portage/portage/lib/portage/process.py", line 1166, 
in _exec2
os.execve(binary, myargs, env)
TypeError: expected str, bytes or os.PathLike object, not int

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File 
"/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/multiprocessing/process.py",
 line 314, in _bootstrap
self.run()
  File 
"/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/multiprocessing/process.py",
 line 108, in run
self._target(*self._args, **self._kwargs)
  File 
"/home/runner/work/portage/portage/lib/portage/util/_async/ForkProcess.py", 
line 328, in _bootstrap
sys.exit(target(*(args or []), **(kwargs or {})))
 ^^^
  File "/home/runner/work/portage/portage/lib/portage/process.py", line 1441, 
in __call__
return self._target(*args, **kwargs)
   ^
  File "/home/runner/work/portage/portage/lib/portage/process.py", line 853, in 
_exec_wrapper
writemsg(f"{e}:\n   {' '.join(mycommand)}\n", noiselevel=-1)
 ^^^
TypeError: sequence item 1: expected str instance, int found

Bug: https://bugs.gentoo.org/916566#c20
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/process/test_spawn_returnproc.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/portage/tests/process/test_spawn_returnproc.py 
b/lib/portage/tests/process/test_spawn_returnproc.py
index 6d823d9c3d..8fbf54d0d2 100644
--- a/lib/portage/tests/process/test_spawn_returnproc.py
+++ b/lib/portage/tests/process/test_spawn_returnproc.py
@@ -32,7 +32,7 @@ class SpawnReturnProcTestCase(TestCase):
 loop = global_event_loop()
 
 async def watch_pid():
-proc = spawn([sleep_binary, ], returnproc=True)
+proc = spawn([sleep_binary, ""], returnproc=True)
 proc.terminate()
 self.assertEqual(await proc.wait(), -signal.SIGTERM)
 



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-24 Thread Zac Medico
commit: 3f4250dc7d32e9915224b1c9c4bc04c2740abcda
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb 23 20:35:04 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sat Feb 24 20:07:39 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=3f4250dc

process.spawn: Fix logic for missing libc.unshare on musl

Fix unshare_* variables to be False when the libc is missing,
libc.unshare is missing, or libc.unshare fails.

Also, if socket.sethostname is missing then _exec2 needs
libc for the network-sandbox sethostname call which is wrapped
by a blanket Exception handler.

Fixes: 419cce79f908 ("process._exec: Use _start_fork for os.fork() error 
handling")
Bug: https://bugs.gentoo.org/925311
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/process.py | 213 +
 1 file changed, 110 insertions(+), 103 deletions(-)

diff --git a/lib/portage/process.py b/lib/portage/process.py
index f4758c824c..d16262e75a 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -956,114 +956,119 @@ def _exec(
 signal.signal(signal.SIGQUIT, signal.SIG_DFL)
 
 # Unshare (while still uid==0)
+have_unshare = False
+libc = None
 if unshare_net or unshare_ipc or unshare_mount or unshare_pid:
 filename = find_library("c")
 if filename is not None:
 libc = LoadLibrary(filename)
 if libc is not None:
-# unshare() may not be supported by libc
-if not hasattr(libc, "unshare"):
-unshare_net = False
-unshare_ipc = False
-unshare_mount = False
-unshare_pid = False
-else:
-# Since a failed unshare call could corrupt process
-# state, first validate that the call can succeed.
-# The parent process should call _unshare_validate
-# before it forks, so that all child processes can
-# reuse _unshare_validate results that have been
-# cached by the parent process.
-errno_value = _unshare_validate(unshare_flags)
-if errno_value == 0 and libc.unshare(unshare_flags) != 0:
-errno_value = ctypes.get_errno()
-if errno_value != 0:
-involved_features = []
-if unshare_ipc:
-involved_features.append("ipc-sandbox")
-if unshare_mount:
-involved_features.append("mount-sandbox")
-if unshare_net:
-involved_features.append("network-sandbox")
-if unshare_pid:
-involved_features.append("pid-sandbox")
-
-writemsg(
-'Unable to unshare: %s (for FEATURES="%s")\n'
-% (
-errno.errorcode.get(errno_value, "?"),
-" ".join(involved_features),
-),
-noiselevel=-1,
-)
-else:
-if unshare_pid:
-# pid namespace requires us to become init
-binary, myargs = (
-portage._python_interpreter,
-[
-portage._python_interpreter,
-os.path.join(portage._bin_path, 
"pid-ns-init"),
-_unicode_encode("" if uid is None else 
str(uid)),
-_unicode_encode("" if gid is None else 
str(gid)),
-_unicode_encode(
-""
-if groups is None
-else ",".join(str(group) for group in 
groups)
-),
-_unicode_encode(
-"" if umask is None else str(umask)
-),
-_unicode_encode(
-",".join(str(fd) for fd in fd_pipes)
-),
-binary,
-]
-+ myargs,
-)
-uid = None
-gid = None
-groups = None
-umask = None
-
-# Use _start_fork for os.fork() error handling, 
ensuring
-# that if exec fails then the child 

[gentoo-commits] proj/portage:master commit in: lib/portage/tests/emerge/

2024-02-23 Thread Zac Medico
commit: 01d06eb1d9dc8c4b16cbc9a6567ed0c07df5901a
Author: Zac Medico  gentoo  org>
AuthorDate: Sat Feb 24 02:45:58 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sat Feb 24 03:28:24 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=01d06eb1

Fix python 3.9 CI jobs since 92ff02b9189f

Use emerge -e to fix this error we've seen since
92ff02b9189f for python 3.9 CI jobs:

https://github.com/gentoo/portage/actions/runs/8014796128/job/21893963019

test_portage_baseline[binhost emerge-gpkg] - AssertionError: 'emerge' failed 
with args '('-e', '--getbinpkgonly', 'dev-libs/A')'

emerge: there are no binary packages to satisfy "dev-libs/B[flag]".
(dependency required by "dev-libs/A-1::test_repo" [binary])
(dependency required by "dev-libs/A" [argument])

Fixes: 92ff02b9189f ("emerge: Skip installed packages with emptytree in 
depgraph selection")
Bug: https://bugs.gentoo.org/651018
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/emerge/conftest.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/portage/tests/emerge/conftest.py 
b/lib/portage/tests/emerge/conftest.py
index d9aec7041e..580d1e09ab 100644
--- a/lib/portage/tests/emerge/conftest.py
+++ b/lib/portage/tests/emerge/conftest.py
@@ -805,7 +805,7 @@ def _generate_all_baseline_commands(playground, binhost):
 test_commands["binhost emerge"] = Noop()
 else:
 # The next emerge has been added to split this test from the rest:
-make_package = Emerge("--buildpkg", "dev-libs/A")
+make_package = Emerge("-e", "--buildpkg", "dev-libs/A")
 getbinpkgonly = Emerge(
 "-e",
 "--getbinpkgonly",



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/util/, lib/portage/util/, lib/portage/util/_eventloop/

2024-02-23 Thread Zac Medico
commit: 3cc986f87ddda86ee93770e03cca06346aee54c5
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb 23 06:06:14 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb 23 06:48:29 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=3cc986f8

AsyncioEventLoop: Call process.run_exitfuncs() before close

For the event loop running in the main thread, call
process.run_exitfuncs() before close with the event loop
running so that anything attached can clean itself up (like
the socks5 ProxyManager for bug 925240). This is necessary
because process.spawn uses the event loop to implement the
new returnproc parameter related to bug 916566.

Bug: https://bugs.gentoo.org/916566
Bug: https://bugs.gentoo.org/925240
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/util/test_socks5.py | 51 ++-
 lib/portage/util/_eventloop/asyncio_event_loop.py | 44 +++
 lib/portage/util/socks5.py| 16 ++-
 3 files changed, 101 insertions(+), 10 deletions(-)

diff --git a/lib/portage/tests/util/test_socks5.py 
b/lib/portage/tests/util/test_socks5.py
index 7b33cb3f6b..4a6d08169d 100644
--- a/lib/portage/tests/util/test_socks5.py
+++ b/lib/portage/tests/util/test_socks5.py
@@ -12,6 +12,8 @@ import time
 import portage
 from portage.tests import TestCase
 from portage.util import socks5
+from portage.util.futures.executor.fork import ForkExecutor
+from portage.util._eventloop.global_event_loop import global_event_loop
 from portage.const import PORTAGE_BIN_PATH
 
 from http.server import BaseHTTPRequestHandler, HTTPServer
@@ -189,8 +191,10 @@ class Socks5ServerTestCase(TestCase):
 path = "/index.html"
 proxy = None
 tempdir = tempfile.mkdtemp()
+previous_exithandlers = portage.process._exithandlers
 
 try:
+portage.process._exithandlers = []
 with AsyncHTTPServer(host, {path: content}, loop) as server:
 settings = {
 "PORTAGE_TMPDIR": tempdir,
@@ -211,5 +215,50 @@ class Socks5ServerTestCase(TestCase):
 
 self.assertEqual(result, content)
 finally:
-await socks5.proxy.stop()
+try:
+# Also run_exitfuncs to test atexit hook cleanup.
+await socks5.proxy.stop()
+self.assertNotEqual(portage.process._exithandlers, [])
+portage.process.run_exitfuncs()
+self.assertEqual(portage.process._exithandlers, [])
+finally:
+portage.process._exithandlers = previous_exithandlers
+shutil.rmtree(tempdir)
+
+
+class Socks5ServerLoopCloseTestCase(TestCase):
+"""
+For bug 925240, test that the socks5 proxy is automatically
+terminated when the main event loop is closed, using a subprocess
+for isolation.
+"""
+
+def testSocks5ServerLoopClose(self):
+asyncio.run(self._testSocks5ServerLoopClose())
+
+async def _testSocks5ServerLoopClose(self):
+loop = asyncio.get_running_loop()
+self.assertEqual(
+await loop.run_in_executor(
+ForkExecutor(loop=loop), 
self._testSocks5ServerLoopCloseSubprocess
+),
+True,
+)
+
+@staticmethod
+def _testSocks5ServerLoopCloseSubprocess():
+loop = global_event_loop()
+tempdir = tempfile.mkdtemp()
+try:
+settings = {
+"PORTAGE_TMPDIR": tempdir,
+"PORTAGE_BIN_PATH": PORTAGE_BIN_PATH,
+}
+
+socks5.get_socks5_proxy(settings)
+loop.run_until_complete(socks5.proxy.ready())
+finally:
+loop.close()
 shutil.rmtree(tempdir)
+
+return not socks5.proxy.is_running()

diff --git a/lib/portage/util/_eventloop/asyncio_event_loop.py 
b/lib/portage/util/_eventloop/asyncio_event_loop.py
index b9e96bb20e..ee9e4c60ef 100644
--- a/lib/portage/util/_eventloop/asyncio_event_loop.py
+++ b/lib/portage/util/_eventloop/asyncio_event_loop.py
@@ -1,8 +1,9 @@
-# Copyright 2018-2023 Gentoo Authors
+# Copyright 2018-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import os
 import signal
+import threading
 
 import asyncio as _real_asyncio
 from asyncio.events import AbstractEventLoop as _AbstractEventLoop
@@ -14,6 +15,7 @@ except ImportError:
 PidfdChildWatcher = None
 
 import portage
+from portage.util import socks5
 
 
 class AsyncioEventLoop(_AbstractEventLoop):
@@ -25,18 +27,14 @@ class AsyncioEventLoop(_AbstractEventLoop):
 def __init__(self, loop=None):
 loop = loop or _real_asyncio.get_event_loop()
 self._loop = loop
-self.run_until_complete = (
-self._run_until_complete
-if portage._internal_caller
-else loop.run_until_complete
-)
+self.run_until_complete = self._run_until_complete
  

[gentoo-commits] proj/portage:master commit in: /, lib/portage/tests/resolver/, lib/_emerge/

2024-02-22 Thread Sam James
commit: 92ff02b9189f8350f44e134d538319e4037f3f71
Author: Gábor Oszkár Dénes  protonmail  com>
AuthorDate: Wed Feb 14 17:40:24 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Fri Feb 23 04:39:26 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=92ff02b9

emerge: Skip installed packages with emptytree in depgraph selection

Running emerge with emptytree tries to find the best match for every
atom it needs to install. Sometimes the best matches would be
already installed packages (with `operation=nomerge`), but these
packages would be silently skipped with full emptytree installation.
This change makes sure that emerge attempts to install every package.
If the package has unmet requirements, emerge will complain.

Bug: https://bugs.gentoo.org/651018
Signed-off-by: Gábor Oszkár Dénes  protonmail.com>
Closes: https://github.com/gentoo/portage/pull/1272
Signed-off-by: Sam James  gentoo.org>

 NEWS   |   6 +
 lib/_emerge/depgraph.py|  13 ++
 lib/portage/tests/resolver/test_depth.py   |   8 +-
 .../test_emptytree_reinstall_unsatisfiability.py   | 137 +
 lib/portage/tests/resolver/test_useflags.py|   6 +-
 5 files changed, 166 insertions(+), 4 deletions(-)

diff --git a/NEWS b/NEWS
index 3fbc727861..94be26de84 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,12 @@ Release notes take the form of the following optional 
categories:
 * Bug fixes
 * Cleanups
 
+portage-3.0.63 (UNRELEASED)
+--
+
+Bug fixes:
+* emerge: Skip installed packages with emptytree in depgraph selection (bug 
#651018).
+
 portage-3.0.62 (2024-02-22)
 --
 

diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py
index 70b83ee1f4..ea96bd58c4 100644
--- a/lib/_emerge/depgraph.py
+++ b/lib/_emerge/depgraph.py
@@ -7639,6 +7639,19 @@ class depgraph:
 if pkg.installed and root_slot in 
self._rebuild.reinstall_list:
 continue
 
+if (
+empty
+and pkg.installed
+and not 
self._frozen_config.excluded_pkgs.findAtomForPackage(
+pkg, modified_use=self._pkg_use_enabled(pkg)
+)
+):
+# With --emptytree option we assume no packages
+# are installed, so we do not select them.
+# But we allow installed packages to satisfy 
dependency requirements
+# if they're explicitly excluded, so we allow them to 
be selected.
+continue
+
 if (
 not pkg.installed
 and 
self._frozen_config.excluded_pkgs.findAtomForPackage(

diff --git a/lib/portage/tests/resolver/test_depth.py 
b/lib/portage/tests/resolver/test_depth.py
index 9c5289f7d0..ab5f8e7ec3 100644
--- a/lib/portage/tests/resolver/test_depth.py
+++ b/lib/portage/tests/resolver/test_depth.py
@@ -1,4 +1,4 @@
-# Copyright 2011-2020 Gentoo Authors
+# Copyright 2011-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from portage.tests import TestCase
@@ -318,6 +318,12 @@ class ResolverDepthTestCase(TestCase):
 "sys-fs/udev-164",
 ],
 ),
+ResolverPlaygroundTestCase(
+["@world"],
+options={"--emptytree": True, "--exclude": ["dev-libs/B"]},
+success=True,
+mergelist=["dev-libs/C-2", "dev-libs/A-2"],
+),
 )
 
 playground = ResolverPlayground(

diff --git 
a/lib/portage/tests/resolver/test_emptytree_reinstall_unsatisfiability.py 
b/lib/portage/tests/resolver/test_emptytree_reinstall_unsatisfiability.py
new file mode 100644
index 00..fcdc01d7f1
--- /dev/null
+++ b/lib/portage/tests/resolver/test_emptytree_reinstall_unsatisfiability.py
@@ -0,0 +1,137 @@
+# Copyright 2024 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from portage.tests import TestCase
+from portage.tests.resolver.ResolverPlayground import (
+ResolverPlayground,
+ResolverPlaygroundTestCase,
+)
+
+
+class EmptytreeReinstallUnsatisfiabilityTestCase(TestCase):
+def testEmptytreeReinstallUnsatisfiability(self):
+"""
+Tests to check if emerge fails and complains when --emptytree
+package dependency graph reinstall is unsatisfied, even if the already
+installed packages successfully satisfy the dependency tree.
+
+See bug #651018 where emerge silently skips package
+reinstalls because of unsatisfied use flag requirements.
+"""
+ebuilds = {
+"dev-libs/A-1": {
+"DEPEND": "dev-libs/B",
+"RDEPEND": "dev-libs/B",
+"EAPI": "2",
+},
+   

[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/

2024-02-22 Thread Zac Medico
commit: d718cea94a180042b2285698b2c19113c5d25987
Author: Zac Medico  gentoo  org>
AuthorDate: Thu Feb 22 06:41:49 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Thu Feb 22 07:28:38 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=d718cea9

_get_running_loop: Support real asyncio.run

When called via the real asyncio.run implementation, wrap
the running asyncio loop. Otherwise, it's not possible to
call portage libraries via the real asyncio.run without
triggering Future "attached to a different loop" errors.

Bug: https://bugs.gentoo.org/761538
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/util/futures/_asyncio/__init__.py | 28 +--
 1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/lib/portage/util/futures/_asyncio/__init__.py 
b/lib/portage/util/futures/_asyncio/__init__.py
index 22241f335d..4eecc46a89 100644
--- a/lib/portage/util/futures/_asyncio/__init__.py
+++ b/lib/portage/util/futures/_asyncio/__init__.py
@@ -325,13 +325,37 @@ def _safe_loop():
 
 
 def _get_running_loop():
+"""
+This calls the real asyncio get_running_loop() and wraps that with
+portage's internal AsyncioEventLoop wrapper. If there is no running
+asyncio event loop but portage has a reference to another running
+loop in this thread, then use that instead.
+
+This behavior enables portage internals to use the real asyncio.run
+while remaining compatible with internal code that does not use the
+real asyncio.run.
+"""
+try:
+_loop = _real_asyncio.get_running_loop()
+except RuntimeError:
+_loop = None
+
 with _thread_weakrefs.lock:
 if _thread_weakrefs.pid == portage.getpid():
 try:
 loop = _thread_weakrefs.loops[threading.get_ident()]
 except KeyError:
-return None
-return loop if loop.is_running() else None
+pass
+else:
+if _loop is loop._loop:
+return loop
+elif _loop is None:
+return loop if loop.is_running() else None
+
+# If _loop it not None here it means it was probably a temporary
+# loop created by asyncio.run, so we don't try to cache it, and
+# just return a temporary wrapper.
+return None if _loop is None else _AsyncioEventLoop(loop=_loop)
 
 
 def _thread_weakrefs_atexit():



[gentoo-commits] proj/portage:master commit in: lib/portage/util/, lib/portage/tests/util/

2024-02-22 Thread Zac Medico
commit: fbaaa4a733aaadc2744b656527756ac4e2b7ab58
Author: Zac Medico  gentoo  org>
AuthorDate: Thu Feb 22 06:47:33 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Thu Feb 22 07:28:38 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=fbaaa4a7

socks5: Use real asyncio.run

Use real asyncio.run to demonstrate that it is compatible with
portage internals. Since the socks5 ProxyManager uses the
process.spawn function, the internal _running_loop function
needs to return the correct loop for use in the wait method of
MultiprocessingProcess, or else it will lead to Future
"attached to a different loop" errors.

Bug: https://bugs.gentoo.org/761538
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/util/test_socks5.py | 45 +++
 lib/portage/util/socks5.py| 30 ---
 2 files changed, 46 insertions(+), 29 deletions(-)

diff --git a/lib/portage/tests/util/test_socks5.py 
b/lib/portage/tests/util/test_socks5.py
index 987b41af29..7b33cb3f6b 100644
--- a/lib/portage/tests/util/test_socks5.py
+++ b/lib/portage/tests/util/test_socks5.py
@@ -1,6 +1,7 @@
-# Copyright 2019-2021 Gentoo Authors
+# Copyright 2019-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
+import asyncio
 import functools
 import shutil
 import socket
@@ -10,7 +11,6 @@ import time
 
 import portage
 from portage.tests import TestCase
-from portage.util._eventloop.global_event_loop import global_event_loop
 from portage.util import socks5
 from portage.const import PORTAGE_BIN_PATH
 
@@ -88,18 +88,20 @@ class AsyncHTTPServerTestCase(TestCase):
 if f is not None:
 f.close()
 
-def test_http_server(self):
+async def _test_http_server(self):
+asyncio.run(self._test_http_server())
+
+async def _test_http_server(self):
 host = "127.0.0.1"
 content = b"Hello World!\n"
 path = "/index.html"
-loop = global_event_loop()
+
+loop = asyncio.get_running_loop()
 for i in range(2):
 with AsyncHTTPServer(host, {path: content}, loop) as server:
 for j in range(2):
-result = loop.run_until_complete(
-loop.run_in_executor(
-None, self._fetch_directly, host, 
server.server_port, path
-)
+result = await loop.run_in_executor(
+None, self._fetch_directly, host, server.server_port, 
path
 )
 self.assertEqual(result, content)
 
@@ -177,7 +179,10 @@ class Socks5ServerTestCase(TestCase):
 return f.read()
 
 def test_socks5_proxy(self):
-loop = global_event_loop()
+asyncio.run(self._test_socks5_proxy())
+
+async def _test_socks5_proxy(self):
+loop = asyncio.get_running_loop()
 
 host = "127.0.0.1"
 content = b"Hello World!"
@@ -193,20 +198,18 @@ class Socks5ServerTestCase(TestCase):
 }
 
 proxy = socks5.get_socks5_proxy(settings)
-loop.run_until_complete(socks5.proxy.ready())
-
-result = loop.run_until_complete(
-loop.run_in_executor(
-None,
-self._fetch_via_proxy,
-proxy,
-host,
-server.server_port,
-path,
-)
+await socks5.proxy.ready()
+
+result = await loop.run_in_executor(
+None,
+self._fetch_via_proxy,
+proxy,
+host,
+server.server_port,
+path,
 )
 
 self.assertEqual(result, content)
 finally:
-socks5.proxy.stop()
+await socks5.proxy.stop()
 shutil.rmtree(tempdir)

diff --git a/lib/portage/util/socks5.py b/lib/portage/util/socks5.py
index 6c68ff4106..74592aeefe 100644
--- a/lib/portage/util/socks5.py
+++ b/lib/portage/util/socks5.py
@@ -2,15 +2,16 @@
 # Copyright 2015-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
+import asyncio
 import errno
 import os
 import socket
+from typing import Union
 
 import portage.data
 from portage import _python_interpreter
 from portage.data import portage_gid, portage_uid, userpriv_groups
 from portage.process import atexit_register, spawn
-from portage.util.futures import asyncio
 
 
 class ProxyManager:
@@ -57,23 +58,36 @@ class ProxyManager:
 **spawn_kwargs,
 )
 
-def stop(self):
+def stop(self) -> Union[None, asyncio.Future]:
 """
 Stop the SOCKSv5 server.
+
+If there is a running asyncio event loop then asyncio.Future is
+returned which should be used to wait 

[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/, lib/portage/package/ebuild/, ...

2024-02-21 Thread Zac Medico
commit: 18cdb6331a66c1cc92f296b1aaf0538f63586875
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Feb 13 08:44:50 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb 21 15:27:31 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=18cdb633

EbuildPhase: async_check_locale

Change config.environ() check_locale calls to async_check_locale
calls in the EbuildPhase _async_start method in order to eliminate
synchronous waiting for child processes in the main event loop
thread.

Bug: https://bugs.gentoo.org/923841
Signed-off-by: Zac Medico  gentoo.org>

 lib/_emerge/EbuildMetadataPhase.py|  4 
 lib/_emerge/EbuildPhase.py| 28 ++-
 lib/portage/package/ebuild/config.py  | 26 +++--
 lib/portage/util/futures/_asyncio/__init__.py |  9 +
 lib/portage/util/locale.py| 28 ++-
 5 files changed, 70 insertions(+), 25 deletions(-)

diff --git a/lib/_emerge/EbuildMetadataPhase.py 
b/lib/_emerge/EbuildMetadataPhase.py
index fc64d84c94..54177840c7 100644
--- a/lib/_emerge/EbuildMetadataPhase.py
+++ b/lib/_emerge/EbuildMetadataPhase.py
@@ -8,6 +8,7 @@ import portage
 
 portage.proxy.lazyimport.lazyimport(
 globals(),
+"_emerge.EbuildPhase:_setup_locale",
 "portage.package.ebuild._metadata_invalid:eapi_invalid",
 )
 from portage import os
@@ -83,6 +84,9 @@ class EbuildMetadataPhase(SubProcess):
 settings.setcpv(self.cpv)
 settings.configdict["pkg"]["EAPI"] = parsed_eapi
 
+# This requires above setcpv and EAPI setup.
+await _setup_locale(self.settings)
+
 debug = settings.get("PORTAGE_DEBUG") == "1"
 master_fd = None
 slave_fd = None

diff --git a/lib/_emerge/EbuildPhase.py b/lib/_emerge/EbuildPhase.py
index 3b366f39c7..b472803438 100644
--- a/lib/_emerge/EbuildPhase.py
+++ b/lib/_emerge/EbuildPhase.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2021 Gentoo Authors
+# Copyright 1999-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import functools
@@ -24,6 +24,7 @@ from portage.package.ebuild.prepare_build_dirs import (
 _prepare_fake_distdir,
 _prepare_fake_filesdir,
 )
+from portage.eapi import _get_eapi_attrs
 from portage.util import writemsg, ensure_dirs
 from portage.util._async.AsyncTaskFuture import AsyncTaskFuture
 from portage.util._async.BuildLogger import BuildLogger
@@ -54,12 +55,34 @@ portage.proxy.lazyimport.lazyimport(
 + "_post_src_install_write_metadata,"
 + "_preinst_bsdflags",
 "portage.util.futures.unix_events:_set_nonblocking",
+"portage.util.locale:async_check_locale,split_LC_ALL",
 )
 from portage import os
 from portage import _encodings
 from portage import _unicode_encode
 
 
+async def _setup_locale(settings):
+eapi_attrs = _get_eapi_attrs(settings["EAPI"])
+if eapi_attrs.posixish_locale:
+split_LC_ALL(settings)
+settings["LC_COLLATE"] = "C"
+# check_locale() returns None when check can not be executed.
+if await async_check_locale(silent=True, env=settings.environ()) is 
False:
+# try another locale
+for l in ("C.UTF-8", "en_US.UTF-8", "en_GB.UTF-8", "C"):
+settings["LC_CTYPE"] = l
+if await async_check_locale(silent=True, 
env=settings.environ()):
+# TODO: output the following only once
+# writemsg(
+# _("!!! LC_CTYPE unsupported, using %s instead\n")
+# % self.settings["LC_CTYPE"]
+# )
+break
+else:
+raise AssertionError("C locale did not pass the test!")
+
+
 class EbuildPhase(CompositeTask):
 __slots__ = ("actionmap", "fd_pipes", "phase", "settings") + 
("_ebuild_lock",)
 
@@ -95,6 +118,9 @@ class EbuildPhase(CompositeTask):
 self._start_task(AsyncTaskFuture(future=future), 
self._async_start_exit)
 
 async def _async_start(self):
+
+await _setup_locale(self.settings)
+
 need_builddir = self.phase not in 
EbuildProcess._phases_without_builddir
 
 if need_builddir:

diff --git a/lib/portage/package/ebuild/config.py 
b/lib/portage/package/ebuild/config.py
index c89354cbf7..bafdc55a08 100644
--- a/lib/portage/package/ebuild/config.py
+++ b/lib/portage/package/ebuild/config.py
@@ -29,7 +29,6 @@ portage.proxy.lazyimport.lazyimport(
 "portage.dbapi.vartree:vartree",
 "portage.package.ebuild.doebuild:_phase_func_map",
 "portage.util.compression_probe:_compressors",
-"portage.util.locale:check_locale,split_LC_ALL",
 )
 from portage import bsd_chflags, load_mod, os, selinux, _unicode_decode
 from portage.const import (
@@ -3371,20 +3370,17 @@ class config:
 mydict["EBUILD_PHASE_FUNC"] = phase_func
 
 if eapi_attrs.posixish_locale:
-split_LC_ALL(mydict)
-  

[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/

2024-02-21 Thread Zac Medico
commit: a42c2164ada634262ae1f791ad60298fe3468a94
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Feb 13 03:39:35 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb 21 15:27:31 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=a42c2164

asyncio: Wrap asyncio.Lock for python 3.9 compat

Wrap asyncio.Lock for compatibility with python 3.9 where the
deprecated loop parameter is required in order to avoid "got
Future  attached to a different loop" errors.

The pordbapi async_aux_get method can use asyncio.Lock to
serialize access to its doebuild_settings attribute in order
to prevent issues like bug 924319.

Bug: https://bugs.gentoo.org/924319
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/util/futures/_asyncio/__init__.py | 17 +
 1 file changed, 17 insertions(+)

diff --git a/lib/portage/util/futures/_asyncio/__init__.py 
b/lib/portage/util/futures/_asyncio/__init__.py
index 8f1b8e8275..b6481c281e 100644
--- a/lib/portage/util/futures/_asyncio/__init__.py
+++ b/lib/portage/util/futures/_asyncio/__init__.py
@@ -9,6 +9,7 @@ __all__ = (
 "CancelledError",
 "Future",
 "InvalidStateError",
+"Lock",
 "TimeoutError",
 "get_child_watcher",
 "get_event_loop",
@@ -22,6 +23,7 @@ __all__ = (
 "wait_for",
 )
 
+import sys
 import types
 import weakref
 
@@ -35,6 +37,7 @@ from asyncio import (
 FIRST_EXCEPTION,
 Future,
 InvalidStateError,
+Lock as _Lock,
 shield,
 TimeoutError,
 wait_for,
@@ -159,6 +162,20 @@ def iscoroutinefunction(func):
 return False
 
 
+class Lock(_Lock):
+"""
+Inject loop parameter for python3.9 or less in order to avoid
+"got Future  attached to a different loop" errors.
+"""
+
+def __init__(self, **kwargs):
+if sys.version_info >= (3, 10):
+kwargs.pop("loop", None)
+elif "loop" not in kwargs:
+kwargs["loop"] = _safe_loop()._loop
+super().__init__(**kwargs)
+
+
 class Task(Future):
 """
 Schedule the execution of a coroutine: wrap it in a future. A task



[gentoo-commits] proj/portage:master commit in: lib/portage/_emirrordist/, lib/portage/tests/update/, lib/portage/dbapi/

2024-02-21 Thread Zac Medico
commit: 389bb304abf5705b9f0e9e75982a682f193af985
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Feb 13 04:35:02 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb 21 15:27:31 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=389bb304

async_aux_get: Use EbuildMetadataPhase deallocate_config future

For the portdbapi async_aux_get method, there is not a very
good place to store a config pool, so instead use asyncio.Lock
to manage access to the portdbapi doebuild_settings attribute
when using the main event loop in the main thread. For other
threads, clone a config instance since we do not have a
thread-safe config pool. This cloning is expensive, but since
portage internals do not trigger this case, it suffices for now
(an AssertionError ensures that internals do not trigger it).
For the main event loop running in the main thread, performance
with the asyncio.Lock should not be significantly different to
performance prior to commit c95fc64abf96, since check_locale
results are typically cached and before there was only a single
shared doebuild_settings instance with access serialized via
the EbuildMetadataPhase _start method.

Update async_aux_get callers to use asyncio.ensure_future on
the returned coroutine when needed, since it used to return
a future instead of a coroutine, and sometimes a future is
needed for add_done_callback usage.

In the portdbapi async_fetch_map method, fix a broken reference
to "future" which should have been "aux_get_future", an error
discovered while testing this patch.

Bug: https://bugs.gentoo.org/924319
Fixes: c95fc64abf96 ("EbuildPhase: async_check_locale")
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/_emirrordist/FetchIterator.py |  10 ++-
 lib/portage/dbapi/porttree.py | 129 --
 lib/portage/tests/update/test_move_ent.py |   3 +
 3 files changed, 97 insertions(+), 45 deletions(-)

diff --git a/lib/portage/_emirrordist/FetchIterator.py 
b/lib/portage/_emirrordist/FetchIterator.py
index eaf3e53596..e4fdd092af 100644
--- a/lib/portage/_emirrordist/FetchIterator.py
+++ b/lib/portage/_emirrordist/FetchIterator.py
@@ -1,4 +1,4 @@
-# Copyright 2013-2018 Gentoo Foundation
+# Copyright 2013-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import threading
@@ -14,6 +14,7 @@ from portage.exception import PortageException, 
PortageKeyError
 from portage.package.ebuild.fetch import DistfileName
 from portage.util._async.AsyncTaskFuture import AsyncTaskFuture
 from portage.util._async.TaskScheduler import TaskScheduler
+from portage.util.futures import asyncio
 from portage.util.futures.iter_completed import iter_gather
 from .FetchTask import FetchTask
 from _emerge.CompositeTask import CompositeTask
@@ -276,8 +277,11 @@ def _async_fetch_tasks(config, hash_filter, repo_config, 
digests_future, cpv, lo
 result.set_result(fetch_tasks)
 
 def future_generator():
-yield config.portdb.async_aux_get(
-cpv, ("RESTRICT",), myrepo=repo_config.name, loop=loop
+yield asyncio.ensure_future(
+config.portdb.async_aux_get(
+cpv, ("RESTRICT",), myrepo=repo_config.name, loop=loop
+),
+loop,
 )
 yield config.portdb.async_fetch_map(cpv, mytree=repo_config.location, 
loop=loop)
 

diff --git a/lib/portage/dbapi/porttree.py b/lib/portage/dbapi/porttree.py
index 61d431f917..4eebe1183f 100644
--- a/lib/portage/dbapi/porttree.py
+++ b/lib/portage/dbapi/porttree.py
@@ -1,4 +1,4 @@
-# Copyright 1998-2021 Gentoo Authors
+# Copyright 1998-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 __all__ = ["close_portdbapi_caches", "FetchlistDict", "portagetree", 
"portdbapi"]
@@ -41,7 +41,9 @@ from portage.util.futures import asyncio
 from portage.util.futures.iter_completed import iter_gather
 from _emerge.EbuildMetadataPhase import EbuildMetadataPhase
 
+import contextlib
 import os as _os
+import threading
 import traceback
 import warnings
 import errno
@@ -239,6 +241,7 @@ class portdbapi(dbapi):
 # this purpose because doebuild makes many changes to the config
 # instance that is passed in.
 self.doebuild_settings = config(clone=self.settings)
+self._doebuild_settings_lock = asyncio.Lock()
 self.depcachedir = os.path.realpath(self.settings.depcachedir)
 
 if os.environ.get("SANDBOX_ON") == "1":
@@ -356,6 +359,17 @@ class portdbapi(dbapi):
 self._better_cache = None
 self._broken_ebuilds = set()
 
+def __getstate__(self):
+state = self.__dict__.copy()
+# These attributes are not picklable, so they are automatically
+# regenerated after unpickling.
+state["_doebuild_settings_lock"] = None
+return state
+
+def __setstate__(self, state):
+self.__dict__.update(state)
+self._doebuild_settings_lock = 

[gentoo-commits] proj/portage:master commit in: lib/portage/tests/dbapi/, lib/portage/tests/resolver/

2024-02-21 Thread Zac Medico
commit: 0dedea99ac13e0e75a83a78890ed73bced1b950b
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Feb 13 04:21:26 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb 21 15:27:31 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=0dedea99

ResolverPlayground: Use egencache to create manifests

Make the ResolverPlayground _create_ebuild_manifests method
call egencache --jobs, which reliably triggers the KeyError
from bug 924319 for multiple tests:

lib/portage/tests/bin/test_doins.py::DoIns::testDoInsFallback Exception in 
callback EbuildMetadataPhase._async_start_done()
handle: )>
Traceback (most recent call last):
  File "/usr/lib/python3.12/asyncio/events.py", line 88, in _run
self._context.run(self._callback, *self._args)
  File "lib/_emerge/EbuildMetadataPhase.py", line 154, in _async_start_done
future.cancelled() or future.result()
  ^^^
  File "lib/_emerge/EbuildMetadataPhase.py", line 130, in _async_start
retval = portage.doebuild(
 ^
  File "lib/portage/package/ebuild/doebuild.py", line 1030, in doebuild
doebuild_environment(
  File "lib/portage/package/ebuild/doebuild.py", line 519, in 
doebuild_environment
eapi = mysettings.configdict["pkg"]["EAPI"]
   
  File "lib/portage/util/__init__.py", line 1684, in __getitem__
return UserDict.__getitem__(self, item_key)
   
  File "lib/portage/cache/mappings.py", line 175, in __getitem__
return self.data[key]
   ~^
KeyError: 'EAPI'

Bug: https://bugs.gentoo.org/924319
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/dbapi/test_portdb_cache.py |  4 ++-
 lib/portage/tests/resolver/ResolverPlayground.py | 38 +---
 2 files changed, 23 insertions(+), 19 deletions(-)

diff --git a/lib/portage/tests/dbapi/test_portdb_cache.py 
b/lib/portage/tests/dbapi/test_portdb_cache.py
index c7c6913b49..c24a4f2098 100644
--- a/lib/portage/tests/dbapi/test_portdb_cache.py
+++ b/lib/portage/tests/dbapi/test_portdb_cache.py
@@ -1,6 +1,7 @@
-# Copyright 2012-2023 Gentoo Authors
+# Copyright 2012-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
+import shutil
 import subprocess
 import sys
 import textwrap
@@ -63,6 +64,7 @@ class PortdbCacheTestCase(TestCase):
 python_cmd = (portage_python, "-b", "-Wd", "-c")
 
 test_commands = (
+(lambda: shutil.rmtree(md5_cache_dir) or True,),
 (lambda: not os.path.exists(pms_cache_dir),),
 (lambda: not os.path.exists(md5_cache_dir),),
 python_cmd

diff --git a/lib/portage/tests/resolver/ResolverPlayground.py 
b/lib/portage/tests/resolver/ResolverPlayground.py
index 2d26012873..75c86b615c 100644
--- a/lib/portage/tests/resolver/ResolverPlayground.py
+++ b/lib/portage/tests/resolver/ResolverPlayground.py
@@ -3,6 +3,7 @@
 
 import bz2
 import fnmatch
+import subprocess
 import tempfile
 import portage
 
@@ -18,8 +19,6 @@ from portage.const import (
 from portage.process import find_binary
 from portage.dep import Atom, _repo_separator
 from portage.dbapi.bintree import binarytree
-from portage.package.ebuild.config import config
-from portage.package.ebuild.digestgen import digestgen
 from portage._sets import load_default_config
 from portage._sets.base import InternalPackageSet
 from portage.tests import cnf_path
@@ -323,22 +322,25 @@ class ResolverPlayground:
 f.write(misc_content)
 
 def _create_ebuild_manifests(self, ebuilds):
-tmpsettings = config(clone=self.settings)
-tmpsettings["PORTAGE_QUIET"] = "1"
-for cpv in ebuilds:
-a = Atom("=" + cpv, allow_repo=True)
-repo = a.repo
-if repo is None:
-repo = "test_repo"
-
-repo_dir = self._get_repo_dir(repo)
-ebuild_dir = os.path.join(repo_dir, a.cp)
-ebuild_path = os.path.join(ebuild_dir, a.cpv.split("/")[1] + 
".ebuild")
-
-portdb = self.trees[self.eroot]["porttree"].dbapi
-tmpsettings["O"] = ebuild_dir
-if not digestgen(mysettings=tmpsettings, myportdb=portdb):
-raise AssertionError(f"digest creation failed for 
{ebuild_path}")
+for repo_name in self._repositories:
+if repo_name == "DEFAULT":
+continue
+egencache_cmd = [
+"egencache",
+f"--repo={repo_name}",
+"--update",
+"--update-manifests",
+"--sign-manifests=n",
+"--strict-manifests=n",
+
f"--repositories-configuration={self.settings['PORTAGE_REPOSITORIES']}",
+f"--jobs={portage.util.cpuinfo.get_cpu_count()}",
+]
+result = subprocess.run(
+egencache_cmd,
+ 

[gentoo-commits] proj/portage:master commit in: lib/portage/cache/, lib/portage/tests/dbapi/

2024-02-21 Thread Zac Medico
commit: 414234a218bc79564ab17312d5cc247a4c8091d7
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Feb 13 03:30:00 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb 21 15:27:30 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=414234a2

anydbm: Pickle support for multiprocessing spawn

The egencache usage in ResolverPlayground that was used to trigger
bug 924319 triggered a pickling error for AuxdbTestCase.test_anydbm
with the multiprocessing spawn start method, so fix the anydbm
cache module to omit the unpicklable database object from pickled
state, and regenerate it after unpickling.

Bug: https://bugs.gentoo.org/924319
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/cache/anydbm.py   | 17 -
 lib/portage/tests/dbapi/test_auxdb.py |  4 +---
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/lib/portage/cache/anydbm.py b/lib/portage/cache/anydbm.py
index 94a270a483..ad7042ae41 100644
--- a/lib/portage/cache/anydbm.py
+++ b/lib/portage/cache/anydbm.py
@@ -1,4 +1,4 @@
-# Copyright 2005-2020 Gentoo Authors
+# Copyright 2005-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 # Author(s): Brian Harring (ferri...@gentoo.org)
 
@@ -67,6 +67,21 @@ class database(fs_template.FsBased):
 raise cache_errors.InitializationError(self.__class__, e)
 self._ensure_access(self._db_path)
 
+def __getstate__(self):
+state = self.__dict__.copy()
+# These attributes are not picklable, so they are automatically
+# regenerated after unpickling.
+state["_database__db"] = None
+return state
+
+def __setstate__(self, state):
+self.__dict__.update(state)
+mode = "w"
+if dbm.whichdb(self._db_path) in ("dbm.gnu", "gdbm"):
+# Allow multiple concurrent writers (see bug #53607).
+mode += "u"
+self.__db = dbm.open(self._db_path, mode, self._perms)
+
 def iteritems(self):
 # dbm doesn't implement items()
 for k in self.__db.keys():

diff --git a/lib/portage/tests/dbapi/test_auxdb.py 
b/lib/portage/tests/dbapi/test_auxdb.py
index 0de0123a5f..aac6ce361c 100644
--- a/lib/portage/tests/dbapi/test_auxdb.py
+++ b/lib/portage/tests/dbapi/test_auxdb.py
@@ -16,9 +16,7 @@ class AuxdbTestCase(TestCase):
 from portage.cache.anydbm import database
 except ImportError:
 self.skipTest("dbm import failed")
-self._test_mod(
-"portage.cache.anydbm.database", multiproc=False, picklable=False
-)
+self._test_mod("portage.cache.anydbm.database", multiproc=False, 
picklable=True)
 
 def test_flat_hash_md5(self):
 self._test_mod("portage.cache.flat_hash.md5_database")



[gentoo-commits] proj/portage:master commit in: lib/portage/package/ebuild/, bin/, man/

2024-02-20 Thread Sam James
commit: 997058a825a340813532bef77a34425cf4a88eb2
Author: Michał Górny  gentoo  org>
AuthorDate: Thu Feb 15 15:33:03 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Wed Feb 21 02:13:45 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=997058a8

Support PROPERTIES=test_userpriv not to drop perms for tests

Support PROPERTIES=test_userpriv and a corresponding ALLOW_TEST=userpriv
to disable FEATURES=userpriv when running the test phase.  This can be
used e.g. in dev-python/reflink that needs to be able to mount
filesystem on a loopback device for testing.

Bug: https://bugs.gentoo.org/924585
Signed-off-by: Michał Górny  gentoo.org>
Closes: https://github.com/gentoo/portage/pull/1274
Signed-off-by: Sam James  gentoo.org>

 bin/phase-functions.sh | 3 ++-
 lib/portage/package/ebuild/config.py   | 3 +++
 lib/portage/package/ebuild/doebuild.py | 3 +++
 man/ebuild.5   | 5 +
 man/make.conf.5| 4 
 5 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/bin/phase-functions.sh b/bin/phase-functions.sh
index cd672a878c..ebcf5f242a 100644
--- a/bin/phase-functions.sh
+++ b/bin/phase-functions.sh
@@ -503,7 +503,8 @@ __dyn_test() {
fi
 
if has test ${PORTAGE_RESTRICT} && ! has all ${ALLOW_TEST} &&
-   ! { has test_network ${PORTAGE_PROPERTIES} && has 
network ${ALLOW_TEST}; }
+   ! { has test_network ${PORTAGE_PROPERTIES} && has 
network ${ALLOW_TEST}; } &&
+   ! { has test_privileged ${PORTAGE_PROPERTIES} && has 
privileged ${ALLOW_TEST}; }
then
einfo "Skipping make test/check due to ebuild restriction."
__vecho ">>> Test phase [disabled because of RESTRICT=test]: 
${CATEGORY}/${PF}"

diff --git a/lib/portage/package/ebuild/config.py 
b/lib/portage/package/ebuild/config.py
index d7b0ca5676..c89354cbf7 100644
--- a/lib/portage/package/ebuild/config.py
+++ b/lib/portage/package/ebuild/config.py
@@ -2114,6 +2114,9 @@ class config:
 "test" in restrict
 and not "all" in allow_test
 and not ("test_network" in properties and "network" in 
allow_test)
+and not (
+"test_privileged" in properties and "privileged" in 
allow_test
+)
 )
 
 if restrict_test and "test" in self.features:

diff --git a/lib/portage/package/ebuild/doebuild.py 
b/lib/portage/package/ebuild/doebuild.py
index 4cf155e033..bc51fdff2d 100644
--- a/lib/portage/package/ebuild/doebuild.py
+++ b/lib/portage/package/ebuild/doebuild.py
@@ -239,6 +239,9 @@ def _doebuild_spawn(phase, settings, actionmap=None, 
**kwargs):
 ebuild_sh_arg,
 )
 
+if phase == "test" and "test_privileged" in 
settings["PORTAGE_PROPERTIES"].split():
+kwargs["droppriv"] = False
+
 settings["EBUILD_PHASE"] = phase
 try:
 return spawn(cmd, settings, **kwargs)

diff --git a/man/ebuild.5 b/man/ebuild.5
index f849f20a29..a32ba4828c 100644
--- a/man/ebuild.5
+++ b/man/ebuild.5
@@ -811,6 +811,11 @@ is installed.
 The package manager may run tests that require an internet connection, even if
 the ebuild has
 .IR RESTRICT=test .
+.TP
+.I test_privileged
+The package manager may run tests that require superuser permissions, even if
+the ebuild has
+.IR RESTRICT=test .
 .RE
 .PD 1
 .TP

diff --git a/man/make.conf.5 b/man/make.conf.5
index 23d8408544..e13f6eec4f 100644
--- a/man/make.conf.5
+++ b/man/make.conf.5
@@ -1323,6 +1323,10 @@ Run tests in packages specifying 
\fBPROPERTIES\fR="\fBtest_network\fR".  Note
 that this will most likely cause Internet access during the test suite which
 could cause additional costs, privacy concerns and intermittent test failures.
 .TP
+.B privileged
+Run tests in packages specifying \fBPROPERTIES\fR="\fBtest_privileged\fR".  
Note
+that this will cause the test suite to be run with superuser permissions.
+.TP
 .RE
 .TP
 .B RESUMECOMMAND



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-20 Thread Sam James
commit: 58b4c156543705f631444040c2b962c4ac760f6a
Author: Sam James  gentoo  org>
AuthorDate: Wed Feb 21 02:07:59 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Wed Feb 21 02:08:07 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=58b4c156

gpkg: add missing newline to gpkg format error

Signed-off-by: Sam James  gentoo.org>

 lib/portage/gpkg.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/portage/gpkg.py b/lib/portage/gpkg.py
index f1d8f97f8e..edb0e43fbf 100644
--- a/lib/portage/gpkg.py
+++ b/lib/portage/gpkg.py
@@ -2111,7 +2111,7 @@ class gpkg:
 
 if self.basename and self.prefix and not 
self.prefix.startswith(self.basename):
 writemsg(
-colorize("WARN", f"Package basename mismatched, using 
{self.prefix}")
+colorize("WARN", f"Package basename mismatched, using 
{self.prefix}\n")
 )
 
 all_files = tar.getmembers()



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-20 Thread Sam James
commit: 14fe81d73c3330ec31bbc712ada40cc5bdda2c61
Author: Sam James  gentoo  org>
AuthorDate: Wed Feb 21 02:07:19 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Wed Feb 21 02:07:19 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=14fe81d7

binpkg: add missing newline to gpkg format error

Signed-off-by: Sam James  gentoo.org>

 lib/portage/binpkg.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/portage/binpkg.py b/lib/portage/binpkg.py
index 2078e3ca58..9ecd52cf3c 100644
--- a/lib/portage/binpkg.py
+++ b/lib/portage/binpkg.py
@@ -67,7 +67,7 @@ def get_binpkg_format(binpkg_path, check_file=False, 
remote=False):
 writemsg(
 colorize(
 "WARN",
-"File {} binpkg format mismatch, actual format: {}".format(
+"File {} binpkg format mismatch, actual format: {}\n".format(
 binpkg_path, file_format
 ),
 )



[gentoo-commits] proj/portage:master commit in: lib/portage/util/_async/

2024-02-14 Thread Zac Medico
commit: 038ad1029ea574b106906380e47479db1041bee2
Author: Zac Medico  gentoo  org>
AuthorDate: Wed Feb 14 05:55:31 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb 14 06:04:22 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=038ad102

BuildLogger: Fix portage.locks._open_fds memory leak

The _file_close_wrapper __getattribute__ method needs to
be overridden to expose its close method, otherwise the
underlying file's close method is called and the closed
file object remains as a memory leak in the global
portage.locks._open_fds dict. For reference, see similar
classes like portage.util.atomic_ofstream which overrides
methods in the same way.

Bug: https://bugs.gentoo.org/919072
Fixes: df212738bbb2 ("BuildLogger: Close self._stdin after fork")
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/util/_async/BuildLogger.py | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lib/portage/util/_async/BuildLogger.py 
b/lib/portage/util/_async/BuildLogger.py
index 9f8a21ab2b..0cfc90a942 100644
--- a/lib/portage/util/_async/BuildLogger.py
+++ b/lib/portage/util/_async/BuildLogger.py
@@ -1,4 +1,4 @@
-# Copyright 2020-2023 Gentoo Authors
+# Copyright 2020-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import functools
@@ -31,6 +31,11 @@ class _file_close_wrapper(ObjectProxy):
 def _get_target(self):
 return object.__getattribute__(self, "_file")
 
+def __getattribute__(self, attr):
+if attr == "close":
+return object.__getattribute__(self, attr)
+return getattr(object.__getattribute__(self, "_file"), attr)
+
 def close(self):
 file = object.__getattribute__(self, "_file")
 if not file.closed:



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/util/futures/, lib/portage/tests/sets/files/, ...

2024-02-12 Thread Zac Medico
commit: 3110ec376cbcb1f5b7fb82ba30ec958798bb32cf
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Feb 13 00:46:52 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Tue Feb 13 05:04:40 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=3110ec37

actions: Fix interaction between start-method and pytest-xdist

Use portage.test.TestCase setUp method to setup the multiprocessing
start method if needed. It needs to be done relatively late in order
to work with the pytest-xdist plugin due to execnet usage.

Bug: https://bugs.gentoo.org/914876
Signed-off-by: Zac Medico  gentoo.org>

 .github/workflows/ci.yml|  2 +-
 lib/portage/tests/__init__.py   | 12 +++-
 lib/portage/tests/env/config/test_PortageModulesFile.py |  3 ++-
 lib/portage/tests/news/test_NewsItem.py |  3 ++-
 lib/portage/tests/sets/files/test_config_file_set.py|  3 ++-
 lib/portage/tests/sets/files/test_static_file_set.py|  3 ++-
 lib/portage/tests/sets/shell/test_shell.py  |  4 ++--
 lib/portage/tests/util/futures/test_retry.py|  1 +
 8 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 15da507238..5bffd97206 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -82,7 +82,6 @@ jobs:
   mv "${bin_file}"{.new,}
 fi
   done < <(find bin -maxdepth 1 -type f)
-  sed -i meson.build -e "s|'-m', 'pytest'|'-c', 'import 
multiprocessing, sys, pytest; multiprocessing.set_start_method(\"spawn\", 
force=True); sys.exit(pytest.console_main())'|"
   - name: Test meson install --destdir /tmp/install-root
 run: |
   echo -e "[binaries]\npython = '$(command -v python)'" > 
/tmp/native.ini
@@ -90,5 +89,6 @@ jobs:
   meson install -C /tmp/build --destdir /tmp/install-root
   - name: Run tests for ${{ matrix.python-version }}
 run: |
+  [[ "${{ matrix.start-method }}" == "spawn" ]] && export 
PORTAGE_MULTIPROCESSING_START_METHOD=spawn
   export PYTEST_ADDOPTS="-vv -ra -l -o console_output_style=count -n 
$(nproc) --dist=worksteal"
   meson test -C /tmp/build --verbose

diff --git a/lib/portage/tests/__init__.py b/lib/portage/tests/__init__.py
index ef59852989..23dd366d89 100644
--- a/lib/portage/tests/__init__.py
+++ b/lib/portage/tests/__init__.py
@@ -1,8 +1,9 @@
 # tests/__init__.py -- Portage Unit Test functionality
-# Copyright 2006-2023 Gentoo Authors
+# Copyright 2006-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import argparse
+import multiprocessing
 import sys
 import time
 import unittest
@@ -79,6 +80,15 @@ class TestCase(unittest.TestCase):
 self.bindir = cnf_bindir
 self.sbindir = cnf_sbindir
 
+def setUp(self):
+"""
+Setup multiprocessing start method if needed. It needs to be
+done relatively late in order to work with the pytest-xdist
+plugin due to execnet usage.
+"""
+if os.environ.get("PORTAGE_MULTIPROCESSING_START_METHOD") == "spawn":
+multiprocessing.set_start_method("spawn", force=True)
+
 def assertRaisesMsg(self, msg, excClass, callableObj, *args, **kwargs):
 """Fail unless an exception of class excClass is thrown
 by callableObj when invoked with arguments args and keyword

diff --git a/lib/portage/tests/env/config/test_PortageModulesFile.py 
b/lib/portage/tests/env/config/test_PortageModulesFile.py
index f9879df687..bca86e0e6c 100644
--- a/lib/portage/tests/env/config/test_PortageModulesFile.py
+++ b/lib/portage/tests/env/config/test_PortageModulesFile.py
@@ -1,4 +1,4 @@
-# Copyright 2006-2009 Gentoo Foundation
+# Copyright 2006-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from portage import os
@@ -13,6 +13,7 @@ class PortageModulesFileTestCase(TestCase):
 modules = ["spanky", "zmedico", "antarus", "ricer", "5", "6"]
 
 def setUp(self):
+super().setUp()
 self.items = {}
 for k, v in zip(self.keys + self.invalid_keys, self.modules):
 self.items[k] = v

diff --git a/lib/portage/tests/news/test_NewsItem.py 
b/lib/portage/tests/news/test_NewsItem.py
index a7903f07e4..7a8393c51f 100644
--- a/lib/portage/tests/news/test_NewsItem.py
+++ b/lib/portage/tests/news/test_NewsItem.py
@@ -1,5 +1,5 @@
 # test_NewsItem.py -- Portage Unit Testing Functionality
-# Copyright 2007-2023 Gentoo Authors
+# Copyright 2007-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from portage.tests import TestCase
@@ -114,6 +114,7 @@ class NewsItemTestCase(TestCase):
 }
 
 def setUp(self) -> None:
+super().setUp()
 self.profile_base = "/var/db/repos/gentoo/profiles/default-linux"
 self.profile = f"{self.profile_base}/x86/2007.0/"

[gentoo-commits] proj/portage:master commit in: lib/portage/package/ebuild/, lib/_emerge/, lib/portage/util/futures/_asyncio/, ...

2024-02-12 Thread Zac Medico
commit: 5c528b1cf44f30d80a3ca5620a810e4fe2bd66f1
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Feb 13 04:47:53 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Tue Feb 13 05:02:14 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=5c528b1c

Revert "EbuildPhase: async_check_locale"

This reverts commit c95fc64abf9698263090b3ffd4a056e989dd2be1
since we had assumed EbuildMetadataPhase._start would serialize
access to the portdbapi doebuild_settings attribute, and that
assumption broke when _async_start was introduced in order to
call async_check_locale.

Bug: https://bugs.gentoo.org/923841
Bug: https://bugs.gentoo.org/924319
Signed-off-by: Zac Medico  gentoo.org>

 lib/_emerge/EbuildMetadataPhase.py| 21 
 lib/_emerge/EbuildPhase.py| 28 +--
 lib/portage/package/ebuild/config.py  | 26 ++---
 lib/portage/util/futures/_asyncio/__init__.py |  9 -
 lib/portage/util/locale.py| 28 +--
 5 files changed, 25 insertions(+), 87 deletions(-)

diff --git a/lib/_emerge/EbuildMetadataPhase.py 
b/lib/_emerge/EbuildMetadataPhase.py
index 53b7ad9624..f4f685e81c 100644
--- a/lib/_emerge/EbuildMetadataPhase.py
+++ b/lib/_emerge/EbuildMetadataPhase.py
@@ -8,14 +8,12 @@ import portage
 
 portage.proxy.lazyimport.lazyimport(
 globals(),
-"_emerge.EbuildPhase:_setup_locale",
 "portage.package.ebuild._metadata_invalid:eapi_invalid",
 )
 from portage import os
 from portage import _encodings
 from portage import _unicode_decode
 from portage import _unicode_encode
-from portage.util.futures import asyncio
 
 import fcntl
 
@@ -46,12 +44,6 @@ class EbuildMetadataPhase(SubProcess):
 _files_dict = slot_dict_class(_file_names, prefix="")
 
 def _start(self):
-asyncio.ensure_future(
-self._async_start(), loop=self.scheduler
-).add_done_callback(self._async_start_done)
-
-async def _async_start(self):
-
 ebuild_path = self.ebuild_hash.location
 
 with open(
@@ -83,9 +75,6 @@ class EbuildMetadataPhase(SubProcess):
 settings.setcpv(self.cpv)
 settings.configdict["pkg"]["EAPI"] = parsed_eapi
 
-# This requires above setcpv and EAPI setup.
-await _setup_locale(self.settings)
-
 debug = settings.get("PORTAGE_DEBUG") == "1"
 master_fd = None
 slave_fd = None
@@ -150,16 +139,6 @@ class EbuildMetadataPhase(SubProcess):
 
 self._proc = retval
 
-def _async_start_done(self, future):
-future.cancelled() or future.result()
-if future.cancelled():
-self.cancel()
-self._was_cancelled()
-
-if self.returncode is not None:
-self._unregister()
-self.wait()
-
 def _output_handler(self):
 while True:
 buf = self._read_buf(self._files.ebuild)

diff --git a/lib/_emerge/EbuildPhase.py b/lib/_emerge/EbuildPhase.py
index c8caf73722..c81bf54a81 100644
--- a/lib/_emerge/EbuildPhase.py
+++ b/lib/_emerge/EbuildPhase.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2024 Gentoo Authors
+# Copyright 1999-2021 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import functools
@@ -24,7 +24,6 @@ from portage.package.ebuild.prepare_build_dirs import (
 _prepare_fake_distdir,
 _prepare_fake_filesdir,
 )
-from portage.eapi import _get_eapi_attrs
 from portage.util import writemsg, ensure_dirs
 from portage.util._async.AsyncTaskFuture import AsyncTaskFuture
 from portage.util._async.BuildLogger import BuildLogger
@@ -55,34 +54,12 @@ portage.proxy.lazyimport.lazyimport(
 + "_post_src_install_write_metadata,"
 + "_preinst_bsdflags",
 "portage.util.futures.unix_events:_set_nonblocking",
-"portage.util.locale:async_check_locale,split_LC_ALL",
 )
 from portage import os
 from portage import _encodings
 from portage import _unicode_encode
 
 
-async def _setup_locale(settings):
-eapi_attrs = _get_eapi_attrs(settings["EAPI"])
-if eapi_attrs.posixish_locale:
-split_LC_ALL(settings)
-settings["LC_COLLATE"] = "C"
-# check_locale() returns None when check can not be executed.
-if await async_check_locale(silent=True, env=settings.environ()) is 
False:
-# try another locale
-for l in ("C.UTF-8", "en_US.UTF-8", "en_GB.UTF-8", "C"):
-settings["LC_CTYPE"] = l
-if await async_check_locale(silent=True, 
env=settings.environ()):
-# TODO: output the following only once
-# writemsg(
-# _("!!! LC_CTYPE unsupported, using %s instead\n")
-# % self.settings["LC_CTYPE"]
-# )
-break
-else:
-raise AssertionError("C locale did not pass the test!")
-
-
 class EbuildPhase(CompositeTask):

[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-11 Thread Zac Medico
commit: 419cce79f9082308c848df0a98f367de4d1c50a3
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb 11 21:58:10 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Mon Feb 12 07:56:10 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=419cce79

process._exec: Use _start_fork for os.fork() error handling

Use _start_fork for os.fork() error handling, ensuring
that if exec fails then the child process will display
a traceback before it exits via os._exit to suppress any
finally blocks from parent's call stack (bug 345289).

Bug: https://bugs.gentoo.org/345289
Bug: https://bugs.gentoo.org/916566
Bug: https://bugs.gentoo.org/924313
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/process.py | 259 -
 1 file changed, 151 insertions(+), 108 deletions(-)

diff --git a/lib/portage/process.py b/lib/portage/process.py
index 6cad250e34..f4758c824c 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -961,7 +961,13 @@ def _exec(
 if filename is not None:
 libc = LoadLibrary(filename)
 if libc is not None:
-try:
+# unshare() may not be supported by libc
+if not hasattr(libc, "unshare"):
+unshare_net = False
+unshare_ipc = False
+unshare_mount = False
+unshare_pid = False
+else:
 # Since a failed unshare call could corrupt process
 # state, first validate that the call can succeed.
 # The parent process should call _unshare_validate
@@ -992,117 +998,154 @@ def _exec(
 )
 else:
 if unshare_pid:
-main_child_pid = os.fork()
-if main_child_pid == 0:
-# pid namespace requires us to become init
-binary, myargs = (
-portage._python_interpreter,
-[
-portage._python_interpreter,
-os.path.join(portage._bin_path, 
"pid-ns-init"),
-_unicode_encode(
-"" if uid is None else str(uid)
-),
-_unicode_encode(
-"" if gid is None else str(gid)
-),
-_unicode_encode(
-""
-if groups is None
-else ",".join(
-str(group) for group in groups
-)
-),
-_unicode_encode(
-"" if umask is None else str(umask)
-),
-_unicode_encode(
-",".join(str(fd) for fd in 
fd_pipes)
-),
-binary,
-]
-+ myargs,
-)
-uid = None
-gid = None
-groups = None
-umask = None
-else:
-# Execute a supervisor process which will 
forward
-# signals to init and forward exit status to 
the
-# parent process. The supervisor process runs 
in
-# the global pid namespace, so skip /proc 
remount
-# and other setup that's intended only for the
-# init process.
-binary, myargs = portage._python_interpreter, [
+# pid namespace requires us to become init
+binary, myargs = (
+portage._python_interpreter,
+[
 portage._python_interpreter,
 os.path.join(portage._bin_path, 
"pid-ns-init"),
-str(main_child_pid),
+_unicode_encode("" if uid is None else 
str(uid)),
+_unicode_encode("" if gid is None else 
str(gid)),
+

[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-11 Thread Zac Medico
commit: a1024a6d02ca3a55f86525e0d8d5089e754d3713
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb 11 05:38:58 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Feb 11 19:46:55 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=a1024a6d

spawn_wrapper: Make pre_exec function picklable

Local functions are unpicklable, which triggered this error
with the multiprocessing "spawn" start method:

AttributeError: Can't pickle local object 
'spawn_wrapper.__call__.._pre_exec'

Bug: https://bugs.gentoo.org/924273
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/_selinux.py | 17 -
 lib/portage/process.py  | 26 ++
 2 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/lib/portage/_selinux.py b/lib/portage/_selinux.py
index bf6ad24895..5ae1b4e715 100644
--- a/lib/portage/_selinux.py
+++ b/lib/portage/_selinux.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2020 Gentoo Authors
+# Copyright 1999-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 # Don't use the unicode-wrapped os and shutil modules here since
@@ -6,6 +6,7 @@
 import os
 import shutil
 import warnings
+from functools import partial
 
 try:
 import selinux
@@ -134,14 +135,12 @@ class spawn_wrapper:
 
 def __call__(self, *args, **kwargs):
 if self._con is not None:
-pre_exec = kwargs.get("pre_exec")
-
-def _pre_exec():
-if pre_exec is not None:
-pre_exec()
-setexec(self._con)
-
-kwargs["pre_exec"] = _pre_exec
+pre_exec = partial(setexec, self._con)
+kwargs["pre_exec"] = (
+portage.process._chain_pre_exec_fns(pre_exec, 
kwargs["pre_exec"])
+if kwargs.get("pre_exec")
+else pre_exec
+)
 
 return self._spawn_func(*args, **kwargs)
 

diff --git a/lib/portage/process.py b/lib/portage/process.py
index a33e7b4747..6cad250e34 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -18,7 +18,7 @@ import os as _os
 import warnings
 
 from dataclasses import dataclass
-from functools import lru_cache
+from functools import lru_cache, partial
 from typing import Any, Optional, Callable, Union
 
 from portage import os
@@ -1383,18 +1383,28 @@ def _start_fork(
 return pid
 
 
-class _setup_pipes_after_fork:
-def __init__(self, target, fd_pipes):
+class _chain_pre_exec_fns:
+"""
+Wraps a target function to call pre_exec functions just before
+the original target function.
+"""
+
+def __init__(self, target, *args):
 self._target = target
-self._fd_pipes = fd_pipes
+self._pre_exec_fns = args
 
 def __call__(self, *args, **kwargs):
-for fd in set(self._fd_pipes.values()):
-os.set_inheritable(fd, True)
-_setup_pipes(self._fd_pipes, close_fds=False, inheritable=True)
+for pre_exec in self._pre_exec_fns:
+pre_exec()
 return self._target(*args, **kwargs)
 
 
+def _setup_pipes_after_fork(fd_pipes):
+for fd in set(fd_pipes.values()):
+os.set_inheritable(fd, True)
+_setup_pipes(fd_pipes, close_fds=False, inheritable=True)
+
+
 def _start_proc(
 target: Callable[..., None],
 args: Optional[tuple[Any, ...]] = (),
@@ -1419,7 +1429,7 @@ def _start_proc(
 # which ForkProcess does not handle because its target
 # function does not necessarily exec.
 if fd_pipes and multiprocessing.get_start_method() == "fork":
-target = _setup_pipes_after_fork(target, fd_pipes)
+target = _chain_pre_exec_fns(target, partial(_setup_pipes_after_fork, 
fd_pipes))
 fd_pipes = None
 
 proc = ForkProcess(



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/dep/, lib/portage/dep/

2024-02-10 Thread Zac Medico
commit: 12f6056da88041f82a9c9dfc23ee0eab39077782
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb 11 04:12:45 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Feb 11 04:36:22 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=12f6056d

_overlap_dnf: deduplicate any-of blocks

Duplicate any-of blocks are eliminated since DNF expansion
of duplicates is nonsensical.

Bug: https://bugs.gentoo.org/891137
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/dep/dep_check.py  | 17 ++---
 lib/portage/tests/dep/test_overlap_dnf.py | 31 +--
 2 files changed, 39 insertions(+), 9 deletions(-)

diff --git a/lib/portage/dep/dep_check.py b/lib/portage/dep/dep_check.py
index 5ca0995a87..c361ee59e2 100644
--- a/lib/portage/dep/dep_check.py
+++ b/lib/portage/dep/dep_check.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2020 Gentoo Authors
+# Copyright 2010-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 __all__ = ["dep_check", "dep_eval", "dep_wordreduce", "dep_zapdeps"]
@@ -963,7 +963,8 @@ def _overlap_dnf(dep_struct):
 order to minimize the number of packages chosen to satisfy cases like
 "|| ( foo bar ) || ( bar baz )" as in bug #632026. Non-overlapping
 groups are excluded from the conversion, since DNF leads to exponential
-explosion of the formula.
+explosion of the formula. Duplicate || groups are eliminated since
+DNF expansion of duplicates is nonsensical (bug #891137).
 
 When dep_struct does not contain any overlapping groups, no DNF
 conversion will be performed, and dep_struct will be returned as-is.
@@ -1021,7 +1022,17 @@ def _overlap_dnf(dep_struct):
 if len(disjunctions) > 1:
 overlap = True
 # convert overlapping disjunctions to DNF
-result.extend(_dnf_convert(sorted(disjunctions.values(), 
key=order_key)))
+dedup_set = set()
+unique_disjunctions = []
+for x in sorted(disjunctions.values(), key=order_key):
+dep_repr = portage.dep.paren_enclose(x, opconvert=True)
+if dep_repr not in dedup_set:
+dedup_set.add(dep_repr)
+unique_disjunctions.append(x)
+if len(unique_disjunctions) > 1:
+result.extend(_dnf_convert(unique_disjunctions))
+else:
+result.extend(unique_disjunctions)
 else:
 # pass through non-overlapping disjunctions
 result.append(disjunctions.popitem()[1])

diff --git a/lib/portage/tests/dep/test_overlap_dnf.py 
b/lib/portage/tests/dep/test_overlap_dnf.py
index 77352021a9..7fd1cfe7dd 100644
--- a/lib/portage/tests/dep/test_overlap_dnf.py
+++ b/lib/portage/tests/dep/test_overlap_dnf.py
@@ -1,4 +1,4 @@
-# Copyright 2017-2023 Gentoo Authors
+# Copyright 2017-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from portage.tests import TestCase
@@ -51,22 +51,41 @@ class OverlapDNFTestCase(TestCase):
 class DuplicateOverlapDNFTestCase(TestCase):
 def testDuplicateOverlapDNF(self):
 """
-Demonstrate unnecessary DNF expansion for duplicate
-any-of blocks as in bug 891137.
+Demonstrate deduplication of any-of blocks, preventing unnecessary
+DNF expansion for duplicate any-of blocks as in bug 891137.
 """
 test_cases = (
+("|| ( cat/A cat/B ) || ( cat/A cat/B )", [["||", "cat/A", 
"cat/B"]]),
 (
-"|| ( cat/A cat/B ) || ( cat/A cat/B )",
+"|| ( cat/A cat/B ) cat/E || ( cat/C cat/D ) || ( cat/A cat/B 
)",
+["cat/E", ["||", "cat/A", "cat/B"], ["||", "cat/C", "cat/D"]],
+),
+(
+"|| ( cat/A cat/B ) cat/D || ( cat/B cat/C ) || ( cat/A cat/B 
)",
 [
+"cat/D",
 [
 "||",
-["cat/A", "cat/A"],
 ["cat/A", "cat/B"],
-["cat/B", "cat/A"],
+["cat/A", "cat/C"],
 ["cat/B", "cat/B"],
+["cat/B", "cat/C"],
 ],
 ],
 ),
+(
+"|| ( cat/A cat/B ) || ( cat/C cat/D )  || ( ( cat/B cat/E ) 
cat/F ) || ( cat/A cat/B )",
+[
+[
+"||",
+["cat/A", "cat/B", "cat/E"],
+["cat/A", "cat/F"],
+["cat/B", "cat/B", "cat/E"],
+["cat/B", "cat/F"],
+],
+["||", "cat/C", "cat/D"],
+],
+),
 )
 
 for dep_str, result in test_cases:



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/gpkg/

2024-02-10 Thread Zac Medico
commit: e02feddd148f4a4a5854edd81b9aa67844d8956f
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb 11 04:31:41 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Feb 11 04:32:27 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=e02feddd

test_gpkg_metadata_url_case: fix string format for pyupgrade

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/gpkg/test_gpkg_metadata_url.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/portage/tests/gpkg/test_gpkg_metadata_url.py 
b/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
index 3773049227..e9f4111279 100644
--- a/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
+++ b/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
@@ -77,7 +77,7 @@ class test_gpkg_metadata_url_case(TestCase):
 test_gpkg.compress(os.path.join(tmpdir, "orig"), meta)
 
 meta_from_url = test_gpkg.get_metadata_url(
-"http://{0}:{1}/test.gpkg.tar".format(*server.server_address)
+"http://{}:{}/test.gpkg.tar".format(*server.server_address)
 )
 
 self.assertEqual(meta, meta_from_url)
@@ -148,7 +148,7 @@ 
IkCfAP49AOYjzuQPP0n5P0SGCINnAVEXN7QLQ4PurY/lt7cT2gEAq01stXjFhrz5
 self.assertRaises(
 InvalidSignature,
 test_gpkg.get_metadata_url,
-
"http://{0}:{1}/test-2.gpkg.tar".format(*server.server_address),
+"http://{}:{}/test-2.gpkg.tar".format(*server.server_address),
 )
 finally:
 if gpg is not None:



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/dep/

2024-02-10 Thread Zac Medico
commit: 15c173dcea2401a13cfb3313918c77d7dbde133d
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb 11 03:28:27 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Feb 11 03:29:23 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=15c173dc

DuplicateOverlapDNFTestCase: Add test for bug 891137

Bug: https://bugs.gentoo.org/891137
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/dep/test_overlap_dnf.py | 30 +-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/lib/portage/tests/dep/test_overlap_dnf.py 
b/lib/portage/tests/dep/test_overlap_dnf.py
index dfeded3b40..77352021a9 100644
--- a/lib/portage/tests/dep/test_overlap_dnf.py
+++ b/lib/portage/tests/dep/test_overlap_dnf.py
@@ -1,4 +1,4 @@
-# Copyright 2017 Gentoo Foundation
+# Copyright 2017-2023 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from portage.tests import TestCase
@@ -46,3 +46,31 @@ class OverlapDNFTestCase(TestCase):
 _overlap_dnf(use_reduce(dep_str, token_class=Atom, 
opconvert=True)),
 result,
 )
+
+
+class DuplicateOverlapDNFTestCase(TestCase):
+def testDuplicateOverlapDNF(self):
+"""
+Demonstrate unnecessary DNF expansion for duplicate
+any-of blocks as in bug 891137.
+"""
+test_cases = (
+(
+"|| ( cat/A cat/B ) || ( cat/A cat/B )",
+[
+[
+"||",
+["cat/A", "cat/A"],
+["cat/A", "cat/B"],
+["cat/B", "cat/A"],
+["cat/B", "cat/B"],
+],
+],
+),
+)
+
+for dep_str, result in test_cases:
+self.assertEqual(
+_overlap_dnf(use_reduce(dep_str, token_class=Atom, 
opconvert=True)),
+result,
+)



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/gpkg/

2024-02-10 Thread Zac Medico
commit: 9100b425a38f0f0631a4377da82f421ad87546b4
Author: Zac Medico  gentoo  org>
AuthorDate: Sat Feb 10 21:01:17 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sat Feb 10 21:02:13 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=9100b425

test_gpkg_metadata_url_case: fix pylint W0611: Unused import random 
(unused-import)

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/gpkg/test_gpkg_metadata_url.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lib/portage/tests/gpkg/test_gpkg_metadata_url.py 
b/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
index 271d3e4d5f..3773049227 100644
--- a/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
+++ b/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
@@ -2,7 +2,6 @@
 # Portage Unit Testing Functionality
 
 import io
-import random
 import tarfile
 import tempfile
 from functools import partial



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/gpkg/

2024-02-10 Thread Zac Medico
commit: d279a3a5c4907f2ed8c7dc52d9c240ee33a35987
Author: Zac Medico  gentoo  org>
AuthorDate: Sat Feb 10 20:41:01 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sat Feb 10 20:50:38 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=d279a3a5

test_gpkg_metadata_url_case: optimize httpd port allocation

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/gpkg/test_gpkg_metadata_url.py | 28 +++-
 1 file changed, 8 insertions(+), 20 deletions(-)

diff --git a/lib/portage/tests/gpkg/test_gpkg_metadata_url.py 
b/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
index 422ca7d3fa..271d3e4d5f 100644
--- a/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
+++ b/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
@@ -19,7 +19,7 @@ from portage.gpg import GPG
 
 
 class test_gpkg_metadata_url_case(TestCase):
-def httpd(self, directory, port, httpd_future):
+def httpd(self, directory, httpd_future):
 try:
 import http.server
 import socketserver
@@ -28,11 +28,11 @@ class test_gpkg_metadata_url_case(TestCase):
 
 Handler = partial(http.server.SimpleHTTPRequestHandler, 
directory=directory)
 
-with socketserver.TCPServer(("127.0.0.1", port), Handler) as httpd:
+with socketserver.TCPServer(("127.0.0.1", 0), Handler) as httpd:
 httpd_future.set_result(httpd)
 httpd.serve_forever()
 
-def start_http_server(self, directory, port):
+def start_http_server(self, directory):
 try:
 import threading
 except ImportError:
@@ -40,7 +40,7 @@ class test_gpkg_metadata_url_case(TestCase):
 
 httpd_future = Future()
 server = threading.Thread(
-target=self.httpd, args=(directory, port, httpd_future), 
daemon=True
+target=self.httpd, args=(directory, httpd_future), daemon=True
 )
 server.start()
 return httpd_future.result()
@@ -59,13 +59,7 @@ class test_gpkg_metadata_url_case(TestCase):
 server = None
 try:
 settings = playground.settings
-for _ in range(0, 5):
-port = random.randint(3, 6)
-try:
-server = self.start_http_server(tmpdir, port)
-except OSError:
-continue
-break
+server = self.start_http_server(tmpdir)
 
 orig_full_path = os.path.join(tmpdir, "orig/")
 os.makedirs(orig_full_path)
@@ -84,7 +78,7 @@ class test_gpkg_metadata_url_case(TestCase):
 test_gpkg.compress(os.path.join(tmpdir, "orig"), meta)
 
 meta_from_url = test_gpkg.get_metadata_url(
-"http://127.0.0.1:; + str(port) + "/test.gpkg.tar"
+"http://{0}:{1}/test.gpkg.tar".format(*server.server_address)
 )
 
 self.assertEqual(meta, meta_from_url)
@@ -111,13 +105,7 @@ class test_gpkg_metadata_url_case(TestCase):
 gpg = GPG(settings)
 gpg.unlock()
 
-for _ in range(0, 5):
-port = random.randint(3, 6)
-try:
-server = self.start_http_server(tmpdir, port)
-except OSError:
-continue
-break
+server = self.start_http_server(tmpdir)
 
 orig_full_path = os.path.join(tmpdir, "orig/")
 os.makedirs(orig_full_path)
@@ -161,7 +149,7 @@ 
IkCfAP49AOYjzuQPP0n5P0SGCINnAVEXN7QLQ4PurY/lt7cT2gEAq01stXjFhrz5
 self.assertRaises(
 InvalidSignature,
 test_gpkg.get_metadata_url,
-"http://127.0.0.1:; + str(port) + "/test-2.gpkg.tar",
+
"http://{0}:{1}/test-2.gpkg.tar".format(*server.server_address),
 )
 finally:
 if gpg is not None:



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/gpkg/

2024-02-10 Thread Zac Medico
commit: d320211266b95dc8cbea295cb234a607246d955f
Author: Zac Medico  gentoo  org>
AuthorDate: Sat Feb 10 20:25:28 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sat Feb 10 20:26:17 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=d3202112

test_gpkg_metadata_url_case: shutdown http server daemon threads

Bug: https://bugs.gentoo.org/924192
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/gpkg/test_gpkg_metadata_url.py | 15 ---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/lib/portage/tests/gpkg/test_gpkg_metadata_url.py 
b/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
index 857defe188..422ca7d3fa 100644
--- a/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
+++ b/lib/portage/tests/gpkg/test_gpkg_metadata_url.py
@@ -7,6 +7,7 @@ import tarfile
 import tempfile
 from functools import partial
 from os import urandom
+from concurrent.futures import Future
 
 from portage.gpkg import gpkg
 from portage import os
@@ -18,7 +19,7 @@ from portage.gpg import GPG
 
 
 class test_gpkg_metadata_url_case(TestCase):
-def httpd(self, directory, port):
+def httpd(self, directory, port, httpd_future):
 try:
 import http.server
 import socketserver
@@ -28,6 +29,7 @@ class test_gpkg_metadata_url_case(TestCase):
 Handler = partial(http.server.SimpleHTTPRequestHandler, 
directory=directory)
 
 with socketserver.TCPServer(("127.0.0.1", port), Handler) as httpd:
+httpd_future.set_result(httpd)
 httpd.serve_forever()
 
 def start_http_server(self, directory, port):
@@ -36,11 +38,12 @@ class test_gpkg_metadata_url_case(TestCase):
 except ImportError:
 self.skipTest("threading module not exists")
 
+httpd_future = Future()
 server = threading.Thread(
-target=self.httpd, args=(directory, port), daemon=True
+target=self.httpd, args=(directory, port, httpd_future), 
daemon=True
 )
 server.start()
-return server
+return httpd_future.result()
 
 def test_gpkg_get_metadata_url(self):
 playground = ResolverPlayground(
@@ -53,6 +56,7 @@ class test_gpkg_metadata_url_case(TestCase):
 }
 )
 tmpdir = tempfile.mkdtemp()
+server = None
 try:
 settings = playground.settings
 for _ in range(0, 5):
@@ -85,6 +89,8 @@ class test_gpkg_metadata_url_case(TestCase):
 
 self.assertEqual(meta, meta_from_url)
 finally:
+if server is not None:
+server.shutdown()
 shutil.rmtree(tmpdir)
 playground.cleanup()
 
@@ -99,6 +105,7 @@ class test_gpkg_metadata_url_case(TestCase):
 )
 tmpdir = tempfile.mkdtemp()
 gpg = None
+server = None
 try:
 settings = playground.settings
 gpg = GPG(settings)
@@ -159,5 +166,7 @@ 
IkCfAP49AOYjzuQPP0n5P0SGCINnAVEXN7QLQ4PurY/lt7cT2gEAq01stXjFhrz5
 finally:
 if gpg is not None:
 gpg.stop()
+if server is not None:
+server.shutdown()
 shutil.rmtree(tmpdir)
 playground.cleanup()



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-09 Thread Zac Medico
commit: 03be12ec5f46629fa928e5fcd45d3fe6745d5d0a
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  9 22:12:02 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sat Feb 10 06:08:59 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=03be12ec

GPG: Use threading.Event for thread safety

Use threading.Event for thread safety during GPG stop, and use the
wait method to improve responsiveness for stop requests.

Bug: https://bugs.gentoo.org/924192
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/gpg.py | 14 --
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/lib/portage/gpg.py b/lib/portage/gpg.py
index 3067872244..d8a4cfcfc4 100644
--- a/lib/portage/gpg.py
+++ b/lib/portage/gpg.py
@@ -1,10 +1,9 @@
-# Copyright 2001-2020 Gentoo Authors
+# Copyright 2001-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import subprocess
 import sys
 import threading
-import time
 
 from portage import os
 from portage.const import SUPPORTED_GENTOO_BINPKG_FORMATS
@@ -24,6 +23,7 @@ class GPG:
 """
 self.settings = settings
 self.thread = None
+self._terminated = None
 self.GPG_signing_base_command = self.settings.get(
 "BINPKG_GPG_SIGNING_BASE_COMMAND"
 )
@@ -73,6 +73,7 @@ class GPG:
 self.GPG_unlock_command = shlex_split(
 varexpand(self.GPG_unlock_command, mydict=self.settings)
 )
+self._terminated = threading.Event()
 self.thread = threading.Thread(target=self.gpg_keepalive, 
daemon=True)
 self.thread.start()
 
@@ -81,16 +82,17 @@ class GPG:
 Stop keepalive thread.
 """
 if self.thread is not None:
-self.keepalive = False
+self._terminated.set()
 
 def gpg_keepalive(self):
 """
 Call GPG unlock command every 5 mins to avoid the passphrase expired.
 """
 count = 0
-while self.keepalive:
+while not self._terminated.is_set():
 if count < 5:
-time.sleep(60)
+if self._terminated.wait(60):
+break
 count += 1
 continue
 else:
@@ -102,5 +104,5 @@ class GPG:
 stdout=subprocess.DEVNULL,
 stderr=subprocess.STDOUT,
 )
-if proc.wait() != os.EX_OK:
+if proc.wait() != os.EX_OK and not self._terminated.is_set():
 raise GPGException("GPG keepalive failed")



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/gpkg/, bin/, lib/_emerge/

2024-02-09 Thread Zac Medico
commit: d7115d18dada572b6b10d6be30d0fa7fb325a2c8
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  9 17:19:54 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sat Feb 10 06:08:58 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=d7115d18

GPG: Proactively stop to avoid "GPG keepalive failed" error in pypy ci jobs

This seems to help mitigate pypy ci job hangs like those fba76a545f2
triggered.

Bug: https://bugs.gentoo.org/924192
Signed-off-by: Zac Medico  gentoo.org>

 bin/quickpkg | 13 -
 lib/_emerge/actions.py   | 10 +++---
 lib/portage/tests/gpkg/test_gpkg_gpg.py  | 23 ++-
 lib/portage/tests/gpkg/test_gpkg_metadata_url.py |  5 -
 4 files changed, 41 insertions(+), 10 deletions(-)

diff --git a/bin/quickpkg b/bin/quickpkg
index 8443a00e64..c688c5312e 100755
--- a/bin/quickpkg
+++ b/bin/quickpkg
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Copyright 1999-2021 Gentoo Authors
+# Copyright 1999-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import argparse
@@ -341,10 +341,6 @@ def quickpkg_main(options, args, eout):
 portage.settings.features.remove("xattr")
 portage.settings.lock()
 
-if portage.settings.get("BINPKG_GPG_SIGNING_KEY", None):
-gpg = GPG(portage.settings)
-gpg.unlock()
-
 infos = {}
 infos["successes"] = []
 infos["missing"] = []
@@ -444,11 +440,18 @@ if __name__ == "__main__":
 def sigwinch_handler(signum, frame):
 lines, eout.term_columns = portage.output.get_term_size()
 
+gpg = None
+if portage.settings.get("BINPKG_GPG_SIGNING_KEY", None):
+gpg = GPG(portage.settings)
+gpg.unlock()
+
 signal.signal(signal.SIGWINCH, sigwinch_handler)
 try:
 retval = quickpkg_main(options, args, eout)
 finally:
 os.umask(old_umask)
 signal.signal(signal.SIGWINCH, signal.SIG_DFL)
+if gpg is not None:
+gpg.stop()
 global_event_loop().close()
 sys.exit(retval)

diff --git a/lib/_emerge/actions.py b/lib/_emerge/actions.py
index 2710c4856c..d36a799244 100644
--- a/lib/_emerge/actions.py
+++ b/lib/_emerge/actions.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2023 Gentoo Authors
+# Copyright 1999-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import collections
@@ -548,8 +548,10 @@ def action_build(
 mergelist_shown = True
 if retval != os.EX_OK:
 return retval
+return os.EX_OK
 
-else:
+gpg = None
+try:
 if not mergelist_shown:
 # If we haven't already shown the merge list above, at
 # least show warnings about missed updates and such.
@@ -688,8 +690,10 @@ def action_build(
 ldpath_mtimes,
 autoclean=1,
 )
-
 return retval
+finally:
+if gpg is not None:
+gpg.stop()
 
 
 def action_config(settings, trees, myopts, myfiles):

diff --git a/lib/portage/tests/gpkg/test_gpkg_gpg.py 
b/lib/portage/tests/gpkg/test_gpkg_gpg.py
index a2dc92150b..d7eae4a82b 100644
--- a/lib/portage/tests/gpkg/test_gpkg_gpg.py
+++ b/lib/portage/tests/gpkg/test_gpkg_gpg.py
@@ -1,4 +1,4 @@
-# Copyright Gentoo Foundation 2006-2020
+# Copyright 2022-2024 Gentoo Authors
 # Portage Unit Testing Functionality
 
 import io
@@ -26,6 +26,7 @@ class test_gpkg_gpg_case(TestCase):
 }
 )
 tmpdir = tempfile.mkdtemp()
+gpg = None
 
 try:
 settings = playground.settings
@@ -68,6 +69,8 @@ class test_gpkg_gpg_case(TestCase):
 InvalidSignature, binpkg_2.decompress, os.path.join(tmpdir, 
"test")
 )
 finally:
+if gpg is not None:
+gpg.stop()
 shutil.rmtree(tmpdir)
 playground.cleanup()
 
@@ -81,6 +84,7 @@ class test_gpkg_gpg_case(TestCase):
 }
 )
 tmpdir = tempfile.mkdtemp()
+gpg = None
 
 try:
 settings = playground.settings
@@ -112,6 +116,8 @@ class test_gpkg_gpg_case(TestCase):
 )
 
 finally:
+if gpg is not None:
+gpg.stop()
 shutil.rmtree(tmpdir)
 playground.cleanup()
 
@@ -133,6 +139,7 @@ class test_gpkg_gpg_case(TestCase):
 }
 )
 tmpdir = tempfile.mkdtemp()
+gpg = None
 
 try:
 settings = playground.settings
@@ -151,6 +158,8 @@ class test_gpkg_gpg_case(TestCase):
 binpkg_2 = gpkg(settings, "test", os.path.join(tmpdir, 
"test-1.gpkg.tar"))
 binpkg_2.decompress(os.path.join(tmpdir, "test"))
 finally:
+if gpg is not None:
+gpg.stop()
 shutil.rmtree(tmpdir)
 playground.cleanup()
 
@@ -165,6 

[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-09 Thread Zac Medico
commit: ad61940b03be2f24c0b54c1070a4923abe18e633
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  9 16:22:45 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb  9 23:52:11 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=ad61940b

gpkg: Less aggressive subprocess.Popen kill in order to avoid BrokenPipeError

Do not kill tar_stream_reader instances if we can successfully
close them, since that can trigger BrokenPipeError during
self.proc.stdin.close() calls, and this state is best avoided
because it's unclear how the caller should handle the error.

If a BrokenPipeError does occur then simply print a traceback
and hope that the corresponding file descriptor is closed
during garbage collection. Do not try to reverse the order
of self.proc.kill() and self.proc.stdin.close() as in commit
fba76a545f2 since that triggered pypy ci job hangs for which
no reliable solution has been found so far.

Bug: https://bugs.gentoo.org/923368
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/gpkg.py | 19 ++-
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/lib/portage/gpkg.py b/lib/portage/gpkg.py
index 031b3f87cb..f1d8f97f8e 100644
--- a/lib/portage/gpkg.py
+++ b/lib/portage/gpkg.py
@@ -1,7 +1,8 @@
-# Copyright 2001-2020 Gentoo Authors
+# Copyright 2001-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import tarfile
+import traceback
 import io
 import threading
 import subprocess
@@ -151,7 +152,10 @@ class tar_stream_writer:
 if self.proc is not None:
 self.killed = True
 self.proc.kill()
-self.proc.stdin.close()
+try:
+self.proc.stdin.close()
+except BrokenPipeError:
+traceback.print_exc()
 self.close()
 
 def _cmd_read_thread(self):
@@ -213,7 +217,7 @@ class tar_stream_writer:
 if self.proc is not None:
 self.proc.stdin.close()
 if self.proc.wait() != os.EX_OK:
-if not self.error:
+if not (self.killed or self.error):
 raise CompressorOperationFailed("compression failed")
 if self.read_thread.is_alive():
 self.read_thread.join()
@@ -349,7 +353,10 @@ class tar_stream_reader:
 if self.proc is not None:
 self.killed = True
 self.proc.kill()
-self.proc.stdin.close()
+try:
+self.proc.stdin.close()
+except BrokenPipeError:
+traceback.print_exc()
 self.close()
 
 def read(self, bufsize=-1):
@@ -986,11 +993,13 @@ class gpkg:
 try:
 image_safe = tar_safe_extract(image, "image")
 image_safe.extractall(decompress_dir)
+image_tar.close()
 except Exception as ex:
 writemsg(colorize("BAD", "!!!Extract failed."))
 raise
 finally:
-image_tar.kill()
+if not image_tar.closed:
+image_tar.kill()
 
 def update_metadata(self, metadata, new_basename=None, force=False):
 """



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/gpkg/

2024-02-09 Thread Zac Medico
commit: 3dac4f892479d6215c378f761505ab3d41a4b3ef
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  9 23:18:11 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb  9 23:18:26 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=3dac4f89

test_gpkg_path_case: Add missing playground cleanup

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/gpkg/test_gpkg_path.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/portage/tests/gpkg/test_gpkg_path.py 
b/lib/portage/tests/gpkg/test_gpkg_path.py
index fc57135949..19451e2e9b 100644
--- a/lib/portage/tests/gpkg/test_gpkg_path.py
+++ b/lib/portage/tests/gpkg/test_gpkg_path.py
@@ -1,4 +1,4 @@
-# Copyright Gentoo Foundation 2006
+# Copyright 2022-2024 Gentoo Authors
 # Portage Unit Testing Functionality
 
 import tempfile
@@ -308,6 +308,7 @@ class test_gpkg_path_case(TestCase):
 self.assertEqual(r, ())
 finally:
 shutil.rmtree(tmpdir)
+playground.cleanup()
 
 def test_gpkg_long_filename(self):
 playground = ResolverPlayground(



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/util/

2024-02-09 Thread Zac Medico
commit: 8dd69569b968ca0193b131c393e70855015a50dd
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  9 22:19:22 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb  9 22:19:35 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=8dd69569

ManifestTestCase: Fix tempdir removal

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/util/test_manifest.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lib/portage/tests/util/test_manifest.py 
b/lib/portage/tests/util/test_manifest.py
index 3412d568d7..2d41b9fc97 100644
--- a/lib/portage/tests/util/test_manifest.py
+++ b/lib/portage/tests/util/test_manifest.py
@@ -11,7 +11,8 @@ from portage.tests import TestCase
 
 class ManifestTestCase(TestCase):
 def test_simple_addFile(self):
-tempdir = Path(tempfile.mkdtemp()) / "app-portage" / "diffball"
+base_tempdir = tempfile.mkdtemp()
+tempdir = Path(base_tempdir) / "app-portage" / "diffball"
 manifest = Manifest(str(tempdir), required_hashes=["SHA512", 
"BLAKE2B"])
 
 (tempdir / "files").mkdir(parents=True)
@@ -30,4 +31,4 @@ class ManifestTestCase(TestCase):
 manifest.getFileData("AUX", "test.patch", "SHA512"),
 
"e30d069dcf284cbcb2d5685f03ca362469026b469dec4f8655d0c9a2bf317f5d9f68f61855ea403f4959bc0b9c003ae824fb9d6ab2472a739950623523af9da9",
 )
-shutil.rmtree(str(tempdir))
+shutil.rmtree(base_tempdir)



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/resolver/

2024-02-09 Thread Zac Medico
commit: be37f0761752f13a855aed66fa6e49e2f7211a0f
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  9 21:36:02 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb  9 21:36:28 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=be37f076

EAPITestCase: Disable playground debug so tempdir is cleaned up

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/resolver/test_eapi.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/portage/tests/resolver/test_eapi.py 
b/lib/portage/tests/resolver/test_eapi.py
index 5d425ccdb9..32dcb49895 100644
--- a/lib/portage/tests/resolver/test_eapi.py
+++ b/lib/portage/tests/resolver/test_eapi.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2020 Gentoo Authors
+# Copyright 2010-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from portage.tests import TestCase
@@ -199,7 +199,7 @@ class EAPITestCase(TestCase):
 mergelist=["dev-libs/A-1.0", "dev-libs/B-1.0"],
 )
 
-playground = ResolverPlayground(ebuilds=ebuilds, debug=True)
+playground = ResolverPlayground(ebuilds=ebuilds)
 try:
 playground.run_TestCase(test_case)
 self.assertEqual(test_case.test_success, True, test_case.fail_msg)



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/glsa/

2024-02-09 Thread Zac Medico
commit: 9cbc53ad6c483500c949c1acd70c6cbb2d7cee86
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  9 21:38:22 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb  9 21:38:39 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=9cbc53ad

SecuritySetTestCase: Disable playground debug so tempdir is cleaned up

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/glsa/test_security_set.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/portage/tests/glsa/test_security_set.py 
b/lib/portage/tests/glsa/test_security_set.py
index a0ba1e5b45..1206d9f80f 100644
--- a/lib/portage/tests/glsa/test_security_set.py
+++ b/lib/portage/tests/glsa/test_security_set.py
@@ -1,4 +1,4 @@
-# Copyright 2013-2023 Gentoo Authors
+# Copyright 2013-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 
@@ -226,7 +226,7 @@ class SecuritySetTestCase(TestCase):
 # Give each GLSA a clean slate
 for glsa in glsas:
 playground = ResolverPlayground(
-ebuilds=ebuilds, installed=installed, world=world, debug=True
+ebuilds=ebuilds, installed=installed, world=world, debug=False
 )
 
 try:



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/util/

2024-02-09 Thread Zac Medico
commit: d702c7684093dccae87df080d0b7701dddf95091
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  9 21:27:50 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb  9 21:30:10 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=d702c768

ManifestTestCase: Remove tempdir

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/util/test_manifest.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/portage/tests/util/test_manifest.py 
b/lib/portage/tests/util/test_manifest.py
index 49bcbc1a54..3412d568d7 100644
--- a/lib/portage/tests/util/test_manifest.py
+++ b/lib/portage/tests/util/test_manifest.py
@@ -1,6 +1,7 @@
-# Copyright 2022 Gentoo Authors
+# Copyright 2022-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
+import shutil
 import tempfile
 
 from pathlib import Path
@@ -29,3 +30,4 @@ class ManifestTestCase(TestCase):
 manifest.getFileData("AUX", "test.patch", "SHA512"),
 
"e30d069dcf284cbcb2d5685f03ca362469026b469dec4f8655d0c9a2bf317f5d9f68f61855ea403f4959bc0b9c003ae824fb9d6ab2472a739950623523af9da9",
 )
+shutil.rmtree(str(tempdir))



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/update/

2024-02-09 Thread Zac Medico
commit: 091e204c2c3d66180cfe59c310291f15ef02e2bb
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  9 21:17:49 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb  9 21:29:40 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=091e204c

MoveEntTestCase: Disable playground debug so tempdir is cleaned up

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/update/test_move_ent.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/portage/tests/update/test_move_ent.py 
b/lib/portage/tests/update/test_move_ent.py
index 169b892a30..fe968f12a0 100644
--- a/lib/portage/tests/update/test_move_ent.py
+++ b/lib/portage/tests/update/test_move_ent.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2021 Gentoo Authors
+# Copyright 2012-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import sys
@@ -287,7 +287,7 @@ class MoveEntTestCase(TestCase):
 f'FEATURES="binpkg-multi-instance 
pkgdir-index-trusted"',
 ),
 },
-debug=True,
+debug=False,
 )
 
 settings = playground.settings



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/dbapi/

2024-02-09 Thread Zac Medico
commit: fc6cace9fddfa3a2c5567e35156b2ee7ef39dce1
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  9 21:03:55 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb  9 21:08:32 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=fc6cace9

AuxdbTestCase: Add missing playground cleanup

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/dbapi/test_auxdb.py | 67 ---
 1 file changed, 38 insertions(+), 29 deletions(-)

diff --git a/lib/portage/tests/dbapi/test_auxdb.py 
b/lib/portage/tests/dbapi/test_auxdb.py
index c11eed73e8..0de0123a5f 100644
--- a/lib/portage/tests/dbapi/test_auxdb.py
+++ b/lib/portage/tests/dbapi/test_auxdb.py
@@ -1,4 +1,4 @@
-# Copyright 2020-2023 Gentoo Authors
+# Copyright 2020-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import functools
@@ -64,41 +64,50 @@ class AuxdbTestCase(TestCase):
 user_config={"modules": (f"portdbapi.auxdbmodule = 
{auxdbmodule}",)},
 )
 
-portdb = playground.trees[playground.eroot]["porttree"].dbapi
-metadata_keys = ["DEFINED_PHASES", "DEPEND", "EAPI", "INHERITED"]
-
-test_func = functools.partial(
-self._run_test_mod_async, ebuilds, metadata_keys, portdb
-)
-
-results = test_func()
-
-self._compare_results(
-ebuilds, eclass_defined_phases, eclass_depend, ebuild_inherited, 
results
-)
+try:
+portdb = playground.trees[playground.eroot]["porttree"].dbapi
+metadata_keys = ["DEFINED_PHASES", "DEPEND", "EAPI", "INHERITED"]
 
-loop = asyncio._wrap_loop()
-picklable_or_fork = picklable or multiprocessing.get_start_method == 
"fork"
-if picklable_or_fork:
-results = loop.run_until_complete(
-loop.run_in_executor(ForkExecutor(), test_func)
+test_func = functools.partial(
+self._run_test_mod_async, ebuilds, metadata_keys, portdb
 )
 
+results = test_func()
+
 self._compare_results(
 ebuilds, eclass_defined_phases, eclass_depend, 
ebuild_inherited, results
 )
 
-auxdb = portdb.auxdb[portdb.getRepositoryPath("test_repo")]
-cpv = next(iter(ebuilds))
-
-modify_auxdb = functools.partial(self._modify_auxdb, auxdb, cpv)
-
-if multiproc and picklable_or_fork:
-loop.run_until_complete(loop.run_in_executor(ForkExecutor(), 
modify_auxdb))
-else:
-modify_auxdb()
-
-self.assertEqual(auxdb[cpv]["RESTRICT"], "test")
+loop = asyncio._wrap_loop()
+picklable_or_fork = picklable or multiprocessing.get_start_method 
== "fork"
+if picklable_or_fork:
+results = loop.run_until_complete(
+loop.run_in_executor(ForkExecutor(), test_func)
+)
+
+self._compare_results(
+ebuilds,
+eclass_defined_phases,
+eclass_depend,
+ebuild_inherited,
+results,
+)
+
+auxdb = portdb.auxdb[portdb.getRepositoryPath("test_repo")]
+cpv = next(iter(ebuilds))
+
+modify_auxdb = functools.partial(self._modify_auxdb, auxdb, cpv)
+
+if multiproc and picklable_or_fork:
+loop.run_until_complete(
+loop.run_in_executor(ForkExecutor(), modify_auxdb)
+)
+else:
+modify_auxdb()
+
+self.assertEqual(auxdb[cpv]["RESTRICT"], "test")
+finally:
+playground.cleanup()
 
 def _compare_results(
 self, ebuilds, eclass_defined_phases, eclass_depend, ebuild_inherited, 
results



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-09 Thread Sam James
commit: c4049323b3c4c04ccc56023fa31169da58b57831
Author: Sam James  gentoo  org>
AuthorDate: Fri Feb  9 08:47:31 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Fri Feb  9 08:51:17 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=c4049323

Revert "gpkg: on error, close stdin before killing external programs"

This reverts commit fba76a545f29fb8b529197c25c64700ef77413ae. This seems
to cause hangs in CI with pypy3.

Zac mentioned this at 
https://github.com/gentoo/portage/pull/1246#issuecomment-1935511379
and I can reproduce it with `tox -e pypy3-test` with PyPy 7.3.15.

Signed-off-by: Sam James  gentoo.org>

 lib/portage/gpkg.py | 8 
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/portage/gpkg.py b/lib/portage/gpkg.py
index 05f1b5f2a6..031b3f87cb 100644
--- a/lib/portage/gpkg.py
+++ b/lib/portage/gpkg.py
@@ -149,9 +149,9 @@ class tar_stream_writer:
 kill external program if any error happened in python
 """
 if self.proc is not None:
-self.proc.stdin.close()
-self.proc.kill()
 self.killed = True
+self.proc.kill()
+self.proc.stdin.close()
 self.close()
 
 def _cmd_read_thread(self):
@@ -347,9 +347,9 @@ class tar_stream_reader:
 kill external program if any error happened in python
 """
 if self.proc is not None:
-self.proc.stdin.close()
-self.proc.kill()
 self.killed = True
+self.proc.kill()
+self.proc.stdin.close()
 self.close()
 
 def read(self, bufsize=-1):



[gentoo-commits] proj/portage:master commit in: lib/portage/util/

2024-02-09 Thread Zac Medico
commit: ba33bc425363977eaf549a087b2469720a79e2a4
Author: Zac Medico  gentoo  org>
AuthorDate: Thu Feb  8 06:46:04 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb  9 08:19:00 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=ba33bc42

check_locale: Use multiprocessing.Process instead of os.fork()

Since os.fork() is unsafe in threaded processes, use
multiprocessing.Process instead. This way the fork will be
eliminated when the default multiprocessing start method
changes to "spawn".

TODO: Make async version of check_locale and call it from
EbuildPhase instead of config.environ(), since it's bad to
synchronously wait for the process in the main event loop
thread where config.environ() tends to be called.

Bug: https://bugs.gentoo.org/923841
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/util/locale.py | 56 +++---
 1 file changed, 33 insertions(+), 23 deletions(-)

diff --git a/lib/portage/util/locale.py b/lib/portage/util/locale.py
index a620dbd544..b5da8d949b 100644
--- a/lib/portage/util/locale.py
+++ b/lib/portage/util/locale.py
@@ -9,7 +9,8 @@ locale.
 
 import locale
 import logging
-import os
+import multiprocessing
+import sys
 import textwrap
 import traceback
 
@@ -96,6 +97,24 @@ def _check_locale(silent):
 return True
 
 
+def _set_and_check_locale(silent, env, mylocale):
+try:
+if env is not None:
+try:
+locale.setlocale(locale.LC_CTYPE, mylocale)
+except locale.Error:
+sys.exit(2)
+
+ret = _check_locale(silent)
+if ret is None:
+sys.exit(2)
+else:
+sys.exit(0 if ret else 1)
+except Exception:
+traceback.print_exc()
+sys.exit(2)
+
+
 def check_locale(silent=False, env=None):
 """
 Check whether the locale is sane. Returns True if it is, prints
@@ -116,29 +135,20 @@ def check_locale(silent=False, env=None):
 except KeyError:
 pass
 
-pid = os.fork()
-if pid == 0:
-try:
-if env is not None:
-try:
-locale.setlocale(locale.LC_CTYPE, 
portage._native_string(mylocale))
-except locale.Error:
-os._exit(2)
-
-ret = _check_locale(silent)
-if ret is None:
-os._exit(2)
-else:
-os._exit(0 if ret else 1)
-except Exception:
-traceback.print_exc()
-os._exit(2)
-
-pid2, ret = os.waitpid(pid, 0)
-assert pid == pid2
+# TODO: Make async version of check_locale and call it from
+# EbuildPhase instead of config.environ(), since it's bad to
+# synchronously wait for the process in the main event loop
+# thread where config.environ() tends to be called.
+proc = multiprocessing.Process(
+target=_set_and_check_locale,
+args=(silent, env, None if env is None else 
portage._native_string(mylocale)),
+)
+proc.start()
+proc.join()
+
 pyret = None
-if os.WIFEXITED(ret):
-ret = os.WEXITSTATUS(ret)
+if proc.exitcode >= 0:
+ret = proc.exitcode
 if ret != 2:
 pyret = ret == 0
 



[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/, lib/portage/util/, ...

2024-02-09 Thread Zac Medico
commit: c95fc64abf9698263090b3ffd4a056e989dd2be1
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  9 06:38:41 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb  9 08:19:00 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=c95fc64a

EbuildPhase: async_check_locale

Change config.environ() check_locale calls to async_check_locale
calls in the EbuildPhase _async_start method in order to eliminate
synchronous waiting for child processes in the main event loop
thread.

Bug: https://bugs.gentoo.org/923841
Signed-off-by: Zac Medico  gentoo.org>

 lib/_emerge/EbuildMetadataPhase.py| 21 
 lib/_emerge/EbuildPhase.py| 28 ++-
 lib/portage/package/ebuild/config.py  | 26 +++--
 lib/portage/util/futures/_asyncio/__init__.py |  9 +
 lib/portage/util/locale.py| 28 ++-
 5 files changed, 87 insertions(+), 25 deletions(-)

diff --git a/lib/_emerge/EbuildMetadataPhase.py 
b/lib/_emerge/EbuildMetadataPhase.py
index f4f685e81c..53b7ad9624 100644
--- a/lib/_emerge/EbuildMetadataPhase.py
+++ b/lib/_emerge/EbuildMetadataPhase.py
@@ -8,12 +8,14 @@ import portage
 
 portage.proxy.lazyimport.lazyimport(
 globals(),
+"_emerge.EbuildPhase:_setup_locale",
 "portage.package.ebuild._metadata_invalid:eapi_invalid",
 )
 from portage import os
 from portage import _encodings
 from portage import _unicode_decode
 from portage import _unicode_encode
+from portage.util.futures import asyncio
 
 import fcntl
 
@@ -44,6 +46,12 @@ class EbuildMetadataPhase(SubProcess):
 _files_dict = slot_dict_class(_file_names, prefix="")
 
 def _start(self):
+asyncio.ensure_future(
+self._async_start(), loop=self.scheduler
+).add_done_callback(self._async_start_done)
+
+async def _async_start(self):
+
 ebuild_path = self.ebuild_hash.location
 
 with open(
@@ -75,6 +83,9 @@ class EbuildMetadataPhase(SubProcess):
 settings.setcpv(self.cpv)
 settings.configdict["pkg"]["EAPI"] = parsed_eapi
 
+# This requires above setcpv and EAPI setup.
+await _setup_locale(self.settings)
+
 debug = settings.get("PORTAGE_DEBUG") == "1"
 master_fd = None
 slave_fd = None
@@ -139,6 +150,16 @@ class EbuildMetadataPhase(SubProcess):
 
 self._proc = retval
 
+def _async_start_done(self, future):
+future.cancelled() or future.result()
+if future.cancelled():
+self.cancel()
+self._was_cancelled()
+
+if self.returncode is not None:
+self._unregister()
+self.wait()
+
 def _output_handler(self):
 while True:
 buf = self._read_buf(self._files.ebuild)

diff --git a/lib/_emerge/EbuildPhase.py b/lib/_emerge/EbuildPhase.py
index c81bf54a81..c8caf73722 100644
--- a/lib/_emerge/EbuildPhase.py
+++ b/lib/_emerge/EbuildPhase.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2021 Gentoo Authors
+# Copyright 1999-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import functools
@@ -24,6 +24,7 @@ from portage.package.ebuild.prepare_build_dirs import (
 _prepare_fake_distdir,
 _prepare_fake_filesdir,
 )
+from portage.eapi import _get_eapi_attrs
 from portage.util import writemsg, ensure_dirs
 from portage.util._async.AsyncTaskFuture import AsyncTaskFuture
 from portage.util._async.BuildLogger import BuildLogger
@@ -54,12 +55,34 @@ portage.proxy.lazyimport.lazyimport(
 + "_post_src_install_write_metadata,"
 + "_preinst_bsdflags",
 "portage.util.futures.unix_events:_set_nonblocking",
+"portage.util.locale:async_check_locale,split_LC_ALL",
 )
 from portage import os
 from portage import _encodings
 from portage import _unicode_encode
 
 
+async def _setup_locale(settings):
+eapi_attrs = _get_eapi_attrs(settings["EAPI"])
+if eapi_attrs.posixish_locale:
+split_LC_ALL(settings)
+settings["LC_COLLATE"] = "C"
+# check_locale() returns None when check can not be executed.
+if await async_check_locale(silent=True, env=settings.environ()) is 
False:
+# try another locale
+for l in ("C.UTF-8", "en_US.UTF-8", "en_GB.UTF-8", "C"):
+settings["LC_CTYPE"] = l
+if await async_check_locale(silent=True, 
env=settings.environ()):
+# TODO: output the following only once
+# writemsg(
+# _("!!! LC_CTYPE unsupported, using %s instead\n")
+# % self.settings["LC_CTYPE"]
+# )
+break
+else:
+raise AssertionError("C locale did not pass the test!")
+
+
 class EbuildPhase(CompositeTask):
 __slots__ = ("actionmap", "fd_pipes", "phase", "settings") + 
("_ebuild_lock",)
 
@@ -94,6 +117,9 @@ class 

[gentoo-commits] proj/portage:master commit in: lib/portage/emaint/

2024-02-08 Thread Sam James
commit: cf9dfb8274a0bcce0776b53e5d184d5a8ce2f17d
Author: Steffen Winter  proton  me>
AuthorDate: Thu Feb  8 23:29:39 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Fri Feb  9 07:29:03 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=cf9dfb82

emaint: Print 'usage: ' only once in help message

`ArgumentParser` prepends 'usage: ' to the usage message already.

Signed-off-by: Steffen Winter  proton.me>
Closes: https://github.com/gentoo/portage/pull/1256
Signed-off-by: Sam James  gentoo.org>

 lib/portage/emaint/main.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/portage/emaint/main.py b/lib/portage/emaint/main.py
index 0c620a1a84..ad6eea359e 100644
--- a/lib/portage/emaint/main.py
+++ b/lib/portage/emaint/main.py
@@ -62,7 +62,7 @@ class OptionItem:
 
 
 def usage(module_controller):
-_usage = "usage: emaint [options] COMMAND"
+_usage = "emaint [options] COMMAND"
 
 desc = (
 "The emaint program provides an interface to system health "



[gentoo-commits] proj/portage:master commit in: lib/portage/util/elf/, lib/portage/dep/soname/, /

2024-02-08 Thread Sam James
commit: da6607f012572b4d13fdc6fd5ecd4a62d62a417b
Author: Matoro Mahri  matoro  tk>
AuthorDate: Tue Oct 31 21:00:34 2023 +
Commit: Sam James  gentoo  org>
CommitDate: Fri Feb  9 07:26:32 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=da6607f0

ELF: add entries for ARC machines

Signed-off-by: Matoro Mahri  matoro.tk>
Closes: https://github.com/gentoo/portage/pull/1166
Signed-off-by: Sam James  gentoo.org>

 NEWS|  1 +
 lib/portage/dep/soname/multilib_category.py | 10 ++
 lib/portage/util/elf/constants.py   |  5 +
 3 files changed, 16 insertions(+)

diff --git a/NEWS b/NEWS
index 220a0f4f7e..396723d8a8 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,7 @@ portage-3.0.62 (UNRELEASED)
 
 Features:
 * cnf: make.conf.example.arc: add for the arc arch.
+* ELF: add entries for ARC machines
 
 portage-3.0.61 (2024-01-05)
 --

diff --git a/lib/portage/dep/soname/multilib_category.py 
b/lib/portage/dep/soname/multilib_category.py
index 14a9eea770..baca439fd2 100644
--- a/lib/portage/dep/soname/multilib_category.py
+++ b/lib/portage/dep/soname/multilib_category.py
@@ -52,6 +52,11 @@ from portage.util.elf.constants import (
 EM_AARCH64,
 EM_ALPHA,
 EM_AMDGPU,
+EM_ARC,
+EM_ARC_COMPACT,
+EM_ARC_COMPACT2,
+EM_ARC_COMPACT3,
+EM_ARC_COMPACT3_64,
 EM_ARM,
 EM_ALTERA_NIOS2,
 EM_IA_64,
@@ -80,6 +85,11 @@ _machine_prefix_map = {
 EM_ALPHA: "alpha",
 EM_AMDGPU: "amdgpu",
 EM_ALTERA_NIOS2: "nios2",
+EM_ARC: "arc",
+EM_ARC_COMPACT: "arc",
+EM_ARC_COMPACT2: "arc",
+EM_ARC_COMPACT3: "arc",
+EM_ARC_COMPACT3_64: "arc",
 EM_ARM: "arm",
 EM_IA_64: "ia64",
 EM_LOONGARCH: "loong",

diff --git a/lib/portage/util/elf/constants.py 
b/lib/portage/util/elf/constants.py
index 022e78d776..9216a35353 100644
--- a/lib/portage/util/elf/constants.py
+++ b/lib/portage/util/elf/constants.py
@@ -31,12 +31,17 @@ EM_S390 = 22
 EM_ARM = 40
 EM_SH = 42
 EM_SPARCV9 = 43
+EM_ARC = 45
 EM_IA_64 = 50
 EM_X86_64 = 62
+EM_ARC_COMPACT = 93
 EM_ALTERA_NIOS2 = 113
 EM_AARCH64 = 183
+EM_ARC_COMPACT2 = 195
 EM_AMDGPU = 224
 EM_RISCV = 243
+EM_ARC_COMPACT3_64 = 253
+EM_ARC_COMPACT3 = 255
 EM_LOONGARCH = 258
 EM_ALPHA = 0x9026
 



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-08 Thread Sam James
commit: fba76a545f29fb8b529197c25c64700ef77413ae
Author: Matoro Mahri  matoro  tk>
AuthorDate: Wed Jan 31 20:25:40 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Fri Feb  9 07:08:22 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=fba76a54

gpkg: on error, close stdin before killing external programs

Otherwise, this may trigger a BrokenPipeError.  Sensitive to buffer
fullness, this was observed on 64k page size system immediately after
triggering E2BIG error from kernel.

Signed-off-by: Matoro Mahri  matoro.tk>
Signed-off-by: Sam James  gentoo.org>

 lib/portage/gpkg.py | 8 
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/portage/gpkg.py b/lib/portage/gpkg.py
index 031b3f87cb..05f1b5f2a6 100644
--- a/lib/portage/gpkg.py
+++ b/lib/portage/gpkg.py
@@ -149,9 +149,9 @@ class tar_stream_writer:
 kill external program if any error happened in python
 """
 if self.proc is not None:
-self.killed = True
-self.proc.kill()
 self.proc.stdin.close()
+self.proc.kill()
+self.killed = True
 self.close()
 
 def _cmd_read_thread(self):
@@ -347,9 +347,9 @@ class tar_stream_reader:
 kill external program if any error happened in python
 """
 if self.proc is not None:
-self.killed = True
-self.proc.kill()
 self.proc.stdin.close()
+self.proc.kill()
+self.killed = True
 self.close()
 
 def read(self, bufsize=-1):



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/process/

2024-02-08 Thread Sam James
commit: a308d831ceb1f1e7d0733700117cc18c8eca09c8
Author: Matoro Mahri  matoro  tk>
AuthorDate: Wed Jan 31 20:26:06 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Fri Feb  9 07:08:22 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=a308d831

tests: process: calculate size of E2BIG environment variable dynamically

The size of the command line required to trigger E2BIG response from the
kernel is defined as MAX_ARG_STRLEN, which is equal to 32 * PAGE_SIZE.
Therefore the minimum size required to trigger the expected error
response must be calculated dynamically based on PAGE_SIZE.

Bug: https://bugs.gentoo.org/923368
Signed-off-by: Matoro Mahri  matoro.tk>
Closes: https://github.com/gentoo/portage/pull/1246
Signed-off-by: Sam James  gentoo.org>

 lib/portage/tests/process/test_spawn_fail_e2big.py | 7 +--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/lib/portage/tests/process/test_spawn_fail_e2big.py 
b/lib/portage/tests/process/test_spawn_fail_e2big.py
index 7a00966302..abb1113fea 100644
--- a/lib/portage/tests/process/test_spawn_fail_e2big.py
+++ b/lib/portage/tests/process/test_spawn_fail_e2big.py
@@ -2,6 +2,7 @@
 # Distributed under the terms of the GNU General Public License v2
 
 import platform
+import resource
 
 import pytest
 
@@ -12,7 +13,9 @@ from portage.const import BASH_BINARY
 @pytest.mark.skipif(platform.system() != "Linux", reason="not Linux")
 def test_spawnE2big(capsys, tmp_path):
 env = dict()
-env["VERY_LARGE_ENV_VAR"] = "X" * 1024 * 256
+# Kernel MAX_ARG_STRLEN is defined as 32 * PAGE_SIZE
+max_arg_strlen_bytes = 32 * resource.getpagesize()
+env["VERY_LARGE_ENV_VAR"] = "X" * max_arg_strlen_bytes
 
 logfile = tmp_path / "logfile"
 echo_output = "Should never appear"
@@ -24,7 +27,7 @@ def test_spawnE2big(capsys, tmp_path):
 with open(logfile) as f:
 logfile_content = f.read()
 assert (
-"Largest environment variable: VERY_LARGE_ENV_VAR (262164 bytes)"
+f"Largest environment variable: VERY_LARGE_ENV_VAR 
({max_arg_strlen_bytes + 20} bytes)"
 in logfile_content
 )
 assert retval == 1



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/locks/

2024-02-07 Thread Zac Medico
commit: ca8d46112c5b0f7ab9f298915b1cee5a39523173
Author: Zac Medico  gentoo  org>
AuthorDate: Wed Feb  7 04:33:16 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb  7 15:31:30 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=ca8d4611

LockNonblockTestCase: Use multiprocessing.Process instead of os.fork()

Since os.fork() is unsafe in threaded processes, use
multiprocessing.Process instead. This way the fork will
be eliminated when the default multiprocessing start
method changes to "spawn".

Bug: https://bugs.gentoo.org/914876
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/locks/test_lock_nonblock.py | 52 +--
 1 file changed, 26 insertions(+), 26 deletions(-)

diff --git a/lib/portage/tests/locks/test_lock_nonblock.py 
b/lib/portage/tests/locks/test_lock_nonblock.py
index 9bb91b428e..d30dfe113b 100644
--- a/lib/portage/tests/locks/test_lock_nonblock.py
+++ b/lib/portage/tests/locks/test_lock_nonblock.py
@@ -1,6 +1,8 @@
 # Copyright 2011-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
+import multiprocessing
+import sys
 import tempfile
 import traceback
 
@@ -17,37 +19,35 @@ class LockNonblockTestCase(TestCase):
 try:
 path = os.path.join(tempdir, "lock_me")
 lock1 = portage.locks.lockfile(path)
-pid = os.fork()
-if pid == 0:
-portage.locks._close_fds()
-# Disable close_fds since we don't exec
-# (see _setup_pipes docstring).
-portage.process._setup_pipes({0: 0, 1: 1, 2: 2}, 
close_fds=False)
-rval = 2
-try:
-try:
-lock2 = portage.locks.lockfile(path, 
flags=os.O_NONBLOCK)
-except portage.exception.TryAgain:
-rval = os.EX_OK
-else:
-rval = 1
-portage.locks.unlockfile(lock2)
-except SystemExit:
-raise
-except:
-traceback.print_exc()
-finally:
-os._exit(rval)
-
-self.assertEqual(pid > 0, True)
-pid, status = os.waitpid(pid, 0)
-self.assertEqual(os.WIFEXITED(status), True)
-self.assertEqual(os.WEXITSTATUS(status), os.EX_OK)
+proc = multiprocessing.Process(target=self._lock_subprocess, 
args=(path,))
+proc.start()
+self.assertEqual(proc.pid > 0, True)
+proc.join()
+self.assertEqual(proc.exitcode, os.EX_OK)
 
 portage.locks.unlockfile(lock1)
 finally:
 shutil.rmtree(tempdir)
 
+@staticmethod
+def _lock_subprocess(path):
+portage.locks._close_fds()
+# Disable close_fds since we don't exec
+# (see _setup_pipes docstring).
+portage.process._setup_pipes({0: 0, 1: 1, 2: 2}, close_fds=False)
+rval = 2
+try:
+try:
+lock2 = portage.locks.lockfile(path, flags=os.O_NONBLOCK)
+except portage.exception.TryAgain:
+rval = os.EX_OK
+else:
+rval = 1
+portage.locks.unlockfile(lock2)
+except Exception:
+traceback.print_exc()
+sys.exit(rval)
+
 def testLockNonblock(self):
 self._testLockNonblock()
 



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-06 Thread Zac Medico
commit: 3e10368e52ecead86b75478ca448ef0f59333e3e
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb  4 04:34:45 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb  7 00:55:46 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=3e10368e

process.spawn: Use _start_proc for returnpid=False

This essentially completes the implementation of bug 916566,
eliminating os.fork() usage when "spawn" becomes the default
multiprocessing start method.

Bug: https://bugs.gentoo.org/916566
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/process.py | 35 +--
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/lib/portage/process.py b/lib/portage/process.py
index 1ca59ee594..a33e7b4747 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -586,10 +586,10 @@ def spawn(
 
 # Create a tee process, giving it our stdout and stderr
 # as well as the read end of the pipe.
-mypids.extend(
+mypids.append(
 spawn(
 ("tee", "-i", "-a", logfile),
-returnpid=True,
+returnproc=True,
 fd_pipes={0: pr, 1: fd_pipes[1], 2: fd_pipes[2]},
 )
 )
@@ -634,7 +634,7 @@ def spawn(
 # fork, so that the result is cached in the main process.
 bool(groups)
 
-start_func = _start_proc if returnproc else _start_fork
+start_func = _start_proc if returnproc or not returnpid else _start_fork
 
 pid = start_func(
 _exec_wrapper,
@@ -666,7 +666,7 @@ def spawn(
 # _start_proc returns a MultiprocessingProcess instance.
 return pid
 
-if not isinstance(pid, int):
+if returnpid and not isinstance(pid, int):
 raise AssertionError(f"fork returned non-integer: {repr(pid)}")
 
 # Add the pid to our local and the global pid lists.
@@ -687,6 +687,8 @@ def spawn(
 )
 return mypids
 
+loop = global_event_loop()
+
 # Otherwise we clean them up.
 while mypids:
 # Pull the last reader in the pipe chain. If all processes
@@ -695,25 +697,22 @@ def spawn(
 pid = mypids.pop(0)
 
 # and wait for it.
-retval = os.waitpid(pid, 0)[1]
+retval = loop.run_until_complete(pid.wait())
 
 if retval:
 # If it failed, kill off anything else that
 # isn't dead yet.
 for pid in mypids:
-# With waitpid and WNOHANG, only check the
-# first element of the tuple since the second
-# element may vary (bug #337465).
-if os.waitpid(pid, os.WNOHANG)[0] == 0:
-os.kill(pid, signal.SIGTERM)
-os.waitpid(pid, 0)
-
-# If it got a signal, return the signal that was sent.
-if retval & 0xFF:
-return (retval & 0xFF) << 8
-
-# Otherwise, return its exit code.
-return retval >> 8
+waiter = asyncio.ensure_future(pid.wait(), loop)
+try:
+loop.run_until_complete(
+asyncio.wait_for(asyncio.shield(waiter), 0.001)
+)
+except (TimeoutError, asyncio.TimeoutError):
+pid.terminate()
+loop.run_until_complete(waiter)
+
+return retval
 
 # Everything succeeded
 return 0



[gentoo-commits] proj/portage:master commit in: lib/portage/util/

2024-02-06 Thread Zac Medico
commit: fa8e8f1895ed889aece2f67725df55d6ccf127fb
Author: Zac Medico  gentoo  org>
AuthorDate: Sat Feb  3 23:41:45 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb  7 00:55:46 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=fa8e8f18

socks5: Migrate to spawn returnproc parameter

Bug: https://bugs.gentoo.org/916566
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/util/socks5.py | 36 +++-
 1 file changed, 19 insertions(+), 17 deletions(-)

diff --git a/lib/portage/util/socks5.py b/lib/portage/util/socks5.py
index fedb8599d5..6c68ff4106 100644
--- a/lib/portage/util/socks5.py
+++ b/lib/portage/util/socks5.py
@@ -1,10 +1,9 @@
 # SOCKSv5 proxy manager for network-sandbox
-# Copyright 2015-2021 Gentoo Authors
+# Copyright 2015-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import errno
 import os
-import signal
 import socket
 
 import portage.data
@@ -22,7 +21,8 @@ class ProxyManager:
 
 def __init__(self):
 self.socket_path = None
-self._pids = []
+self._proc = None
+self._proc_waiter = None
 
 def start(self, settings):
 """
@@ -51,9 +51,9 @@ class ProxyManager:
 spawn_kwargs.update(
 uid=portage_uid, gid=portage_gid, groups=userpriv_groups, 
umask=0o077
 )
-self._pids = spawn(
+self._proc = spawn(
 [_python_interpreter, server_bin, self.socket_path],
-returnpid=True,
+returnproc=True,
 **spawn_kwargs,
 )
 
@@ -61,12 +61,19 @@ class ProxyManager:
 """
 Stop the SOCKSv5 server.
 """
-for p in self._pids:
-os.kill(p, signal.SIGINT)
-os.waitpid(p, 0)
+if self._proc is not None:
+self._proc.terminate()
+loop = asyncio.get_event_loop()
+if self._proc_waiter is None:
+self._proc_waiter = asyncio.ensure_future(self._proc.wait(), 
loop)
+if loop.is_running():
+self._proc_waiter.add_done_callback(lambda future: 
future.result())
+else:
+loop.run_until_complete(self._proc_waiter)
 
 self.socket_path = None
-self._pids = []
+self._proc = None
+self._proc_waiter = None
 
 def is_running(self):
 """
@@ -80,16 +87,11 @@ class ProxyManager:
 """
 Wait for the proxy socket to become ready. This method is a coroutine.
 """
+if self._proc_waiter is None:
+self._proc_waiter = asyncio.ensure_future(self._proc.wait())
 
 while True:
-try:
-wait_retval = os.waitpid(self._pids[0], os.WNOHANG)
-except OSError as e:
-if e.errno == errno.EINTR:
-continue
-raise
-
-if wait_retval is not None and wait_retval != (0, 0):
+if self._proc_waiter.done():
 raise OSError(3, "No such process")
 
 try:



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-06 Thread Zac Medico
commit: 1ae4f574f69eca3146137ffb40c5afd8a4872777
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb  4 00:22:08 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb  7 00:55:46 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=1ae4f574

process.spawn: Enable returnpid warning for internals

All internal returnpid consumers have been migrated to
use the new returnproc parameter instead.

Bug: https://bugs.gentoo.org/916566
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/process.py | 11 +--
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/lib/portage/process.py b/lib/portage/process.py
index 20327b38bc..1ca59ee594 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -680,12 +680,11 @@ def spawn(
 # If the caller wants to handle cleaning up the processes, we tell
 # it about all processes that were created.
 if returnpid:
-if not portage._internal_caller:
-warnings.warn(
-"The portage.process.spawn returnpid paramenter is deprecated 
and replaced by returnproc",
-UserWarning,
-stacklevel=1,
-)
+warnings.warn(
+"The portage.process.spawn returnpid parameter is deprecated and 
replaced by returnproc",
+UserWarning,
+stacklevel=1,
+)
 return mypids
 
 # Otherwise we clean them up.



[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/, lib/portage/sync/modules/rsync/

2024-02-06 Thread Zac Medico
commit: 62332ee82b8b88fa5a65aafa7c221ccdaa7d65a8
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb  4 00:11:07 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb  7 00:55:46 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=62332ee8

RsyncSync: Migrate to spawn returnproc parameter

Bug: https://bugs.gentoo.org/916566
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/sync/modules/rsync/rsync.py   | 40 +--
 lib/portage/util/futures/_asyncio/__init__.py |  6 +++-
 2 files changed, 24 insertions(+), 22 deletions(-)

diff --git a/lib/portage/sync/modules/rsync/rsync.py 
b/lib/portage/sync/modules/rsync/rsync.py
index 175c7f2e8e..5d442d2626 100644
--- a/lib/portage/sync/modules/rsync/rsync.py
+++ b/lib/portage/sync/modules/rsync/rsync.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2023 Gentoo Authors
+# Copyright 1999-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import datetime
@@ -708,48 +708,47 @@ class RsyncSync(NewBase):
 command.append(syncuri.rstrip("/") + "/metadata/timestamp.chk")
 command.append(tmpservertimestampfile)
 content = None
-pids = []
+proc = None
+proc_waiter = None
+loop = asyncio.get_event_loop()
 try:
 # Timeout here in case the server is unresponsive.  The
 # --timeout rsync option doesn't apply to the initial
 # connection attempt.
 try:
-if self.rsync_initial_timeout:
-
portage.exception.AlarmSignal.register(self.rsync_initial_timeout)
-
-pids.extend(
-portage.process.spawn(command, returnpid=True, 
**self.spawn_kwargs)
+proc = portage.process.spawn(
+command, returnproc=True, **self.spawn_kwargs
+)
+proc_waiter = asyncio.ensure_future(proc.wait(), loop)
+future = (
+asyncio.wait_for(
+asyncio.shield(proc_waiter), self.rsync_initial_timeout
+)
+if self.rsync_initial_timeout
+else proc_waiter
 )
-exitcode = os.waitpid(pids[0], 0)[1]
+exitcode = loop.run_until_complete(future)
 if self.usersync_uid is not None:
 portage.util.apply_permissions(
 tmpservertimestampfile, uid=os.getuid()
 )
 content = portage.grabfile(tmpservertimestampfile)
 finally:
-if self.rsync_initial_timeout:
-portage.exception.AlarmSignal.unregister()
 try:
 os.unlink(tmpservertimestampfile)
 except OSError:
 pass
-except portage.exception.AlarmSignal:
+except (TimeoutError, asyncio.TimeoutError):
 # timed out
 print("timed out")
 # With waitpid and WNOHANG, only check the
 # first element of the tuple since the second
 # element may vary (bug #337465).
-if pids and os.waitpid(pids[0], os.WNOHANG)[0] == 0:
-os.kill(pids[0], signal.SIGTERM)
-os.waitpid(pids[0], 0)
+if proc_waiter and not proc_waiter.done():
+proc.terminate()
+loop.run_until_complete(proc_waiter)
 # This is the same code rsync uses for timeout.
 exitcode = 30
-else:
-if exitcode != os.EX_OK:
-if exitcode & 0xFF:
-exitcode = (exitcode & 0xFF) << 8
-else:
-exitcode = exitcode >> 8
 
 if content:
 try:
@@ -758,7 +757,6 @@ class RsyncSync(NewBase):
 )
 except (OverflowError, ValueError):
 pass
-del command, pids, content
 
 if exitcode == os.EX_OK:
 if (servertimestamp != 0) and (servertimestamp == timestamp):

diff --git a/lib/portage/util/futures/_asyncio/__init__.py 
b/lib/portage/util/futures/_asyncio/__init__.py
index a5a6cb3a5b..8f1b8e8275 100644
--- a/lib/portage/util/futures/_asyncio/__init__.py
+++ b/lib/portage/util/futures/_asyncio/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2018-2021 Gentoo Authors
+# Copyright 2018-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 __all__ = (
@@ -15,9 +15,11 @@ __all__ = (
 "set_child_watcher",
 "get_event_loop_policy",
 "set_event_loop_policy",
+"shield",
 "sleep",
 "Task",
 "wait",
+"wait_for",
 )
 
 import types
@@ -33,7 +35,9 @@ from asyncio import (
 FIRST_EXCEPTION,
 Future,
 InvalidStateError,
+shield,
 TimeoutError,
+wait_for,
 )
 
 import threading



[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/executor/, lib/portage/tests/util/futures/

2024-02-06 Thread Zac Medico
commit: 6c4b542b2a830587869b6180e879b719e97fda66
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Feb  6 04:00:29 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb  7 00:49:26 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=6c4b542b

ForkExecutor: multiprocessing spawn compat

Use the AsyncFunction create_pipe=False parameter to avoid issues
in the pipe code triggered with the "spawn" multiprocessing start
method when spawn uses multiprocessing.Process (bug 916566), since
these jobs should inherit stdio streams and run in the foreground
with no log. Also fix RetryForkExecutorTestCase to avoid pickling
issues.

Bug: https://bugs.gentoo.org/923854
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/util/futures/test_retry.py | 42 ++--
 lib/portage/util/futures/executor/fork.py|  6 ++--
 2 files changed, 31 insertions(+), 17 deletions(-)

diff --git a/lib/portage/tests/util/futures/test_retry.py 
b/lib/portage/tests/util/futures/test_retry.py
index a5b56bdc7f..6bd3f4b64a 100644
--- a/lib/portage/tests/util/futures/test_retry.py
+++ b/lib/portage/tests/util/futures/test_retry.py
@@ -1,4 +1,4 @@
-# Copyright 2018-2023 Gentoo Authors
+# Copyright 2018-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from concurrent.futures import Future, ThreadPoolExecutor
@@ -9,7 +9,6 @@ import threading
 import weakref
 import time
 
-import portage
 from portage.tests import TestCase
 from portage.util._eventloop.global_event_loop import global_event_loop
 from portage.util.backoff import RandomExponentialBackoff
@@ -229,16 +228,19 @@ class RetryForkExecutorTestCase(RetryTestCase):
 
 @contextlib.contextmanager
 def _wrap_coroutine_func(self, coroutine_func):
+uses_subprocess = isinstance(self._executor, ForkExecutor)
 parent_loop = global_event_loop()
-parent_pid = portage.getpid()
 pending = weakref.WeakValueDictionary()
 
 # Since ThreadPoolExecutor does not propagate cancellation of a
 # parent_future to the underlying coroutine, use kill_switch to
 # propagate task cancellation to wrapper, so that HangForever's
 # thread returns when retry eventually cancels parent_future.
-def wrapper(kill_switch):
-if portage.getpid() == parent_pid:
+if uses_subprocess:
+wrapper = _run_coroutine_in_subprocess(coroutine_func)
+else:
+
+def wrapper(kill_switch):
 # thread in main process
 def done_callback(result):
 result.cancelled() or result.exception() or result.result()
@@ -262,22 +264,19 @@ class RetryForkExecutorTestCase(RetryTestCase):
 else:
 return future.result().result()
 
-# child process
-loop = global_event_loop()
-try:
-return loop.run_until_complete(coroutine_func())
-finally:
-loop.close()
-
 def execute_wrapper():
-kill_switch = threading.Event()
+# Use kill_switch for threads because they can't be killed
+# like processes. Do not pass kill_switch to subprocesses
+# because it is not picklable.
+kill_switch = None if uses_subprocess else threading.Event()
+wrapper_args = [kill_switch] if kill_switch else []
 parent_future = asyncio.ensure_future(
-parent_loop.run_in_executor(self._executor, wrapper, 
kill_switch),
+parent_loop.run_in_executor(self._executor, wrapper, 
*wrapper_args),
 loop=parent_loop,
 )
 
 def kill_callback(parent_future):
-if not kill_switch.is_set():
+if kill_switch is not None and not kill_switch.is_set():
 kill_switch.set()
 
 parent_future.add_done_callback(kill_callback)
@@ -298,6 +297,19 @@ class RetryForkExecutorTestCase(RetryTestCase):
 future.cancelled() or future.exception() or future.result()
 
 
+class _run_coroutine_in_subprocess:
+def __init__(self, coroutine_func):
+self._coroutine_func = coroutine_func
+
+def __call__(self):
+# child process
+loop = global_event_loop()
+try:
+return loop.run_until_complete(self._coroutine_func())
+finally:
+loop.close()
+
+
 class RetryThreadExecutorTestCase(RetryForkExecutorTestCase):
 def _setUpExecutor(self):
 self._executor = ThreadPoolExecutor(max_workers=1)

diff --git a/lib/portage/util/futures/executor/fork.py 
b/lib/portage/util/futures/executor/fork.py
index 61ad6aecfb..1e3d010724 100644
--- a/lib/portage/util/futures/executor/fork.py
+++ b/lib/portage/util/futures/executor/fork.py
@@ -1,4 +1,4 @@
-# Copyright 2018 Gentoo Foundation
+# Copyright 2018-2024 Gentoo Authors
 # Distributed under the 

[gentoo-commits] proj/portage:master commit in: lib/portage/util/_async/

2024-02-06 Thread Zac Medico
commit: 7d7ef237f3dddcf450fedab5aabfd57d1fb3406d
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Feb  6 01:35:25 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb  7 00:36:56 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=7d7ef237

ForkProcess: Handle BrokenPipeError in _send_fd_pipes

Convert _send_fd_pipes BrokenPipeError to asyncio.CancelledError,
in order to gracefully handle a concurrently terminated child
process as in testAsynchronousLockWaitCancel. Even if the child
terminated abnormally, then there is no harm in suppressing the
exception here, since the child error should have gone to stderr.

Bug: https://bugs.gentoo.org/923852
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/util/_async/ForkProcess.py | 25 +
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/lib/portage/util/_async/ForkProcess.py 
b/lib/portage/util/_async/ForkProcess.py
index cb240d0712..ebcbd94107 100644
--- a/lib/portage/util/_async/ForkProcess.py
+++ b/lib/portage/util/_async/ForkProcess.py
@@ -153,15 +153,24 @@ class ForkProcess(SpawnProcess):
 This performs blocking IO, intended for invocation via run_in_executor.
 """
 fd_list = list(set(self._fd_pipes.values()))
-self._files.connection.send(
-(self._fd_pipes, fd_list),
-)
-for fd in fd_list:
-multiprocessing.reduction.send_handle(
-self._files.connection,
-fd,
-self.pid,
+try:
+self._files.connection.send(
+(self._fd_pipes, fd_list),
 )
+for fd in fd_list:
+multiprocessing.reduction.send_handle(
+self._files.connection,
+fd,
+self.pid,
+)
+except BrokenPipeError as e:
+# This case is triggered by testAsynchronousLockWaitCancel
+# when the test case terminates the child process while
+# this thread is still sending the fd_pipes (bug 923852).
+# Even if the child terminated abnormally, then there is
+# no harm in suppressing the exception here, since the
+# child error should have gone to stderr.
+raise asyncio.CancelledError from e
 
 # self._fd_pipes contains duplicates that must be closed.
 for fd in fd_list:



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/util/futures/asyncio/

2024-02-06 Thread Zac Medico
commit: 66d8f8388e5b9da7e07510f78ec487913e2ceaf5
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb  4 00:16:29 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Wed Feb  7 00:36:17 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=66d8f838

ChildWatcherTestCase: Remove obsolete test which uses spawn returnpid

This test was added for bug 649588 when there was still an
internal event loop implementation for python2. It is no
longer relevant and uses the deprecated spawn returnpid
parameter, so remove it.

Bug: https://bugs.gentoo.org/916566
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/util/futures/asyncio/meson.build |  1 -
 .../util/futures/asyncio/test_child_watcher.py | 50 --
 2 files changed, 51 deletions(-)

diff --git a/lib/portage/tests/util/futures/asyncio/meson.build 
b/lib/portage/tests/util/futures/asyncio/meson.build
index ba727052fc..2de0668d6b 100644
--- a/lib/portage/tests/util/futures/asyncio/meson.build
+++ b/lib/portage/tests/util/futures/asyncio/meson.build
@@ -1,6 +1,5 @@
 py.install_sources(
 [
-'test_child_watcher.py',
 'test_event_loop_in_fork.py',
 'test_pipe_closed.py',
 'test_policy_wrapper_recursion.py',

diff --git a/lib/portage/tests/util/futures/asyncio/test_child_watcher.py 
b/lib/portage/tests/util/futures/asyncio/test_child_watcher.py
deleted file mode 100644
index cd100598b7..00
--- a/lib/portage/tests/util/futures/asyncio/test_child_watcher.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright 2018-2021 Gentoo Authors
-# Distributed under the terms of the GNU General Public License v2
-
-import os
-
-from portage.process import find_binary, spawn
-from portage.tests import TestCase
-from portage.util._eventloop.global_event_loop import global_event_loop
-from portage.util.futures import asyncio
-from portage.util.futures.unix_events import DefaultEventLoopPolicy
-
-
-class ChildWatcherTestCase(TestCase):
-def testChildWatcher(self):
-true_binary = find_binary("true")
-self.assertNotEqual(true_binary, None)
-
-initial_policy = asyncio.get_event_loop_policy()
-if not isinstance(initial_policy, DefaultEventLoopPolicy):
-asyncio.set_event_loop_policy(DefaultEventLoopPolicy())
-
-loop = None
-try:
-try:
-asyncio.set_child_watcher(None)
-except NotImplementedError:
-pass
-else:
-self.assertTrue(False)
-
-args_tuple = ("hello", "world")
-
-loop = asyncio._wrap_loop()
-future = loop.create_future()
-
-def callback(pid, returncode, *args):
-future.set_result((pid, returncode, args))
-
-async def watch_pid():
-with asyncio.get_child_watcher() as watcher:
-pids = spawn([true_binary], returnpid=True)
-watcher.add_child_handler(pids[0], callback, *args_tuple)
-self.assertEqual((await future), (pids[0], os.EX_OK, 
args_tuple))
-
-loop.run_until_complete(watch_pid())
-finally:
-asyncio.set_event_loop_policy(initial_policy)
-if loop not in (None, global_event_loop()):
-loop.close()
-self.assertFalse(global_event_loop().is_closed())



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/bin/

2024-02-05 Thread Zac Medico
commit: a5d92fafab93f62e5b28b97a443e0e06a368251d
Author: Zac Medico  gentoo  org>
AuthorDate: Mon Feb  5 22:51:47 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Mon Feb  5 22:54:04 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=a5d92faf

tests/bin/setup_env.py: multiprocessing spawn compat

Replace unpicklable local pre_exec function with spawn
cwd argument.

Bug: https://bugs.gentoo.org/914876
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/bin/setup_env.py | 7 ++-
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/lib/portage/tests/bin/setup_env.py 
b/lib/portage/tests/bin/setup_env.py
index faef118b05..5787f87682 100644
--- a/lib/portage/tests/bin/setup_env.py
+++ b/lib/portage/tests/bin/setup_env.py
@@ -1,5 +1,5 @@
 # setup_env.py -- Make sure bin subdir has sane env for testing
-# Copyright 2007-2013 Gentoo Foundation
+# Copyright 2007-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import tempfile
@@ -78,10 +78,7 @@ def portage_func(func, args, exit_status=0):
 f = open("/dev/null", "wb")
 fd_pipes = {0: 0, 1: f.fileno(), 2: f.fileno()}
 
-def pre_exec():
-os.chdir(env["S"])
-
-spawn([func] + args.split(), env=env, fd_pipes=fd_pipes, pre_exec=pre_exec)
+spawn([func] + args.split(), env=env, fd_pipes=fd_pipes, cwd=env["S"])
 f.close()
 
 



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-04 Thread Zac Medico
commit: f97e414ce980299acc962e357db24106d62e4c7c
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb  4 06:12:00 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Feb  4 08:27:37 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=f97e414c

set_term_size: Wait asynchronously if event loop is running

When set_term_size is called from an asynchronous context, we
can wait for the process to exit asynchronously. This will
prevent a "RuntimeError: This event loop is already running"
error in the future when synchronous spawn calls will need
to run the event loop in order to wait for the spawned
process(es).

Bug: https://bugs.gentoo.org/923750
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/output.py | 15 ---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/lib/portage/output.py b/lib/portage/output.py
index cdeeb18e99..7d3a6278f3 100644
--- a/lib/portage/output.py
+++ b/lib/portage/output.py
@@ -1,4 +1,4 @@
-# Copyright 1998-2021 Gentoo Authors
+# Copyright 1998-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 __docformat__ = "epytext"
@@ -13,7 +13,9 @@ import portage
 
 portage.proxy.lazyimport.lazyimport(
 globals(),
+"portage.process:spawn",
 "portage.util:writemsg",
+"portage.util.futures:asyncio",
 )
 import portage.util.formatter as formatter
 
@@ -557,13 +559,20 @@ def set_term_size(lines, columns, fd):
 Set the number of lines and columns for the tty that is connected to fd.
 For portability, this simply calls `stty rows $lines columns $columns`.
 """
-from portage.process import spawn
 
 cmd = ["stty", "rows", str(lines), "columns", str(columns)]
 try:
-spawn(cmd, env=os.environ, fd_pipes={0: fd})
+proc = spawn(cmd, env=os.environ, fd_pipes={0: fd}, returnproc=True)
 except CommandNotFound:
 writemsg(_("portage: stty: command not found\n"), noiselevel=-1)
+else:
+loop = asyncio.get_event_loop()
+if loop.is_running():
+asyncio.ensure_future(proc.wait(), loop).add_done_callback(
+lambda future: future.result()
+)
+else:
+loop.run_until_complete(proc.wait())
 
 
 class EOutput:



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-02-04 Thread Zac Medico
commit: c65d2d8b6c17d849e85f8c13c1a287ff1a07ccbd
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb  4 07:22:37 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Feb  4 08:27:37 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=c65d2d8b

process.spawn: Avoid os.environ pickling error

This error was observed when pickling os.environ for
muliprocessing start method "spawn" after set_term_size
was updated to use returnproc:

  File "/usr/lib/python3.12/multiprocessing/reduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj)
AttributeError: Can't pickle local object '_createenviron..encode'. Did 
you mean: '_download_dir'?

Bug: https://bugs.gentoo.org/923750
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/process.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lib/portage/process.py b/lib/portage/process.py
index 01426179d7..b223ecb887 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -527,6 +527,9 @@ def spawn(
 mycommand = mycommand.split()
 
 env = os.environ if env is None else env
+# Sometimes os.environ can fail to pickle as shown in bug 923750
+# comment 4, so copy it to a dict.
+env = env if isinstance(env, dict) else dict(env)
 
 env_stats = None
 if warn_on_large_env:



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/ebuild/, lib/portage/, lib/_emerge/

2024-02-04 Thread Zac Medico
commit: 052a4076cdf29fde8481646c636190d9db35f0ff
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Feb  4 08:08:01 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Feb  4 08:10:12 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=052a4076

Revert "process.spawn: Use multiprocessing.Process for returnproc"

This reverts commit 305612d1b04aa06d3d1a1c8b51d046a644742fd5.
It triggered a "Bad file descriptor" during the instprep
phase as reported in bug 923755.

Bug: https://bugs.gentoo.org/923755
Signed-off-by: Zac Medico  gentoo.org>

 lib/_emerge/SpawnProcess.py|  4 +-
 lib/portage/process.py | 72 --
 lib/portage/tests/ebuild/test_doebuild_fd_pipes.py |  6 +-
 3 files changed, 15 insertions(+), 67 deletions(-)

diff --git a/lib/_emerge/SpawnProcess.py b/lib/_emerge/SpawnProcess.py
index 5e582e3322..7f4a23892b 100644
--- a/lib/_emerge/SpawnProcess.py
+++ b/lib/_emerge/SpawnProcess.py
@@ -224,9 +224,7 @@ class SpawnProcess(SubProcess):
 got_pty, master_fd, slave_fd = 
_create_pty_or_pipe(copy_term_size=stdout_pipe)
 return (master_fd, slave_fd)
 
-def _spawn(
-self, args: list[str], **kwargs
-) -> portage.process.MultiprocessingProcess:
+def _spawn(self, args: list[str], **kwargs) -> portage.process.Process:
 spawn_func = portage.process.spawn
 
 if self._selinux_type is not None:

diff --git a/lib/portage/process.py b/lib/portage/process.py
index d64ffa924f..01426179d7 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -19,7 +19,7 @@ import warnings
 
 from dataclasses import dataclass
 from functools import lru_cache
-from typing import Any, Optional, Callable, Union
+from typing import Any, Optional, Callable
 
 from portage import os
 from portage import _encodings
@@ -28,7 +28,6 @@ import portage
 
 portage.proxy.lazyimport.lazyimport(
 globals(),
-"portage.util._async.ForkProcess:ForkProcess",
 "portage.util._eventloop.global_event_loop:global_event_loop",
 "portage.util.futures:asyncio",
 "portage.util:dump_traceback,writemsg,writemsg_level",
@@ -297,19 +296,12 @@ class AbstractProcess:
 
 class Process(AbstractProcess):
 """
-An object that wraps OS processes which do not have an
-associated multiprocessing.Process instance. Ultimately,
-we need to stop using os.fork() to create these processes
-because it is unsafe for threaded processes as discussed
-in https://github.com/python/cpython/issues/84559.
-
-Note that if subprocess.Popen is used without pass_fds
-or preexec_fn parameters, then it avoids using os.fork()
-by instead using posix_spawn. This approach is not used
-by spawn because it needs to execute python code prior
-to exec, so it instead uses multiprocessing.Process,
-which only uses os.fork() when the multiprocessing start
-method is fork.
+An object that wraps OS processes created by spawn.
+In the future, spawn will return objects of a different type
+but with a compatible interface to this class, in order
+to encapsulate implementation-dependent objects like
+multiprocessing.Process which are designed to manage
+the process lifecycle and need to persist until it exits.
 """
 
 def __init__(self, pid: int):
@@ -469,7 +461,7 @@ def spawn(
 unshare_mount=False,
 unshare_pid=False,
 warn_on_large_env=False,
-) -> Union[int, MultiprocessingProcess, list[int]]:
+):
 """
 Spawns a given command.
 
@@ -487,8 +479,8 @@ def spawn(
 @param returnpid: Return the Process IDs for a successful spawn.
 NOTE: This requires the caller clean up all the PIDs, otherwise spawn will 
clean them.
 @type returnpid: Boolean
-@param returnproc: Return a MultiprocessingProcess instance (conflicts 
with logfile parameter).
-NOTE: This requires the caller to asynchronously wait for the 
MultiprocessingProcess instance.
+@param returnproc: Return a Process object for a successful spawn 
(conflicts with logfile parameter).
+NOTE: This requires the caller to asynchronously wait for the Process.
 @type returnproc: Boolean
 @param uid: User ID to spawn as; useful for dropping privilages
 @type uid: Integer
@@ -631,9 +623,7 @@ def spawn(
 # fork, so that the result is cached in the main process.
 bool(groups)
 
-start_func = _start_proc if returnproc else _start_fork
-
-pid = start_func(
+pid = _start_fork(
 _exec_wrapper,
 args=(
 binary,
@@ -659,10 +649,6 @@ def spawn(
 close_fds=close_fds,
 )
 
-if returnproc:
-# _start_proc returns a MultiprocessingProcess instance.
-return pid
-
 if not isinstance(pid, int):
 raise AssertionError(f"fork returned non-integer: {repr(pid)}")
 
@@ -684,6 +670,8 @@ def spawn(
 stacklevel=1,
 )
 return mypids
+

[gentoo-commits] proj/portage:master commit in: lib/portage/util/_async/

2024-02-03 Thread Zac Medico
commit: 9f96ce5105e7bd2580ae9acc34d6ebad914dae47
Author: Zac Medico  gentoo  org>
AuthorDate: Sat Feb  3 19:36:05 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sat Feb  3 19:55:19 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=9f96ce51

ForkProcess: Use duplicate fd_pipes in _send_fd_pipes thread

In order to allow callers to manage the lifecycle of fd_pipes
file descriptors, create duplicates for _send_fd_pipes to
close when it has finished sending them.

This fixes bug 916601 in a nice way, allowing commit
3b1234ba69a31709cd5aec1ae070901e3a28bb7c to be reverted.

Bug: https://bugs.gentoo.org/916601
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/util/_async/ForkProcess.py | 55 +-
 1 file changed, 48 insertions(+), 7 deletions(-)

diff --git a/lib/portage/util/_async/ForkProcess.py 
b/lib/portage/util/_async/ForkProcess.py
index 711bd2a7ba..3acbe34fc6 100644
--- a/lib/portage/util/_async/ForkProcess.py
+++ b/lib/portage/util/_async/ForkProcess.py
@@ -24,6 +24,8 @@ class ForkProcess(SpawnProcess):
 "kwargs",
 "target",
 "_child_connection",
+# Duplicate file descriptors for use by _send_fd_pipes background 
thread.
+"_fd_pipes",
 )
 
 _file_names = ("connection", "slave_fd")
@@ -53,7 +55,13 @@ class ForkProcess(SpawnProcess):
 duplex=self._HAVE_SEND_HANDLE
 )
 
-self._proc = self._spawn(self.args, fd_pipes=self.fd_pipes)
+# Handle fd_pipes in _main instead, since file descriptors are
+# not inherited with the multiprocessing "spawn" start method.
+# Pass fd_pipes=None to spawn here so that it doesn't leave
+# a closed stdin duplicate in fd_pipes (that would trigger
+# "Bad file descriptor" error if we tried to send it via
+# send_handle).
+self._proc = self._spawn(self.args, fd_pipes=None)
 
 self._registered = True
 
@@ -74,6 +82,25 @@ class ForkProcess(SpawnProcess):
 self.fd_pipes[1] = slave_fd
 self.fd_pipes[2] = slave_fd
 self._files = self._files_dict(connection=connection, 
slave_fd=slave_fd)
+
+# Create duplicate file descriptors in self._fd_pipes
+# so that the caller is free to manage the lifecycle
+# of the original fd_pipes.
+self._fd_pipes = {}
+fd_map = {}
+for dest, src in list(self.fd_pipes.items()):
+if src not in fd_map:
+src_new = fd_map[src] = os.dup(src)
+old_fdflags = fcntl.fcntl(src, fcntl.F_GETFD)
+fcntl.fcntl(src_new, fcntl.F_SETFD, old_fdflags)
+os.set_inheritable(
+src_new, not bool(old_fdflags & fcntl.FD_CLOEXEC)
+)
+self._fd_pipes[dest] = fd_map[src]
+
+asyncio.ensure_future(
+self._proc.wait(), self.scheduler
+).add_done_callback(self._close_fd_pipes)
 else:
 master_fd = connection
 
@@ -81,6 +108,19 @@ class ForkProcess(SpawnProcess):
 master_fd, log_file_path=self.logfile, stdout_fd=stdout_fd
 )
 
+def _close_fd_pipes(self, future):
+"""
+Cleanup self._fd_pipes if needed, since _send_fd_pipes could
+have been cancelled.
+"""
+# future.result() raises asyncio.CancelledError if
+# future.cancelled(), but that should not happen.
+future.result()
+if self._fd_pipes is not None:
+for fd in set(self._fd_pipes.values()):
+os.close(fd)
+self._fd_pipes = None
+
 @property
 def _fd_pipes_send_handle(self):
 """Returns True if we have a connection to implement fd_pipes via 
send_handle."""
@@ -95,9 +135,9 @@ class ForkProcess(SpawnProcess):
 Communicate with _bootstrap to send fd_pipes via send_handle.
 This performs blocking IO, intended for invocation via run_in_executor.
 """
-fd_list = list(set(self.fd_pipes.values()))
+fd_list = list(set(self._fd_pipes.values()))
 self._files.connection.send(
-(self.fd_pipes, fd_list),
+(self._fd_pipes, fd_list),
 )
 for fd in fd_list:
 multiprocessing.reduction.send_handle(
@@ -106,6 +146,11 @@ class ForkProcess(SpawnProcess):
 self.pid,
 )
 
+# self._fd_pipes contains duplicates that must be closed.
+for fd in fd_list:
+os.close(fd)
+self._fd_pipes = None
+
 async def _main(self, build_logger, pipe_logger, loop=None):
 try:
 if self._fd_pipes_send_handle:
@@ -167,10 +212,6 @@ class ForkProcess(SpawnProcess):
 )
 fd_pipes[0] = stdin_dup
 
- 

[gentoo-commits] proj/portage:master commit in: lib/portage/tests/process/

2024-02-03 Thread Zac Medico
commit: 7ea1175091886baa677d11290d4b725a64e68710
Author: Zac Medico  gentoo  org>
AuthorDate: Sat Feb  3 19:36:13 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sat Feb  3 19:55:19 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=7ea11750

Revert "testAsyncFunctionStdin: multiprocessing spawn compat"

This reverts commit 3b1234ba69a31709cd5aec1ae070901e3a28bb7c,
since ForkProcess now solves the problem by creating temporary
duplicate file descriptors.

Bug: https://bugs.gentoo.org/916601
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/process/test_AsyncFunction.py | 19 ---
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/lib/portage/tests/process/test_AsyncFunction.py 
b/lib/portage/tests/process/test_AsyncFunction.py
index a056f268bd..eb426a5c02 100644
--- a/lib/portage/tests/process/test_AsyncFunction.py
+++ b/lib/portage/tests/process/test_AsyncFunction.py
@@ -1,4 +1,4 @@
-# Copyright 2020-2023 Gentoo Authors
+# Copyright 2020-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import functools
@@ -44,20 +44,17 @@ class AsyncFunctionTestCase(TestCase):
 ),
 )
 reader.start()
-# For compatibility with the multiprocessing spawn start
-# method, we delay restoration of the stdin file descriptor,
-# since this file descriptor is sent to the subprocess
-# asynchronously.
-_set_nonblocking(pw.fileno())
-with open(pw.fileno(), mode="wb", buffering=0, closefd=False) as 
pipe_write:
-await _writer(pipe_write, test_string.encode("utf_8"))
-pw.close()
-self.assertEqual((await reader.async_wait()), os.EX_OK)
-self.assertEqual(reader.result, test_string)
 finally:
 os.dup2(stdin_backup, portage._get_stdin().fileno())
 os.close(stdin_backup)
 
+_set_nonblocking(pw.fileno())
+with open(pw.fileno(), mode="wb", buffering=0, closefd=False) as 
pipe_write:
+await _writer(pipe_write, test_string.encode("utf_8"))
+pw.close()
+self.assertEqual((await reader.async_wait()), os.EX_OK)
+self.assertEqual(reader.result, test_string)
+
 def testAsyncFunctionStdin(self):
 loop = asyncio._wrap_loop()
 loop.run_until_complete(self._testAsyncFunctionStdin(loop=loop))



[gentoo-commits] proj/portage:master commit in: lib/portage/, lib/portage/package/ebuild/, lib/_emerge/, ...

2024-02-02 Thread Zac Medico
commit: 055c66ec9482064aaaf51bfb6b01e260ea27808e
Author: Zac Medico  gentoo  org>
AuthorDate: Fri Feb  2 16:01:18 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Fri Feb  2 16:01:18 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=055c66ec

SpawnProcess: Use spawn returnproc parameter

Migrate SpawnProcess to use the spawn returnproc parameter,
and make adaptations to descendent classes as needed. Introduce a
portage.process.MultiprocessingProcess class for ForkProcess to wrap
multiprocessing.Process instances, needed because ForkProcess
inherits from SpawnProcess. Use portage.process.Process to wrap
the pid in EbuildMetadataPhase, so that returnproc support in
the doebuild function can be reserved for a later commit.

Bug: https://bugs.gentoo.org/916566
Signed-off-by: Zac Medico  gentoo.org>

 lib/_emerge/EbuildMetadataPhase.py  |   4 +-
 lib/_emerge/SpawnProcess.py |  16 ++---
 lib/_emerge/SubProcess.py   |  25 +++
 lib/portage/package/ebuild/doebuild.py  |   4 +-
 lib/portage/process.py  | 120 
 lib/portage/util/_async/ForkProcess.py  |  87 +++
 lib/portage/util/_async/PopenProcess.py |   5 +-
 7 files changed, 143 insertions(+), 118 deletions(-)

diff --git a/lib/_emerge/EbuildMetadataPhase.py 
b/lib/_emerge/EbuildMetadataPhase.py
index 8905a058fc..a7c9650d74 100644
--- a/lib/_emerge/EbuildMetadataPhase.py
+++ b/lib/_emerge/EbuildMetadataPhase.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2020 Gentoo Authors
+# Copyright 1999-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from _emerge.SubProcess import SubProcess
@@ -137,7 +137,7 @@ class EbuildMetadataPhase(SubProcess):
 self._async_wait()
 return
 
-self.pid = retval[0]
+self._proc = portage.process.Process(retval[0])
 
 def _output_handler(self):
 while True:

diff --git a/lib/_emerge/SpawnProcess.py b/lib/_emerge/SpawnProcess.py
index 40740df9aa..7f4a23892b 100644
--- a/lib/_emerge/SpawnProcess.py
+++ b/lib/_emerge/SpawnProcess.py
@@ -1,4 +1,4 @@
-# Copyright 2008-2023 Gentoo Authors
+# Copyright 2008-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import functools
@@ -123,24 +123,16 @@ class SpawnProcess(SubProcess):
 kwargs[k] = v
 
 kwargs["fd_pipes"] = fd_pipes
-kwargs["returnpid"] = True
+kwargs["returnproc"] = True
 kwargs.pop("logfile", None)
 
-retval = self._spawn(self.args, **kwargs)
+self._proc = self._spawn(self.args, **kwargs)
 
 if slave_fd is not None:
 os.close(slave_fd)
 if null_input is not None:
 os.close(null_input)
 
-if isinstance(retval, int):
-# spawn failed
-self.returncode = retval
-self._async_wait()
-return
-
-self.pid = retval[0]
-
 if not fd_pipes:
 self._registered = True
 self._async_waitpid()
@@ -232,7 +224,7 @@ class SpawnProcess(SubProcess):
 got_pty, master_fd, slave_fd = 
_create_pty_or_pipe(copy_term_size=stdout_pipe)
 return (master_fd, slave_fd)
 
-def _spawn(self, args, **kwargs):
+def _spawn(self, args: list[str], **kwargs) -> portage.process.Process:
 spawn_func = portage.process.spawn
 
 if self._selinux_type is not None:

diff --git a/lib/_emerge/SubProcess.py b/lib/_emerge/SubProcess.py
index b734591d11..029bbc3f44 100644
--- a/lib/_emerge/SubProcess.py
+++ b/lib/_emerge/SubProcess.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2020 Gentoo Authors
+# Copyright 1999-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import logging
@@ -12,12 +12,16 @@ import errno
 
 
 class SubProcess(AbstractPollTask):
-__slots__ = ("pid",) + ("_dummy_pipe_fd", "_files", "_waitpid_id")
+__slots__ = ("_dummy_pipe_fd", "_files", "_proc", "_waitpid_id")
 
 # This is how much time we allow for waitpid to succeed after
 # we've sent a kill signal to our subprocess.
 _cancel_timeout = 1  # seconds
 
+@property
+def pid(self):
+return self._proc.pid
+
 def _poll(self):
 # Simply rely on _async_waitpid_cb to set the returncode.
 return self.returncode
@@ -58,15 +62,11 @@ class SubProcess(AbstractPollTask):
 if self.returncode is not None:
 self._async_wait()
 elif self._waitpid_id is None:
-self._waitpid_id = self.pid
-self.scheduler._asyncio_child_watcher.add_child_handler(
-self.pid, self._async_waitpid_cb
-)
-
-def _async_waitpid_cb(self, pid, returncode):
-if pid != self.pid:
-raise AssertionError(f"expected pid {self.pid}, got {pid}")
-self.returncode = returncode
+self._waitpid_id = 

[gentoo-commits] proj/portage:master commit in: lib/portage/, lib/portage/tests/process/

2024-02-01 Thread Zac Medico
commit: c556fee09b6b103a9bb8c2afa1c7a7ef25022598
Author: Zac Medico  gentoo  org>
AuthorDate: Thu Feb  1 05:48:54 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Thu Feb  1 16:10:28 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=c556fee0

process.spawn: Add returnproc parameter

In order to migrate away from unsafe os.fork() usage in threaded
processes (https://github.com/python/cpython/issues/84559), add a
returnproc parameter that is similar to returnpid, which causes
spawn to return a single Process object instead of a list of pids.
The Process API is a subset of asyncio.subprocess.Process. The
returnproc parameter conflicts with the logfile parameter, since
the caller is expected to use the fd_pipes parameter to implement
logging (this was also true for the returnpid parameter).

In the future, spawn will return objects of a different type but
with a compatible interface to Process, in order to encapsulate
implementation-dependent objects like multiprocessing.Process which
are designed to manage the process lifecycle and need to persist
until it exits.

Trigger a UserWarning when the returnpid parameter is used, in
order to encourage migration to returnproc (do not use
DeprecationWarning since it is hidden by default). This warning
will be temporarily suppressed for portage internals, until they
finish migrating to returnproc. There are probably very few if
any external consumers of spawn with the returnpid parameter,
so it seems safe to move quickly with this deprecation.

Bug: https://bugs.gentoo.org/916566
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/process.py | 84 ++
 lib/portage/tests/process/meson.build  |  1 +
 lib/portage/tests/process/test_spawn_returnproc.py | 39 ++
 3 files changed, 124 insertions(+)

diff --git a/lib/portage/process.py b/lib/portage/process.py
index 71750a715f..6ec52efc4a 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -15,6 +15,7 @@ import subprocess
 import sys
 import traceback
 import os as _os
+import warnings
 
 from dataclasses import dataclass
 from functools import lru_cache
@@ -27,6 +28,7 @@ import portage
 
 portage.proxy.lazyimport.lazyimport(
 globals(),
+"portage.util._eventloop.global_event_loop:global_event_loop",
 "portage.util:dump_traceback,writemsg,writemsg_level",
 )
 
@@ -277,12 +279,78 @@ def calc_env_stats(env) -> EnvStats:
 env_too_large_warnings = 0
 
 
+class Process:
+"""
+An object that wraps OS processes created by spawn.
+In the future, spawn will return objects of a different type
+but with a compatible interface to this class, in order
+to encapsulate implementation-dependent objects like
+multiprocessing.Process which are designed to manage
+the process lifecycle and need to persist until it exits.
+"""
+
+def __init__(self, pid):
+self.pid = pid
+self.returncode = None
+self._exit_waiters = []
+
+def __repr__(self):
+return f"<{self.__class__.__name__} {self.pid}>"
+
+async def wait(self):
+"""
+Wait for the child process to terminate.
+
+Set and return the returncode attribute.
+"""
+if self.returncode is not None:
+return self.returncode
+
+loop = global_event_loop()
+if not self._exit_waiters:
+loop._asyncio_child_watcher.add_child_handler(self.pid, 
self._child_handler)
+waiter = loop.create_future()
+self._exit_waiters.append(waiter)
+return await waiter
+
+def _child_handler(self, pid, returncode):
+if pid != self.pid:
+raise AssertionError(f"expected pid {self.pid}, got {pid}")
+self.returncode = returncode
+
+for waiter in self._exit_waiters:
+if not waiter.cancelled():
+waiter.set_result(returncode)
+self._exit_waiters = None
+
+def send_signal(self, sig):
+"""Send a signal to the process."""
+if self.returncode is not None:
+# Skip signalling a process that we know has already died.
+return
+
+try:
+os.kill(self.pid, sig)
+except ProcessLookupError:
+# Suppress the race condition error; bpo-40550.
+pass
+
+def terminate(self):
+"""Terminate the process with SIGTERM"""
+self.send_signal(signal.SIGTERM)
+
+def kill(self):
+"""Kill the process with SIGKILL"""
+self.send_signal(signal.SIGKILL)
+
+
 def spawn(
 mycommand,
 env=None,
 opt_name=None,
 fd_pipes=None,
 returnpid=False,
+returnproc=False,
 uid=None,
 gid=None,
 groups=None,
@@ -315,6 +383,9 @@ def spawn(
 @param returnpid: Return the Process IDs for a successful spawn.
 NOTE: This requires the caller clean up all the PIDs, otherwise spawn will 
clean them.
 @type returnpid: Boolean
+  

[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-01-29 Thread Zac Medico
commit: c82dc8c8fe92979b82df4c71bb9961973367e8f9
Author: Zac Medico  gentoo  org>
AuthorDate: Mon Jan 29 17:38:20 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Mon Jan 29 17:43:27 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=c82dc8c8

_start_fork: Ensure any _setup_pipes exception is displayed

Fixes: 7ec7a647ef6e ("process.spawn: add abstraction layer for os.fork()")
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/process.py | 17 +
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/lib/portage/process.py b/lib/portage/process.py
index be1c5d350a..71750a715f 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -658,13 +658,8 @@ def _exec_wrapper(
 writemsg(
 f"ERROR: Executing {mycommand} failed with E2BIG. Child 
process environment size: {env_stats.env_size} bytes. Largest environment 
variable: {env_stats.env_largest_name} ({env_stats.env_largest_size} bytes)\n"
 )
-
-# We need to catch _any_ exception so that it doesn't
-# propagate out of this function and cause exiting
-# with anything other than os._exit()
 writemsg(f"{e}:\n   {' '.join(mycommand)}\n", noiselevel=-1)
-traceback.print_exc()
-sys.stderr.flush()
+raise
 
 
 def _exec(
@@ -1174,8 +1169,14 @@ def _start_fork(
 pid = os.fork()
 
 if pid == 0:
-_setup_pipes(fd_pipes, close_fds=close_fds, inheritable=True)
-target(*args, **kwargs)
+try:
+_setup_pipes(fd_pipes, close_fds=close_fds, inheritable=True)
+target(*args, **kwargs)
+except Exception:
+# We need to catch _any_ exception and display it since the 
child
+# process must unconditionally exit via os._exit() if exec 
fails.
+traceback.print_exc()
+sys.stderr.flush()
 finally:
 # Don't used portage.getpid() here, in case there is a race
 # with getpid cache invalidation via _ForkWatcher hook.



[gentoo-commits] proj/portage:master commit in: lib/portage/

2024-01-29 Thread Zac Medico
commit: 7ec7a647ef6e2d94e6f2b387d0a012e78cafbaff
Author: Zac Medico  gentoo  org>
AuthorDate: Mon Jan 29 08:20:55 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Mon Jan 29 08:43:52 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=7ec7a647

process.spawn: add abstraction layer for os.fork()

Since os.fork() is unsafe in threaded processes, add a basic
abstraction layer that will ultimately allow support of other process
cloning implementations.

Usage of os.fork() is now wrapped in a _start_fork function which can
easily be replaced with an alternative implementation.  This function
takes target, args, and kwargs arguments which are equivalent to
the corresponding multiprocessing.Process parameters. It also has
fd_pipes and close_fds parameters, since implementation of these is
dependent on the process cloning implementation.

The process cloning implementation does not need to be bothered with
the details of the _exec function, since spawn uses an _exec_wrapper
function as a target function which calls _exec and handles any
exceptions appropriately (special exception handling is required for
success of test_spawnE2big related to bug 830187).

Bug: https://bugs.gentoo.org/916566
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/process.py | 203 +
 1 file changed, 139 insertions(+), 64 deletions(-)

diff --git a/lib/portage/process.py b/lib/portage/process.py
index 0d58adecad..be1c5d350a 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -18,6 +18,7 @@ import os as _os
 
 from dataclasses import dataclass
 from functools import lru_cache
+from typing import Any, Optional, Callable
 
 from portage import os
 from portage import _encodings
@@ -450,68 +451,31 @@ def spawn(
 # fork, so that the result is cached in the main process.
 bool(groups)
 
-parent_pid = portage.getpid()
-pid = None
-try:
-pid = os.fork()
-
-if pid == 0:
-try:
-_exec(
-binary,
-mycommand,
-opt_name,
-fd_pipes,
-env,
-gid,
-groups,
-uid,
-umask,
-cwd,
-pre_exec,
-close_fds,
-unshare_net,
-unshare_ipc,
-unshare_mount,
-unshare_pid,
-unshare_flags,
-)
-except SystemExit:
-raise
-except Exception as e:
-if isinstance(e, OSError) and e.errno == errno.E2BIG:
-# If exec() failed with E2BIG, then this is
-# potentially because the environment variables
-# grew to large. The following will gather some
-# stats about the environment and print a
-# diagnostic message to help identifying the
-# culprit. See also
-# - https://bugs.gentoo.org/721088
-# - https://bugs.gentoo.org/830187
-if not env_stats:
-env_stats = calc_env_stats(env)
-
-writemsg(
-f"ERROR: Executing {mycommand} failed with E2BIG. 
Child process environment size: {env_stats.env_size} bytes. Largest environment 
variable: {env_stats.env_largest_name} ({env_stats.env_largest_size} bytes)\n"
-)
-
-# We need to catch _any_ exception so that it doesn't
-# propagate out of this function and cause exiting
-# with anything other than os._exit()
-writemsg(f"{e}:\n   {' '.join(mycommand)}\n", noiselevel=-1)
-traceback.print_exc()
-sys.stderr.flush()
-
-finally:
-# Don't used portage.getpid() here, in case there is a race
-# with getpid cache invalidation via _ForkWatcher hook.
-if pid == 0 or (pid is None and _os.getpid() != parent_pid):
-# Call os._exit() from a finally block in order
-# to suppress any finally blocks from earlier
-# in the call stack (see bug #345289). This
-# finally block has to be setup before the fork
-# in order to avoid a race condition.
-os._exit(1)
+pid = _start_fork(
+_exec_wrapper,
+args=(
+binary,
+mycommand,
+opt_name,
+fd_pipes,
+env,
+gid,
+groups,
+uid,
+umask,
+cwd,
+pre_exec,
+close_fds,
+unshare_net,
+unshare_ipc,
+unshare_mount,
+unshare_pid,
+unshare_flags,
+env_stats,
+),
+  

[gentoo-commits] proj/portage:master commit in: lib/portage/util/, lib/portage/, lib/portage/tests/locks/

2024-01-29 Thread Zac Medico
commit: d4c495c6bd543d07f3c5d6e72145e177999edaab
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Jan 28 23:44:23 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Mon Jan 29 04:08:42 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=d4c495c6

Rely on os.register_at_fork for _ForkWatcher hook invocation

Because os.register_at_fork is used instead of
multiprocessing.util.register_after_fork since commit
cc7c40199850acb3d36f0a6452987231d592c360, we can rely on the
_ForkWatcher hook being automatically invoked after os.fork().

Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/process.py| 10 +++---
 lib/portage/tests/locks/test_lock_nonblock.py |  3 +--
 lib/portage/util/locale.py|  3 +--
 3 files changed, 5 insertions(+), 11 deletions(-)

diff --git a/lib/portage/process.py b/lib/portage/process.py
index 28f977a046..0d58adecad 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -1,5 +1,5 @@
 # portage.py -- core Portage functionality
-# Copyright 1998-2023 Gentoo Authors
+# Copyright 1998-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 
@@ -456,7 +456,6 @@ def spawn(
 pid = os.fork()
 
 if pid == 0:
-portage._ForkWatcher.hook(portage._ForkWatcher)
 try:
 _exec(
 binary,
@@ -504,8 +503,8 @@ def spawn(
 sys.stderr.flush()
 
 finally:
-# Don't used portage.getpid() here, due to a race with the above
-# portage._ForkWatcher cache update.
+# Don't used portage.getpid() here, in case there is a race
+# with getpid cache invalidation via _ForkWatcher hook.
 if pid == 0 or (pid is None and _os.getpid() != parent_pid):
 # Call os._exit() from a finally block in order
 # to suppress any finally blocks from earlier
@@ -775,9 +774,6 @@ def _exec(
 if unshare_pid:
 main_child_pid = os.fork()
 if main_child_pid == 0:
-# The portage.getpid() cache may need to be 
updated here,
-# in case the pre_exec function invokes 
portage APIs.
-portage._ForkWatcher.hook(portage._ForkWatcher)
 # pid namespace requires us to become init
 binary, myargs = (
 portage._python_interpreter,

diff --git a/lib/portage/tests/locks/test_lock_nonblock.py 
b/lib/portage/tests/locks/test_lock_nonblock.py
index e3f9b4d023..9bb91b428e 100644
--- a/lib/portage/tests/locks/test_lock_nonblock.py
+++ b/lib/portage/tests/locks/test_lock_nonblock.py
@@ -1,4 +1,4 @@
-# Copyright 2011-2020 Gentoo Authors
+# Copyright 2011-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import tempfile
@@ -19,7 +19,6 @@ class LockNonblockTestCase(TestCase):
 lock1 = portage.locks.lockfile(path)
 pid = os.fork()
 if pid == 0:
-portage._ForkWatcher.hook(portage._ForkWatcher)
 portage.locks._close_fds()
 # Disable close_fds since we don't exec
 # (see _setup_pipes docstring).

diff --git a/lib/portage/util/locale.py b/lib/portage/util/locale.py
index 0d0c120157..a620dbd544 100644
--- a/lib/portage/util/locale.py
+++ b/lib/portage/util/locale.py
@@ -1,4 +1,4 @@
-# Copyright 2015-2020 Gentoo Authors
+# Copyright 2015-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 """
@@ -118,7 +118,6 @@ def check_locale(silent=False, env=None):
 
 pid = os.fork()
 if pid == 0:
-portage._ForkWatcher.hook(portage._ForkWatcher)
 try:
 if env is not None:
 try:



[gentoo-commits] proj/portage:master commit in: lib/portage/util/file_copy/, lib/_emerge/, lib/portage/util/_async/, ...

2024-01-28 Thread Zac Medico
commit: 3d55e159c473075c7b2f87c92293b0df6fa57563
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Jan 28 22:01:58 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sun Jan 28 23:09:26 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=3d55e159

*/*: rerun black w/ 24.1.0

Signed-off-by: Zac Medico  gentoo.org>

 lib/_emerge/AsynchronousTask.py |  6 +--
 lib/_emerge/EbuildMetadataPhase.py  | 11 ++---
 lib/_emerge/MergeListItem.py|  1 -
 lib/_emerge/PipeReader.py   |  1 -
 lib/_emerge/SpawnProcess.py |  1 -
 lib/_emerge/depgraph.py | 59 ++---
 lib/_emerge/resolver/slot_collision.py  | 12 ++---
 lib/portage/_emirrordist/DeletionIterator.py|  8 ++--
 lib/portage/_emirrordist/FetchIterator.py   |  8 ++--
 lib/portage/_sets/libs.py   |  1 -
 lib/portage/dbapi/porttree.py   |  1 -
 lib/portage/dbapi/vartree.py| 28 +++-
 lib/portage/dep/__init__.py |  1 -
 lib/portage/package/ebuild/config.py| 38 
 lib/portage/package/ebuild/doebuild.py  | 16 ---
 lib/portage/proxy/objectproxy.py|  1 -
 lib/portage/tests/ebuild/test_fetch.py  | 10 ++---
 lib/portage/tests/process/test_AsyncFunction.py |  8 ++--
 lib/portage/util/_async/PipeLogger.py   |  1 -
 lib/portage/util/_async/TaskScheduler.py|  1 -
 lib/portage/util/_dyn_libs/LinkageMapELF.py |  3 --
 lib/portage/util/file_copy/__init__.py  |  7 +--
 lib/portage/util/futures/_sync_decorator.py |  8 ++--
 23 files changed, 119 insertions(+), 112 deletions(-)

diff --git a/lib/_emerge/AsynchronousTask.py b/lib/_emerge/AsynchronousTask.py
index 4290eede36..4049ba5eb6 100644
--- a/lib/_emerge/AsynchronousTask.py
+++ b/lib/_emerge/AsynchronousTask.py
@@ -46,9 +46,9 @@ class AsynchronousTask(SlotObject):
 )
 self.addExitListener(exit_listener)
 waiter.add_done_callback(
-lambda waiter: self.removeExitListener(exit_listener)
-if waiter.cancelled()
-else None
+lambda waiter: (
+self.removeExitListener(exit_listener) if waiter.cancelled() 
else None
+)
 )
 if self.returncode is not None:
 # If the returncode is not None, it means the exit event has 
already

diff --git a/lib/_emerge/EbuildMetadataPhase.py 
b/lib/_emerge/EbuildMetadataPhase.py
index fd695e0253..8905a058fc 100644
--- a/lib/_emerge/EbuildMetadataPhase.py
+++ b/lib/_emerge/EbuildMetadataPhase.py
@@ -19,7 +19,6 @@ import fcntl
 
 
 class EbuildMetadataPhase(SubProcess):
-
 """
 Asynchronous interface for the ebuild "depend" phase which is
 used to extract metadata from the ebuild.
@@ -200,12 +199,10 @@ class EbuildMetadataPhase(SubProcess):
 # entries for unsupported EAPIs.
 if self.eapi_supported:
 if metadata.get("INHERITED", False):
-metadata[
-"_eclasses_"
-] = self.portdb.repositories.get_repo_for_location(
-self.repo_path
-).eclass_db.get_eclass_data(
-metadata["INHERITED"].split()
+metadata["_eclasses_"] = (
+self.portdb.repositories.get_repo_for_location(
+self.repo_path
+
).eclass_db.get_eclass_data(metadata["INHERITED"].split())
 )
 else:
 metadata["_eclasses_"] = {}

diff --git a/lib/_emerge/MergeListItem.py b/lib/_emerge/MergeListItem.py
index efe485c2e0..ae894704a4 100644
--- a/lib/_emerge/MergeListItem.py
+++ b/lib/_emerge/MergeListItem.py
@@ -13,7 +13,6 @@ from _emerge.PackageUninstall import PackageUninstall
 
 
 class MergeListItem(CompositeTask):
-
 """
 TODO: For parallel scheduling, everything here needs asynchronous
 execution support (start, poll, and wait methods).

diff --git a/lib/_emerge/PipeReader.py b/lib/_emerge/PipeReader.py
index 026346e0bb..76ab7f1882 100644
--- a/lib/_emerge/PipeReader.py
+++ b/lib/_emerge/PipeReader.py
@@ -8,7 +8,6 @@ from _emerge.AbstractPollTask import AbstractPollTask
 
 
 class PipeReader(AbstractPollTask):
-
 """
 Reads output from one or more files and saves it in memory,
 for retrieval via the getvalue() method. This is driven by

diff --git a/lib/_emerge/SpawnProcess.py b/lib/_emerge/SpawnProcess.py
index 72fa72c613..40740df9aa 100644
--- a/lib/_emerge/SpawnProcess.py
+++ b/lib/_emerge/SpawnProcess.py
@@ -16,7 +16,6 @@ from portage.util.futures import asyncio
 
 
 class SpawnProcess(SubProcess):
-
 """
 Constructor keyword args are passed into 

[gentoo-commits] proj/portage:master commit in: lib/portage/util/_dyn_libs/, lib/portage/tests/util/dyn_libs/

2024-01-20 Thread Zac Medico
commit: 95d3e5e80ab9561db870858c2caf6e3bffbf47b0
Author: Zac Medico  gentoo  org>
AuthorDate: Mon Jan 15 20:30:57 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Sat Jan 20 05:18:12 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=95d3e5e8

installed_dynlibs: Resolve *.so symlinks

Resolve *.so symlinks to check if they point to regular files
inside the top directory. If a symlink points outside the top
directory then try to follow the corresponding file inside the
top directory if it exists, and otherwise stop following.

Bug: https://bugs.gentoo.org/921170
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/tests/util/dyn_libs/meson.build|  1 +
 .../tests/util/dyn_libs/test_installed_dynlibs.py  | 65 ++
 lib/portage/util/_dyn_libs/dyn_libs.py | 43 +-
 3 files changed, 106 insertions(+), 3 deletions(-)

diff --git a/lib/portage/tests/util/dyn_libs/meson.build 
b/lib/portage/tests/util/dyn_libs/meson.build
index ddb08f5b1a..8f2c919c13 100644
--- a/lib/portage/tests/util/dyn_libs/meson.build
+++ b/lib/portage/tests/util/dyn_libs/meson.build
@@ -1,5 +1,6 @@
 py.install_sources(
 [
+'test_installed_dynlibs.py',
 'test_soname_deps.py',
 '__init__.py',
 '__test__.py',

diff --git a/lib/portage/tests/util/dyn_libs/test_installed_dynlibs.py 
b/lib/portage/tests/util/dyn_libs/test_installed_dynlibs.py
new file mode 100644
index 00..421dcf6061
--- /dev/null
+++ b/lib/portage/tests/util/dyn_libs/test_installed_dynlibs.py
@@ -0,0 +1,65 @@
+# Copyright 2024 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+import os
+import tempfile
+
+from portage.const import BASH_BINARY
+from portage.tests import TestCase
+from portage.util import ensure_dirs
+from portage.util._dyn_libs.dyn_libs import installed_dynlibs
+from portage.util.file_copy import copyfile
+
+
+class InstalledDynlibsTestCase(TestCase):
+def testInstalledDynlibsRegular(self):
+"""
+Return True for *.so regular files.
+"""
+with tempfile.TemporaryDirectory() as directory:
+bash_copy = os.path.join(directory, "lib", "libfoo.so")
+ensure_dirs(os.path.dirname(bash_copy))
+copyfile(BASH_BINARY, bash_copy)
+self.assertTrue(installed_dynlibs(directory))
+
+def testInstalledDynlibsOnlySymlink(self):
+"""
+If a *.so symlink is installed but does not point to a regular
+file inside the top directory, installed_dynlibs should return
+False (bug 921170).
+"""
+with tempfile.TemporaryDirectory() as directory:
+symlink_path = os.path.join(directory, "lib", "libfoo.so")
+ensure_dirs(os.path.dirname(symlink_path))
+os.symlink(BASH_BINARY, symlink_path)
+self.assertFalse(installed_dynlibs(directory))
+
+def testInstalledDynlibsSymlink(self):
+"""
+Return True for a *.so symlink pointing to a regular file inside
+the top directory.
+"""
+with tempfile.TemporaryDirectory() as directory:
+bash_copy = os.path.join(directory, BASH_BINARY.lstrip(os.sep))
+ensure_dirs(os.path.dirname(bash_copy))
+copyfile(BASH_BINARY, bash_copy)
+symlink_path = os.path.join(directory, "lib", "libfoo.so")
+ensure_dirs(os.path.dirname(symlink_path))
+os.symlink(bash_copy, symlink_path)
+self.assertTrue(installed_dynlibs(directory))
+
+def testInstalledDynlibsAbsoluteSymlink(self):
+"""
+If a *.so symlink target is outside of the top directory,
+traversal follows the corresponding file inside the top
+directory if it exists, and otherwise stops following the
+symlink.
+"""
+with tempfile.TemporaryDirectory() as directory:
+bash_copy = os.path.join(directory, BASH_BINARY.lstrip(os.sep))
+ensure_dirs(os.path.dirname(bash_copy))
+copyfile(BASH_BINARY, bash_copy)
+symlink_path = os.path.join(directory, "lib", "libfoo.so")
+ensure_dirs(os.path.dirname(symlink_path))
+os.symlink(BASH_BINARY, symlink_path)
+self.assertTrue(installed_dynlibs(directory))

diff --git a/lib/portage/util/_dyn_libs/dyn_libs.py 
b/lib/portage/util/_dyn_libs/dyn_libs.py
index ee28e8839c..6f8a07d70d 100644
--- a/lib/portage/util/_dyn_libs/dyn_libs.py
+++ b/lib/portage/util/_dyn_libs/dyn_libs.py
@@ -1,14 +1,51 @@
-# Copyright 2021 Gentoo Authors
+# Copyright 2021-2024 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import os
+import stat
+
+import portage
 
 
 def installed_dynlibs(directory):
-for _dirpath, _dirnames, filenames in os.walk(directory):
+"""
+This traverses installed *.so symlinks to check if they point to
+regular files. If a symlink 

[gentoo-commits] proj/portage:master commit in: lib/portage/dbapi/

2024-01-15 Thread Sam James
commit: adc0052b2c022851458f788868e6b194ed1cfe9f
Author: Sam James  gentoo  org>
AuthorDate: Tue Jan 16 07:51:47 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Tue Jan 16 07:52:11 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=adc0052b

bintree: add missing newlines to signed binpkg update notice

Signed-off-by: Sam James  gentoo.org>

 lib/portage/dbapi/bintree.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/portage/dbapi/bintree.py b/lib/portage/dbapi/bintree.py
index ee574f435f..f4251b47d6 100644
--- a/lib/portage/dbapi/bintree.py
+++ b/lib/portage/dbapi/bintree.py
@@ -306,7 +306,7 @@ class bindbapi(fakedbapi):
 colorize(
 "WARN",
 f"Binpkg update ignored for signed package: 
{binpkg_path}, "
-"the file will be removed.",
+"the file will be removed.\n",
 )
 )
 self.bintree.remove(cpv)
@@ -734,7 +734,7 @@ class binarytree:
 writemsg(
 colorize(
 "WARN",
-f"Binpkg update ignored for signed package: 
{binpkg_path}",
+f"Binpkg update ignored for signed package: 
{binpkg_path}\n",
 )
 )
 continue



[gentoo-commits] proj/portage:master commit in: lib/portage/dbapi/

2024-01-15 Thread Zac Medico
commit: 68f4ea8a90d8759a1aa859d9188017e21797bdd0
Author: Zac Medico  gentoo  org>
AuthorDate: Tue Jan 16 01:08:36 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Tue Jan 16 05:25:20 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=68f4ea8a

Handle SignatureException during package moves

Ignore package moves for packages that raise SignatureException,
just as they are ignored for packages that have a valid signature.

Bug: https://bugs.gentoo.org/922142
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/dbapi/bintree.py | 16 
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/lib/portage/dbapi/bintree.py b/lib/portage/dbapi/bintree.py
index 2342d571b9..ee574f435f 100644
--- a/lib/portage/dbapi/bintree.py
+++ b/lib/portage/dbapi/bintree.py
@@ -296,8 +296,12 @@ class bindbapi(fakedbapi):
 encoding_key = True
 elif binpkg_format == "gpkg":
 mybinpkg = portage.gpkg.gpkg(self.settings, cpv_str, binpkg_path)
-mydata = mybinpkg.get_metadata()
-if mybinpkg.signature_exist:
+try:
+mydata = mybinpkg.get_metadata()
+signature_exist = mybinpkg.signature_exist
+except SignatureException:
+signature_exist = True
+if signature_exist:
 writemsg(
 colorize(
 "WARN",
@@ -721,8 +725,12 @@ class binarytree:
 decode_metadata_name = False
 elif binpkg_format == "gpkg":
 mybinpkg = portage.gpkg.gpkg(self.settings, mycpv, binpkg_path)
-mydata = mybinpkg.get_metadata()
-if mybinpkg.signature_exist:
+try:
+mydata = mybinpkg.get_metadata()
+signature_exist = mybinpkg.signature_exist
+except SignatureException:
+signature_exist = True
+if signature_exist:
 writemsg(
 colorize(
 "WARN",



[gentoo-commits] proj/portage:master commit in: lib/portage/dbapi/

2024-01-15 Thread Sam James
commit: c9ed8136f9f0c56dc7b72d400eaa0acbd338778f
Author: Konstantin Tokarev  yandex  ru>
AuthorDate: Fri Jan 12 17:47:45 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Tue Jan 16 05:16:08 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=c9ed8136

vartree: _needs_move() should replace file if filecmp fails for whatever reason

When trying to update I've got "OSError: [Errno 5] Input/output error"
exception traceback from portage originating from _needs_move(). It turned
out that 3 files in my root filesystem were corrupted, which caused filecmp
to fail. This patch makes portage replace original file in this case.

Bug: https://bugs.gentoo.org/722270
Signed-off-by: Konstantin Tokarev  yandex.ru>
Closes: https://github.com/gentoo/portage/pull/1233
Signed-off-by: Sam James  gentoo.org>

 lib/portage/dbapi/vartree.py | 14 +-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/lib/portage/dbapi/vartree.py b/lib/portage/dbapi/vartree.py
index 5d39ca1965..a00c731c57 100644
--- a/lib/portage/dbapi/vartree.py
+++ b/lib/portage/dbapi/vartree.py
@@ -6283,7 +6283,19 @@ class dblink:
 if not _cmpxattr(src_bytes, dest_bytes, exclude=excluded_xattrs):
 return True
 
-return not filecmp.cmp(src_bytes, dest_bytes, shallow=False)
+try:
+files_equal = filecmp.cmp(src_bytes, dest_bytes, shallow=False)
+except Exception as e:
+writemsg(
+_(
+"Exception '%s' happened when comparing files %s and %s, 
will replace the latter\n"
+)
+% (e, mysrc, mydest),
+noiselevel=-1,
+)
+return True
+
+return not files_equal
 
 
 def merge(



[gentoo-commits] proj/portage:master commit in: lib/portage/dbapi/

2024-01-15 Thread Sam James
commit: 58b094bc79e999e44a5b108e2b7273c164aa906e
Author: Alfred Wingate  protonmail  com>
AuthorDate: Fri Jan  5 19:30:38 2024 +
Commit: Sam James  gentoo  org>
CommitDate: Tue Jan 16 05:16:09 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=58b094bc

bintree: use urllib provided attributes for hostname, user and password

In addition to simplifying it also solves an issue with parsing raw
ipv6 addresses.

Bug: https://bugs.gentoo.org/921400
Signed-off-by: Alfred Wingate  protonmail.com>
Closes: https://github.com/gentoo/portage/pull/1230
Signed-off-by: Sam James  gentoo.org>

 lib/portage/dbapi/bintree.py | 18 --
 1 file changed, 4 insertions(+), 14 deletions(-)

diff --git a/lib/portage/dbapi/bintree.py b/lib/portage/dbapi/bintree.py
index 5fc4716980..2342d571b9 100644
--- a/lib/portage/dbapi/bintree.py
+++ b/lib/portage/dbapi/bintree.py
@@ -1344,23 +1344,13 @@ class binarytree:
 for repo in reversed(list(self._binrepos_conf.values())):
 base_url = repo.sync_uri
 parsed_url = urlparse(base_url)
-host = parsed_url.netloc
+host = parsed_url.hostname or ""
 port = parsed_url.port
-user = None
-passwd = None
-user_passwd = ""
+user = parsed_url.username
+passwd = parsed_url.password
+user_passwd = user + "@" if user else ""
 gpkg_only_warned = False
 
-if "@" in host:
-user, host = host.split("@", 1)
-user_passwd = user + "@"
-if ":" in user:
-user, passwd = user.split(":", 1)
-
-if port is not None:
-port_str = f":{port}"
-if host.endswith(port_str):
-host = host[: -len(port_str)]
 pkgindex_file = os.path.join(
 self.settings["EROOT"],
 CACHE_PATH,



[gentoo-commits] proj/portage:master commit in: lib/portage/tests/resolver/, lib/_emerge/

2024-01-08 Thread Zac Medico
commit: 4b885b9ca063c990b1e218c73a786e9d434717e8
Author: Zac Medico  gentoo  org>
AuthorDate: Mon Jan  8 06:04:37 2024 +
Commit: Zac Medico  gentoo  org>
CommitDate: Mon Jan  8 08:08:51 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=4b885b9c

_calc_depclean: add dep_check action

Add a dep_check action which can be used to check the
dependencies of all installed packages. The plan is for depgraph
to use this action to check for broken dependencies prior to the
merge order calculation. The new frozen_config parameter will
allow depgraph to pass a shared frozen config to _calc_depclean.

The result of the dep_check action becomes stale as soon as there
is any change to the installed packages. So, in order to account
for dependencies that may become broken or satisfied during the
process of updating installed packages, the merge order
calculation will need to refresh the dep_check calculation for
every merge order choice that it makes. This refresh will need
to be optimized to identify the portion of the graph that would
become stale due to a given change, so that it can avoid
unnecessary repetition of work.

Bug: https://bugs.gentoo.org/921333
Signed-off-by: Zac Medico  gentoo.org>

 lib/_emerge/actions.py   | 21 ++-
 lib/_emerge/depgraph.py  |  7 ++-
 lib/portage/tests/resolver/ResolverPlayground.py | 43 --
 lib/portage/tests/resolver/meson.build   |  1 +
 lib/portage/tests/resolver/test_broken_deps.py   | 76 
 5 files changed, 138 insertions(+), 10 deletions(-)

diff --git a/lib/_emerge/actions.py b/lib/_emerge/actions.py
index 20f3978f77..2710c4856c 100644
--- a/lib/_emerge/actions.py
+++ b/lib/_emerge/actions.py
@@ -909,7 +909,16 @@ _depclean_result = collections.namedtuple(
 )
 
 
-def _calc_depclean(settings, trees, ldpath_mtimes, myopts, action, args_set, 
spinner):
+def _calc_depclean(
+settings,
+trees,
+ldpath_mtimes,
+myopts,
+action,
+args_set,
+spinner,
+frozen_config=None,
+):
 allow_missing_deps = bool(args_set)
 
 debug = "--debug" in myopts
@@ -988,12 +997,14 @@ def _calc_depclean(settings, trees, ldpath_mtimes, 
myopts, action, args_set, spi
 
 writemsg_level("\nCalculating dependencies  ")
 resolver_params = create_depgraph_params(myopts, "remove")
-resolver = depgraph(settings, trees, myopts, resolver_params, spinner)
+resolver = depgraph(
+settings, trees, myopts, resolver_params, spinner, 
frozen_config=frozen_config
+)
 resolver._load_vdb()
 vardb = resolver._frozen_config.trees[eroot]["vartree"].dbapi
 real_vardb = trees[eroot]["vartree"].dbapi
 
-if action == "depclean":
+if action in ("dep_check", "depclean"):
 if args_set:
 if deselect:
 # Start with an empty set.
@@ -1002,6 +1013,7 @@ def _calc_depclean(settings, trees, ldpath_mtimes, 
myopts, action, args_set, spi
 # Pull in any sets nested within the selected set.
 selected_set.update(psets["selected"].getNonAtoms())
 
+if args_set or action == "dep_check":
 # Pull in everything that's installed but not matched
 # by an argument atom since we don't want to clean any
 # package if something depends on it.
@@ -1098,6 +1110,9 @@ def _calc_depclean(settings, trees, ldpath_mtimes, 
myopts, action, args_set, spi
 if not success:
 return _depclean_result(1, [], False, 0, resolver)
 
+if action == "dep_check":
+return _depclean_result(0, [], False, 0, resolver)
+
 def unresolved_deps():
 soname_deps = set()
 unresolvable = set()

diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py
index a2865cad23..b859e68224 100644
--- a/lib/_emerge/depgraph.py
+++ b/lib/_emerge/depgraph.py
@@ -11723,6 +11723,7 @@ def backtrack_depgraph(
 myaction: Optional[str],
 myfiles: list[str],
 spinner: "_emerge.stdout_spinner.stdout_spinner",
+frozen_config: Optional[_frozen_depgraph_config] = None,
 ) -> tuple[Any, depgraph, list[str]]:
 """
 
@@ -11747,6 +11748,7 @@ def _backtrack_depgraph(
 myaction: Optional[str],
 myfiles: list[str],
 spinner: "_emerge.stdout_spinner.stdout_spinner",
+frozen_config: Optional[_frozen_depgraph_config] = None,
 ) -> tuple[Any, depgraph, list[str], int, int]:
 debug = "--debug" in myopts
 mydepgraph = None
@@ -11756,7 +11758,10 @@ def _backtrack_depgraph(
 backtracker = Backtracker(max_depth)
 backtracked = 0
 
-frozen_config = _frozen_depgraph_config(settings, trees, myopts, myparams, 
spinner)
+if frozen_config is None:
+frozen_config = _frozen_depgraph_config(
+settings, trees, myopts, myparams, spinner
+)
 
 while backtracker:
 if debug and mydepgraph is not None:

diff --git a/lib/portage/tests/resolver/ResolverPlayground.py 

  1   2   3   4   5   6   7   8   9   10   >