]> git.ipfire.org Git - people/ms/pakfire.git/blob - src/pakfire/base.py
libpakfire: Export repositories to Python
[people/ms/pakfire.git] / src / pakfire / base.py
1 #!/usr/bin/python3
2 ###############################################################################
3 # #
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2011 Pakfire development team #
6 # #
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 3 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, see <http://www.gnu.org/licenses/>. #
19 # #
20 ###############################################################################
21
22 import logging
23 import os
24 import random
25 import string
26
27 from . import _pakfire
28 from . import distro
29 from . import logger
30 from . import packages
31 from . import util
32
33 from .config import Config
34 from .system import system
35
36 from .constants import *
37 from .i18n import _
38
39 class Pakfire(_pakfire.Pakfire):
40 __version__ = PAKFIRE_VERSION
41
42 def __init__(self, path=None, config=None, arch=None, distro=None, offline=False):
43 _pakfire.Pakfire.__init__(self, path, arch, offline=offline)
44
45 # Initialise logging system
46 self.log = self._setup_logger()
47
48 # Default to system distribution
49 self.distro = distro or system.distro
50
51 # Load configuration
52 self.config = config or Config("general.conf")
53
54 def _setup_logger(self):
55 log = logging.getLogger("pakfire")
56 log.propagate = 0
57
58 # Always process all messages (include debug)
59 log.setLevel(logging.DEBUG)
60
61 # Pass everything down to libpakfire
62 handler = logger.PakfireLogHandler(self)
63 log.addHandler(handler)
64
65 return log
66
67 def __enter__(self):
68 """
69 Called to initialize this Pakfire instance when
70 the context is entered.
71 """
72 # Dump the configuration when we enter the context
73 self.config.dump()
74
75 # Refresh repositories
76 self.refresh_repositories()
77
78 return PakfireContext(self)
79
80 def __exit__(self, type, value, traceback):
81 pass
82
83 def refresh_repositories(self, force=False):
84 for repo in self.repos:
85 repo.refresh(force=force)
86
87 def clean(self):
88 # Clean up repository caches
89 for repo in self.repos:
90 repo.clean()
91
92
93 class PakfireContext(object):
94 """
95 This context has functions that require
96 pakfire to be initialized.
97
98 That means that repository data has to be downloaded
99 and imported to be searchable, etc.
100 """
101 def __init__(self, pakfire):
102 self.pakfire = pakfire
103
104 @property
105 def repos(self):
106 """
107 Shortcut to access any configured
108 repositories for this Pakfire instance
109 """
110 return self.pakfire.repos
111
112 def check(self, **kwargs):
113 """
114 Try to fix any errors in the system.
115 """
116 # Detect any errors in the dependency tree.
117 # For that we create an empty request and solver and try to solve
118 # something.
119 request = _pakfire.Request(self.pakfire)
120 request.verify()
121
122 return request.solve(**kwargs)
123
124 def info(self, args):
125 pkgs = []
126
127 with _pakfire.Repo(self.pakfire, "tmp", clean=True) as r:
128 for arg in args:
129 if os.path.exists(arg) and not os.path.isdir(arg):
130 archive = _pakfire.Archive(self.pakfire, arg)
131
132 # Add the archive to the repository
133 pkg = r.add_archive(archive)
134 pkgs.append(pkg)
135
136 else:
137 pkgs += self.pakfire.whatprovides(arg, name_only=True)
138
139 return sorted(pkgs)
140
141 def provides(self, patterns):
142 pkgs = []
143
144 for pattern in patterns:
145 for pkg in self.pakfire.whatprovides(pattern):
146 if pkg in pkgs:
147 continue
148
149 pkgs.append(pkg)
150
151 return sorted(pkgs)
152
153 def search(self, pattern):
154 return self.pakfire.search(pattern)
155
156 def extract(self, filenames, target=None):
157 if target and target == "/":
158 raise ValueError("Cannot extract to: %s" % target)
159
160 archives = []
161
162 # Open all archives
163 for filename in filenames:
164 a = _pakfire.Archive(self.pakfire, filename)
165 archives.append(a)
166
167 # Nothing to do when no archives where opened
168 if not archives:
169 return
170
171 # Extract them all
172 for archive in archives:
173 archive.extract(target)
174
175 # Transactions
176
177 def install(self, requires, **kwargs):
178 request = _pakfire.Request(self.pakfire)
179
180 # XXX handle files and URLs
181
182 for req in requires:
183 # Handle groups
184 # TODO should move into libpakfire
185 if req.startswith("@"):
186 sel = _pakfire.Selector(self.pakfire)
187 sel.set(_pakfire.PAKFIRE_PKG_GROUP, _pakfire.PAKFIRE_EQ, req[1:])
188 request.install(sel)
189 continue
190
191 # Handle everything else
192 relation = _pakfire.Relation(self.pakfire, req)
193 request.install(relation)
194
195 return request.solve(**kwargs)
196
197 def reinstall(self, pkgs, strict=False, logger=None):
198 """
199 Reinstall one or more packages
200 """
201 raise NotImplementedError
202
203 def erase(self, pkgs, **kwargs):
204 request = _pakfire.Request(self.pakfire)
205
206 for pkg in pkgs:
207 relation = _pakfire.Relation(self.pakfire, pkg)
208 request.erase(relation)
209
210 return request.solve(**kwargs)
211
212 def update(self, reqs=None, excludes=None, **kwargs):
213 request = _pakfire.Request(self.pakfire)
214
215 # Add all packages that should be updated to the request
216 for req in reqs or []:
217 relation = _pakfire.Relation(self.pakfire, req)
218 request.upgrade(relation)
219
220 # Otherwise we will try to upgrade everything
221 else:
222 request.upgrade_all()
223
224 # Exclude packages that should not be updated
225 for exclude in excludes or []:
226 relation = _pakfire.Relation(self.pakfire, exclude)
227 request.lock(relation)
228
229 return request.solve(**kwargs)
230
231 def downgrade(self, pkgs, logger=None, **kwargs):
232 assert pkgs
233
234 if logger is None:
235 logger = logging.getLogger("pakfire")
236
237 # Create a new request.
238 request = self.pakfire.create_request()
239
240 # Fill request.
241 for pattern in pkgs:
242 best = None
243 for pkg in self.pakfire.repos.whatprovides(pattern):
244 # Only consider installed packages.
245 if not pkg.is_installed():
246 continue
247
248 if best and pkg > best:
249 best = pkg
250 elif best is None:
251 best = pkg
252
253 if best is None:
254 logger.warning(_("\"%s\" package does not seem to be installed.") % pattern)
255 else:
256 rel = self.pakfire.create_relation("%s < %s" % (best.name, best.friendly_version))
257 request.install(rel)
258
259 # Solve the request.
260 solver = self.pakfire.solve(request, allow_downgrade=True, **kwargs)
261 assert solver.status is True
262
263 # Create the transaction.
264 t = transaction.Transaction.from_solver(self.pakfire, solver)
265 t.dump(logger=logger)
266
267 if not t:
268 logger.info(_("Nothing to do"))
269 return
270
271 if not t.cli_yesno():
272 return
273
274 t.run()
275
276
277 class PakfireKey(Pakfire):
278 pass