Coverage for daklib/ls.py: 63%
57 statements
« prev ^ index » next coverage.py v7.6.0, created at 2026-08-03 16:46 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2026-08-03 16:46 +0000
1"""List packages according to various criteria
3@copyright: 2014, Ansgar Burchardt <ansgar@debian.org>
4@license: GPL-2+
5"""
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21from collections import defaultdict
22from collections.abc import Collection, Iterable
24import sqlalchemy.dialects.postgresql as pgsql
25from sqlalchemy import ColumnElement, sql
27from daklib.dbconn import DBConn, package_list
30def list_packages(
31 packages: Collection[str],
32 suites: Collection[str] | None = None,
33 components: Collection[str] | None = None,
34 architectures: Collection[str] | None = None,
35 binary_types: Collection[str] | None = None,
36 source_and_binary=False,
37 regex=False,
38 format: str | None = None,
39 highest: str | None = None,
40) -> Iterable[str] | Iterable[dict[str, dict[str, dict[str, dict[str, str]]]]]:
41 session = DBConn().session()
42 try:
43 t = package_list
45 comparison_operator = "~" if regex else "="
47 where: ColumnElement[bool] = sql.false()
48 for package in packages:
49 where = where | t.c.package.op(comparison_operator)(package)
50 if source_and_binary:
51 where = where | t.c.source.op(comparison_operator)(package)
53 if suites is not None:
54 where = where & (t.c.suite.in_(suites) | t.c.codename.in_(suites))
55 if components is not None: 55 ↛ 56line 55 didn't jump to line 56 because the condition on line 55 was never true
56 where = where & t.c.component.in_(components)
57 if architectures is not None: 57 ↛ 58line 57 didn't jump to line 58 because the condition on line 57 was never true
58 where = where & t.c.architecture.in_(architectures)
59 if binary_types is not None: 59 ↛ 60line 59 didn't jump to line 60 because the condition on line 59 was never true
60 where = where & t.c.type.in_(binary_types)
62 if format is None:
63 c_architectures = sql.func.string_agg(
64 t.c.architecture,
65 pgsql.aggregate_order_by(
66 sql.literal(", "),
67 t.c.architecture_is_source.desc(),
68 t.c.architecture,
69 ),
70 )
71 query = (
72 sql.select(t.c.package, t.c.version, t.c.display_suite, c_architectures)
73 .where(where)
74 .group_by(t.c.package, t.c.version, t.c.display_suite)
75 .order_by(t.c.package, t.c.version, t.c.display_suite)
76 )
77 result = session.execute(query).mappings().all()
79 if len(result) == 0:
80 return
82 lengths = {
83 "package": max(10, max(len(row[t.c.package]) for row in result)),
84 "version": max(13, max(len(row[t.c.version]) for row in result)),
85 "suite": max(10, max(len(row[t.c.display_suite]) for row in result)),
86 }
87 format = "{0:{lengths[package]}} | {1:{lengths[version]}} | {2:{lengths[suite]}} | {3}"
89 for row in result:
90 yield format.format(
91 row[t.c.package],
92 row[t.c.version],
93 row[t.c.display_suite],
94 row[c_architectures],
95 lengths=lengths,
96 )
97 elif format in ("control-suite", "heidi"): 97 ↛ 103line 97 didn't jump to line 103 because the condition on line 97 was always true
98 query = sql.select(t.c.package, t.c.version, t.c.architecture).where(where)
99 for row in session.execute(query).mappings():
100 yield "{0} {1} {2}".format(
101 row[t.c.package], row[t.c.version], row[t.c.architecture]
102 )
103 elif format == "python":
104 c_architectures = sql.func.string_agg(
105 t.c.architecture,
106 pgsql.aggregate_order_by(
107 sql.literal(","),
108 t.c.architecture_is_source.desc(),
109 t.c.architecture,
110 ),
111 )
112 query = (
113 sql.select(
114 t.c.package,
115 t.c.version,
116 t.c.display_suite,
117 c_architectures,
118 t.c.source,
119 t.c.source_version,
120 t.c.component,
121 )
122 .where(where)
123 .group_by(
124 t.c.package,
125 t.c.version,
126 t.c.display_suite,
127 t.c.source,
128 t.c.component,
129 t.c.source_version,
130 )
131 )
132 result = session.execute(query).mappings().all()
134 if len(result) == 0:
135 return
137 def val():
138 return defaultdict(val)
140 ret = val()
141 for row in result:
142 ret[row[t.c.package]][row[t.c.display_suite]][row[t.c.version]] = {
143 "component": row[t.c.component],
144 "architectures": row[c_architectures].split(","),
145 "source": row[t.c.source],
146 "source_version": row[t.c.source_version],
147 }
149 yield ret
150 return
151 else:
152 raise ValueError("Unknown output format requested.")
154 if highest is not None: 154 ↛ 155line 154 didn't jump to line 155
155 query = (
156 sql.select(t.c.package, sql.func.max(t.c.version))
157 .where(where)
158 .group_by(t.c.package)
159 .order_by(t.c.package)
160 )
161 yield ""
162 for r in session.execute(query):
163 yield "{0} ({1} {2})".format(r[0], highest, r[1])
164 finally:
165 session.close()