commit: 16655f8588e331766944d61402736be381001aab
Author: Florian Schmaus <flow <AT> gentoo <DOT> org>
AuthorDate: Tue Oct 28 08:05:30 2025 +0000
Commit: Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Sat Nov 1 09:48:56 2025 +0000
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=16655f85
bintree: fix AttributeError by swapping try and for-loop
Swap the try-block and for-loop in _populate_remote().
Instead of the try-block being within the for-loop, the try-block is
no the outermost block containing the for-loop. This is sensible
because previously the beginning of the for-loop's body contained some
logic that was unrelated to the for-loop, i.e., the checks if the repo
is frozen or if the currently cached index is within its TTL.
This also fixes an AttributeError where in
ttl = float(pkgindex.header.get("TTL", 0))
where pkgindex would be None in a subsequent iteration of the
loop. Now this code resides outside the for-loop (where it belongs).
This fixes the issue initially reported in
https://bugs.gentoo.org/965098#c0
Furthermore, we move the finally block where we kill the download proc
and unlink the tmp file, which was previously attached to the
outermost try-block, to the innermost try-block because it needs to be
executed on every download attempt.
Bug: https://bugs.gentoo.org/965098
Signed-off-by: Florian Schmaus <flow <AT> gentoo.org>
Part-of: https://github.com/gentoo/portage/pull/1490
Signed-off-by: Sam James <sam <AT> gentoo.org>
lib/portage/dbapi/bintree.py | 121 ++++++++++++++++++++-----------------------
1 file changed, 57 insertions(+), 64 deletions(-)
diff --git a/lib/portage/dbapi/bintree.py b/lib/portage/dbapi/bintree.py
index ebd0ec08c0..55c1298705 100644
--- a/lib/portage/dbapi/bintree.py
+++ b/lib/portage/dbapi/bintree.py
@@ -1419,31 +1419,31 @@ class binarytree:
rmt_idx = self._new_pkgindex()
proc = None
tmp_filename = None
- for remote_pkgindex_file in ("Packages.gz", "Packages"):
+ try:
+ if local_timestamp and (repo.frozen or not getbinpkg_refresh):
+ if repo.frozen:
+ raise UseCachedCopyOfRemoteIndex("frozen")
+ raise UseCachedCopyOfRemoteIndex("")
+
try:
+ ttl = float(pkgindex.header.get("TTL", 0))
+ except ValueError:
+ pass
+ else:
+ if (
+ download_timestamp
+ and ttl
+ and download_timestamp + ttl > time.time()
+ ):
+ raise UseCachedCopyOfRemoteIndex("within TTL")
+
+ for remote_pkgindex_file in ("Packages.gz", "Packages"):
# urlparse.urljoin() only works correctly with recognized
# protocols and requires the base url to have a trailing
# slash, so join manually...
url = base_url.rstrip("/") + "/" + remote_pkgindex_file
f = None
- if local_timestamp and (repo.frozen or not
getbinpkg_refresh):
- if repo.frozen:
- raise UseCachedCopyOfRemoteIndex("frozen")
- raise UseCachedCopyOfRemoteIndex("")
-
- try:
- ttl = float(pkgindex.header.get("TTL", 0))
- except ValueError:
- pass
- else:
- if (
- download_timestamp
- and ttl
- and download_timestamp + ttl > time.time()
- ):
- raise UseCachedCopyOfRemoteIndex("within TTL")
-
# Set proxy settings for _urlopen -> urllib_request
proxies = {}
for proto in ("http", "https"):
@@ -1519,6 +1519,28 @@ class binarytree:
raise UseCachedCopyOfRemoteIndex(
"up-to-date", extra_info
)
+ if (
+ remote_pkgindex_file == "Packages.gz"
+ and isinstance(err, urllib.error.HTTPError)
+ and err.code == 404
+ ):
+ # Ignore 404s for Packages.gz, as the file is
+ # not guaranteed to exist.
+ continue
+
+ # This includes URLError which is raised for SSL
+ # certificate errors when PEP 476 is supported.
+ writemsg(
+ _(
+ "\n\n!!! [%s] Error fetching binhost
package"
+ " info from '%s'\n"
+ )
+ % (binrepo_name, _hide_url_passwd(base_url))
+ )
+ error_msg = str(err)
+ writemsg(f"!!!{binrepo_name} {error_msg}\n\n")
+ del err
+ pkgindex = None
if parsed_url.scheme in ("ftp", "http", "https"):
# This protocol is supposedly supported by
urlopen,
@@ -1654,57 +1676,28 @@ class binarytree:
),
noiselevel=-1,
)
+ if proc is not None:
+ if proc.poll() is None:
+ proc.kill()
+ proc.wait()
+ proc = None
+ if tmp_filename is not None:
+ try:
+ os.unlink(tmp_filename)
+ except OSError:
+ pass
# We successfully fetched the remote index, break
# out of the ("Packages.gz", "Packages") loop.
break
- except UseCachedCopyOfRemoteIndex as exc:
- changed = False
- rmt_idx = pkgindex
- if getbinpkg_refresh or repo.frozen:
- extra_info = exc.extra_info if verbose else ""
- writemsg(
- _(
- "[%s] Local copy of remote index is %s and
will be used%s.\n"
- )
- % (binrepo_name, exc.desc, extra_info),
- )
- # We are using the cached index, break out of the
- # ("Packages.gz", "Packages") loop.
- break
- except OSError as e:
- if (
- remote_pkgindex_file == "Packages.gz"
- and isinstance(e, urllib.error.HTTPError)
- and e.code == 404
- ):
- # Ignore 404s for Packages.gz, as the file is
- # not guaranteed to exist.
- continue
-
- # This includes URLError which is raised for SSL
- # certificate errors when PEP 476 is supported.
+ except UseCachedCopyOfRemoteIndex as exc:
+ changed = False
+ rmt_idx = pkgindex
+ if getbinpkg_refresh or repo.frozen:
+ extra_info = exc.extra_info if verbose else ""
writemsg(
- _(
- "\n\n!!! [%s] Error fetching binhost package"
- " info from '%s'\n"
- )
- % (binrepo_name, _hide_url_passwd(base_url))
+ _("[%s] Local copy of remote index is %s and will be
used%s.\n")
+ % (binrepo_name, exc.desc, extra_info),
)
- error_msg = str(e)
- writemsg(f"!!!{binrepo_name} {error_msg}\n\n")
- del e
- pkgindex = None
- finally:
- if proc is not None:
- if proc.poll() is None:
- proc.kill()
- proc.wait()
- proc = None
- if tmp_filename is not None:
- try:
- os.unlink(tmp_filename)
- except OSError:
- pass
if pkgindex is rmt_idx and changed:
pkgindex.modified = False # don't update the header