1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
# Copyright 2014-2020 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
import errno
import io
import functools
import operator
import os
import portage
from portage import _encodings
from portage.dep import Atom
from portage.exception import FileNotFound
from portage.cache.index.IndexStreamIterator import IndexStreamIterator
from portage.cache.index.pkg_desc_index import (
pkg_desc_index_line_read,
pkg_desc_index_node,
)
from portage.util.iterators.MultiIterGroupBy import MultiIterGroupBy
class IndexedPortdb:
"""
A portdbapi interface that uses a package description index to
improve performance. If the description index is missing for a
particular repository, then all metadata for that repository is
obtained using the normal pordbapi.aux_get method.
For performance reasons, the match method only supports package
name and version constraints. For the same reason, the xmatch
method is not implemented.
"""
# Match returns unordered results.
match_unordered = True
_copy_attrs = (
"cpv_exists",
"findname",
"getFetchMap",
"_aux_cache_keys",
"_cpv_sort_ascending",
"_have_root_eclass_dir",
)
def __init__(self, portdb):
self._portdb = portdb
for k in self._copy_attrs:
setattr(self, k, getattr(portdb, k))
self._desc_cache = None
self._cp_map = None
self._unindexed_cp_map = None
def _init_index(self):
cp_map = {}
desc_cache = {}
self._desc_cache = desc_cache
self._cp_map = cp_map
index_missing = []
streams = []
for repo_path in self._portdb.porttrees:
outside_repo = os.path.join(
self._portdb.depcachedir, repo_path.lstrip(os.sep)
)
filenames = []
for parent_dir in (repo_path, outside_repo):
filenames.append(os.path.join(parent_dir, "metadata", "pkg_desc_index"))
repo_name = self._portdb.getRepositoryName(repo_path)
try:
f = None
for filename in filenames:
try:
f = io.open(filename, encoding=_encodings["repo.content"])
except IOError as e:
if e.errno not in (errno.ENOENT, errno.ESTALE):
raise
else:
break
if f is None:
raise FileNotFound(filename)
streams.append(
iter(
IndexStreamIterator(
f,
functools.partial(pkg_desc_index_line_read, repo=repo_name),
)
)
)
except FileNotFound:
index_missing.append(repo_path)
if index_missing:
self._unindexed_cp_map = {}
class _NonIndexedStream:
def __iter__(self_):
for cp in self._portdb.cp_all(trees=index_missing):
# Don't call cp_list yet, since it's a waste
# if the package name does not match the current
# search.
self._unindexed_cp_map[cp] = index_missing
yield pkg_desc_index_node(cp, (), None)
streams.append(iter(_NonIndexedStream()))
if streams:
if len(streams) == 1:
cp_group_iter = ([node] for node in streams[0])
else:
cp_group_iter = MultiIterGroupBy(streams, key=operator.attrgetter("cp"))
for cp_group in cp_group_iter:
new_cp = None
cp_list = cp_map.get(cp_group[0].cp)
if cp_list is None:
new_cp = cp_group[0].cp
cp_list = []
cp_map[cp_group[0].cp] = cp_list
for entry in cp_group:
cp_list.extend(entry.cpv_list)
if entry.desc is not None:
for cpv in entry.cpv_list:
desc_cache[cpv] = entry.desc
if new_cp is not None:
yield cp_group[0].cp
def cp_all(self, sort=True):
"""
Returns an ordered iterator instead of a list, so that search
results can be displayed incrementally.
"""
if self._cp_map is None:
return self._init_index()
return iter(sorted(self._cp_map)) if sort else iter(self._cp_map)
def match(self, atom):
"""
For performance reasons, only package name and version
constraints are supported, and the returned list is
unordered.
"""
if not isinstance(atom, Atom):
atom = Atom(atom)
cp_list = self._cp_map.get(atom.cp)
if cp_list is None:
return []
if self._unindexed_cp_map is not None:
try:
unindexed = self._unindexed_cp_map.pop(atom.cp)
except KeyError:
pass
else:
cp_list.extend(self._portdb.cp_list(atom.cp, mytree=unindexed))
if atom == atom.cp:
return cp_list[:]
return portage.match_from_list(atom, cp_list)
def aux_get(self, cpv, attrs, myrepo=None):
if len(attrs) == 1 and attrs[0] == "DESCRIPTION":
try:
return [self._desc_cache[cpv]]
except KeyError:
pass
return self._portdb.aux_get(cpv, attrs)
|