Lorenzob commited on
Commit
859a779
·
verified ·
1 Parent(s): 85e17ff

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. .gitlab-ci.yml +35 -0
  3. .travis.yml +14 -0
  4. AUTHORS +7 -0
  5. COPYING.GPL +340 -0
  6. Dockerfile +11 -0
  7. README.md +49 -17
  8. TODO +4 -0
  9. apt/__init__.py +38 -0
  10. apt/auth.py +309 -0
  11. apt/cache.py +1038 -0
  12. apt/cdrom.py +92 -0
  13. apt/debfile.py +868 -0
  14. apt/package.py +1604 -0
  15. apt/progress/__init__.py +31 -0
  16. apt/progress/base.py +354 -0
  17. apt/progress/text.py +293 -0
  18. apt/py.typed +0 -0
  19. apt/utils.py +99 -0
  20. aptsources/__init__.py +9 -0
  21. aptsources/distinfo.py +393 -0
  22. aptsources/distro.py +613 -0
  23. aptsources/sourceslist.py +516 -0
  24. build/data/templates/Blankon.info +370 -0
  25. build/data/templates/Blankon.mirrors +17 -0
  26. build/data/templates/Debian.info +95 -0
  27. build/data/templates/Debian.mirrors +371 -0
  28. build/data/templates/Kali.info +32 -0
  29. build/data/templates/Kali.mirrors +2 -0
  30. build/data/templates/Tanglu.info +42 -0
  31. build/data/templates/Tanglu.mirrors +3 -0
  32. build/data/templates/Ubuntu.info +223 -0
  33. build/data/templates/Ubuntu.mirrors +659 -0
  34. build/data/templates/gNewSense.info +55 -0
  35. build/data/templates/gNewSense.mirrors +489 -0
  36. build/lib.linux-x86_64-cpython-310/apt/__init__.py +38 -0
  37. build/lib.linux-x86_64-cpython-310/apt/auth.py +309 -0
  38. build/lib.linux-x86_64-cpython-310/apt/cache.py +1038 -0
  39. build/lib.linux-x86_64-cpython-310/apt/cdrom.py +92 -0
  40. build/lib.linux-x86_64-cpython-310/apt/debfile.py +868 -0
  41. build/lib.linux-x86_64-cpython-310/apt/package.py +1604 -0
  42. build/lib.linux-x86_64-cpython-310/apt/progress/__init__.py +31 -0
  43. build/lib.linux-x86_64-cpython-310/apt/progress/base.py +354 -0
  44. build/lib.linux-x86_64-cpython-310/apt/progress/text.py +293 -0
  45. build/lib.linux-x86_64-cpython-310/apt/py.typed +0 -0
  46. build/lib.linux-x86_64-cpython-310/apt/utils.py +99 -0
  47. build/lib.linux-x86_64-cpython-310/apt_inst.cpython-310-x86_64-linux-gnu.so +0 -0
  48. build/lib.linux-x86_64-cpython-310/apt_pkg.cpython-310-x86_64-linux-gnu.so +3 -0
  49. build/lib.linux-x86_64-cpython-310/aptsources/__init__.py +9 -0
  50. build/lib.linux-x86_64-cpython-310/aptsources/distinfo.py +393 -0
.gitattributes CHANGED
@@ -44,3 +44,4 @@ LLaMA-Factory/data/oaast_rm.json filter=lfs diff=lfs merge=lfs -text
44
  LLaMA-Factory/data/oaast_sft.json filter=lfs diff=lfs merge=lfs -text
45
  sample_data/mnist_test.csv filter=lfs diff=lfs merge=lfs -text
46
  sample_data/mnist_train_small.csv filter=lfs diff=lfs merge=lfs -text
 
 
44
  LLaMA-Factory/data/oaast_sft.json filter=lfs diff=lfs merge=lfs -text
45
  sample_data/mnist_test.csv filter=lfs diff=lfs merge=lfs -text
46
  sample_data/mnist_train_small.csv filter=lfs diff=lfs merge=lfs -text
47
+ build/lib.linux-x86_64-cpython-310/apt_pkg.cpython-310-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
.gitlab-ci.yml ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ image: ubuntu:jammy
2
+ variables:
3
+ DEBIAN_FRONTEND: noninteractive
4
+
5
+ typing:
6
+ script:
7
+ - apt update
8
+ - apt install -q -y python3-pip
9
+ - python3 -m pip install -U mypy==0.942
10
+ - env MYPYPATH=$PWD/typehinting/ mypy --strict ./apt
11
+
12
+ pep8:
13
+ script:
14
+ - apt update
15
+ - apt install -q -y pycodestyle
16
+ - env python3 tests/testmanual_pycodestyle.py
17
+
18
+ test:
19
+ script:
20
+ - apt update
21
+ - apt build-dep -q -y ./
22
+ - dpkg-buildpackage
23
+ artifacts:
24
+ paths:
25
+ - build/sphinx/html/
26
+
27
+ pages:
28
+ stage: deploy
29
+ script:
30
+ - mv build/sphinx/html/ public
31
+ artifacts:
32
+ paths:
33
+ - public
34
+ rules:
35
+ - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
.travis.yml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ language: cpp
2
+ cache: ccache
3
+ sudo: required
4
+ services:
5
+ - docker
6
+ env:
7
+ - DISTRO=ubuntu:eoan
8
+ install:
9
+ - sed -i -e "s#1000#$(id -u)#g" -e "s#debian:testing#$DISTRO#g" Dockerfile
10
+ - docker build --tag=apt-ci .
11
+ script:
12
+ - docker run --rm -w $PWD -v $HOME/.ccache:$HOME/.ccache -v $PWD:$PWD --user=travis apt-ci env ./debian/rules build-arch
13
+ - docker run --rm -w $PWD -v $PWD:$PWD --user=travis apt-ci env python3 tests/testmanual_pycodestyle.py
14
+ - docker run --rm -w $PWD -v $PWD:$PWD --user=travis apt-ci env MYPYPATH=$PWD/typehinting/ mypy ./apt
AUTHORS ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ jgg Jason Gunthorpe <[email protected]>
2
+ mdz Matt Zimmerman <[email protected]>
3
+ mvo Michael Vogt <[email protected]>
4
+ Michiel Sikkes <[email protected]>
5
+ Sebastian Heinlein <[email protected]>
6
+ Sean Wheller <[email protected]>
7
+ Julian Andres Klode <[email protected]>
COPYING.GPL ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Library General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) 19yy <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License
307
+ along with this program; if not, write to the Free Software
308
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309
+
310
+
311
+ Also add information on how to contact you by electronic and paper mail.
312
+
313
+ If the program is interactive, make it output a short notice like this
314
+ when it starts in an interactive mode:
315
+
316
+ Gnomovision version 69, Copyright (C) 19yy name of author
317
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318
+ This is free software, and you are welcome to redistribute it
319
+ under certain conditions; type `show c' for details.
320
+
321
+ The hypothetical commands `show w' and `show c' should show the appropriate
322
+ parts of the General Public License. Of course, the commands you use may
323
+ be called something other than `show w' and `show c'; they could even be
324
+ mouse-clicks or menu items--whatever suits your program.
325
+
326
+ You should also get your employer (if you work as a programmer) or your
327
+ school, if any, to sign a "copyright disclaimer" for the program, if
328
+ necessary. Here is a sample; alter the names:
329
+
330
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
332
+
333
+ <signature of Ty Coon>, 1 April 1989
334
+ Ty Coon, President of Vice
335
+
336
+ This General Public License does not permit incorporating your program into
337
+ proprietary programs. If your program is a subroutine library, you may
338
+ consider it more useful to permit linking proprietary applications with the
339
+ library. If this is what you want to do, use the GNU Library General
340
+ Public License instead of this License.
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM debian:testing
2
+ COPY . /tmp
3
+ WORKDIR /tmp
4
+ RUN sed -i s#://deb.debian.org#://cdn-fastly.deb.debian.org# /etc/apt/sources.list \
5
+ && apt-get update \
6
+ && adduser --home /home/travis travis --quiet --disabled-login --gecos "" --uid 1000 \
7
+ && env DEBIAN_FRONTEND=noninteractive apt-get build-dep -y /tmp \
8
+ && env DEBIAN_FRONTEND=noninteractive apt-get install -y ccache python3-pip \
9
+ && python3 -m pip install -U mypy \
10
+ && rm -r /tmp/* \
11
+ && apt-get clean
README.md CHANGED
@@ -1,17 +1,49 @@
1
- ---
2
- license: mit
3
- language:
4
- - it
5
- - en
6
- - fr
7
- - es
8
- - de
9
- - zh
10
- metrics:
11
- - code_eval
12
- - bleurt
13
- tags:
14
- - text-generation-inference
15
- library_name: flair
16
- pipeline_tag: text-generation
17
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python-apt is a wrapper to use features of apt from python.
2
+
3
+ It contains the following modules:
4
+
5
+ ## C++ Wrapper:
6
+
7
+ * apt_pkg - access to libapt-pkg (wrapper to the lowlevel c++ code)
8
+ * apt_inst - access to libapt-inst (wrapper to the lowlevel c++ code)
9
+
10
+ ## Python module:
11
+
12
+ * apt - high level python interface build on top of apt_pkg, apt_inst
13
+ * aptsources - high level manipulation of sources.list
14
+
15
+
16
+ # Development
17
+
18
+ ## Building
19
+
20
+ To build python-apt run:
21
+ ```
22
+ $ python setup.py build
23
+ ```
24
+ You may need to install the build-dependencies via:
25
+ ```
26
+ $ sudo apt build-dep ./
27
+ ```
28
+ first.
29
+
30
+ ## Running the tests
31
+
32
+ Run the tests with:
33
+ ```
34
+ $ python tests/test_all.py
35
+ $ python3 tests/test_all.py
36
+ ```
37
+
38
+ ## Running mypy:
39
+
40
+ To check if the "apt" python module is mypy clean, run:
41
+ ```
42
+ $ MYPYPATH=./typehinting/ mypy ./apt
43
+ ```
44
+
45
+ To use the annotation with your source code, run:
46
+ ```
47
+ $ MYPYPATH=/usr/lib/python3/dist-packages/apt mypy ./my-program
48
+ ```
49
+ (adjust from python3 to python2.7 if you run there).
TODO ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ * aptsources:
2
+ - make the distro detection in sources.list more clever by using the
3
+ origin informaton to avoid adding full uris to (unofficial/internal)
4
+ mirrors
apt/__init__.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2005-2009 Canonical
2
+ #
3
+ # Author: Michael Vogt <[email protected]>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License as
7
+ # published by the Free Software Foundation; either version 2 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
+ # USA
19
+ # import the core of apt_pkg
20
+ """High-Level Interface for working with apt."""
21
+ from __future__ import print_function
22
+
23
+ import apt_pkg
24
+
25
+ # import some fancy classes
26
+ from apt.package import Package as Package, Version as Version
27
+ from apt.cache import Cache as Cache, ProblemResolver as ProblemResolver
28
+ Cache # pyflakes
29
+ ProblemResolver # pyflakes
30
+ Version # pyflakes
31
+ from apt.cdrom import Cdrom as Cdrom
32
+
33
+ # init the package system, but do not re-initialize config
34
+ if "APT" not in apt_pkg.config:
35
+ apt_pkg.init_config()
36
+ apt_pkg.init_system()
37
+
38
+ __all__ = ['Cache', 'Cdrom', 'Package']
apt/auth.py ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+ # auth - authentication key management
4
+ #
5
+ # Copyright (c) 2004 Canonical
6
+ # Copyright (c) 2012 Sebastian Heinlein
7
+ #
8
+ # Author: Michael Vogt <[email protected]>
9
+ # Sebastian Heinlein <[email protected]>
10
+ #
11
+ # This program is free software; you can redistribute it and/or
12
+ # modify it under the terms of the GNU General Public License as
13
+ # published by the Free Software Foundation; either version 2 of the
14
+ # License, or (at your option) any later version.
15
+ #
16
+ # This program is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with this program; if not, write to the Free Software
23
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
24
+ # USA
25
+ """Handle GnuPG keys used to trust signed repositories."""
26
+
27
+ from __future__ import print_function
28
+
29
+ import errno
30
+ import os
31
+ import os.path
32
+ import shutil
33
+ import subprocess
34
+ import sys
35
+ import tempfile
36
+
37
+ import apt_pkg
38
+ from apt_pkg import gettext as _
39
+
40
+ from typing import List, Optional, Tuple
41
+
42
+
43
+ class AptKeyError(Exception):
44
+ pass
45
+
46
+
47
+ class AptKeyIDTooShortError(AptKeyError):
48
+ """Internal class do not rely on it."""
49
+
50
+
51
+ class TrustedKey(object):
52
+
53
+ """Represents a trusted key."""
54
+
55
+ def __init__(self, name, keyid, date):
56
+ # type: (str, str, str) -> None
57
+ self.raw_name = name
58
+ # Allow to translated some known keys
59
+ self.name = _(name)
60
+ self.keyid = keyid
61
+ self.date = date
62
+
63
+ def __str__(self):
64
+ # type: () -> str
65
+ return "%s\n%s %s" % (self.name, self.keyid, self.date)
66
+
67
+
68
+ def _call_apt_key_script(*args, **kwargs):
69
+ # type: (str, Optional[str]) -> str
70
+ """Run the apt-key script with the given arguments."""
71
+ conf = None
72
+ cmd = [apt_pkg.config.find_file("Dir::Bin::Apt-Key", "/usr/bin/apt-key")]
73
+ cmd.extend(args)
74
+ env = os.environ.copy()
75
+ env["LANG"] = "C"
76
+ env["APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE"] = "1"
77
+ try:
78
+ if apt_pkg.config.find_dir("Dir") != "/":
79
+ # If the key is to be installed into a chroot we have to export the
80
+ # configuration from the chroot to the apt-key script by using
81
+ # a temporary APT_CONFIG file. The apt-key script uses apt-config
82
+ # shell internally
83
+ conf = tempfile.NamedTemporaryFile(
84
+ prefix="apt-key", suffix=".conf")
85
+ conf.write(apt_pkg.config.dump().encode("UTF-8"))
86
+ conf.flush()
87
+ env["APT_CONFIG"] = conf.name
88
+ proc = subprocess.Popen(cmd, env=env, universal_newlines=True,
89
+ stdin=subprocess.PIPE,
90
+ stdout=subprocess.PIPE,
91
+ stderr=subprocess.PIPE)
92
+
93
+ stdin = kwargs.get("stdin", None)
94
+
95
+ output, stderr = proc.communicate(stdin) # type: str, str
96
+
97
+ if proc.returncode:
98
+ raise AptKeyError(
99
+ "The apt-key script failed with return code %s:\n"
100
+ "%s\n"
101
+ "stdout: %s\n"
102
+ "stderr: %s" % (
103
+ proc.returncode, " ".join(cmd), output, stderr))
104
+ elif stderr:
105
+ sys.stderr.write(stderr) # Forward stderr
106
+
107
+ return output.strip()
108
+ finally:
109
+ if conf is not None:
110
+ conf.close()
111
+
112
+
113
+ def add_key_from_file(filename):
114
+ # type: (str) -> None
115
+ """Import a GnuPG key file to trust repositores signed by it.
116
+
117
+ Keyword arguments:
118
+ filename -- the absolute path to the public GnuPG key file
119
+ """
120
+ if not os.path.abspath(filename):
121
+ raise AptKeyError("An absolute path is required: %s" % filename)
122
+ if not os.access(filename, os.R_OK):
123
+ raise AptKeyError("Key file cannot be accessed: %s" % filename)
124
+ _call_apt_key_script("add", filename)
125
+
126
+
127
+ def add_key_from_keyserver(keyid, keyserver):
128
+ # type: (str, str) -> None
129
+ """Import a GnuPG key file to trust repositores signed by it.
130
+
131
+ Keyword arguments:
132
+ keyid -- the long keyid (fingerprint) of the key, e.g.
133
+ A1BD8E9D78F7FE5C3E65D8AF8B48AD6246925553
134
+ keyserver -- the URL or hostname of the key server
135
+ """
136
+ tmp_keyring_dir = tempfile.mkdtemp()
137
+ try:
138
+ _add_key_from_keyserver(keyid, keyserver, tmp_keyring_dir)
139
+ except Exception:
140
+ raise
141
+ finally:
142
+ # We are racing with gpg when removing sockets, so ignore
143
+ # failure to delete non-existing files.
144
+ def onerror(func, path, exc_info):
145
+ # type: (object, str, Tuple[type, Exception, object]) -> None
146
+ if (isinstance(exc_info[1], OSError) and
147
+ exc_info[1].errno == errno.ENOENT):
148
+ return
149
+ raise
150
+
151
+ shutil.rmtree(tmp_keyring_dir, onerror=onerror)
152
+
153
+
154
+ def _add_key_from_keyserver(keyid, keyserver, tmp_keyring_dir):
155
+ # type: (str, str, str) -> None
156
+ if len(keyid.replace(" ", "").replace("0x", "")) < (160 / 4):
157
+ raise AptKeyIDTooShortError(
158
+ "Only fingerprints (v4, 160bit) are supported")
159
+ # create a temp keyring dir
160
+ tmp_secret_keyring = os.path.join(tmp_keyring_dir, "secring.gpg")
161
+ tmp_keyring = os.path.join(tmp_keyring_dir, "pubring.gpg")
162
+ # default options for gpg
163
+ gpg_default_options = [
164
+ "gpg",
165
+ "--no-default-keyring", "--no-options",
166
+ "--homedir", tmp_keyring_dir,
167
+ ]
168
+ # download the key to a temp keyring first
169
+ res = subprocess.call(gpg_default_options + [
170
+ "--secret-keyring", tmp_secret_keyring,
171
+ "--keyring", tmp_keyring,
172
+ "--keyserver", keyserver,
173
+ "--recv", keyid,
174
+ ])
175
+ if res != 0:
176
+ raise AptKeyError("recv from '%s' failed for '%s'" % (
177
+ keyserver, keyid))
178
+ # FIXME:
179
+ # - with gnupg 1.4.18 the downloaded key is actually checked(!),
180
+ # i.e. gnupg will not import anything that the server sends
181
+ # into the keyring, so the below checks are now redundant *if*
182
+ # gnupg 1.4.18 is used
183
+
184
+ # now export again using the long key id (to ensure that there is
185
+ # really only this one key in our keyring) and not someone MITM us
186
+ tmp_export_keyring = os.path.join(tmp_keyring_dir, "export-keyring.gpg")
187
+ res = subprocess.call(gpg_default_options + [
188
+ "--keyring", tmp_keyring,
189
+ "--output", tmp_export_keyring,
190
+ "--export", keyid,
191
+ ])
192
+ if res != 0:
193
+ raise AptKeyError("export of '%s' failed", keyid)
194
+ # now verify the fingerprint, this is probably redundant as we
195
+ # exported by the fingerprint in the previous command but its
196
+ # still good paranoia
197
+ output = subprocess.Popen(
198
+ gpg_default_options + [
199
+ "--keyring", tmp_export_keyring,
200
+ "--fingerprint",
201
+ "--batch",
202
+ "--fixed-list-mode",
203
+ "--with-colons",
204
+ ],
205
+ stdout=subprocess.PIPE,
206
+ universal_newlines=True).communicate()[0]
207
+ got_fingerprint = None
208
+ for line in output.splitlines():
209
+ if line.startswith("fpr:"):
210
+ got_fingerprint = line.split(":")[9]
211
+ # stop after the first to ensure no subkey trickery
212
+ break
213
+ # strip the leading "0x" is there is one and uppercase (as this is
214
+ # what gnupg is using)
215
+ signing_key_fingerprint = keyid.replace("0x", "").upper()
216
+ if got_fingerprint != signing_key_fingerprint:
217
+ # make the error match what gnupg >= 1.4.18 will output when
218
+ # it checks the key itself before importing it
219
+ raise AptKeyError(
220
+ "recv from '%s' failed for '%s'" % (
221
+ keyserver, signing_key_fingerprint))
222
+ # finally add it
223
+ add_key_from_file(tmp_export_keyring)
224
+
225
+
226
+ def add_key(content):
227
+ # type: (str) -> None
228
+ """Import a GnuPG key to trust repositores signed by it.
229
+
230
+ Keyword arguments:
231
+ content -- the content of the GnuPG public key
232
+ """
233
+ _call_apt_key_script("adv", "--quiet", "--batch",
234
+ "--import", "-", stdin=content)
235
+
236
+
237
+ def remove_key(fingerprint):
238
+ # type: (str) -> None
239
+ """Remove a GnuPG key to no longer trust repositores signed by it.
240
+
241
+ Keyword arguments:
242
+ fingerprint -- the fingerprint identifying the key
243
+ """
244
+ _call_apt_key_script("rm", fingerprint)
245
+
246
+
247
+ def export_key(fingerprint):
248
+ # type: (str) -> str
249
+ """Return the GnuPG key in text format.
250
+
251
+ Keyword arguments:
252
+ fingerprint -- the fingerprint identifying the key
253
+ """
254
+ return _call_apt_key_script("export", fingerprint)
255
+
256
+
257
+ def update():
258
+ # type: () -> str
259
+ """Update the local keyring with the archive keyring and remove from
260
+ the local keyring the archive keys which are no longer valid. The
261
+ archive keyring is shipped in the archive-keyring package of your
262
+ distribution, e.g. the debian-archive-keyring package in Debian.
263
+ """
264
+ return _call_apt_key_script("update")
265
+
266
+
267
+ def net_update():
268
+ # type: () -> str
269
+ """Work similar to the update command above, but get the archive
270
+ keyring from an URI instead and validate it against a master key.
271
+ This requires an installed wget(1) and an APT build configured to
272
+ have a server to fetch from and a master keyring to validate. APT
273
+ in Debian does not support this command and relies on update
274
+ instead, but Ubuntu's APT does.
275
+ """
276
+ return _call_apt_key_script("net-update")
277
+
278
+
279
+ def list_keys():
280
+ # type: () -> List[TrustedKey]
281
+ """Returns a list of TrustedKey instances for each key which is
282
+ used to trust repositories.
283
+ """
284
+ # The output of `apt-key list` is difficult to parse since the
285
+ # --with-colons parameter isn't user
286
+ output = _call_apt_key_script("adv", "--with-colons", "--batch",
287
+ "--fixed-list-mode", "--list-keys")
288
+ res = []
289
+ for line in output.split("\n"):
290
+ fields = line.split(":")
291
+ if fields[0] == "pub":
292
+ keyid = fields[4]
293
+ if fields[0] == "uid":
294
+ uid = fields[9]
295
+ creation_date = fields[5]
296
+ key = TrustedKey(uid, keyid, creation_date)
297
+ res.append(key)
298
+ return res
299
+
300
+
301
+ if __name__ == "__main__":
302
+ # Add some known keys we would like to see translated so that they get
303
+ # picked up by gettext
304
+ lambda: _("Ubuntu Archive Automatic Signing Key <[email protected]>")
305
+ lambda: _("Ubuntu CD Image Automatic Signing Key <[email protected]>")
306
+
307
+ apt_pkg.init()
308
+ for trusted_key in list_keys():
309
+ print(trusted_key)
apt/cache.py ADDED
@@ -0,0 +1,1038 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # cache.py - apt cache abstraction
2
+ #
3
+ # Copyright (c) 2005-2009 Canonical
4
+ #
5
+ # Author: Michael Vogt <[email protected]>
6
+ #
7
+ # This program is free software; you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as
9
+ # published by the Free Software Foundation; either version 2 of the
10
+ # License, or (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
20
+ # USA
21
+
22
+ from __future__ import print_function
23
+
24
+ import fnmatch
25
+ import os
26
+ import warnings
27
+ import weakref
28
+
29
+
30
+ from typing import (Any, Callable, Dict, Iterator, List, Optional,
31
+ Set, Tuple, Union, cast, KeysView)
32
+
33
+ import apt_pkg
34
+ from apt.package import Package, Version
35
+ import apt.progress.text
36
+ from apt.progress.base import AcquireProgress, InstallProgress, OpProgress
37
+
38
+
39
+ class FetchCancelledException(IOError):
40
+ """Exception that is thrown when the user cancels a fetch operation."""
41
+
42
+
43
+ class FetchFailedException(IOError):
44
+ """Exception that is thrown when fetching fails."""
45
+
46
+
47
+ class UntrustedException(FetchFailedException):
48
+ """Exception that is thrown when fetching fails for trust reasons"""
49
+
50
+
51
+ class LockFailedException(IOError):
52
+ """Exception that is thrown when locking fails."""
53
+
54
+
55
+ class CacheClosedException(Exception):
56
+ """Exception that is thrown when the cache is used after close()."""
57
+
58
+
59
+ class _WrappedLock(object):
60
+ """Wraps an apt_pkg.FileLock to raise LockFailedException.
61
+
62
+ Initialized using a directory path."""
63
+
64
+ def __init__(self, path):
65
+ # type: (str) -> None
66
+ self._path = path
67
+ self._lock = apt_pkg.FileLock(os.path.join(path, "lock"))
68
+
69
+ def __enter__(self):
70
+ # type: () -> None
71
+ try:
72
+ return self._lock.__enter__()
73
+ except apt_pkg.Error as e:
74
+ raise LockFailedException(("Failed to lock directory %s: %s") %
75
+ (self._path, e))
76
+
77
+ def __exit__(self, typ, value, traceback):
78
+ # type: (object, object, object) -> None
79
+ return self._lock.__exit__(typ, value, traceback)
80
+
81
+
82
+ class Cache(object):
83
+ """Dictionary-like package cache.
84
+
85
+ The APT cache file contains a hash table mapping names of binary
86
+ packages to their metadata. A Cache object is the in-core
87
+ representation of the same. It provides access to APTs idea of the
88
+ list of available packages.
89
+
90
+ The cache can be used like a mapping from package names to Package
91
+ objects (although only getting items is supported).
92
+
93
+ Keyword arguments:
94
+ progress -- a OpProgress object,
95
+ rootdir -- an alternative root directory. if that is given the system
96
+ sources.list and system lists/files are not read, only file relative
97
+ to the given rootdir,
98
+ memonly -- build the cache in memory only.
99
+
100
+
101
+ .. versionchanged:: 1.0
102
+
103
+ The cache now supports package names with special architecture
104
+ qualifiers such as :all and :native. It does not export them
105
+ in :meth:`keys()`, though, to keep :meth:`keys()` a unique set.
106
+ """
107
+
108
+ def __init__(self, progress=None, rootdir=None, memonly=False):
109
+ # type: (Optional[OpProgress], Optional[str], bool) -> None
110
+ self._cache = cast(apt_pkg.Cache, None) # type: apt_pkg.Cache
111
+ self._depcache = cast(apt_pkg.DepCache, None) # type: apt_pkg.DepCache
112
+ self._records = cast(apt_pkg.PackageRecords, None) # type: apt_pkg.PackageRecords # noqa
113
+ self._list = cast(apt_pkg.SourceList, None) # type: apt_pkg.SourceList
114
+ self._callbacks = {} # type: Dict[str, List[Union[Callable[..., None],str]]] # noqa
115
+ self._callbacks2 = {} # type: Dict[str, List[Tuple[Callable[..., Any], Tuple[Any, ...], Dict[Any,Any]]]] # noqa
116
+ self._weakref = weakref.WeakValueDictionary() # type: weakref.WeakValueDictionary[str, apt.Package] # noqa
117
+ self._weakversions = weakref.WeakSet() # type: weakref.WeakSet[Version] # noqa
118
+ self._changes_count = -1
119
+ self._sorted_set = None # type: Optional[List[str]]
120
+
121
+ self.connect("cache_post_open", "_inc_changes_count")
122
+ self.connect("cache_post_change", "_inc_changes_count")
123
+ if memonly:
124
+ # force apt to build its caches in memory
125
+ apt_pkg.config.set("Dir::Cache::pkgcache", "")
126
+ if rootdir:
127
+ rootdir = os.path.abspath(rootdir)
128
+ if os.path.exists(rootdir + "/etc/apt/apt.conf"):
129
+ apt_pkg.read_config_file(apt_pkg.config,
130
+ rootdir + "/etc/apt/apt.conf")
131
+ if os.path.isdir(rootdir + "/etc/apt/apt.conf.d"):
132
+ apt_pkg.read_config_dir(apt_pkg.config,
133
+ rootdir + "/etc/apt/apt.conf.d")
134
+ apt_pkg.config.set("Dir", rootdir)
135
+ apt_pkg.config.set("Dir::State::status",
136
+ rootdir + "/var/lib/dpkg/status")
137
+ # also set dpkg to the rootdir path so that its called for the
138
+ # --print-foreign-architectures call
139
+ apt_pkg.config.set("Dir::bin::dpkg",
140
+ os.path.join(rootdir, "usr", "bin", "dpkg"))
141
+ # create required dirs/files when run with special rootdir
142
+ # automatically
143
+ self._check_and_create_required_dirs(rootdir)
144
+ # Call InitSystem so the change to Dir::State::Status is actually
145
+ # recognized (LP: #320665)
146
+ apt_pkg.init_system()
147
+
148
+ # Prepare a lock object (context manager for archive lock)
149
+ archive_dir = apt_pkg.config.find_dir("Dir::Cache::Archives")
150
+ self._archive_lock = _WrappedLock(archive_dir)
151
+
152
+ self.open(progress)
153
+
154
+ def fix_broken(self):
155
+ # type: () -> None
156
+ """Fix broken packages."""
157
+ self._depcache.fix_broken()
158
+
159
+ def _inc_changes_count(self):
160
+ # type: () -> None
161
+ """Increase the number of changes"""
162
+ self._changes_count += 1
163
+
164
+ def _check_and_create_required_dirs(self, rootdir):
165
+ # type: (str) -> None
166
+ """
167
+ check if the required apt directories/files are there and if
168
+ not create them
169
+ """
170
+ files = [
171
+ "/var/lib/dpkg/status",
172
+ "/etc/apt/sources.list",
173
+ ]
174
+ dirs = [
175
+ "/var/lib/dpkg",
176
+ "/etc/apt/",
177
+ "/var/cache/apt/archives/partial",
178
+ "/var/lib/apt/lists/partial",
179
+ ]
180
+ for d in dirs:
181
+ if not os.path.exists(rootdir + d):
182
+ #print "creating: ", rootdir + d
183
+ os.makedirs(rootdir + d)
184
+ for f in files:
185
+ if not os.path.exists(rootdir + f):
186
+ open(rootdir + f, "w").close()
187
+
188
+ def _run_callbacks(self, name):
189
+ # type: (str) -> None
190
+ """ internal helper to run a callback """
191
+ if name in self._callbacks:
192
+ for callback in self._callbacks[name]:
193
+ if callback == '_inc_changes_count':
194
+ self._inc_changes_count()
195
+ else:
196
+ callback() # type: ignore
197
+
198
+ if name in self._callbacks2:
199
+ for callback, args, kwds in self._callbacks2[name]:
200
+ callback(self, *args, **kwds)
201
+
202
+ def open(self, progress=None):
203
+ # type: (Optional[OpProgress]) -> None
204
+ """ Open the package cache, after that it can be used like
205
+ a dictionary
206
+ """
207
+ if progress is None:
208
+ progress = apt.progress.base.OpProgress()
209
+ # close old cache on (re)open
210
+ self.close()
211
+ self.op_progress = progress
212
+ self._run_callbacks("cache_pre_open")
213
+
214
+ self._cache = apt_pkg.Cache(progress)
215
+ self._depcache = apt_pkg.DepCache(self._cache)
216
+ self._records = apt_pkg.PackageRecords(self._cache)
217
+ self._list = apt_pkg.SourceList()
218
+ self._list.read_main_list()
219
+ self._sorted_set = None
220
+ self.__remap()
221
+
222
+ self._have_multi_arch = len(apt_pkg.get_architectures()) > 1
223
+
224
+ progress.done()
225
+ self._run_callbacks("cache_post_open")
226
+
227
+ def __remap(self):
228
+ # type: () -> None
229
+ """Called after cache reopen() to relocate to new cache.
230
+
231
+ Relocate objects like packages and versions from the old
232
+ underlying cache to the new one.
233
+ """
234
+ for key in list(self._weakref.keys()):
235
+ try:
236
+ pkg = self._weakref[key]
237
+ except KeyError:
238
+ continue
239
+
240
+ try:
241
+ pkg._pkg = self._cache[pkg._pkg.name, pkg._pkg.architecture]
242
+ except LookupError:
243
+ del self._weakref[key]
244
+
245
+ for ver in list(self._weakversions):
246
+ # Package has been reseated above, reseat version
247
+ for v in ver.package._pkg.version_list:
248
+ # Requirements as in debListParser::SameVersion
249
+ if (v.hash == ver._cand.hash and
250
+ (v.size == 0 or ver._cand.size == 0 or
251
+ v.size == ver._cand.size) and
252
+ v.multi_arch == ver._cand.multi_arch and
253
+ v.ver_str == ver._cand.ver_str):
254
+ ver._cand = v
255
+ break
256
+ else:
257
+ self._weakversions.remove(ver)
258
+
259
+ def close(self):
260
+ # type: () -> None
261
+ """ Close the package cache """
262
+ # explicitely free the FDs that _records has open
263
+ del self._records
264
+ self._records = cast(apt_pkg.PackageRecords, None)
265
+
266
+ def __enter__(self):
267
+ # type: () -> Cache
268
+ """ Enter the with statement """
269
+ return self
270
+
271
+ def __exit__(self, exc_type, exc_value, traceback):
272
+ # type: (object, object, object) -> None
273
+ """ Exit the with statement """
274
+ self.close()
275
+
276
+ def __getitem__(self, key):
277
+ # type: (object) -> Package
278
+ """ look like a dictionary (get key) """
279
+ try:
280
+ key = str(key)
281
+ rawpkg = self._cache[key]
282
+ except KeyError:
283
+ raise KeyError('The cache has no package named %r' % key)
284
+
285
+ # It might be excluded due to not having a version or something
286
+ if not self.__is_real_pkg(rawpkg):
287
+ raise KeyError('The cache has no package named %r' % key)
288
+
289
+ pkg = self._rawpkg_to_pkg(rawpkg)
290
+
291
+ return pkg
292
+
293
+ def get(self, key, default=None):
294
+ # type: (object, object) -> Any
295
+ """Return *self*[*key*] or *default* if *key* not in *self*.
296
+
297
+ .. versionadded:: 1.1
298
+ """
299
+ try:
300
+ return self[key]
301
+ except KeyError:
302
+ return default
303
+
304
+ def _rawpkg_to_pkg(self, rawpkg):
305
+ # type: (apt_pkg.Package) -> Package
306
+ """Returns the apt.Package object for an apt_pkg.Package object.
307
+
308
+ .. versionadded:: 1.0.0
309
+ """
310
+ fullname = rawpkg.get_fullname(pretty=True)
311
+
312
+ return self._weakref.setdefault(fullname, Package(self, rawpkg))
313
+
314
+ def __iter__(self):
315
+ # type: () -> Iterator[Package]
316
+ # We iterate sorted over package names here. With this we read the
317
+ # package lists linearly if we need to access the package records,
318
+ # instead of having to do thousands of random seeks; the latter
319
+ # is disastrous if we use compressed package indexes, and slower than
320
+ # necessary for uncompressed indexes.
321
+ for pkgname in self.keys():
322
+ pkg = Package(self, self._cache[pkgname])
323
+ yield self._weakref.setdefault(pkgname, pkg)
324
+
325
+ def __is_real_pkg(self, rawpkg):
326
+ # type: (apt_pkg.Package) -> bool
327
+ """Check if the apt_pkg.Package provided is a real package."""
328
+ return rawpkg.has_versions
329
+
330
+ def has_key(self, key):
331
+ # type: (object) -> bool
332
+ return key in self
333
+
334
+ def __contains__(self, key):
335
+ # type: (object) -> bool
336
+ try:
337
+ return self.__is_real_pkg(self._cache[str(key)])
338
+ except KeyError:
339
+ return False
340
+
341
+ def __len__(self):
342
+ # type: () -> int
343
+ return len(self.keys())
344
+
345
+ def keys(self):
346
+ # type: () -> List[str]
347
+ if self._sorted_set is None:
348
+ self._sorted_set = sorted(p.get_fullname(pretty=True)
349
+ for p in self._cache.packages
350
+ if self.__is_real_pkg(p))
351
+ return list(self._sorted_set) # We need a copy here, caller may modify
352
+
353
+ def get_changes(self):
354
+ # type: () -> List[Package]
355
+ """ Get the marked changes """
356
+ changes = []
357
+ marked_keep = self._depcache.marked_keep
358
+ for rawpkg in self._cache.packages:
359
+ if not marked_keep(rawpkg):
360
+ changes.append(self._rawpkg_to_pkg(rawpkg))
361
+ return changes
362
+
363
+ def upgrade(self, dist_upgrade=False):
364
+ # type: (bool) -> None
365
+ """Upgrade all packages.
366
+
367
+ If the parameter *dist_upgrade* is True, new dependencies will be
368
+ installed as well (and conflicting packages may be removed). The
369
+ default value is False.
370
+ """
371
+ self.cache_pre_change()
372
+ self._depcache.upgrade(dist_upgrade)
373
+ self.cache_post_change()
374
+
375
+ @property
376
+ def required_download(self):
377
+ # type: () -> int
378
+ """Get the size of the packages that are required to download."""
379
+ if self._records is None:
380
+ raise CacheClosedException(
381
+ "Cache object used after close() called")
382
+ pm = apt_pkg.PackageManager(self._depcache)
383
+ fetcher = apt_pkg.Acquire()
384
+ pm.get_archives(fetcher, self._list, self._records)
385
+ return fetcher.fetch_needed
386
+
387
+ @property
388
+ def required_space(self):
389
+ # type: () -> int
390
+ """Get the size of the additional required space on the fs."""
391
+ return self._depcache.usr_size
392
+
393
+ @property
394
+ def req_reinstall_pkgs(self):
395
+ # type: () -> Set[str]
396
+ """Return the packages not downloadable packages in reqreinst state."""
397
+ reqreinst = set()
398
+ get_candidate_ver = self._depcache.get_candidate_ver
399
+ states = frozenset((apt_pkg.INSTSTATE_REINSTREQ,
400
+ apt_pkg.INSTSTATE_HOLD_REINSTREQ))
401
+ for pkg in self._cache.packages:
402
+ cand = get_candidate_ver(pkg)
403
+ if cand and not cand.downloadable and pkg.inst_state in states:
404
+ reqreinst.add(pkg.get_fullname(pretty=True))
405
+ return reqreinst
406
+
407
+ def _run_fetcher(self, fetcher, allow_unauthenticated):
408
+ # type: (apt_pkg.Acquire, Optional[bool]) -> int
409
+ if allow_unauthenticated is None:
410
+ allow_unauthenticated = apt_pkg.config.find_b("APT::Get::"
411
+ "AllowUnauthenticated", False)
412
+
413
+ untrusted = [item for item in fetcher.items if not item.is_trusted]
414
+ if untrusted and not allow_unauthenticated:
415
+ raise UntrustedException("Untrusted packages:\n%s" %
416
+ "\n".join(i.desc_uri for i in untrusted))
417
+
418
+ # do the actual fetching
419
+ res = fetcher.run()
420
+
421
+ # now check the result (this is the code from apt-get.cc)
422
+ failed = False
423
+ err_msg = ""
424
+ for item in fetcher.items:
425
+ if item.status == item.STAT_DONE:
426
+ continue
427
+ if item.STAT_IDLE:
428
+ continue
429
+ err_msg += "Failed to fetch %s %s\n" % (item.desc_uri,
430
+ item.error_text)
431
+ failed = True
432
+
433
+ # we raise a exception if the download failed or it was cancelt
434
+ if res == fetcher.RESULT_CANCELLED:
435
+ raise FetchCancelledException(err_msg)
436
+ elif failed:
437
+ raise FetchFailedException(err_msg)
438
+ return res
439
+
440
+ def _fetch_archives(self,
441
+ fetcher, # type: apt_pkg.Acquire
442
+ pm, # type: apt_pkg.PackageManager
443
+ allow_unauthenticated=None, # type: Optional[bool]
444
+ ):
445
+ # type: (...) -> int
446
+ """ fetch the needed archives """
447
+ if self._records is None:
448
+ raise CacheClosedException(
449
+ "Cache object used after close() called")
450
+
451
+ # this may as well throw a SystemError exception
452
+ if not pm.get_archives(fetcher, self._list, self._records):
453
+ return False
454
+
455
+ # now run the fetcher, throw exception if something fails to be
456
+ # fetched
457
+ return self._run_fetcher(fetcher, allow_unauthenticated)
458
+
459
+ def fetch_archives(self,
460
+ progress=None, # type: Optional[AcquireProgress]
461
+ fetcher=None, # type: Optional[apt_pkg.Acquire]
462
+ allow_unauthenticated=None, # type: Optional[bool]
463
+ ):
464
+ # type: (...) -> int
465
+ """Fetch the archives for all packages marked for install/upgrade.
466
+
467
+ You can specify either an :class:`apt.progress.base.AcquireProgress()`
468
+ object for the parameter *progress*, or specify an already
469
+ existing :class:`apt_pkg.Acquire` object for the parameter *fetcher*.
470
+
471
+ The return value of the function is undefined. If an error occurred,
472
+ an exception of type :class:`FetchFailedException` or
473
+ :class:`FetchCancelledException` is raised.
474
+
475
+ The keyword-only parameter *allow_unauthenticated* specifies whether
476
+ to allow unauthenticated downloads. If not specified, it defaults to
477
+ the configuration option `APT::Get::AllowUnauthenticated`.
478
+
479
+ .. versionadded:: 0.8.0
480
+ """
481
+ if progress is not None and fetcher is not None:
482
+ raise ValueError("Takes a progress or a an Acquire object")
483
+ if progress is None:
484
+ progress = apt.progress.text.AcquireProgress()
485
+ if fetcher is None:
486
+ fetcher = apt_pkg.Acquire(progress)
487
+
488
+ with self._archive_lock:
489
+ return self._fetch_archives(fetcher,
490
+ apt_pkg.PackageManager(self._depcache),
491
+ allow_unauthenticated)
492
+
493
+ def is_virtual_package(self, pkgname):
494
+ # type: (str) -> bool
495
+ """Return whether the package is a virtual package."""
496
+ try:
497
+ pkg = self._cache[pkgname]
498
+ except KeyError:
499
+ return False
500
+ else:
501
+ return bool(pkg.has_provides and not pkg.has_versions)
502
+
503
+ def get_providing_packages(self, pkgname, candidate_only=True,
504
+ include_nonvirtual=False):
505
+ # type: (str, bool, bool) -> List[Package]
506
+ """Return a list of all packages providing a package.
507
+
508
+ Return a list of packages which provide the virtual package of the
509
+ specified name.
510
+
511
+ If 'candidate_only' is False, return all packages with at
512
+ least one version providing the virtual package. Otherwise,
513
+ return only those packages where the candidate version
514
+ provides the virtual package.
515
+
516
+ If 'include_nonvirtual' is True then it will search for all
517
+ packages providing pkgname, even if pkgname is not itself
518
+ a virtual pkg.
519
+ """
520
+
521
+ providers = set() # type: Set[Package]
522
+ get_candidate_ver = self._depcache.get_candidate_ver
523
+ try:
524
+ vp = self._cache[pkgname]
525
+ if vp.has_versions and not include_nonvirtual:
526
+ return list(providers)
527
+ except KeyError:
528
+ return list(providers)
529
+
530
+ for provides, providesver, version in vp.provides_list:
531
+ rawpkg = version.parent_pkg
532
+ if not candidate_only or (version == get_candidate_ver(rawpkg)):
533
+ providers.add(self._rawpkg_to_pkg(rawpkg))
534
+ return list(providers)
535
+
536
+ def update(self, fetch_progress=None, pulse_interval=0,
537
+ raise_on_error=True, sources_list=None):
538
+ # type: (Optional[AcquireProgress], int, bool, Optional[str]) -> int
539
+ """Run the equivalent of apt-get update.
540
+
541
+ You probably want to call open() afterwards, in order to utilise the
542
+ new cache. Otherwise, the old cache will be used which can lead to
543
+ strange bugs.
544
+
545
+ The first parameter *fetch_progress* may be set to an instance of
546
+ apt.progress.FetchProgress, the default is apt.progress.FetchProgress()
547
+ .
548
+ sources_list -- Update a alternative sources.list than the default.
549
+ Note that the sources.list.d directory is ignored in this case
550
+ """
551
+ with _WrappedLock(apt_pkg.config.find_dir("Dir::State::Lists")):
552
+ if sources_list:
553
+ old_sources_list = apt_pkg.config.find("Dir::Etc::sourcelist")
554
+ old_sources_list_d = (
555
+ apt_pkg.config.find("Dir::Etc::sourceparts"))
556
+ old_cleanup = apt_pkg.config.find("APT::List-Cleanup")
557
+ apt_pkg.config.set("Dir::Etc::sourcelist",
558
+ os.path.abspath(sources_list))
559
+ apt_pkg.config.set("Dir::Etc::sourceparts", "xxx")
560
+ apt_pkg.config.set("APT::List-Cleanup", "0")
561
+ slist = apt_pkg.SourceList()
562
+ slist.read_main_list()
563
+ else:
564
+ slist = self._list
565
+
566
+ try:
567
+ if fetch_progress is None:
568
+ fetch_progress = apt.progress.base.AcquireProgress()
569
+ try:
570
+ res = self._cache.update(fetch_progress, slist,
571
+ pulse_interval)
572
+ except SystemError as e:
573
+ raise FetchFailedException(e)
574
+ if not res and raise_on_error:
575
+ raise FetchFailedException()
576
+ else:
577
+ return res
578
+ finally:
579
+ if sources_list:
580
+ apt_pkg.config.set("Dir::Etc::sourcelist",
581
+ old_sources_list)
582
+ apt_pkg.config.set("Dir::Etc::sourceparts",
583
+ old_sources_list_d)
584
+ apt_pkg.config.set("APT::List-Cleanup",
585
+ old_cleanup)
586
+
587
+ def install_archives(self, pm, install_progress):
588
+ # type: (apt_pkg.PackageManager, InstallProgress) -> int
589
+ """
590
+ The first parameter *pm* refers to an object returned by
591
+ apt_pkg.PackageManager().
592
+
593
+ The second parameter *install_progress* refers to an InstallProgress()
594
+ object of the module apt.progress.
595
+
596
+ This releases a system lock in newer versions, if there is any,
597
+ and reestablishes it afterwards.
598
+ """
599
+ # compat with older API
600
+ try:
601
+ install_progress.startUpdate() # type: ignore
602
+ except AttributeError:
603
+ install_progress.start_update()
604
+
605
+ did_unlock = apt_pkg.pkgsystem_is_locked()
606
+ if did_unlock:
607
+ apt_pkg.pkgsystem_unlock_inner()
608
+
609
+ try:
610
+ res = install_progress.run(pm)
611
+ finally:
612
+ if did_unlock:
613
+ apt_pkg.pkgsystem_lock_inner()
614
+
615
+ try:
616
+ install_progress.finishUpdate() # type: ignore
617
+ except AttributeError:
618
+ install_progress.finish_update()
619
+ return res
620
+
621
+ def commit(self,
622
+ fetch_progress=None, # type: Optional[AcquireProgress]
623
+ install_progress=None, # type: Optional[InstallProgress]
624
+ allow_unauthenticated=None, # type: Optional[bool]
625
+ ):
626
+ # type: (...) -> bool
627
+ """Apply the marked changes to the cache.
628
+
629
+ The first parameter, *fetch_progress*, refers to a FetchProgress()
630
+ object as found in apt.progress, the default being
631
+ apt.progress.FetchProgress().
632
+
633
+ The second parameter, *install_progress*, is a
634
+ apt.progress.InstallProgress() object.
635
+
636
+ The keyword-only parameter *allow_unauthenticated* specifies whether
637
+ to allow unauthenticated downloads. If not specified, it defaults to
638
+ the configuration option `APT::Get::AllowUnauthenticated`.
639
+ """
640
+ # FIXME:
641
+ # use the new acquire/pkgmanager interface here,
642
+ # raise exceptions when a download or install fails
643
+ # and send proper error strings to the application.
644
+ # Current a failed download will just display "error"
645
+ # which is less than optimal!
646
+
647
+ if fetch_progress is None:
648
+ fetch_progress = apt.progress.base.AcquireProgress()
649
+ if install_progress is None:
650
+ install_progress = apt.progress.base.InstallProgress()
651
+
652
+ assert install_progress is not None
653
+
654
+ with apt_pkg.SystemLock():
655
+ pm = apt_pkg.PackageManager(self._depcache)
656
+ fetcher = apt_pkg.Acquire(fetch_progress)
657
+ with self._archive_lock:
658
+ while True:
659
+ # fetch archives first
660
+ res = self._fetch_archives(fetcher, pm,
661
+ allow_unauthenticated)
662
+
663
+ # then install
664
+ res = self.install_archives(pm, install_progress)
665
+ if res == pm.RESULT_COMPLETED:
666
+ break
667
+ elif res == pm.RESULT_FAILED:
668
+ raise SystemError("installArchives() failed")
669
+ elif res == pm.RESULT_INCOMPLETE:
670
+ pass
671
+ else:
672
+ raise SystemError("internal-error: unknown result "
673
+ "code from InstallArchives: %s" %
674
+ res)
675
+ # reload the fetcher for media swaping
676
+ fetcher.shutdown()
677
+ return (res == pm.RESULT_COMPLETED)
678
+
679
+ def clear(self):
680
+ # type: () -> None
681
+ """ Unmark all changes """
682
+ self._depcache.init()
683
+
684
+ # cache changes
685
+
686
+ def cache_post_change(self):
687
+ # type: () -> None
688
+ " called internally if the cache has changed, emit a signal then "
689
+ self._run_callbacks("cache_post_change")
690
+
691
+ def cache_pre_change(self):
692
+ # type: () -> None
693
+ """ called internally if the cache is about to change, emit
694
+ a signal then """
695
+ self._run_callbacks("cache_pre_change")
696
+
697
+ def connect(self, name, callback):
698
+ # type: (str, Union[Callable[..., None],str]) -> None
699
+ """Connect to a signal.
700
+
701
+ .. deprecated:: 1.0
702
+
703
+ Please use connect2() instead, as this function is very
704
+ likely to cause a memory leak.
705
+ """
706
+ if callback != '_inc_changes_count':
707
+ warnings.warn("connect() likely causes a reference"
708
+ " cycle, use connect2() instead", RuntimeWarning, 2)
709
+ if name not in self._callbacks:
710
+ self._callbacks[name] = []
711
+ self._callbacks[name].append(callback)
712
+
713
+ def connect2(self, name, callback, *args, **kwds):
714
+ # type: (str, Callable[..., Any], object, object) -> None
715
+ """Connect to a signal.
716
+
717
+ The callback will be passed the cache as an argument, and
718
+ any arguments passed to this function. Make sure that, if you
719
+ pass a method of a class as your callback, your class does not
720
+ contain a reference to the cache.
721
+
722
+ Cyclic references to the cache can cause issues if the Cache object
723
+ is replaced by a new one, because the cache keeps a lot of objects and
724
+ tens of open file descriptors.
725
+
726
+ currently only used for cache_{post,pre}_{changed,open}.
727
+
728
+ .. versionadded:: 1.0
729
+ """
730
+ if name not in self._callbacks2:
731
+ self._callbacks2[name] = []
732
+ self._callbacks2[name].append((callback, args, kwds))
733
+
734
+ def actiongroup(self):
735
+ # type: () -> apt_pkg.ActionGroup
736
+ """Return an `ActionGroup` object for the current cache.
737
+
738
+ Action groups can be used to speedup actions. The action group is
739
+ active as soon as it is created, and disabled when the object is
740
+ deleted or when release() is called.
741
+
742
+ You can use the action group as a context manager, this is the
743
+ recommended way::
744
+
745
+ with cache.actiongroup():
746
+ for package in my_selected_packages:
747
+ package.mark_install()
748
+
749
+ This way, the action group is automatically released as soon as the
750
+ with statement block is left. It also has the benefit of making it
751
+ clear which parts of the code run with a action group and which
752
+ don't.
753
+ """
754
+ return apt_pkg.ActionGroup(self._depcache)
755
+
756
+ @property
757
+ def dpkg_journal_dirty(self):
758
+ # type: () -> bool
759
+ """Return True if the dpkg was interrupted
760
+
761
+ All dpkg operations will fail until this is fixed, the action to
762
+ fix the system if dpkg got interrupted is to run
763
+ 'dpkg --configure -a' as root.
764
+ """
765
+ dpkg_status_dir = os.path.dirname(
766
+ apt_pkg.config.find_file("Dir::State::status"))
767
+ for f in os.listdir(os.path.join(dpkg_status_dir, "updates")):
768
+ if fnmatch.fnmatch(f, "[0-9]*"):
769
+ return True
770
+ return False
771
+
772
+ @property
773
+ def broken_count(self):
774
+ # type: () -> int
775
+ """Return the number of packages with broken dependencies."""
776
+ return self._depcache.broken_count
777
+
778
+ @property
779
+ def delete_count(self):
780
+ # type: () -> int
781
+ """Return the number of packages marked for deletion."""
782
+ return self._depcache.del_count
783
+
784
+ @property
785
+ def install_count(self):
786
+ # type: () -> int
787
+ """Return the number of packages marked for installation."""
788
+ return self._depcache.inst_count
789
+
790
+ @property
791
+ def keep_count(self):
792
+ # type: () -> int
793
+ """Return the number of packages marked as keep."""
794
+ return self._depcache.keep_count
795
+
796
+
797
+ class ProblemResolver(object):
798
+ """Resolve problems due to dependencies and conflicts.
799
+
800
+ The first argument 'cache' is an instance of apt.Cache.
801
+ """
802
+
803
+ def __init__(self, cache):
804
+ # type: (Cache) -> None
805
+ self._resolver = apt_pkg.ProblemResolver(cache._depcache)
806
+ self._cache = cache
807
+
808
+ def clear(self, package):
809
+ # type: (Package) -> None
810
+ """Reset the package to the default state."""
811
+ self._resolver.clear(package._pkg)
812
+
813
+ def protect(self, package):
814
+ # type: (Package) -> None
815
+ """Protect a package so it won't be removed."""
816
+ self._resolver.protect(package._pkg)
817
+
818
+ def remove(self, package):
819
+ # type: (Package) -> None
820
+ """Mark a package for removal."""
821
+ self._resolver.remove(package._pkg)
822
+
823
+ def resolve(self):
824
+ # type: () -> None
825
+ """Resolve dependencies, try to remove packages where needed."""
826
+ self._cache.cache_pre_change()
827
+ self._resolver.resolve()
828
+ self._cache.cache_post_change()
829
+
830
+ def resolve_by_keep(self):
831
+ # type: () -> None
832
+ """Resolve dependencies, do not try to remove packages."""
833
+ self._cache.cache_pre_change()
834
+ self._resolver.resolve_by_keep()
835
+ self._cache.cache_post_change()
836
+
837
+
838
+ # ----------------------------- experimental interface
839
+
840
+
841
+ class Filter(object):
842
+ """ Filter base class """
843
+
844
+ def apply(self, pkg):
845
+ # type: (Package) -> bool
846
+ """ Filter function, return True if the package matchs a
847
+ filter criteria and False otherwise
848
+ """
849
+ return True
850
+
851
+
852
+ class MarkedChangesFilter(Filter):
853
+ """ Filter that returns all marked changes """
854
+
855
+ def apply(self, pkg):
856
+ # type: (Package) -> bool
857
+ if pkg.marked_install or pkg.marked_delete or pkg.marked_upgrade:
858
+ return True
859
+ else:
860
+ return False
861
+
862
+
863
+ class InstalledFilter(Filter):
864
+ """Filter that returns all installed packages.
865
+
866
+ .. versionadded:: 1.0.0
867
+ """
868
+
869
+ def apply(self, pkg):
870
+ # type: (Package) -> bool
871
+ return pkg.is_installed
872
+
873
+
874
+ class _FilteredCacheHelper(object):
875
+ """Helper class for FilteredCache to break a reference cycle."""
876
+
877
+ def __init__(self, cache):
878
+ # type: (Cache) -> None
879
+ # Do not keep a reference to the cache, or you have a cycle!
880
+
881
+ self._filtered = {} # type: Dict[str,bool]
882
+ self._filters = [] # type: List[Filter]
883
+ cache.connect2("cache_post_change", self.filter_cache_post_change)
884
+ cache.connect2("cache_post_open", self.filter_cache_post_change)
885
+
886
+ def _reapply_filter(self, cache):
887
+ # type: (Cache) -> None
888
+ " internal helper to refilter "
889
+ # Do not keep a reference to the cache, or you have a cycle!
890
+ self._filtered = {}
891
+ for pkg in cache:
892
+ for f in self._filters:
893
+ if f.apply(pkg):
894
+ self._filtered[pkg.name] = True
895
+ break
896
+
897
+ def set_filter(self, filter):
898
+ # type: (Filter) -> None
899
+ """Set the current active filter."""
900
+ self._filters = []
901
+ self._filters.append(filter)
902
+
903
+ def filter_cache_post_change(self, cache):
904
+ # type: (Cache) -> None
905
+ """Called internally if the cache changes, emit a signal then."""
906
+ # Do not keep a reference to the cache, or you have a cycle!
907
+ self._reapply_filter(cache)
908
+
909
+
910
+ class FilteredCache(object):
911
+ """ A package cache that is filtered.
912
+
913
+ Can work on a existing cache or create a new one
914
+ """
915
+
916
+ def __init__(self, cache=None, progress=None):
917
+ # type: (Optional[Cache], Optional[OpProgress]) -> None
918
+ if cache is None:
919
+ self.cache = Cache(progress)
920
+ else:
921
+ self.cache = cache
922
+ self._helper = _FilteredCacheHelper(self.cache)
923
+
924
+ def __len__(self):
925
+ # type: () -> int
926
+ return len(self._helper._filtered)
927
+
928
+ def __getitem__(self, key):
929
+ # type: (str) -> Package
930
+ return self.cache[key]
931
+
932
+ def __iter__(self):
933
+ # type: () -> Iterator[Package]
934
+ for pkgname in self._helper._filtered:
935
+ yield self.cache[pkgname]
936
+
937
+ def keys(self):
938
+ # type: () -> KeysView[str]
939
+ return self._helper._filtered.keys()
940
+
941
+ def has_key(self, key):
942
+ # type: (object) -> bool
943
+ return key in self
944
+
945
+ def __contains__(self, key):
946
+ # type: (object) -> bool
947
+ try:
948
+ # Normalize package name for multi arch
949
+ return self.cache[key].name in self._helper._filtered
950
+ except KeyError:
951
+ return False
952
+
953
+ def set_filter(self, filter):
954
+ # type: (Filter) -> None
955
+ """Set the current active filter."""
956
+ self._helper.set_filter(filter)
957
+ self.cache.cache_post_change()
958
+
959
+ def filter_cache_post_change(self):
960
+ # type: () -> None
961
+ """Called internally if the cache changes, emit a signal then."""
962
+ self._helper.filter_cache_post_change(self.cache)
963
+
964
+ def __getattr__(self, key):
965
+ # type: (str) -> Any
966
+ """we try to look exactly like a real cache."""
967
+ return getattr(self.cache, key)
968
+
969
+
970
+ def cache_pre_changed(cache):
971
+ # type: (Cache) -> None
972
+ print("cache pre changed")
973
+
974
+
975
+ def cache_post_changed(cache):
976
+ # type: (Cache) -> None
977
+ print("cache post changed")
978
+
979
+
980
+ def _test():
981
+ # type: () -> None
982
+ """Internal test code."""
983
+ print("Cache self test")
984
+ apt_pkg.init()
985
+ cache = Cache(apt.progress.text.OpProgress())
986
+ cache.connect2("cache_pre_change", cache_pre_changed)
987
+ cache.connect2("cache_post_change", cache_post_changed)
988
+ print(("aptitude" in cache))
989
+ pkg = cache["aptitude"]
990
+ print(pkg.name)
991
+ print(len(cache))
992
+
993
+ for pkgname in cache.keys():
994
+ assert cache[pkgname].name == pkgname
995
+
996
+ cache.upgrade()
997
+ changes = cache.get_changes()
998
+ print(len(changes))
999
+ for pkg in changes:
1000
+ assert pkg.name
1001
+
1002
+ # see if fetching works
1003
+ for dirname in ["/tmp/pytest", "/tmp/pytest/partial"]:
1004
+ if not os.path.exists(dirname):
1005
+ os.mkdir(dirname)
1006
+ apt_pkg.config.set("Dir::Cache::Archives", "/tmp/pytest")
1007
+ pm = apt_pkg.PackageManager(cache._depcache)
1008
+ fetcher = apt_pkg.Acquire(apt.progress.text.AcquireProgress())
1009
+ cache._fetch_archives(fetcher, pm, None)
1010
+ #sys.exit(1)
1011
+
1012
+ print("Testing filtered cache (argument is old cache)")
1013
+ filtered = FilteredCache(cache)
1014
+ filtered.cache.connect2("cache_pre_change", cache_pre_changed)
1015
+ filtered.cache.connect2("cache_post_change", cache_post_changed)
1016
+ filtered.cache.upgrade()
1017
+ filtered.set_filter(MarkedChangesFilter())
1018
+ print(len(filtered))
1019
+ for pkgname in filtered.keys():
1020
+ assert pkgname == filtered[pkgname].name
1021
+
1022
+ print(len(filtered))
1023
+
1024
+ print("Testing filtered cache (no argument)")
1025
+ filtered = FilteredCache(progress=apt.progress.base.OpProgress())
1026
+ filtered.cache.connect2("cache_pre_change", cache_pre_changed)
1027
+ filtered.cache.connect2("cache_post_change", cache_post_changed)
1028
+ filtered.cache.upgrade()
1029
+ filtered.set_filter(MarkedChangesFilter())
1030
+ print(len(filtered))
1031
+ for pkgname in filtered.keys():
1032
+ assert pkgname == filtered[pkgname].name
1033
+
1034
+ print(len(filtered))
1035
+
1036
+
1037
+ if __name__ == '__main__':
1038
+ _test()
apt/cdrom.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # cdrom.py - CDROM handling
2
+ #
3
+ # Copyright (c) 2005-2009 Canonical
4
+ # Copyright (c) 2009 Julian Andres Klode <[email protected]>
5
+ #
6
+ # Author: Michael Vogt <[email protected]>
7
+ #
8
+ # This program is free software; you can redistribute it and/or
9
+ # modify it under the terms of the GNU General Public License as
10
+ # published by the Free Software Foundation; either version 2 of the
11
+ # License, or (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21
+ # USA
22
+ """Classes related to cdrom handling."""
23
+ from __future__ import print_function
24
+
25
+ from typing import Optional
26
+ import glob
27
+
28
+ import apt_pkg
29
+ from apt.progress.base import CdromProgress
30
+
31
+
32
+ class Cdrom(apt_pkg.Cdrom):
33
+ """Support for apt-cdrom like features.
34
+
35
+ This class has several optional parameters for initialisation, which may
36
+ be used to influence the behaviour of the object:
37
+
38
+ The optional parameter `progress` is a CdromProgress() subclass, which will
39
+ ask for the correct cdrom, etc. If not specified or None, a CdromProgress()
40
+ object will be used.
41
+
42
+ The optional parameter `mountpoint` may be used to specify an alternative
43
+ mountpoint.
44
+
45
+ If the optional parameter `nomount` is True, the cdroms will not be
46
+ mounted. This is the default behaviour.
47
+ """
48
+
49
+ def __init__(self, progress=None, mountpoint=None, nomount=True):
50
+ # type: (Optional[CdromProgress], Optional[str], bool) -> None
51
+ apt_pkg.Cdrom.__init__(self)
52
+ if progress is None:
53
+ self._progress = CdromProgress()
54
+ else:
55
+ self._progress = progress
56
+ # see if we have a alternative mountpoint
57
+ if mountpoint is not None:
58
+ apt_pkg.config.set("Acquire::cdrom::mount", mountpoint)
59
+ # do not mess with mount points by default
60
+ if nomount:
61
+ apt_pkg.config.set("APT::CDROM::NoMount", "true")
62
+ else:
63
+ apt_pkg.config.set("APT::CDROM::NoMount", "false")
64
+
65
+ def add(self, progress=None):
66
+ # type: (Optional[CdromProgress]) -> bool
67
+ """Add cdrom to the sources.list."""
68
+ return apt_pkg.Cdrom.add(self, progress or self._progress)
69
+
70
+ def ident(self, progress=None):
71
+ # type: (Optional[CdromProgress]) -> str
72
+ """Identify the cdrom."""
73
+ return apt_pkg.Cdrom.ident(self, progress or self._progress)
74
+
75
+ @property
76
+ def in_sources_list(self):
77
+ # type: () -> bool
78
+ """Check if the cdrom is already in the current sources.list."""
79
+ cd_id = self.ident()
80
+ if cd_id is None:
81
+ # FIXME: throw exception instead
82
+ return False
83
+ # Get a list of files
84
+ src = glob.glob(apt_pkg.config.find_dir("Dir::Etc::sourceparts") + '*')
85
+ src.append(apt_pkg.config.find_file("Dir::Etc::sourcelist"))
86
+ # Check each file
87
+ for fname in src:
88
+ with open(fname) as fobj:
89
+ for line in fobj:
90
+ if not line.lstrip().startswith("#") and cd_id in line:
91
+ return True
92
+ return False
apt/debfile.py ADDED
@@ -0,0 +1,868 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2005-2010 Canonical
2
+ #
3
+ # Author: Michael Vogt <[email protected]>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License as
7
+ # published by the Free Software Foundation; either version 2 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
+ # USA
19
+ """Classes for working with locally available Debian packages."""
20
+ from __future__ import print_function
21
+
22
+ import apt
23
+ import apt_inst
24
+ import apt_pkg
25
+ import gzip
26
+ import os
27
+ import sys
28
+
29
+ from typing import Dict, Iterable, List, Optional, Set, Tuple, Union, cast
30
+
31
+ from apt_pkg import gettext as _
32
+ from io import BytesIO
33
+
34
+
35
+ class NoDebArchiveException(IOError):
36
+ """Exception which is raised if a file is no Debian archive."""
37
+
38
+
39
+ class DebPackage(object):
40
+ """A Debian Package (.deb file)."""
41
+
42
+ # Constants for comparing the local package file with the version
43
+ # in the cache
44
+ (VERSION_NONE,
45
+ VERSION_OUTDATED,
46
+ VERSION_SAME,
47
+ VERSION_NEWER) = range(4)
48
+
49
+ debug = 0
50
+
51
+ def __init__(self, filename=None, cache=None):
52
+ # type: (Optional[str], Optional[apt.Cache]) -> None
53
+ if cache is None:
54
+ cache = apt.Cache()
55
+ self._cache = cache
56
+ self._debfile = cast(apt_inst.DebFile, None)
57
+ self.pkgname = ""
58
+ self.filename = None # type: Optional[str]
59
+ self._sections = {} # type: Union[Dict[str, str], apt_pkg.TagSection[str]] # noqa
60
+ self._need_pkgs = [] # type: List[str]
61
+ self._check_was_run = False
62
+ self._failure_string = ""
63
+ self._multiarch = None # type: Optional[str]
64
+ if filename:
65
+ self.open(filename)
66
+
67
+ def open(self, filename):
68
+ # type: (str) -> None
69
+ """ open given debfile """
70
+ self._dbg(3, "open '%s'" % filename)
71
+ self._need_pkgs = []
72
+ self._installed_conflicts = set() # type: Set[str]
73
+ self._failure_string = ""
74
+ self.filename = filename
75
+ self._debfile = apt_inst.DebFile(self.filename)
76
+ control = self._debfile.control.extractdata("control")
77
+ self._sections = apt_pkg.TagSection(control)
78
+ self.pkgname = self._sections["Package"]
79
+ self._check_was_run = False
80
+
81
+ def __getitem__(self, key):
82
+ # type: (str) -> str
83
+ return self._sections[key]
84
+
85
+ def __contains__(self, key):
86
+ # type: (str) -> bool
87
+ return key in self._sections
88
+
89
+ @property
90
+ def filelist(self):
91
+ # type: () -> List[str]
92
+ """return the list of files in the deb."""
93
+ files = []
94
+ try:
95
+ self._debfile.data.go(lambda item, data: files.append(item.name))
96
+ except SystemError:
97
+ return [_("List of files for '%s' could not be read") %
98
+ self.filename]
99
+ return files
100
+
101
+ @property
102
+ def control_filelist(self):
103
+ # type: () -> List[str]
104
+ """ return the list of files in control.tar.gz """
105
+ control = []
106
+ try:
107
+ self._debfile.control.go(
108
+ lambda item, data: control.append(item.name))
109
+ except SystemError:
110
+ return [_("List of control files for '%s' could not be read") %
111
+ self.filename]
112
+ return sorted(control)
113
+
114
+ # helper that will return a pkgname with a multiarch suffix if needed
115
+ def _maybe_append_multiarch_suffix(self, pkgname,
116
+ in_conflict_checking=False):
117
+ # type: (str, bool) -> str
118
+ # trivial cases
119
+ if ":" in pkgname:
120
+ return pkgname
121
+ if not self._multiarch:
122
+ return pkgname
123
+ elif self._cache.is_virtual_package(pkgname):
124
+ return pkgname
125
+ elif (pkgname in self._cache and
126
+ self._cache[pkgname].candidate is not None and
127
+ cast(apt.package.Version,
128
+ self._cache[pkgname].candidate).architecture == "all"):
129
+ return pkgname
130
+ # now do the real multiarch checking
131
+ multiarch_pkgname = "%s:%s" % (pkgname, self._multiarch)
132
+ # the upper layers will handle this
133
+ if multiarch_pkgname not in self._cache:
134
+ return multiarch_pkgname
135
+ multiarch_pkg = self._cache[multiarch_pkgname]
136
+ if multiarch_pkg.candidate is None:
137
+ return multiarch_pkgname
138
+ # now check the multiarch state
139
+ cand = multiarch_pkg.candidate._cand
140
+ #print pkgname, multiarch_pkgname, cand.multi_arch
141
+ # the default is to add the suffix, unless its a pkg that can satify
142
+ # foreign dependencies
143
+ if cand.multi_arch & cand.MULTI_ARCH_FOREIGN:
144
+ return pkgname
145
+ # for conflicts we need a special case here, any not multiarch enabled
146
+ # package has a implicit conflict
147
+ if (in_conflict_checking and
148
+ not (cand.multi_arch & cand.MULTI_ARCH_SAME)):
149
+ return pkgname
150
+ return multiarch_pkgname
151
+
152
+ def _is_or_group_satisfied(self, or_group):
153
+ # type: (List[Tuple[str, str, str]]) -> bool
154
+ """Return True if at least one dependency of the or-group is satisfied.
155
+
156
+ This method gets an 'or_group' and analyzes if at least one dependency
157
+ of this group is already satisfied.
158
+ """
159
+ self._dbg(2, "_checkOrGroup(): %s " % (or_group))
160
+
161
+ for dep in or_group:
162
+ depname = dep[0]
163
+ ver = dep[1]
164
+ oper = dep[2]
165
+
166
+ # multiarch
167
+ depname = self._maybe_append_multiarch_suffix(depname)
168
+
169
+ # check for virtual pkgs
170
+ if depname not in self._cache:
171
+ if self._cache.is_virtual_package(depname):
172
+ self._dbg(
173
+ 3, "_is_or_group_satisfied(): %s is virtual dep" %
174
+ depname)
175
+ for pkg in self._cache.get_providing_packages(depname):
176
+ if pkg.is_installed:
177
+ return True
178
+ continue
179
+ # check real dependency
180
+ inst = self._cache[depname].installed
181
+ if inst is not None and apt_pkg.check_dep(inst.version, oper, ver):
182
+ return True
183
+
184
+ # if no real dependency is installed, check if there is
185
+ # a package installed that provides this dependency
186
+ # (e.g. scrollkeeper dependecies are provided by rarian-compat)
187
+ # but only do that if there is no version required in the
188
+ # dependency (we do not supprot versionized dependencies)
189
+ if not oper:
190
+ for ppkg in self._cache.get_providing_packages(
191
+ depname, include_nonvirtual=True):
192
+ if ppkg.is_installed:
193
+ self._dbg(
194
+ 3, "found installed '%s' that provides '%s'" % (
195
+ ppkg.name, depname))
196
+ return True
197
+ return False
198
+
199
+ def _satisfy_or_group(self, or_group):
200
+ # type: (List[Tuple[str, str, str]]) -> bool
201
+ """Try to satisfy the or_group."""
202
+ for dep in or_group:
203
+ depname, ver, oper = dep
204
+
205
+ # multiarch
206
+ depname = self._maybe_append_multiarch_suffix(depname)
207
+
208
+ # if we don't have it in the cache, it may be virtual
209
+ if depname not in self._cache:
210
+ if not self._cache.is_virtual_package(depname):
211
+ continue
212
+ providers = self._cache.get_providing_packages(depname)
213
+ # if a package just has a single virtual provider, we
214
+ # just pick that (just like apt)
215
+ if len(providers) != 1:
216
+ continue
217
+ depname = providers[0].name
218
+
219
+ # now check if we can satisfy the deps with the candidate(s)
220
+ # in the cache
221
+ pkg = self._cache[depname]
222
+ cand = self._cache._depcache.get_candidate_ver(pkg._pkg)
223
+ if not cand:
224
+ continue
225
+ if not apt_pkg.check_dep(cand.ver_str, oper, ver):
226
+ continue
227
+
228
+ # check if we need to install it
229
+ self._dbg(2, "Need to get: %s" % depname)
230
+ self._need_pkgs.append(depname)
231
+ return True
232
+
233
+ # if we reach this point, we failed
234
+ or_str = ""
235
+ for dep in or_group:
236
+ or_str += dep[0]
237
+ if ver and oper:
238
+ or_str += " (%s %s)" % (dep[2], dep[1])
239
+ if dep != or_group[len(or_group) - 1]:
240
+ or_str += "|"
241
+ self._failure_string += _(
242
+ "Dependency is not satisfiable: %s\n") % or_str
243
+ return False
244
+
245
+ def _check_single_pkg_conflict(self, pkgname, ver, oper):
246
+ # type: (str, str, str) -> bool
247
+ """Return True if a pkg conflicts with a real installed/marked pkg."""
248
+ # FIXME: deal with conflicts against its own provides
249
+ # (e.g. Provides: ftp-server, Conflicts: ftp-server)
250
+ self._dbg(
251
+ 3, "_check_single_pkg_conflict() pkg='%s' ver='%s' oper='%s'" % (
252
+ pkgname, ver, oper))
253
+ pkg = self._cache[pkgname]
254
+ if pkg.is_installed:
255
+ assert pkg.installed is not None
256
+ pkgver = pkg.installed.version
257
+ elif pkg.marked_install:
258
+ assert pkg.candidate is not None
259
+ pkgver = pkg.candidate.version
260
+ else:
261
+ return False
262
+ #print "pkg: %s" % pkgname
263
+ #print "ver: %s" % ver
264
+ #print "pkgver: %s " % pkgver
265
+ #print "oper: %s " % oper
266
+ if (apt_pkg.check_dep(pkgver, oper, ver) and not
267
+ self.replaces_real_pkg(pkgname, oper, ver)):
268
+ self._failure_string += _("Conflicts with the installed package "
269
+ "'%s'") % pkg.name
270
+ self._dbg(3, "conflicts with installed pkg '%s'" % pkg.name)
271
+ return True
272
+ return False
273
+
274
+ def _check_conflicts_or_group(self, or_group):
275
+ # type: (List[Tuple[str, str, str]]) -> bool
276
+ """Check the or-group for conflicts with installed pkgs."""
277
+ self._dbg(2, "_check_conflicts_or_group(): %s " % (or_group))
278
+ for dep in or_group:
279
+ depname = dep[0]
280
+ ver = dep[1]
281
+ oper = dep[2]
282
+
283
+ # FIXME: is this good enough? i.e. will apt always populate
284
+ # the cache with conflicting pkgnames for our arch?
285
+ depname = self._maybe_append_multiarch_suffix(
286
+ depname, in_conflict_checking=True)
287
+
288
+ # check conflicts with virtual pkgs
289
+ if depname not in self._cache:
290
+ # FIXME: we have to check for virtual replaces here as
291
+ # well (to pass tests/gdebi-test8.deb)
292
+ if self._cache.is_virtual_package(depname):
293
+ for pkg in self._cache.get_providing_packages(depname):
294
+ self._dbg(3, "conflicts virtual check: %s" % pkg.name)
295
+ # P/C/R on virtal pkg, e.g. ftpd
296
+ if self.pkgname == pkg.name:
297
+ self._dbg(3, "conflict on self, ignoring")
298
+ continue
299
+ if self._check_single_pkg_conflict(
300
+ pkg.name, ver, oper):
301
+ self._installed_conflicts.add(pkg.name)
302
+ continue
303
+ if self._check_single_pkg_conflict(depname, ver, oper):
304
+ self._installed_conflicts.add(depname)
305
+ return bool(self._installed_conflicts)
306
+
307
+ @property
308
+ def conflicts(self):
309
+ # type: () -> List[List[Tuple[str, str, str]]]
310
+ """List of packages conflicting with this package."""
311
+ key = "Conflicts"
312
+ try:
313
+ return apt_pkg.parse_depends(self._sections[key], False)
314
+ except KeyError:
315
+ return []
316
+
317
+ @property
318
+ def depends(self):
319
+ # type: () -> List[List[Tuple[str, str, str]]]
320
+ """List of packages on which this package depends on."""
321
+ depends = []
322
+ # find depends
323
+ for key in "Depends", "Pre-Depends":
324
+ try:
325
+ depends.extend(
326
+ apt_pkg.parse_depends(self._sections[key], False))
327
+ except KeyError:
328
+ pass
329
+ return depends
330
+
331
+ @property
332
+ def provides(self):
333
+ # type: () -> List[List[Tuple[str, str, str]]]
334
+ """List of virtual packages which are provided by this package."""
335
+ key = "Provides"
336
+ try:
337
+ return apt_pkg.parse_depends(self._sections[key], False)
338
+ except KeyError:
339
+ return []
340
+
341
+ @property
342
+ def replaces(self):
343
+ # type: () -> List[List[Tuple[str, str, str]]]
344
+ """List of packages which are replaced by this package."""
345
+ key = "Replaces"
346
+ try:
347
+ return apt_pkg.parse_depends(self._sections[key], False)
348
+ except KeyError:
349
+ return []
350
+
351
+ def replaces_real_pkg(self, pkgname, oper, ver):
352
+ # type: (str, str, str) -> bool
353
+ """Return True if a given non-virtual package is replaced.
354
+
355
+ Return True if the deb packages replaces a real (not virtual)
356
+ packages named (pkgname, oper, ver).
357
+ """
358
+ self._dbg(3, "replaces_real_pkg() %s %s %s" % (pkgname, oper, ver))
359
+ pkg = self._cache[pkgname]
360
+ pkgver = None # type: Optional[str]
361
+ if pkg.is_installed:
362
+ assert pkg.installed is not None
363
+ pkgver = pkg.installed.version
364
+ elif pkg.marked_install:
365
+ assert pkg.candidate is not None
366
+ pkgver = pkg.candidate.version
367
+ else:
368
+ pkgver = None
369
+ for or_group in self.replaces:
370
+ for (name, ver, oper) in or_group:
371
+ if (name == pkgname and (pkgver is None or
372
+ apt_pkg.check_dep(pkgver, oper, ver))):
373
+ self._dbg(3, "we have a replaces in our package for the "
374
+ "conflict against '%s'" % (pkgname))
375
+ return True
376
+ return False
377
+
378
+ def check_conflicts(self):
379
+ # type: () -> bool
380
+ """Check if there are conflicts with existing or selected packages.
381
+
382
+ Check if the package conflicts with a existing or to be installed
383
+ package. Return True if the pkg is OK.
384
+ """
385
+ res = True
386
+ for or_group in self.conflicts:
387
+ if self._check_conflicts_or_group(or_group):
388
+ #print "Conflicts with a exisiting pkg!"
389
+ #self._failure_string = "Conflicts with a exisiting pkg!"
390
+ res = False
391
+ return res
392
+
393
+ def check_breaks_existing_packages(self):
394
+ # type: () -> bool
395
+ """
396
+ check if installing the package would break exsisting
397
+ package on the system, e.g. system has:
398
+ smc depends on smc-data (= 1.4)
399
+ and user tries to installs smc-data 1.6
400
+ """
401
+ # show progress information as this step may take some time
402
+ size = float(len(self._cache))
403
+ steps = max(int(size / 50), 1)
404
+ debver = self._sections["Version"]
405
+ debarch = self._sections["Architecture"]
406
+ # store what we provide so that we can later check against that
407
+ provides = [x[0][0] for x in self.provides]
408
+ for (i, pkg) in enumerate(self._cache):
409
+ if i % steps == 0:
410
+ self._cache.op_progress.update(float(i) / size * 100.0)
411
+ if not pkg.is_installed:
412
+ continue
413
+ assert pkg.installed is not None
414
+ # check if the exising dependencies are still satisfied
415
+ # with the package
416
+ ver = pkg._pkg.current_ver
417
+ for dep_or in pkg.installed.dependencies:
418
+ for dep in dep_or.or_dependencies:
419
+ if dep.name == self.pkgname:
420
+ if not apt_pkg.check_dep(
421
+ debver, dep.relation, dep.version):
422
+ self._dbg(2, "would break (depends) %s" % pkg.name)
423
+ # TRANSLATORS: the first '%s' is the package that
424
+ # breaks, the second the dependency that makes it
425
+ # break, the third the relation (e.g. >=) and the
426
+ # latest the version for the releation
427
+ self._failure_string += _(
428
+ "Breaks existing package '%(pkgname)s' "
429
+ "dependency %(depname)s "
430
+ "(%(deprelation)s %(depversion)s)") % {
431
+ 'pkgname': pkg.name,
432
+ 'depname': dep.name,
433
+ 'deprelation': dep.relation,
434
+ 'depversion': dep.version}
435
+ self._cache.op_progress.done()
436
+ return False
437
+ # now check if there are conflicts against this package on
438
+ # the existing system
439
+ if "Conflicts" in ver.depends_list:
440
+ for conflicts_ver_list in ver.depends_list["Conflicts"]:
441
+ for c_or in conflicts_ver_list:
442
+ if (c_or.target_pkg.name == self.pkgname and
443
+ c_or.target_pkg.architecture == debarch):
444
+ if apt_pkg.check_dep(
445
+ debver, c_or.comp_type, c_or.target_ver):
446
+ self._dbg(
447
+ 2, "would break (conflicts) %s" % pkg.name)
448
+ # TRANSLATORS: the first '%s' is the package
449
+ # that conflicts, the second the packagename
450
+ # that it conflicts with (so the name of the
451
+ # deb the user tries to install), the third is
452
+ # the relation (e.g. >=) and the last is the
453
+ # version for the relation
454
+ self._failure_string += _(
455
+ "Breaks existing package '%(pkgname)s' "
456
+ "conflict: %(targetpkg)s "
457
+ "(%(comptype)s %(targetver)s)") % {
458
+ 'pkgname': pkg.name,
459
+ 'targetpkg': c_or.target_pkg.name,
460
+ 'comptype': c_or.comp_type,
461
+ 'targetver': c_or.target_ver}
462
+ self._cache.op_progress.done()
463
+ return False
464
+ if (c_or.target_pkg.name in provides and
465
+ self.pkgname != pkg.name):
466
+ self._dbg(
467
+ 2, "would break (conflicts) %s" % provides)
468
+ self._failure_string += _(
469
+ "Breaks existing package '%(pkgname)s' "
470
+ "that conflict: '%(targetpkg)s'. But the "
471
+ "'%(debfile)s' provides it via: "
472
+ "'%(provides)s'") % {
473
+ 'provides': ",".join(provides),
474
+ 'debfile': self.filename,
475
+ 'targetpkg': c_or.target_pkg.name,
476
+ 'pkgname': pkg.name}
477
+ self._cache.op_progress.done()
478
+ return False
479
+ self._cache.op_progress.done()
480
+ return True
481
+
482
+ def compare_to_version_in_cache(self, use_installed=True):
483
+ # type: (bool) -> int
484
+ """Compare the package to the version available in the cache.
485
+
486
+ Checks if the package is already installed or availabe in the cache
487
+ and if so in what version, returns one of (VERSION_NONE,
488
+ VERSION_OUTDATED, VERSION_SAME, VERSION_NEWER).
489
+ """
490
+ self._dbg(3, "compare_to_version_in_cache")
491
+ pkgname = self._sections["Package"]
492
+ architecture = self._sections["Architecture"]
493
+
494
+ # Arch qualify the package name
495
+ pkgname = ":".join([pkgname, architecture])
496
+
497
+ debver = self._sections["Version"]
498
+ self._dbg(1, "debver: %s" % debver)
499
+ if pkgname in self._cache:
500
+ pkg = self._cache[pkgname]
501
+ if use_installed and pkg.installed is not None:
502
+ cachever = pkg.installed.version
503
+ elif not use_installed and pkg.candidate is not None:
504
+ cachever = pkg.candidate.version
505
+ else:
506
+ return self.VERSION_NONE
507
+ if cachever is not None:
508
+ cmp = apt_pkg.version_compare(cachever, debver)
509
+ self._dbg(1, "CompareVersion(debver,instver): %s" % cmp)
510
+ if cmp == 0:
511
+ return self.VERSION_SAME
512
+ elif cmp < 0:
513
+ return self.VERSION_NEWER
514
+ elif cmp > 0:
515
+ return self.VERSION_OUTDATED
516
+ return self.VERSION_NONE
517
+
518
+ def check(self, allow_downgrade=False):
519
+ # type: (bool) -> bool
520
+ """Check if the package is installable."""
521
+ self._dbg(3, "check")
522
+
523
+ self._check_was_run = True
524
+
525
+ # check arch
526
+ if "Architecture" not in self._sections:
527
+ self._dbg(1, "ERROR: no architecture field")
528
+ self._failure_string = _("No Architecture field in the package")
529
+ return False
530
+ arch = self._sections["Architecture"]
531
+ if arch != "all" and arch != apt_pkg.config.find("APT::Architecture"):
532
+ if arch in apt_pkg.get_architectures():
533
+ self._multiarch = arch
534
+ self.pkgname = "%s:%s" % (self.pkgname, self._multiarch)
535
+ self._dbg(1, "Found multiarch arch: '%s'" % arch)
536
+ else:
537
+ self._dbg(1, "ERROR: Wrong architecture dude!")
538
+ self._failure_string = _("Wrong architecture '%s' "
539
+ "-- Run dpkg --add-architecture to "
540
+ "add it and update afterwards") % arch
541
+ return False
542
+
543
+ # check version
544
+ if (not allow_downgrade and
545
+ self.compare_to_version_in_cache() == self.VERSION_OUTDATED):
546
+ if self._cache[self.pkgname].installed:
547
+ # the deb is older than the installed
548
+ self._failure_string = _(
549
+ "A later version is already installed")
550
+ return False
551
+
552
+ # FIXME: this sort of error handling sux
553
+ self._failure_string = ""
554
+
555
+ # check conflicts
556
+ if not self.check_conflicts():
557
+ return False
558
+
559
+ # check if installing it would break anything on the
560
+ # current system
561
+ if not self.check_breaks_existing_packages():
562
+ return False
563
+
564
+ # try to satisfy the dependencies
565
+ if not self._satisfy_depends(self.depends):
566
+ return False
567
+
568
+ # check for conflicts again (this time with the packages that are
569
+ # makeed for install)
570
+ if not self.check_conflicts():
571
+ return False
572
+
573
+ if self._cache._depcache.broken_count > 0:
574
+ self._failure_string = _("Failed to satisfy all dependencies "
575
+ "(broken cache)")
576
+ # clean the cache again
577
+ self._cache.clear()
578
+ return False
579
+ return True
580
+
581
+ def satisfy_depends_str(self, dependsstr):
582
+ # type: (str) -> bool
583
+ """Satisfy the dependencies in the given string."""
584
+ return self._satisfy_depends(apt_pkg.parse_depends(dependsstr, False))
585
+
586
+ def _satisfy_depends(self, depends):
587
+ # type: (List[List[Tuple[str, str, str]]]) -> bool
588
+ """Satisfy the dependencies."""
589
+ # turn off MarkAndSweep via a action group (if available)
590
+ try:
591
+ _actiongroup = apt_pkg.ActionGroup(self._cache._depcache)
592
+ _actiongroup # pyflakes
593
+ except AttributeError:
594
+ pass
595
+ # check depends
596
+ for or_group in depends:
597
+ if not self._is_or_group_satisfied(or_group):
598
+ if not self._satisfy_or_group(or_group):
599
+ return False
600
+ # now try it out in the cache
601
+ for pkg in self._need_pkgs:
602
+ try:
603
+ self._cache[pkg].mark_install(from_user=False)
604
+ except SystemError:
605
+ self._failure_string = _("Cannot install '%s'") % pkg
606
+ self._cache.clear()
607
+ return False
608
+ return True
609
+
610
+ @property
611
+ def missing_deps(self):
612
+ # type: () -> List[str]
613
+ """Return missing dependencies."""
614
+ self._dbg(1, "Installing: %s" % self._need_pkgs)
615
+ if not self._check_was_run:
616
+ raise AttributeError(
617
+ "property only available after check() was run")
618
+ return self._need_pkgs
619
+
620
+ @property
621
+ def required_changes(self):
622
+ # type: () -> Tuple[List[str], List[str], List[str]]
623
+ """Get the changes required to satisfy the dependencies.
624
+
625
+ Returns: a tuple with (install, remove, unauthenticated)
626
+ """
627
+ install = []
628
+ remove = []
629
+ unauthenticated = []
630
+ if not self._check_was_run:
631
+ raise AttributeError(
632
+ "property only available after check() was run")
633
+ for pkg in self._cache:
634
+ if pkg.marked_install or pkg.marked_upgrade:
635
+ assert pkg.candidate is not None
636
+ install.append(pkg.name)
637
+ # check authentication, one authenticated origin is enough
638
+ # libapt will skip non-authenticated origins then
639
+ authenticated = False
640
+ for origin in pkg.candidate.origins:
641
+ authenticated |= origin.trusted
642
+ if not authenticated:
643
+ unauthenticated.append(pkg.name)
644
+ if pkg.marked_delete:
645
+ remove.append(pkg.name)
646
+ return (install, remove, unauthenticated)
647
+
648
+ @staticmethod
649
+ def to_hex(in_data):
650
+ # type: (str) -> str
651
+ hex = ""
652
+ for (i, c) in enumerate(in_data):
653
+ if i % 80 == 0:
654
+ hex += "\n"
655
+ hex += "%2.2x " % ord(c)
656
+ return hex
657
+
658
+ @staticmethod
659
+ def to_strish(in_data):
660
+ # type: (Union[str, Iterable[int]]) -> str
661
+ s = ""
662
+ # py2 compat, in_data is type string
663
+ if isinstance(in_data, str):
664
+ for c in in_data:
665
+ if ord(c) < 10 or ord(c) > 127:
666
+ s += " "
667
+ else:
668
+ s += c
669
+ # py3 compat, in_data is type bytes
670
+ else:
671
+ for b in in_data:
672
+ if b < 10 or b > 127:
673
+ s += " "
674
+ else:
675
+ s += chr(b)
676
+ return s
677
+
678
+ def _get_content(self, part, name, auto_decompress=True, auto_hex=True):
679
+ # type: (apt_inst.TarFile, str, bool, bool) -> str
680
+ if name.startswith("./"):
681
+ name = name[2:]
682
+ data = part.extractdata(name)
683
+ # check for zip content
684
+ if name.endswith(".gz") and auto_decompress:
685
+ io = BytesIO(data)
686
+ gz = gzip.GzipFile(fileobj=io)
687
+ data = _("Automatically decompressed:\n\n").encode("utf-8")
688
+ data += gz.read()
689
+ # auto-convert to hex
690
+ try:
691
+ return data.decode("utf-8")
692
+ except Exception:
693
+ new_data = _("Automatically converted to printable ascii:\n")
694
+ new_data += self.to_strish(data)
695
+ return new_data
696
+
697
+ def control_content(self, name):
698
+ # type: (str) -> str
699
+ """ return the content of a specific control.tar.gz file """
700
+ try:
701
+ return self._get_content(self._debfile.control, name)
702
+ except LookupError:
703
+ return ""
704
+
705
+ def data_content(self, name):
706
+ # type: (str) -> str
707
+ """ return the content of a specific control.tar.gz file """
708
+ try:
709
+ return self._get_content(self._debfile.data, name)
710
+ except LookupError:
711
+ return ""
712
+
713
+ def _dbg(self, level, msg):
714
+ # type: (int, str) -> None
715
+ """Write debugging output to sys.stderr."""
716
+ if level <= self.debug:
717
+ print(msg, file=sys.stderr)
718
+
719
+ def install(self, install_progress=None):
720
+ # type: (Optional[apt.progress.base.InstallProgress]) -> int
721
+ """Install the package."""
722
+ if self.filename is None:
723
+ raise apt_pkg.Error("No filename specified")
724
+ if install_progress is None:
725
+ return os.spawnlp(os.P_WAIT, "dpkg", "dpkg", "-i", self.filename)
726
+ else:
727
+ try:
728
+ install_progress.start_update()
729
+ except AttributeError:
730
+ install_progress.startUpdate() # type: ignore
731
+ res = install_progress.run(self.filename)
732
+ try:
733
+ install_progress.finish_update()
734
+ except AttributeError:
735
+ install_progress.finishUpdate() # type: ignore
736
+ return res
737
+
738
+
739
+ class DscSrcPackage(DebPackage):
740
+ """A locally available source package."""
741
+
742
+ def __init__(self, filename=None, cache=None):
743
+ # type: (Optional[str], Optional[apt.Cache]) -> None
744
+ DebPackage.__init__(self, None, cache)
745
+ self.filename = filename # type: Optional[str]
746
+ self._depends = [] # type: List[List[Tuple[str, str, str]]]
747
+ self._conflicts = [] # type: List[List[Tuple[str, str, str]]]
748
+ self._installed_conflicts = set() # type: Set[str]
749
+ self.pkgname = ""
750
+ self.binaries = [] # type: List[str]
751
+ self._sections = {} # type: Dict[str, str]
752
+ if self.filename is not None:
753
+ self.open(self.filename)
754
+
755
+ @property
756
+ def depends(self):
757
+ # type: () -> List[List[Tuple[str, str, str]]]
758
+ """Return the dependencies of the package"""
759
+ return self._depends
760
+
761
+ @property
762
+ def conflicts(self):
763
+ # type: () -> List[List[Tuple[str, str, str]]]
764
+ """Return the dependencies of the package"""
765
+ return self._conflicts
766
+
767
+ @property
768
+ def filelist(self):
769
+ # type: () -> List[str]
770
+ """Return the list of files associated with this dsc file"""
771
+ # Files stanza looks like (hash, size, filename, ...)
772
+ return self._sections['Files'].split()[2::3]
773
+
774
+ def open(self, file):
775
+ # type: (str) -> None
776
+ """Open the package."""
777
+ depends_tags = ["Build-Depends", "Build-Depends-Indep"]
778
+ conflicts_tags = ["Build-Conflicts", "Build-Conflicts-Indep"]
779
+ fd = apt_pkg.open_maybe_clear_signed_file(file)
780
+ fobj = os.fdopen(fd)
781
+ tagfile = apt_pkg.TagFile(fobj)
782
+ try:
783
+ for sec in tagfile:
784
+ for tag in depends_tags:
785
+ if tag not in sec:
786
+ continue
787
+ self._depends.extend(apt_pkg.parse_src_depends(sec[tag]))
788
+ for tag in conflicts_tags:
789
+ if tag not in sec:
790
+ continue
791
+ self._conflicts.extend(apt_pkg.parse_src_depends(sec[tag]))
792
+ if 'Source' in sec:
793
+ self.pkgname = sec['Source']
794
+ if 'Binary' in sec:
795
+ self.binaries = [b.strip() for b in
796
+ sec['Binary'].split(',')]
797
+ for tag in sec.keys():
798
+ if tag in sec:
799
+ self._sections[tag] = sec[tag]
800
+ finally:
801
+ del tagfile
802
+ fobj.close()
803
+
804
+ s = _("Install Build-Dependencies for "
805
+ "source package '%s' that builds %s\n") % (self.pkgname,
806
+ " ".join(self.binaries))
807
+ self._sections["Description"] = s
808
+ self._check_was_run = False
809
+
810
+ def check(self, allow_downgrade=False):
811
+ # type: (bool) -> bool
812
+ """Check if the package is installable.
813
+
814
+ The second parameter is ignored and only exists for compatibility
815
+ with parent type."""
816
+ if not self.check_conflicts():
817
+ for pkgname in self._installed_conflicts:
818
+ if self._cache[pkgname]._pkg.essential:
819
+ raise Exception(_("An essential package would be removed"))
820
+ self._cache[pkgname].mark_delete()
821
+ # properties are ok now
822
+ self._check_was_run = True
823
+ # FIXME: a additional run of the check_conflicts()
824
+ # after _satisfy_depends() should probably be done
825
+ return self._satisfy_depends(self.depends)
826
+
827
+
828
+ def _test():
829
+ # type: () -> None
830
+ """Test function"""
831
+ from apt.cache import Cache
832
+ from apt.progress.base import InstallProgress
833
+
834
+ cache = Cache()
835
+
836
+ vp = "www-browser"
837
+ print("%s virtual: %s" % (vp, cache.is_virtual_package(vp)))
838
+ providers = cache.get_providing_packages(vp)
839
+ print("Providers for %s :" % vp)
840
+ for pkg in providers:
841
+ print(" %s" % pkg.name)
842
+
843
+ d = DebPackage(sys.argv[1], cache)
844
+ print("Deb: %s" % d.pkgname)
845
+ if not d.check():
846
+ print("can't be satified")
847
+ print(d._failure_string)
848
+ print("missing deps: %s" % d.missing_deps)
849
+ print(d.required_changes)
850
+
851
+ print(d.filelist)
852
+
853
+ print("Installing ...")
854
+ ret = d.install(InstallProgress())
855
+ print(ret)
856
+
857
+ #s = DscSrcPackage(cache, "../tests/3ddesktop_0.2.9-6.dsc")
858
+ #s.check_dep()
859
+ #print "Missing deps: ",s.missingDeps
860
+ #print "Print required changes: ", s.requiredChanges
861
+
862
+ s = DscSrcPackage(cache=cache)
863
+ ds = "libc6 (>= 2.3.2), libaio (>= 0.3.96) | libaio1 (>= 0.3.96)"
864
+ print(s._satisfy_depends(apt_pkg.parse_depends(ds, False)))
865
+
866
+
867
+ if __name__ == "__main__":
868
+ _test()
apt/package.py ADDED
@@ -0,0 +1,1604 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # package.py - apt package abstraction
2
+ #
3
+ # Copyright (c) 2005-2009 Canonical
4
+ #
5
+ # Author: Michael Vogt <[email protected]>
6
+ #
7
+ # This program is free software; you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as
9
+ # published by the Free Software Foundation; either version 2 of the
10
+ # License, or (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
20
+ # USA
21
+ """Functionality related to packages."""
22
+ from __future__ import print_function
23
+
24
+ import logging
25
+ import os
26
+ import sys
27
+ import re
28
+ import socket
29
+ import subprocess
30
+ import threading
31
+
32
+ from http.client import BadStatusLine
33
+ from urllib.error import HTTPError
34
+ from urllib.request import urlopen
35
+
36
+ from typing import (Any, Iterable, Iterator, List, Optional, Set,
37
+ Tuple, Union, no_type_check, Mapping,
38
+ Sequence)
39
+
40
+ import apt_pkg
41
+ import apt.progress.text
42
+
43
+ from apt.progress.base import (
44
+ AcquireProgress,
45
+ InstallProgress,
46
+ )
47
+
48
+ from apt_pkg import gettext as _
49
+
50
+ __all__ = ('BaseDependency', 'Dependency', 'Origin', 'Package', 'Record',
51
+ 'Version', 'VersionList')
52
+
53
+
54
+ def _file_is_same(path, size, hashes):
55
+ # type: (str, int, apt_pkg.HashStringList) -> bool
56
+ """Return ``True`` if the file is the same."""
57
+ if os.path.exists(path) and os.path.getsize(path) == size:
58
+ with open(path) as fobj:
59
+ return apt_pkg.Hashes(fobj).hashes == hashes
60
+ return False
61
+
62
+
63
+ class FetchError(Exception):
64
+ """Raised when a file could not be fetched."""
65
+
66
+
67
+ class UntrustedError(FetchError):
68
+ """Raised when a file did not have a trusted hash."""
69
+
70
+
71
+ class BaseDependency(object):
72
+ """A single dependency."""
73
+
74
+ class __dstr(str):
75
+ """Compare helper for compatibility with old third-party code.
76
+
77
+ Old third-party code might still compare the relation with the
78
+ previously used relations (<<,<=,==,!=,>=,>>,) instead of the curently
79
+ used ones (<,<=,=,!=,>=,>,). This compare helper lets < match to <<,
80
+ > match to >> and = match to ==.
81
+ """
82
+
83
+ def __eq__(self, other):
84
+ # type: (object) -> bool
85
+ if str.__eq__(self, other):
86
+ return True
87
+ elif str.__eq__(self, '<'):
88
+ return str.__eq__('<<', other)
89
+ elif str.__eq__(self, '>'):
90
+ return str.__eq__('>>', other)
91
+ elif str.__eq__(self, '='):
92
+ return str.__eq__('==', other)
93
+ else:
94
+ return False
95
+
96
+ def __ne__(self, other):
97
+ # type: (object) -> bool
98
+ return not self.__eq__(other)
99
+
100
+ def __init__(self, version, dep):
101
+ # type: (Version, apt_pkg.Dependency) -> None
102
+ self._version = version # apt.package.Version
103
+ self._dep = dep # apt_pkg.Dependency
104
+
105
+ def __str__(self):
106
+ # type: () -> str
107
+ return '%s: %s' % (self.rawtype, self.rawstr)
108
+
109
+ def __repr__(self):
110
+ # type: () -> str
111
+ return ('<BaseDependency: name:%r relation:%r version:%r rawtype:%r>'
112
+ % (self.name, self.relation, self.version, self.rawtype))
113
+
114
+ @property
115
+ def name(self):
116
+ # type: () -> str
117
+ """The name of the target package."""
118
+ return self._dep.target_pkg.name
119
+
120
+ @property
121
+ def relation(self):
122
+ # type: () -> str
123
+ """The relation (<, <=, =, !=, >=, >, '') in mathematical notation.
124
+
125
+ The empty string will be returned in case of an unversioned dependency.
126
+ """
127
+ return self.__dstr(self._dep.comp_type)
128
+
129
+ @property
130
+ def relation_deb(self):
131
+ # type: () -> str
132
+ """The relation (<<, <=, =, !=, >=, >>, '') in Debian notation.
133
+
134
+ The empty string will be returned in case of an unversioned dependency.
135
+ For more details see the Debian Policy Manual on the syntax of
136
+ relationship fields:
137
+ https://www.debian.org/doc/debian-policy/ch-relationships.html#s-depsyntax # noqa
138
+
139
+ .. versionadded:: 1.0.0
140
+ """
141
+ return self._dep.comp_type_deb
142
+
143
+ @property
144
+ def version(self):
145
+ # type: () -> str
146
+ """The target version or an empty string.
147
+
148
+ Note that the version is only an empty string in case of an unversioned
149
+ dependency. In this case the relation is also an empty string.
150
+ """
151
+ return self._dep.target_ver
152
+
153
+ @property
154
+ def target_versions(self):
155
+ # type: () -> List[Version]
156
+ """A list of all Version objects which satisfy this dependency.
157
+
158
+ .. versionadded:: 1.0.0
159
+ """
160
+ tvers = []
161
+ _tvers = self._dep.all_targets() # type: List[apt_pkg.Version]
162
+ for _tver in _tvers: # type: apt_pkg.Version
163
+ _pkg = _tver.parent_pkg # type: apt_pkg.Package
164
+ cache = self._version.package._pcache # apt.cache.Cache
165
+ pkg = cache._rawpkg_to_pkg(_pkg) # apt.package.Package
166
+ tver = Version(pkg, _tver) # apt.package.Version
167
+ tvers.append(tver)
168
+ return tvers
169
+
170
+ @property
171
+ def installed_target_versions(self):
172
+ # type: () -> List[Version]
173
+ """A list of all installed Version objects which satisfy this dep.
174
+
175
+ .. versionadded:: 1.0.0
176
+ """
177
+ return [tver for tver in self.target_versions if tver.is_installed]
178
+
179
+ @property
180
+ def rawstr(self):
181
+ # type: () -> str
182
+ """String represenation of the dependency.
183
+
184
+ Returns the string representation of the dependency as it would be
185
+ written in the debian/control file. The string representation does not
186
+ include the type of the dependency.
187
+
188
+ Example for an unversioned dependency:
189
+ python3
190
+
191
+ Example for a versioned dependency:
192
+ python3 >= 3.2
193
+
194
+ .. versionadded:: 1.0.0
195
+ """
196
+ if self.version:
197
+ return '%s %s %s' % (self.name, self.relation_deb, self.version)
198
+ else:
199
+ return self.name
200
+
201
+ @property
202
+ def rawtype(self):
203
+ # type: () -> str
204
+ """Type of the dependency.
205
+
206
+ This should be one of 'Breaks', 'Conflicts', 'Depends', 'Enhances',
207
+ 'PreDepends', 'Recommends', 'Replaces', 'Suggests'.
208
+
209
+ Additional types might be added in the future.
210
+ """
211
+ return self._dep.dep_type_untranslated
212
+
213
+ @property
214
+ def pre_depend(self):
215
+ # type: () -> bool
216
+ """Whether this is a PreDepends."""
217
+ return self._dep.dep_type_untranslated == 'PreDepends'
218
+
219
+
220
+ class Dependency(List[BaseDependency]):
221
+ """Represent an Or-group of dependencies.
222
+
223
+ Attributes defined here:
224
+ or_dependencies - The possible choices
225
+ rawstr - String represenation of the Or-group of dependencies
226
+ rawtype - The type of the dependencies in the Or-group
227
+ target_version - A list of Versions which satisfy this Or-group of deps
228
+ """
229
+
230
+ def __init__(self, version, base_deps, rawtype):
231
+ # type: (Version, List[BaseDependency], str) -> None
232
+ super(Dependency, self).__init__(base_deps)
233
+ self._version = version # apt.package.Version
234
+ self._rawtype = rawtype
235
+
236
+ def __str__(self):
237
+ # type: () -> str
238
+ return '%s: %s' % (self.rawtype, self.rawstr)
239
+
240
+ def __repr__(self):
241
+ # type: () -> str
242
+ return '<Dependency: [%s]>' % (', '.join(repr(bd) for bd in self))
243
+
244
+ @property
245
+ def or_dependencies(self):
246
+ # type: () -> Dependency
247
+ return self
248
+
249
+ @property
250
+ def rawstr(self):
251
+ # type: () -> str
252
+ """String represenation of the Or-group of dependencies.
253
+
254
+ Returns the string representation of the Or-group of dependencies as it
255
+ would be written in the debian/control file. The string representation
256
+ does not include the type of the Or-group of dependencies.
257
+
258
+ Example:
259
+ python2 >= 2.7 | python3
260
+
261
+ .. versionadded:: 1.0.0
262
+ """
263
+ return ' | '.join(bd.rawstr for bd in self)
264
+
265
+ @property
266
+ def rawtype(self):
267
+ # type: () -> str
268
+ """Type of the Or-group of dependency.
269
+
270
+ This should be one of 'Breaks', 'Conflicts', 'Depends', 'Enhances',
271
+ 'PreDepends', 'Recommends', 'Replaces', 'Suggests'.
272
+
273
+ Additional types might be added in the future.
274
+
275
+ .. versionadded:: 1.0.0
276
+ """
277
+ return self._rawtype
278
+
279
+ @property
280
+ def target_versions(self):
281
+ # type: () -> List[Version]
282
+ """A list of all Version objects which satisfy this Or-group of deps.
283
+
284
+ .. versionadded:: 1.0.0
285
+ """
286
+ tvers = [] # type: List[Version]
287
+ for bd in self: # apt.package.Dependency
288
+ for tver in bd.target_versions: # apt.package.Version
289
+ if tver not in tvers:
290
+ tvers.append(tver)
291
+ return tvers
292
+
293
+ @property
294
+ def installed_target_versions(self):
295
+ # type: () -> List[Version]
296
+ """A list of all installed Version objects which satisfy this dep.
297
+
298
+ .. versionadded:: 1.0.0
299
+ """
300
+ return [tver for tver in self.target_versions if tver.is_installed]
301
+
302
+
303
+ class Origin(object):
304
+ """The origin of a version.
305
+
306
+ Attributes defined here:
307
+ archive - The archive (eg. unstable)
308
+ component - The component (eg. main)
309
+ label - The Label, as set in the Release file
310
+ origin - The Origin, as set in the Release file
311
+ codename - The Codename, as set in the Release file
312
+ site - The hostname of the site.
313
+ trusted - Boolean value whether this is trustworthy.
314
+ """
315
+
316
+ def __init__(self, pkg, packagefile):
317
+ # type: (Package, apt_pkg.PackageFile) -> None
318
+ self.archive = packagefile.archive
319
+ self.component = packagefile.component
320
+ self.label = packagefile.label
321
+ self.origin = packagefile.origin
322
+ self.codename = packagefile.codename
323
+ self.site = packagefile.site
324
+ self.not_automatic = packagefile.not_automatic
325
+ # check the trust
326
+ indexfile = pkg._pcache._list.find_index(packagefile)
327
+ if indexfile and indexfile.is_trusted:
328
+ self.trusted = True
329
+ else:
330
+ self.trusted = False
331
+
332
+ def __repr__(self):
333
+ # type: () -> str
334
+ return ("<Origin component:%r archive:%r origin:%r label:%r "
335
+ "site:%r isTrusted:%r>") % (self.component, self.archive,
336
+ self.origin, self.label,
337
+ self.site, self.trusted)
338
+
339
+
340
+ class Record(Mapping[Any, Any]):
341
+ """Record in a Packages file
342
+
343
+ Represent a record as stored in a Packages file. You can use this like
344
+ a dictionary mapping the field names of the record to their values::
345
+
346
+ >>> record = Record("Package: python-apt\\nVersion: 0.8.0\\n\\n")
347
+ >>> record["Package"]
348
+ 'python-apt'
349
+ >>> record["Version"]
350
+ '0.8.0'
351
+
352
+ For example, to get the tasks of a package from a cache, you could do::
353
+
354
+ package.candidate.record["Tasks"].split()
355
+
356
+ Of course, you can also use the :attr:`Version.tasks` property.
357
+
358
+ """
359
+
360
+ def __init__(self, record_str):
361
+ # type: (str) -> None
362
+ self._rec = apt_pkg.TagSection(record_str)
363
+
364
+ def __hash__(self):
365
+ # type: () -> int
366
+ return hash(self._rec)
367
+
368
+ def __str__(self):
369
+ # type: () -> str
370
+ return str(self._rec)
371
+
372
+ def __getitem__(self, key):
373
+ # type: (str) -> str
374
+ return self._rec[key]
375
+
376
+ def __contains__(self, key):
377
+ # type: (object) -> bool
378
+ return key in self._rec
379
+
380
+ def __iter__(self):
381
+ # type: () -> Iterator[str]
382
+ return iter(self._rec.keys())
383
+
384
+ def iteritems(self):
385
+ # type: () -> Iterable[Tuple[object, str]]
386
+ """An iterator over the (key, value) items of the record."""
387
+ for key in self._rec.keys():
388
+ yield key, self._rec[key]
389
+
390
+ def get(self, key, default=None):
391
+ # type: (str, object) -> object
392
+ """Return record[key] if key in record, else *default*.
393
+
394
+ The parameter *default* must be either a string or None.
395
+ """
396
+ return self._rec.get(key, default)
397
+
398
+ def has_key(self, key):
399
+ # type: (str) -> bool
400
+ """deprecated form of ``key in x``."""
401
+ return key in self._rec
402
+
403
+ def __len__(self):
404
+ # type: () -> int
405
+ return len(self._rec)
406
+
407
+
408
+ class Version(object):
409
+ """Representation of a package version.
410
+
411
+ The Version class contains all information related to a
412
+ specific package version.
413
+
414
+ .. versionadded:: 0.7.9
415
+ """
416
+
417
+ def __init__(self, package, cand):
418
+ # type: (Package, apt_pkg.Version) -> None
419
+ self.package = package
420
+ self._cand = cand
421
+ self.package._pcache._weakversions.add(self)
422
+
423
+ def _cmp(self, other):
424
+ # type: (Any) -> Union[int, Any]
425
+ """Compares against another apt.Version object or a version string.
426
+
427
+ This method behaves like Python 2's cmp builtin and returns an integer
428
+ according to the outcome. The return value is negative in case of
429
+ self < other, zero if self == other and positive if self > other.
430
+
431
+ The comparison includes the package name and architecture if other is
432
+ an apt.Version object. If other isn't an apt.Version object it'll be
433
+ assumed that other is a version string (without package name/arch).
434
+
435
+ .. versionchanged:: 1.0.0
436
+ """
437
+ # Assume that other is an apt.Version object.
438
+ try:
439
+ self_name = self.package.fullname
440
+ other_name = other.package.fullname
441
+ if self_name < other_name:
442
+ return -1
443
+ elif self_name > other_name:
444
+ return 1
445
+ return apt_pkg.version_compare(self._cand.ver_str, other.version)
446
+ except AttributeError:
447
+ # Assume that other is a string that only contains the version.
448
+ try:
449
+ return apt_pkg.version_compare(self._cand.ver_str, other)
450
+ except TypeError:
451
+ return NotImplemented
452
+
453
+ def __eq__(self, other):
454
+ # type: (object) -> bool
455
+ return self._cmp(other) == 0
456
+
457
+ def __ge__(self, other):
458
+ # type: (Version) -> bool
459
+ return self._cmp(other) >= 0
460
+
461
+ def __gt__(self, other):
462
+ # type: (Version) -> bool
463
+ return self._cmp(other) > 0
464
+
465
+ def __le__(self, other):
466
+ # type: (Version) -> bool
467
+ return self._cmp(other) <= 0
468
+
469
+ def __lt__(self, other):
470
+ # type: (Version) -> bool
471
+ return self._cmp(other) < 0
472
+
473
+ def __ne__(self, other):
474
+ # type: (object) -> Union[bool, Any]
475
+ try:
476
+ return self._cmp(other) != 0
477
+ except TypeError:
478
+ return NotImplemented
479
+
480
+ def __hash__(self):
481
+ # type: () -> int
482
+ return self._cand.hash
483
+
484
+ def __str__(self):
485
+ # type: () -> str
486
+ return '%s=%s' % (self.package.name, self.version)
487
+
488
+ def __repr__(self):
489
+ # type: () -> str
490
+ return '<Version: package:%r version:%r>' % (self.package.name,
491
+ self.version)
492
+
493
+ @property
494
+ def _records(self):
495
+ # type: () -> apt_pkg.PackageRecords
496
+ """Internal helper that moves the Records to the right position."""
497
+ # If changing lookup, change fetch_binary() as well
498
+ if not self.package._pcache._records.lookup(self._cand.file_list[0]):
499
+ raise LookupError("Could not lookup record")
500
+
501
+ return self.package._pcache._records
502
+
503
+ @property
504
+ def _translated_records(self):
505
+ # type: () -> Optional[apt_pkg.PackageRecords]
506
+ """Internal helper to get the translated description."""
507
+ desc_iter = self._cand.translated_description
508
+ if self.package._pcache._records.lookup(desc_iter.file_list.pop(0)):
509
+ return self.package._pcache._records
510
+ return None
511
+
512
+ @property
513
+ def installed_size(self):
514
+ # type: () -> int
515
+ """Return the size of the package when installed."""
516
+ return self._cand.installed_size
517
+
518
+ @property
519
+ def homepage(self):
520
+ # type: () -> str
521
+ """Return the homepage for the package."""
522
+ return self._records.homepage
523
+
524
+ @property
525
+ def size(self):
526
+ # type: () -> int
527
+ """Return the size of the package."""
528
+ return self._cand.size
529
+
530
+ @property
531
+ def architecture(self):
532
+ # type: () -> str
533
+ """Return the architecture of the package version."""
534
+ return self._cand.arch
535
+
536
+ @property
537
+ def downloadable(self):
538
+ # type: () -> bool
539
+ """Return whether the version of the package is downloadable."""
540
+ return bool(self._cand.downloadable)
541
+
542
+ @property
543
+ def is_installed(self):
544
+ # type: () -> bool
545
+ """Return wether this version of the package is currently installed.
546
+
547
+ .. versionadded:: 1.0.0
548
+ """
549
+ inst_ver = self.package.installed
550
+ return (inst_ver is not None and inst_ver._cand.id == self._cand.id)
551
+
552
+ @property
553
+ def version(self):
554
+ # type: () -> str
555
+ """Return the version as a string."""
556
+ return self._cand.ver_str
557
+
558
+ @property
559
+ def summary(self):
560
+ # type: () -> Optional[str]
561
+ """Return the short description (one line summary)."""
562
+ records = self._translated_records
563
+ return records.short_desc if records is not None else None
564
+
565
+ @property
566
+ def raw_description(self):
567
+ # type: () -> str
568
+ """return the long description (raw)."""
569
+ return self._records.long_desc
570
+
571
+ @property
572
+ def section(self):
573
+ # type: () -> str
574
+ """Return the section of the package."""
575
+ return self._cand.section
576
+
577
+ @property
578
+ def description(self):
579
+ # type: () -> str
580
+ """Return the formatted long description.
581
+
582
+ Return the formatted long description according to the Debian policy
583
+ (Chapter 5.6.13).
584
+ See http://www.debian.org/doc/debian-policy/ch-controlfields.html
585
+ for more information.
586
+ """
587
+ desc = ''
588
+ records = self._translated_records
589
+ dsc = records.long_desc if records is not None else None
590
+
591
+ if not dsc:
592
+ return _("Missing description for '%s'."
593
+ "Please report.") % (self.package.name)
594
+
595
+ try:
596
+ if not isinstance(dsc, str):
597
+ # Only convert where needed (i.e. Python 2.X)
598
+ dsc = dsc.decode("utf-8")
599
+ except UnicodeDecodeError as err:
600
+ return _("Invalid unicode in description for '%s' (%s). "
601
+ "Please report.") % (self.package.name, err)
602
+
603
+ lines = iter(dsc.split("\n"))
604
+ # Skip the first line, since its a duplication of the summary
605
+ next(lines)
606
+ for raw_line in lines:
607
+ if raw_line.strip() == ".":
608
+ # The line is just line break
609
+ if not desc.endswith("\n"):
610
+ desc += "\n\n"
611
+ continue
612
+ if raw_line.startswith(" "):
613
+ # The line should be displayed verbatim without word wrapping
614
+ if not desc.endswith("\n"):
615
+ line = "\n%s\n" % raw_line[2:]
616
+ else:
617
+ line = "%s\n" % raw_line[2:]
618
+ elif raw_line.startswith(" "):
619
+ # The line is part of a paragraph.
620
+ if desc.endswith("\n") or desc == "":
621
+ # Skip the leading white space
622
+ line = raw_line[1:]
623
+ else:
624
+ line = raw_line
625
+ else:
626
+ line = raw_line
627
+ # Add current line to the description
628
+ desc += line
629
+ return desc
630
+
631
+ @property
632
+ def source_name(self):
633
+ # type: () -> str
634
+ """Return the name of the source package."""
635
+ try:
636
+ return self._records.source_pkg or self.package.shortname
637
+ except IndexError:
638
+ return self.package.shortname
639
+
640
+ @property
641
+ def source_version(self):
642
+ # type: () -> str
643
+ """Return the version of the source package."""
644
+ try:
645
+ return self._records.source_ver or self._cand.ver_str
646
+ except IndexError:
647
+ return self._cand.ver_str
648
+
649
+ @property
650
+ def priority(self):
651
+ # type: () -> str
652
+ """Return the priority of the package, as string."""
653
+ return self._cand.priority_str
654
+
655
+ @property
656
+ def policy_priority(self):
657
+ # type: () -> int
658
+ """Return the internal policy priority as a number.
659
+ See apt_preferences(5) for more information about what it means.
660
+ """
661
+ return self.package._pcache._depcache.policy.get_priority(self._cand)
662
+
663
+ @property
664
+ def record(self):
665
+ # type: () -> Record
666
+ """Return a Record() object for this version.
667
+
668
+ Return a Record() object for this version which provides access
669
+ to the raw attributes of the candidate version
670
+ """
671
+ return Record(self._records.record)
672
+
673
+ def get_dependencies(self, *types):
674
+ # type: (str) -> List[Dependency]
675
+ """Return a list of Dependency objects for the given types.
676
+
677
+ Multiple types can be specified. Possible types are:
678
+ 'Breaks', 'Conflicts', 'Depends', 'Enhances', 'PreDepends',
679
+ 'Recommends', 'Replaces', 'Suggests'
680
+
681
+ Additional types might be added in the future.
682
+ """
683
+ depends_list = []
684
+ depends = self._cand.depends_list
685
+ for type_ in types:
686
+ try:
687
+ for dep_ver_list in depends[type_]:
688
+ base_deps = []
689
+ for dep_or in dep_ver_list:
690
+ base_deps.append(BaseDependency(self, dep_or))
691
+ depends_list.append(Dependency(self, base_deps, type_))
692
+ except KeyError:
693
+ pass
694
+ return depends_list
695
+
696
+ @property
697
+ def provides(self):
698
+ # type: () -> List[str]
699
+ """ Return a list of names that this version provides."""
700
+ return [p[0] for p in self._cand.provides_list]
701
+
702
+ @property
703
+ def enhances(self):
704
+ # type: () -> List[Dependency]
705
+ """Return the list of enhances for the package version."""
706
+ return self.get_dependencies("Enhances")
707
+
708
+ @property
709
+ def dependencies(self):
710
+ # type: () -> List[Dependency]
711
+ """Return the dependencies of the package version."""
712
+ return self.get_dependencies("PreDepends", "Depends")
713
+
714
+ @property
715
+ def recommends(self):
716
+ # type: () -> List[Dependency]
717
+ """Return the recommends of the package version."""
718
+ return self.get_dependencies("Recommends")
719
+
720
+ @property
721
+ def suggests(self):
722
+ # type: () -> List[Dependency]
723
+ """Return the suggests of the package version."""
724
+ return self.get_dependencies("Suggests")
725
+
726
+ @property
727
+ def origins(self):
728
+ # type: () -> List[Origin]
729
+ """Return a list of origins for the package version."""
730
+ origins = []
731
+ for (packagefile, _unused) in self._cand.file_list:
732
+ origins.append(Origin(self.package, packagefile))
733
+ return origins
734
+
735
+ @property
736
+ def filename(self):
737
+ # type: () -> str
738
+ """Return the path to the file inside the archive.
739
+
740
+ .. versionadded:: 0.7.10
741
+ """
742
+ return self._records.filename
743
+
744
+ @property
745
+ def md5(self):
746
+ # type: () -> str
747
+ """Return the md5sum of the binary.
748
+
749
+ .. versionadded:: 0.7.10
750
+ """
751
+ return self._records.md5_hash
752
+
753
+ @property
754
+ def sha1(self):
755
+ # type: () -> str
756
+ """Return the sha1sum of the binary.
757
+
758
+ .. versionadded:: 0.7.10
759
+ """
760
+ return self._records.sha1_hash
761
+
762
+ @property
763
+ def sha256(self):
764
+ # type: () -> str
765
+ """Return the sha256sum of the binary.
766
+
767
+ .. versionadded:: 0.7.10
768
+ """
769
+ return self._records.sha256_hash
770
+
771
+ @property
772
+ def tasks(self):
773
+ # type: () -> Set[str]
774
+ """Get the tasks of the package.
775
+
776
+ A set of the names of the tasks this package belongs to.
777
+
778
+ .. versionadded:: 0.8.0
779
+ """
780
+ return set(self.record["Task"].split())
781
+
782
+ def _uris(self):
783
+ # type: () -> Iterator[str]
784
+ """Return an iterator over all available urls.
785
+
786
+ .. versionadded:: 0.7.10
787
+ """
788
+ for (packagefile, _unused) in self._cand.file_list:
789
+ indexfile = self.package._pcache._list.find_index(packagefile)
790
+ if indexfile:
791
+ yield indexfile.archive_uri(self._records.filename)
792
+
793
+ @property
794
+ def uris(self):
795
+ # type: () -> List[str]
796
+ """Return a list of all available uris for the binary.
797
+
798
+ .. versionadded:: 0.7.10
799
+ """
800
+ return list(self._uris())
801
+
802
+ @property
803
+ def uri(self):
804
+ # type: () -> Optional[str]
805
+ """Return a single URI for the binary.
806
+
807
+ .. versionadded:: 0.7.10
808
+ """
809
+ try:
810
+ return next(iter(self._uris()))
811
+ except StopIteration:
812
+ return None
813
+
814
+ def fetch_binary(self, destdir='', progress=None,
815
+ allow_unauthenticated=None):
816
+ # type: (str, Optional[AcquireProgress], Optional[bool]) -> str
817
+ """Fetch the binary version of the package.
818
+
819
+ The parameter *destdir* specifies the directory where the package will
820
+ be fetched to.
821
+
822
+ The parameter *progress* may refer to an apt_pkg.AcquireProgress()
823
+ object. If not specified or None, apt.progress.text.AcquireProgress()
824
+ is used.
825
+
826
+ The keyword-only parameter *allow_unauthenticated* specifies whether
827
+ to allow unauthenticated downloads. If not specified, it defaults to
828
+ the configuration option `APT::Get::AllowUnauthenticated`.
829
+
830
+ .. versionadded:: 0.7.10
831
+ """
832
+ if allow_unauthenticated is None:
833
+ allow_unauthenticated = apt_pkg.config.find_b("APT::Get::"
834
+ "AllowUnauthenticated", False)
835
+ base = os.path.basename(self._records.filename)
836
+ destfile = os.path.join(destdir, base)
837
+ if _file_is_same(destfile, self.size, self._records.hashes):
838
+ logging.debug('Ignoring already existing file: %s' % destfile)
839
+ return os.path.abspath(destfile)
840
+
841
+ # Verify that the index is actually trusted
842
+ pfile, offset = self._cand.file_list[0]
843
+ index = self.package._pcache._list.find_index(pfile)
844
+
845
+ if not (allow_unauthenticated or (index and index.is_trusted)):
846
+ raise UntrustedError("Could not fetch %s %s source package: "
847
+ "Source %r is not trusted" %
848
+ (self.package.name, self.version,
849
+ getattr(index, "describe", "<unkown>")))
850
+ if not self.uri:
851
+ raise ValueError("No URI for this binary.")
852
+ hashes = self._records.hashes
853
+ if not (allow_unauthenticated or hashes.usable):
854
+ raise UntrustedError("The item %r could not be fetched: "
855
+ "No trusted hash found." %
856
+ destfile)
857
+ acq = apt_pkg.Acquire(progress or apt.progress.text.AcquireProgress())
858
+ acqfile = apt_pkg.AcquireFile(acq, self.uri, hashes,
859
+ self.size, base, destfile=destfile)
860
+ acq.run()
861
+
862
+ if acqfile.status != acqfile.STAT_DONE:
863
+ raise FetchError("The item %r could not be fetched: %s" %
864
+ (acqfile.destfile, acqfile.error_text))
865
+
866
+ return os.path.abspath(destfile)
867
+
868
+ def fetch_source(self, destdir="", progress=None, unpack=True,
869
+ allow_unauthenticated=None):
870
+ # type: (str, Optional[AcquireProgress], bool, Optional[bool]) -> str
871
+ """Get the source code of a package.
872
+
873
+ The parameter *destdir* specifies the directory where the source will
874
+ be fetched to.
875
+
876
+ The parameter *progress* may refer to an apt_pkg.AcquireProgress()
877
+ object. If not specified or None, apt.progress.text.AcquireProgress()
878
+ is used.
879
+
880
+ The parameter *unpack* describes whether the source should be unpacked
881
+ (``True``) or not (``False``). By default, it is unpacked.
882
+
883
+ If *unpack* is ``True``, the path to the extracted directory is
884
+ returned. Otherwise, the path to the .dsc file is returned.
885
+
886
+ The keyword-only parameter *allow_unauthenticated* specifies whether
887
+ to allow unauthenticated downloads. If not specified, it defaults to
888
+ the configuration option `APT::Get::AllowUnauthenticated`.
889
+ """
890
+ if allow_unauthenticated is None:
891
+ allow_unauthenticated = apt_pkg.config.find_b("APT::Get::"
892
+ "AllowUnauthenticated", False)
893
+
894
+ src = apt_pkg.SourceRecords()
895
+ acq = apt_pkg.Acquire(progress or apt.progress.text.AcquireProgress())
896
+
897
+ dsc = None
898
+ record = self._records
899
+ source_name = record.source_pkg or self.package.shortname
900
+ source_version = record.source_ver or self._cand.ver_str
901
+ source_lookup = src.lookup(source_name)
902
+
903
+ while source_lookup and source_version != src.version:
904
+ source_lookup = src.lookup(source_name)
905
+ if not source_lookup:
906
+ raise ValueError("No source for %r" % self)
907
+ files = list()
908
+
909
+ if not (allow_unauthenticated or src.index.is_trusted):
910
+ raise UntrustedError("Could not fetch %s %s source package: "
911
+ "Source %r is not trusted" %
912
+ (self.package.name, self.version,
913
+ src.index.describe))
914
+ for fil in src.files:
915
+ base = os.path.basename(fil.path)
916
+ destfile = os.path.join(destdir, base)
917
+ if fil.type == 'dsc':
918
+ dsc = destfile
919
+ if _file_is_same(destfile, fil.size, fil.hashes):
920
+ logging.debug('Ignoring already existing file: %s' % destfile)
921
+ continue
922
+
923
+ if not (allow_unauthenticated or fil.hashes.usable):
924
+ raise UntrustedError("The item %r could not be fetched: "
925
+ "No trusted hash found." %
926
+ destfile)
927
+ files.append(apt_pkg.AcquireFile(acq,
928
+ src.index.archive_uri(fil.path),
929
+ fil.hashes, fil.size, base, destfile=destfile))
930
+ acq.run()
931
+
932
+ if dsc is None:
933
+ raise ValueError("No source for %r" % self)
934
+
935
+ for item in acq.items:
936
+ if item.status != item.STAT_DONE:
937
+ raise FetchError("The item %r could not be fetched: %s" %
938
+ (item.destfile, item.error_text))
939
+
940
+ if unpack:
941
+ outdir = src.package + '-' + apt_pkg.upstream_version(src.version)
942
+ outdir = os.path.join(destdir, outdir)
943
+ subprocess.check_call(["dpkg-source", "-x", dsc, outdir])
944
+ return os.path.abspath(outdir)
945
+ else:
946
+ return os.path.abspath(dsc)
947
+
948
+
949
+ class VersionList(Sequence[Version]):
950
+ """Provide a mapping & sequence interface to all versions of a package.
951
+
952
+ This class can be used like a dictionary, where version strings are the
953
+ keys. It can also be used as a sequence, where integers are the keys.
954
+
955
+ You can also convert this to a dictionary or a list, using the usual way
956
+ of dict(version_list) or list(version_list). This is useful if you need
957
+ to access the version objects multiple times, because they do not have to
958
+ be recreated this way.
959
+
960
+ Examples ('package.versions' being a version list):
961
+ '0.7.92' in package.versions # Check whether 0.7.92 is a valid version.
962
+ package.versions[0] # Return first version or raise IndexError
963
+ package.versions[0:2] # Return a new VersionList for objects 0-2
964
+ package.versions['0.7.92'] # Return version 0.7.92 or raise KeyError
965
+ package.versions.keys() # All keys, as strings.
966
+ max(package.versions)
967
+ """
968
+
969
+ def __init__(self, package, slice_=None):
970
+ # type: (Package, Optional[slice]) -> None
971
+ self._package = package # apt.package.Package()
972
+ self._versions = package._pkg.version_list # [apt_pkg.Version(), ...]
973
+ if slice_:
974
+ self._versions = self._versions[slice_]
975
+
976
+ def __getitem__(self, item):
977
+ # type: (Union[int, slice, str]) -> Any
978
+ # FIXME: Should not be returning Any, should have overloads; but
979
+ # pyflakes complains
980
+ if isinstance(item, slice):
981
+ return self.__class__(self._package, item)
982
+ try:
983
+ # Sequence interface, item is an integer
984
+ return Version(self._package, self._versions[item]) # type: ignore
985
+ except TypeError:
986
+ # Dictionary interface item is a string.
987
+ for ver in self._versions:
988
+ if ver.ver_str == item:
989
+ return Version(self._package, ver)
990
+ raise KeyError("Version: %r not found." % (item))
991
+
992
+ def __str__(self):
993
+ # type: () -> str
994
+ return '[%s]' % (', '.join(str(ver) for ver in self))
995
+
996
+ def __repr__(self):
997
+ # type: () -> str
998
+ return '<VersionList: %r>' % self.keys()
999
+
1000
+ def __iter__(self):
1001
+ # type: () -> Iterator[Version]
1002
+ """Return an iterator over all value objects."""
1003
+ return (Version(self._package, ver) for ver in self._versions)
1004
+
1005
+ def __contains__(self, item):
1006
+ # type: (object) -> bool
1007
+ if isinstance(item, Version): # Sequence interface
1008
+ item = item.version
1009
+ # Dictionary interface.
1010
+ for ver in self._versions:
1011
+ if ver.ver_str == item:
1012
+ return True
1013
+ return False
1014
+
1015
+ def __eq__(self, other):
1016
+ # type: (Any) -> bool
1017
+ return list(self) == list(other)
1018
+
1019
+ def __len__(self):
1020
+ # type: () -> int
1021
+ return len(self._versions)
1022
+
1023
+ # Mapping interface
1024
+
1025
+ def keys(self):
1026
+ # type: () -> List[str]
1027
+ """Return a list of all versions, as strings."""
1028
+ return [ver.ver_str for ver in self._versions]
1029
+
1030
+ def get(self, key, default=None):
1031
+ # type: (str, Optional[Version]) -> Optional[Version]
1032
+ """Return the key or the default."""
1033
+ try:
1034
+ return self[key] # type: ignore # FIXME: should be deterined automatically # noqa
1035
+ except LookupError:
1036
+ return default
1037
+
1038
+
1039
+ class Package(object):
1040
+ """Representation of a package in a cache.
1041
+
1042
+ This class provides methods and properties for working with a package. It
1043
+ lets you mark the package for installation, check if it is installed, and
1044
+ much more.
1045
+ """
1046
+
1047
+ def __init__(self, pcache, pkgiter):
1048
+ # type: (apt.Cache, apt_pkg.Package) -> None
1049
+ """ Init the Package object """
1050
+ self._pkg = pkgiter
1051
+ self._pcache = pcache # python cache in cache.py
1052
+ self._changelog = "" # Cached changelog
1053
+
1054
+ def __str__(self):
1055
+ # type: () -> str
1056
+ return self.name
1057
+
1058
+ def __repr__(self):
1059
+ # type: () -> str
1060
+ return '<Package: name:%r architecture=%r id:%r>' % (
1061
+ self._pkg.name, self._pkg.architecture, self._pkg.id)
1062
+
1063
+ def __lt__(self, other):
1064
+ # type: (Package) -> bool
1065
+ return self.name < other.name
1066
+
1067
+ @property
1068
+ def candidate(self):
1069
+ # type: () -> Optional[Version]
1070
+ """Return the candidate version of the package.
1071
+
1072
+ This property is writeable to allow you to set the candidate version
1073
+ of the package. Just assign a Version() object, and it will be set as
1074
+ the candidate version.
1075
+ """
1076
+ cand = self._pcache._depcache.get_candidate_ver(self._pkg)
1077
+ if cand is not None:
1078
+ return Version(self, cand)
1079
+ return None
1080
+
1081
+ @candidate.setter
1082
+ def candidate(self, version):
1083
+ # type: (Version) -> None
1084
+ """Set the candidate version of the package."""
1085
+ self._pcache.cache_pre_change()
1086
+ self._pcache._depcache.set_candidate_ver(self._pkg, version._cand)
1087
+ self._pcache.cache_post_change()
1088
+
1089
+ @property
1090
+ def installed(self):
1091
+ # type: () -> Optional[Version]
1092
+ """Return the currently installed version of the package.
1093
+
1094
+ .. versionadded:: 0.7.9
1095
+ """
1096
+ if self._pkg.current_ver is not None:
1097
+ return Version(self, self._pkg.current_ver)
1098
+ return None
1099
+
1100
+ @property
1101
+ def name(self):
1102
+ # type: () -> str
1103
+ """Return the name of the package, possibly including architecture.
1104
+
1105
+ If the package is not part of the system's preferred architecture,
1106
+ return the same as :attr:`fullname`, otherwise return the same
1107
+ as :attr:`shortname`
1108
+
1109
+ .. versionchanged:: 0.7.100.3
1110
+
1111
+ As part of multi-arch, this field now may include architecture
1112
+ information.
1113
+ """
1114
+ return self._pkg.get_fullname(True)
1115
+
1116
+ @property
1117
+ def fullname(self):
1118
+ # type: () -> str
1119
+ """Return the name of the package, including architecture.
1120
+
1121
+ Note that as for :meth:`architecture`, this returns the
1122
+ native architecture for Architecture: all packages.
1123
+
1124
+ .. versionadded:: 0.7.100.3"""
1125
+ return self._pkg.get_fullname(False)
1126
+
1127
+ @property
1128
+ def shortname(self):
1129
+ # type: () -> str
1130
+ """Return the name of the package, without architecture.
1131
+
1132
+ .. versionadded:: 0.7.100.3"""
1133
+ return self._pkg.name
1134
+
1135
+ @property
1136
+ def id(self):
1137
+ # type: () -> int
1138
+ """Return a uniq ID for the package.
1139
+
1140
+ This can be used eg. to store additional information about the pkg."""
1141
+ return self._pkg.id
1142
+
1143
+ @property
1144
+ def essential(self):
1145
+ # type: () -> bool
1146
+ """Return True if the package is an essential part of the system."""
1147
+ return self._pkg.essential
1148
+
1149
+ def architecture(self):
1150
+ # type: () -> str
1151
+ """Return the Architecture of the package.
1152
+
1153
+ Note that for Architecture: all packages, this returns the
1154
+ native architecture, as they are internally treated like native
1155
+ packages. To get the concrete architecture, look at the
1156
+ :attr:`Version.architecture` attribute.
1157
+
1158
+ .. versionchanged:: 0.7.100.3
1159
+ This is now the package's architecture in the multi-arch sense,
1160
+ previously it was the architecture of the candidate version
1161
+ and deprecated.
1162
+ """
1163
+ return self._pkg.architecture
1164
+
1165
+ # depcache states
1166
+
1167
+ @property
1168
+ def marked_install(self):
1169
+ # type: () -> bool
1170
+ """Return ``True`` if the package is marked for install."""
1171
+ return self._pcache._depcache.marked_install(self._pkg)
1172
+
1173
+ @property
1174
+ def marked_upgrade(self):
1175
+ # type: () -> bool
1176
+ """Return ``True`` if the package is marked for upgrade."""
1177
+ return self._pcache._depcache.marked_upgrade(self._pkg)
1178
+
1179
+ @property
1180
+ def marked_delete(self):
1181
+ # type: () -> bool
1182
+ """Return ``True`` if the package is marked for delete."""
1183
+ return self._pcache._depcache.marked_delete(self._pkg)
1184
+
1185
+ @property
1186
+ def marked_keep(self):
1187
+ # type: () -> bool
1188
+ """Return ``True`` if the package is marked for keep."""
1189
+ return self._pcache._depcache.marked_keep(self._pkg)
1190
+
1191
+ @property
1192
+ def marked_downgrade(self):
1193
+ # type: () -> bool
1194
+ """ Package is marked for downgrade """
1195
+ return self._pcache._depcache.marked_downgrade(self._pkg)
1196
+
1197
+ @property
1198
+ def marked_reinstall(self):
1199
+ # type: () -> bool
1200
+ """Return ``True`` if the package is marked for reinstall."""
1201
+ return self._pcache._depcache.marked_reinstall(self._pkg)
1202
+
1203
+ @property
1204
+ def is_installed(self):
1205
+ # type: () -> bool
1206
+ """Return ``True`` if the package is installed."""
1207
+ return (self._pkg.current_ver is not None)
1208
+
1209
+ @property
1210
+ def is_upgradable(self):
1211
+ # type: () -> bool
1212
+ """Return ``True`` if the package is upgradable."""
1213
+ return (self.is_installed and
1214
+ self._pcache._depcache.is_upgradable(self._pkg))
1215
+
1216
+ @property
1217
+ def is_auto_removable(self):
1218
+ # type: () -> bool
1219
+ """Return ``True`` if the package is no longer required.
1220
+
1221
+ If the package has been installed automatically as a dependency of
1222
+ another package, and if no packages depend on it anymore, the package
1223
+ is no longer required.
1224
+ """
1225
+ return ((self.is_installed or self.marked_install) and
1226
+ self._pcache._depcache.is_garbage(self._pkg))
1227
+
1228
+ @property
1229
+ def is_auto_installed(self):
1230
+ # type: () -> bool
1231
+ """Return whether the package is marked as automatically installed."""
1232
+ return self._pcache._depcache.is_auto_installed(self._pkg)
1233
+ # sizes
1234
+
1235
+ @property
1236
+ def installed_files(self):
1237
+ # type: () -> List[str]
1238
+ """Return a list of files installed by the package.
1239
+
1240
+ Return a list of unicode names of the files which have
1241
+ been installed by this package
1242
+ """
1243
+ for name in self.name, self.fullname:
1244
+ path = "/var/lib/dpkg/info/%s.list" % name
1245
+ try:
1246
+ with open(path, "rb") as file_list:
1247
+ return file_list.read().decode("utf-8").split(u"\n")
1248
+ except EnvironmentError:
1249
+ continue
1250
+
1251
+ return []
1252
+
1253
+ def get_changelog(self, uri=None, cancel_lock=None):
1254
+ # type: (Optional[str], Optional[threading.Event]) -> str
1255
+ """
1256
+ Download the changelog of the package and return it as unicode
1257
+ string.
1258
+
1259
+ The parameter *uri* refers to the uri of the changelog file. It may
1260
+ contain multiple named variables which will be substitued. These
1261
+ variables are (src_section, prefix, src_pkg, src_ver). An example is
1262
+ the Ubuntu changelog::
1263
+
1264
+ "http://changelogs.ubuntu.com/changelogs/pool" \\
1265
+ "/%(src_section)s/%(prefix)s/%(src_pkg)s" \\
1266
+ "/%(src_pkg)s_%(src_ver)s/changelog"
1267
+
1268
+ The parameter *cancel_lock* refers to an instance of threading.Event,
1269
+ which if set, prevents the download.
1270
+ """
1271
+ # Return a cached changelog if available
1272
+ if self._changelog != u"":
1273
+ return self._changelog
1274
+
1275
+ if not self.candidate:
1276
+ return _("The list of changes is not available")
1277
+
1278
+ if uri is None:
1279
+ if self.candidate.origins[0].origin == "Debian":
1280
+ uri = "http://packages.debian.org/changelogs/pool" \
1281
+ "/%(src_section)s/%(prefix)s/%(src_pkg)s" \
1282
+ "/%(src_pkg)s_%(src_ver)s/changelog"
1283
+ elif self.candidate.origins[0].origin == "Ubuntu":
1284
+ uri = "http://changelogs.ubuntu.com/changelogs/pool" \
1285
+ "/%(src_section)s/%(prefix)s/%(src_pkg)s" \
1286
+ "/%(src_pkg)s_%(src_ver)s/changelog"
1287
+ else:
1288
+ res = _("The list of changes is not available")
1289
+ if isinstance(res, str):
1290
+ return res
1291
+ else:
1292
+ return res.decode("utf-8")
1293
+
1294
+ # get the src package name
1295
+ src_pkg = self.candidate.source_name
1296
+
1297
+ # assume "main" section
1298
+ src_section = "main"
1299
+ # use the section of the candidate as a starting point
1300
+ section = self.candidate.section
1301
+
1302
+ # get the source version
1303
+ src_ver = self.candidate.source_version
1304
+
1305
+ try:
1306
+ # try to get the source version of the pkg, this differs
1307
+ # for some (e.g. libnspr4 on ubuntu)
1308
+ # this feature only works if the correct deb-src are in the
1309
+ # sources.list otherwise we fall back to the binary version number
1310
+ src_records = apt_pkg.SourceRecords()
1311
+ except SystemError:
1312
+ pass
1313
+ else:
1314
+ while src_records.lookup(src_pkg):
1315
+ if not src_records.version:
1316
+ continue
1317
+ if self.candidate.source_version == src_records.version:
1318
+ # Direct match, use it and do not do more lookups.
1319
+ src_ver = src_records.version
1320
+ section = src_records.section
1321
+ break
1322
+ if apt_pkg.version_compare(src_records.version, src_ver) > 0:
1323
+ # The version is higher, it seems to match.
1324
+ src_ver = src_records.version
1325
+ section = src_records.section
1326
+
1327
+ section_split = section.split("/", 1)
1328
+ if len(section_split) > 1:
1329
+ src_section = section_split[0]
1330
+ del section_split
1331
+
1332
+ # lib is handled special
1333
+ prefix = src_pkg[0]
1334
+ if src_pkg.startswith("lib"):
1335
+ prefix = "lib" + src_pkg[3]
1336
+
1337
+ # stip epoch
1338
+ src_ver_split = src_ver.split(":", 1)
1339
+ if len(src_ver_split) > 1:
1340
+ src_ver = "".join(src_ver_split[1:])
1341
+ del src_ver_split
1342
+
1343
+ uri = uri % {"src_section": src_section,
1344
+ "prefix": prefix,
1345
+ "src_pkg": src_pkg,
1346
+ "src_ver": src_ver}
1347
+
1348
+ timeout = socket.getdefaulttimeout()
1349
+
1350
+ # FIXME: when python2.4 vanishes from the archive,
1351
+ # merge this into a single try..finally block (pep 341)
1352
+ try:
1353
+ try:
1354
+ # Set a timeout for the changelog download
1355
+ socket.setdefaulttimeout(2)
1356
+
1357
+ # Check if the download was canceled
1358
+ if cancel_lock and cancel_lock.is_set():
1359
+ return u""
1360
+ # FIXME: python3.2: Should be closed manually
1361
+ changelog_file = urlopen(uri)
1362
+ # do only get the lines that are new
1363
+ changelog = u""
1364
+ regexp = "^%s \\((.*)\\)(.*)$" % (re.escape(src_pkg))
1365
+ while True:
1366
+ # Check if the download was canceled
1367
+ if cancel_lock and cancel_lock.is_set():
1368
+ return u""
1369
+ # Read changelog line by line
1370
+ line_raw = changelog_file.readline()
1371
+ if not line_raw:
1372
+ break
1373
+ # The changelog is encoded in utf-8, but since there isn't
1374
+ # any http header, urllib2 seems to treat it as ascii
1375
+ line = line_raw.decode("utf-8")
1376
+
1377
+ #print line.encode('utf-8')
1378
+ match = re.match(regexp, line)
1379
+ if match:
1380
+ # strip epoch from installed version
1381
+ # and from changelog too
1382
+ installed = getattr(self.installed, 'version', None)
1383
+ if installed and ":" in installed:
1384
+ installed = installed.split(":", 1)[1]
1385
+ changelog_ver = match.group(1)
1386
+ if changelog_ver and ":" in changelog_ver:
1387
+ changelog_ver = changelog_ver.split(":", 1)[1]
1388
+
1389
+ if (installed and apt_pkg.version_compare(
1390
+ changelog_ver, installed) <= 0):
1391
+ break
1392
+ # EOF (shouldn't really happen)
1393
+ changelog += line
1394
+
1395
+ # Print an error if we failed to extract a changelog
1396
+ if len(changelog) == 0:
1397
+ changelog = _("The list of changes is not available")
1398
+ if not isinstance(changelog, str):
1399
+ changelog = changelog.decode("utf-8")
1400
+ self._changelog = changelog
1401
+
1402
+ except HTTPError:
1403
+ if self.candidate.origins[0].origin == "Ubuntu":
1404
+ res = _("The list of changes is not available yet.\n\n"
1405
+ "Please use "
1406
+ "http://launchpad.net/ubuntu/+source/%s/"
1407
+ "%s/+changelog\n"
1408
+ "until the changes become available or try again "
1409
+ "later.") % (src_pkg, src_ver)
1410
+ else:
1411
+ res = _("The list of changes is not available")
1412
+ if isinstance(res, str):
1413
+ return res
1414
+ else:
1415
+ return res.decode("utf-8")
1416
+ except (IOError, BadStatusLine):
1417
+ res = _("Failed to download the list of changes. \nPlease "
1418
+ "check your Internet connection.")
1419
+ if isinstance(res, str):
1420
+ return res
1421
+ else:
1422
+ return res.decode("utf-8")
1423
+ finally:
1424
+ socket.setdefaulttimeout(timeout)
1425
+ return self._changelog
1426
+
1427
+ @property
1428
+ def versions(self):
1429
+ # type: () -> VersionList
1430
+ """Return a VersionList() object for all available versions.
1431
+
1432
+ .. versionadded:: 0.7.9
1433
+ """
1434
+ return VersionList(self)
1435
+
1436
+ @property
1437
+ def is_inst_broken(self):
1438
+ # type: () -> bool
1439
+ """Return True if the to-be-installed package is broken."""
1440
+ return self._pcache._depcache.is_inst_broken(self._pkg)
1441
+
1442
+ @property
1443
+ def is_now_broken(self):
1444
+ # type: () -> bool
1445
+ """Return True if the installed package is broken."""
1446
+ return self._pcache._depcache.is_now_broken(self._pkg)
1447
+
1448
+ @property
1449
+ def has_config_files(self):
1450
+ # type: () -> bool
1451
+ """Checks whether the package is is the config-files state."""
1452
+ return self. _pkg.current_state == apt_pkg.CURSTATE_CONFIG_FILES
1453
+
1454
+ # depcache actions
1455
+
1456
+ def mark_keep(self):
1457
+ # type: () -> None
1458
+ """Mark a package for keep."""
1459
+ self._pcache.cache_pre_change()
1460
+ self._pcache._depcache.mark_keep(self._pkg)
1461
+ self._pcache.cache_post_change()
1462
+
1463
+ def mark_delete(self, auto_fix=True, purge=False):
1464
+ # type: (bool, bool) -> None
1465
+ """Mark a package for deletion.
1466
+
1467
+ If *auto_fix* is ``True``, the resolver will be run, trying to fix
1468
+ broken packages. This is the default.
1469
+
1470
+ If *purge* is ``True``, remove the configuration files of the package
1471
+ as well. The default is to keep the configuration.
1472
+ """
1473
+ self._pcache.cache_pre_change()
1474
+ self._pcache._depcache.mark_delete(self._pkg, purge)
1475
+ # try to fix broken stuffsta
1476
+ if auto_fix and self._pcache._depcache.broken_count > 0:
1477
+ fix = apt_pkg.ProblemResolver(self._pcache._depcache)
1478
+ fix.clear(self._pkg)
1479
+ fix.protect(self._pkg)
1480
+ fix.remove(self._pkg)
1481
+ fix.resolve()
1482
+ self._pcache.cache_post_change()
1483
+
1484
+ def mark_install(self, auto_fix=True, auto_inst=True, from_user=True):
1485
+ # type: (bool, bool, bool) -> None
1486
+ """Mark a package for install.
1487
+
1488
+ If *autoFix* is ``True``, the resolver will be run, trying to fix
1489
+ broken packages. This is the default.
1490
+
1491
+ If *autoInst* is ``True``, the dependencies of the packages will be
1492
+ installed automatically. This is the default.
1493
+
1494
+ If *fromUser* is ``True``, this package will not be marked as
1495
+ automatically installed. This is the default. Set it to False if you
1496
+ want to be able to automatically remove the package at a later stage
1497
+ when no other package depends on it.
1498
+ """
1499
+ self._pcache.cache_pre_change()
1500
+ self._pcache._depcache.mark_install(self._pkg, auto_inst, from_user)
1501
+ # try to fix broken stuff
1502
+ if auto_fix and self._pcache._depcache.broken_count > 0:
1503
+ fixer = apt_pkg.ProblemResolver(self._pcache._depcache)
1504
+ fixer.clear(self._pkg)
1505
+ fixer.protect(self._pkg)
1506
+ fixer.resolve(True)
1507
+ self._pcache.cache_post_change()
1508
+
1509
+ def mark_upgrade(self, from_user=True):
1510
+ # type: (bool) -> None
1511
+ """Mark a package for upgrade."""
1512
+ if self.is_upgradable:
1513
+ auto = self.is_auto_installed
1514
+ self.mark_install(from_user=from_user)
1515
+ self.mark_auto(auto)
1516
+ else:
1517
+ # FIXME: we may want to throw a exception here
1518
+ sys.stderr.write(("MarkUpgrade() called on a non-upgradeable pkg: "
1519
+ "'%s'\n") % self._pkg.name)
1520
+
1521
+ def mark_auto(self, auto=True):
1522
+ # type: (bool) -> None
1523
+ """Mark a package as automatically installed.
1524
+
1525
+ Call this function to mark a package as automatically installed. If the
1526
+ optional parameter *auto* is set to ``False``, the package will not be
1527
+ marked as automatically installed anymore. The default is ``True``.
1528
+ """
1529
+ self._pcache._depcache.mark_auto(self._pkg, auto)
1530
+
1531
+ def commit(self, fprogress, iprogress):
1532
+ # type: (AcquireProgress, InstallProgress) -> None
1533
+ """Commit the changes.
1534
+
1535
+ The parameter *fprogress* refers to a apt_pkg.AcquireProgress() object,
1536
+ like apt.progress.text.AcquireProgress().
1537
+
1538
+ The parameter *iprogress* refers to an InstallProgress() object, as
1539
+ found in apt.progress.base.
1540
+ """
1541
+ self._pcache._depcache.commit(fprogress, iprogress)
1542
+
1543
+
1544
+ @no_type_check
1545
+ def _test():
1546
+ """Self-test."""
1547
+ print("Self-test for the Package modul")
1548
+ import random
1549
+ apt_pkg.init()
1550
+ progress = apt.progress.text.OpProgress()
1551
+ cache = apt.Cache(progress)
1552
+ pkg = cache["apt-utils"]
1553
+ print("Name: %s " % pkg.name)
1554
+ print("ID: %s " % pkg.id)
1555
+ print("Priority (Candidate): %s " % pkg.candidate.priority)
1556
+ print("Priority (Installed): %s " % pkg.installed.priority)
1557
+ print("Installed: %s " % pkg.installed.version)
1558
+ print("Candidate: %s " % pkg.candidate.version)
1559
+ print("CandidateDownloadable: %s" % pkg.candidate.downloadable)
1560
+ print("CandidateOrigins: %s" % pkg.candidate.origins)
1561
+ print("SourcePkg: %s " % pkg.candidate.source_name)
1562
+ print("Section: %s " % pkg.section)
1563
+ print("Summary: %s" % pkg.candidate.summary)
1564
+ print("Description (formatted) :\n%s" % pkg.candidate.description)
1565
+ print("Description (unformatted):\n%s" % pkg.candidate.raw_description)
1566
+ print("InstalledSize: %s " % pkg.candidate.installed_size)
1567
+ print("PackageSize: %s " % pkg.candidate.size)
1568
+ print("Dependencies: %s" % pkg.installed.dependencies)
1569
+ print("Recommends: %s" % pkg.installed.recommends)
1570
+ for dep in pkg.candidate.dependencies:
1571
+ print(",".join("%s (%s) (%s) (%s)" % (o.name, o.version, o.relation,
1572
+ o.pre_depend) for o in dep.or_dependencies))
1573
+ print("arch: %s" % pkg.candidate.architecture)
1574
+ print("homepage: %s" % pkg.candidate.homepage)
1575
+ print("rec: ", pkg.candidate.record)
1576
+
1577
+ print(cache["2vcard"].get_changelog())
1578
+ for i in True, False:
1579
+ print("Running install on random upgradable pkgs with AutoFix: ", i)
1580
+ for pkg in cache:
1581
+ if pkg.is_upgradable:
1582
+ if random.randint(0, 1) == 1:
1583
+ pkg.mark_install(i)
1584
+ print("Broken: %s " % cache._depcache.broken_count)
1585
+ print("InstCount: %s " % cache._depcache.inst_count)
1586
+
1587
+ print()
1588
+ # get a new cache
1589
+ for i in True, False:
1590
+ print("Randomly remove some packages with AutoFix: %s" % i)
1591
+ cache = apt.Cache(progress)
1592
+ for name in cache.keys():
1593
+ if random.randint(0, 1) == 1:
1594
+ try:
1595
+ cache[name].mark_delete(i)
1596
+ except SystemError:
1597
+ print("Error trying to remove: %s " % name)
1598
+ print("Broken: %s " % cache._depcache.broken_count)
1599
+ print("DelCount: %s " % cache._depcache.del_count)
1600
+
1601
+
1602
+ # self-test
1603
+ if __name__ == "__main__":
1604
+ _test()
apt/progress/__init__.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # apt/progress/__init__.py - Initialization file for apt.progress.
2
+ #
3
+ # Copyright (c) 2009 Julian Andres Klode <[email protected]>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License as
7
+ # published by the Free Software Foundation; either version 2 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
+ # USA
19
+ """Progress reporting.
20
+
21
+ This package provides progress reporting for the python-apt package. The module
22
+ 'base' provides classes with no output, and the module 'text' provides classes
23
+ for terminals, etc.
24
+ """
25
+
26
+ from __future__ import print_function
27
+
28
+ from typing import Sequence
29
+
30
+
31
+ __all__ = [] # type: Sequence[str]
apt/progress/base.py ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # apt/progress/base.py - Base classes for progress reporting.
2
+ #
3
+ # Copyright (C) 2009 Julian Andres Klode <[email protected]>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License as
7
+ # published by the Free Software Foundation; either version 2 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
+ # USA
19
+ # pylint: disable-msg = R0201
20
+ """Base classes for progress reporting.
21
+
22
+ Custom progress classes should inherit from these classes. They can also be
23
+ used as dummy progress classes which simply do nothing.
24
+ """
25
+ from __future__ import print_function
26
+
27
+ import errno
28
+ import fcntl
29
+ import io
30
+ import os
31
+ import re
32
+ import select
33
+ import sys
34
+
35
+ from typing import Optional, Union
36
+
37
+ import apt_pkg
38
+
39
+ __all__ = ['AcquireProgress', 'CdromProgress', 'InstallProgress', 'OpProgress']
40
+
41
+
42
+ class AcquireProgress(object):
43
+ """Monitor object for downloads controlled by the Acquire class.
44
+
45
+ This is an mostly abstract class. You should subclass it and implement the
46
+ methods to get something useful.
47
+ """
48
+
49
+ current_bytes = current_cps = fetched_bytes = last_bytes = total_bytes \
50
+ = 0.0
51
+ current_items = elapsed_time = total_items = 0
52
+
53
+ def done(self, item):
54
+ # type: (apt_pkg.AcquireItemDesc) -> None
55
+ """Invoked when an item is successfully and completely fetched."""
56
+
57
+ def fail(self, item):
58
+ # type: (apt_pkg.AcquireItemDesc) -> None
59
+ """Invoked when an item could not be fetched."""
60
+
61
+ def fetch(self, item):
62
+ # type: (apt_pkg.AcquireItemDesc) -> None
63
+ """Invoked when some of the item's data is fetched."""
64
+
65
+ def ims_hit(self, item):
66
+ # type: (apt_pkg.AcquireItemDesc) -> None
67
+ """Invoked when an item is confirmed to be up-to-date.
68
+
69
+ Invoked when an item is confirmed to be up-to-date. For instance,
70
+ when an HTTP download is informed that the file on the server was
71
+ not modified.
72
+ """
73
+
74
+ def media_change(self, media, drive):
75
+ # type: (str, str) -> bool
76
+ """Prompt the user to change the inserted removable media.
77
+
78
+ The parameter 'media' decribes the name of the media type that
79
+ should be changed, whereas the parameter 'drive' should be the
80
+ identifying name of the drive whose media should be changed.
81
+
82
+ This method should not return until the user has confirmed to the user
83
+ interface that the media change is complete. It must return True if
84
+ the user confirms the media change, or False to cancel it.
85
+ """
86
+ return False
87
+
88
+ def pulse(self, owner):
89
+ # type: (apt_pkg.Acquire) -> bool
90
+ """Periodically invoked while the Acquire process is underway.
91
+
92
+ This method gets invoked while the Acquire progress given by the
93
+ parameter 'owner' is underway. It should display information about
94
+ the current state.
95
+
96
+ This function returns a boolean value indicating whether the
97
+ acquisition should be continued (True) or cancelled (False).
98
+ """
99
+ return True
100
+
101
+ def start(self):
102
+ # type: () -> None
103
+ """Invoked when the Acquire process starts running."""
104
+ # Reset all our values.
105
+ self.current_bytes = 0.0
106
+ self.current_cps = 0.0
107
+ self.current_items = 0
108
+ self.elapsed_time = 0
109
+ self.fetched_bytes = 0.0
110
+ self.last_bytes = 0.0
111
+ self.total_bytes = 0.0
112
+ self.total_items = 0
113
+
114
+ def stop(self):
115
+ # type: () -> None
116
+ """Invoked when the Acquire process stops running."""
117
+
118
+
119
+ class CdromProgress(object):
120
+ """Base class for reporting the progress of adding a cdrom.
121
+
122
+ Can be used with apt_pkg.Cdrom to produce an utility like apt-cdrom. The
123
+ attribute 'total_steps' defines the total number of steps and can be used
124
+ in update() to display the current progress.
125
+ """
126
+
127
+ total_steps = 0
128
+
129
+ def ask_cdrom_name(self):
130
+ # type: () -> Optional[str]
131
+ """Ask for the name of the cdrom.
132
+
133
+ If a name has been provided, return it. Otherwise, return None to
134
+ cancel the operation.
135
+ """
136
+
137
+ def change_cdrom(self):
138
+ # type: () -> bool
139
+ """Ask for the CD-ROM to be changed.
140
+
141
+ Return True once the cdrom has been changed or False to cancel the
142
+ operation.
143
+ """
144
+
145
+ def update(self, text, current):
146
+ # type: (str, int) -> None
147
+ """Periodically invoked to update the interface.
148
+
149
+ The string 'text' defines the text which should be displayed. The
150
+ integer 'current' defines the number of completed steps.
151
+ """
152
+
153
+
154
+ class InstallProgress(object):
155
+ """Class to report the progress of installing packages."""
156
+
157
+ child_pid, percent, select_timeout, status = 0, 0.0, 0.1, ""
158
+
159
+ def __init__(self):
160
+ # type: () -> None
161
+ (self.statusfd, self.writefd) = os.pipe()
162
+ # These will leak fds, but fixing this safely requires API changes.
163
+ self.write_stream = os.fdopen(self.writefd, "w") # type: io.TextIOBase
164
+ self.status_stream = os.fdopen(self.statusfd, "r") # type: io.TextIOBase # noqa
165
+ fcntl.fcntl(self.statusfd, fcntl.F_SETFL, os.O_NONBLOCK)
166
+
167
+ def start_update(self):
168
+ # type: () -> None
169
+ """(Abstract) Start update."""
170
+
171
+ def finish_update(self):
172
+ # type: () -> None
173
+ """(Abstract) Called when update has finished."""
174
+
175
+ def __enter__(self):
176
+ # type: () -> InstallProgress
177
+ return self
178
+
179
+ def __exit__(self, type, value, traceback):
180
+ # type: (object, object, object) -> None
181
+ self.write_stream.close()
182
+ self.status_stream.close()
183
+
184
+ def error(self, pkg, errormsg):
185
+ # type: (str, str) -> None
186
+ """(Abstract) Called when a error is detected during the install."""
187
+
188
+ def conffile(self, current, new):
189
+ # type: (str, str) -> None
190
+ """(Abstract) Called when a conffile question from dpkg is detected."""
191
+
192
+ def status_change(self, pkg, percent, status):
193
+ # type: (str, float, str) -> None
194
+ """(Abstract) Called when the APT status changed."""
195
+
196
+ def dpkg_status_change(self, pkg, status):
197
+ # type: (str, str) -> None
198
+ """(Abstract) Called when the dpkg status changed."""
199
+
200
+ def processing(self, pkg, stage):
201
+ # type: (str, str) -> None
202
+ """(Abstract) Sent just before a processing stage starts.
203
+
204
+ The parameter 'stage' is one of "upgrade", "install"
205
+ (both sent before unpacking), "configure", "trigproc", "remove",
206
+ "purge". This method is used for dpkg only.
207
+ """
208
+
209
+ def run(self, obj):
210
+ # type: (Union[apt_pkg.PackageManager, Union[bytes, str]]) -> int
211
+ """Install using the object 'obj'.
212
+
213
+ This functions runs install actions. The parameter 'obj' may either
214
+ be a PackageManager object in which case its do_install() method is
215
+ called or the path to a deb file.
216
+
217
+ If the object is a PackageManager, the functions returns the result
218
+ of calling its do_install() method. Otherwise, the function returns
219
+ the exit status of dpkg. In both cases, 0 means that there were no
220
+ problems.
221
+ """
222
+ pid = self.fork()
223
+ if pid == 0:
224
+ try:
225
+ # PEP-446 implemented in Python 3.4 made all descriptors
226
+ # CLOEXEC, but we need to be able to pass writefd to dpkg
227
+ # when we spawn it
228
+ os.set_inheritable(self.writefd, True)
229
+ except AttributeError: # if we don't have os.set_inheritable()
230
+ pass
231
+ # pm.do_install might raise a exception,
232
+ # when this happens, we need to catch
233
+ # it, otherwise os._exit() is not run
234
+ # and the execution continues in the
235
+ # parent code leading to very confusing bugs
236
+ try:
237
+ os._exit(obj.do_install(self.write_stream.fileno())) # type: ignore # noqa
238
+ except AttributeError:
239
+ os._exit(os.spawnlp(os.P_WAIT, "dpkg", "dpkg", "--status-fd",
240
+ str(self.write_stream.fileno()), "-i",
241
+ obj)) # type: ignore # noqa
242
+ except Exception as e:
243
+ sys.stderr.write("%s\n" % e)
244
+ os._exit(apt_pkg.PackageManager.RESULT_FAILED)
245
+
246
+ self.child_pid = pid
247
+ res = self.wait_child()
248
+ return os.WEXITSTATUS(res)
249
+
250
+ def fork(self):
251
+ # type: () -> int
252
+ """Fork."""
253
+ return os.fork()
254
+
255
+ def update_interface(self):
256
+ # type: () -> None
257
+ """Update the interface."""
258
+ try:
259
+ line = self.status_stream.readline()
260
+ except IOError as err:
261
+ # resource temporarly unavailable is ignored
262
+ if err.errno != errno.EAGAIN and err.errno != errno.EWOULDBLOCK:
263
+ print(err.strerror)
264
+ return
265
+
266
+ pkgname = status = status_str = percent = base = ""
267
+
268
+ if line.startswith('pm'):
269
+ try:
270
+ (status, pkgname, percent, status_str) = line.split(":", 3)
271
+ except ValueError:
272
+ # silently ignore lines that can't be parsed
273
+ return
274
+ elif line.startswith('status'):
275
+ try:
276
+ (base, pkgname, status, status_str) = line.split(":", 3)
277
+ except ValueError:
278
+ (base, pkgname, status) = line.split(":", 2)
279
+ elif line.startswith('processing'):
280
+ (status, status_str, pkgname) = line.split(":", 2)
281
+ self.processing(pkgname.strip(), status_str.strip())
282
+
283
+ # Always strip the status message
284
+ pkgname = pkgname.strip()
285
+ status_str = status_str.strip()
286
+ status = status.strip()
287
+
288
+ if status == 'pmerror' or status == 'error':
289
+ self.error(pkgname, status_str)
290
+ elif status == 'conffile-prompt' or status == 'pmconffile':
291
+ match = re.match("\\s*\'(.*)\'\\s*\'(.*)\'.*", status_str)
292
+ if match:
293
+ self.conffile(match.group(1), match.group(2))
294
+ elif status == "pmstatus":
295
+ # FIXME: Float comparison
296
+ if float(percent) != self.percent or status_str != self.status:
297
+ self.status_change(pkgname, float(percent), status_str.strip())
298
+ self.percent = float(percent)
299
+ self.status = status_str.strip()
300
+ elif base == "status":
301
+ self.dpkg_status_change(pkgname, status)
302
+
303
+ def wait_child(self):
304
+ # type: () -> int
305
+ """Wait for child progress to exit.
306
+
307
+ This method is responsible for calling update_interface() from time to
308
+ time. It exits once the child has exited. The return values is the
309
+ full status returned from os.waitpid() (not only the return code).
310
+ """
311
+ (pid, res) = (0, 0)
312
+ while True:
313
+ try:
314
+ select.select([self.status_stream], [], [],
315
+ self.select_timeout)
316
+ except select.error as error:
317
+ (errno_, _errstr) = error.args
318
+ if errno_ != errno.EINTR:
319
+ raise
320
+
321
+ self.update_interface()
322
+ try:
323
+ (pid, res) = os.waitpid(self.child_pid, os.WNOHANG)
324
+ if pid == self.child_pid:
325
+ break
326
+ except OSError as err:
327
+ if err.errno == errno.ECHILD:
328
+ break
329
+ if err.errno != errno.EINTR:
330
+ raise
331
+
332
+ return res
333
+
334
+
335
+ class OpProgress(object):
336
+ """Monitor objects for operations.
337
+
338
+ Display the progress of operations such as opening the cache."""
339
+
340
+ major_change, op, percent, subop = False, "", 0.0, ""
341
+
342
+ def update(self, percent=None):
343
+ # type: (Optional[float]) -> None
344
+ """Called periodically to update the user interface.
345
+
346
+ You may use the optional argument 'percent' to set the attribute
347
+ 'percent' in this call.
348
+ """
349
+ if percent is not None:
350
+ self.percent = percent
351
+
352
+ def done(self):
353
+ # type: () -> None
354
+ """Called once an operation has been completed."""
apt/progress/text.py ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2009 Julian Andres Klode <[email protected]>
2
+ #
3
+ # This program is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU General Public License as
5
+ # published by the Free Software Foundation; either version 2 of the
6
+ # License, or (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program; if not, write to the Free Software
15
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
16
+ # USA
17
+ """Progress reporting for text interfaces."""
18
+ from __future__ import print_function
19
+
20
+ import io
21
+ import os
22
+ import signal
23
+ import sys
24
+
25
+ import types
26
+ from typing import Callable, Optional, Union
27
+
28
+
29
+ import apt_pkg
30
+ from apt.progress import base
31
+
32
+
33
+ __all__ = ['AcquireProgress', 'CdromProgress', 'OpProgress']
34
+
35
+
36
+ def _(msg):
37
+ # type: (str) -> str
38
+ """Translate the message, also try apt if translation is missing."""
39
+ res = apt_pkg.gettext(msg)
40
+ if res == msg:
41
+ res = apt_pkg.gettext(msg, "apt")
42
+ return res
43
+
44
+
45
+ class TextProgress(object):
46
+ """Internal Base class for text progress classes."""
47
+
48
+ def __init__(self, outfile=None):
49
+ # type: (Optional[io.TextIOBase]) -> None
50
+ self._file = outfile or sys.stdout
51
+ self._width = 0
52
+
53
+ def _write(self, msg, newline=True, maximize=False):
54
+ # type: (str, bool, bool) -> None
55
+ """Write the message on the terminal, fill remaining space."""
56
+ self._file.write("\r")
57
+ self._file.write(msg)
58
+
59
+ # Fill remaining stuff with whitespace
60
+ if self._width > len(msg):
61
+ self._file.write((self._width - len(msg)) * ' ')
62
+ elif maximize: # Needed for OpProgress.
63
+ self._width = max(self._width, len(msg))
64
+ if newline:
65
+ self._file.write("\n")
66
+ else:
67
+ #self._file.write("\r")
68
+ self._file.flush()
69
+
70
+
71
+ class OpProgress(base.OpProgress, TextProgress):
72
+ """Operation progress reporting.
73
+
74
+ This closely resembles OpTextProgress in libapt-pkg.
75
+ """
76
+
77
+ def __init__(self, outfile=None):
78
+ # type: (Optional[io.TextIOBase]) -> None
79
+ TextProgress.__init__(self, outfile)
80
+ base.OpProgress.__init__(self)
81
+ self.old_op = ""
82
+
83
+ def update(self, percent=None):
84
+ # type: (Optional[float]) -> None
85
+ """Called periodically to update the user interface."""
86
+ base.OpProgress.update(self, percent)
87
+ if self.major_change and self.old_op:
88
+ self._write(self.old_op)
89
+ self._write("%s... %i%%\r" % (self.op, self.percent), False, True)
90
+ self.old_op = self.op
91
+
92
+ def done(self):
93
+ # type: () -> None
94
+ """Called once an operation has been completed."""
95
+ base.OpProgress.done(self)
96
+ if self.old_op:
97
+ self._write(_("%c%s... Done") % ('\r', self.old_op), True, True)
98
+ self.old_op = ""
99
+
100
+
101
+ class AcquireProgress(base.AcquireProgress, TextProgress):
102
+ """AcquireProgress for the text interface."""
103
+
104
+ def __init__(self, outfile=None):
105
+ # type: (Optional[io.TextIOBase]) -> None
106
+ TextProgress.__init__(self, outfile)
107
+ base.AcquireProgress.__init__(self)
108
+ self._signal = None # type: Union[Callable[[int, Optional[types.FrameType]], None], int, signal.Handlers, None] # noqa
109
+ self._width = 80
110
+ self._id = 1
111
+
112
+ def start(self):
113
+ # type: () -> None
114
+ """Start an Acquire progress.
115
+
116
+ In this case, the function sets up a signal handler for SIGWINCH, i.e.
117
+ window resize signals. And it also sets id to 1.
118
+ """
119
+ base.AcquireProgress.start(self)
120
+ self._signal = signal.signal(signal.SIGWINCH, self._winch)
121
+ # Get the window size.
122
+ self._winch()
123
+ self._id = 1
124
+
125
+ def _winch(self, *dummy):
126
+ # type: (object) -> None
127
+ """Signal handler for window resize signals."""
128
+ if hasattr(self._file, "fileno") and os.isatty(self._file.fileno()):
129
+ import fcntl
130
+ import termios
131
+ import struct
132
+ buf = fcntl.ioctl(self._file, termios.TIOCGWINSZ, 8 * b' ') # noqa
133
+ dummy, col, dummy, dummy = struct.unpack('hhhh', buf)
134
+ self._width = col - 1 # 1 for the cursor
135
+
136
+ def ims_hit(self, item):
137
+ # type: (apt_pkg.AcquireItemDesc) -> None
138
+ """Called when an item is update (e.g. not modified on the server)."""
139
+ base.AcquireProgress.ims_hit(self, item)
140
+ line = _('Hit ') + item.description
141
+ if item.owner.filesize:
142
+ line += ' [%sB]' % apt_pkg.size_to_str(item.owner.filesize)
143
+ self._write(line)
144
+
145
+ def fail(self, item):
146
+ # type: (apt_pkg.AcquireItemDesc) -> None
147
+ """Called when an item is failed."""
148
+ base.AcquireProgress.fail(self, item)
149
+ if item.owner.status == item.owner.STAT_DONE:
150
+ self._write(_("Ign ") + item.description)
151
+ else:
152
+ self._write(_("Err ") + item.description)
153
+ self._write(" %s" % item.owner.error_text)
154
+
155
+ def fetch(self, item):
156
+ # type: (apt_pkg.AcquireItemDesc) -> None
157
+ """Called when some of the item's data is fetched."""
158
+ base.AcquireProgress.fetch(self, item)
159
+ # It's complete already (e.g. Hit)
160
+ if item.owner.complete:
161
+ return
162
+ item.owner.id = self._id
163
+ self._id += 1
164
+ line = _("Get:") + "%s %s" % (item.owner.id, item.description)
165
+ if item.owner.filesize:
166
+ line += (" [%sB]" % apt_pkg.size_to_str(item.owner.filesize))
167
+
168
+ self._write(line)
169
+
170
+ def pulse(self, owner):
171
+ # type: (apt_pkg.Acquire) -> bool
172
+ """Periodically invoked while the Acquire process is underway.
173
+
174
+ Return False if the user asked to cancel the whole Acquire process."""
175
+ base.AcquireProgress.pulse(self, owner)
176
+ # only show progress on a tty to not clutter log files etc
177
+ if (hasattr(self._file, "fileno") and
178
+ not os.isatty(self._file.fileno())):
179
+ return True
180
+
181
+ # calculate progress
182
+ percent = (((self.current_bytes + self.current_items) * 100.0) /
183
+ float(self.total_bytes + self.total_items))
184
+
185
+ shown = False
186
+ tval = '%i%%' % percent
187
+ end = ""
188
+ if self.current_cps:
189
+ eta = int(float(self.total_bytes - self.current_bytes) /
190
+ self.current_cps)
191
+ end = " %sB/s %s" % (apt_pkg.size_to_str(self.current_cps),
192
+ apt_pkg.time_to_str(eta))
193
+
194
+ for worker in owner.workers:
195
+ val = ''
196
+ if not worker.current_item:
197
+ if worker.status:
198
+ val = ' [%s]' % worker.status
199
+ if len(tval) + len(val) + len(end) >= self._width:
200
+ break
201
+ tval += val
202
+ shown = True
203
+ continue
204
+ shown = True
205
+
206
+ if worker.current_item.owner.id:
207
+ val += " [%i %s" % (worker.current_item.owner.id,
208
+ worker.current_item.shortdesc)
209
+ else:
210
+ val += ' [%s' % worker.current_item.description
211
+ if worker.current_item.owner.active_subprocess:
212
+ val += ' %s' % worker.current_item.owner.active_subprocess
213
+
214
+ val += ' %sB' % apt_pkg.size_to_str(worker.current_size)
215
+
216
+ # Add the total size and percent
217
+ if worker.total_size and not worker.current_item.owner.complete:
218
+ val += "/%sB %i%%" % (
219
+ apt_pkg.size_to_str(worker.total_size),
220
+ worker.current_size * 100.0 / worker.total_size)
221
+
222
+ val += ']'
223
+
224
+ if len(tval) + len(val) + len(end) >= self._width:
225
+ # Display as many items as screen width
226
+ break
227
+ else:
228
+ tval += val
229
+
230
+ if not shown:
231
+ tval += _(" [Working]")
232
+
233
+ if self.current_cps:
234
+ tval += (self._width - len(end) - len(tval)) * ' ' + end
235
+
236
+ self._write(tval, False)
237
+ return True
238
+
239
+ def media_change(self, medium, drive):
240
+ # type: (str, str) -> bool
241
+ """Prompt the user to change the inserted removable media."""
242
+ base.AcquireProgress.media_change(self, medium, drive)
243
+ self._write(_("Media change: please insert the disc labeled\n"
244
+ " '%s'\n"
245
+ "in the drive '%s' and press enter\n") % (medium, drive))
246
+ return input() not in ('c', 'C')
247
+
248
+ def stop(self):
249
+ # type: () -> None
250
+ """Invoked when the Acquire process stops running."""
251
+ base.AcquireProgress.stop(self)
252
+ # Trick for getting a translation from apt
253
+ self._write((_("Fetched %sB in %s (%sB/s)\n") % (
254
+ apt_pkg.size_to_str(self.fetched_bytes),
255
+ apt_pkg.time_to_str(self.elapsed_time),
256
+ apt_pkg.size_to_str(self.current_cps))).rstrip("\n"))
257
+
258
+ # Delete the signal again.
259
+ import signal
260
+ signal.signal(signal.SIGWINCH, self._signal)
261
+
262
+
263
+ class CdromProgress(base.CdromProgress, TextProgress):
264
+ """Text CD-ROM progress."""
265
+
266
+ def ask_cdrom_name(self):
267
+ # type: () -> Optional[str]
268
+ """Ask the user to provide a name for the disc."""
269
+ base.CdromProgress.ask_cdrom_name(self)
270
+ self._write(_("Please provide a name for this medium, such as "
271
+ "'Debian 2.1r1 Disk 1'"), False)
272
+ try:
273
+ return str(input(":"))
274
+ except KeyboardInterrupt:
275
+ return None
276
+
277
+ def update(self, text, current):
278
+ # type: (str, int) -> None
279
+ """Set the current progress."""
280
+ base.CdromProgress.update(self, text, current)
281
+ if text:
282
+ self._write(text, False)
283
+
284
+ def change_cdrom(self):
285
+ # type: () -> bool
286
+ """Ask the user to change the CD-ROM."""
287
+ base.CdromProgress.change_cdrom(self)
288
+ self._write(_("Please insert an installation medium and press enter"),
289
+ False)
290
+ try:
291
+ return bool(input() == '')
292
+ except KeyboardInterrupt:
293
+ return False
apt/py.typed ADDED
File without changes
apt/utils.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2009 Canonical
2
+ #
3
+ # Authors:
4
+ # Michael Vogt
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License as
8
+ # published by the Free Software Foundation; either version 2 of the
9
+ # License, or (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but WITHOUT
12
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
+ # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
+ # details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along with
17
+ # this program; if not, write to the Free Software Foundation, Inc.,
18
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ from __future__ import print_function
20
+
21
+ import datetime
22
+ import os
23
+
24
+ from typing import Optional, Tuple
25
+
26
+ import apt
27
+ import apt_pkg
28
+
29
+
30
+ def get_maintenance_end_date(release_date, m_months):
31
+ # type: (datetime.datetime, int) -> Tuple[int, int]
32
+ """
33
+ get the (year, month) tuple when the maintenance for the distribution
34
+ ends. Needs the data of the release and the number of months that
35
+ its is supported as input
36
+ """
37
+ # calc end date
38
+ years = m_months // 12
39
+ months = m_months % 12
40
+ support_end_year = (release_date.year + years +
41
+ (release_date.month + months) // 12)
42
+ support_end_month = (release_date.month + months) % 12
43
+ # special case: this happens when e.g. doing 2010-06 + 18 months
44
+ if support_end_month == 0:
45
+ support_end_month = 12
46
+ support_end_year -= 1
47
+ return (support_end_year, support_end_month)
48
+
49
+
50
+ def get_release_date_from_release_file(path):
51
+ # type: (str) -> Optional[int]
52
+ """
53
+ return the release date as time_t for the given release file
54
+ """
55
+ if not path or not os.path.exists(path):
56
+ return None
57
+
58
+ with os.fdopen(apt_pkg.open_maybe_clear_signed_file(path)) as data:
59
+ tag = apt_pkg.TagFile(data)
60
+ section = next(tag)
61
+ if "Date" not in section:
62
+ return None
63
+ date = section["Date"]
64
+ return apt_pkg.str_to_time(date)
65
+
66
+
67
+ def get_release_filename_for_pkg(cache, pkgname, label, release):
68
+ # type: (apt.Cache, str, str, str) -> Optional[str]
69
+ " get the release file that provides this pkg "
70
+ if pkgname not in cache:
71
+ return None
72
+ pkg = cache[pkgname]
73
+ ver = None
74
+ # look for the version that comes from the repos with
75
+ # the given label and origin
76
+ for aver in pkg._pkg.version_list:
77
+ if aver is None or aver.file_list is None:
78
+ continue
79
+ for ver_file, _index in aver.file_list:
80
+ # print verFile
81
+ if (ver_file.origin == label and
82
+ ver_file.label == label and
83
+ ver_file.archive == release):
84
+ ver = aver
85
+ if not ver:
86
+ return None
87
+ indexfile = cache._list.find_index(ver.file_list[0][0])
88
+ for metaindex in cache._list.list:
89
+ for m in metaindex.index_files:
90
+ if (indexfile and
91
+ indexfile.describe == m.describe and
92
+ indexfile.is_trusted):
93
+ dirname = apt_pkg.config.find_dir("Dir::State::lists")
94
+ for relfile in ['InRelease', 'Release']:
95
+ name = (apt_pkg.uri_to_filename(metaindex.uri) +
96
+ "dists_%s_%s" % (metaindex.dist, relfile))
97
+ if os.path.exists(dirname + name):
98
+ return dirname + name
99
+ return None
aptsources/__init__.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import print_function
2
+
3
+ import apt_pkg
4
+
5
+
6
+ # init the package system, but do not re-initialize config
7
+ if "APT" not in apt_pkg.config:
8
+ apt_pkg.init_config()
9
+ apt_pkg.init_system()
aptsources/distinfo.py ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # distinfo.py - provide meta information for distro repositories
2
+ #
3
+ # Copyright (c) 2005 Gustavo Noronha Silva <[email protected]>
4
+ # Copyright (c) 2006-2007 Sebastian Heinlein <[email protected]>
5
+ #
6
+ # Authors: Gustavo Noronha Silva <[email protected]>
7
+ # Sebastian Heinlein <[email protected]>
8
+ #
9
+ # This program is free software; you can redistribute it and/or
10
+ # modify it under the terms of the GNU General Public License as
11
+ # published by the Free Software Foundation; either version 2 of the
12
+ # License, or (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program; if not, write to the Free Software
21
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22
+ # USA
23
+
24
+ from __future__ import print_function
25
+
26
+ import csv
27
+ import errno
28
+ import logging
29
+ import os
30
+ from subprocess import Popen, PIPE
31
+ import re
32
+
33
+ import apt_pkg
34
+
35
+ from apt_pkg import gettext as _
36
+
37
+
38
+ def _expand_template(template, csv_path):
39
+ """Expand the given template.
40
+
41
+ A template file consists of a header, followed by paragraphs
42
+ of templated suites, followed by a footer. A templated suite
43
+ is any paragraph where the Suite field contains {.
44
+
45
+ This function expands all templated suites using the information
46
+ found in the CSV file supplied by distro-info-data.
47
+
48
+ It yields lines of template info.
49
+ """
50
+
51
+ known_suites = set()
52
+
53
+ # Copy out any header, and gather all hardcoded suites
54
+ with apt_pkg.TagFile(template) as tmpl:
55
+ for section in tmpl:
56
+ if "X-Exclude-Suites" in section:
57
+ known_suites.update(section["X-Exclude-Suites"].split(", "))
58
+ if "Suite" in section:
59
+ if "{" in section["Suite"]:
60
+ break
61
+
62
+ known_suites.add(section["Suite"])
63
+
64
+ yield from str(section).splitlines()
65
+ else:
66
+ # We did not break, so we did copy all of them
67
+ return
68
+
69
+ for section in tmpl:
70
+ if "Suite" in section:
71
+ known_suites.add(section["Suite"])
72
+
73
+ with open(csv_path) as csv_object:
74
+ releases = reversed(list(csv.DictReader(csv_object)))
75
+
76
+ # Perform template substitution on the middle of the list
77
+ for rel in releases:
78
+ if rel["series"] in known_suites:
79
+ continue
80
+ yield ""
81
+ rel["version"] = rel["version"].replace(" LTS", "")
82
+ with apt_pkg.TagFile(template) as tmpl:
83
+ for section in tmpl:
84
+ # Only work on template sections, this skips head and tails
85
+ if "Suite" not in section or "{" not in section["Suite"]:
86
+ continue
87
+ if "X-Version" in section:
88
+ # Version requirements. Maybe should be made nicer
89
+ ver = rel["version"]
90
+ if any(
91
+ (field.startswith("le") and
92
+ apt_pkg.version_compare(field[3:], ver) < 0) or
93
+ (field.startswith("ge") and
94
+ apt_pkg.version_compare(field[3:], ver) > 0)
95
+ for field in section["X-Version"].split(", ")):
96
+ continue
97
+
98
+ for line in str(section).format(**rel).splitlines():
99
+ if line.startswith("X-Version"):
100
+ continue
101
+ yield line
102
+
103
+ # Copy out remaining suites
104
+ with apt_pkg.TagFile(template) as tmpl:
105
+ # Skip the head again, we don't want to copy it twice
106
+ for section in tmpl:
107
+ if "Suite" in section and "{" in section["Suite"]:
108
+ break
109
+
110
+ for section in tmpl:
111
+ # Ignore any template parts and copy the rest out,
112
+ # this is the inverse of the template substitution loop
113
+ if "Suite" in section and "{" in section["Suite"]:
114
+ continue
115
+
116
+ yield from str(section).splitlines()
117
+
118
+
119
+ class Template(object):
120
+
121
+ def __init__(self):
122
+ self.name = None
123
+ self.child = False
124
+ self.parents = [] # ref to parent template(s)
125
+ self.match_name = None
126
+ self.description = None
127
+ self.base_uri = None
128
+ self.type = None
129
+ self.components = []
130
+ self.children = []
131
+ self.match_uri = None
132
+ self.mirror_set = {}
133
+ self.distribution = None
134
+ self.available = True
135
+ self.official = True
136
+
137
+ def has_component(self, comp):
138
+ ''' Check if the distribution provides the given component '''
139
+ return comp in (c.name for c in self.components)
140
+
141
+ def is_mirror(self, url):
142
+ ''' Check if a given url of a repository is a valid mirror '''
143
+ proto, hostname, dir = split_url(url)
144
+ if hostname in self.mirror_set:
145
+ return self.mirror_set[hostname].has_repository(proto, dir)
146
+ else:
147
+ return False
148
+
149
+
150
+ class Component(object):
151
+
152
+ def __init__(self, name, desc=None, long_desc=None, parent_component=None):
153
+ self.name = name
154
+ self.description = desc
155
+ self.description_long = long_desc
156
+ self.parent_component = parent_component
157
+
158
+ def get_parent_component(self):
159
+ return self.parent_component
160
+
161
+ def set_parent_component(self, parent):
162
+ self.parent_component = parent
163
+
164
+ def get_description(self):
165
+ if self.description_long is not None:
166
+ return self.description_long
167
+ elif self.description is not None:
168
+ return self.description
169
+ else:
170
+ return None
171
+
172
+ def set_description(self, desc):
173
+ self.description = desc
174
+
175
+ def set_description_long(self, desc):
176
+ self.description_long = desc
177
+
178
+ def get_description_long(self):
179
+ return self.description_long
180
+
181
+
182
+ class Mirror(object):
183
+ ''' Storage for mirror related information '''
184
+
185
+ def __init__(self, proto, hostname, dir, location=None):
186
+ self.hostname = hostname
187
+ self.repositories = []
188
+ self.add_repository(proto, dir)
189
+ self.location = location
190
+
191
+ def add_repository(self, proto, dir):
192
+ self.repositories.append(Repository(proto, dir))
193
+
194
+ def get_repositories_for_proto(self, proto):
195
+ return [r for r in self.repositories if r.proto == proto]
196
+
197
+ def has_repository(self, proto, dir):
198
+ if dir is None:
199
+ return False
200
+ for r in self.repositories:
201
+ if r.proto == proto and dir in r.dir:
202
+ return True
203
+ return False
204
+
205
+ def get_repo_urls(self):
206
+ return [r.get_url(self.hostname) for r in self.repositories]
207
+
208
+ def get_location(self):
209
+ return self.location
210
+
211
+ def set_location(self, location):
212
+ self.location = location
213
+
214
+
215
+ class Repository(object):
216
+
217
+ def __init__(self, proto, dir):
218
+ self.proto = proto
219
+ self.dir = dir
220
+
221
+ def get_info(self):
222
+ return self.proto, self.dir
223
+
224
+ def get_url(self, hostname):
225
+ return "%s://%s/%s" % (self.proto, hostname, self.dir)
226
+
227
+
228
+ def split_url(url):
229
+ ''' split a given URL into the protocoll, the hostname and the dir part '''
230
+ split = re.split(":*\\/+", url, maxsplit=2)
231
+ while len(split) < 3:
232
+ split.append(None)
233
+ return split
234
+
235
+
236
+ class DistInfo(object):
237
+
238
+ def __init__(self, dist=None, base_dir="/usr/share/python-apt/templates"):
239
+ self.metarelease_uri = ''
240
+ self.templates = []
241
+ self.arch = apt_pkg.config.find("APT::Architecture")
242
+
243
+ location = None
244
+ match_loc = re.compile(r"^#LOC:(.+)$")
245
+ match_mirror_line = re.compile(
246
+ r"^(#LOC:.+)|(((http)|(ftp)|(rsync)|(file)|(mirror)|(https))://"
247
+ r"[A-Za-z0-9/\.:\-_@]+)$")
248
+ #match_mirror_line = re.compile(r".+")
249
+
250
+ if not dist:
251
+ try:
252
+ dist = Popen(["lsb_release", "-i", "-s"],
253
+ universal_newlines=True,
254
+ stdout=PIPE).communicate()[0].strip()
255
+ except (OSError, IOError) as exc:
256
+ if exc.errno != errno.ENOENT:
257
+ logging.warning(
258
+ 'lsb_release failed, using defaults:' % exc)
259
+ dist = "Debian"
260
+
261
+ self.dist = dist
262
+
263
+ map_mirror_sets = {}
264
+
265
+ dist_fname = "%s/%s.info" % (base_dir, dist)
266
+ csv_fname = "/usr/share/distro-info/{}.csv".format(dist.lower())
267
+
268
+ template = None
269
+ component = None
270
+ for line in _expand_template(dist_fname, csv_fname):
271
+ tokens = line.split(':', 1)
272
+ if len(tokens) < 2:
273
+ continue
274
+ field = tokens[0].strip()
275
+ value = tokens[1].strip()
276
+ if field == 'ChangelogURI':
277
+ self.changelogs_uri = _(value)
278
+ elif field == 'MetaReleaseURI':
279
+ self.metarelease_uri = value
280
+ elif field == 'Suite':
281
+ self.finish_template(template, component)
282
+ component = None
283
+ template = Template()
284
+ template.name = value
285
+ template.distribution = dist
286
+ template.match_name = "^%s$" % value
287
+ elif field == 'MatchName':
288
+ template.match_name = value
289
+ elif field == 'ParentSuite':
290
+ template.child = True
291
+ for nanny in self.templates:
292
+ # look for parent and add back ref to it
293
+ if nanny.name == value:
294
+ template.parents.append(nanny)
295
+ nanny.children.append(template)
296
+ elif field == 'Available':
297
+ template.available = apt_pkg.string_to_bool(value)
298
+ elif field == 'Official':
299
+ template.official = apt_pkg.string_to_bool(value)
300
+ elif field == 'RepositoryType':
301
+ template.type = value
302
+ elif field == 'BaseURI' and not template.base_uri:
303
+ template.base_uri = value
304
+ elif field == 'BaseURI-%s' % self.arch:
305
+ template.base_uri = value
306
+ elif field == 'MatchURI' and not template.match_uri:
307
+ template.match_uri = value
308
+ elif field == 'MatchURI-%s' % self.arch:
309
+ template.match_uri = value
310
+ elif (field == 'MirrorsFile' or
311
+ field == 'MirrorsFile-%s' % self.arch):
312
+ # Make the path absolute.
313
+ value = os.path.isabs(value) and value or \
314
+ os.path.abspath(os.path.join(base_dir, value))
315
+ if value not in map_mirror_sets:
316
+ mirror_set = {}
317
+ try:
318
+ with open(value) as value_f:
319
+ mirror_data = list(filter(
320
+ match_mirror_line.match,
321
+ [x.strip() for x in value_f]))
322
+ except Exception:
323
+ print("WARNING: Failed to read mirror file")
324
+ mirror_data = []
325
+ for line in mirror_data:
326
+ if line.startswith("#LOC:"):
327
+ location = match_loc.sub(r"\1", line)
328
+ continue
329
+ (proto, hostname, dir) = split_url(line)
330
+ if hostname in mirror_set:
331
+ mirror_set[hostname].add_repository(proto, dir)
332
+ else:
333
+ mirror_set[hostname] = Mirror(
334
+ proto, hostname, dir, location)
335
+ map_mirror_sets[value] = mirror_set
336
+ template.mirror_set = map_mirror_sets[value]
337
+ elif field == 'Description':
338
+ template.description = _(value)
339
+ elif field == 'Component':
340
+ if (component and not
341
+ template.has_component(component.name)):
342
+ template.components.append(component)
343
+ component = Component(value)
344
+ elif field == 'CompDescription':
345
+ component.set_description(_(value))
346
+ elif field == 'CompDescriptionLong':
347
+ component.set_description_long(_(value))
348
+ elif field == 'ParentComponent':
349
+ component.set_parent_component(value)
350
+ self.finish_template(template, component)
351
+ template = None
352
+ component = None
353
+
354
+ def finish_template(self, template, component):
355
+ " finish the current tempalte "
356
+ if not template:
357
+ return
358
+ # reuse some properties of the parent template
359
+ if template.match_uri is None and template.child:
360
+ for t in template.parents:
361
+ if t.match_uri:
362
+ template.match_uri = t.match_uri
363
+ break
364
+ if template.mirror_set == {} and template.child:
365
+ for t in template.parents:
366
+ if t.match_uri:
367
+ template.mirror_set = t.mirror_set
368
+ break
369
+ if component and not template.has_component(component.name):
370
+ template.components.append(component)
371
+ component = None
372
+ # the official attribute is inherited
373
+ for t in template.parents:
374
+ template.official = t.official
375
+ self.templates.append(template)
376
+
377
+
378
+ if __name__ == "__main__":
379
+ d = DistInfo("Ubuntu", "/usr/share/python-apt/templates")
380
+ logging.info(d.changelogs_uri)
381
+ for template in d.templates:
382
+ logging.info("\nSuite: %s" % template.name)
383
+ logging.info("Desc: %s" % template.description)
384
+ logging.info("BaseURI: %s" % template.base_uri)
385
+ logging.info("MatchURI: %s" % template.match_uri)
386
+ if template.mirror_set != {}:
387
+ logging.info("Mirrors: %s" % list(template.mirror_set.keys()))
388
+ for comp in template.components:
389
+ logging.info(" %s -%s -%s" % (comp.name,
390
+ comp.description,
391
+ comp.description_long))
392
+ for child in template.children:
393
+ logging.info(" %s" % child.description)
aptsources/distro.py ADDED
@@ -0,0 +1,613 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # distro.py - Provide a distro abstraction of the sources.list
2
+ #
3
+ # Copyright (c) 2004-2009 Canonical Ltd.
4
+ # Copyright (c) 2006-2007 Sebastian Heinlein
5
+ # Copyright (c) 2016 Harald Sitter
6
+ #
7
+ # Authors: Sebastian Heinlein <[email protected]>
8
+ # Michael Vogt <[email protected]>
9
+ # Harald Sitter <[email protected]>
10
+ #
11
+ # This program is free software; you can redistribute it and/or
12
+ # modify it under the terms of the GNU General Public License as
13
+ # published by the Free Software Foundation; either version 2 of the
14
+ # License, or (at your option) any later version.
15
+ #
16
+ # This program is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with this program; if not, write to the Free Software
23
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
24
+ # USA
25
+
26
+ import gettext
27
+ import logging
28
+ import re
29
+ import shlex
30
+ import os
31
+
32
+ from xml.etree.ElementTree import ElementTree
33
+
34
+ from apt_pkg import gettext as _
35
+
36
+
37
+ class NoDistroTemplateException(Exception):
38
+ pass
39
+
40
+
41
+ class Distribution(object):
42
+
43
+ def __init__(self, id, codename, description, release, is_like=[]):
44
+ """ Container for distribution specific informations """
45
+ # LSB information
46
+ self.id = id
47
+ self.codename = codename
48
+ self.description = description
49
+ self.release = release
50
+ self.is_like = is_like
51
+
52
+ self.binary_type = "deb"
53
+ self.source_type = "deb-src"
54
+
55
+ def get_sources(self, sourceslist):
56
+ """
57
+ Find the corresponding template, main and child sources
58
+ for the distribution
59
+ """
60
+
61
+ self.sourceslist = sourceslist
62
+ # corresponding sources
63
+ self.source_template = None
64
+ self.child_sources = []
65
+ self.main_sources = []
66
+ self.disabled_sources = []
67
+ self.cdrom_sources = []
68
+ self.download_comps = []
69
+ self.enabled_comps = []
70
+ self.cdrom_comps = []
71
+ self.used_media = []
72
+ self.get_source_code = False
73
+ self.source_code_sources = []
74
+
75
+ # location of the sources
76
+ self.default_server = ""
77
+ self.main_server = ""
78
+ self.nearest_server = ""
79
+ self.used_servers = []
80
+
81
+ # find the distro template
82
+ for template in self.sourceslist.matcher.templates:
83
+ if (self.is_codename(template.name) and
84
+ template.distribution == self.id):
85
+ #print "yeah! found a template for %s" % self.description
86
+ #print template.description, template.base_uri, \
87
+ # template.components
88
+ self.source_template = template
89
+ break
90
+ if self.source_template is None:
91
+ raise NoDistroTemplateException(
92
+ "Error: could not find a distribution template for %s/%s" %
93
+ (self.id, self.codename))
94
+
95
+ # find main and child sources
96
+ media = []
97
+ comps = []
98
+ cdrom_comps = []
99
+ enabled_comps = []
100
+ #source_code = []
101
+ for source in self.sourceslist.list:
102
+ if (not source.invalid and
103
+ self.is_codename(source.dist) and
104
+ source.template and
105
+ source.template.official and
106
+ self.is_codename(source.template.name)):
107
+ #print "yeah! found a distro repo: %s" % source.line
108
+ # cdroms need do be handled differently
109
+ if (source.uri.startswith("cdrom:") and
110
+ not source.disabled):
111
+ self.cdrom_sources.append(source)
112
+ cdrom_comps.extend(source.comps)
113
+ elif (source.uri.startswith("cdrom:") and
114
+ source.disabled):
115
+ self.cdrom_sources.append(source)
116
+ elif (source.type == self.binary_type and
117
+ not source.disabled):
118
+ self.main_sources.append(source)
119
+ comps.extend(source.comps)
120
+ media.append(source.uri)
121
+ elif (source.type == self.binary_type and
122
+ source.disabled):
123
+ self.disabled_sources.append(source)
124
+ elif (source.type == self.source_type and
125
+ not source.disabled):
126
+ self.source_code_sources.append(source)
127
+ elif (source.type == self.source_type and
128
+ source.disabled):
129
+ self.disabled_sources.append(source)
130
+ if (not source.invalid and
131
+ source.template in self.source_template.children):
132
+ if (not source.disabled and
133
+ source.type == self.binary_type):
134
+ self.child_sources.append(source)
135
+ elif (not source.disabled and
136
+ source.type == self.source_type):
137
+ self.source_code_sources.append(source)
138
+ else:
139
+ self.disabled_sources.append(source)
140
+ self.download_comps = set(comps)
141
+ self.cdrom_comps = set(cdrom_comps)
142
+ enabled_comps.extend(comps)
143
+ enabled_comps.extend(cdrom_comps)
144
+ self.enabled_comps = set(enabled_comps)
145
+ self.used_media = set(media)
146
+ self.get_mirrors()
147
+
148
+ def get_mirrors(self, mirror_template=None):
149
+ """
150
+ Provide a set of mirrors where you can get the distribution from
151
+ """
152
+ # the main server is stored in the template
153
+ self.main_server = self.source_template.base_uri
154
+
155
+ # other used servers
156
+ for medium in self.used_media:
157
+ if not medium.startswith("cdrom:"):
158
+ # seems to be a network source
159
+ self.used_servers.append(medium)
160
+
161
+ if len(self.main_sources) == 0:
162
+ self.default_server = self.main_server
163
+ else:
164
+ self.default_server = self.main_sources[0].uri
165
+
166
+ # get a list of country codes and real names
167
+ self.countries = {}
168
+ fname = "/usr/share/xml/iso-codes/iso_3166.xml"
169
+ if os.path.exists(fname):
170
+ et = ElementTree(file=fname)
171
+ # python2.6 compat, the next two lines can get removed
172
+ # once we do not use py2.6 anymore
173
+ if getattr(et, "iter", None) is None:
174
+ et.iter = et.getiterator
175
+ it = et.iter('iso_3166_entry')
176
+ for elm in it:
177
+ try:
178
+ descr = elm.attrib["common_name"]
179
+ except KeyError:
180
+ descr = elm.attrib["name"]
181
+ try:
182
+ code = elm.attrib["alpha_2_code"]
183
+ except KeyError:
184
+ code = elm.attrib["alpha_3_code"]
185
+ self.countries[code.lower()] = gettext.dgettext('iso_3166',
186
+ descr)
187
+
188
+ # try to guess the nearest mirror from the locale
189
+ self.country = None
190
+ self.country_code = None
191
+ locale = os.getenv("LANG", default="en_UK")
192
+ a = locale.find("_")
193
+ z = locale.find(".")
194
+ if z == -1:
195
+ z = len(locale)
196
+ country_code = locale[a + 1:z].lower()
197
+
198
+ if mirror_template:
199
+ self.nearest_server = mirror_template % country_code
200
+
201
+ if country_code in self.countries:
202
+ self.country = self.countries[country_code]
203
+ self.country_code = country_code
204
+
205
+ def _get_mirror_name(self, server):
206
+ ''' Try to get a human readable name for the main mirror of a country
207
+ Customize for different distributions '''
208
+ country = None
209
+ i = server.find("://")
210
+ li = server.find(".archive.ubuntu.com")
211
+ if i != -1 and li != -1:
212
+ country = server[i + len("://"):li]
213
+ if country in self.countries:
214
+ # TRANSLATORS: %s is a country
215
+ return _("Server for %s") % self.countries[country]
216
+ else:
217
+ return("%s" % server.rstrip("/ "))
218
+
219
+ def get_server_list(self):
220
+ ''' Return a list of used and suggested servers '''
221
+
222
+ def compare_mirrors(mir1, mir2):
223
+ ''' Helper function that handles comaprision of mirror urls
224
+ that could contain trailing slashes'''
225
+ return re.match(mir1.strip("/ "), mir2.rstrip("/ "))
226
+
227
+ # Store all available servers:
228
+ # Name, URI, active
229
+ mirrors = []
230
+ if (len(self.used_servers) < 1 or
231
+ (len(self.used_servers) == 1 and
232
+ compare_mirrors(self.used_servers[0], self.main_server))):
233
+ mirrors.append([_("Main server"), self.main_server, True])
234
+ if self.nearest_server:
235
+ mirrors.append([self._get_mirror_name(self.nearest_server),
236
+ self.nearest_server, False])
237
+ elif (len(self.used_servers) == 1 and not
238
+ compare_mirrors(self.used_servers[0], self.main_server)):
239
+ mirrors.append([_("Main server"), self.main_server, False])
240
+ # Only one server is used
241
+ server = self.used_servers[0]
242
+
243
+ # Append the nearest server if it's not already used
244
+ if self.nearest_server:
245
+ if not compare_mirrors(server, self.nearest_server):
246
+ mirrors.append([self._get_mirror_name(self.nearest_server),
247
+ self.nearest_server, False])
248
+ if server:
249
+ mirrors.append([self._get_mirror_name(server), server, True])
250
+
251
+ elif len(self.used_servers) > 1:
252
+ # More than one server is used. Since we don't handle this case
253
+ # in the user interface we set "custom servers" to true and
254
+ # append a list of all used servers
255
+ mirrors.append([_("Main server"), self.main_server, False])
256
+ if self.nearest_server:
257
+ mirrors.append([self._get_mirror_name(self.nearest_server),
258
+ self.nearest_server, False])
259
+ mirrors.append([_("Custom servers"), None, True])
260
+ for server in self.used_servers:
261
+ mirror_entry = [self._get_mirror_name(server), server, False]
262
+ if (compare_mirrors(server, self.nearest_server) or
263
+ compare_mirrors(server, self.main_server)):
264
+ continue
265
+ elif mirror_entry not in mirrors:
266
+ mirrors.append(mirror_entry)
267
+
268
+ return mirrors
269
+
270
+ def add_source(self, type=None,
271
+ uri=None, dist=None, comps=None, comment=""):
272
+ """
273
+ Add distribution specific sources
274
+ """
275
+ if uri is None:
276
+ # FIXME: Add support for the server selector
277
+ uri = self.default_server
278
+ if dist is None:
279
+ dist = self.codename
280
+ if comps is None:
281
+ comps = list(self.enabled_comps)
282
+ if type is None:
283
+ type = self.binary_type
284
+ new_source = self.sourceslist.add(type, uri, dist, comps, comment)
285
+ # if source code is enabled add a deb-src line after the new
286
+ # source
287
+ if self.get_source_code and type == self.binary_type:
288
+ self.sourceslist.add(
289
+ self.source_type, uri, dist, comps, comment,
290
+ file=new_source.file,
291
+ pos=self.sourceslist.list.index(new_source) + 1)
292
+
293
+ def enable_component(self, comp):
294
+ """
295
+ Enable a component in all main, child and source code sources
296
+ (excluding cdrom based sources)
297
+
298
+ comp: the component that should be enabled
299
+ """
300
+ comps = set([comp])
301
+ # look for parent components that we may have to add
302
+ for source in self.main_sources:
303
+ for c in source.template.components:
304
+ if c.name == comp and c.parent_component:
305
+ comps.add(c.parent_component)
306
+ for c in comps:
307
+ self._enable_component(c)
308
+
309
+ def _enable_component(self, comp):
310
+
311
+ def add_component_only_once(source, comps_per_dist):
312
+ """
313
+ Check if we already added the component to the repository, since
314
+ a repository could be splitted into different apt lines. If not
315
+ add the component
316
+ """
317
+ # if we don't have that distro, just return (can happen for e.g.
318
+ # dapper-update only in deb-src
319
+ if source.dist not in comps_per_dist:
320
+ return
321
+ # if we have seen this component already for this distro,
322
+ # return (nothing to do)
323
+ if comp in comps_per_dist[source.dist]:
324
+ return
325
+ # add it
326
+ source.comps.append(comp)
327
+ comps_per_dist[source.dist].add(comp)
328
+
329
+ sources = []
330
+ sources.extend(self.main_sources)
331
+ sources.extend(self.child_sources)
332
+ # store what comps are enabled already per distro (where distro is
333
+ # e.g. "dapper", "dapper-updates")
334
+ comps_per_dist = {}
335
+ comps_per_sdist = {}
336
+ for s in sources:
337
+ if s.type == self.binary_type:
338
+ if s.dist not in comps_per_dist:
339
+ comps_per_dist[s.dist] = set()
340
+ for c in s.comps:
341
+ comps_per_dist[s.dist].add(c)
342
+ for s in self.source_code_sources:
343
+ if s.type == self.source_type:
344
+ if s.dist not in comps_per_sdist:
345
+ comps_per_sdist[s.dist] = set()
346
+ for c in s.comps:
347
+ comps_per_sdist[s.dist].add(c)
348
+
349
+ # check if there is a main source at all
350
+ if len(self.main_sources) < 1:
351
+ # create a new main source
352
+ self.add_source(comps=["%s" % comp])
353
+ else:
354
+ # add the comp to all main, child and source code sources
355
+ for source in sources:
356
+ add_component_only_once(source, comps_per_dist)
357
+
358
+ for source in self.source_code_sources:
359
+ add_component_only_once(source, comps_per_sdist)
360
+
361
+ # check if there is a main source code source at all
362
+ if self.get_source_code:
363
+ if len(self.source_code_sources) < 1:
364
+ # create a new main source
365
+ self.add_source(type=self.source_type, comps=["%s" % comp])
366
+ else:
367
+ # add the comp to all main, child and source code sources
368
+ for source in self.source_code_sources:
369
+ add_component_only_once(source, comps_per_sdist)
370
+
371
+ def disable_component(self, comp):
372
+ """
373
+ Disable a component in all main, child and source code sources
374
+ (excluding cdrom based sources)
375
+ """
376
+ sources = []
377
+ sources.extend(self.main_sources)
378
+ sources.extend(self.child_sources)
379
+ sources.extend(self.source_code_sources)
380
+ if comp in self.cdrom_comps:
381
+ sources = []
382
+ sources.extend(self.main_sources)
383
+ for source in sources:
384
+ if comp in source.comps:
385
+ source.comps.remove(comp)
386
+ if len(source.comps) < 1:
387
+ self.sourceslist.remove(source)
388
+
389
+ def change_server(self, uri):
390
+ ''' Change the server of all distro specific sources to
391
+ a given host '''
392
+
393
+ def change_server_of_source(source, uri, seen):
394
+ # Avoid creating duplicate entries
395
+ source.uri = uri
396
+ for comp in source.comps:
397
+ if [source.uri, source.dist, comp] in seen:
398
+ source.comps.remove(comp)
399
+ else:
400
+ seen.append([source.uri, source.dist, comp])
401
+ if len(source.comps) < 1:
402
+ self.sourceslist.remove(source)
403
+
404
+ seen_binary = []
405
+ seen_source = []
406
+ self.default_server = uri
407
+ for source in self.main_sources:
408
+ change_server_of_source(source, uri, seen_binary)
409
+ for source in self.child_sources:
410
+ # Do not change the forces server of a child source
411
+ if (source.template.base_uri is None or
412
+ source.template.base_uri != source.uri):
413
+ change_server_of_source(source, uri, seen_binary)
414
+ for source in self.source_code_sources:
415
+ change_server_of_source(source, uri, seen_source)
416
+
417
+ def is_codename(self, name):
418
+ ''' Compare a given name with the release codename. '''
419
+ if name == self.codename:
420
+ return True
421
+ else:
422
+ return False
423
+
424
+
425
+ class DebianDistribution(Distribution):
426
+ ''' Class to support specific Debian features '''
427
+
428
+ def is_codename(self, name):
429
+ ''' Compare a given name with the release codename and check if
430
+ if it can be used as a synonym for a development releases '''
431
+ if name == self.codename or self.release in ("testing", "unstable"):
432
+ return True
433
+ else:
434
+ return False
435
+
436
+ def _get_mirror_name(self, server):
437
+ ''' Try to get a human readable name for the main mirror of a country
438
+ Debian specific '''
439
+ country = None
440
+ i = server.find("://ftp.")
441
+ li = server.find(".debian.org")
442
+ if i != -1 and li != -1:
443
+ country = server[i + len("://ftp."):li]
444
+ if country in self.countries:
445
+ # TRANSLATORS: %s is a country
446
+ return _("Server for %s") % gettext.dgettext(
447
+ "iso_3166", self.countries[country].rstrip()).rstrip()
448
+ else:
449
+ return("%s" % server.rstrip("/ "))
450
+
451
+ def get_mirrors(self):
452
+ Distribution.get_mirrors(
453
+ self, mirror_template="http://ftp.%s.debian.org/debian/")
454
+
455
+
456
+ class UbuntuDistribution(Distribution):
457
+ ''' Class to support specific Ubuntu features '''
458
+
459
+ def get_mirrors(self):
460
+ Distribution.get_mirrors(
461
+ self, mirror_template="http://%s.archive.ubuntu.com/ubuntu/")
462
+
463
+
464
+ class UbuntuRTMDistribution(UbuntuDistribution):
465
+ ''' Class to support specific Ubuntu RTM features '''
466
+
467
+ def get_mirrors(self):
468
+ self.main_server = self.source_template.base_uri
469
+
470
+
471
+ def _lsb_release():
472
+ """Call lsb_release --idrc and return a mapping."""
473
+ from subprocess import Popen, PIPE
474
+ import errno
475
+ result = {'Codename': 'sid', 'Distributor ID': 'Debian',
476
+ 'Description': 'Debian GNU/Linux unstable (sid)',
477
+ 'Release': 'unstable'}
478
+ try:
479
+ out = Popen(['lsb_release', '-idrc'], stdout=PIPE).communicate()[0]
480
+ # Convert to unicode string, needed for Python 3.1
481
+ out = out.decode("utf-8")
482
+ result.update(line.split(":\t") for line in out.split("\n")
483
+ if ':\t' in line)
484
+ except OSError as exc:
485
+ if exc.errno != errno.ENOENT:
486
+ logging.warning('lsb_release failed, using defaults:' % exc)
487
+ return result
488
+
489
+
490
+ def _system_image_channel():
491
+ """Get the current channel from system-image-cli -i if possible."""
492
+ from subprocess import Popen, PIPE
493
+ import errno
494
+ try:
495
+ from subprocess import DEVNULL
496
+ except ImportError:
497
+ # no DEVNULL in 2.7
498
+ DEVNULL = os.open(os.devnull, os.O_RDWR)
499
+ try:
500
+ out = Popen(
501
+ ['system-image-cli', '-i'], stdout=PIPE, stderr=DEVNULL,
502
+ universal_newlines=True).communicate()[0]
503
+ for line in out.splitlines():
504
+ if line.startswith('channel: '):
505
+ return line.split(': ', 1)[1]
506
+ except OSError as exc:
507
+ if exc.errno != errno.ENOENT:
508
+ logging.warning(
509
+ 'system-image-cli failed, using defaults: %s' % exc)
510
+ return None
511
+
512
+
513
+ class _OSRelease:
514
+
515
+ DEFAULT_OS_RELEASE_FILE = '/etc/os-release'
516
+ OS_RELEASE_FILE = '/etc/os-release'
517
+
518
+ def __init__(self, lsb_compat=True):
519
+ self.result = {}
520
+ self.valid = False
521
+ self.file = _OSRelease.OS_RELEASE_FILE
522
+
523
+ if not os.path.isfile(self.file):
524
+ return
525
+
526
+ self.parse()
527
+ self.valid = True
528
+
529
+ if lsb_compat:
530
+ self.inject_lsb_compat()
531
+
532
+ def inject_lsb_compat(self):
533
+ self.result['Distributor ID'] = self.result['ID']
534
+ self.result['Description'] = self.result['PRETTY_NAME']
535
+ # Optionals as per os-release spec.
536
+ self.result['Codename'] = self.result.get('VERSION_CODENAME')
537
+ if not self.result['Codename']:
538
+ # Transient Ubuntu 16.04 field (LP: #1598212)
539
+ self.result['Codename'] = self.result.get('UBUNTU_CODENAME')
540
+ self.result['Release'] = self.result.get('VERSION_ID')
541
+
542
+ def parse(self):
543
+ f = open(self.file, 'r')
544
+ for line in f:
545
+ line = line.strip()
546
+ if not line:
547
+ continue
548
+ self.parse_entry(*line.split('=', 1))
549
+ f.close()
550
+
551
+ def parse_entry(self, key, value):
552
+ value = self.parse_value(value) # Values can be shell strings...
553
+ if key == "ID_LIKE" and isinstance(value, str):
554
+ # ID_LIKE is specified as quoted space-separated list. This will
555
+ # be parsed as string that we need to split manually.
556
+ value = value.split(' ')
557
+ self.result[key] = value
558
+
559
+ def parse_value(self, value):
560
+ values = shlex.split(value)
561
+ if len(values) == 1:
562
+ return values[0]
563
+ return values
564
+
565
+
566
+ def get_distro(id=None, codename=None, description=None, release=None,
567
+ is_like=[]):
568
+ """
569
+ Check the currently used distribution and return the corresponding
570
+ distriubtion class that supports distro specific features.
571
+
572
+ If no paramter are given the distro will be auto detected via
573
+ a call to lsb-release
574
+ """
575
+ # make testing easier
576
+ if not (id and codename and description and release):
577
+ os_release = _OSRelease()
578
+ os_result = []
579
+ lsb_result = _lsb_release()
580
+ if os_release.valid:
581
+ os_result = os_release.result
582
+ # TODO: We cannot presently use os-release to fully replace lsb_release
583
+ # because os-release's ID, VERSION_ID and VERSION_CODENAME fields
584
+ # are specified as lowercase. In lsb_release they can be upcase
585
+ # or captizalized. So, switching to os-release would consitute
586
+ # a behavior break a which point lsb_release support should be
587
+ # fully removed.
588
+ # This in particular is a problem for template matching, as this
589
+ # matches against Distribution objects and depends on string
590
+ # case.
591
+ lsb_result = _lsb_release()
592
+ id = lsb_result['Distributor ID']
593
+ codename = lsb_result['Codename']
594
+ description = lsb_result['Description']
595
+ release = lsb_result['Release']
596
+ # Not available with LSB, use get directly.
597
+ is_like = os_result.get('ID_LIKE', [])
598
+ if id == "Ubuntu":
599
+ channel = _system_image_channel()
600
+ if channel is not None and "ubuntu-rtm/" in channel:
601
+ id = "Ubuntu-RTM"
602
+ codename = channel.rsplit("/", 1)[1].split("-", 1)[0]
603
+ description = codename
604
+ release = codename
605
+ if id == "Ubuntu":
606
+ return UbuntuDistribution(id, codename, description, release, is_like)
607
+ if id == "Ubuntu-RTM":
608
+ return UbuntuRTMDistribution(
609
+ id, codename, description, release, is_like)
610
+ elif id == "Debian":
611
+ return DebianDistribution(id, codename, description, release, is_like)
612
+ else:
613
+ return Distribution(id, codename, description, release, is_like)
aptsources/sourceslist.py ADDED
@@ -0,0 +1,516 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # sourceslist.py - Provide an abstraction of the sources.list
2
+ #
3
+ # Copyright (c) 2004-2009 Canonical Ltd.
4
+ # Copyright (c) 2004 Michiel Sikkes
5
+ # Copyright (c) 2006-2007 Sebastian Heinlein
6
+ #
7
+ # Authors: Michiel Sikkes <[email protected]>
8
+ # Michael Vogt <[email protected]>
9
+ # Sebastian Heinlein <[email protected]>
10
+ #
11
+ # This program is free software; you can redistribute it and/or
12
+ # modify it under the terms of the GNU General Public License as
13
+ # published by the Free Software Foundation; either version 2 of the
14
+ # License, or (at your option) any later version.
15
+ #
16
+ # This program is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with this program; if not, write to the Free Software
23
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
24
+ # USA
25
+
26
+ from __future__ import absolute_import, print_function
27
+
28
+ import glob
29
+ import logging
30
+ import os.path
31
+ import re
32
+ import shutil
33
+ import time
34
+
35
+ import apt_pkg
36
+ from .distinfo import DistInfo
37
+ #from apt_pkg import gettext as _
38
+
39
+
40
+ # some global helpers
41
+
42
+ __all__ = ['is_mirror', 'SourceEntry', 'NullMatcher', 'SourcesList',
43
+ 'SourceEntryMatcher']
44
+
45
+
46
+ def is_mirror(master_uri, compare_uri):
47
+ """ check if the given add_url is idential or a mirror of orig_uri e.g.:
48
+ master_uri = archive.ubuntu.com
49
+ compare_uri = de.archive.ubuntu.com
50
+ -> True
51
+ """
52
+ # remove traling spaces and "/"
53
+ compare_uri = compare_uri.rstrip("/ ")
54
+ master_uri = master_uri.rstrip("/ ")
55
+ # uri is identical
56
+ if compare_uri == master_uri:
57
+ #print "Identical"
58
+ return True
59
+ # add uri is a master site and orig_uri has the from "XX.mastersite"
60
+ # (e.g. de.archive.ubuntu.com)
61
+ try:
62
+ compare_srv = compare_uri.split("//")[1]
63
+ master_srv = master_uri.split("//")[1]
64
+ #print "%s == %s " % (add_srv, orig_srv)
65
+ except IndexError: # ok, somethings wrong here
66
+ #print "IndexError"
67
+ return False
68
+ # remove the leading "<country>." (if any) and see if that helps
69
+ if "." in compare_srv and \
70
+ compare_srv[compare_srv.index(".") + 1:] == master_srv:
71
+ #print "Mirror"
72
+ return True
73
+ return False
74
+
75
+
76
+ def uniq(s):
77
+ """ simple and efficient way to return uniq collection
78
+
79
+ This is not intended for use with a SourceList. It is provided
80
+ for internal use only. It does not have a leading underscore to
81
+ not break any old code that uses it; but it should not be used
82
+ in new code (and is not listed in __all__)."""
83
+ return list(set(s))
84
+
85
+
86
+ class SourceEntry(object):
87
+ """ single sources.list entry """
88
+
89
+ def __init__(self, line, file=None):
90
+ self.invalid = False # is the source entry valid
91
+ self.disabled = False # is it disabled ('#' in front)
92
+ self.type = "" # what type (deb, deb-src)
93
+ self.architectures = [] # architectures
94
+ self.trusted = None # Trusted
95
+ self.uri = "" # base-uri
96
+ self.dist = "" # distribution (dapper, edgy, etc)
97
+ self.comps = [] # list of available componetns (may empty)
98
+ self.comment = "" # (optional) comment
99
+ self.line = line # the original sources.list line
100
+ if file is None:
101
+ file = apt_pkg.config.find_dir(
102
+ "Dir::Etc") + apt_pkg.config.find("Dir::Etc::sourcelist")
103
+ self.file = file # the file that the entry is located in
104
+ self.parse(line)
105
+ self.template = None # type DistInfo.Suite
106
+ self.children = []
107
+
108
+ def __eq__(self, other):
109
+ """ equal operator for two sources.list entries """
110
+ return (self.disabled == other.disabled and
111
+ self.type == other.type and
112
+ self.uri.rstrip('/') == other.uri.rstrip('/') and
113
+ self.dist == other.dist and
114
+ self.comps == other.comps)
115
+
116
+ def mysplit(self, line):
117
+ """ a split() implementation that understands the sources.list
118
+ format better and takes [] into account (for e.g. cdroms) """
119
+ line = line.strip()
120
+ pieces = []
121
+ tmp = ""
122
+ # we are inside a [..] block
123
+ p_found = False
124
+ space_found = False
125
+ for i in range(len(line)):
126
+ if line[i] == "[":
127
+ if space_found:
128
+ space_found = False
129
+ p_found = True
130
+ pieces.append(tmp)
131
+ tmp = line[i]
132
+ else:
133
+ p_found = True
134
+ tmp += line[i]
135
+ elif line[i] == "]":
136
+ p_found = False
137
+ tmp += line[i]
138
+ elif space_found and not line[i].isspace():
139
+ # we skip one or more space
140
+ space_found = False
141
+ pieces.append(tmp)
142
+ tmp = line[i]
143
+ elif line[i].isspace() and not p_found:
144
+ # found a whitespace
145
+ space_found = True
146
+ else:
147
+ tmp += line[i]
148
+ # append last piece
149
+ if len(tmp) > 0:
150
+ pieces.append(tmp)
151
+ return pieces
152
+
153
+ def parse(self, line):
154
+ """ parse a given sources.list (textual) line and break it up
155
+ into the field we have """
156
+ self.line = line
157
+ line = line.strip()
158
+ # check if the source is enabled/disabled
159
+ if line == "" or line == "#": # empty line
160
+ self.invalid = True
161
+ return
162
+ if line[0] == "#":
163
+ self.disabled = True
164
+ pieces = line[1:].strip().split()
165
+ # if it looks not like a disabled deb line return
166
+ if not pieces[0] in ("rpm", "rpm-src", "deb", "deb-src"):
167
+ self.invalid = True
168
+ return
169
+ else:
170
+ line = line[1:]
171
+ # check for another "#" in the line (this is treated as a comment)
172
+ i = line.find("#")
173
+ if i > 0:
174
+ self.comment = line[i + 1:]
175
+ line = line[:i]
176
+ # source is ok, split it and see what we have
177
+ pieces = self.mysplit(line)
178
+ # Sanity check
179
+ if len(pieces) < 3:
180
+ self.invalid = True
181
+ return
182
+ # Type, deb or deb-src
183
+ self.type = pieces[0].strip()
184
+ # Sanity check
185
+ if self.type not in ("deb", "deb-src", "rpm", "rpm-src"):
186
+ self.invalid = True
187
+ return
188
+
189
+ if pieces[1].strip()[0] == "[":
190
+ options = pieces.pop(1).strip("[]").split()
191
+ for option in options:
192
+ try:
193
+ key, value = option.split("=", 1)
194
+ except Exception:
195
+ self.invalid = True
196
+ else:
197
+ if key == "arch":
198
+ self.architectures = value.split(",")
199
+ elif key == "trusted":
200
+ self.trusted = apt_pkg.string_to_bool(value)
201
+ else:
202
+ self.invalid = True
203
+
204
+ # URI
205
+ self.uri = pieces[1].strip()
206
+ if len(self.uri) < 1:
207
+ self.invalid = True
208
+ # distro and components (optional)
209
+ # Directory or distro
210
+ self.dist = pieces[2].strip()
211
+ if len(pieces) > 3:
212
+ # List of components
213
+ self.comps = pieces[3:]
214
+ else:
215
+ self.comps = []
216
+
217
+ def set_enabled(self, new_value):
218
+ """ set a line to enabled or disabled """
219
+ self.disabled = not new_value
220
+ # enable, remove all "#" from the start of the line
221
+ if new_value:
222
+ self.line = self.line.lstrip().lstrip('#')
223
+ else:
224
+ # disabled, add a "#"
225
+ if self.line.strip()[0] != "#":
226
+ self.line = "#" + self.line
227
+
228
+ def __str__(self):
229
+ """ debug helper """
230
+ return self.str().strip()
231
+
232
+ def str(self):
233
+ """ return the current line as string """
234
+ if self.invalid:
235
+ return self.line
236
+ line = ""
237
+ if self.disabled:
238
+ line = "# "
239
+
240
+ line += self.type
241
+
242
+ if self.architectures and self.trusted is not None:
243
+ line += " [arch=%s trusted=%s]" % (
244
+ ",".join(self.architectures), "yes" if self.trusted else "no")
245
+ elif self.trusted is not None:
246
+ line += " [trusted=%s]" % ("yes" if self.trusted else "no")
247
+ elif self.architectures:
248
+ line += " [arch=%s]" % ",".join(self.architectures)
249
+ line += " %s %s" % (self.uri, self.dist)
250
+ if len(self.comps) > 0:
251
+ line += " " + " ".join(self.comps)
252
+ if self.comment != "":
253
+ line += " #" + self.comment
254
+ line += "\n"
255
+ return line
256
+
257
+
258
+ class NullMatcher(object):
259
+ """ a Matcher that does nothing """
260
+
261
+ def match(self, s):
262
+ return True
263
+
264
+
265
+ class SourcesList(object):
266
+ """ represents the full sources.list + sources.list.d file """
267
+
268
+ def __init__(self,
269
+ withMatcher=True,
270
+ matcherPath="/usr/share/python-apt/templates/"):
271
+ self.list = [] # the actual SourceEntries Type
272
+ if withMatcher:
273
+ self.matcher = SourceEntryMatcher(matcherPath)
274
+ else:
275
+ self.matcher = NullMatcher()
276
+ self.refresh()
277
+
278
+ def refresh(self):
279
+ """ update the list of known entries """
280
+ self.list = []
281
+ # read sources.list
282
+ file = apt_pkg.config.find_file("Dir::Etc::sourcelist")
283
+ if os.path.exists(file):
284
+ self.load(file)
285
+ # read sources.list.d
286
+ partsdir = apt_pkg.config.find_dir("Dir::Etc::sourceparts")
287
+ for file in glob.glob("%s/*.list" % partsdir):
288
+ self.load(file)
289
+ # check if the source item fits a predefined template
290
+ for source in self.list:
291
+ if not source.invalid:
292
+ self.matcher.match(source)
293
+
294
+ def __iter__(self):
295
+ """ simple iterator to go over self.list, returns SourceEntry
296
+ types """
297
+ for entry in self.list:
298
+ yield entry
299
+
300
+ def __find(self, *predicates, **attrs):
301
+ uri = attrs.pop('uri', None)
302
+ for source in self.list:
303
+ if uri and uri.rstrip('/') != source.uri.rstrip('/'):
304
+ continue
305
+ if (all(getattr(source, key) == attrs[key] for key in attrs) and
306
+ all(predicate(source) for predicate in predicates)):
307
+ yield source
308
+
309
+ def add(self, type, uri, dist, orig_comps, comment="", pos=-1, file=None,
310
+ architectures=[]):
311
+ """
312
+ Add a new source to the sources.list.
313
+ The method will search for existing matching repos and will try to
314
+ reuse them as far as possible
315
+ """
316
+
317
+ type = type.strip()
318
+ disabled = type.startswith("#")
319
+ if disabled:
320
+ type = type[1:].lstrip()
321
+ architectures = set(architectures)
322
+ # create a working copy of the component list so that
323
+ # we can modify it later
324
+ comps = orig_comps[:]
325
+ sources = self.__find(lambda s: set(s.architectures) == architectures,
326
+ disabled=disabled, invalid=False, type=type,
327
+ uri=uri, dist=dist)
328
+ # check if we have this source already in the sources.list
329
+ for source in sources:
330
+ for new_comp in comps:
331
+ if new_comp in source.comps:
332
+ # we have this component already, delete it
333
+ # from the new_comps list
334
+ del comps[comps.index(new_comp)]
335
+ if len(comps) == 0:
336
+ return source
337
+
338
+ sources = self.__find(lambda s: set(s.architectures) == architectures,
339
+ invalid=False, type=type, uri=uri, dist=dist)
340
+ for source in sources:
341
+ if source.disabled == disabled:
342
+ # if there is a repo with the same (disabled, type, uri, dist)
343
+ # just add the components
344
+ if set(source.comps) != set(comps):
345
+ source.comps = uniq(source.comps + comps)
346
+ return source
347
+ elif source.disabled and not disabled:
348
+ # enable any matching (type, uri, dist), but disabled repo
349
+ if set(source.comps) == set(comps):
350
+ source.disabled = False
351
+ return source
352
+ # there isn't any matching source, so create a new line and parse it
353
+ parts = [
354
+ "#" if disabled else "",
355
+ type,
356
+ ("[arch=%s]" % ",".join(architectures)) if architectures else "",
357
+ uri,
358
+ dist,
359
+ ]
360
+ parts.extend(comps)
361
+ if comment:
362
+ parts.append("#" + comment)
363
+ line = " ".join(part for part in parts if part) + "\n"
364
+
365
+ new_entry = SourceEntry(line)
366
+ if file is not None:
367
+ new_entry.file = file
368
+ self.matcher.match(new_entry)
369
+ if pos < 0:
370
+ self.list.append(new_entry)
371
+ else:
372
+ self.list.insert(pos, new_entry)
373
+ return new_entry
374
+
375
+ def remove(self, source_entry):
376
+ """ remove the specified entry from the sources.list """
377
+ self.list.remove(source_entry)
378
+
379
+ def restore_backup(self, backup_ext):
380
+ " restore sources.list files based on the backup extension "
381
+ file = apt_pkg.config.find_file("Dir::Etc::sourcelist")
382
+ if os.path.exists(file + backup_ext) and os.path.exists(file):
383
+ shutil.copy(file + backup_ext, file)
384
+ # now sources.list.d
385
+ partsdir = apt_pkg.config.find_dir("Dir::Etc::sourceparts")
386
+ for file in glob.glob("%s/*.list" % partsdir):
387
+ if os.path.exists(file + backup_ext):
388
+ shutil.copy(file + backup_ext, file)
389
+
390
+ def backup(self, backup_ext=None):
391
+ """ make a backup of the current source files, if no backup extension
392
+ is given, the current date/time is used (and returned) """
393
+ already_backuped = set()
394
+ if backup_ext is None:
395
+ backup_ext = time.strftime("%y%m%d.%H%M")
396
+ for source in self.list:
397
+ if (source.file not in already_backuped and
398
+ os.path.exists(source.file)):
399
+ shutil.copy(source.file, "%s%s" % (source.file, backup_ext))
400
+ return backup_ext
401
+
402
+ def load(self, file):
403
+ """ (re)load the current sources """
404
+ try:
405
+ with open(file, "r") as f:
406
+ for line in f:
407
+ source = SourceEntry(line, file)
408
+ self.list.append(source)
409
+ except Exception:
410
+ logging.warning("could not open file '%s'\n" % file)
411
+
412
+ def save(self):
413
+ """ save the current sources """
414
+ files = {}
415
+ # write an empty default config file if there aren't any sources
416
+ if len(self.list) == 0:
417
+ path = apt_pkg.config.find_file("Dir::Etc::sourcelist")
418
+ header = (
419
+ "## See sources.list(5) for more information, especialy\n"
420
+ "# Remember that you can only use http, ftp or file URIs\n"
421
+ "# CDROMs are managed through the apt-cdrom tool.\n")
422
+
423
+ with open(path, "w") as f:
424
+ f.write(header)
425
+ return
426
+
427
+ try:
428
+ for source in self.list:
429
+ if source.file not in files:
430
+ files[source.file] = open(source.file, "w")
431
+ files[source.file].write(source.str())
432
+ finally:
433
+ for f in files:
434
+ files[f].close()
435
+
436
+ def check_for_relations(self, sources_list):
437
+ """get all parent and child channels in the sources list"""
438
+ parents = []
439
+ used_child_templates = {}
440
+ for source in sources_list:
441
+ # try to avoid checking uninterressting sources
442
+ if source.template is None:
443
+ continue
444
+ # set up a dict with all used child templates and corresponding
445
+ # source entries
446
+ if source.template.child:
447
+ key = source.template
448
+ if key not in used_child_templates:
449
+ used_child_templates[key] = []
450
+ temp = used_child_templates[key]
451
+ temp.append(source)
452
+ else:
453
+ # store each source with children aka. a parent :)
454
+ if len(source.template.children) > 0:
455
+ parents.append(source)
456
+ #print self.used_child_templates
457
+ #print self.parents
458
+ return (parents, used_child_templates)
459
+
460
+
461
+ class SourceEntryMatcher(object):
462
+ """ matcher class to make a source entry look nice
463
+ lots of predefined matchers to make it i18n/gettext friendly
464
+ """
465
+
466
+ def __init__(self, matcherPath):
467
+ self.templates = []
468
+ # Get the human readable channel and comp names from the channel .infos
469
+ spec_files = glob.glob("%s/*.info" % matcherPath)
470
+ for f in spec_files:
471
+ f = os.path.basename(f)
472
+ i = f.find(".info")
473
+ f = f[0:i]
474
+ dist = DistInfo(f, base_dir=matcherPath)
475
+ for template in dist.templates:
476
+ if template.match_uri is not None:
477
+ self.templates.append(template)
478
+ return
479
+
480
+ def match(self, source):
481
+ """Add a matching template to the source"""
482
+ found = False
483
+ for template in self.templates:
484
+ if (re.search(template.match_uri, source.uri) and
485
+ re.match(template.match_name, source.dist) and
486
+ # deb is a valid fallback for deb-src (if that is not
487
+ # definied, see #760035
488
+ (source.type == template.type or template.type == "deb")):
489
+ found = True
490
+ source.template = template
491
+ break
492
+ elif (template.is_mirror(source.uri) and
493
+ re.match(template.match_name, source.dist)):
494
+ found = True
495
+ source.template = template
496
+ break
497
+ return found
498
+
499
+
500
+ # some simple tests
501
+ if __name__ == "__main__":
502
+ apt_pkg.init_config()
503
+ sources = SourcesList()
504
+
505
+ for entry in sources:
506
+ logging.info("entry %s" % entry.str())
507
+ #print entry.uri
508
+
509
+ mirror = is_mirror("http://archive.ubuntu.com/ubuntu/",
510
+ "http://de.archive.ubuntu.com/ubuntu/")
511
+ logging.info("is_mirror(): %s" % mirror)
512
+
513
+ logging.info(is_mirror("http://archive.ubuntu.com/ubuntu",
514
+ "http://de.archive.ubuntu.com/ubuntu/"))
515
+ logging.info(is_mirror("http://archive.ubuntu.com/ubuntu/",
516
+ "http://de.archive.ubuntu.com/ubuntu"))
build/data/templates/Blankon.info ADDED
@@ -0,0 +1,370 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ChangelogURI: http://arsip.blankonlinux.or.id/blankon/changelogs/pool/%s/%s/%s/%s_%s/changelog
2
+
3
+ Suite: tambora
4
+ RepositoryType: deb
5
+ BaseURI: http://arsip.blankonlinux.or.id/blankon/
6
+ MatchURI: arsip.blankonlinux.or.id/blankon
7
+ MirrorsFile-amd64: /usr/share/python-apt/templates/Blankon.mirrors
8
+ MirrorsFile-i386: /usr/share/python-apt/templates/Blankon.mirrors
9
+ Description: Blankon 10.0 'Tambora'
10
+ Component: main
11
+ CompDescription: Officially supported
12
+ CompDescriptionLong: Blankon-supported Free/Open Source software
13
+ Component: extras
14
+ CompDescription: Community-maintained
15
+ CompDescriptionLong: Community-maintained Free/Open Source software
16
+ Component: restricted
17
+ CompDescription: Non-free drivers
18
+ CompDescriptionLong: Proprietary drivers for devices
19
+ Component: extras-restricted
20
+ CompDescription: Restricted software
21
+ CompDescriptionLong: Software restricted by copyright or legal issues
22
+
23
+ Suite: tambora
24
+ MatchName: .*
25
+ BaseURI: cdrom:\[Blankon.*10.0
26
+ MatchURI: cdrom:\[Blankon.*10.0
27
+ Description: CDROM with Blankon 10.0 'Tambora'
28
+ Available: False
29
+ Component: main
30
+ CompDescription: Officially supported
31
+ Component: restricted
32
+ CompDescription: Restricted copyright
33
+
34
+ Suite: tambora-security
35
+ ParentSuite: tambora
36
+ RepositoryType: deb
37
+ BaseURI: http://arsip.blankonlinux.or.id/blankon/
38
+ MatchURI: arsip.blankonlinux.or.id/blankon
39
+ Description: Important security updates
40
+
41
+ Suite: tambora-updates
42
+ ParentSuite: tambora
43
+ RepositoryType: deb
44
+ Description: Recommended updates
45
+
46
+ Suite: suroboyo
47
+ RepositoryType: deb
48
+ BaseURI: http://arsip.blankonlinux.or.id/blankon/
49
+ MatchURI: arsip.blankonlinux.or.id/blankon
50
+ MirrorsFile-amd64: /usr/share/python-apt/templates/Blankon.mirrors
51
+ MirrorsFile-i386: /usr/share/python-apt/templates/Blankon.mirrors
52
+ Description: Blankon 9.0 'Suroboyo'
53
+ Component: main
54
+ CompDescription: Officially supported
55
+ CompDescriptionLong: Blankon-supported Free/Open Source software
56
+ Component: extras
57
+ CompDescription: Community-maintained
58
+ CompDescriptionLong: Community-maintained Free/Open Source software
59
+ Component: restricted
60
+ CompDescription: Non-free drivers
61
+ CompDescriptionLong: Proprietary drivers for devices
62
+ Component: extras-restricted
63
+ CompDescription: Restricted software
64
+ CompDescriptionLong: Software restricted by copyright or legal issues
65
+
66
+ Suite: suroboyo
67
+ MatchName: .*
68
+ BaseURI: cdrom:\[Blankon.*9.0
69
+ MatchURI: cdrom:\[Blankon.*9.0
70
+ Description: CDROM with Blankon 9.0 'Suroboyo'
71
+ Available: False
72
+ Component: main
73
+ CompDescription: Officially supported
74
+ Component: restricted
75
+ CompDescription: Restricted copyright
76
+
77
+ Suite: suroboyo-security
78
+ ParentSuite: suroboyo
79
+ RepositoryType: deb
80
+ BaseURI: http://arsip.blankonlinux.or.id/blankon/
81
+ MatchURI: arsip.blankonlinux.or.id/blankon
82
+ Description: Important security updates
83
+
84
+ Suite: suroboyo-updates
85
+ ParentSuite: suroboyo
86
+ RepositoryType: deb
87
+ Description: Recommended updates
88
+
89
+ Suite: rote
90
+ RepositoryType: deb
91
+ BaseURI: http://arsip.blankonlinux.or.id/blankon/
92
+ MatchURI: arsip.blankonlinux.or.id/blankon
93
+ MirrorsFile-amd64: /usr/share/python-apt/templates/Blankon.mirrors
94
+ MirrorsFile-i386: /usr/share/python-apt/templates/Blankon.mirrors
95
+ Description: Blankon 8.0 'Rote'
96
+ Component: main
97
+ CompDescription: Officially supported
98
+ CompDescriptionLong: Blankon-supported Free/Open Source software
99
+ Component: extras
100
+ CompDescription: Community-maintained
101
+ CompDescriptionLong: Community-maintained Free/Open Source software
102
+ Component: restricted
103
+ CompDescription: Non-free drivers
104
+ CompDescriptionLong: Proprietary drivers for devices
105
+ Component: extras-restricted
106
+ CompDescription: Restricted software
107
+ CompDescriptionLong: Software restricted by copyright or legal issues
108
+
109
+ Suite: rote
110
+ MatchName: .*
111
+ BaseURI: cdrom:\[Blankon.*8.0
112
+ MatchURI: cdrom:\[Blankon.*8.0
113
+ Description: CDROM with Blankon 8.0 'Rote'
114
+ Available: False
115
+ Component: main
116
+ CompDescription: Officially supported
117
+ Component: restricted
118
+ CompDescription: Restricted copyright
119
+
120
+ Suite: rote-security
121
+ ParentSuite: rote
122
+ RepositoryType: deb
123
+ BaseURI: http://arsip.blankonlinux.or.id/blankon/
124
+ MatchURI: arsip.blankonlinux.or.id/blankon
125
+ Description: Important security updates
126
+
127
+ Suite: rote-updates
128
+ ParentSuite: rote
129
+ RepositoryType: deb
130
+ Description: Recommended updates
131
+
132
+ Suite: pattimura
133
+ RepositoryType: deb
134
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
135
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
136
+ MirrorsFile-amd64: /usr/share/python-apt/templates/Blankon.mirrors
137
+ MirrorsFile-i386: /usr/share/python-apt/templates/Blankon.mirrors
138
+ Description: Blankon 7.0 'Pattimura'
139
+ Component: main
140
+ CompDescription: Officially supported
141
+ CompDescriptionLong: Blankon-supported Free/Open Source software
142
+ Component: extras
143
+ CompDescription: Community-maintained
144
+ CompDescriptionLong: Community-maintained Free/Open Source software
145
+ Component: restricted
146
+ CompDescription: Non-free drivers
147
+ CompDescriptionLong: Proprietary drivers for devices
148
+ Component: extras-restricted
149
+ CompDescription: Restricted software
150
+ CompDescriptionLong: Software restricted by copyright or legal issues
151
+
152
+ Suite: pattimura
153
+ MatchName: .*
154
+ BaseURI: cdrom:\[Blankon.*7.0
155
+ MatchURI: cdrom:\[Blankon.*7.0
156
+ Description: CDROM with Blankon 7.0 'Pattimura'
157
+ Available: False
158
+ Component: main
159
+ CompDescription: Officially supported
160
+ Component: restricted
161
+ CompDescription: Restricted copyright
162
+
163
+ Suite: pattimura-security
164
+ ParentSuite: pattimura
165
+ RepositoryType: deb
166
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
167
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
168
+ Description: Important security updates
169
+
170
+ Suite: pattimura-updates
171
+ ParentSuite: pattimura
172
+ RepositoryType: deb
173
+ Description: Recommended updates
174
+
175
+ Suite: ombilin
176
+ RepositoryType: deb
177
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
178
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
179
+ MirrorsFile-amd64: /usr/share/python-apt/templates/Blankon.mirrors
180
+ MirrorsFile-i386: /usr/share/python-apt/templates/Blankon.mirrors
181
+ Description: Blankon 6.0 'Ombilin'
182
+ Component: main
183
+ CompDescription: Officially supported
184
+ CompDescriptionLong: Blankon-supported Free/Open Source software
185
+ Component: extras
186
+ CompDescription: Community-maintained
187
+ CompDescriptionLong: Community-maintained Free/Open Source software
188
+ Component: restricted
189
+ CompDescription: Non-free drivers
190
+ CompDescriptionLong: Proprietary drivers for devices
191
+ Component: extras-restricted
192
+ CompDescription: Restricted software
193
+ CompDescriptionLong: Software restricted by copyright or legal issues
194
+
195
+ Suite: ombilin
196
+ MatchName: .*
197
+ BaseURI: cdrom:\[Blankon.*6.0
198
+ MatchURI: cdrom:\[Blankon.*6.0
199
+ Description: CDROM with Blankon 6.0 'Ombilin'
200
+ Available: False
201
+ Component: main
202
+ CompDescription: Officially supported
203
+ Component: restricted
204
+ CompDescription: Restricted copyright
205
+
206
+ Suite: ombilin-security
207
+ ParentSuite: ombilin
208
+ RepositoryType: deb
209
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
210
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
211
+ Description: Important security updates
212
+
213
+ Suite: ombilin-updates
214
+ ParentSuite: ombilin
215
+ RepositoryType: deb
216
+ Description: Recommended updates
217
+
218
+ Suite: nanggar
219
+ RepositoryType: deb
220
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
221
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
222
+ MirrorsFile-amd64: /usr/share/python-apt/templates/Blankon.mirrors
223
+ MirrorsFile-i386: /usr/share/python-apt/templates/Blankon.mirrors
224
+ Description: Blankon 5.0 'Nanggar'
225
+ Component: main
226
+ CompDescription: Officially supported
227
+ CompDescriptionLong: BlankOn-supported Open Source software
228
+ Component: universe
229
+ CompDescription: Community-maintained
230
+ CompDescriptionLong: Community-maintained Open Source software
231
+ Component: restricted
232
+ CompDescription: Non-free drivers
233
+ CompDescriptionLong: Proprietary drivers for devices
234
+ Component: multiverse
235
+ CompDescription: Restricted software
236
+ CompDescriptionLong: Software restricted by copyright or legal issues
237
+
238
+ Suite: nanggar
239
+ MatchName: .*
240
+ BaseURI: cdrom:\[Blankon.*5.0
241
+ MatchURI: cdrom:\[Blankon.*5.0
242
+ Description: Cdrom with Blankon 5.0 'Nanggar'
243
+ Available: False
244
+ Component: main
245
+ CompDescription: Officially supported
246
+ Component: restricted
247
+ CompDescription: Restricted copyright
248
+
249
+ Suite: nanggar-security
250
+ ParentSuite: nanggar
251
+ RepositoryType: deb
252
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
253
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
254
+ Description: Important security updates
255
+
256
+ Suite: nanggar-updates
257
+ ParentSuite: nanggar
258
+ RepositoryType: deb
259
+ Description: Recommended updates
260
+
261
+ Suite: meuligoe
262
+ RepositoryType: deb
263
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
264
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
265
+ MirrorsFile-amd64: /usr/share/python-apt/templates/Blankon.mirrors
266
+ MirrorsFile-i386: /usr/share/python-apt/templates/Blankon.mirrors
267
+ Description: Blankon 4.1 'Meuligoe'
268
+ Component: main
269
+ CompDescription: Officially supported
270
+ CompDescriptionLong: BlankOn-supported Open Source software
271
+ Component: restricted
272
+ CompDescription: Non-free drivers
273
+ CompDescriptionLong: Proprietary drivers for devices
274
+
275
+ Suite: meuligoe
276
+ MatchName: .*
277
+ BaseURI: cdrom:\[Blankon.*4.1
278
+ MatchURI: cdrom:\[Blankon.*4.1
279
+ Description: Cdrom with Blankon 4.1 'Meuligoe'
280
+ Available: False
281
+ Component: main
282
+ CompDescription: Officially supported
283
+ Component: restricted
284
+ CompDescription: Restricted copyright
285
+
286
+ Suite: meuligoe-security
287
+ ParentSuite: meuligoe
288
+ RepositoryType: deb
289
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
290
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
291
+ Description: Important security updates
292
+
293
+ Suite: meuligoe-updates
294
+ ParentSuite: meuligoe
295
+ RepositoryType: deb
296
+ Description: Recommended updates
297
+
298
+ Suite: lontara
299
+ RepositoryType: deb
300
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
301
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
302
+ MirrorsFile-amd64: /usr/share/python-apt/templates/Blankon.mirrors
303
+ MirrorsFile-i386: /usr/share/python-apt/templates/Blankon.mirrors
304
+ Description: BlankOn 3.0 'Lontara'
305
+ Component: main
306
+ CompDescription: Officially supported
307
+ CompDescriptionLong: BlankOn-supported Open Source software
308
+ Component: restricted
309
+ CompDescription: Non-free drivers
310
+ CompDescriptionLong: Proprietary drivers for devices
311
+
312
+ Suite: lontara
313
+ MatchName: .*
314
+ BaseURI: cdrom:\[Blankon.*3.0
315
+ MatchURI: cdrom:\[Blankon.*3.0
316
+ Description: Cdrom with Blankon 3.0 'Lontara'
317
+ Available: False
318
+ Component: main
319
+ CompDescription: Officially supported
320
+ Component: restricted
321
+ CompDescription: Restricted copyright
322
+
323
+ Suite: lontara-security
324
+ ParentSuite: lontara
325
+ RepositoryType: deb
326
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
327
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
328
+ Description: Important security updates
329
+
330
+ Suite: lontara-updates
331
+ ParentSuite: lontara
332
+ RepositoryType: deb
333
+ Description: Recommended updates
334
+
335
+ Suite: konde
336
+ RepositoryType: deb
337
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
338
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
339
+ MirrorsFile-amd64: /usr/share/python-apt/templates/Blankon.mirrors
340
+ MirrorsFile-i386: /usr/share/python-apt/templates/Blankon.mirrors
341
+ Description: Blankon 2.0 'Konde'
342
+ Component: main
343
+ CompDescription: Officially supported
344
+ CompDescriptionLong: BlankOn-supported Open Source software
345
+ Component: restricted
346
+ CompDescription: Non-free drivers
347
+ CompDescriptionLong: Proprietary drivers for devices
348
+
349
+ Suite: konde
350
+ MatchName: .*
351
+ BaseURI: cdrom:\[Blankon.*2.0
352
+ MatchURI: cdrom:\[Blankon.*2.0
353
+ Description: Cdrom with Blankon 2.0 'Konde'
354
+ Available: False
355
+ Component: main
356
+ CompDescription: Officially supported
357
+ Component: restricted
358
+ CompDescription: Restricted copyright
359
+
360
+ Suite: konde-security
361
+ ParentSuite: konde
362
+ RepositoryType: deb
363
+ BaseURI: http://arsip.blankonlinux.or.id/blankon-legacy/
364
+ MatchURI: arsip.blankonlinux.or.id/blankon-legacy
365
+ Description: Important security updates
366
+
367
+ Suite: konde-updates
368
+ ParentSuite: konde
369
+ RepositoryType: deb
370
+ Description: Recommended updates
build/data/templates/Blankon.mirrors ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #LOC:ID
2
+ http://kambing.ui.ac.id/blankon/
3
+ http://mirror.omadata.com/blankon/
4
+ http://repo.ugm.ac.id/repo/blankon/
5
+ http://buaya.klas.or.id/blankon/
6
+ http://bos.fkip.uns.ac.id/blankon
7
+ http://pandawa.ipb.ac.id/blankon/
8
+ http://dl2.foss-id.web.id/blankon/
9
+ http://shol.vlsm.org/blankon/
10
+ http://openstorage.gunadarma.ac.id/blankon/
11
+ http://debian.rab.co.id/blankon/
12
+ http://singo.ub.ac.id/blankon/
13
+ http://ftp.paudni.kemdiknas.go.id/blankon/
14
+ http://blankon.idrepo.or.id/blankon/
15
+ http://mirror.kioss.undip.ac.id/blankon/
16
+ http://repo.unnes.ac.id/repo/blankon/
17
+ http://kartolo.sby.datautama.net.id/blankon/
build/data/templates/Debian.info ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ChangelogURI: http://packages.debian.org/changelogs/pool/%s/%s/%s/%s_%s/changelog
2
+ X-Exclude-Suites: buzz, rex, bo, hamm, slink, potato, woody, experimental
3
+
4
+ Suite: {series}
5
+ RepositoryType: deb
6
+ BaseURI: http://deb.debian.org/debian/
7
+ MatchURI: ((http|ftp)[0-9]*\.([a-z]*\.){{0,1}}|deb\.|httpredir\.)debian\.org
8
+ MirrorsFile: Debian.mirrors
9
+ Description: Debian {version} '{codename}'
10
+ Component: main
11
+ CompDescription: Officially supported
12
+ Component: contrib
13
+ CompDescription: DFSG-compatible Software with Non-Free Dependencies
14
+ Component: non-free
15
+ CompDescription: Non-DFSG-compatible Software
16
+
17
+ Suite: {series}-security
18
+ RepositoryType: deb
19
+ BaseURI: http://security.debian.org/
20
+ MatchURI: security\.debian\.org
21
+ ParentSuite: {series}
22
+ Description: Security updates
23
+ X-Version: ge 11
24
+
25
+ Suite: {series}/updates
26
+ RepositoryType: deb
27
+ BaseURI: http://security.debian.org/
28
+ MatchURI: security\.debian\.org
29
+ ParentSuite: {series}
30
+ Description: Security updates
31
+ X-Version: le 10
32
+
33
+ Suite: {series}-updates
34
+ RepositoryType: deb
35
+ ParentSuite: {series}
36
+ Description: Recommended updates
37
+ X-Version: ge 7
38
+
39
+ Suite: {series}-proposed-updates
40
+ RepositoryType: deb
41
+ ParentSuite: {series}
42
+ Description: Proposed updates
43
+
44
+ Suite: stable
45
+ RepositoryType: deb
46
+ BaseURI: http://http.us.debian.org/debian/
47
+ MatchURI: ftp[0-9]*\.([a-z]*\.){0,1}debian\.org
48
+ MirrorsFile: Debian.mirrors
49
+ Description: Debian current stable release
50
+ Component: main
51
+ CompDescription: Officially supported
52
+ Component: contrib
53
+ CompDescription: DFSG-compatible Software with Non-Free Dependencies
54
+ Component: non-free
55
+ CompDescription: Non-DFSG-compatible Software
56
+
57
+ Suite: testing
58
+ RepositoryType: deb
59
+ BaseURI: http://http.us.debian.org/debian/
60
+ MatchURI: ftp[0-9]*\.([a-z]*\.){0,1}debian\.org
61
+ MirrorsFile: Debian.mirrors
62
+ Description: Debian testing
63
+ Component: main
64
+ CompDescription: Officially supported
65
+ Component: contrib
66
+ CompDescription: DFSG-compatible Software with Non-Free Dependencies
67
+ Component: non-free
68
+ CompDescription: Non-DFSG-compatible Software
69
+
70
+ Suite: sid
71
+ RepositoryType: deb
72
+ BaseURI: http://http.us.debian.org/debian/
73
+ MatchURI: ftp[0-9]*\.([a-z]*\.){0,1}debian\.org
74
+ MirrorsFile: Debian.mirrors
75
+ Description: Debian 'Sid' (unstable)
76
+ Component: main
77
+ CompDescription: Officially supported
78
+ Component: contrib
79
+ CompDescription: DFSG-compatible Software with Non-Free Dependencies
80
+ Component: non-free
81
+ CompDescription: Non-DFSG-compatible Software
82
+
83
+ Suite: unstable
84
+ RepositoryType: deb
85
+ BaseURI: http://http.us.debian.org/debian/
86
+ MatchURI: ftp[0-9]*\.([a-z]*\.){0,1}debian\.org
87
+ MirrorsFile: Debian.mirrors
88
+ Description: Debian 'Sid' (unstable)
89
+ Component: main
90
+ CompDescription: Officially supported
91
+ Component: contrib
92
+ CompDescription: DFSG-compatible Software with Non-Free Dependencies
93
+ Component: non-free
94
+ CompDescription: Non-DFSG-compatible Software
95
+
build/data/templates/Debian.mirrors ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #LOC:AM
2
+ http://ftp.am.debian.org/debian/
3
+ http://mirrors.asnet.am/debian/
4
+ #LOC:AR
5
+ http://debian.unnoba.edu.ar/debian/
6
+ http://mirror.sitsa.com.ar/debian/
7
+ #LOC:AT
8
+ http://debian.anexia.at/debian/
9
+ http://debian.lagis.at/debian/
10
+ http://debian.mur.at/debian/
11
+ http://debian.sil.at/debian/
12
+ http://ftp.at.debian.org/debian/
13
+ http://ftp.tu-graz.ac.at/mirror/debian/
14
+ http://mirror.alwyzon.net/debian/
15
+ #LOC:AU
16
+ http://debian.mirror.digitalpacific.com.au/debian/
17
+ http://debian.mirror.serversaustralia.com.au/debian/
18
+ http://ftp.au.debian.org/debian/
19
+ http://mirror.aarnet.edu.au/debian/
20
+ http://mirror.amaze.com.au/debian/
21
+ http://mirror.linux.org.au/debian/
22
+ http://mirror.overthewire.com.au/debian/
23
+ http://mirror.realcompute.io/debian/
24
+ #LOC:BE
25
+ http://ftp.be.debian.org/debian/
26
+ http://ftp.belnet.be/debian/
27
+ http://mirror.as35701.net/debian/
28
+ #LOC:BG
29
+ http://debian.ipacct.com/debian/
30
+ http://debian.mnet.bg/debian/
31
+ http://debian.telecoms.bg/debian/
32
+ http://ftp.bg.debian.org/debian/
33
+ http://ftp.uni-sofia.bg/debian/
34
+ http://mirror.telepoint.bg/debian/
35
+ http://mirrors.netix.net/debian/
36
+ #LOC:BR
37
+ http://alcateia.ufscar.br/debian/
38
+ http://debian.c3sl.ufpr.br/debian/
39
+ http://ftp.br.debian.org/debian/
40
+ http://mirror.uepg.br/debian/
41
+ #LOC:BY
42
+ http://ftp.by.debian.org/debian/
43
+ http://ftp.byfly.by/debian/
44
+ http://mirror.datacenter.by/debian/
45
+ #LOC:CA
46
+ http://debian.mirror.iweb.ca/debian/
47
+ http://mirror.csclub.uwaterloo.ca/debian/
48
+ http://mirror.estone.ca/debian/
49
+ http://mirror.it.ubc.ca/debian/
50
+ #LOC:CH
51
+ http://debian.ethz.ch/debian/
52
+ http://ftp.ch.debian.org/debian/
53
+ http://mirror.init7.net/debian/
54
+ http://mirror.iway.ch/debian/
55
+ http://mirror.sinavps.ch/debian/
56
+ http://mirror1.infomaniak.com/debian/
57
+ http://mirror2.infomaniak.com/debian/
58
+ #LOC:CL
59
+ http://debian.redlibre.cl/debian/
60
+ http://ftp.cl.debian.org/debian/
61
+ http://mirror.insacom.cl/debian/
62
+ http://mirror.ufro.cl/debian/
63
+ #LOC:CN
64
+ http://mirror.bjtu.edu.cn/debian/
65
+ http://mirror.nju.edu.cn/debian/
66
+ http://mirror.sjtu.edu.cn/debian/
67
+ http://mirrors.163.com/debian/
68
+ http://mirrors.bfsu.edu.cn/debian/
69
+ http://mirrors.hit.edu.cn/debian/
70
+ http://mirrors.neusoft.edu.cn/debian/
71
+ http://mirrors.tuna.tsinghua.edu.cn/debian/
72
+ http://mirrors.ustc.edu.cn/debian/
73
+ #LOC:CR
74
+ http://debianmirror.una.ac.cr/debian/
75
+ http://mirrors.ucr.ac.cr/debian/
76
+ #LOC:CZ
77
+ http://debian.mirror.web4u.cz/
78
+ http://debian.superhosting.cz/debian/
79
+ http://ftp.cvut.cz/debian/
80
+ http://ftp.cz.debian.org/debian/
81
+ http://ftp.debian.cz/debian/
82
+ http://ftp.sh.cvut.cz/debian/
83
+ http://ftp.zcu.cz/debian/
84
+ http://merlin.fit.vutbr.cz/debian/
85
+ http://mirror.dkm.cz/debian/
86
+ http://mirror.it4i.cz/debian/
87
+ http://mirrors.nic.cz/debian/
88
+ #LOC:DE
89
+ http://artfiles.org/debian/
90
+ http://de.mirrors.clouvider.net/debian/
91
+ http://debian.charite.de/debian/
92
+ http://debian.inf.tu-dresden.de/debian/
93
+ http://debian.intergenia.de/debian/
94
+ http://debian.mirror.iphh.net/debian/
95
+ http://debian.mirror.lrz.de/debian/
96
+ http://debian.netcologne.de/debian/
97
+ http://debian.tu-bs.de/debian/
98
+ http://ftp-stud.hs-esslingen.de/debian/
99
+ http://ftp.de.debian.org/debian/
100
+ http://ftp.fau.de/debian/
101
+ http://ftp.gwdg.de/debian/
102
+ http://ftp.hosteurope.de/mirror/ftp.debian.org/debian/
103
+ http://ftp.plusline.net/debian/
104
+ http://ftp.tu-chemnitz.de/debian/
105
+ http://ftp.tu-clausthal.de/debian/
106
+ http://ftp.uni-bayreuth.de/debian/
107
+ http://ftp.uni-hannover.de/debian/debian/
108
+ http://ftp.uni-kl.de/debian/
109
+ http://ftp.uni-mainz.de/debian/
110
+ http://ftp.wrz.de/debian/
111
+ http://mirror.23m.com/debian/
112
+ http://mirror.de.leaseweb.net/debian/
113
+ http://mirror.dogado.de/debian/
114
+ http://mirror.ipb.de/debian/
115
+ http://mirror.netzwerge.de/debian/
116
+ http://mirror.united-gameserver.de/debian/
117
+ http://mirror.wtnet.de/debian/
118
+ http://mirrors.xtom.de/debian/
119
+ http://packages.hs-regensburg.de/debian/
120
+ http://pubmirror.plutex.de/debian/
121
+ #LOC:DK
122
+ http://ftp.dk.debian.org/debian/
123
+ http://mirror.asergo.com/debian/
124
+ http://mirror.one.com/debian/
125
+ http://mirrors.dotsrc.org/debian/
126
+ http://mirrors.rackhosting.com/debian/
127
+ #LOC:EE
128
+ http://mirrors.xtom.ee/debian/
129
+ #LOC:ES
130
+ http://debian.grn.cat/debian/
131
+ http://debian.redimadrid.es/debian/
132
+ http://debian.redparra.com/debian/
133
+ http://debian.uvigo.es/debian/
134
+ http://ftp.caliu.cat/debian/
135
+ http://ftp.cica.es/debian/
136
+ http://ftp.es.debian.org/debian/
137
+ http://ftp.udc.es/debian/
138
+ http://mirror.librelabucm.org/debian/
139
+ http://repo.ifca.es/debian/
140
+ http://softlibre.unizar.es/debian/
141
+ http://ulises.hostalia.com/debian/
142
+ #LOC:FI
143
+ http://ftp.fi.debian.org/debian/
144
+ http://www.nic.funet.fi/debian/
145
+ #LOC:FR
146
+ http://apt.tetaneutral.net/debian/
147
+ http://deb-mir1.naitways.net/debian/
148
+ http://debian.apt-mirror.de/debian/
149
+ http://debian.obspm.fr/debian/
150
+ http://debian.polytech-lille.fr/debian/
151
+ http://debian.proxad.net/debian/
152
+ http://debian.univ-tlse2.fr/debian/
153
+ http://ftp.ec-m.fr/debian/
154
+ http://ftp.fr.debian.org/debian/
155
+ http://ftp.lip6.fr/pub/linux/distributions/debian/
156
+ http://ftp.rezopole.net/debian/
157
+ http://ftp.u-picardie.fr/debian/
158
+ http://ftp.u-strasbg.fr/debian/
159
+ http://ftp.univ-pau.fr/linux/mirrors/debian/
160
+ http://miroir.univ-lorraine.fr/debian/
161
+ http://mirror.johnnybegood.fr/debian/
162
+ http://mirror.plusserver.com/debian/debian/
163
+ http://mirrors.ircam.fr/pub/debian/
164
+ #LOC:GB
165
+ http://debian.mirror.uk.sargasso.net/debian/
166
+ http://debian.mirrors.uk2.net/debian/
167
+ http://free.hands.com/debian/
168
+ http://ftp.ticklers.org/debian/
169
+ http://ftp.uk.debian.org/debian/
170
+ http://mirror.cov.ukservers.com/debian/
171
+ http://mirror.lchost.net/debian/
172
+ http://mirror.mythic-beasts.com/debian/
173
+ http://mirror.ox.ac.uk/debian/
174
+ http://mirror.positive-internet.com/debian/
175
+ http://mirror.sov.uk.goscomb.net/debian/
176
+ http://mirrors.coreix.net/debian/
177
+ http://mirrorservice.org/sites/ftp.debian.org/debian/
178
+ http://uk.mirrors.clouvider.net/debian/
179
+ http://ukdebian.mirror.anlx.net/debian/
180
+ #LOC:GE
181
+ http://debian.grena.ge/debian/
182
+ #LOC:GR
183
+ http://debian.otenet.gr/debian/
184
+ #LOC:HK
185
+ http://ftp.hk.debian.org/debian/
186
+ http://mirror.xtom.com.hk/debian/
187
+ #LOC:HR
188
+ http://debian.carnet.hr/debian/
189
+ http://debian.iskon.hr/debian/
190
+ http://ftp.hr.debian.org/debian/
191
+ #LOC:HU
192
+ http://ftp.bme.hu/debian/
193
+ http://ftp.fsn.hu/debian/
194
+ http://ftp.hu.debian.org/debian/
195
+ http://repo.jztkft.hu/debian/
196
+ #LOC:ID
197
+ http://kartolo.sby.datautama.net.id/debian/
198
+ http://kebo.pens.ac.id/debian/
199
+ http://mirror.unair.ac.id/debian/
200
+ http://mr.heru.id/debian/
201
+ #LOC:IL
202
+ http://debian.interhost.co.il/debian/
203
+ #LOC:IN
204
+ http://mirror.cse.iitk.ac.in/debian/
205
+ #LOC:IR
206
+ http://archive.debian.petiak.ir/debian/
207
+ http://debian.asis.ai/debian/
208
+ http://debian.hostiran.ir/debian/
209
+ http://debian.parspack.com/debian/
210
+ http://mirror.aminidc.com/debian/
211
+ http://mirrors.pardisco.co/debian/
212
+ #LOC:IS
213
+ http://ftp.is.debian.org/debian/
214
+ #LOC:IT
215
+ http://debian.connesi.it/debian/
216
+ http://debian.mirror.garr.it/debian/
217
+ http://ftp.it.debian.org/debian/
218
+ http://ftp.linux.it/debian/
219
+ http://giano.com.dist.unige.it/debian/
220
+ http://mirror.units.it/debian/
221
+ #LOC:JP
222
+ http://debian-mirror.sakura.ne.jp/debian/
223
+ http://ftp.jp.debian.org/debian/
224
+ http://ftp.riken.jp/Linux/debian/debian/
225
+ http://mirrors.xtom.jp/debian/
226
+ #LOC:KE
227
+ http://debian.mirror.ac.ke/debian/
228
+ http://debian.mirror.liquidtelecom.com/debian/
229
+ #LOC:KR
230
+ http://ftp.kaist.ac.kr/debian/
231
+ http://ftp.kr.debian.org/debian/
232
+ http://ftp.lanet.kr/debian/
233
+ http://mirror.anigil.com/debian/
234
+ #LOC:KZ
235
+ http://mirror.hoster.kz/debian/
236
+ http://mirror.ps.kz/debian/
237
+ #LOC:LT
238
+ http://debian.balt.net/debian/
239
+ http://debian.mirror.vu.lt/debian/
240
+ http://ftp.lt.debian.org/debian/
241
+ http://mirror.litnet.lt/debian/
242
+ http://mirror.vpsnet.com/debian/
243
+ #LOC:LU
244
+ http://debian.mirror.root.lu/debian/
245
+ #LOC:LV
246
+ http://debian.koyanet.lv/debian/
247
+ http://mirror.cloudhosting.lv/debian/
248
+ #LOC:MD
249
+ http://ftp.md.debian.org/debian/
250
+ http://mirror.as43289.net/debian/
251
+ http://mirrors.mivocloud.com/debian/
252
+ #LOC:MK
253
+ http://mirror.onevip.mk/debian/
254
+ #LOC:NC
255
+ http://mirror.lagoon.nc/debian/
256
+ #LOC:NL
257
+ http://debian.mirror.cambrium.nl/debian/
258
+ http://debian.snt.utwente.nl/debian/
259
+ http://ftp.nl.debian.org/debian/
260
+ http://mirror.duocast.net/debian/
261
+ http://mirror.i3d.net/debian/
262
+ http://mirror.nforce.com/debian/
263
+ http://mirror.nl.datapacket.com/debian/
264
+ http://mirror.nl.leaseweb.net/debian/
265
+ http://mirror.seedvps.com/debian/
266
+ http://mirrors.xtom.nl/debian/
267
+ http://nl.mirrors.clouvider.net/debian/
268
+ #LOC:NO
269
+ http://ftp.no.debian.org/debian/
270
+ http://ftp.uio.no/debian/
271
+ #LOC:NZ
272
+ http://ftp.nz.debian.org/debian/
273
+ http://mirror.fsmg.org.nz/debian/
274
+ #LOC:PL
275
+ http://debian.inhost.pro/debian/
276
+ http://ftp.agh.edu.pl/debian/
277
+ http://ftp.icm.edu.pl/pub/Linux/debian/
278
+ http://ftp.pl.debian.org/debian/
279
+ http://ftp.psnc.pl/debian/
280
+ http://ftp.task.gda.pl/debian/
281
+ #LOC:PT
282
+ http://ftp.eq.uc.pt/software/Linux/debian/
283
+ http://ftp.rnl.tecnico.ulisboa.pt/pub/debian/
284
+ http://mirrors.ptisp.pt/debian/
285
+ http://mirrors.up.pt/debian/
286
+ #LOC:RE
287
+ http://debian.mithril.re/debian/
288
+ http://depot-debian.univ-reunion.fr/debian/
289
+ #LOC:RO
290
+ http://mirror.linux.ro/debian/
291
+ http://mirrors.hostico.ro/debian/
292
+ http://mirrors.nav.ro/debian/
293
+ http://mirrors.nxthost.com/debian/
294
+ #LOC:RS
295
+ http://mirror.pmf.kg.ac.rs/debian/
296
+ #LOC:RU
297
+ http://ftp.psn.ru/debian/
298
+ http://ftp.ru.debian.org/debian/
299
+ http://mirror.corbina.net/debian/
300
+ http://mirror.docker.ru/debian/
301
+ http://mirror.mephi.ru/debian/
302
+ http://mirror.surf/debian/
303
+ http://mirror.truenetwork.ru/debian/
304
+ http://mirrors.powernet.com.ru/debian/
305
+ #LOC:SE
306
+ http://debian.lth.se/debian/
307
+ http://debian.mirror.su.se/debian/
308
+ http://ftp.acc.umu.se/debian/
309
+ http://ftp.se.debian.org/debian/
310
+ http://ftpmirror1.infania.net/debian/
311
+ http://mirror.zetup.net/debian/
312
+ http://mirrors.glesys.net/debian/
313
+ #LOC:SG
314
+ http://mirror.coganng.com/debian/
315
+ http://mirror.sg.gs/debian/
316
+ #LOC:SI
317
+ http://ftp.si.debian.org/debian/
318
+ #LOC:SK
319
+ http://ftp.antik.sk/debian/
320
+ http://ftp.debian.sk/debian/
321
+ http://ftp.sk.debian.org/debian/
322
+ #LOC:TH
323
+ http://ftp.debianclub.org/debian/
324
+ http://mirror.applebred.net/debian/
325
+ http://mirror.kku.ac.th/debian/
326
+ #LOC:TR
327
+ http://debian.gnu.gen.tr/debian/
328
+ http://ftp.linux.org.tr/debian/
329
+ http://ftp.tr.debian.org/debian/
330
+ #LOC:TW
331
+ http://debian.cs.nycu.edu.tw/debian/
332
+ http://debian.csie.ncku.edu.tw/debian/
333
+ http://debian.csie.ntu.edu.tw/debian/
334
+ http://ftp.tw.debian.org/debian/
335
+ http://opensource.nchc.org.tw/debian/
336
+ http://tw1.mirror.blendbyte.net/debian/
337
+ #LOC:UA
338
+ http://debian.astra.in.ua/debian/
339
+ http://debian.netforce.hosting/debian/
340
+ http://debian.volia.net/debian/
341
+ http://fastmirror.pp.ua/debian/
342
+ http://mirror.mirohost.net/debian/
343
+ #LOC:US
344
+ http://atl.mirrors.clouvider.net/debian/
345
+ http://debian-archive.trafficmanager.net/debian/
346
+ http://debian.cs.binghamton.edu/debian/
347
+ http://debian.osuosl.org/debian/
348
+ http://ftp.us.debian.org/debian/
349
+ http://la.mirrors.clouvider.net/debian/
350
+ http://mirror.clarkson.edu/debian/
351
+ http://mirror.cogentco.com/debian/
352
+ http://mirror.dal.nexril.net/debian/
353
+ http://mirror.keystealth.org/debian/
354
+ http://mirror.siena.edu/debian/
355
+ http://mirror.steadfast.net/debian/
356
+ http://mirror.us.leaseweb.net/debian/
357
+ http://mirror.us.oneandone.net/debian/
358
+ http://mirrors.accretive-networks.net/debian/
359
+ http://mirrors.lug.mtu.edu/debian/
360
+ http://mirrors.namecheap.com/debian/
361
+ http://mirrors.ocf.berkeley.edu/debian/
362
+ http://mirrors.vcea.wsu.edu/debian/
363
+ http://mirrors.wikimedia.org/debian/
364
+ http://nyc.mirrors.clouvider.net/debian/
365
+ #LOC:VN
366
+ http://debian.xtdv.net/debian/
367
+ http://mirror.bizflycloud.vn/debian/
368
+ http://mirrors.bkns.vn/debian/
369
+ #LOC:ZA
370
+ http://debian.saix.net/
371
+ http://ftp.is.co.za/debian/
build/data/templates/Kali.info ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ChangelogURI: http://pkg.kali.org/media/packages/%s/%s/%s/%s/changelog-%s
2
+
3
+ Suite: kali-rolling
4
+ RepositoryType: deb
5
+ BaseURI: http://http.kali.org/kali/
6
+ MatchURI: (kali\.download|(archive.*|http)\.kali\.org)
7
+ MirrorsFile: Kali.mirrors
8
+ Description: Kali Rolling
9
+ Component: main
10
+ CompDescription: Officially supported
11
+ Component: contrib
12
+ CompDescription: DFSG-compatible Software with Non-Free Dependencies
13
+ Component: non-free
14
+ CompDescription: Non-DFSG-compatible Software
15
+
16
+ Suite: kali-dev
17
+ RepositoryType: deb
18
+ BaseURI: http://http.kali.org/kali/
19
+ MatchURI: (kali\.download|(archive.*|http)\.kali\.org)
20
+ MirrorsFile: Kali.mirrors
21
+ Description: Kali Development Release
22
+ Component: main
23
+ CompDescription: Officially supported
24
+ Component: contrib
25
+ CompDescription: DFSG-compatible Software with Non-Free Dependencies
26
+ Component: non-free
27
+ CompDescription: Non-DFSG-compatible Software
28
+
29
+ Suite: kali-experimental
30
+ RepositoryType: deb
31
+ ParentSuite: kali-dev
32
+ Description: Kali Experimental Release
build/data/templates/Kali.mirrors ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ http://http.kali.org/kali/
2
+ http://kali.download/kali/
build/data/templates/Tanglu.info ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ChangelogURI: http://changelogs.tanglu.org/changelogs/%s/%s/%s/%s_%s/changelog
2
+
3
+ Suite: aequorea
4
+ RepositoryType: deb
5
+ BaseURI: http://archive.tanglu.org/tanglu/
6
+ MatchURI: archive.tanglu.org/tanglu
7
+ MirrorsFile: Tanglu.mirrors
8
+ Description: Tanglu 1.0 'Aequorea'
9
+ Component: main
10
+ CompDescription: Officially supported
11
+ Component: contrib
12
+ CompDescription: DFSG-compatible Software with Non-Free Dependencies
13
+ Component: non-free
14
+ CompDescription: Non-DFSG-compatible Software
15
+
16
+ Suite: aequorea-updates
17
+ RepositoryType: deb
18
+ ParentSuite: aequorea
19
+ Description: Recommended updates
20
+
21
+ Suite: bartholomea
22
+ RepositoryType: deb
23
+ BaseURI: http://archive.tanglu.org/tanglu/
24
+ MatchURI: archive.tanglu.org/tanglu
25
+ MirrorsFile: Tanglu.mirrors
26
+ Description: Tanglu 2.0 'Bartholomea'
27
+ Component: main
28
+ CompDescription: Officially supported
29
+ Component: contrib
30
+ CompDescription: DFSG-compatible Software with Non-Free Dependencies
31
+ Component: non-free
32
+ CompDescription: Non-DFSG-compatible Software
33
+
34
+ Suite: bartholomea-security
35
+ RepositoryType: deb
36
+ ParentSuite: bartholomea
37
+ Description: Security updates
38
+
39
+ Suite: bartholomea-updates
40
+ RepositoryType: deb
41
+ ParentSuite: bartholomea
42
+ Description: Recommended updates
build/data/templates/Tanglu.mirrors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ #LOC:DE
2
+ http://de.archive.tanglu.org/tanglu/
3
+ http://mirror1.hs-esslingen.de/pub/Mirrors/archive.tanglu.org/tanglu/
build/data/templates/Ubuntu.info ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ChangelogURI: http://changelogs.ubuntu.com/changelogs/pool/%s/%s/%s/%s_%s/changelog
2
+
3
+ Suite: devel
4
+ RepositoryType: deb
5
+ BaseURI: http://ports.ubuntu.com/ubuntu-ports/
6
+ MatchURI: ports.ubuntu.com/ubuntu-ports
7
+ BaseURI-amd64: http://archive.ubuntu.com/ubuntu
8
+ MatchURI-amd64: archive.ubuntu.com/ubuntu
9
+ BaseURI-i386: http://archive.ubuntu.com/ubuntu
10
+ MatchURI-i386: archive.ubuntu.com/ubuntu
11
+ MirrorsFile-amd64: Ubuntu.mirrors
12
+ MirrorsFile-i386: Ubuntu.mirrors
13
+ Description: Ubuntu development series
14
+ Component: main
15
+ CompDescription: Officially supported
16
+ CompDescriptionLong: Canonical-supported free and open-source software
17
+ Component: universe
18
+ CompDescription: Community-maintained
19
+ CompDescriptionLong: Community-maintained free and open-source software
20
+ Component: restricted
21
+ CompDescription: Non-free drivers
22
+ CompDescriptionLong: Proprietary drivers for devices
23
+ Component: multiverse
24
+ ParentComponent: universe
25
+ CompDescription: Restricted software
26
+ CompDescriptionLong: Software restricted by copyright or legal issues
27
+
28
+ Suite: devel
29
+ ParentSuite: devel
30
+ RepositoryType: deb-src
31
+ BaseURI: http://archive.ubuntu.com/ubuntu/
32
+ MatchURI: archive.ubuntu.com/ubuntu|ports.ubuntu.com/ubuntu-ports
33
+ Description: Ubuntu development series
34
+
35
+ Suite: devel
36
+ Official: false
37
+ RepositoryType: deb
38
+ BaseURI: http://extras.ubuntu.com
39
+ MatchURI: extras.ubuntu.com
40
+ Description: Independent
41
+ Component: main
42
+ CompDescription: Provided by third-party software developers
43
+ CompDescriptionLong: Software offered by third party developers.
44
+
45
+ Suite: devel-security
46
+ ParentSuite: devel
47
+ RepositoryType: deb
48
+ BaseURI: http://ports.ubuntu.com/ubuntu-ports/
49
+ MatchURI: ports.ubuntu.com/ubuntu-ports
50
+ BaseURI-amd64: http://security.ubuntu.com/ubuntu/
51
+ MatchURI-amd64: archive.ubuntu.com/ubuntu|security.ubuntu.com
52
+ BaseURI-i386: http://security.ubuntu.com/ubuntu/
53
+ MatchURI-i386: archive.ubuntu.com/ubuntu|security.ubuntu.com
54
+ Description: Important security updates
55
+
56
+ Suite: devel-security
57
+ ParentSuite: devel
58
+ RepositoryType: deb-src
59
+ BaseURI: http://archive.ubuntu.com/ubuntu/
60
+ MatchURI: archive.ubuntu.com/ubuntu|ports.ubuntu.com/ubuntu-ports|security.ubuntu.com
61
+ Description: Important security updates
62
+
63
+ Suite: devel-updates
64
+ ParentSuite: devel
65
+ RepositoryType: deb
66
+ Description: Recommended updates
67
+
68
+ Suite: devel-updates
69
+ ParentSuite: devel
70
+ RepositoryType: deb-src
71
+ BaseURI: http://archive.ubuntu.com/ubuntu/
72
+ MatchURI: archive.ubuntu.com/ubuntu|ports.ubuntu.com/ubuntu-ports
73
+ Description: Recommended updates
74
+
75
+ Suite: devel-proposed
76
+ ParentSuite: devel
77
+ RepositoryType: deb
78
+ Description: Pre-released updates
79
+
80
+ Suite: devel-proposed
81
+ ParentSuite: devel
82
+ RepositoryType: deb-src
83
+ BaseURI: http://archive.ubuntu.com/ubuntu/
84
+ MatchURI: archive.ubuntu.com/ubuntu|ports.ubuntu.com/ubuntu-ports
85
+ Description: Pre-released updates
86
+
87
+ Suite: devel-backports
88
+ ParentSuite: devel
89
+ RepositoryType: deb
90
+ Description: Unsupported updates
91
+
92
+ Suite: devel-backports
93
+ ParentSuite: devel
94
+ RepositoryType: deb-src
95
+ BaseURI: http://archive.ubuntu.com/ubuntu/
96
+ MatchURI: archive.ubuntu.com/ubuntu|ports.ubuntu.com/ubuntu-ports
97
+ Description: Unsupported updates
98
+
99
+ Suite: {series}
100
+ RepositoryType: deb
101
+ BaseURI: http://ports.ubuntu.com/ubuntu-ports/
102
+ MatchURI: ports.ubuntu.com/ubuntu-ports
103
+ BaseURI-amd64: http://archive.ubuntu.com/ubuntu
104
+ MatchURI-amd64: archive.ubuntu.com/ubuntu
105
+ BaseURI-i386: http://archive.ubuntu.com/ubuntu
106
+ MatchURI-i386: archive.ubuntu.com/ubuntu
107
+ MirrorsFile-amd64: Ubuntu.mirrors
108
+ MirrorsFile-i386: Ubuntu.mirrors
109
+ Description: Ubuntu {version} '{codename}'
110
+ Component: main
111
+ CompDescription: Officially supported
112
+ CompDescriptionLong: Canonical-supported free and open-source software
113
+ Component: universe
114
+ CompDescription: Community-maintained
115
+ CompDescriptionLong: Community-maintained free and open-source software
116
+ Component: restricted
117
+ CompDescription: Non-free drivers
118
+ CompDescriptionLong: Proprietary drivers for devices
119
+ Component: multiverse
120
+ ParentComponent: universe
121
+ CompDescription: Restricted software
122
+ CompDescriptionLong: Software restricted by copyright or legal issues
123
+
124
+ Suite: {series}
125
+ ParentSuite: {series}
126
+ RepositoryType: deb-src
127
+ BaseURI: http://archive.ubuntu.com/ubuntu/
128
+ MatchURI: archive.ubuntu.com/ubuntu|ports.ubuntu.com/ubuntu-ports
129
+ Description: Ubuntu {version} '{codename}'
130
+ X-Version: ge 11.04
131
+
132
+ Suite: {series}
133
+ RepositoryType: deb
134
+ MatchName: .*
135
+ BaseURI: cdrom:\[Ubuntu.*{version}
136
+ MatchURI: cdrom:\[Ubuntu.*{version}
137
+ Description: Installation medium with Ubuntu {version} '{codename}'
138
+ Available: False
139
+ Component: main
140
+ CompDescription: Officially supported
141
+ Component: restricted
142
+ CompDescription: Restricted copyright
143
+
144
+ Suite: {series}
145
+ Official: false
146
+ RepositoryType: deb
147
+ BaseURI: http://archive.canonical.com
148
+ MatchURI: archive.canonical.com
149
+ Description: Canonical Partners
150
+ Component: partner
151
+ CompDescription: Software packaged by Canonical for their partners
152
+ CompDescriptionLong: This software is not part of Ubuntu.
153
+ X-Version: le 20.04, ge 10.10
154
+
155
+ Suite: {series}
156
+ Official: false
157
+ RepositoryType: deb
158
+ BaseURI: http://extras.ubuntu.com
159
+ MatchURI: extras.ubuntu.com
160
+ Description: Independent
161
+ Component: main
162
+ CompDescription: Provided by third-party software developers
163
+ CompDescriptionLong: Software offered by third party developers.
164
+ X-Version: le 15.04, ge 10.10
165
+
166
+ Suite: {series}-security
167
+ ParentSuite: {series}
168
+ RepositoryType: deb
169
+ BaseURI: http://ports.ubuntu.com/ubuntu-ports/
170
+ MatchURI: ports.ubuntu.com/ubuntu-ports
171
+ BaseURI-amd64: http://security.ubuntu.com/ubuntu/
172
+ MatchURI-amd64: archive.ubuntu.com/ubuntu|security.ubuntu.com
173
+ BaseURI-i386: http://security.ubuntu.com/ubuntu/
174
+ MatchURI-i386: archive.ubuntu.com/ubuntu|security.ubuntu.com
175
+ Description: Important security updates
176
+
177
+ Suite: {series}-security
178
+ ParentSuite: {series}
179
+ RepositoryType: deb-src
180
+ BaseURI: http://archive.ubuntu.com/ubuntu/
181
+ MatchURI: archive.ubuntu.com/ubuntu|ports.ubuntu.com/ubuntu-ports|security.ubuntu.com
182
+ Description: Important security updates
183
+ X-Version: ge 11.04
184
+
185
+ Suite: {series}-updates
186
+ ParentSuite: {series}
187
+ RepositoryType: deb
188
+ Description: Recommended updates
189
+
190
+ Suite: {series}-updates
191
+ ParentSuite: {series}
192
+ RepositoryType: deb-src
193
+ BaseURI: http://archive.ubuntu.com/ubuntu/
194
+ MatchURI: archive.ubuntu.com/ubuntu|ports.ubuntu.com/ubuntu-ports
195
+ Description: Recommended updates
196
+ X-Version: ge 11.04
197
+
198
+ Suite: {series}-proposed
199
+ ParentSuite: {series}
200
+ RepositoryType: deb
201
+ Description: Pre-released updates
202
+
203
+ Suite: {series}-proposed
204
+ ParentSuite: {series}
205
+ RepositoryType: deb-src
206
+ BaseURI: http://archive.ubuntu.com/ubuntu/
207
+ MatchURI: archive.ubuntu.com/ubuntu|ports.ubuntu.com/ubuntu-ports
208
+ Description: Pre-released updates
209
+ X-Version: ge 11.04
210
+
211
+ Suite: {series}-backports
212
+ ParentSuite: {series}
213
+ RepositoryType: deb
214
+ Description: Unsupported updates
215
+
216
+ Suite: {series}-backports
217
+ ParentSuite: {series}
218
+ RepositoryType: deb-src
219
+ BaseURI: http://archive.ubuntu.com/ubuntu/
220
+ MatchURI: archive.ubuntu.com/ubuntu|ports.ubuntu.com/ubuntu-ports
221
+ Description: Unsupported updates
222
+ X-Version: ge 11.04
223
+
build/data/templates/Ubuntu.mirrors ADDED
@@ -0,0 +1,659 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ mirror://mirrors.ubuntu.com/mirrors.txt
2
+ #LOC:AM
3
+ http://mirrors.asnet.am/ubuntu/
4
+ http://ubuntu.mirror.gnc.am/ubuntu/
5
+ #LOC:AR
6
+ http://mirrors.eze.sysarmy.com/ubuntu/
7
+ http://ubuntu.unc.edu.ar/ubuntu/
8
+ https://mirror.sitsa.com.ar/ubuntu/
9
+ https://mirrors.dc.clear.net.ar/ubuntu/
10
+ https://ubuntu.zero.com.ar/ubuntu/
11
+ #LOC:AT
12
+ http://mirror.easyname.at/ubuntu-archive/
13
+ http://ubuntu.anexia.at/ubuntu/
14
+ http://ubuntu.lagis.at/ubuntu/
15
+ http://ubuntu.uni-klu.ac.at/ubuntu/
16
+ https://mirror.alwyzon.net/ubuntu/
17
+ #LOC:AU
18
+ http://ftp.iinet.net.au/pub/ubuntu/
19
+ http://mirror.aarnet.edu.au/pub/ubuntu/archive/
20
+ http://mirror.internode.on.net/pub/ubuntu/ubuntu/
21
+ http://mirror.netspace.net.au/pub/ubuntu/
22
+ http://mirror.overthewire.com.au/ubuntu/
23
+ https://mirror.datamossa.io/ubuntu/archive/
24
+ https://mirror.internet.asn.au/pub/ubuntu/archive/
25
+ https://mirror.realcompute.io/ubuntu/
26
+ https://ubuntu.mirror.digitalpacific.com.au/archive/
27
+ https://ubuntu.mirror.serversaustralia.com.au/ubuntu/
28
+ #LOC:AZ
29
+ http://aze.archive.ubuntu.com/ubuntu/
30
+ https://mirror.hostart.az/Ubuntu/
31
+ https://mirror.yer.az/ubuntu/archive/
32
+ #LOC:BA
33
+ http://archive.ubuntu.mirror.ba/ubuntu/
34
+ #LOC:BD
35
+ http://mirror.dhakacom.com/ubuntu-archive/
36
+ http://mirror.xeonbd.com/ubuntu-archive/
37
+ https://mirror.limda.net/Ubuntu/
38
+ #LOC:BE
39
+ http://ftp.belnet.be/ubuntu/
40
+ http://mirror.unix-solutions.be/ubuntu/
41
+ https://mirrors-ubuntu.behostings.com/ubuntu/
42
+ #LOC:BG
43
+ http://mirror.bg.host.ag/ubuntu/
44
+ http://mirrors.neterra.net/ubuntu/
45
+ http://ubuntu.ipacct.com/ubuntu/
46
+ https://mirror.telepoint.bg/ubuntu/
47
+ https://mirrors.storpool.com/ubuntu/archive/
48
+ #LOC:BR
49
+ http://mirror.ufam.edu.br/ubuntu/
50
+ http://mirror.ufscar.br/ubuntu/
51
+ http://mirror.unesp.br/ubuntu/
52
+ http://sft.if.usp.br/ubuntu/
53
+ http://ubuntu-archive.locaweb.com.br/ubuntu/
54
+ http://ubuntu.c3sl.ufpr.br/ubuntu/
55
+ http://ubuntu.mti.mt.gov.br/
56
+ https://mirror.uepg.br/ubuntu/
57
+ https://ubuntu.itsbrasil.net/ubuntu/
58
+ https://ubuntu.letscloud.io/ubuntu/
59
+ #LOC:BY
60
+ http://ftp.byfly.by/ubuntu/
61
+ http://mirror.datacenter.by/ubuntu/
62
+ #LOC:CA
63
+ ftp://ftp.cs.mun.ca/pub/mirror/ubuntu/
64
+ http://archive.ubuntu.mirror.rafal.ca/ubuntu/
65
+ http://gpl.savoirfairelinux.net/pub/mirrors/ubuntu/
66
+ http://mirror.ca-tr.kamatera.com/ubuntu/
67
+ http://mirror.its.dal.ca/ubuntu/
68
+ http://mirror.rcg.sfu.ca/mirror/ubuntu/
69
+ http://mirrors.layeronline.com/ubuntu/
70
+ http://ubuntu.bhs.mirrors.ovh.net/ubuntu/
71
+ http://ubuntu.mirror.globo.tech/
72
+ http://ubuntu.mirror.iweb.ca/
73
+ http://ubuntu.mirror.rafal.ca/ubuntu/
74
+ https://mirror.0xem.ma/ubuntu/
75
+ https://mirror.csclub.uwaterloo.ca/ubuntu/
76
+ https://mirror.hep.gg/ubuntu/
77
+ https://mirror.it.ubc.ca/ubuntu/
78
+ https://mirror.reenigne.net/ubuntu/
79
+ https://mirror01-pcl-ott1.accuris.ca/ubuntu/
80
+ https://mirrors.switch.ca/ubuntu/
81
+ https://muug.ca/mirror/ubuntu/
82
+ #LOC:CD
83
+ https://mirrors.united.cd/ubuntu/
84
+ #LOC:CH
85
+ http://archive.ubuntu.csg.uzh.ch/ubuntu/
86
+ http://mirror.infomaniak.ch/ubuntu/
87
+ http://pkg.adfinis.com/ubuntu/
88
+ http://ubuntu.ethz.ch/ubuntu/
89
+ https://mirror.init7.net/ubuntu/
90
+ https://mirror.solnet.ch/ubuntu/
91
+ #LOC:CL
92
+ http://mirror.uchile.cl/ubuntu/
93
+ https://mirror.hnd.cl/ubuntu/
94
+ https://mirror.ufro.cl/ubuntu/
95
+ #LOC:CN
96
+ http://ftp.sjtu.edu.cn/ubuntu/
97
+ http://mirror.lzu.edu.cn/ubuntu/
98
+ http://mirrors.aliyun.com/ubuntu/
99
+ http://mirrors.cn99.com/ubuntu/
100
+ http://mirrors.cqu.edu.cn/ubuntu/
101
+ http://mirrors.huaweicloud.com/repository/ubuntu/
102
+ http://mirrors.sohu.com/ubuntu/
103
+ http://mirrors.yun-idc.com/ubuntu/
104
+ https://mirror.bjtu.edu.cn/ubuntu/
105
+ https://mirror.nju.edu.cn/ubuntu/
106
+ https://mirror.nyist.edu.cn/ubuntu/
107
+ https://mirrors.bfsu.edu.cn/ubuntu/
108
+ https://mirrors.bupt.edu.cn/ubuntu/
109
+ https://mirrors.cloud.tencent.com/ubuntu/
110
+ https://mirrors.cnnic.cn/ubuntu/
111
+ https://mirrors.hit.edu.cn/ubuntu/
112
+ https://mirrors.jlu.edu.cn/ubuntu/
113
+ https://mirrors.sdu.edu.cn/ubuntu/
114
+ https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/
115
+ https://mirrors.tuna.tsinghua.edu.cn/ubuntu/
116
+ https://mirrors.ustc.edu.cn/ubuntu/
117
+ https://mirrors.xjtu.edu.cn/ubuntu/
118
+ https://mirrors.zju.edu.cn/ubuntu/
119
+ https://repo.huaweicloud.com/ubuntu/
120
+ #LOC:CO
121
+ http://mirror.unimagdalena.edu.co/ubuntu/
122
+ https://edgeuno-bog2.mm.fcix.net/ubuntu/
123
+ #LOC:CR
124
+ http://ubuntu.ucr.ac.cr/ubuntu/
125
+ #LOC:CY
126
+ http://mirror.library.ucy.ac.cy/linux/ubuntu/archive/
127
+ #LOC:CZ
128
+ http://ftp.cvut.cz/ubuntu/
129
+ http://ftp.linux.cz/pub/linux/ubuntu/
130
+ http://ucho.ignum.cz/ubuntu/
131
+ https://cz.archive.ubuntu.com/ubuntu/
132
+ https://ftp.sh.cvut.cz/ubuntu/
133
+ https://mirror.dkm.cz/ubuntu/
134
+ https://mirror.it4i.cz/ubuntu/
135
+ #LOC:DE
136
+ ftp://ftp.fu-berlin.de/linux/ubuntu/
137
+ http://artfiles.org/ubuntu.com/
138
+ http://ftp-stud.hs-esslingen.de/ubuntu/
139
+ http://ftp.fau.de/ubuntu/
140
+ http://ftp.halifax.rwth-aachen.de/ubuntu/
141
+ http://ftp.hosteurope.de/mirror/archive.ubuntu.com/
142
+ http://ftp.rz.tu-bs.de/pub/mirror/ubuntu-packages/
143
+ http://ftp.stw-bonn.de/ubuntu/
144
+ http://ftp.tu-chemnitz.de/pub/linux/ubuntu-ports/
145
+ http://ftp.tu-chemnitz.de/pub/linux/ubuntu/
146
+ http://ftp.tu-ilmenau.de/mirror/ubuntu/
147
+ http://ftp.uni-bayreuth.de/linux/ubuntu/ubuntu/
148
+ http://ftp.uni-kl.de/pub/linux/ubuntu/
149
+ http://ftp.uni-mainz.de/ubuntu/
150
+ http://ftp5.gwdg.de/pub/linux/debian/ubuntu/
151
+ http://mirror.daniel-jost.net/ubuntu/
152
+ http://mirror.eu-fr.kamatera.com/ubuntu/
153
+ http://mirror.funkfreundelandshut.de/ubuntu/
154
+ http://mirror.ipb.de/ubuntu/
155
+ http://mirror.kamp.de/ubuntu/
156
+ http://mirror.serverloft.eu/ubuntu/ubuntu/
157
+ http://mirror.wtnet.de/ubuntu/
158
+ http://mirror2.tuxinator.org/ubuntu/
159
+ http://packages.oth-regensburg.de/ubuntu/
160
+ http://suse.uni-leipzig.de/pub/releases.ubuntu.com/ubuntu/
161
+ http://ubuntu.mirror.lrz.de/ubuntu/
162
+ http://ubuntu.mirror.tudos.de/ubuntu/
163
+ https://de.mirrors.clouvider.net/ubuntu/
164
+ https://debian.charite.de/ubuntu/
165
+ https://ftp.uni-stuttgart.de/ubuntu/
166
+ https://mirror.23m.com/ubuntu/
167
+ https://mirror.creoline.net/ubuntu/
168
+ https://mirror.cxserv.de/ubuntu/
169
+ https://mirror.de.leaseweb.net/ubuntu/
170
+ https://mirror.dogado.de/ubuntu/
171
+ https://mirror.kkg.berlin/ubuntu/
172
+ https://mirror.netcologne.de/ubuntu/
173
+ https://mirror.netzwerge.de/ubuntu/
174
+ https://mirror.scaleuptech.com/ubuntu/
175
+ https://mirror.united-gameserver.de/ubuntu/
176
+ https://mirrors.xtom.de/ubuntu/
177
+ #LOC:DK
178
+ http://ftp.klid.dk/ftp/ubuntu/
179
+ http://mirror.one.com/ubuntu/
180
+ http://mirrors.dotsrc.org/ubuntu/
181
+ https://mirror.asergo.com/ubuntu/
182
+ https://mirror.netsite.dk/ubuntu/archive/
183
+ https://mirrors.c0urier.net/linux/ubuntu/
184
+ #LOC:EC
185
+ http://mirror.cedia.org.ec/ubuntu/
186
+ http://mirror.espol.edu.ec/ubuntu/
187
+ #LOC:EE
188
+ http://ftp.aso.ee/ubuntu/
189
+ https://mirrors.xtom.ee/ubuntu/
190
+ #LOC:ES
191
+ http://dafi.inf.um.es/ubuntu/
192
+ http://ftp.caliu.cat/pub/distribucions/ubuntu/archive/
193
+ http://ftp.udc.es/ubuntu/
194
+ http://mirror.tedra.es/ubuntu/
195
+ http://softlibre.unizar.es/ubuntu/archive/
196
+ http://ubuntu.cica.es/ubuntu/
197
+ http://ubuntu.grn.cat/ubuntu/
198
+ https://ftp.csuc.cat/ubuntu/archive/
199
+ https://labs.eif.urjc.es/mirror/ubuntu/
200
+ https://ubuntu.uvigo.es/
201
+ #LOC:FI
202
+ http://mirrors.nic.funet.fi/ubuntu/
203
+ https://mirror.5i.fi/ubuntu/
204
+ #LOC:FR
205
+ http://distrib-coffee.ipsl.jussieu.fr/pub/linux/ubuntu/
206
+ http://miroir.univ-lorraine.fr/ubuntu/
207
+ http://mirror.plusserver.com/ubuntu/ubuntu/
208
+ http://mirrors.ircam.fr/pub/ubuntu/archive/
209
+ http://ubuntu.mirror.serverloft.de/ubuntu/
210
+ http://ubuntu.mirrors.ovh.net/ubuntu/
211
+ http://ubuntu.univ-nantes.fr/ubuntu/
212
+ http://ubuntu.univ-reims.fr/ubuntu/
213
+ https://ftp.u-picardie.fr/mirror/ubuntu/ubuntu/
214
+ https://mirror.johnnybegood.fr/ubuntu/
215
+ https://mirror.ubuntu.ikoula.com/
216
+ https://ubuntu.lafibre.info/ubuntu/
217
+ https://www-ftp.lip6.fr/pub/linux/distributions/Ubuntu/archive/
218
+ #LOC:GB
219
+ http://archive.ubuntu.com/ubuntu/
220
+ http://mirror.as29550.net/archive.ubuntu.com/
221
+ http://mirror.bytemark.co.uk/ubuntu/
222
+ http://mirror.cov.ukservers.com/ubuntu/
223
+ http://mirror.eu-lo.kamatera.com/ubuntu/
224
+ http://mirror.freethought-internet.co.uk/ubuntu/
225
+ http://mirror.mythic-beasts.com/ubuntu/
226
+ http://mirror.ox.ac.uk/sites/archive.ubuntu.com/ubuntu/
227
+ http://mirror.sov.uk.goscomb.net/ubuntu/
228
+ http://mirror.vorboss.net/ubuntu-archive/
229
+ http://mirrors.coreix.net/ubuntu/
230
+ http://mirrors.melbourne.co.uk/ubuntu/
231
+ http://mirrors.ukfast.co.uk/sites/archive.ubuntu.com/
232
+ http://ubuntu.mirrors.uk2.net/ubuntu/
233
+ http://ubuntu.positive-internet.com/ubuntu/
234
+ http://www.mirrorservice.org/sites/archive.ubuntu.com/ubuntu/
235
+ https://mirror.pulsant.com/sites/ubuntu-archive/
236
+ https://mirror.vinehost.net/ubuntu/
237
+ https://mirrors.gethosted.online/ubuntu/
238
+ https://uk.mirrors.clouvider.net/ubuntu/
239
+ #LOC:GE
240
+ http://ubuntu.grena.ge/ubuntu/
241
+ #LOC:GL
242
+ http://mirror.greennet.gl/ubuntu/
243
+ #LOC:GR
244
+ http://ftp.cc.uoc.gr/mirrors/linux/ubuntu/packages/
245
+ http://ftp.ntua.gr/ubuntu/
246
+ http://ubuntu.otenet.gr/
247
+ #LOC:HK
248
+ http://hk.mirrors.thegigabit.com/ubuntu/
249
+ http://mirror-hk.koddos.net/ubuntu/
250
+ http://mirror.as.kamatera.com/ubuntu/
251
+ http://www.ubuntu.org.tw/
252
+ https://mirror.xtom.com.hk/ubuntu/
253
+ #LOC:HR
254
+ http://ubuntu.grad.hr/ubuntu/
255
+ #LOC:HU
256
+ http://repo.jztkft.hu/ubuntu/
257
+ https://mirror.niif.hu/ubuntu/
258
+ https://mirrors.sth.sze.hu/ubuntu/
259
+ https://quantum-mirror.hu/mirrors/pub/ubuntu/
260
+ #LOC:ID
261
+ http://kartolo.sby.datautama.net.id/ubuntu/
262
+ http://kebo.pens.ac.id/ubuntu/
263
+ http://mirror.beon.co.id/ubuntu/
264
+ http://mirror.biznetgio.com/ubuntu/
265
+ http://mirror.cepatcloud.id/ubuntu/
266
+ http://mirror.cloudxchange.id/ubuntu/
267
+ http://mirror.deace.id/ubuntu/
268
+ http://mirror.poliwangi.ac.id/ubuntu/
269
+ http://mirror.telkomuniversity.ac.id/ubuntu/
270
+ http://mirror.unej.ac.id/ubuntu/
271
+ http://repo.ugm.ac.id/ubuntu/
272
+ http://suro.ubaya.ac.id/ubuntu/
273
+ https://buaya.klas.or.id/ubuntu/
274
+ https://linux.domainesia.com/ubuntu/ubuntu-archive/
275
+ https://mirror.amscloud.co.id/ubuntu/
276
+ https://mirror.citraix.net/ubuntu/
277
+ https://mirror.dewabiz.com/ubuntu/
278
+ https://mirror.faizuladib.com/ubuntu/
279
+ https://mirror.gi.co.id/ubuntu/
280
+ https://mirror.nevacloud.com/ubuntu/ubuntu-archive/
281
+ https://mirror.papua.go.id/ubuntu/
282
+ https://mirror.repository.id/ubuntu/
283
+ https://mirror.unair.ac.id/ubuntu/
284
+ https://mirrors.idcloudhost.com/ubuntu/
285
+ https://mr.heru.id/ubuntu/
286
+ https://repo.usk.ac.id/ubuntu/
287
+ https://sby.mirror.bignet.id/ubuntu/
288
+ #LOC:IE
289
+ http://ftp.heanet.ie/pub/ubuntu/
290
+ https://mirror.webworld.ie/ubuntu/
291
+ #LOC:IL
292
+ http://mirror.il-jr.kamatera.com/ubuntu/
293
+ http://mirror.il-pt.kamatera.com/ubuntu/
294
+ http://mirror.il-rh.kamatera.com/ubuntu/
295
+ http://mirror.il-ta.kamatera.com/ubuntu/
296
+ http://mirror.il.kamatera.com/ubuntu/
297
+ http://mirror.isoc.org.il/pub/ubuntu/
298
+ http://rep-ubuntu-il.upress.io/ubuntu/
299
+ #LOC:IN
300
+ http://ftp.iitm.ac.in/ubuntu/
301
+ http://mirror.cse.iitk.ac.in/ubuntu/
302
+ http://mirrors.piconets.webwerks.in/ubuntu-mirror/ubuntu/
303
+ http://repos.del.extreme-ix.org/ubuntu/
304
+ http://ubuntu.hbcse.tifr.res.in/ubuntu/
305
+ https://in.mirror.coganng.com/ubuntu-ports/
306
+ https://in.mirror.coganng.com/ubuntu/
307
+ https://mirrors.nxtgen.com/ubuntu-mirror/ubuntu/
308
+ https://repo.extreme-ix.org/ubuntu/
309
+ https://ubuntu-archive.mirror.net.in/
310
+ https://ubuntu-ports.mirror.net.in/
311
+ #LOC:IR
312
+ http://archive.ubuntu.asiatech.ir/
313
+ http://mirror.aminidc.com/ubuntu/
314
+ http://mirror.faraso.org/ubuntu/
315
+ http://repo.iut.ac.ir/repo/Ubuntu/
316
+ http://ubuntu.byteiran.com/ubuntu/
317
+ https://archive.ubuntu.petiak.ir/ubuntu/
318
+ https://ir.ubuntu.sindad.cloud/ubuntu/
319
+ https://mirror.0-1.cloud/ubuntu/
320
+ https://mirror.iranserver.com/ubuntu/
321
+ https://mirror.rasanegar.com/ubuntu/
322
+ https://mirrors.pardisco.co/ubuntu/
323
+ https://ubuntu-mirror.kimiahost.com/
324
+ https://ubuntu.bardia.tech/
325
+ https://ubuntu.hostiran.ir/ubuntuarchive/
326
+ https://ubuntu.shatel.ir/ubuntu/
327
+ #LOC:IS
328
+ http://ubuntu.hysing.is/ubuntu/
329
+ https://is.mirror.flokinet.net/ubuntu/
330
+ https://mirrors.opensource.is/ubuntu/
331
+ #LOC:IT
332
+ http://giano.com.dist.unige.it/ubuntu/
333
+ https://it1.mirror.vhosting-it.com/ubuntu/
334
+ https://it2.mirror.vhosting-it.com/ubuntu/
335
+ https://ubuntu.mirror.garr.it/ubuntu/
336
+ #LOC:JP
337
+ http://archive.g4t1.pro/ubuntu/
338
+ http://ftp.jaist.ac.jp/pub/Linux/ubuntu/
339
+ http://ftp.riken.jp/Linux/ubuntu/
340
+ http://ftp.tsukuba.wide.ad.jp/Linux/ubuntu/
341
+ http://mirror.fairway.ne.jp/ubuntu/
342
+ http://ubuntutym.u-toyama.ac.jp/ubuntu/
343
+ http://www.ftp.ne.jp/Linux/packages/ubuntu/archive/
344
+ https://ftp.udx.icscoe.jp/Linux/ubuntu/
345
+ https://jp.mirror.coganng.com/ubuntu-ports/
346
+ https://jp.mirror.coganng.com/ubuntu/
347
+ https://linux.yz.yamagata-u.ac.jp/ubuntu/
348
+ https://mirror.nishi.network/ubuntu-ports/
349
+ https://mirror.nishi.network/ubuntu/
350
+ #LOC:KG
351
+ http://mir.linux.kg/ubuntu/
352
+ #LOC:KH
353
+ https://mirror.sabay.com.kh/ubuntu/
354
+ #LOC:KR
355
+ http://ftp.daum.net/ubuntu/
356
+ https://devpg.net/ubuntu/
357
+ https://ftp.lanet.kr/ubuntu-ports/
358
+ https://ftp.lanet.kr/ubuntu/
359
+ https://mirror.elice.io/ubuntu/
360
+ https://mirror.hserver.kr/ubuntu/
361
+ https://mirror.kakao.com/ubuntu/
362
+ https://mirror.yuki.net.uk/ubuntu-ports/
363
+ https://mirror.yuki.net.uk/ubuntu/
364
+ #LOC:KZ
365
+ http://mirror.hoster.kz/ubuntu/
366
+ http://mirror.neolabs.kz/ubuntu/
367
+ http://mirror.ps.kz/ubuntu/
368
+ #LOC:LT
369
+ http://ftp.litnet.lt/ubuntu/
370
+ http://ubuntu-archive.mirror.serveriai.lt/
371
+ http://ubuntu.mirror.vu.lt/ubuntu/
372
+ #LOC:LU
373
+ http://ubuntu.mirror.root.lu/ubuntu/
374
+ #LOC:LV
375
+ http://mirror.cloudhosting.lv/ubuntu/
376
+ http://ubuntu-arch.linux.edu.lv/ubuntu/
377
+ http://ubuntu.koyanet.lv/ubuntu/
378
+ #LOC:MA
379
+ https://mirror.marwan.ma/ubuntu/
380
+ #LOC:MD
381
+ http://mirror.as43289.net/ubuntu/
382
+ http://mirrors.mivocloud.com/ubuntu/
383
+ #LOC:MG
384
+ http://ubuntu.dts.mg/ubuntu/
385
+ #LOC:MK
386
+ http://mirror.onevip.mk/ubuntu/
387
+ http://mirror.t-home.mk/ubuntu/
388
+ #LOC:MN
389
+ http://mirror.datacenter.mn/ubuntu/
390
+ #LOC:MU
391
+ https://ubuntu-mirror.cloud.mu/ubuntu-ports/
392
+ https://ubuntu-mirror.cloud.mu/ubuntu/
393
+ #LOC:MY
394
+ http://my.mirrors.thegigabit.com/ubuntu/
395
+ http://ubuntu.mirror.myduniahost.com/ubuntu/
396
+ http://ubuntu.tuxuri.com/ubuntu/
397
+ https://mirrors.gbnetwork.com/ubuntu/
398
+ https://mirrors.ipserverone.com/ubuntu/
399
+ #LOC:NA
400
+ http://download.nust.na/pub/ubuntu/ubuntu/
401
+ #LOC:NC
402
+ http://archive.ubuntu.nautile.nc/ubuntu/
403
+ http://ubuntu.lagoon.nc/ubuntu/
404
+ #LOC:NL
405
+ ftp://ftpserv.tudelft.nl/pub/Linux/archive.ubuntu.com/
406
+ http://ftp.nluug.nl/os/Linux/distr/ubuntu/
407
+ http://ftp.snt.utwente.nl/pub/os/linux/ubuntu/
408
+ http://ftp.tudelft.nl/archive.ubuntu.com/
409
+ http://mirror.eu.kamatera.com/ubuntu/
410
+ http://mirror.hostnet.nl/ubuntu/archive/
411
+ http://mirror.nforce.com/pub/linux/ubuntu/
412
+ http://mirror.nl.datapacket.com/ubuntu/
413
+ http://mirror.previder.nl/ubuntu/
414
+ http://mirror.serverion.com/ubuntu/
415
+ http://mirror.serverius.net/ubuntu/
416
+ http://mirror.transip.net/ubuntu/ubuntu/
417
+ http://mirror.vpgrp.io/ubuntu/
418
+ http://nl.archive.ubuntu.com/ubuntu/
419
+ http://nl3.archive.ubuntu.com/ubuntu/
420
+ http://osmirror.rug.nl/ubuntu/
421
+ http://ubuntu.mirror.cambrium.nl/ubuntu/
422
+ http://ubuntu.mirror.true.nl/ubuntu/
423
+ https://mirror.lyrahosting.com/ubuntuarchive/
424
+ https://mirror.nl.altushost.com/ubuntu/
425
+ https://mirror.nl.leaseweb.net/ubuntu/
426
+ https://mirrors.evoluso.com/ubuntu/
427
+ https://mirrors.hostiserver.com/ubuntu/
428
+ https://mirrors.xtom.nl/ubuntu/
429
+ https://nl.mirrors.clouvider.net/ubuntu/
430
+ https://ubuntu.mirror.wearetriple.com/archive/
431
+ #LOC:NO
432
+ http://ftp.uninett.no/ubuntu/
433
+ http://no.archive.ubuntu.com/ubuntu/
434
+ http://no.mirrors.blix.com/ubuntu/
435
+ http://ubuntu.uib.no/archive/
436
+ https://ubuntu.hi.no/archive/
437
+ #LOC:NP
438
+ http://ntc.net.np/ubuntu/
439
+ http://ubuntu.ntc.net.np/ubuntu/
440
+ #LOC:NZ
441
+ http://mirror.fsmg.org.nz/ubuntu/
442
+ http://ubuntu.mirrors.theom.nz/
443
+ http://ucmirror.canterbury.ac.nz/ubuntu/
444
+ https://mirror.2degrees.nz/ubuntu/
445
+ #LOC:PH
446
+ http://mirror.pregi.net/ubuntu/
447
+ http://mirror.rise.ph/ubuntu/
448
+ #LOC:PL
449
+ http://ftp.agh.edu.pl/ubuntu/
450
+ http://ftp.icm.edu.pl/pub/Linux/ubuntu/
451
+ http://ftp.vectranet.pl/ubuntu/
452
+ http://ubuntu.man.lodz.pl/ubuntu/
453
+ http://ubuntu.task.gda.pl/ubuntu/
454
+ https://ftp.psnc.pl/linux/ubuntu/
455
+ #LOC:PR
456
+ http://mirrors.upr.edu/ubuntu/
457
+ #LOC:PT
458
+ http://archive.ubuntumirror.dei.uc.pt/ubuntu/
459
+ http://ftp.rnl.tecnico.ulisboa.pt/pub/ubuntu/archive/
460
+ http://glua.ua.pt/pub/ubuntu/
461
+ http://mirrors.up.pt/ubuntu/
462
+ https://mirrors.ptisp.pt/ubuntu/
463
+ #LOC:RO
464
+ http://mirrors.nxthost.com/ubuntu/
465
+ http://mirrors.pidginhost.com/ubuntu/
466
+ https://mirror.efect.ro/ubuntu/archive/
467
+ https://mirror.flokinet.net/ubuntu/
468
+ https://mirrors.chroot.ro/ubuntu/
469
+ https://mirrors.hostico.ro/ubuntu/archive/
470
+ https://mirrors.nav.ro/ubuntu/
471
+ https://ubuntu-mirror.magnetic-it.com/ubuntu/
472
+ https://ubuntu.mirrors.orange.ro/ubuntu/
473
+ #LOC:RU
474
+ http://mirror.corbina.net/ubuntu/
475
+ http://mirror.docker.ru/ubuntu/
476
+ http://mirror.hyperdedic.ru/ubuntu/
477
+ http://mirror.logol.ru/ubuntu/
478
+ http://mirror.timeweb.ru/ubuntu/
479
+ http://mirror.yandex.ru/ubuntu/
480
+ http://mirrors.powernet.com.ru/ubuntu/
481
+ https://mirror.linux-ia64.org/ubuntu/
482
+ https://mirror.truenetwork.ru/ubuntu/
483
+ #LOC:SA
484
+ https://mirrors.isu.net.sa/apt-mirror/
485
+ #LOC:SE
486
+ http://ftp.acc.umu.se/ubuntu/
487
+ http://ftp.lysator.liu.se/ubuntu/
488
+ http://mirror.zetup.net/ubuntu/
489
+ http://ubuntu.mirror.su.se/ubuntu/
490
+ https://ftpmirror1.infania.net/ubuntu/
491
+ https://mirror.bahnhof.net/ubuntu/
492
+ https://mirror.se.altushost.com/ubuntu/
493
+ #LOC:SG
494
+ http://0ms.run/mirrors/ftp.udx.icscoe.jp/Linux/ubuntu/
495
+ http://mirror.aktkn.sg/ubuntu/
496
+ http://mirror.sg.gs/ubuntu/
497
+ http://mirror.soonkeat.sg/ubuntu/
498
+ http://ossmirror.mycloud.services/os/linux/ubuntu/
499
+ https://mirror.coganng.com/ubuntu-ports/
500
+ https://mirror.coganng.com/ubuntu/
501
+ #LOC:SI
502
+ http://ftp.arnes.si/pub/mirrors/ubuntu/
503
+ #LOC:SK
504
+ http://ftp.energotel.sk/pub/linux/ubuntu/
505
+ http://tux.rainside.sk/ubuntu/
506
+ https://mirror.vnet.sk/ubuntu/
507
+ #LOC:TH
508
+ http://mirror.thaidns.co.th/ubuntu/
509
+ http://mirror1.ku.ac.th/ubuntu/
510
+ http://mirror1.totbb.net/ubuntu/
511
+ http://mirrors.psu.ac.th/ubuntu/
512
+ https://mirror.kku.ac.th/ubuntu/
513
+ https://mirrors.nipa.cloud/ubuntu/
514
+ #LOC:TR
515
+ http://mirror.kapteyan.com.tr/ubuntu/
516
+ http://mirror.ni.net.tr/ubuntu/
517
+ http://ubuntu.turhost.com/ubuntu/
518
+ http://ubuntu.vargonen.com/ubuntu/
519
+ https://ftp.linux.org.tr/ubuntu/
520
+ https://kozyatagi.mirror.guzel.net.tr/ubuntu/
521
+ https://mirror.alastyr.com/ubuntu/ubuntu-archive/
522
+ https://mirror.onlinehosting.com.tr/ubuntu/
523
+ https://mirror.rabisu.com/ubuntu/ubuntu-archive/
524
+ https://mirror.sh.com.tr/ubuntu/
525
+ https://mirror.verinomi.com/ubuntu/ubuntu-archive/
526
+ #LOC:TW
527
+ http://free.nchc.org.tw/ubuntu/
528
+ http://ftp.mirror.tw/pub/ubuntu/ubuntu/
529
+ http://ftp.tku.edu.tw/ubuntu/
530
+ http://ftp.tw.debian.org/ubuntu/
531
+ http://mirror.nwlab.tk/ubuntu/
532
+ http://mirror01.idc.hinet.net/ubuntu/
533
+ http://ubuntu.cs.nctu.edu.tw/ubuntu/
534
+ https://ftp.tc.edu.tw/Linux/ubuntu/
535
+ https://ftp.ubuntu-tw.net/ubuntu/
536
+ https://mirror.ossplanet.net/ubuntu/
537
+ https://tw1.mirror.blendbyte.net/ubuntu/
538
+ https://ubuntu.ccns.ncku.edu.tw/ubuntu/
539
+ #LOC:TZ
540
+ http://deb-mirror.habari.co.tz/ubuntu/
541
+ http://mirror.aptus.co.tz/pub/ubuntuarchive/
542
+ #LOC:UA
543
+ http://mirror.mirohost.net/ubuntu/
544
+ http://ubuntu.colocall.net/ubuntu/
545
+ http://ubuntu.mirrors.omnilance.com/ubuntu/
546
+ http://ubuntu.org.ua/ubuntu/
547
+ http://ubuntu.volia.net/ubuntu-archive/
548
+ https://ubuntu.astra.in.ua/ubuntu/
549
+ https://ubuntu.netforce.hosting/ubuntu/
550
+ #LOC:US
551
+ http://archive.linux.duke.edu/ubuntu/
552
+ http://babylon.cs.uh.edu/mirror-sites/ubuntu/
553
+ http://ftp.usf.edu/pub/ubuntu/
554
+ http://ftp.ussg.iu.edu/linux/ubuntu/
555
+ http://mirror.arizona.edu/ubuntu/
556
+ http://mirror.brightridge.com/ubuntuarchive/
557
+ http://mirror.cc.vt.edu/pub2/ubuntu/
558
+ http://mirror.cogentco.com/pub/linux/ubuntu/
559
+ http://mirror.cs.jmu.edu/pub/ubuntu/
560
+ http://mirror.math.princeton.edu/pub/ubuntu/
561
+ http://mirror.math.ucdavis.edu/ubuntu/
562
+ http://mirror.metrocast.net/ubuntu/
563
+ http://mirror.mrjester.net/ubuntu/archive/
564
+ http://mirror.nodesdirect.com/ubuntu/
565
+ http://mirror.pit.teraswitch.com/ubuntu/
566
+ http://mirror.pnl.gov/ubuntu/
567
+ http://mirror.rustytel.net/ubuntu/
568
+ http://mirror.siena.edu/ubuntu/
569
+ http://mirror.steadfastnet.com/ubuntu/
570
+ http://mirror.team-cymru.com/ubuntu/
571
+ http://mirror.team-cymru.org/ubuntu/
572
+ http://mirror.umd.edu/ubuntu/
573
+ http://mirror.uoregon.edu/ubuntu/
574
+ http://mirror.us-midwest-1.nexcess.net/ubuntu/
575
+ http://mirror.us-ny2.kamatera.com/ubuntu/
576
+ http://mirror.us-sc.kamatera.com/ubuntu/
577
+ http://mirror.us-tx.kamatera.com/ubuntu/
578
+ http://mirror.vcu.edu/pub/gnu+linux/ubuntu/
579
+ http://mirrors.accretive-networks.net/ubuntu/
580
+ http://mirrors.advancedhosters.com/ubuntu/
581
+ http://mirrors.arpnetworks.com/Ubuntu/
582
+ http://mirrors.cat.pdx.edu/ubuntu/
583
+ http://mirrors.cmich.edu/ubuntu/
584
+ http://mirrors.codec-cluster.org/ubuntu/
585
+ http://mirrors.gigenet.com/ubuntuarchive/
586
+ http://mirrors.liquidweb.com/ubuntu/
587
+ http://mirrors.lug.mtu.edu/ubuntu/
588
+ http://mirrors.maine.edu/ubuntu/
589
+ http://mirrors.mit.edu/ubuntu/
590
+ http://mirrors.namecheap.com/ubuntu/
591
+ http://mirrors.ocf.berkeley.edu/ubuntu/
592
+ http://mirrors.rit.edu/ubuntu/
593
+ http://mirrors.sonic.net/ubuntu/
594
+ http://mirrors.syringanetworks.net/ubuntu-archive/
595
+ http://mirrors.tripadvisor.com/ubuntu/
596
+ http://mirrors.us.kernel.org/ubuntu/
597
+ http://mirrors.usinternet.com/ubuntu/archive/
598
+ http://mirrors.vcea.wsu.edu/ubuntu/
599
+ http://mirrors.xmission.com/ubuntu/
600
+ http://plug-mirror.rcac.purdue.edu/ubuntu/
601
+ http://pubmirrors.dal.corespace.com/ubuntu/
602
+ http://reflector.westga.edu/repos/Ubuntu/archive/
603
+ http://repo.miserver.it.umich.edu/ubuntu/
604
+ http://repos.forethought.net/ubuntu/
605
+ http://ubuntu.cs.utah.edu/ubuntu/
606
+ http://ubuntu.mirror.constant.com/
607
+ http://ubuntu.mirror.frontiernet.net/ubuntu/
608
+ http://ubuntu.mirrors.pair.com/archive/
609
+ http://ubuntu.osuosl.org/ubuntu/
610
+ http://ubuntu.phoenixnap.com/ubuntu/
611
+ http://ubuntu.securedservers.com/
612
+ http://www.club.cc.cmu.edu/pub/ubuntu/
613
+ http://www.gtlib.gatech.edu/pub/ubuntu/
614
+ https://atl.mirrors.clouvider.net/ubuntu/
615
+ https://dal.mirrors.clouvider.net/ubuntu/
616
+ https://la.mirrors.clouvider.net/ubuntu/
617
+ https://lug.mines.edu/mirrors/ubuntu/
618
+ https://mirror.clarkson.edu/ubuntu/
619
+ https://mirror.cs.pitt.edu/ubuntu/archive/
620
+ https://mirror.d.umn.edu/ubuntu/
621
+ https://mirror.dal.nexril.net/ubuntu/
622
+ https://mirror.enzu.com/ubuntu/
623
+ https://mirror.fcix.net/ubuntu/
624
+ https://mirror.hostduplex.com/ubuntu/
625
+ https://mirror.lstn.net/ubuntu/
626
+ https://mirror.mia.velocihost.net/ubuntu/
627
+ https://mirror.servaxnet.com/ubuntu/
628
+ https://mirror.ubuntu.serverforge.org/
629
+ https://mirror.us.leaseweb.net/ubuntu/
630
+ https://mirrors.bloomu.edu/ubuntu/
631
+ https://mirrors.egr.msu.edu/ubuntu/
632
+ https://mirrors.iu13.net/ubuntu/
633
+ https://mirrors.ocf.berkeley.edu/ubuntu-ports/
634
+ https://mirrors.sarak.as/ubuntu/
635
+ https://mirrors.tscak.com/ubuntu/
636
+ https://mirrors.wikimedia.org/ubuntu/
637
+ https://mirrors.xtom.com/ubuntu/
638
+ https://nyc.mirrors.clouvider.net/ubuntu/
639
+ https://repo.ialab.dsu.edu/ubuntu/
640
+ https://ubuntu.mirror.shastacoe.net/ubuntu/
641
+ #LOC:UY
642
+ http://repos.interior.edu.uy/ubuntu/
643
+ https://ubuntu.repo.cure.edu.uy/mirror/
644
+ #LOC:UZ
645
+ http://mirror.dc.uz/ubuntu/
646
+ http://ubuntu.snet.uz/ubuntu/
647
+ #LOC:VN
648
+ http://mirror.bizflycloud.vn/ubuntu/
649
+ http://mirror.clearsky.vn/ubuntu/
650
+ http://mirror.vietnix.vn/ubuntu/
651
+ http://mirrors.nhanhoa.com/ubuntu/
652
+ http://mirrors.vhost.vn/ubuntu/
653
+ http://opensource.xtdv.net/ubuntu/
654
+ https://mirrors.bkns.vn/ubuntu/
655
+ #LOC:ZA
656
+ http://mirror.hostafrica.co.za/ubuntu/
657
+ http://mirror.wiru.co.za/ubuntu/
658
+ http://ubuntu.mirror.ac.za/ubuntu/
659
+ http://ubuntu.mirror.rain.co.za/ubuntu/
build/data/templates/gNewSense.info ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ChangelogURI: http://packages.debian.org/changelogs/pool/%s/%s/%s/%s_%s/changelog
2
+
3
+ # gNS 3, to be based on Squeeze.
4
+ Suite: parkes
5
+ RepositoryType: deb
6
+ BaseURI: http://archive.gnewsense.org/gnewsense/
7
+ MatchURI: archive.gnewsense.org/gnewsense
8
+ MirrorsFile: gNewSense.mirrors
9
+ Description: gNewSense 3 - "parkes"
10
+ Component: main
11
+ CompDescription: Main supported software
12
+ CompDescriptionLong: Core software
13
+
14
+ Suite: parkes/updates
15
+ RepositoryType: deb
16
+ BaseURI: http://security.gnewsense.org/gnewsense
17
+ MatchURI: security.gnewsense.org/security
18
+ ParentSuite: parkes
19
+ Description: Security updates
20
+
21
+ # gNS 2.x, based on hardy
22
+ Suite: deltah
23
+ RepositoryType: deb
24
+ BaseURI: http://archive.gnewsense.org/gnewsense/
25
+ MatchURI: archive.gnewsense.org/gnewsense
26
+ MirrorsFile: gNewSense.mirrors
27
+ Description: gNewSense "deltah"
28
+ Component: main
29
+ CompDescription: Main supported software
30
+ CompDescriptionLong: Core software
31
+ Component: universe
32
+ CompDescription: Other useful software
33
+ CompDescriptionLong: Non-core useful software
34
+
35
+ Suite: deltah-security
36
+ ParentSuite: deltah
37
+ RepositoryType: deb
38
+ BaseURI: http://security.gnewsense.org/gnewsense/
39
+ MatchURI: archive.gnewsense.org/gnewsense|security.gnewsense.org
40
+ Description: Important security updates
41
+
42
+ Suite: deltah-updates
43
+ ParentSuite: deltah
44
+ RepositoryType: deb
45
+ BaseURI: http://archive.gnewsense.org/gnewsense/
46
+ MatchURI: archive.gnewsense.org/gnewsense
47
+ Description: Unsupported updates
48
+
49
+ Suite: deltah-backports
50
+ ParentSuite: deltah
51
+ RepositoryType: deb
52
+ BaseURI: http://archive.gnewsense.org/gnewsense/
53
+ MatchURI: archive.gnewsense.org/gnewsense
54
+ Description: Package Backports
55
+
build/data/templates/gNewSense.mirrors ADDED
@@ -0,0 +1,489 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ #LOC:AD
3
+ http://ad.archive.gnewsense.org/gnewsense-metad/gnewsense/
4
+ #LOC:AE
5
+ http://ae.archive.gnewsense.org/gnewsense-metad/gnewsense/
6
+ #LOC:AF
7
+ http://af.archive.gnewsense.org/gnewsense-metad/gnewsense/
8
+ #LOC:AG
9
+ http://ag.archive.gnewsense.org/gnewsense-metad/gnewsense/
10
+ #LOC:AI
11
+ http://ai.archive.gnewsense.org/gnewsense-metad/gnewsense/
12
+ #LOC:AL
13
+ http://al.archive.gnewsense.org/gnewsense-metad/gnewsense/
14
+ #LOC:AM
15
+ http://am.archive.gnewsense.org/gnewsense-metad/gnewsense/
16
+ #LOC:AN
17
+ http://an.archive.gnewsense.org/gnewsense-metad/gnewsense/
18
+ #LOC:AO
19
+ http://ao.archive.gnewsense.org/gnewsense-metad/gnewsense/
20
+ #LOC:AQ
21
+ http://aq.archive.gnewsense.org/gnewsense-metad/gnewsense/
22
+ #LOC:AR
23
+ http://ar.archive.gnewsense.org/gnewsense-metad/gnewsense/
24
+ #LOC:AS
25
+ http://as.archive.gnewsense.org/gnewsense-metad/gnewsense/
26
+ #LOC:AT
27
+ http://at.archive.gnewsense.org/gnewsense-metad/gnewsense/
28
+ #LOC:AU
29
+ http://au.archive.gnewsense.org/gnewsense-metad/gnewsense/
30
+ #LOC:AW
31
+ http://aw.archive.gnewsense.org/gnewsense-metad/gnewsense/
32
+ #LOC:AX
33
+ http://ax.archive.gnewsense.org/gnewsense-metad/gnewsense/
34
+ #LOC:AZ
35
+ http://az.archive.gnewsense.org/gnewsense-metad/gnewsense/
36
+ #LOC:BA
37
+ http://ba.archive.gnewsense.org/gnewsense-metad/gnewsense/
38
+ #LOC:BB
39
+ http://bb.archive.gnewsense.org/gnewsense-metad/gnewsense/
40
+ #LOC:BD
41
+ http://bd.archive.gnewsense.org/gnewsense-metad/gnewsense/
42
+ #LOC:BE
43
+ http://be.archive.gnewsense.org/gnewsense-metad/gnewsense/
44
+ #LOC:BF
45
+ http://bf.archive.gnewsense.org/gnewsense-metad/gnewsense/
46
+ #LOC:BG
47
+ http://bg.archive.gnewsense.org/gnewsense-metad/gnewsense/
48
+ #LOC:BH
49
+ http://bh.archive.gnewsense.org/gnewsense-metad/gnewsense/
50
+ #LOC:BI
51
+ http://bi.archive.gnewsense.org/gnewsense-metad/gnewsense/
52
+ #LOC:BJ
53
+ http://bj.archive.gnewsense.org/gnewsense-metad/gnewsense/
54
+ #LOC:BM
55
+ http://bm.archive.gnewsense.org/gnewsense-metad/gnewsense/
56
+ #LOC:BN
57
+ http://bn.archive.gnewsense.org/gnewsense-metad/gnewsense/
58
+ #LOC:BO
59
+ http://bo.archive.gnewsense.org/gnewsense-metad/gnewsense/
60
+ #LOC:BR
61
+ http://br.archive.gnewsense.org/gnewsense-metad/gnewsense/
62
+ #LOC:BS
63
+ http://bs.archive.gnewsense.org/gnewsense-metad/gnewsense/
64
+ #LOC:BT
65
+ http://bt.archive.gnewsense.org/gnewsense-metad/gnewsense/
66
+ #LOC:BV
67
+ http://bv.archive.gnewsense.org/gnewsense-metad/gnewsense/
68
+ #LOC:BW
69
+ http://bw.archive.gnewsense.org/gnewsense-metad/gnewsense/
70
+ #LOC:BY
71
+ http://by.archive.gnewsense.org/gnewsense-metad/gnewsense/
72
+ #LOC:BZ
73
+ http://bz.archive.gnewsense.org/gnewsense-metad/gnewsense/
74
+ #LOC:CA
75
+ http://ca.archive.gnewsense.org/gnewsense-metad/gnewsense/
76
+ #LOC:CC
77
+ http://cc.archive.gnewsense.org/gnewsense-metad/gnewsense/
78
+ #LOC:CD
79
+ http://cd.archive.gnewsense.org/gnewsense-metad/gnewsense/
80
+ #LOC:CF
81
+ http://cf.archive.gnewsense.org/gnewsense-metad/gnewsense/
82
+ #LOC:CG
83
+ http://cg.archive.gnewsense.org/gnewsense-metad/gnewsense/
84
+ #LOC:CH
85
+ http://ch.archive.gnewsense.org/gnewsense-metad/gnewsense/
86
+ #LOC:CI
87
+ http://ci.archive.gnewsense.org/gnewsense-metad/gnewsense/
88
+ #LOC:CK
89
+ http://ck.archive.gnewsense.org/gnewsense-metad/gnewsense/
90
+ #LOC:CL
91
+ http://cl.archive.gnewsense.org/gnewsense-metad/gnewsense/
92
+ #LOC:CM
93
+ http://cm.archive.gnewsense.org/gnewsense-metad/gnewsense/
94
+ #LOC:CN
95
+ http://cn.archive.gnewsense.org/gnewsense-metad/gnewsense/
96
+ #LOC:CO
97
+ http://co.archive.gnewsense.org/gnewsense-metad/gnewsense/
98
+ #LOC:CR
99
+ http://cr.archive.gnewsense.org/gnewsense-metad/gnewsense/
100
+ #LOC:CU
101
+ http://cu.archive.gnewsense.org/gnewsense-metad/gnewsense/
102
+ #LOC:CV
103
+ http://cv.archive.gnewsense.org/gnewsense-metad/gnewsense/
104
+ #LOC:CX
105
+ http://cx.archive.gnewsense.org/gnewsense-metad/gnewsense/
106
+ #LOC:CY
107
+ http://cy.archive.gnewsense.org/gnewsense-metad/gnewsense/
108
+ #LOC:CZ
109
+ http://cz.archive.gnewsense.org/gnewsense-metad/gnewsense/
110
+ #LOC:DE
111
+ http://de.archive.gnewsense.org/gnewsense-metad/gnewsense/
112
+ #LOC:DJ
113
+ http://dj.archive.gnewsense.org/gnewsense-metad/gnewsense/
114
+ #LOC:DK
115
+ http://dk.archive.gnewsense.org/gnewsense-metad/gnewsense/
116
+ #LOC:DM
117
+ http://dm.archive.gnewsense.org/gnewsense-metad/gnewsense/
118
+ #LOC:DO
119
+ http://do.archive.gnewsense.org/gnewsense-metad/gnewsense/
120
+ #LOC:DZ
121
+ http://dz.archive.gnewsense.org/gnewsense-metad/gnewsense/
122
+ #LOC:EC
123
+ http://ec.archive.gnewsense.org/gnewsense-metad/gnewsense/
124
+ #LOC:EE
125
+ http://ee.archive.gnewsense.org/gnewsense-metad/gnewsense/
126
+ #LOC:EG
127
+ http://eg.archive.gnewsense.org/gnewsense-metad/gnewsense/
128
+ #LOC:EH
129
+ http://eh.archive.gnewsense.org/gnewsense-metad/gnewsense/
130
+ #LOC:ER
131
+ http://er.archive.gnewsense.org/gnewsense-metad/gnewsense/
132
+ #LOC:ES
133
+ http://es.archive.gnewsense.org/gnewsense-metad/gnewsense/
134
+ #LOC:ET
135
+ http://et.archive.gnewsense.org/gnewsense-metad/gnewsense/
136
+ #LOC:FI
137
+ http://fi.archive.gnewsense.org/gnewsense-metad/gnewsense/
138
+ #LOC:FJ
139
+ http://fj.archive.gnewsense.org/gnewsense-metad/gnewsense/
140
+ #LOC:FK
141
+ http://fk.archive.gnewsense.org/gnewsense-metad/gnewsense/
142
+ #LOC:FM
143
+ http://fm.archive.gnewsense.org/gnewsense-metad/gnewsense/
144
+ #LOC:FO
145
+ http://fo.archive.gnewsense.org/gnewsense-metad/gnewsense/
146
+ #LOC:FR
147
+ http://fr.archive.gnewsense.org/gnewsense-metad/gnewsense/
148
+ #LOC:GA
149
+ http://ga.archive.gnewsense.org/gnewsense-metad/gnewsense/
150
+ #LOC:GB
151
+ http://gb.archive.gnewsense.org/gnewsense-metad/gnewsense/
152
+ #LOC:GD
153
+ http://gd.archive.gnewsense.org/gnewsense-metad/gnewsense/
154
+ #LOC:GE
155
+ http://ge.archive.gnewsense.org/gnewsense-metad/gnewsense/
156
+ #LOC:GF
157
+ http://gf.archive.gnewsense.org/gnewsense-metad/gnewsense/
158
+ #LOC:GG
159
+ http://gg.archive.gnewsense.org/gnewsense-metad/gnewsense/
160
+ #LOC:GH
161
+ http://gh.archive.gnewsense.org/gnewsense-metad/gnewsense/
162
+ #LOC:GI
163
+ http://gi.archive.gnewsense.org/gnewsense-metad/gnewsense/
164
+ #LOC:GL
165
+ http://gl.archive.gnewsense.org/gnewsense-metad/gnewsense/
166
+ #LOC:GM
167
+ http://gm.archive.gnewsense.org/gnewsense-metad/gnewsense/
168
+ #LOC:GN
169
+ http://gn.archive.gnewsense.org/gnewsense-metad/gnewsense/
170
+ #LOC:GP
171
+ http://gp.archive.gnewsense.org/gnewsense-metad/gnewsense/
172
+ #LOC:GQ
173
+ http://gq.archive.gnewsense.org/gnewsense-metad/gnewsense/
174
+ #LOC:GR
175
+ http://gr.archive.gnewsense.org/gnewsense-metad/gnewsense/
176
+ #LOC:GS
177
+ http://gs.archive.gnewsense.org/gnewsense-metad/gnewsense/
178
+ #LOC:GT
179
+ http://gt.archive.gnewsense.org/gnewsense-metad/gnewsense/
180
+ #LOC:GU
181
+ http://gu.archive.gnewsense.org/gnewsense-metad/gnewsense/
182
+ #LOC:GW
183
+ http://gw.archive.gnewsense.org/gnewsense-metad/gnewsense/
184
+ #LOC:GY
185
+ http://gy.archive.gnewsense.org/gnewsense-metad/gnewsense/
186
+ #LOC:HK
187
+ http://hk.archive.gnewsense.org/gnewsense-metad/gnewsense/
188
+ #LOC:HM
189
+ http://hm.archive.gnewsense.org/gnewsense-metad/gnewsense/
190
+ #LOC:HN
191
+ http://hn.archive.gnewsense.org/gnewsense-metad/gnewsense/
192
+ #LOC:HR
193
+ http://hr.archive.gnewsense.org/gnewsense-metad/gnewsense/
194
+ #LOC:HT
195
+ http://ht.archive.gnewsense.org/gnewsense-metad/gnewsense/
196
+ #LOC:HU
197
+ http://hu.archive.gnewsense.org/gnewsense-metad/gnewsense/
198
+ #LOC:ID
199
+ http://id.archive.gnewsense.org/gnewsense-metad/gnewsense/
200
+ #LOC:IE
201
+ http://ie.archive.gnewsense.org/gnewsense-metad/gnewsense/
202
+ #LOC:IL
203
+ http://il.archive.gnewsense.org/gnewsense-metad/gnewsense/
204
+ #LOC:IM
205
+ http://im.archive.gnewsense.org/gnewsense-metad/gnewsense/
206
+ #LOC:IN
207
+ http://in.archive.gnewsense.org/gnewsense-metad/gnewsense/
208
+ #LOC:IO
209
+ http://io.archive.gnewsense.org/gnewsense-metad/gnewsense/
210
+ #LOC:IQ
211
+ http://iq.archive.gnewsense.org/gnewsense-metad/gnewsense/
212
+ #LOC:IR
213
+ http://ir.archive.gnewsense.org/gnewsense-metad/gnewsense/
214
+ #LOC:IS
215
+ http://is.archive.gnewsense.org/gnewsense-metad/gnewsense/
216
+ #LOC:IT
217
+ http://it.archive.gnewsense.org/gnewsense-metad/gnewsense/
218
+ #LOC:JE
219
+ http://je.archive.gnewsense.org/gnewsense-metad/gnewsense/
220
+ #LOC:JM
221
+ http://jm.archive.gnewsense.org/gnewsense-metad/gnewsense/
222
+ #LOC:JO
223
+ http://jo.archive.gnewsense.org/gnewsense-metad/gnewsense/
224
+ #LOC:JP
225
+ http://jp.archive.gnewsense.org/gnewsense-metad/gnewsense/
226
+ #LOC:KE
227
+ http://ke.archive.gnewsense.org/gnewsense-metad/gnewsense/
228
+ #LOC:KG
229
+ http://kg.archive.gnewsense.org/gnewsense-metad/gnewsense/
230
+ #LOC:KH
231
+ http://kh.archive.gnewsense.org/gnewsense-metad/gnewsense/
232
+ #LOC:KI
233
+ http://ki.archive.gnewsense.org/gnewsense-metad/gnewsense/
234
+ #LOC:KM
235
+ http://km.archive.gnewsense.org/gnewsense-metad/gnewsense/
236
+ #LOC:KN
237
+ http://kn.archive.gnewsense.org/gnewsense-metad/gnewsense/
238
+ #LOC:KP
239
+ http://kp.archive.gnewsense.org/gnewsense-metad/gnewsense/
240
+ #LOC:KR
241
+ http://kr.archive.gnewsense.org/gnewsense-metad/gnewsense/
242
+ #LOC:KW
243
+ http://kw.archive.gnewsense.org/gnewsense-metad/gnewsense/
244
+ #LOC:KY
245
+ http://ky.archive.gnewsense.org/gnewsense-metad/gnewsense/
246
+ #LOC:KZ
247
+ http://kz.archive.gnewsense.org/gnewsense-metad/gnewsense/
248
+ #LOC:LA
249
+ http://la.archive.gnewsense.org/gnewsense-metad/gnewsense/
250
+ #LOC:LB
251
+ http://lb.archive.gnewsense.org/gnewsense-metad/gnewsense/
252
+ #LOC:LC
253
+ http://lc.archive.gnewsense.org/gnewsense-metad/gnewsense/
254
+ #LOC:LI
255
+ http://li.archive.gnewsense.org/gnewsense-metad/gnewsense/
256
+ #LOC:LK
257
+ http://lk.archive.gnewsense.org/gnewsense-metad/gnewsense/
258
+ #LOC:LR
259
+ http://lr.archive.gnewsense.org/gnewsense-metad/gnewsense/
260
+ #LOC:LS
261
+ http://ls.archive.gnewsense.org/gnewsense-metad/gnewsense/
262
+ #LOC:LT
263
+ http://lt.archive.gnewsense.org/gnewsense-metad/gnewsense/
264
+ #LOC:LU
265
+ http://lu.archive.gnewsense.org/gnewsense-metad/gnewsense/
266
+ #LOC:LV
267
+ http://lv.archive.gnewsense.org/gnewsense-metad/gnewsense/
268
+ #LOC:LY
269
+ http://ly.archive.gnewsense.org/gnewsense-metad/gnewsense/
270
+ #LOC:MA
271
+ http://ma.archive.gnewsense.org/gnewsense-metad/gnewsense/
272
+ #LOC:MC
273
+ http://mc.archive.gnewsense.org/gnewsense-metad/gnewsense/
274
+ #LOC:MD
275
+ http://md.archive.gnewsense.org/gnewsense-metad/gnewsense/
276
+ #LOC:ME
277
+ http://me.archive.gnewsense.org/gnewsense-metad/gnewsense/
278
+ #LOC:MG
279
+ http://mg.archive.gnewsense.org/gnewsense-metad/gnewsense/
280
+ #LOC:MH
281
+ http://mh.archive.gnewsense.org/gnewsense-metad/gnewsense/
282
+ #LOC:MK
283
+ http://mk.archive.gnewsense.org/gnewsense-metad/gnewsense/
284
+ #LOC:ML
285
+ http://ml.archive.gnewsense.org/gnewsense-metad/gnewsense/
286
+ #LOC:MM
287
+ http://mm.archive.gnewsense.org/gnewsense-metad/gnewsense/
288
+ #LOC:MN
289
+ http://mn.archive.gnewsense.org/gnewsense-metad/gnewsense/
290
+ #LOC:MO
291
+ http://mo.archive.gnewsense.org/gnewsense-metad/gnewsense/
292
+ #LOC:MP
293
+ http://mp.archive.gnewsense.org/gnewsense-metad/gnewsense/
294
+ #LOC:MQ
295
+ http://mq.archive.gnewsense.org/gnewsense-metad/gnewsense/
296
+ #LOC:MR
297
+ http://mr.archive.gnewsense.org/gnewsense-metad/gnewsense/
298
+ #LOC:MS
299
+ http://ms.archive.gnewsense.org/gnewsense-metad/gnewsense/
300
+ #LOC:MT
301
+ http://mt.archive.gnewsense.org/gnewsense-metad/gnewsense/
302
+ #LOC:MU
303
+ http://mu.archive.gnewsense.org/gnewsense-metad/gnewsense/
304
+ #LOC:MV
305
+ http://mv.archive.gnewsense.org/gnewsense-metad/gnewsense/
306
+ #LOC:MW
307
+ http://mw.archive.gnewsense.org/gnewsense-metad/gnewsense/
308
+ #LOC:MX
309
+ http://mx.archive.gnewsense.org/gnewsense-metad/gnewsense/
310
+ #LOC:MY
311
+ http://my.archive.gnewsense.org/gnewsense-metad/gnewsense/
312
+ #LOC:MZ
313
+ http://mz.archive.gnewsense.org/gnewsense-metad/gnewsense/
314
+ #LOC:NA
315
+ http://na.archive.gnewsense.org/gnewsense-metad/gnewsense/
316
+ #LOC:NC
317
+ http://nc.archive.gnewsense.org/gnewsense-metad/gnewsense/
318
+ #LOC:NE
319
+ http://ne.archive.gnewsense.org/gnewsense-metad/gnewsense/
320
+ #LOC:NF
321
+ http://nf.archive.gnewsense.org/gnewsense-metad/gnewsense/
322
+ #LOC:NG
323
+ http://ng.archive.gnewsense.org/gnewsense-metad/gnewsense/
324
+ #LOC:NI
325
+ http://ni.archive.gnewsense.org/gnewsense-metad/gnewsense/
326
+ #LOC:NL
327
+ http://nl.archive.gnewsense.org/gnewsense-metad/gnewsense/
328
+ #LOC:NO
329
+ http://no.archive.gnewsense.org/gnewsense-metad/gnewsense/
330
+ #LOC:NP
331
+ http://np.archive.gnewsense.org/gnewsense-metad/gnewsense/
332
+ #LOC:NR
333
+ http://nr.archive.gnewsense.org/gnewsense-metad/gnewsense/
334
+ #LOC:NU
335
+ http://nu.archive.gnewsense.org/gnewsense-metad/gnewsense/
336
+ #LOC:NZ
337
+ http://nz.archive.gnewsense.org/gnewsense-metad/gnewsense/
338
+ #LOC:OM
339
+ http://om.archive.gnewsense.org/gnewsense-metad/gnewsense/
340
+ #LOC:PA
341
+ http://pa.archive.gnewsense.org/gnewsense-metad/gnewsense/
342
+ #LOC:PE
343
+ http://pe.archive.gnewsense.org/gnewsense-metad/gnewsense/
344
+ #LOC:PF
345
+ http://pf.archive.gnewsense.org/gnewsense-metad/gnewsense/
346
+ #LOC:PG
347
+ http://pg.archive.gnewsense.org/gnewsense-metad/gnewsense/
348
+ #LOC:PH
349
+ http://ph.archive.gnewsense.org/gnewsense-metad/gnewsense/
350
+ #LOC:PK
351
+ http://pk.archive.gnewsense.org/gnewsense-metad/gnewsense/
352
+ #LOC:PL
353
+ http://pl.archive.gnewsense.org/gnewsense-metad/gnewsense/
354
+ #LOC:PM
355
+ http://pm.archive.gnewsense.org/gnewsense-metad/gnewsense/
356
+ #LOC:PN
357
+ http://pn.archive.gnewsense.org/gnewsense-metad/gnewsense/
358
+ #LOC:PR
359
+ http://pr.archive.gnewsense.org/gnewsense-metad/gnewsense/
360
+ #LOC:PS
361
+ http://ps.archive.gnewsense.org/gnewsense-metad/gnewsense/
362
+ #LOC:PT
363
+ http://pt.archive.gnewsense.org/gnewsense-metad/gnewsense/
364
+ #LOC:PW
365
+ http://pw.archive.gnewsense.org/gnewsense-metad/gnewsense/
366
+ #LOC:PY
367
+ http://py.archive.gnewsense.org/gnewsense-metad/gnewsense/
368
+ #LOC:QA
369
+ http://qa.archive.gnewsense.org/gnewsense-metad/gnewsense/
370
+ #LOC:RE
371
+ http://re.archive.gnewsense.org/gnewsense-metad/gnewsense/
372
+ #LOC:RO
373
+ http://ro.archive.gnewsense.org/gnewsense-metad/gnewsense/
374
+ #LOC:RS
375
+ http://rs.archive.gnewsense.org/gnewsense-metad/gnewsense/
376
+ #LOC:RU
377
+ http://ru.archive.gnewsense.org/gnewsense-metad/gnewsense/
378
+ #LOC:RW
379
+ http://rw.archive.gnewsense.org/gnewsense-metad/gnewsense/
380
+ #LOC:SA
381
+ http://sa.archive.gnewsense.org/gnewsense-metad/gnewsense/
382
+ #LOC:SB
383
+ http://sb.archive.gnewsense.org/gnewsense-metad/gnewsense/
384
+ #LOC:SC
385
+ http://sc.archive.gnewsense.org/gnewsense-metad/gnewsense/
386
+ #LOC:SD
387
+ http://sd.archive.gnewsense.org/gnewsense-metad/gnewsense/
388
+ #LOC:SE
389
+ http://se.archive.gnewsense.org/gnewsense-metad/gnewsense/
390
+ #LOC:SG
391
+ http://sg.archive.gnewsense.org/gnewsense-metad/gnewsense/
392
+ #LOC:SH
393
+ http://sh.archive.gnewsense.org/gnewsense-metad/gnewsense/
394
+ #LOC:SI
395
+ http://si.archive.gnewsense.org/gnewsense-metad/gnewsense/
396
+ #LOC:SJ
397
+ http://sj.archive.gnewsense.org/gnewsense-metad/gnewsense/
398
+ #LOC:SK
399
+ http://sk.archive.gnewsense.org/gnewsense-metad/gnewsense/
400
+ #LOC:SL
401
+ http://sl.archive.gnewsense.org/gnewsense-metad/gnewsense/
402
+ #LOC:SM
403
+ http://sm.archive.gnewsense.org/gnewsense-metad/gnewsense/
404
+ #LOC:SN
405
+ http://sn.archive.gnewsense.org/gnewsense-metad/gnewsense/
406
+ #LOC:SO
407
+ http://so.archive.gnewsense.org/gnewsense-metad/gnewsense/
408
+ #LOC:SR
409
+ http://sr.archive.gnewsense.org/gnewsense-metad/gnewsense/
410
+ #LOC:ST
411
+ http://st.archive.gnewsense.org/gnewsense-metad/gnewsense/
412
+ #LOC:SV
413
+ http://sv.archive.gnewsense.org/gnewsense-metad/gnewsense/
414
+ #LOC:SY
415
+ http://sy.archive.gnewsense.org/gnewsense-metad/gnewsense/
416
+ #LOC:SZ
417
+ http://sz.archive.gnewsense.org/gnewsense-metad/gnewsense/
418
+ #LOC:TC
419
+ http://tc.archive.gnewsense.org/gnewsense-metad/gnewsense/
420
+ #LOC:TD
421
+ http://td.archive.gnewsense.org/gnewsense-metad/gnewsense/
422
+ #LOC:TF
423
+ http://tf.archive.gnewsense.org/gnewsense-metad/gnewsense/
424
+ #LOC:TG
425
+ http://tg.archive.gnewsense.org/gnewsense-metad/gnewsense/
426
+ #LOC:TH
427
+ http://th.archive.gnewsense.org/gnewsense-metad/gnewsense/
428
+ #LOC:TJ
429
+ http://tj.archive.gnewsense.org/gnewsense-metad/gnewsense/
430
+ #LOC:TK
431
+ http://tk.archive.gnewsense.org/gnewsense-metad/gnewsense/
432
+ #LOC:TL
433
+ http://tl.archive.gnewsense.org/gnewsense-metad/gnewsense/
434
+ #LOC:TM
435
+ http://tm.archive.gnewsense.org/gnewsense-metad/gnewsense/
436
+ #LOC:TN
437
+ http://tn.archive.gnewsense.org/gnewsense-metad/gnewsense/
438
+ #LOC:TO
439
+ http://to.archive.gnewsense.org/gnewsense-metad/gnewsense/
440
+ #LOC:TR
441
+ http://tr.archive.gnewsense.org/gnewsense-metad/gnewsense/
442
+ #LOC:TT
443
+ http://tt.archive.gnewsense.org/gnewsense-metad/gnewsense/
444
+ #LOC:TV
445
+ http://tv.archive.gnewsense.org/gnewsense-metad/gnewsense/
446
+ #LOC:TW
447
+ http://tw.archive.gnewsense.org/gnewsense-metad/gnewsense/
448
+ #LOC:TZ
449
+ http://tz.archive.gnewsense.org/gnewsense-metad/gnewsense/
450
+ #LOC:UA
451
+ http://ua.archive.gnewsense.org/gnewsense-metad/gnewsense/
452
+ #LOC:UG
453
+ http://ug.archive.gnewsense.org/gnewsense-metad/gnewsense/
454
+ #LOC:UM
455
+ http://um.archive.gnewsense.org/gnewsense-metad/gnewsense/
456
+ #LOC:US
457
+ http://us.archive.gnewsense.org/gnewsense-metad/gnewsense/
458
+ #LOC:UY
459
+ http://uy.archive.gnewsense.org/gnewsense-metad/gnewsense/
460
+ #LOC:UZ
461
+ http://uz.archive.gnewsense.org/gnewsense-metad/gnewsense/
462
+ #LOC:VA
463
+ http://va.archive.gnewsense.org/gnewsense-metad/gnewsense/
464
+ #LOC:VC
465
+ http://vc.archive.gnewsense.org/gnewsense-metad/gnewsense/
466
+ #LOC:VE
467
+ http://ve.archive.gnewsense.org/gnewsense-metad/gnewsense/
468
+ #LOC:VG
469
+ http://vg.archive.gnewsense.org/gnewsense-metad/gnewsense/
470
+ #LOC:VI
471
+ http://vi.archive.gnewsense.org/gnewsense-metad/gnewsense/
472
+ #LOC:VN
473
+ http://vn.archive.gnewsense.org/gnewsense-metad/gnewsense/
474
+ #LOC:VU
475
+ http://vu.archive.gnewsense.org/gnewsense-metad/gnewsense/
476
+ #LOC:WF
477
+ http://wf.archive.gnewsense.org/gnewsense-metad/gnewsense/
478
+ #LOC:WS
479
+ http://ws.archive.gnewsense.org/gnewsense-metad/gnewsense/
480
+ #LOC:YE
481
+ http://ye.archive.gnewsense.org/gnewsense-metad/gnewsense/
482
+ #LOC:YT
483
+ http://yt.archive.gnewsense.org/gnewsense-metad/gnewsense/
484
+ #LOC:ZA
485
+ http://za.archive.gnewsense.org/gnewsense-metad/gnewsense/
486
+ #LOC:ZM
487
+ http://zm.archive.gnewsense.org/gnewsense-metad/gnewsense/
488
+ #LOC:ZW
489
+ http://zw.archive.gnewsense.org/gnewsense-metad/gnewsense/
build/lib.linux-x86_64-cpython-310/apt/__init__.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2005-2009 Canonical
2
+ #
3
+ # Author: Michael Vogt <[email protected]>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License as
7
+ # published by the Free Software Foundation; either version 2 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
+ # USA
19
+ # import the core of apt_pkg
20
+ """High-Level Interface for working with apt."""
21
+ from __future__ import print_function
22
+
23
+ import apt_pkg
24
+
25
+ # import some fancy classes
26
+ from apt.package import Package as Package, Version as Version
27
+ from apt.cache import Cache as Cache, ProblemResolver as ProblemResolver
28
+ Cache # pyflakes
29
+ ProblemResolver # pyflakes
30
+ Version # pyflakes
31
+ from apt.cdrom import Cdrom as Cdrom
32
+
33
+ # init the package system, but do not re-initialize config
34
+ if "APT" not in apt_pkg.config:
35
+ apt_pkg.init_config()
36
+ apt_pkg.init_system()
37
+
38
+ __all__ = ['Cache', 'Cdrom', 'Package']
build/lib.linux-x86_64-cpython-310/apt/auth.py ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+ # auth - authentication key management
4
+ #
5
+ # Copyright (c) 2004 Canonical
6
+ # Copyright (c) 2012 Sebastian Heinlein
7
+ #
8
+ # Author: Michael Vogt <[email protected]>
9
+ # Sebastian Heinlein <[email protected]>
10
+ #
11
+ # This program is free software; you can redistribute it and/or
12
+ # modify it under the terms of the GNU General Public License as
13
+ # published by the Free Software Foundation; either version 2 of the
14
+ # License, or (at your option) any later version.
15
+ #
16
+ # This program is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with this program; if not, write to the Free Software
23
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
24
+ # USA
25
+ """Handle GnuPG keys used to trust signed repositories."""
26
+
27
+ from __future__ import print_function
28
+
29
+ import errno
30
+ import os
31
+ import os.path
32
+ import shutil
33
+ import subprocess
34
+ import sys
35
+ import tempfile
36
+
37
+ import apt_pkg
38
+ from apt_pkg import gettext as _
39
+
40
+ from typing import List, Optional, Tuple
41
+
42
+
43
+ class AptKeyError(Exception):
44
+ pass
45
+
46
+
47
+ class AptKeyIDTooShortError(AptKeyError):
48
+ """Internal class do not rely on it."""
49
+
50
+
51
+ class TrustedKey(object):
52
+
53
+ """Represents a trusted key."""
54
+
55
+ def __init__(self, name, keyid, date):
56
+ # type: (str, str, str) -> None
57
+ self.raw_name = name
58
+ # Allow to translated some known keys
59
+ self.name = _(name)
60
+ self.keyid = keyid
61
+ self.date = date
62
+
63
+ def __str__(self):
64
+ # type: () -> str
65
+ return "%s\n%s %s" % (self.name, self.keyid, self.date)
66
+
67
+
68
+ def _call_apt_key_script(*args, **kwargs):
69
+ # type: (str, Optional[str]) -> str
70
+ """Run the apt-key script with the given arguments."""
71
+ conf = None
72
+ cmd = [apt_pkg.config.find_file("Dir::Bin::Apt-Key", "/usr/bin/apt-key")]
73
+ cmd.extend(args)
74
+ env = os.environ.copy()
75
+ env["LANG"] = "C"
76
+ env["APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE"] = "1"
77
+ try:
78
+ if apt_pkg.config.find_dir("Dir") != "/":
79
+ # If the key is to be installed into a chroot we have to export the
80
+ # configuration from the chroot to the apt-key script by using
81
+ # a temporary APT_CONFIG file. The apt-key script uses apt-config
82
+ # shell internally
83
+ conf = tempfile.NamedTemporaryFile(
84
+ prefix="apt-key", suffix=".conf")
85
+ conf.write(apt_pkg.config.dump().encode("UTF-8"))
86
+ conf.flush()
87
+ env["APT_CONFIG"] = conf.name
88
+ proc = subprocess.Popen(cmd, env=env, universal_newlines=True,
89
+ stdin=subprocess.PIPE,
90
+ stdout=subprocess.PIPE,
91
+ stderr=subprocess.PIPE)
92
+
93
+ stdin = kwargs.get("stdin", None)
94
+
95
+ output, stderr = proc.communicate(stdin) # type: str, str
96
+
97
+ if proc.returncode:
98
+ raise AptKeyError(
99
+ "The apt-key script failed with return code %s:\n"
100
+ "%s\n"
101
+ "stdout: %s\n"
102
+ "stderr: %s" % (
103
+ proc.returncode, " ".join(cmd), output, stderr))
104
+ elif stderr:
105
+ sys.stderr.write(stderr) # Forward stderr
106
+
107
+ return output.strip()
108
+ finally:
109
+ if conf is not None:
110
+ conf.close()
111
+
112
+
113
+ def add_key_from_file(filename):
114
+ # type: (str) -> None
115
+ """Import a GnuPG key file to trust repositores signed by it.
116
+
117
+ Keyword arguments:
118
+ filename -- the absolute path to the public GnuPG key file
119
+ """
120
+ if not os.path.abspath(filename):
121
+ raise AptKeyError("An absolute path is required: %s" % filename)
122
+ if not os.access(filename, os.R_OK):
123
+ raise AptKeyError("Key file cannot be accessed: %s" % filename)
124
+ _call_apt_key_script("add", filename)
125
+
126
+
127
+ def add_key_from_keyserver(keyid, keyserver):
128
+ # type: (str, str) -> None
129
+ """Import a GnuPG key file to trust repositores signed by it.
130
+
131
+ Keyword arguments:
132
+ keyid -- the long keyid (fingerprint) of the key, e.g.
133
+ A1BD8E9D78F7FE5C3E65D8AF8B48AD6246925553
134
+ keyserver -- the URL or hostname of the key server
135
+ """
136
+ tmp_keyring_dir = tempfile.mkdtemp()
137
+ try:
138
+ _add_key_from_keyserver(keyid, keyserver, tmp_keyring_dir)
139
+ except Exception:
140
+ raise
141
+ finally:
142
+ # We are racing with gpg when removing sockets, so ignore
143
+ # failure to delete non-existing files.
144
+ def onerror(func, path, exc_info):
145
+ # type: (object, str, Tuple[type, Exception, object]) -> None
146
+ if (isinstance(exc_info[1], OSError) and
147
+ exc_info[1].errno == errno.ENOENT):
148
+ return
149
+ raise
150
+
151
+ shutil.rmtree(tmp_keyring_dir, onerror=onerror)
152
+
153
+
154
+ def _add_key_from_keyserver(keyid, keyserver, tmp_keyring_dir):
155
+ # type: (str, str, str) -> None
156
+ if len(keyid.replace(" ", "").replace("0x", "")) < (160 / 4):
157
+ raise AptKeyIDTooShortError(
158
+ "Only fingerprints (v4, 160bit) are supported")
159
+ # create a temp keyring dir
160
+ tmp_secret_keyring = os.path.join(tmp_keyring_dir, "secring.gpg")
161
+ tmp_keyring = os.path.join(tmp_keyring_dir, "pubring.gpg")
162
+ # default options for gpg
163
+ gpg_default_options = [
164
+ "gpg",
165
+ "--no-default-keyring", "--no-options",
166
+ "--homedir", tmp_keyring_dir,
167
+ ]
168
+ # download the key to a temp keyring first
169
+ res = subprocess.call(gpg_default_options + [
170
+ "--secret-keyring", tmp_secret_keyring,
171
+ "--keyring", tmp_keyring,
172
+ "--keyserver", keyserver,
173
+ "--recv", keyid,
174
+ ])
175
+ if res != 0:
176
+ raise AptKeyError("recv from '%s' failed for '%s'" % (
177
+ keyserver, keyid))
178
+ # FIXME:
179
+ # - with gnupg 1.4.18 the downloaded key is actually checked(!),
180
+ # i.e. gnupg will not import anything that the server sends
181
+ # into the keyring, so the below checks are now redundant *if*
182
+ # gnupg 1.4.18 is used
183
+
184
+ # now export again using the long key id (to ensure that there is
185
+ # really only this one key in our keyring) and not someone MITM us
186
+ tmp_export_keyring = os.path.join(tmp_keyring_dir, "export-keyring.gpg")
187
+ res = subprocess.call(gpg_default_options + [
188
+ "--keyring", tmp_keyring,
189
+ "--output", tmp_export_keyring,
190
+ "--export", keyid,
191
+ ])
192
+ if res != 0:
193
+ raise AptKeyError("export of '%s' failed", keyid)
194
+ # now verify the fingerprint, this is probably redundant as we
195
+ # exported by the fingerprint in the previous command but its
196
+ # still good paranoia
197
+ output = subprocess.Popen(
198
+ gpg_default_options + [
199
+ "--keyring", tmp_export_keyring,
200
+ "--fingerprint",
201
+ "--batch",
202
+ "--fixed-list-mode",
203
+ "--with-colons",
204
+ ],
205
+ stdout=subprocess.PIPE,
206
+ universal_newlines=True).communicate()[0]
207
+ got_fingerprint = None
208
+ for line in output.splitlines():
209
+ if line.startswith("fpr:"):
210
+ got_fingerprint = line.split(":")[9]
211
+ # stop after the first to ensure no subkey trickery
212
+ break
213
+ # strip the leading "0x" is there is one and uppercase (as this is
214
+ # what gnupg is using)
215
+ signing_key_fingerprint = keyid.replace("0x", "").upper()
216
+ if got_fingerprint != signing_key_fingerprint:
217
+ # make the error match what gnupg >= 1.4.18 will output when
218
+ # it checks the key itself before importing it
219
+ raise AptKeyError(
220
+ "recv from '%s' failed for '%s'" % (
221
+ keyserver, signing_key_fingerprint))
222
+ # finally add it
223
+ add_key_from_file(tmp_export_keyring)
224
+
225
+
226
+ def add_key(content):
227
+ # type: (str) -> None
228
+ """Import a GnuPG key to trust repositores signed by it.
229
+
230
+ Keyword arguments:
231
+ content -- the content of the GnuPG public key
232
+ """
233
+ _call_apt_key_script("adv", "--quiet", "--batch",
234
+ "--import", "-", stdin=content)
235
+
236
+
237
+ def remove_key(fingerprint):
238
+ # type: (str) -> None
239
+ """Remove a GnuPG key to no longer trust repositores signed by it.
240
+
241
+ Keyword arguments:
242
+ fingerprint -- the fingerprint identifying the key
243
+ """
244
+ _call_apt_key_script("rm", fingerprint)
245
+
246
+
247
+ def export_key(fingerprint):
248
+ # type: (str) -> str
249
+ """Return the GnuPG key in text format.
250
+
251
+ Keyword arguments:
252
+ fingerprint -- the fingerprint identifying the key
253
+ """
254
+ return _call_apt_key_script("export", fingerprint)
255
+
256
+
257
+ def update():
258
+ # type: () -> str
259
+ """Update the local keyring with the archive keyring and remove from
260
+ the local keyring the archive keys which are no longer valid. The
261
+ archive keyring is shipped in the archive-keyring package of your
262
+ distribution, e.g. the debian-archive-keyring package in Debian.
263
+ """
264
+ return _call_apt_key_script("update")
265
+
266
+
267
+ def net_update():
268
+ # type: () -> str
269
+ """Work similar to the update command above, but get the archive
270
+ keyring from an URI instead and validate it against a master key.
271
+ This requires an installed wget(1) and an APT build configured to
272
+ have a server to fetch from and a master keyring to validate. APT
273
+ in Debian does not support this command and relies on update
274
+ instead, but Ubuntu's APT does.
275
+ """
276
+ return _call_apt_key_script("net-update")
277
+
278
+
279
+ def list_keys():
280
+ # type: () -> List[TrustedKey]
281
+ """Returns a list of TrustedKey instances for each key which is
282
+ used to trust repositories.
283
+ """
284
+ # The output of `apt-key list` is difficult to parse since the
285
+ # --with-colons parameter isn't user
286
+ output = _call_apt_key_script("adv", "--with-colons", "--batch",
287
+ "--fixed-list-mode", "--list-keys")
288
+ res = []
289
+ for line in output.split("\n"):
290
+ fields = line.split(":")
291
+ if fields[0] == "pub":
292
+ keyid = fields[4]
293
+ if fields[0] == "uid":
294
+ uid = fields[9]
295
+ creation_date = fields[5]
296
+ key = TrustedKey(uid, keyid, creation_date)
297
+ res.append(key)
298
+ return res
299
+
300
+
301
+ if __name__ == "__main__":
302
+ # Add some known keys we would like to see translated so that they get
303
+ # picked up by gettext
304
+ lambda: _("Ubuntu Archive Automatic Signing Key <[email protected]>")
305
+ lambda: _("Ubuntu CD Image Automatic Signing Key <[email protected]>")
306
+
307
+ apt_pkg.init()
308
+ for trusted_key in list_keys():
309
+ print(trusted_key)
build/lib.linux-x86_64-cpython-310/apt/cache.py ADDED
@@ -0,0 +1,1038 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # cache.py - apt cache abstraction
2
+ #
3
+ # Copyright (c) 2005-2009 Canonical
4
+ #
5
+ # Author: Michael Vogt <[email protected]>
6
+ #
7
+ # This program is free software; you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as
9
+ # published by the Free Software Foundation; either version 2 of the
10
+ # License, or (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
20
+ # USA
21
+
22
+ from __future__ import print_function
23
+
24
+ import fnmatch
25
+ import os
26
+ import warnings
27
+ import weakref
28
+
29
+
30
+ from typing import (Any, Callable, Dict, Iterator, List, Optional,
31
+ Set, Tuple, Union, cast, KeysView)
32
+
33
+ import apt_pkg
34
+ from apt.package import Package, Version
35
+ import apt.progress.text
36
+ from apt.progress.base import AcquireProgress, InstallProgress, OpProgress
37
+
38
+
39
+ class FetchCancelledException(IOError):
40
+ """Exception that is thrown when the user cancels a fetch operation."""
41
+
42
+
43
+ class FetchFailedException(IOError):
44
+ """Exception that is thrown when fetching fails."""
45
+
46
+
47
+ class UntrustedException(FetchFailedException):
48
+ """Exception that is thrown when fetching fails for trust reasons"""
49
+
50
+
51
+ class LockFailedException(IOError):
52
+ """Exception that is thrown when locking fails."""
53
+
54
+
55
+ class CacheClosedException(Exception):
56
+ """Exception that is thrown when the cache is used after close()."""
57
+
58
+
59
+ class _WrappedLock(object):
60
+ """Wraps an apt_pkg.FileLock to raise LockFailedException.
61
+
62
+ Initialized using a directory path."""
63
+
64
+ def __init__(self, path):
65
+ # type: (str) -> None
66
+ self._path = path
67
+ self._lock = apt_pkg.FileLock(os.path.join(path, "lock"))
68
+
69
+ def __enter__(self):
70
+ # type: () -> None
71
+ try:
72
+ return self._lock.__enter__()
73
+ except apt_pkg.Error as e:
74
+ raise LockFailedException(("Failed to lock directory %s: %s") %
75
+ (self._path, e))
76
+
77
+ def __exit__(self, typ, value, traceback):
78
+ # type: (object, object, object) -> None
79
+ return self._lock.__exit__(typ, value, traceback)
80
+
81
+
82
+ class Cache(object):
83
+ """Dictionary-like package cache.
84
+
85
+ The APT cache file contains a hash table mapping names of binary
86
+ packages to their metadata. A Cache object is the in-core
87
+ representation of the same. It provides access to APTs idea of the
88
+ list of available packages.
89
+
90
+ The cache can be used like a mapping from package names to Package
91
+ objects (although only getting items is supported).
92
+
93
+ Keyword arguments:
94
+ progress -- a OpProgress object,
95
+ rootdir -- an alternative root directory. if that is given the system
96
+ sources.list and system lists/files are not read, only file relative
97
+ to the given rootdir,
98
+ memonly -- build the cache in memory only.
99
+
100
+
101
+ .. versionchanged:: 1.0
102
+
103
+ The cache now supports package names with special architecture
104
+ qualifiers such as :all and :native. It does not export them
105
+ in :meth:`keys()`, though, to keep :meth:`keys()` a unique set.
106
+ """
107
+
108
+ def __init__(self, progress=None, rootdir=None, memonly=False):
109
+ # type: (Optional[OpProgress], Optional[str], bool) -> None
110
+ self._cache = cast(apt_pkg.Cache, None) # type: apt_pkg.Cache
111
+ self._depcache = cast(apt_pkg.DepCache, None) # type: apt_pkg.DepCache
112
+ self._records = cast(apt_pkg.PackageRecords, None) # type: apt_pkg.PackageRecords # noqa
113
+ self._list = cast(apt_pkg.SourceList, None) # type: apt_pkg.SourceList
114
+ self._callbacks = {} # type: Dict[str, List[Union[Callable[..., None],str]]] # noqa
115
+ self._callbacks2 = {} # type: Dict[str, List[Tuple[Callable[..., Any], Tuple[Any, ...], Dict[Any,Any]]]] # noqa
116
+ self._weakref = weakref.WeakValueDictionary() # type: weakref.WeakValueDictionary[str, apt.Package] # noqa
117
+ self._weakversions = weakref.WeakSet() # type: weakref.WeakSet[Version] # noqa
118
+ self._changes_count = -1
119
+ self._sorted_set = None # type: Optional[List[str]]
120
+
121
+ self.connect("cache_post_open", "_inc_changes_count")
122
+ self.connect("cache_post_change", "_inc_changes_count")
123
+ if memonly:
124
+ # force apt to build its caches in memory
125
+ apt_pkg.config.set("Dir::Cache::pkgcache", "")
126
+ if rootdir:
127
+ rootdir = os.path.abspath(rootdir)
128
+ if os.path.exists(rootdir + "/etc/apt/apt.conf"):
129
+ apt_pkg.read_config_file(apt_pkg.config,
130
+ rootdir + "/etc/apt/apt.conf")
131
+ if os.path.isdir(rootdir + "/etc/apt/apt.conf.d"):
132
+ apt_pkg.read_config_dir(apt_pkg.config,
133
+ rootdir + "/etc/apt/apt.conf.d")
134
+ apt_pkg.config.set("Dir", rootdir)
135
+ apt_pkg.config.set("Dir::State::status",
136
+ rootdir + "/var/lib/dpkg/status")
137
+ # also set dpkg to the rootdir path so that its called for the
138
+ # --print-foreign-architectures call
139
+ apt_pkg.config.set("Dir::bin::dpkg",
140
+ os.path.join(rootdir, "usr", "bin", "dpkg"))
141
+ # create required dirs/files when run with special rootdir
142
+ # automatically
143
+ self._check_and_create_required_dirs(rootdir)
144
+ # Call InitSystem so the change to Dir::State::Status is actually
145
+ # recognized (LP: #320665)
146
+ apt_pkg.init_system()
147
+
148
+ # Prepare a lock object (context manager for archive lock)
149
+ archive_dir = apt_pkg.config.find_dir("Dir::Cache::Archives")
150
+ self._archive_lock = _WrappedLock(archive_dir)
151
+
152
+ self.open(progress)
153
+
154
+ def fix_broken(self):
155
+ # type: () -> None
156
+ """Fix broken packages."""
157
+ self._depcache.fix_broken()
158
+
159
+ def _inc_changes_count(self):
160
+ # type: () -> None
161
+ """Increase the number of changes"""
162
+ self._changes_count += 1
163
+
164
+ def _check_and_create_required_dirs(self, rootdir):
165
+ # type: (str) -> None
166
+ """
167
+ check if the required apt directories/files are there and if
168
+ not create them
169
+ """
170
+ files = [
171
+ "/var/lib/dpkg/status",
172
+ "/etc/apt/sources.list",
173
+ ]
174
+ dirs = [
175
+ "/var/lib/dpkg",
176
+ "/etc/apt/",
177
+ "/var/cache/apt/archives/partial",
178
+ "/var/lib/apt/lists/partial",
179
+ ]
180
+ for d in dirs:
181
+ if not os.path.exists(rootdir + d):
182
+ #print "creating: ", rootdir + d
183
+ os.makedirs(rootdir + d)
184
+ for f in files:
185
+ if not os.path.exists(rootdir + f):
186
+ open(rootdir + f, "w").close()
187
+
188
+ def _run_callbacks(self, name):
189
+ # type: (str) -> None
190
+ """ internal helper to run a callback """
191
+ if name in self._callbacks:
192
+ for callback in self._callbacks[name]:
193
+ if callback == '_inc_changes_count':
194
+ self._inc_changes_count()
195
+ else:
196
+ callback() # type: ignore
197
+
198
+ if name in self._callbacks2:
199
+ for callback, args, kwds in self._callbacks2[name]:
200
+ callback(self, *args, **kwds)
201
+
202
+ def open(self, progress=None):
203
+ # type: (Optional[OpProgress]) -> None
204
+ """ Open the package cache, after that it can be used like
205
+ a dictionary
206
+ """
207
+ if progress is None:
208
+ progress = apt.progress.base.OpProgress()
209
+ # close old cache on (re)open
210
+ self.close()
211
+ self.op_progress = progress
212
+ self._run_callbacks("cache_pre_open")
213
+
214
+ self._cache = apt_pkg.Cache(progress)
215
+ self._depcache = apt_pkg.DepCache(self._cache)
216
+ self._records = apt_pkg.PackageRecords(self._cache)
217
+ self._list = apt_pkg.SourceList()
218
+ self._list.read_main_list()
219
+ self._sorted_set = None
220
+ self.__remap()
221
+
222
+ self._have_multi_arch = len(apt_pkg.get_architectures()) > 1
223
+
224
+ progress.done()
225
+ self._run_callbacks("cache_post_open")
226
+
227
+ def __remap(self):
228
+ # type: () -> None
229
+ """Called after cache reopen() to relocate to new cache.
230
+
231
+ Relocate objects like packages and versions from the old
232
+ underlying cache to the new one.
233
+ """
234
+ for key in list(self._weakref.keys()):
235
+ try:
236
+ pkg = self._weakref[key]
237
+ except KeyError:
238
+ continue
239
+
240
+ try:
241
+ pkg._pkg = self._cache[pkg._pkg.name, pkg._pkg.architecture]
242
+ except LookupError:
243
+ del self._weakref[key]
244
+
245
+ for ver in list(self._weakversions):
246
+ # Package has been reseated above, reseat version
247
+ for v in ver.package._pkg.version_list:
248
+ # Requirements as in debListParser::SameVersion
249
+ if (v.hash == ver._cand.hash and
250
+ (v.size == 0 or ver._cand.size == 0 or
251
+ v.size == ver._cand.size) and
252
+ v.multi_arch == ver._cand.multi_arch and
253
+ v.ver_str == ver._cand.ver_str):
254
+ ver._cand = v
255
+ break
256
+ else:
257
+ self._weakversions.remove(ver)
258
+
259
+ def close(self):
260
+ # type: () -> None
261
+ """ Close the package cache """
262
+ # explicitely free the FDs that _records has open
263
+ del self._records
264
+ self._records = cast(apt_pkg.PackageRecords, None)
265
+
266
+ def __enter__(self):
267
+ # type: () -> Cache
268
+ """ Enter the with statement """
269
+ return self
270
+
271
+ def __exit__(self, exc_type, exc_value, traceback):
272
+ # type: (object, object, object) -> None
273
+ """ Exit the with statement """
274
+ self.close()
275
+
276
+ def __getitem__(self, key):
277
+ # type: (object) -> Package
278
+ """ look like a dictionary (get key) """
279
+ try:
280
+ key = str(key)
281
+ rawpkg = self._cache[key]
282
+ except KeyError:
283
+ raise KeyError('The cache has no package named %r' % key)
284
+
285
+ # It might be excluded due to not having a version or something
286
+ if not self.__is_real_pkg(rawpkg):
287
+ raise KeyError('The cache has no package named %r' % key)
288
+
289
+ pkg = self._rawpkg_to_pkg(rawpkg)
290
+
291
+ return pkg
292
+
293
+ def get(self, key, default=None):
294
+ # type: (object, object) -> Any
295
+ """Return *self*[*key*] or *default* if *key* not in *self*.
296
+
297
+ .. versionadded:: 1.1
298
+ """
299
+ try:
300
+ return self[key]
301
+ except KeyError:
302
+ return default
303
+
304
+ def _rawpkg_to_pkg(self, rawpkg):
305
+ # type: (apt_pkg.Package) -> Package
306
+ """Returns the apt.Package object for an apt_pkg.Package object.
307
+
308
+ .. versionadded:: 1.0.0
309
+ """
310
+ fullname = rawpkg.get_fullname(pretty=True)
311
+
312
+ return self._weakref.setdefault(fullname, Package(self, rawpkg))
313
+
314
+ def __iter__(self):
315
+ # type: () -> Iterator[Package]
316
+ # We iterate sorted over package names here. With this we read the
317
+ # package lists linearly if we need to access the package records,
318
+ # instead of having to do thousands of random seeks; the latter
319
+ # is disastrous if we use compressed package indexes, and slower than
320
+ # necessary for uncompressed indexes.
321
+ for pkgname in self.keys():
322
+ pkg = Package(self, self._cache[pkgname])
323
+ yield self._weakref.setdefault(pkgname, pkg)
324
+
325
+ def __is_real_pkg(self, rawpkg):
326
+ # type: (apt_pkg.Package) -> bool
327
+ """Check if the apt_pkg.Package provided is a real package."""
328
+ return rawpkg.has_versions
329
+
330
+ def has_key(self, key):
331
+ # type: (object) -> bool
332
+ return key in self
333
+
334
+ def __contains__(self, key):
335
+ # type: (object) -> bool
336
+ try:
337
+ return self.__is_real_pkg(self._cache[str(key)])
338
+ except KeyError:
339
+ return False
340
+
341
+ def __len__(self):
342
+ # type: () -> int
343
+ return len(self.keys())
344
+
345
+ def keys(self):
346
+ # type: () -> List[str]
347
+ if self._sorted_set is None:
348
+ self._sorted_set = sorted(p.get_fullname(pretty=True)
349
+ for p in self._cache.packages
350
+ if self.__is_real_pkg(p))
351
+ return list(self._sorted_set) # We need a copy here, caller may modify
352
+
353
+ def get_changes(self):
354
+ # type: () -> List[Package]
355
+ """ Get the marked changes """
356
+ changes = []
357
+ marked_keep = self._depcache.marked_keep
358
+ for rawpkg in self._cache.packages:
359
+ if not marked_keep(rawpkg):
360
+ changes.append(self._rawpkg_to_pkg(rawpkg))
361
+ return changes
362
+
363
+ def upgrade(self, dist_upgrade=False):
364
+ # type: (bool) -> None
365
+ """Upgrade all packages.
366
+
367
+ If the parameter *dist_upgrade* is True, new dependencies will be
368
+ installed as well (and conflicting packages may be removed). The
369
+ default value is False.
370
+ """
371
+ self.cache_pre_change()
372
+ self._depcache.upgrade(dist_upgrade)
373
+ self.cache_post_change()
374
+
375
+ @property
376
+ def required_download(self):
377
+ # type: () -> int
378
+ """Get the size of the packages that are required to download."""
379
+ if self._records is None:
380
+ raise CacheClosedException(
381
+ "Cache object used after close() called")
382
+ pm = apt_pkg.PackageManager(self._depcache)
383
+ fetcher = apt_pkg.Acquire()
384
+ pm.get_archives(fetcher, self._list, self._records)
385
+ return fetcher.fetch_needed
386
+
387
+ @property
388
+ def required_space(self):
389
+ # type: () -> int
390
+ """Get the size of the additional required space on the fs."""
391
+ return self._depcache.usr_size
392
+
393
+ @property
394
+ def req_reinstall_pkgs(self):
395
+ # type: () -> Set[str]
396
+ """Return the packages not downloadable packages in reqreinst state."""
397
+ reqreinst = set()
398
+ get_candidate_ver = self._depcache.get_candidate_ver
399
+ states = frozenset((apt_pkg.INSTSTATE_REINSTREQ,
400
+ apt_pkg.INSTSTATE_HOLD_REINSTREQ))
401
+ for pkg in self._cache.packages:
402
+ cand = get_candidate_ver(pkg)
403
+ if cand and not cand.downloadable and pkg.inst_state in states:
404
+ reqreinst.add(pkg.get_fullname(pretty=True))
405
+ return reqreinst
406
+
407
+ def _run_fetcher(self, fetcher, allow_unauthenticated):
408
+ # type: (apt_pkg.Acquire, Optional[bool]) -> int
409
+ if allow_unauthenticated is None:
410
+ allow_unauthenticated = apt_pkg.config.find_b("APT::Get::"
411
+ "AllowUnauthenticated", False)
412
+
413
+ untrusted = [item for item in fetcher.items if not item.is_trusted]
414
+ if untrusted and not allow_unauthenticated:
415
+ raise UntrustedException("Untrusted packages:\n%s" %
416
+ "\n".join(i.desc_uri for i in untrusted))
417
+
418
+ # do the actual fetching
419
+ res = fetcher.run()
420
+
421
+ # now check the result (this is the code from apt-get.cc)
422
+ failed = False
423
+ err_msg = ""
424
+ for item in fetcher.items:
425
+ if item.status == item.STAT_DONE:
426
+ continue
427
+ if item.STAT_IDLE:
428
+ continue
429
+ err_msg += "Failed to fetch %s %s\n" % (item.desc_uri,
430
+ item.error_text)
431
+ failed = True
432
+
433
+ # we raise a exception if the download failed or it was cancelt
434
+ if res == fetcher.RESULT_CANCELLED:
435
+ raise FetchCancelledException(err_msg)
436
+ elif failed:
437
+ raise FetchFailedException(err_msg)
438
+ return res
439
+
440
+ def _fetch_archives(self,
441
+ fetcher, # type: apt_pkg.Acquire
442
+ pm, # type: apt_pkg.PackageManager
443
+ allow_unauthenticated=None, # type: Optional[bool]
444
+ ):
445
+ # type: (...) -> int
446
+ """ fetch the needed archives """
447
+ if self._records is None:
448
+ raise CacheClosedException(
449
+ "Cache object used after close() called")
450
+
451
+ # this may as well throw a SystemError exception
452
+ if not pm.get_archives(fetcher, self._list, self._records):
453
+ return False
454
+
455
+ # now run the fetcher, throw exception if something fails to be
456
+ # fetched
457
+ return self._run_fetcher(fetcher, allow_unauthenticated)
458
+
459
+ def fetch_archives(self,
460
+ progress=None, # type: Optional[AcquireProgress]
461
+ fetcher=None, # type: Optional[apt_pkg.Acquire]
462
+ allow_unauthenticated=None, # type: Optional[bool]
463
+ ):
464
+ # type: (...) -> int
465
+ """Fetch the archives for all packages marked for install/upgrade.
466
+
467
+ You can specify either an :class:`apt.progress.base.AcquireProgress()`
468
+ object for the parameter *progress*, or specify an already
469
+ existing :class:`apt_pkg.Acquire` object for the parameter *fetcher*.
470
+
471
+ The return value of the function is undefined. If an error occurred,
472
+ an exception of type :class:`FetchFailedException` or
473
+ :class:`FetchCancelledException` is raised.
474
+
475
+ The keyword-only parameter *allow_unauthenticated* specifies whether
476
+ to allow unauthenticated downloads. If not specified, it defaults to
477
+ the configuration option `APT::Get::AllowUnauthenticated`.
478
+
479
+ .. versionadded:: 0.8.0
480
+ """
481
+ if progress is not None and fetcher is not None:
482
+ raise ValueError("Takes a progress or a an Acquire object")
483
+ if progress is None:
484
+ progress = apt.progress.text.AcquireProgress()
485
+ if fetcher is None:
486
+ fetcher = apt_pkg.Acquire(progress)
487
+
488
+ with self._archive_lock:
489
+ return self._fetch_archives(fetcher,
490
+ apt_pkg.PackageManager(self._depcache),
491
+ allow_unauthenticated)
492
+
493
+ def is_virtual_package(self, pkgname):
494
+ # type: (str) -> bool
495
+ """Return whether the package is a virtual package."""
496
+ try:
497
+ pkg = self._cache[pkgname]
498
+ except KeyError:
499
+ return False
500
+ else:
501
+ return bool(pkg.has_provides and not pkg.has_versions)
502
+
503
+ def get_providing_packages(self, pkgname, candidate_only=True,
504
+ include_nonvirtual=False):
505
+ # type: (str, bool, bool) -> List[Package]
506
+ """Return a list of all packages providing a package.
507
+
508
+ Return a list of packages which provide the virtual package of the
509
+ specified name.
510
+
511
+ If 'candidate_only' is False, return all packages with at
512
+ least one version providing the virtual package. Otherwise,
513
+ return only those packages where the candidate version
514
+ provides the virtual package.
515
+
516
+ If 'include_nonvirtual' is True then it will search for all
517
+ packages providing pkgname, even if pkgname is not itself
518
+ a virtual pkg.
519
+ """
520
+
521
+ providers = set() # type: Set[Package]
522
+ get_candidate_ver = self._depcache.get_candidate_ver
523
+ try:
524
+ vp = self._cache[pkgname]
525
+ if vp.has_versions and not include_nonvirtual:
526
+ return list(providers)
527
+ except KeyError:
528
+ return list(providers)
529
+
530
+ for provides, providesver, version in vp.provides_list:
531
+ rawpkg = version.parent_pkg
532
+ if not candidate_only or (version == get_candidate_ver(rawpkg)):
533
+ providers.add(self._rawpkg_to_pkg(rawpkg))
534
+ return list(providers)
535
+
536
+ def update(self, fetch_progress=None, pulse_interval=0,
537
+ raise_on_error=True, sources_list=None):
538
+ # type: (Optional[AcquireProgress], int, bool, Optional[str]) -> int
539
+ """Run the equivalent of apt-get update.
540
+
541
+ You probably want to call open() afterwards, in order to utilise the
542
+ new cache. Otherwise, the old cache will be used which can lead to
543
+ strange bugs.
544
+
545
+ The first parameter *fetch_progress* may be set to an instance of
546
+ apt.progress.FetchProgress, the default is apt.progress.FetchProgress()
547
+ .
548
+ sources_list -- Update a alternative sources.list than the default.
549
+ Note that the sources.list.d directory is ignored in this case
550
+ """
551
+ with _WrappedLock(apt_pkg.config.find_dir("Dir::State::Lists")):
552
+ if sources_list:
553
+ old_sources_list = apt_pkg.config.find("Dir::Etc::sourcelist")
554
+ old_sources_list_d = (
555
+ apt_pkg.config.find("Dir::Etc::sourceparts"))
556
+ old_cleanup = apt_pkg.config.find("APT::List-Cleanup")
557
+ apt_pkg.config.set("Dir::Etc::sourcelist",
558
+ os.path.abspath(sources_list))
559
+ apt_pkg.config.set("Dir::Etc::sourceparts", "xxx")
560
+ apt_pkg.config.set("APT::List-Cleanup", "0")
561
+ slist = apt_pkg.SourceList()
562
+ slist.read_main_list()
563
+ else:
564
+ slist = self._list
565
+
566
+ try:
567
+ if fetch_progress is None:
568
+ fetch_progress = apt.progress.base.AcquireProgress()
569
+ try:
570
+ res = self._cache.update(fetch_progress, slist,
571
+ pulse_interval)
572
+ except SystemError as e:
573
+ raise FetchFailedException(e)
574
+ if not res and raise_on_error:
575
+ raise FetchFailedException()
576
+ else:
577
+ return res
578
+ finally:
579
+ if sources_list:
580
+ apt_pkg.config.set("Dir::Etc::sourcelist",
581
+ old_sources_list)
582
+ apt_pkg.config.set("Dir::Etc::sourceparts",
583
+ old_sources_list_d)
584
+ apt_pkg.config.set("APT::List-Cleanup",
585
+ old_cleanup)
586
+
587
+ def install_archives(self, pm, install_progress):
588
+ # type: (apt_pkg.PackageManager, InstallProgress) -> int
589
+ """
590
+ The first parameter *pm* refers to an object returned by
591
+ apt_pkg.PackageManager().
592
+
593
+ The second parameter *install_progress* refers to an InstallProgress()
594
+ object of the module apt.progress.
595
+
596
+ This releases a system lock in newer versions, if there is any,
597
+ and reestablishes it afterwards.
598
+ """
599
+ # compat with older API
600
+ try:
601
+ install_progress.startUpdate() # type: ignore
602
+ except AttributeError:
603
+ install_progress.start_update()
604
+
605
+ did_unlock = apt_pkg.pkgsystem_is_locked()
606
+ if did_unlock:
607
+ apt_pkg.pkgsystem_unlock_inner()
608
+
609
+ try:
610
+ res = install_progress.run(pm)
611
+ finally:
612
+ if did_unlock:
613
+ apt_pkg.pkgsystem_lock_inner()
614
+
615
+ try:
616
+ install_progress.finishUpdate() # type: ignore
617
+ except AttributeError:
618
+ install_progress.finish_update()
619
+ return res
620
+
621
+ def commit(self,
622
+ fetch_progress=None, # type: Optional[AcquireProgress]
623
+ install_progress=None, # type: Optional[InstallProgress]
624
+ allow_unauthenticated=None, # type: Optional[bool]
625
+ ):
626
+ # type: (...) -> bool
627
+ """Apply the marked changes to the cache.
628
+
629
+ The first parameter, *fetch_progress*, refers to a FetchProgress()
630
+ object as found in apt.progress, the default being
631
+ apt.progress.FetchProgress().
632
+
633
+ The second parameter, *install_progress*, is a
634
+ apt.progress.InstallProgress() object.
635
+
636
+ The keyword-only parameter *allow_unauthenticated* specifies whether
637
+ to allow unauthenticated downloads. If not specified, it defaults to
638
+ the configuration option `APT::Get::AllowUnauthenticated`.
639
+ """
640
+ # FIXME:
641
+ # use the new acquire/pkgmanager interface here,
642
+ # raise exceptions when a download or install fails
643
+ # and send proper error strings to the application.
644
+ # Current a failed download will just display "error"
645
+ # which is less than optimal!
646
+
647
+ if fetch_progress is None:
648
+ fetch_progress = apt.progress.base.AcquireProgress()
649
+ if install_progress is None:
650
+ install_progress = apt.progress.base.InstallProgress()
651
+
652
+ assert install_progress is not None
653
+
654
+ with apt_pkg.SystemLock():
655
+ pm = apt_pkg.PackageManager(self._depcache)
656
+ fetcher = apt_pkg.Acquire(fetch_progress)
657
+ with self._archive_lock:
658
+ while True:
659
+ # fetch archives first
660
+ res = self._fetch_archives(fetcher, pm,
661
+ allow_unauthenticated)
662
+
663
+ # then install
664
+ res = self.install_archives(pm, install_progress)
665
+ if res == pm.RESULT_COMPLETED:
666
+ break
667
+ elif res == pm.RESULT_FAILED:
668
+ raise SystemError("installArchives() failed")
669
+ elif res == pm.RESULT_INCOMPLETE:
670
+ pass
671
+ else:
672
+ raise SystemError("internal-error: unknown result "
673
+ "code from InstallArchives: %s" %
674
+ res)
675
+ # reload the fetcher for media swaping
676
+ fetcher.shutdown()
677
+ return (res == pm.RESULT_COMPLETED)
678
+
679
+ def clear(self):
680
+ # type: () -> None
681
+ """ Unmark all changes """
682
+ self._depcache.init()
683
+
684
+ # cache changes
685
+
686
+ def cache_post_change(self):
687
+ # type: () -> None
688
+ " called internally if the cache has changed, emit a signal then "
689
+ self._run_callbacks("cache_post_change")
690
+
691
+ def cache_pre_change(self):
692
+ # type: () -> None
693
+ """ called internally if the cache is about to change, emit
694
+ a signal then """
695
+ self._run_callbacks("cache_pre_change")
696
+
697
+ def connect(self, name, callback):
698
+ # type: (str, Union[Callable[..., None],str]) -> None
699
+ """Connect to a signal.
700
+
701
+ .. deprecated:: 1.0
702
+
703
+ Please use connect2() instead, as this function is very
704
+ likely to cause a memory leak.
705
+ """
706
+ if callback != '_inc_changes_count':
707
+ warnings.warn("connect() likely causes a reference"
708
+ " cycle, use connect2() instead", RuntimeWarning, 2)
709
+ if name not in self._callbacks:
710
+ self._callbacks[name] = []
711
+ self._callbacks[name].append(callback)
712
+
713
+ def connect2(self, name, callback, *args, **kwds):
714
+ # type: (str, Callable[..., Any], object, object) -> None
715
+ """Connect to a signal.
716
+
717
+ The callback will be passed the cache as an argument, and
718
+ any arguments passed to this function. Make sure that, if you
719
+ pass a method of a class as your callback, your class does not
720
+ contain a reference to the cache.
721
+
722
+ Cyclic references to the cache can cause issues if the Cache object
723
+ is replaced by a new one, because the cache keeps a lot of objects and
724
+ tens of open file descriptors.
725
+
726
+ currently only used for cache_{post,pre}_{changed,open}.
727
+
728
+ .. versionadded:: 1.0
729
+ """
730
+ if name not in self._callbacks2:
731
+ self._callbacks2[name] = []
732
+ self._callbacks2[name].append((callback, args, kwds))
733
+
734
+ def actiongroup(self):
735
+ # type: () -> apt_pkg.ActionGroup
736
+ """Return an `ActionGroup` object for the current cache.
737
+
738
+ Action groups can be used to speedup actions. The action group is
739
+ active as soon as it is created, and disabled when the object is
740
+ deleted or when release() is called.
741
+
742
+ You can use the action group as a context manager, this is the
743
+ recommended way::
744
+
745
+ with cache.actiongroup():
746
+ for package in my_selected_packages:
747
+ package.mark_install()
748
+
749
+ This way, the action group is automatically released as soon as the
750
+ with statement block is left. It also has the benefit of making it
751
+ clear which parts of the code run with a action group and which
752
+ don't.
753
+ """
754
+ return apt_pkg.ActionGroup(self._depcache)
755
+
756
+ @property
757
+ def dpkg_journal_dirty(self):
758
+ # type: () -> bool
759
+ """Return True if the dpkg was interrupted
760
+
761
+ All dpkg operations will fail until this is fixed, the action to
762
+ fix the system if dpkg got interrupted is to run
763
+ 'dpkg --configure -a' as root.
764
+ """
765
+ dpkg_status_dir = os.path.dirname(
766
+ apt_pkg.config.find_file("Dir::State::status"))
767
+ for f in os.listdir(os.path.join(dpkg_status_dir, "updates")):
768
+ if fnmatch.fnmatch(f, "[0-9]*"):
769
+ return True
770
+ return False
771
+
772
+ @property
773
+ def broken_count(self):
774
+ # type: () -> int
775
+ """Return the number of packages with broken dependencies."""
776
+ return self._depcache.broken_count
777
+
778
+ @property
779
+ def delete_count(self):
780
+ # type: () -> int
781
+ """Return the number of packages marked for deletion."""
782
+ return self._depcache.del_count
783
+
784
+ @property
785
+ def install_count(self):
786
+ # type: () -> int
787
+ """Return the number of packages marked for installation."""
788
+ return self._depcache.inst_count
789
+
790
+ @property
791
+ def keep_count(self):
792
+ # type: () -> int
793
+ """Return the number of packages marked as keep."""
794
+ return self._depcache.keep_count
795
+
796
+
797
+ class ProblemResolver(object):
798
+ """Resolve problems due to dependencies and conflicts.
799
+
800
+ The first argument 'cache' is an instance of apt.Cache.
801
+ """
802
+
803
+ def __init__(self, cache):
804
+ # type: (Cache) -> None
805
+ self._resolver = apt_pkg.ProblemResolver(cache._depcache)
806
+ self._cache = cache
807
+
808
+ def clear(self, package):
809
+ # type: (Package) -> None
810
+ """Reset the package to the default state."""
811
+ self._resolver.clear(package._pkg)
812
+
813
+ def protect(self, package):
814
+ # type: (Package) -> None
815
+ """Protect a package so it won't be removed."""
816
+ self._resolver.protect(package._pkg)
817
+
818
+ def remove(self, package):
819
+ # type: (Package) -> None
820
+ """Mark a package for removal."""
821
+ self._resolver.remove(package._pkg)
822
+
823
+ def resolve(self):
824
+ # type: () -> None
825
+ """Resolve dependencies, try to remove packages where needed."""
826
+ self._cache.cache_pre_change()
827
+ self._resolver.resolve()
828
+ self._cache.cache_post_change()
829
+
830
+ def resolve_by_keep(self):
831
+ # type: () -> None
832
+ """Resolve dependencies, do not try to remove packages."""
833
+ self._cache.cache_pre_change()
834
+ self._resolver.resolve_by_keep()
835
+ self._cache.cache_post_change()
836
+
837
+
838
+ # ----------------------------- experimental interface
839
+
840
+
841
+ class Filter(object):
842
+ """ Filter base class """
843
+
844
+ def apply(self, pkg):
845
+ # type: (Package) -> bool
846
+ """ Filter function, return True if the package matchs a
847
+ filter criteria and False otherwise
848
+ """
849
+ return True
850
+
851
+
852
+ class MarkedChangesFilter(Filter):
853
+ """ Filter that returns all marked changes """
854
+
855
+ def apply(self, pkg):
856
+ # type: (Package) -> bool
857
+ if pkg.marked_install or pkg.marked_delete or pkg.marked_upgrade:
858
+ return True
859
+ else:
860
+ return False
861
+
862
+
863
+ class InstalledFilter(Filter):
864
+ """Filter that returns all installed packages.
865
+
866
+ .. versionadded:: 1.0.0
867
+ """
868
+
869
+ def apply(self, pkg):
870
+ # type: (Package) -> bool
871
+ return pkg.is_installed
872
+
873
+
874
+ class _FilteredCacheHelper(object):
875
+ """Helper class for FilteredCache to break a reference cycle."""
876
+
877
+ def __init__(self, cache):
878
+ # type: (Cache) -> None
879
+ # Do not keep a reference to the cache, or you have a cycle!
880
+
881
+ self._filtered = {} # type: Dict[str,bool]
882
+ self._filters = [] # type: List[Filter]
883
+ cache.connect2("cache_post_change", self.filter_cache_post_change)
884
+ cache.connect2("cache_post_open", self.filter_cache_post_change)
885
+
886
+ def _reapply_filter(self, cache):
887
+ # type: (Cache) -> None
888
+ " internal helper to refilter "
889
+ # Do not keep a reference to the cache, or you have a cycle!
890
+ self._filtered = {}
891
+ for pkg in cache:
892
+ for f in self._filters:
893
+ if f.apply(pkg):
894
+ self._filtered[pkg.name] = True
895
+ break
896
+
897
+ def set_filter(self, filter):
898
+ # type: (Filter) -> None
899
+ """Set the current active filter."""
900
+ self._filters = []
901
+ self._filters.append(filter)
902
+
903
+ def filter_cache_post_change(self, cache):
904
+ # type: (Cache) -> None
905
+ """Called internally if the cache changes, emit a signal then."""
906
+ # Do not keep a reference to the cache, or you have a cycle!
907
+ self._reapply_filter(cache)
908
+
909
+
910
+ class FilteredCache(object):
911
+ """ A package cache that is filtered.
912
+
913
+ Can work on a existing cache or create a new one
914
+ """
915
+
916
+ def __init__(self, cache=None, progress=None):
917
+ # type: (Optional[Cache], Optional[OpProgress]) -> None
918
+ if cache is None:
919
+ self.cache = Cache(progress)
920
+ else:
921
+ self.cache = cache
922
+ self._helper = _FilteredCacheHelper(self.cache)
923
+
924
+ def __len__(self):
925
+ # type: () -> int
926
+ return len(self._helper._filtered)
927
+
928
+ def __getitem__(self, key):
929
+ # type: (str) -> Package
930
+ return self.cache[key]
931
+
932
+ def __iter__(self):
933
+ # type: () -> Iterator[Package]
934
+ for pkgname in self._helper._filtered:
935
+ yield self.cache[pkgname]
936
+
937
+ def keys(self):
938
+ # type: () -> KeysView[str]
939
+ return self._helper._filtered.keys()
940
+
941
+ def has_key(self, key):
942
+ # type: (object) -> bool
943
+ return key in self
944
+
945
+ def __contains__(self, key):
946
+ # type: (object) -> bool
947
+ try:
948
+ # Normalize package name for multi arch
949
+ return self.cache[key].name in self._helper._filtered
950
+ except KeyError:
951
+ return False
952
+
953
+ def set_filter(self, filter):
954
+ # type: (Filter) -> None
955
+ """Set the current active filter."""
956
+ self._helper.set_filter(filter)
957
+ self.cache.cache_post_change()
958
+
959
+ def filter_cache_post_change(self):
960
+ # type: () -> None
961
+ """Called internally if the cache changes, emit a signal then."""
962
+ self._helper.filter_cache_post_change(self.cache)
963
+
964
+ def __getattr__(self, key):
965
+ # type: (str) -> Any
966
+ """we try to look exactly like a real cache."""
967
+ return getattr(self.cache, key)
968
+
969
+
970
+ def cache_pre_changed(cache):
971
+ # type: (Cache) -> None
972
+ print("cache pre changed")
973
+
974
+
975
+ def cache_post_changed(cache):
976
+ # type: (Cache) -> None
977
+ print("cache post changed")
978
+
979
+
980
+ def _test():
981
+ # type: () -> None
982
+ """Internal test code."""
983
+ print("Cache self test")
984
+ apt_pkg.init()
985
+ cache = Cache(apt.progress.text.OpProgress())
986
+ cache.connect2("cache_pre_change", cache_pre_changed)
987
+ cache.connect2("cache_post_change", cache_post_changed)
988
+ print(("aptitude" in cache))
989
+ pkg = cache["aptitude"]
990
+ print(pkg.name)
991
+ print(len(cache))
992
+
993
+ for pkgname in cache.keys():
994
+ assert cache[pkgname].name == pkgname
995
+
996
+ cache.upgrade()
997
+ changes = cache.get_changes()
998
+ print(len(changes))
999
+ for pkg in changes:
1000
+ assert pkg.name
1001
+
1002
+ # see if fetching works
1003
+ for dirname in ["/tmp/pytest", "/tmp/pytest/partial"]:
1004
+ if not os.path.exists(dirname):
1005
+ os.mkdir(dirname)
1006
+ apt_pkg.config.set("Dir::Cache::Archives", "/tmp/pytest")
1007
+ pm = apt_pkg.PackageManager(cache._depcache)
1008
+ fetcher = apt_pkg.Acquire(apt.progress.text.AcquireProgress())
1009
+ cache._fetch_archives(fetcher, pm, None)
1010
+ #sys.exit(1)
1011
+
1012
+ print("Testing filtered cache (argument is old cache)")
1013
+ filtered = FilteredCache(cache)
1014
+ filtered.cache.connect2("cache_pre_change", cache_pre_changed)
1015
+ filtered.cache.connect2("cache_post_change", cache_post_changed)
1016
+ filtered.cache.upgrade()
1017
+ filtered.set_filter(MarkedChangesFilter())
1018
+ print(len(filtered))
1019
+ for pkgname in filtered.keys():
1020
+ assert pkgname == filtered[pkgname].name
1021
+
1022
+ print(len(filtered))
1023
+
1024
+ print("Testing filtered cache (no argument)")
1025
+ filtered = FilteredCache(progress=apt.progress.base.OpProgress())
1026
+ filtered.cache.connect2("cache_pre_change", cache_pre_changed)
1027
+ filtered.cache.connect2("cache_post_change", cache_post_changed)
1028
+ filtered.cache.upgrade()
1029
+ filtered.set_filter(MarkedChangesFilter())
1030
+ print(len(filtered))
1031
+ for pkgname in filtered.keys():
1032
+ assert pkgname == filtered[pkgname].name
1033
+
1034
+ print(len(filtered))
1035
+
1036
+
1037
+ if __name__ == '__main__':
1038
+ _test()
build/lib.linux-x86_64-cpython-310/apt/cdrom.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # cdrom.py - CDROM handling
2
+ #
3
+ # Copyright (c) 2005-2009 Canonical
4
+ # Copyright (c) 2009 Julian Andres Klode <[email protected]>
5
+ #
6
+ # Author: Michael Vogt <[email protected]>
7
+ #
8
+ # This program is free software; you can redistribute it and/or
9
+ # modify it under the terms of the GNU General Public License as
10
+ # published by the Free Software Foundation; either version 2 of the
11
+ # License, or (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21
+ # USA
22
+ """Classes related to cdrom handling."""
23
+ from __future__ import print_function
24
+
25
+ from typing import Optional
26
+ import glob
27
+
28
+ import apt_pkg
29
+ from apt.progress.base import CdromProgress
30
+
31
+
32
+ class Cdrom(apt_pkg.Cdrom):
33
+ """Support for apt-cdrom like features.
34
+
35
+ This class has several optional parameters for initialisation, which may
36
+ be used to influence the behaviour of the object:
37
+
38
+ The optional parameter `progress` is a CdromProgress() subclass, which will
39
+ ask for the correct cdrom, etc. If not specified or None, a CdromProgress()
40
+ object will be used.
41
+
42
+ The optional parameter `mountpoint` may be used to specify an alternative
43
+ mountpoint.
44
+
45
+ If the optional parameter `nomount` is True, the cdroms will not be
46
+ mounted. This is the default behaviour.
47
+ """
48
+
49
+ def __init__(self, progress=None, mountpoint=None, nomount=True):
50
+ # type: (Optional[CdromProgress], Optional[str], bool) -> None
51
+ apt_pkg.Cdrom.__init__(self)
52
+ if progress is None:
53
+ self._progress = CdromProgress()
54
+ else:
55
+ self._progress = progress
56
+ # see if we have a alternative mountpoint
57
+ if mountpoint is not None:
58
+ apt_pkg.config.set("Acquire::cdrom::mount", mountpoint)
59
+ # do not mess with mount points by default
60
+ if nomount:
61
+ apt_pkg.config.set("APT::CDROM::NoMount", "true")
62
+ else:
63
+ apt_pkg.config.set("APT::CDROM::NoMount", "false")
64
+
65
+ def add(self, progress=None):
66
+ # type: (Optional[CdromProgress]) -> bool
67
+ """Add cdrom to the sources.list."""
68
+ return apt_pkg.Cdrom.add(self, progress or self._progress)
69
+
70
+ def ident(self, progress=None):
71
+ # type: (Optional[CdromProgress]) -> str
72
+ """Identify the cdrom."""
73
+ return apt_pkg.Cdrom.ident(self, progress or self._progress)
74
+
75
+ @property
76
+ def in_sources_list(self):
77
+ # type: () -> bool
78
+ """Check if the cdrom is already in the current sources.list."""
79
+ cd_id = self.ident()
80
+ if cd_id is None:
81
+ # FIXME: throw exception instead
82
+ return False
83
+ # Get a list of files
84
+ src = glob.glob(apt_pkg.config.find_dir("Dir::Etc::sourceparts") + '*')
85
+ src.append(apt_pkg.config.find_file("Dir::Etc::sourcelist"))
86
+ # Check each file
87
+ for fname in src:
88
+ with open(fname) as fobj:
89
+ for line in fobj:
90
+ if not line.lstrip().startswith("#") and cd_id in line:
91
+ return True
92
+ return False
build/lib.linux-x86_64-cpython-310/apt/debfile.py ADDED
@@ -0,0 +1,868 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2005-2010 Canonical
2
+ #
3
+ # Author: Michael Vogt <[email protected]>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License as
7
+ # published by the Free Software Foundation; either version 2 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
+ # USA
19
+ """Classes for working with locally available Debian packages."""
20
+ from __future__ import print_function
21
+
22
+ import apt
23
+ import apt_inst
24
+ import apt_pkg
25
+ import gzip
26
+ import os
27
+ import sys
28
+
29
+ from typing import Dict, Iterable, List, Optional, Set, Tuple, Union, cast
30
+
31
+ from apt_pkg import gettext as _
32
+ from io import BytesIO
33
+
34
+
35
+ class NoDebArchiveException(IOError):
36
+ """Exception which is raised if a file is no Debian archive."""
37
+
38
+
39
+ class DebPackage(object):
40
+ """A Debian Package (.deb file)."""
41
+
42
+ # Constants for comparing the local package file with the version
43
+ # in the cache
44
+ (VERSION_NONE,
45
+ VERSION_OUTDATED,
46
+ VERSION_SAME,
47
+ VERSION_NEWER) = range(4)
48
+
49
+ debug = 0
50
+
51
+ def __init__(self, filename=None, cache=None):
52
+ # type: (Optional[str], Optional[apt.Cache]) -> None
53
+ if cache is None:
54
+ cache = apt.Cache()
55
+ self._cache = cache
56
+ self._debfile = cast(apt_inst.DebFile, None)
57
+ self.pkgname = ""
58
+ self.filename = None # type: Optional[str]
59
+ self._sections = {} # type: Union[Dict[str, str], apt_pkg.TagSection[str]] # noqa
60
+ self._need_pkgs = [] # type: List[str]
61
+ self._check_was_run = False
62
+ self._failure_string = ""
63
+ self._multiarch = None # type: Optional[str]
64
+ if filename:
65
+ self.open(filename)
66
+
67
+ def open(self, filename):
68
+ # type: (str) -> None
69
+ """ open given debfile """
70
+ self._dbg(3, "open '%s'" % filename)
71
+ self._need_pkgs = []
72
+ self._installed_conflicts = set() # type: Set[str]
73
+ self._failure_string = ""
74
+ self.filename = filename
75
+ self._debfile = apt_inst.DebFile(self.filename)
76
+ control = self._debfile.control.extractdata("control")
77
+ self._sections = apt_pkg.TagSection(control)
78
+ self.pkgname = self._sections["Package"]
79
+ self._check_was_run = False
80
+
81
+ def __getitem__(self, key):
82
+ # type: (str) -> str
83
+ return self._sections[key]
84
+
85
+ def __contains__(self, key):
86
+ # type: (str) -> bool
87
+ return key in self._sections
88
+
89
+ @property
90
+ def filelist(self):
91
+ # type: () -> List[str]
92
+ """return the list of files in the deb."""
93
+ files = []
94
+ try:
95
+ self._debfile.data.go(lambda item, data: files.append(item.name))
96
+ except SystemError:
97
+ return [_("List of files for '%s' could not be read") %
98
+ self.filename]
99
+ return files
100
+
101
+ @property
102
+ def control_filelist(self):
103
+ # type: () -> List[str]
104
+ """ return the list of files in control.tar.gz """
105
+ control = []
106
+ try:
107
+ self._debfile.control.go(
108
+ lambda item, data: control.append(item.name))
109
+ except SystemError:
110
+ return [_("List of control files for '%s' could not be read") %
111
+ self.filename]
112
+ return sorted(control)
113
+
114
+ # helper that will return a pkgname with a multiarch suffix if needed
115
+ def _maybe_append_multiarch_suffix(self, pkgname,
116
+ in_conflict_checking=False):
117
+ # type: (str, bool) -> str
118
+ # trivial cases
119
+ if ":" in pkgname:
120
+ return pkgname
121
+ if not self._multiarch:
122
+ return pkgname
123
+ elif self._cache.is_virtual_package(pkgname):
124
+ return pkgname
125
+ elif (pkgname in self._cache and
126
+ self._cache[pkgname].candidate is not None and
127
+ cast(apt.package.Version,
128
+ self._cache[pkgname].candidate).architecture == "all"):
129
+ return pkgname
130
+ # now do the real multiarch checking
131
+ multiarch_pkgname = "%s:%s" % (pkgname, self._multiarch)
132
+ # the upper layers will handle this
133
+ if multiarch_pkgname not in self._cache:
134
+ return multiarch_pkgname
135
+ multiarch_pkg = self._cache[multiarch_pkgname]
136
+ if multiarch_pkg.candidate is None:
137
+ return multiarch_pkgname
138
+ # now check the multiarch state
139
+ cand = multiarch_pkg.candidate._cand
140
+ #print pkgname, multiarch_pkgname, cand.multi_arch
141
+ # the default is to add the suffix, unless its a pkg that can satify
142
+ # foreign dependencies
143
+ if cand.multi_arch & cand.MULTI_ARCH_FOREIGN:
144
+ return pkgname
145
+ # for conflicts we need a special case here, any not multiarch enabled
146
+ # package has a implicit conflict
147
+ if (in_conflict_checking and
148
+ not (cand.multi_arch & cand.MULTI_ARCH_SAME)):
149
+ return pkgname
150
+ return multiarch_pkgname
151
+
152
+ def _is_or_group_satisfied(self, or_group):
153
+ # type: (List[Tuple[str, str, str]]) -> bool
154
+ """Return True if at least one dependency of the or-group is satisfied.
155
+
156
+ This method gets an 'or_group' and analyzes if at least one dependency
157
+ of this group is already satisfied.
158
+ """
159
+ self._dbg(2, "_checkOrGroup(): %s " % (or_group))
160
+
161
+ for dep in or_group:
162
+ depname = dep[0]
163
+ ver = dep[1]
164
+ oper = dep[2]
165
+
166
+ # multiarch
167
+ depname = self._maybe_append_multiarch_suffix(depname)
168
+
169
+ # check for virtual pkgs
170
+ if depname not in self._cache:
171
+ if self._cache.is_virtual_package(depname):
172
+ self._dbg(
173
+ 3, "_is_or_group_satisfied(): %s is virtual dep" %
174
+ depname)
175
+ for pkg in self._cache.get_providing_packages(depname):
176
+ if pkg.is_installed:
177
+ return True
178
+ continue
179
+ # check real dependency
180
+ inst = self._cache[depname].installed
181
+ if inst is not None and apt_pkg.check_dep(inst.version, oper, ver):
182
+ return True
183
+
184
+ # if no real dependency is installed, check if there is
185
+ # a package installed that provides this dependency
186
+ # (e.g. scrollkeeper dependecies are provided by rarian-compat)
187
+ # but only do that if there is no version required in the
188
+ # dependency (we do not supprot versionized dependencies)
189
+ if not oper:
190
+ for ppkg in self._cache.get_providing_packages(
191
+ depname, include_nonvirtual=True):
192
+ if ppkg.is_installed:
193
+ self._dbg(
194
+ 3, "found installed '%s' that provides '%s'" % (
195
+ ppkg.name, depname))
196
+ return True
197
+ return False
198
+
199
+ def _satisfy_or_group(self, or_group):
200
+ # type: (List[Tuple[str, str, str]]) -> bool
201
+ """Try to satisfy the or_group."""
202
+ for dep in or_group:
203
+ depname, ver, oper = dep
204
+
205
+ # multiarch
206
+ depname = self._maybe_append_multiarch_suffix(depname)
207
+
208
+ # if we don't have it in the cache, it may be virtual
209
+ if depname not in self._cache:
210
+ if not self._cache.is_virtual_package(depname):
211
+ continue
212
+ providers = self._cache.get_providing_packages(depname)
213
+ # if a package just has a single virtual provider, we
214
+ # just pick that (just like apt)
215
+ if len(providers) != 1:
216
+ continue
217
+ depname = providers[0].name
218
+
219
+ # now check if we can satisfy the deps with the candidate(s)
220
+ # in the cache
221
+ pkg = self._cache[depname]
222
+ cand = self._cache._depcache.get_candidate_ver(pkg._pkg)
223
+ if not cand:
224
+ continue
225
+ if not apt_pkg.check_dep(cand.ver_str, oper, ver):
226
+ continue
227
+
228
+ # check if we need to install it
229
+ self._dbg(2, "Need to get: %s" % depname)
230
+ self._need_pkgs.append(depname)
231
+ return True
232
+
233
+ # if we reach this point, we failed
234
+ or_str = ""
235
+ for dep in or_group:
236
+ or_str += dep[0]
237
+ if ver and oper:
238
+ or_str += " (%s %s)" % (dep[2], dep[1])
239
+ if dep != or_group[len(or_group) - 1]:
240
+ or_str += "|"
241
+ self._failure_string += _(
242
+ "Dependency is not satisfiable: %s\n") % or_str
243
+ return False
244
+
245
+ def _check_single_pkg_conflict(self, pkgname, ver, oper):
246
+ # type: (str, str, str) -> bool
247
+ """Return True if a pkg conflicts with a real installed/marked pkg."""
248
+ # FIXME: deal with conflicts against its own provides
249
+ # (e.g. Provides: ftp-server, Conflicts: ftp-server)
250
+ self._dbg(
251
+ 3, "_check_single_pkg_conflict() pkg='%s' ver='%s' oper='%s'" % (
252
+ pkgname, ver, oper))
253
+ pkg = self._cache[pkgname]
254
+ if pkg.is_installed:
255
+ assert pkg.installed is not None
256
+ pkgver = pkg.installed.version
257
+ elif pkg.marked_install:
258
+ assert pkg.candidate is not None
259
+ pkgver = pkg.candidate.version
260
+ else:
261
+ return False
262
+ #print "pkg: %s" % pkgname
263
+ #print "ver: %s" % ver
264
+ #print "pkgver: %s " % pkgver
265
+ #print "oper: %s " % oper
266
+ if (apt_pkg.check_dep(pkgver, oper, ver) and not
267
+ self.replaces_real_pkg(pkgname, oper, ver)):
268
+ self._failure_string += _("Conflicts with the installed package "
269
+ "'%s'") % pkg.name
270
+ self._dbg(3, "conflicts with installed pkg '%s'" % pkg.name)
271
+ return True
272
+ return False
273
+
274
+ def _check_conflicts_or_group(self, or_group):
275
+ # type: (List[Tuple[str, str, str]]) -> bool
276
+ """Check the or-group for conflicts with installed pkgs."""
277
+ self._dbg(2, "_check_conflicts_or_group(): %s " % (or_group))
278
+ for dep in or_group:
279
+ depname = dep[0]
280
+ ver = dep[1]
281
+ oper = dep[2]
282
+
283
+ # FIXME: is this good enough? i.e. will apt always populate
284
+ # the cache with conflicting pkgnames for our arch?
285
+ depname = self._maybe_append_multiarch_suffix(
286
+ depname, in_conflict_checking=True)
287
+
288
+ # check conflicts with virtual pkgs
289
+ if depname not in self._cache:
290
+ # FIXME: we have to check for virtual replaces here as
291
+ # well (to pass tests/gdebi-test8.deb)
292
+ if self._cache.is_virtual_package(depname):
293
+ for pkg in self._cache.get_providing_packages(depname):
294
+ self._dbg(3, "conflicts virtual check: %s" % pkg.name)
295
+ # P/C/R on virtal pkg, e.g. ftpd
296
+ if self.pkgname == pkg.name:
297
+ self._dbg(3, "conflict on self, ignoring")
298
+ continue
299
+ if self._check_single_pkg_conflict(
300
+ pkg.name, ver, oper):
301
+ self._installed_conflicts.add(pkg.name)
302
+ continue
303
+ if self._check_single_pkg_conflict(depname, ver, oper):
304
+ self._installed_conflicts.add(depname)
305
+ return bool(self._installed_conflicts)
306
+
307
+ @property
308
+ def conflicts(self):
309
+ # type: () -> List[List[Tuple[str, str, str]]]
310
+ """List of packages conflicting with this package."""
311
+ key = "Conflicts"
312
+ try:
313
+ return apt_pkg.parse_depends(self._sections[key], False)
314
+ except KeyError:
315
+ return []
316
+
317
+ @property
318
+ def depends(self):
319
+ # type: () -> List[List[Tuple[str, str, str]]]
320
+ """List of packages on which this package depends on."""
321
+ depends = []
322
+ # find depends
323
+ for key in "Depends", "Pre-Depends":
324
+ try:
325
+ depends.extend(
326
+ apt_pkg.parse_depends(self._sections[key], False))
327
+ except KeyError:
328
+ pass
329
+ return depends
330
+
331
+ @property
332
+ def provides(self):
333
+ # type: () -> List[List[Tuple[str, str, str]]]
334
+ """List of virtual packages which are provided by this package."""
335
+ key = "Provides"
336
+ try:
337
+ return apt_pkg.parse_depends(self._sections[key], False)
338
+ except KeyError:
339
+ return []
340
+
341
+ @property
342
+ def replaces(self):
343
+ # type: () -> List[List[Tuple[str, str, str]]]
344
+ """List of packages which are replaced by this package."""
345
+ key = "Replaces"
346
+ try:
347
+ return apt_pkg.parse_depends(self._sections[key], False)
348
+ except KeyError:
349
+ return []
350
+
351
+ def replaces_real_pkg(self, pkgname, oper, ver):
352
+ # type: (str, str, str) -> bool
353
+ """Return True if a given non-virtual package is replaced.
354
+
355
+ Return True if the deb packages replaces a real (not virtual)
356
+ packages named (pkgname, oper, ver).
357
+ """
358
+ self._dbg(3, "replaces_real_pkg() %s %s %s" % (pkgname, oper, ver))
359
+ pkg = self._cache[pkgname]
360
+ pkgver = None # type: Optional[str]
361
+ if pkg.is_installed:
362
+ assert pkg.installed is not None
363
+ pkgver = pkg.installed.version
364
+ elif pkg.marked_install:
365
+ assert pkg.candidate is not None
366
+ pkgver = pkg.candidate.version
367
+ else:
368
+ pkgver = None
369
+ for or_group in self.replaces:
370
+ for (name, ver, oper) in or_group:
371
+ if (name == pkgname and (pkgver is None or
372
+ apt_pkg.check_dep(pkgver, oper, ver))):
373
+ self._dbg(3, "we have a replaces in our package for the "
374
+ "conflict against '%s'" % (pkgname))
375
+ return True
376
+ return False
377
+
378
+ def check_conflicts(self):
379
+ # type: () -> bool
380
+ """Check if there are conflicts with existing or selected packages.
381
+
382
+ Check if the package conflicts with a existing or to be installed
383
+ package. Return True if the pkg is OK.
384
+ """
385
+ res = True
386
+ for or_group in self.conflicts:
387
+ if self._check_conflicts_or_group(or_group):
388
+ #print "Conflicts with a exisiting pkg!"
389
+ #self._failure_string = "Conflicts with a exisiting pkg!"
390
+ res = False
391
+ return res
392
+
393
+ def check_breaks_existing_packages(self):
394
+ # type: () -> bool
395
+ """
396
+ check if installing the package would break exsisting
397
+ package on the system, e.g. system has:
398
+ smc depends on smc-data (= 1.4)
399
+ and user tries to installs smc-data 1.6
400
+ """
401
+ # show progress information as this step may take some time
402
+ size = float(len(self._cache))
403
+ steps = max(int(size / 50), 1)
404
+ debver = self._sections["Version"]
405
+ debarch = self._sections["Architecture"]
406
+ # store what we provide so that we can later check against that
407
+ provides = [x[0][0] for x in self.provides]
408
+ for (i, pkg) in enumerate(self._cache):
409
+ if i % steps == 0:
410
+ self._cache.op_progress.update(float(i) / size * 100.0)
411
+ if not pkg.is_installed:
412
+ continue
413
+ assert pkg.installed is not None
414
+ # check if the exising dependencies are still satisfied
415
+ # with the package
416
+ ver = pkg._pkg.current_ver
417
+ for dep_or in pkg.installed.dependencies:
418
+ for dep in dep_or.or_dependencies:
419
+ if dep.name == self.pkgname:
420
+ if not apt_pkg.check_dep(
421
+ debver, dep.relation, dep.version):
422
+ self._dbg(2, "would break (depends) %s" % pkg.name)
423
+ # TRANSLATORS: the first '%s' is the package that
424
+ # breaks, the second the dependency that makes it
425
+ # break, the third the relation (e.g. >=) and the
426
+ # latest the version for the releation
427
+ self._failure_string += _(
428
+ "Breaks existing package '%(pkgname)s' "
429
+ "dependency %(depname)s "
430
+ "(%(deprelation)s %(depversion)s)") % {
431
+ 'pkgname': pkg.name,
432
+ 'depname': dep.name,
433
+ 'deprelation': dep.relation,
434
+ 'depversion': dep.version}
435
+ self._cache.op_progress.done()
436
+ return False
437
+ # now check if there are conflicts against this package on
438
+ # the existing system
439
+ if "Conflicts" in ver.depends_list:
440
+ for conflicts_ver_list in ver.depends_list["Conflicts"]:
441
+ for c_or in conflicts_ver_list:
442
+ if (c_or.target_pkg.name == self.pkgname and
443
+ c_or.target_pkg.architecture == debarch):
444
+ if apt_pkg.check_dep(
445
+ debver, c_or.comp_type, c_or.target_ver):
446
+ self._dbg(
447
+ 2, "would break (conflicts) %s" % pkg.name)
448
+ # TRANSLATORS: the first '%s' is the package
449
+ # that conflicts, the second the packagename
450
+ # that it conflicts with (so the name of the
451
+ # deb the user tries to install), the third is
452
+ # the relation (e.g. >=) and the last is the
453
+ # version for the relation
454
+ self._failure_string += _(
455
+ "Breaks existing package '%(pkgname)s' "
456
+ "conflict: %(targetpkg)s "
457
+ "(%(comptype)s %(targetver)s)") % {
458
+ 'pkgname': pkg.name,
459
+ 'targetpkg': c_or.target_pkg.name,
460
+ 'comptype': c_or.comp_type,
461
+ 'targetver': c_or.target_ver}
462
+ self._cache.op_progress.done()
463
+ return False
464
+ if (c_or.target_pkg.name in provides and
465
+ self.pkgname != pkg.name):
466
+ self._dbg(
467
+ 2, "would break (conflicts) %s" % provides)
468
+ self._failure_string += _(
469
+ "Breaks existing package '%(pkgname)s' "
470
+ "that conflict: '%(targetpkg)s'. But the "
471
+ "'%(debfile)s' provides it via: "
472
+ "'%(provides)s'") % {
473
+ 'provides': ",".join(provides),
474
+ 'debfile': self.filename,
475
+ 'targetpkg': c_or.target_pkg.name,
476
+ 'pkgname': pkg.name}
477
+ self._cache.op_progress.done()
478
+ return False
479
+ self._cache.op_progress.done()
480
+ return True
481
+
482
+ def compare_to_version_in_cache(self, use_installed=True):
483
+ # type: (bool) -> int
484
+ """Compare the package to the version available in the cache.
485
+
486
+ Checks if the package is already installed or availabe in the cache
487
+ and if so in what version, returns one of (VERSION_NONE,
488
+ VERSION_OUTDATED, VERSION_SAME, VERSION_NEWER).
489
+ """
490
+ self._dbg(3, "compare_to_version_in_cache")
491
+ pkgname = self._sections["Package"]
492
+ architecture = self._sections["Architecture"]
493
+
494
+ # Arch qualify the package name
495
+ pkgname = ":".join([pkgname, architecture])
496
+
497
+ debver = self._sections["Version"]
498
+ self._dbg(1, "debver: %s" % debver)
499
+ if pkgname in self._cache:
500
+ pkg = self._cache[pkgname]
501
+ if use_installed and pkg.installed is not None:
502
+ cachever = pkg.installed.version
503
+ elif not use_installed and pkg.candidate is not None:
504
+ cachever = pkg.candidate.version
505
+ else:
506
+ return self.VERSION_NONE
507
+ if cachever is not None:
508
+ cmp = apt_pkg.version_compare(cachever, debver)
509
+ self._dbg(1, "CompareVersion(debver,instver): %s" % cmp)
510
+ if cmp == 0:
511
+ return self.VERSION_SAME
512
+ elif cmp < 0:
513
+ return self.VERSION_NEWER
514
+ elif cmp > 0:
515
+ return self.VERSION_OUTDATED
516
+ return self.VERSION_NONE
517
+
518
+ def check(self, allow_downgrade=False):
519
+ # type: (bool) -> bool
520
+ """Check if the package is installable."""
521
+ self._dbg(3, "check")
522
+
523
+ self._check_was_run = True
524
+
525
+ # check arch
526
+ if "Architecture" not in self._sections:
527
+ self._dbg(1, "ERROR: no architecture field")
528
+ self._failure_string = _("No Architecture field in the package")
529
+ return False
530
+ arch = self._sections["Architecture"]
531
+ if arch != "all" and arch != apt_pkg.config.find("APT::Architecture"):
532
+ if arch in apt_pkg.get_architectures():
533
+ self._multiarch = arch
534
+ self.pkgname = "%s:%s" % (self.pkgname, self._multiarch)
535
+ self._dbg(1, "Found multiarch arch: '%s'" % arch)
536
+ else:
537
+ self._dbg(1, "ERROR: Wrong architecture dude!")
538
+ self._failure_string = _("Wrong architecture '%s' "
539
+ "-- Run dpkg --add-architecture to "
540
+ "add it and update afterwards") % arch
541
+ return False
542
+
543
+ # check version
544
+ if (not allow_downgrade and
545
+ self.compare_to_version_in_cache() == self.VERSION_OUTDATED):
546
+ if self._cache[self.pkgname].installed:
547
+ # the deb is older than the installed
548
+ self._failure_string = _(
549
+ "A later version is already installed")
550
+ return False
551
+
552
+ # FIXME: this sort of error handling sux
553
+ self._failure_string = ""
554
+
555
+ # check conflicts
556
+ if not self.check_conflicts():
557
+ return False
558
+
559
+ # check if installing it would break anything on the
560
+ # current system
561
+ if not self.check_breaks_existing_packages():
562
+ return False
563
+
564
+ # try to satisfy the dependencies
565
+ if not self._satisfy_depends(self.depends):
566
+ return False
567
+
568
+ # check for conflicts again (this time with the packages that are
569
+ # makeed for install)
570
+ if not self.check_conflicts():
571
+ return False
572
+
573
+ if self._cache._depcache.broken_count > 0:
574
+ self._failure_string = _("Failed to satisfy all dependencies "
575
+ "(broken cache)")
576
+ # clean the cache again
577
+ self._cache.clear()
578
+ return False
579
+ return True
580
+
581
+ def satisfy_depends_str(self, dependsstr):
582
+ # type: (str) -> bool
583
+ """Satisfy the dependencies in the given string."""
584
+ return self._satisfy_depends(apt_pkg.parse_depends(dependsstr, False))
585
+
586
+ def _satisfy_depends(self, depends):
587
+ # type: (List[List[Tuple[str, str, str]]]) -> bool
588
+ """Satisfy the dependencies."""
589
+ # turn off MarkAndSweep via a action group (if available)
590
+ try:
591
+ _actiongroup = apt_pkg.ActionGroup(self._cache._depcache)
592
+ _actiongroup # pyflakes
593
+ except AttributeError:
594
+ pass
595
+ # check depends
596
+ for or_group in depends:
597
+ if not self._is_or_group_satisfied(or_group):
598
+ if not self._satisfy_or_group(or_group):
599
+ return False
600
+ # now try it out in the cache
601
+ for pkg in self._need_pkgs:
602
+ try:
603
+ self._cache[pkg].mark_install(from_user=False)
604
+ except SystemError:
605
+ self._failure_string = _("Cannot install '%s'") % pkg
606
+ self._cache.clear()
607
+ return False
608
+ return True
609
+
610
+ @property
611
+ def missing_deps(self):
612
+ # type: () -> List[str]
613
+ """Return missing dependencies."""
614
+ self._dbg(1, "Installing: %s" % self._need_pkgs)
615
+ if not self._check_was_run:
616
+ raise AttributeError(
617
+ "property only available after check() was run")
618
+ return self._need_pkgs
619
+
620
+ @property
621
+ def required_changes(self):
622
+ # type: () -> Tuple[List[str], List[str], List[str]]
623
+ """Get the changes required to satisfy the dependencies.
624
+
625
+ Returns: a tuple with (install, remove, unauthenticated)
626
+ """
627
+ install = []
628
+ remove = []
629
+ unauthenticated = []
630
+ if not self._check_was_run:
631
+ raise AttributeError(
632
+ "property only available after check() was run")
633
+ for pkg in self._cache:
634
+ if pkg.marked_install or pkg.marked_upgrade:
635
+ assert pkg.candidate is not None
636
+ install.append(pkg.name)
637
+ # check authentication, one authenticated origin is enough
638
+ # libapt will skip non-authenticated origins then
639
+ authenticated = False
640
+ for origin in pkg.candidate.origins:
641
+ authenticated |= origin.trusted
642
+ if not authenticated:
643
+ unauthenticated.append(pkg.name)
644
+ if pkg.marked_delete:
645
+ remove.append(pkg.name)
646
+ return (install, remove, unauthenticated)
647
+
648
+ @staticmethod
649
+ def to_hex(in_data):
650
+ # type: (str) -> str
651
+ hex = ""
652
+ for (i, c) in enumerate(in_data):
653
+ if i % 80 == 0:
654
+ hex += "\n"
655
+ hex += "%2.2x " % ord(c)
656
+ return hex
657
+
658
+ @staticmethod
659
+ def to_strish(in_data):
660
+ # type: (Union[str, Iterable[int]]) -> str
661
+ s = ""
662
+ # py2 compat, in_data is type string
663
+ if isinstance(in_data, str):
664
+ for c in in_data:
665
+ if ord(c) < 10 or ord(c) > 127:
666
+ s += " "
667
+ else:
668
+ s += c
669
+ # py3 compat, in_data is type bytes
670
+ else:
671
+ for b in in_data:
672
+ if b < 10 or b > 127:
673
+ s += " "
674
+ else:
675
+ s += chr(b)
676
+ return s
677
+
678
+ def _get_content(self, part, name, auto_decompress=True, auto_hex=True):
679
+ # type: (apt_inst.TarFile, str, bool, bool) -> str
680
+ if name.startswith("./"):
681
+ name = name[2:]
682
+ data = part.extractdata(name)
683
+ # check for zip content
684
+ if name.endswith(".gz") and auto_decompress:
685
+ io = BytesIO(data)
686
+ gz = gzip.GzipFile(fileobj=io)
687
+ data = _("Automatically decompressed:\n\n").encode("utf-8")
688
+ data += gz.read()
689
+ # auto-convert to hex
690
+ try:
691
+ return data.decode("utf-8")
692
+ except Exception:
693
+ new_data = _("Automatically converted to printable ascii:\n")
694
+ new_data += self.to_strish(data)
695
+ return new_data
696
+
697
+ def control_content(self, name):
698
+ # type: (str) -> str
699
+ """ return the content of a specific control.tar.gz file """
700
+ try:
701
+ return self._get_content(self._debfile.control, name)
702
+ except LookupError:
703
+ return ""
704
+
705
+ def data_content(self, name):
706
+ # type: (str) -> str
707
+ """ return the content of a specific control.tar.gz file """
708
+ try:
709
+ return self._get_content(self._debfile.data, name)
710
+ except LookupError:
711
+ return ""
712
+
713
+ def _dbg(self, level, msg):
714
+ # type: (int, str) -> None
715
+ """Write debugging output to sys.stderr."""
716
+ if level <= self.debug:
717
+ print(msg, file=sys.stderr)
718
+
719
+ def install(self, install_progress=None):
720
+ # type: (Optional[apt.progress.base.InstallProgress]) -> int
721
+ """Install the package."""
722
+ if self.filename is None:
723
+ raise apt_pkg.Error("No filename specified")
724
+ if install_progress is None:
725
+ return os.spawnlp(os.P_WAIT, "dpkg", "dpkg", "-i", self.filename)
726
+ else:
727
+ try:
728
+ install_progress.start_update()
729
+ except AttributeError:
730
+ install_progress.startUpdate() # type: ignore
731
+ res = install_progress.run(self.filename)
732
+ try:
733
+ install_progress.finish_update()
734
+ except AttributeError:
735
+ install_progress.finishUpdate() # type: ignore
736
+ return res
737
+
738
+
739
+ class DscSrcPackage(DebPackage):
740
+ """A locally available source package."""
741
+
742
+ def __init__(self, filename=None, cache=None):
743
+ # type: (Optional[str], Optional[apt.Cache]) -> None
744
+ DebPackage.__init__(self, None, cache)
745
+ self.filename = filename # type: Optional[str]
746
+ self._depends = [] # type: List[List[Tuple[str, str, str]]]
747
+ self._conflicts = [] # type: List[List[Tuple[str, str, str]]]
748
+ self._installed_conflicts = set() # type: Set[str]
749
+ self.pkgname = ""
750
+ self.binaries = [] # type: List[str]
751
+ self._sections = {} # type: Dict[str, str]
752
+ if self.filename is not None:
753
+ self.open(self.filename)
754
+
755
+ @property
756
+ def depends(self):
757
+ # type: () -> List[List[Tuple[str, str, str]]]
758
+ """Return the dependencies of the package"""
759
+ return self._depends
760
+
761
+ @property
762
+ def conflicts(self):
763
+ # type: () -> List[List[Tuple[str, str, str]]]
764
+ """Return the dependencies of the package"""
765
+ return self._conflicts
766
+
767
+ @property
768
+ def filelist(self):
769
+ # type: () -> List[str]
770
+ """Return the list of files associated with this dsc file"""
771
+ # Files stanza looks like (hash, size, filename, ...)
772
+ return self._sections['Files'].split()[2::3]
773
+
774
+ def open(self, file):
775
+ # type: (str) -> None
776
+ """Open the package."""
777
+ depends_tags = ["Build-Depends", "Build-Depends-Indep"]
778
+ conflicts_tags = ["Build-Conflicts", "Build-Conflicts-Indep"]
779
+ fd = apt_pkg.open_maybe_clear_signed_file(file)
780
+ fobj = os.fdopen(fd)
781
+ tagfile = apt_pkg.TagFile(fobj)
782
+ try:
783
+ for sec in tagfile:
784
+ for tag in depends_tags:
785
+ if tag not in sec:
786
+ continue
787
+ self._depends.extend(apt_pkg.parse_src_depends(sec[tag]))
788
+ for tag in conflicts_tags:
789
+ if tag not in sec:
790
+ continue
791
+ self._conflicts.extend(apt_pkg.parse_src_depends(sec[tag]))
792
+ if 'Source' in sec:
793
+ self.pkgname = sec['Source']
794
+ if 'Binary' in sec:
795
+ self.binaries = [b.strip() for b in
796
+ sec['Binary'].split(',')]
797
+ for tag in sec.keys():
798
+ if tag in sec:
799
+ self._sections[tag] = sec[tag]
800
+ finally:
801
+ del tagfile
802
+ fobj.close()
803
+
804
+ s = _("Install Build-Dependencies for "
805
+ "source package '%s' that builds %s\n") % (self.pkgname,
806
+ " ".join(self.binaries))
807
+ self._sections["Description"] = s
808
+ self._check_was_run = False
809
+
810
+ def check(self, allow_downgrade=False):
811
+ # type: (bool) -> bool
812
+ """Check if the package is installable.
813
+
814
+ The second parameter is ignored and only exists for compatibility
815
+ with parent type."""
816
+ if not self.check_conflicts():
817
+ for pkgname in self._installed_conflicts:
818
+ if self._cache[pkgname]._pkg.essential:
819
+ raise Exception(_("An essential package would be removed"))
820
+ self._cache[pkgname].mark_delete()
821
+ # properties are ok now
822
+ self._check_was_run = True
823
+ # FIXME: a additional run of the check_conflicts()
824
+ # after _satisfy_depends() should probably be done
825
+ return self._satisfy_depends(self.depends)
826
+
827
+
828
+ def _test():
829
+ # type: () -> None
830
+ """Test function"""
831
+ from apt.cache import Cache
832
+ from apt.progress.base import InstallProgress
833
+
834
+ cache = Cache()
835
+
836
+ vp = "www-browser"
837
+ print("%s virtual: %s" % (vp, cache.is_virtual_package(vp)))
838
+ providers = cache.get_providing_packages(vp)
839
+ print("Providers for %s :" % vp)
840
+ for pkg in providers:
841
+ print(" %s" % pkg.name)
842
+
843
+ d = DebPackage(sys.argv[1], cache)
844
+ print("Deb: %s" % d.pkgname)
845
+ if not d.check():
846
+ print("can't be satified")
847
+ print(d._failure_string)
848
+ print("missing deps: %s" % d.missing_deps)
849
+ print(d.required_changes)
850
+
851
+ print(d.filelist)
852
+
853
+ print("Installing ...")
854
+ ret = d.install(InstallProgress())
855
+ print(ret)
856
+
857
+ #s = DscSrcPackage(cache, "../tests/3ddesktop_0.2.9-6.dsc")
858
+ #s.check_dep()
859
+ #print "Missing deps: ",s.missingDeps
860
+ #print "Print required changes: ", s.requiredChanges
861
+
862
+ s = DscSrcPackage(cache=cache)
863
+ ds = "libc6 (>= 2.3.2), libaio (>= 0.3.96) | libaio1 (>= 0.3.96)"
864
+ print(s._satisfy_depends(apt_pkg.parse_depends(ds, False)))
865
+
866
+
867
+ if __name__ == "__main__":
868
+ _test()
build/lib.linux-x86_64-cpython-310/apt/package.py ADDED
@@ -0,0 +1,1604 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # package.py - apt package abstraction
2
+ #
3
+ # Copyright (c) 2005-2009 Canonical
4
+ #
5
+ # Author: Michael Vogt <[email protected]>
6
+ #
7
+ # This program is free software; you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as
9
+ # published by the Free Software Foundation; either version 2 of the
10
+ # License, or (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
20
+ # USA
21
+ """Functionality related to packages."""
22
+ from __future__ import print_function
23
+
24
+ import logging
25
+ import os
26
+ import sys
27
+ import re
28
+ import socket
29
+ import subprocess
30
+ import threading
31
+
32
+ from http.client import BadStatusLine
33
+ from urllib.error import HTTPError
34
+ from urllib.request import urlopen
35
+
36
+ from typing import (Any, Iterable, Iterator, List, Optional, Set,
37
+ Tuple, Union, no_type_check, Mapping,
38
+ Sequence)
39
+
40
+ import apt_pkg
41
+ import apt.progress.text
42
+
43
+ from apt.progress.base import (
44
+ AcquireProgress,
45
+ InstallProgress,
46
+ )
47
+
48
+ from apt_pkg import gettext as _
49
+
50
+ __all__ = ('BaseDependency', 'Dependency', 'Origin', 'Package', 'Record',
51
+ 'Version', 'VersionList')
52
+
53
+
54
+ def _file_is_same(path, size, hashes):
55
+ # type: (str, int, apt_pkg.HashStringList) -> bool
56
+ """Return ``True`` if the file is the same."""
57
+ if os.path.exists(path) and os.path.getsize(path) == size:
58
+ with open(path) as fobj:
59
+ return apt_pkg.Hashes(fobj).hashes == hashes
60
+ return False
61
+
62
+
63
+ class FetchError(Exception):
64
+ """Raised when a file could not be fetched."""
65
+
66
+
67
+ class UntrustedError(FetchError):
68
+ """Raised when a file did not have a trusted hash."""
69
+
70
+
71
+ class BaseDependency(object):
72
+ """A single dependency."""
73
+
74
+ class __dstr(str):
75
+ """Compare helper for compatibility with old third-party code.
76
+
77
+ Old third-party code might still compare the relation with the
78
+ previously used relations (<<,<=,==,!=,>=,>>,) instead of the curently
79
+ used ones (<,<=,=,!=,>=,>,). This compare helper lets < match to <<,
80
+ > match to >> and = match to ==.
81
+ """
82
+
83
+ def __eq__(self, other):
84
+ # type: (object) -> bool
85
+ if str.__eq__(self, other):
86
+ return True
87
+ elif str.__eq__(self, '<'):
88
+ return str.__eq__('<<', other)
89
+ elif str.__eq__(self, '>'):
90
+ return str.__eq__('>>', other)
91
+ elif str.__eq__(self, '='):
92
+ return str.__eq__('==', other)
93
+ else:
94
+ return False
95
+
96
+ def __ne__(self, other):
97
+ # type: (object) -> bool
98
+ return not self.__eq__(other)
99
+
100
+ def __init__(self, version, dep):
101
+ # type: (Version, apt_pkg.Dependency) -> None
102
+ self._version = version # apt.package.Version
103
+ self._dep = dep # apt_pkg.Dependency
104
+
105
+ def __str__(self):
106
+ # type: () -> str
107
+ return '%s: %s' % (self.rawtype, self.rawstr)
108
+
109
+ def __repr__(self):
110
+ # type: () -> str
111
+ return ('<BaseDependency: name:%r relation:%r version:%r rawtype:%r>'
112
+ % (self.name, self.relation, self.version, self.rawtype))
113
+
114
+ @property
115
+ def name(self):
116
+ # type: () -> str
117
+ """The name of the target package."""
118
+ return self._dep.target_pkg.name
119
+
120
+ @property
121
+ def relation(self):
122
+ # type: () -> str
123
+ """The relation (<, <=, =, !=, >=, >, '') in mathematical notation.
124
+
125
+ The empty string will be returned in case of an unversioned dependency.
126
+ """
127
+ return self.__dstr(self._dep.comp_type)
128
+
129
+ @property
130
+ def relation_deb(self):
131
+ # type: () -> str
132
+ """The relation (<<, <=, =, !=, >=, >>, '') in Debian notation.
133
+
134
+ The empty string will be returned in case of an unversioned dependency.
135
+ For more details see the Debian Policy Manual on the syntax of
136
+ relationship fields:
137
+ https://www.debian.org/doc/debian-policy/ch-relationships.html#s-depsyntax # noqa
138
+
139
+ .. versionadded:: 1.0.0
140
+ """
141
+ return self._dep.comp_type_deb
142
+
143
+ @property
144
+ def version(self):
145
+ # type: () -> str
146
+ """The target version or an empty string.
147
+
148
+ Note that the version is only an empty string in case of an unversioned
149
+ dependency. In this case the relation is also an empty string.
150
+ """
151
+ return self._dep.target_ver
152
+
153
+ @property
154
+ def target_versions(self):
155
+ # type: () -> List[Version]
156
+ """A list of all Version objects which satisfy this dependency.
157
+
158
+ .. versionadded:: 1.0.0
159
+ """
160
+ tvers = []
161
+ _tvers = self._dep.all_targets() # type: List[apt_pkg.Version]
162
+ for _tver in _tvers: # type: apt_pkg.Version
163
+ _pkg = _tver.parent_pkg # type: apt_pkg.Package
164
+ cache = self._version.package._pcache # apt.cache.Cache
165
+ pkg = cache._rawpkg_to_pkg(_pkg) # apt.package.Package
166
+ tver = Version(pkg, _tver) # apt.package.Version
167
+ tvers.append(tver)
168
+ return tvers
169
+
170
+ @property
171
+ def installed_target_versions(self):
172
+ # type: () -> List[Version]
173
+ """A list of all installed Version objects which satisfy this dep.
174
+
175
+ .. versionadded:: 1.0.0
176
+ """
177
+ return [tver for tver in self.target_versions if tver.is_installed]
178
+
179
+ @property
180
+ def rawstr(self):
181
+ # type: () -> str
182
+ """String represenation of the dependency.
183
+
184
+ Returns the string representation of the dependency as it would be
185
+ written in the debian/control file. The string representation does not
186
+ include the type of the dependency.
187
+
188
+ Example for an unversioned dependency:
189
+ python3
190
+
191
+ Example for a versioned dependency:
192
+ python3 >= 3.2
193
+
194
+ .. versionadded:: 1.0.0
195
+ """
196
+ if self.version:
197
+ return '%s %s %s' % (self.name, self.relation_deb, self.version)
198
+ else:
199
+ return self.name
200
+
201
+ @property
202
+ def rawtype(self):
203
+ # type: () -> str
204
+ """Type of the dependency.
205
+
206
+ This should be one of 'Breaks', 'Conflicts', 'Depends', 'Enhances',
207
+ 'PreDepends', 'Recommends', 'Replaces', 'Suggests'.
208
+
209
+ Additional types might be added in the future.
210
+ """
211
+ return self._dep.dep_type_untranslated
212
+
213
+ @property
214
+ def pre_depend(self):
215
+ # type: () -> bool
216
+ """Whether this is a PreDepends."""
217
+ return self._dep.dep_type_untranslated == 'PreDepends'
218
+
219
+
220
+ class Dependency(List[BaseDependency]):
221
+ """Represent an Or-group of dependencies.
222
+
223
+ Attributes defined here:
224
+ or_dependencies - The possible choices
225
+ rawstr - String represenation of the Or-group of dependencies
226
+ rawtype - The type of the dependencies in the Or-group
227
+ target_version - A list of Versions which satisfy this Or-group of deps
228
+ """
229
+
230
+ def __init__(self, version, base_deps, rawtype):
231
+ # type: (Version, List[BaseDependency], str) -> None
232
+ super(Dependency, self).__init__(base_deps)
233
+ self._version = version # apt.package.Version
234
+ self._rawtype = rawtype
235
+
236
+ def __str__(self):
237
+ # type: () -> str
238
+ return '%s: %s' % (self.rawtype, self.rawstr)
239
+
240
+ def __repr__(self):
241
+ # type: () -> str
242
+ return '<Dependency: [%s]>' % (', '.join(repr(bd) for bd in self))
243
+
244
+ @property
245
+ def or_dependencies(self):
246
+ # type: () -> Dependency
247
+ return self
248
+
249
+ @property
250
+ def rawstr(self):
251
+ # type: () -> str
252
+ """String represenation of the Or-group of dependencies.
253
+
254
+ Returns the string representation of the Or-group of dependencies as it
255
+ would be written in the debian/control file. The string representation
256
+ does not include the type of the Or-group of dependencies.
257
+
258
+ Example:
259
+ python2 >= 2.7 | python3
260
+
261
+ .. versionadded:: 1.0.0
262
+ """
263
+ return ' | '.join(bd.rawstr for bd in self)
264
+
265
+ @property
266
+ def rawtype(self):
267
+ # type: () -> str
268
+ """Type of the Or-group of dependency.
269
+
270
+ This should be one of 'Breaks', 'Conflicts', 'Depends', 'Enhances',
271
+ 'PreDepends', 'Recommends', 'Replaces', 'Suggests'.
272
+
273
+ Additional types might be added in the future.
274
+
275
+ .. versionadded:: 1.0.0
276
+ """
277
+ return self._rawtype
278
+
279
+ @property
280
+ def target_versions(self):
281
+ # type: () -> List[Version]
282
+ """A list of all Version objects which satisfy this Or-group of deps.
283
+
284
+ .. versionadded:: 1.0.0
285
+ """
286
+ tvers = [] # type: List[Version]
287
+ for bd in self: # apt.package.Dependency
288
+ for tver in bd.target_versions: # apt.package.Version
289
+ if tver not in tvers:
290
+ tvers.append(tver)
291
+ return tvers
292
+
293
+ @property
294
+ def installed_target_versions(self):
295
+ # type: () -> List[Version]
296
+ """A list of all installed Version objects which satisfy this dep.
297
+
298
+ .. versionadded:: 1.0.0
299
+ """
300
+ return [tver for tver in self.target_versions if tver.is_installed]
301
+
302
+
303
+ class Origin(object):
304
+ """The origin of a version.
305
+
306
+ Attributes defined here:
307
+ archive - The archive (eg. unstable)
308
+ component - The component (eg. main)
309
+ label - The Label, as set in the Release file
310
+ origin - The Origin, as set in the Release file
311
+ codename - The Codename, as set in the Release file
312
+ site - The hostname of the site.
313
+ trusted - Boolean value whether this is trustworthy.
314
+ """
315
+
316
+ def __init__(self, pkg, packagefile):
317
+ # type: (Package, apt_pkg.PackageFile) -> None
318
+ self.archive = packagefile.archive
319
+ self.component = packagefile.component
320
+ self.label = packagefile.label
321
+ self.origin = packagefile.origin
322
+ self.codename = packagefile.codename
323
+ self.site = packagefile.site
324
+ self.not_automatic = packagefile.not_automatic
325
+ # check the trust
326
+ indexfile = pkg._pcache._list.find_index(packagefile)
327
+ if indexfile and indexfile.is_trusted:
328
+ self.trusted = True
329
+ else:
330
+ self.trusted = False
331
+
332
+ def __repr__(self):
333
+ # type: () -> str
334
+ return ("<Origin component:%r archive:%r origin:%r label:%r "
335
+ "site:%r isTrusted:%r>") % (self.component, self.archive,
336
+ self.origin, self.label,
337
+ self.site, self.trusted)
338
+
339
+
340
+ class Record(Mapping[Any, Any]):
341
+ """Record in a Packages file
342
+
343
+ Represent a record as stored in a Packages file. You can use this like
344
+ a dictionary mapping the field names of the record to their values::
345
+
346
+ >>> record = Record("Package: python-apt\\nVersion: 0.8.0\\n\\n")
347
+ >>> record["Package"]
348
+ 'python-apt'
349
+ >>> record["Version"]
350
+ '0.8.0'
351
+
352
+ For example, to get the tasks of a package from a cache, you could do::
353
+
354
+ package.candidate.record["Tasks"].split()
355
+
356
+ Of course, you can also use the :attr:`Version.tasks` property.
357
+
358
+ """
359
+
360
+ def __init__(self, record_str):
361
+ # type: (str) -> None
362
+ self._rec = apt_pkg.TagSection(record_str)
363
+
364
+ def __hash__(self):
365
+ # type: () -> int
366
+ return hash(self._rec)
367
+
368
+ def __str__(self):
369
+ # type: () -> str
370
+ return str(self._rec)
371
+
372
+ def __getitem__(self, key):
373
+ # type: (str) -> str
374
+ return self._rec[key]
375
+
376
+ def __contains__(self, key):
377
+ # type: (object) -> bool
378
+ return key in self._rec
379
+
380
+ def __iter__(self):
381
+ # type: () -> Iterator[str]
382
+ return iter(self._rec.keys())
383
+
384
+ def iteritems(self):
385
+ # type: () -> Iterable[Tuple[object, str]]
386
+ """An iterator over the (key, value) items of the record."""
387
+ for key in self._rec.keys():
388
+ yield key, self._rec[key]
389
+
390
+ def get(self, key, default=None):
391
+ # type: (str, object) -> object
392
+ """Return record[key] if key in record, else *default*.
393
+
394
+ The parameter *default* must be either a string or None.
395
+ """
396
+ return self._rec.get(key, default)
397
+
398
+ def has_key(self, key):
399
+ # type: (str) -> bool
400
+ """deprecated form of ``key in x``."""
401
+ return key in self._rec
402
+
403
+ def __len__(self):
404
+ # type: () -> int
405
+ return len(self._rec)
406
+
407
+
408
+ class Version(object):
409
+ """Representation of a package version.
410
+
411
+ The Version class contains all information related to a
412
+ specific package version.
413
+
414
+ .. versionadded:: 0.7.9
415
+ """
416
+
417
+ def __init__(self, package, cand):
418
+ # type: (Package, apt_pkg.Version) -> None
419
+ self.package = package
420
+ self._cand = cand
421
+ self.package._pcache._weakversions.add(self)
422
+
423
+ def _cmp(self, other):
424
+ # type: (Any) -> Union[int, Any]
425
+ """Compares against another apt.Version object or a version string.
426
+
427
+ This method behaves like Python 2's cmp builtin and returns an integer
428
+ according to the outcome. The return value is negative in case of
429
+ self < other, zero if self == other and positive if self > other.
430
+
431
+ The comparison includes the package name and architecture if other is
432
+ an apt.Version object. If other isn't an apt.Version object it'll be
433
+ assumed that other is a version string (without package name/arch).
434
+
435
+ .. versionchanged:: 1.0.0
436
+ """
437
+ # Assume that other is an apt.Version object.
438
+ try:
439
+ self_name = self.package.fullname
440
+ other_name = other.package.fullname
441
+ if self_name < other_name:
442
+ return -1
443
+ elif self_name > other_name:
444
+ return 1
445
+ return apt_pkg.version_compare(self._cand.ver_str, other.version)
446
+ except AttributeError:
447
+ # Assume that other is a string that only contains the version.
448
+ try:
449
+ return apt_pkg.version_compare(self._cand.ver_str, other)
450
+ except TypeError:
451
+ return NotImplemented
452
+
453
+ def __eq__(self, other):
454
+ # type: (object) -> bool
455
+ return self._cmp(other) == 0
456
+
457
+ def __ge__(self, other):
458
+ # type: (Version) -> bool
459
+ return self._cmp(other) >= 0
460
+
461
+ def __gt__(self, other):
462
+ # type: (Version) -> bool
463
+ return self._cmp(other) > 0
464
+
465
+ def __le__(self, other):
466
+ # type: (Version) -> bool
467
+ return self._cmp(other) <= 0
468
+
469
+ def __lt__(self, other):
470
+ # type: (Version) -> bool
471
+ return self._cmp(other) < 0
472
+
473
+ def __ne__(self, other):
474
+ # type: (object) -> Union[bool, Any]
475
+ try:
476
+ return self._cmp(other) != 0
477
+ except TypeError:
478
+ return NotImplemented
479
+
480
+ def __hash__(self):
481
+ # type: () -> int
482
+ return self._cand.hash
483
+
484
+ def __str__(self):
485
+ # type: () -> str
486
+ return '%s=%s' % (self.package.name, self.version)
487
+
488
+ def __repr__(self):
489
+ # type: () -> str
490
+ return '<Version: package:%r version:%r>' % (self.package.name,
491
+ self.version)
492
+
493
+ @property
494
+ def _records(self):
495
+ # type: () -> apt_pkg.PackageRecords
496
+ """Internal helper that moves the Records to the right position."""
497
+ # If changing lookup, change fetch_binary() as well
498
+ if not self.package._pcache._records.lookup(self._cand.file_list[0]):
499
+ raise LookupError("Could not lookup record")
500
+
501
+ return self.package._pcache._records
502
+
503
+ @property
504
+ def _translated_records(self):
505
+ # type: () -> Optional[apt_pkg.PackageRecords]
506
+ """Internal helper to get the translated description."""
507
+ desc_iter = self._cand.translated_description
508
+ if self.package._pcache._records.lookup(desc_iter.file_list.pop(0)):
509
+ return self.package._pcache._records
510
+ return None
511
+
512
+ @property
513
+ def installed_size(self):
514
+ # type: () -> int
515
+ """Return the size of the package when installed."""
516
+ return self._cand.installed_size
517
+
518
+ @property
519
+ def homepage(self):
520
+ # type: () -> str
521
+ """Return the homepage for the package."""
522
+ return self._records.homepage
523
+
524
+ @property
525
+ def size(self):
526
+ # type: () -> int
527
+ """Return the size of the package."""
528
+ return self._cand.size
529
+
530
+ @property
531
+ def architecture(self):
532
+ # type: () -> str
533
+ """Return the architecture of the package version."""
534
+ return self._cand.arch
535
+
536
+ @property
537
+ def downloadable(self):
538
+ # type: () -> bool
539
+ """Return whether the version of the package is downloadable."""
540
+ return bool(self._cand.downloadable)
541
+
542
+ @property
543
+ def is_installed(self):
544
+ # type: () -> bool
545
+ """Return wether this version of the package is currently installed.
546
+
547
+ .. versionadded:: 1.0.0
548
+ """
549
+ inst_ver = self.package.installed
550
+ return (inst_ver is not None and inst_ver._cand.id == self._cand.id)
551
+
552
+ @property
553
+ def version(self):
554
+ # type: () -> str
555
+ """Return the version as a string."""
556
+ return self._cand.ver_str
557
+
558
+ @property
559
+ def summary(self):
560
+ # type: () -> Optional[str]
561
+ """Return the short description (one line summary)."""
562
+ records = self._translated_records
563
+ return records.short_desc if records is not None else None
564
+
565
+ @property
566
+ def raw_description(self):
567
+ # type: () -> str
568
+ """return the long description (raw)."""
569
+ return self._records.long_desc
570
+
571
+ @property
572
+ def section(self):
573
+ # type: () -> str
574
+ """Return the section of the package."""
575
+ return self._cand.section
576
+
577
+ @property
578
+ def description(self):
579
+ # type: () -> str
580
+ """Return the formatted long description.
581
+
582
+ Return the formatted long description according to the Debian policy
583
+ (Chapter 5.6.13).
584
+ See http://www.debian.org/doc/debian-policy/ch-controlfields.html
585
+ for more information.
586
+ """
587
+ desc = ''
588
+ records = self._translated_records
589
+ dsc = records.long_desc if records is not None else None
590
+
591
+ if not dsc:
592
+ return _("Missing description for '%s'."
593
+ "Please report.") % (self.package.name)
594
+
595
+ try:
596
+ if not isinstance(dsc, str):
597
+ # Only convert where needed (i.e. Python 2.X)
598
+ dsc = dsc.decode("utf-8")
599
+ except UnicodeDecodeError as err:
600
+ return _("Invalid unicode in description for '%s' (%s). "
601
+ "Please report.") % (self.package.name, err)
602
+
603
+ lines = iter(dsc.split("\n"))
604
+ # Skip the first line, since its a duplication of the summary
605
+ next(lines)
606
+ for raw_line in lines:
607
+ if raw_line.strip() == ".":
608
+ # The line is just line break
609
+ if not desc.endswith("\n"):
610
+ desc += "\n\n"
611
+ continue
612
+ if raw_line.startswith(" "):
613
+ # The line should be displayed verbatim without word wrapping
614
+ if not desc.endswith("\n"):
615
+ line = "\n%s\n" % raw_line[2:]
616
+ else:
617
+ line = "%s\n" % raw_line[2:]
618
+ elif raw_line.startswith(" "):
619
+ # The line is part of a paragraph.
620
+ if desc.endswith("\n") or desc == "":
621
+ # Skip the leading white space
622
+ line = raw_line[1:]
623
+ else:
624
+ line = raw_line
625
+ else:
626
+ line = raw_line
627
+ # Add current line to the description
628
+ desc += line
629
+ return desc
630
+
631
+ @property
632
+ def source_name(self):
633
+ # type: () -> str
634
+ """Return the name of the source package."""
635
+ try:
636
+ return self._records.source_pkg or self.package.shortname
637
+ except IndexError:
638
+ return self.package.shortname
639
+
640
+ @property
641
+ def source_version(self):
642
+ # type: () -> str
643
+ """Return the version of the source package."""
644
+ try:
645
+ return self._records.source_ver or self._cand.ver_str
646
+ except IndexError:
647
+ return self._cand.ver_str
648
+
649
+ @property
650
+ def priority(self):
651
+ # type: () -> str
652
+ """Return the priority of the package, as string."""
653
+ return self._cand.priority_str
654
+
655
+ @property
656
+ def policy_priority(self):
657
+ # type: () -> int
658
+ """Return the internal policy priority as a number.
659
+ See apt_preferences(5) for more information about what it means.
660
+ """
661
+ return self.package._pcache._depcache.policy.get_priority(self._cand)
662
+
663
+ @property
664
+ def record(self):
665
+ # type: () -> Record
666
+ """Return a Record() object for this version.
667
+
668
+ Return a Record() object for this version which provides access
669
+ to the raw attributes of the candidate version
670
+ """
671
+ return Record(self._records.record)
672
+
673
+ def get_dependencies(self, *types):
674
+ # type: (str) -> List[Dependency]
675
+ """Return a list of Dependency objects for the given types.
676
+
677
+ Multiple types can be specified. Possible types are:
678
+ 'Breaks', 'Conflicts', 'Depends', 'Enhances', 'PreDepends',
679
+ 'Recommends', 'Replaces', 'Suggests'
680
+
681
+ Additional types might be added in the future.
682
+ """
683
+ depends_list = []
684
+ depends = self._cand.depends_list
685
+ for type_ in types:
686
+ try:
687
+ for dep_ver_list in depends[type_]:
688
+ base_deps = []
689
+ for dep_or in dep_ver_list:
690
+ base_deps.append(BaseDependency(self, dep_or))
691
+ depends_list.append(Dependency(self, base_deps, type_))
692
+ except KeyError:
693
+ pass
694
+ return depends_list
695
+
696
+ @property
697
+ def provides(self):
698
+ # type: () -> List[str]
699
+ """ Return a list of names that this version provides."""
700
+ return [p[0] for p in self._cand.provides_list]
701
+
702
+ @property
703
+ def enhances(self):
704
+ # type: () -> List[Dependency]
705
+ """Return the list of enhances for the package version."""
706
+ return self.get_dependencies("Enhances")
707
+
708
+ @property
709
+ def dependencies(self):
710
+ # type: () -> List[Dependency]
711
+ """Return the dependencies of the package version."""
712
+ return self.get_dependencies("PreDepends", "Depends")
713
+
714
+ @property
715
+ def recommends(self):
716
+ # type: () -> List[Dependency]
717
+ """Return the recommends of the package version."""
718
+ return self.get_dependencies("Recommends")
719
+
720
+ @property
721
+ def suggests(self):
722
+ # type: () -> List[Dependency]
723
+ """Return the suggests of the package version."""
724
+ return self.get_dependencies("Suggests")
725
+
726
+ @property
727
+ def origins(self):
728
+ # type: () -> List[Origin]
729
+ """Return a list of origins for the package version."""
730
+ origins = []
731
+ for (packagefile, _unused) in self._cand.file_list:
732
+ origins.append(Origin(self.package, packagefile))
733
+ return origins
734
+
735
+ @property
736
+ def filename(self):
737
+ # type: () -> str
738
+ """Return the path to the file inside the archive.
739
+
740
+ .. versionadded:: 0.7.10
741
+ """
742
+ return self._records.filename
743
+
744
+ @property
745
+ def md5(self):
746
+ # type: () -> str
747
+ """Return the md5sum of the binary.
748
+
749
+ .. versionadded:: 0.7.10
750
+ """
751
+ return self._records.md5_hash
752
+
753
+ @property
754
+ def sha1(self):
755
+ # type: () -> str
756
+ """Return the sha1sum of the binary.
757
+
758
+ .. versionadded:: 0.7.10
759
+ """
760
+ return self._records.sha1_hash
761
+
762
+ @property
763
+ def sha256(self):
764
+ # type: () -> str
765
+ """Return the sha256sum of the binary.
766
+
767
+ .. versionadded:: 0.7.10
768
+ """
769
+ return self._records.sha256_hash
770
+
771
+ @property
772
+ def tasks(self):
773
+ # type: () -> Set[str]
774
+ """Get the tasks of the package.
775
+
776
+ A set of the names of the tasks this package belongs to.
777
+
778
+ .. versionadded:: 0.8.0
779
+ """
780
+ return set(self.record["Task"].split())
781
+
782
+ def _uris(self):
783
+ # type: () -> Iterator[str]
784
+ """Return an iterator over all available urls.
785
+
786
+ .. versionadded:: 0.7.10
787
+ """
788
+ for (packagefile, _unused) in self._cand.file_list:
789
+ indexfile = self.package._pcache._list.find_index(packagefile)
790
+ if indexfile:
791
+ yield indexfile.archive_uri(self._records.filename)
792
+
793
+ @property
794
+ def uris(self):
795
+ # type: () -> List[str]
796
+ """Return a list of all available uris for the binary.
797
+
798
+ .. versionadded:: 0.7.10
799
+ """
800
+ return list(self._uris())
801
+
802
+ @property
803
+ def uri(self):
804
+ # type: () -> Optional[str]
805
+ """Return a single URI for the binary.
806
+
807
+ .. versionadded:: 0.7.10
808
+ """
809
+ try:
810
+ return next(iter(self._uris()))
811
+ except StopIteration:
812
+ return None
813
+
814
+ def fetch_binary(self, destdir='', progress=None,
815
+ allow_unauthenticated=None):
816
+ # type: (str, Optional[AcquireProgress], Optional[bool]) -> str
817
+ """Fetch the binary version of the package.
818
+
819
+ The parameter *destdir* specifies the directory where the package will
820
+ be fetched to.
821
+
822
+ The parameter *progress* may refer to an apt_pkg.AcquireProgress()
823
+ object. If not specified or None, apt.progress.text.AcquireProgress()
824
+ is used.
825
+
826
+ The keyword-only parameter *allow_unauthenticated* specifies whether
827
+ to allow unauthenticated downloads. If not specified, it defaults to
828
+ the configuration option `APT::Get::AllowUnauthenticated`.
829
+
830
+ .. versionadded:: 0.7.10
831
+ """
832
+ if allow_unauthenticated is None:
833
+ allow_unauthenticated = apt_pkg.config.find_b("APT::Get::"
834
+ "AllowUnauthenticated", False)
835
+ base = os.path.basename(self._records.filename)
836
+ destfile = os.path.join(destdir, base)
837
+ if _file_is_same(destfile, self.size, self._records.hashes):
838
+ logging.debug('Ignoring already existing file: %s' % destfile)
839
+ return os.path.abspath(destfile)
840
+
841
+ # Verify that the index is actually trusted
842
+ pfile, offset = self._cand.file_list[0]
843
+ index = self.package._pcache._list.find_index(pfile)
844
+
845
+ if not (allow_unauthenticated or (index and index.is_trusted)):
846
+ raise UntrustedError("Could not fetch %s %s source package: "
847
+ "Source %r is not trusted" %
848
+ (self.package.name, self.version,
849
+ getattr(index, "describe", "<unkown>")))
850
+ if not self.uri:
851
+ raise ValueError("No URI for this binary.")
852
+ hashes = self._records.hashes
853
+ if not (allow_unauthenticated or hashes.usable):
854
+ raise UntrustedError("The item %r could not be fetched: "
855
+ "No trusted hash found." %
856
+ destfile)
857
+ acq = apt_pkg.Acquire(progress or apt.progress.text.AcquireProgress())
858
+ acqfile = apt_pkg.AcquireFile(acq, self.uri, hashes,
859
+ self.size, base, destfile=destfile)
860
+ acq.run()
861
+
862
+ if acqfile.status != acqfile.STAT_DONE:
863
+ raise FetchError("The item %r could not be fetched: %s" %
864
+ (acqfile.destfile, acqfile.error_text))
865
+
866
+ return os.path.abspath(destfile)
867
+
868
+ def fetch_source(self, destdir="", progress=None, unpack=True,
869
+ allow_unauthenticated=None):
870
+ # type: (str, Optional[AcquireProgress], bool, Optional[bool]) -> str
871
+ """Get the source code of a package.
872
+
873
+ The parameter *destdir* specifies the directory where the source will
874
+ be fetched to.
875
+
876
+ The parameter *progress* may refer to an apt_pkg.AcquireProgress()
877
+ object. If not specified or None, apt.progress.text.AcquireProgress()
878
+ is used.
879
+
880
+ The parameter *unpack* describes whether the source should be unpacked
881
+ (``True``) or not (``False``). By default, it is unpacked.
882
+
883
+ If *unpack* is ``True``, the path to the extracted directory is
884
+ returned. Otherwise, the path to the .dsc file is returned.
885
+
886
+ The keyword-only parameter *allow_unauthenticated* specifies whether
887
+ to allow unauthenticated downloads. If not specified, it defaults to
888
+ the configuration option `APT::Get::AllowUnauthenticated`.
889
+ """
890
+ if allow_unauthenticated is None:
891
+ allow_unauthenticated = apt_pkg.config.find_b("APT::Get::"
892
+ "AllowUnauthenticated", False)
893
+
894
+ src = apt_pkg.SourceRecords()
895
+ acq = apt_pkg.Acquire(progress or apt.progress.text.AcquireProgress())
896
+
897
+ dsc = None
898
+ record = self._records
899
+ source_name = record.source_pkg or self.package.shortname
900
+ source_version = record.source_ver or self._cand.ver_str
901
+ source_lookup = src.lookup(source_name)
902
+
903
+ while source_lookup and source_version != src.version:
904
+ source_lookup = src.lookup(source_name)
905
+ if not source_lookup:
906
+ raise ValueError("No source for %r" % self)
907
+ files = list()
908
+
909
+ if not (allow_unauthenticated or src.index.is_trusted):
910
+ raise UntrustedError("Could not fetch %s %s source package: "
911
+ "Source %r is not trusted" %
912
+ (self.package.name, self.version,
913
+ src.index.describe))
914
+ for fil in src.files:
915
+ base = os.path.basename(fil.path)
916
+ destfile = os.path.join(destdir, base)
917
+ if fil.type == 'dsc':
918
+ dsc = destfile
919
+ if _file_is_same(destfile, fil.size, fil.hashes):
920
+ logging.debug('Ignoring already existing file: %s' % destfile)
921
+ continue
922
+
923
+ if not (allow_unauthenticated or fil.hashes.usable):
924
+ raise UntrustedError("The item %r could not be fetched: "
925
+ "No trusted hash found." %
926
+ destfile)
927
+ files.append(apt_pkg.AcquireFile(acq,
928
+ src.index.archive_uri(fil.path),
929
+ fil.hashes, fil.size, base, destfile=destfile))
930
+ acq.run()
931
+
932
+ if dsc is None:
933
+ raise ValueError("No source for %r" % self)
934
+
935
+ for item in acq.items:
936
+ if item.status != item.STAT_DONE:
937
+ raise FetchError("The item %r could not be fetched: %s" %
938
+ (item.destfile, item.error_text))
939
+
940
+ if unpack:
941
+ outdir = src.package + '-' + apt_pkg.upstream_version(src.version)
942
+ outdir = os.path.join(destdir, outdir)
943
+ subprocess.check_call(["dpkg-source", "-x", dsc, outdir])
944
+ return os.path.abspath(outdir)
945
+ else:
946
+ return os.path.abspath(dsc)
947
+
948
+
949
+ class VersionList(Sequence[Version]):
950
+ """Provide a mapping & sequence interface to all versions of a package.
951
+
952
+ This class can be used like a dictionary, where version strings are the
953
+ keys. It can also be used as a sequence, where integers are the keys.
954
+
955
+ You can also convert this to a dictionary or a list, using the usual way
956
+ of dict(version_list) or list(version_list). This is useful if you need
957
+ to access the version objects multiple times, because they do not have to
958
+ be recreated this way.
959
+
960
+ Examples ('package.versions' being a version list):
961
+ '0.7.92' in package.versions # Check whether 0.7.92 is a valid version.
962
+ package.versions[0] # Return first version or raise IndexError
963
+ package.versions[0:2] # Return a new VersionList for objects 0-2
964
+ package.versions['0.7.92'] # Return version 0.7.92 or raise KeyError
965
+ package.versions.keys() # All keys, as strings.
966
+ max(package.versions)
967
+ """
968
+
969
+ def __init__(self, package, slice_=None):
970
+ # type: (Package, Optional[slice]) -> None
971
+ self._package = package # apt.package.Package()
972
+ self._versions = package._pkg.version_list # [apt_pkg.Version(), ...]
973
+ if slice_:
974
+ self._versions = self._versions[slice_]
975
+
976
+ def __getitem__(self, item):
977
+ # type: (Union[int, slice, str]) -> Any
978
+ # FIXME: Should not be returning Any, should have overloads; but
979
+ # pyflakes complains
980
+ if isinstance(item, slice):
981
+ return self.__class__(self._package, item)
982
+ try:
983
+ # Sequence interface, item is an integer
984
+ return Version(self._package, self._versions[item]) # type: ignore
985
+ except TypeError:
986
+ # Dictionary interface item is a string.
987
+ for ver in self._versions:
988
+ if ver.ver_str == item:
989
+ return Version(self._package, ver)
990
+ raise KeyError("Version: %r not found." % (item))
991
+
992
+ def __str__(self):
993
+ # type: () -> str
994
+ return '[%s]' % (', '.join(str(ver) for ver in self))
995
+
996
+ def __repr__(self):
997
+ # type: () -> str
998
+ return '<VersionList: %r>' % self.keys()
999
+
1000
+ def __iter__(self):
1001
+ # type: () -> Iterator[Version]
1002
+ """Return an iterator over all value objects."""
1003
+ return (Version(self._package, ver) for ver in self._versions)
1004
+
1005
+ def __contains__(self, item):
1006
+ # type: (object) -> bool
1007
+ if isinstance(item, Version): # Sequence interface
1008
+ item = item.version
1009
+ # Dictionary interface.
1010
+ for ver in self._versions:
1011
+ if ver.ver_str == item:
1012
+ return True
1013
+ return False
1014
+
1015
+ def __eq__(self, other):
1016
+ # type: (Any) -> bool
1017
+ return list(self) == list(other)
1018
+
1019
+ def __len__(self):
1020
+ # type: () -> int
1021
+ return len(self._versions)
1022
+
1023
+ # Mapping interface
1024
+
1025
+ def keys(self):
1026
+ # type: () -> List[str]
1027
+ """Return a list of all versions, as strings."""
1028
+ return [ver.ver_str for ver in self._versions]
1029
+
1030
+ def get(self, key, default=None):
1031
+ # type: (str, Optional[Version]) -> Optional[Version]
1032
+ """Return the key or the default."""
1033
+ try:
1034
+ return self[key] # type: ignore # FIXME: should be deterined automatically # noqa
1035
+ except LookupError:
1036
+ return default
1037
+
1038
+
1039
+ class Package(object):
1040
+ """Representation of a package in a cache.
1041
+
1042
+ This class provides methods and properties for working with a package. It
1043
+ lets you mark the package for installation, check if it is installed, and
1044
+ much more.
1045
+ """
1046
+
1047
+ def __init__(self, pcache, pkgiter):
1048
+ # type: (apt.Cache, apt_pkg.Package) -> None
1049
+ """ Init the Package object """
1050
+ self._pkg = pkgiter
1051
+ self._pcache = pcache # python cache in cache.py
1052
+ self._changelog = "" # Cached changelog
1053
+
1054
+ def __str__(self):
1055
+ # type: () -> str
1056
+ return self.name
1057
+
1058
+ def __repr__(self):
1059
+ # type: () -> str
1060
+ return '<Package: name:%r architecture=%r id:%r>' % (
1061
+ self._pkg.name, self._pkg.architecture, self._pkg.id)
1062
+
1063
+ def __lt__(self, other):
1064
+ # type: (Package) -> bool
1065
+ return self.name < other.name
1066
+
1067
+ @property
1068
+ def candidate(self):
1069
+ # type: () -> Optional[Version]
1070
+ """Return the candidate version of the package.
1071
+
1072
+ This property is writeable to allow you to set the candidate version
1073
+ of the package. Just assign a Version() object, and it will be set as
1074
+ the candidate version.
1075
+ """
1076
+ cand = self._pcache._depcache.get_candidate_ver(self._pkg)
1077
+ if cand is not None:
1078
+ return Version(self, cand)
1079
+ return None
1080
+
1081
+ @candidate.setter
1082
+ def candidate(self, version):
1083
+ # type: (Version) -> None
1084
+ """Set the candidate version of the package."""
1085
+ self._pcache.cache_pre_change()
1086
+ self._pcache._depcache.set_candidate_ver(self._pkg, version._cand)
1087
+ self._pcache.cache_post_change()
1088
+
1089
+ @property
1090
+ def installed(self):
1091
+ # type: () -> Optional[Version]
1092
+ """Return the currently installed version of the package.
1093
+
1094
+ .. versionadded:: 0.7.9
1095
+ """
1096
+ if self._pkg.current_ver is not None:
1097
+ return Version(self, self._pkg.current_ver)
1098
+ return None
1099
+
1100
+ @property
1101
+ def name(self):
1102
+ # type: () -> str
1103
+ """Return the name of the package, possibly including architecture.
1104
+
1105
+ If the package is not part of the system's preferred architecture,
1106
+ return the same as :attr:`fullname`, otherwise return the same
1107
+ as :attr:`shortname`
1108
+
1109
+ .. versionchanged:: 0.7.100.3
1110
+
1111
+ As part of multi-arch, this field now may include architecture
1112
+ information.
1113
+ """
1114
+ return self._pkg.get_fullname(True)
1115
+
1116
+ @property
1117
+ def fullname(self):
1118
+ # type: () -> str
1119
+ """Return the name of the package, including architecture.
1120
+
1121
+ Note that as for :meth:`architecture`, this returns the
1122
+ native architecture for Architecture: all packages.
1123
+
1124
+ .. versionadded:: 0.7.100.3"""
1125
+ return self._pkg.get_fullname(False)
1126
+
1127
+ @property
1128
+ def shortname(self):
1129
+ # type: () -> str
1130
+ """Return the name of the package, without architecture.
1131
+
1132
+ .. versionadded:: 0.7.100.3"""
1133
+ return self._pkg.name
1134
+
1135
+ @property
1136
+ def id(self):
1137
+ # type: () -> int
1138
+ """Return a uniq ID for the package.
1139
+
1140
+ This can be used eg. to store additional information about the pkg."""
1141
+ return self._pkg.id
1142
+
1143
+ @property
1144
+ def essential(self):
1145
+ # type: () -> bool
1146
+ """Return True if the package is an essential part of the system."""
1147
+ return self._pkg.essential
1148
+
1149
+ def architecture(self):
1150
+ # type: () -> str
1151
+ """Return the Architecture of the package.
1152
+
1153
+ Note that for Architecture: all packages, this returns the
1154
+ native architecture, as they are internally treated like native
1155
+ packages. To get the concrete architecture, look at the
1156
+ :attr:`Version.architecture` attribute.
1157
+
1158
+ .. versionchanged:: 0.7.100.3
1159
+ This is now the package's architecture in the multi-arch sense,
1160
+ previously it was the architecture of the candidate version
1161
+ and deprecated.
1162
+ """
1163
+ return self._pkg.architecture
1164
+
1165
+ # depcache states
1166
+
1167
+ @property
1168
+ def marked_install(self):
1169
+ # type: () -> bool
1170
+ """Return ``True`` if the package is marked for install."""
1171
+ return self._pcache._depcache.marked_install(self._pkg)
1172
+
1173
+ @property
1174
+ def marked_upgrade(self):
1175
+ # type: () -> bool
1176
+ """Return ``True`` if the package is marked for upgrade."""
1177
+ return self._pcache._depcache.marked_upgrade(self._pkg)
1178
+
1179
+ @property
1180
+ def marked_delete(self):
1181
+ # type: () -> bool
1182
+ """Return ``True`` if the package is marked for delete."""
1183
+ return self._pcache._depcache.marked_delete(self._pkg)
1184
+
1185
+ @property
1186
+ def marked_keep(self):
1187
+ # type: () -> bool
1188
+ """Return ``True`` if the package is marked for keep."""
1189
+ return self._pcache._depcache.marked_keep(self._pkg)
1190
+
1191
+ @property
1192
+ def marked_downgrade(self):
1193
+ # type: () -> bool
1194
+ """ Package is marked for downgrade """
1195
+ return self._pcache._depcache.marked_downgrade(self._pkg)
1196
+
1197
+ @property
1198
+ def marked_reinstall(self):
1199
+ # type: () -> bool
1200
+ """Return ``True`` if the package is marked for reinstall."""
1201
+ return self._pcache._depcache.marked_reinstall(self._pkg)
1202
+
1203
+ @property
1204
+ def is_installed(self):
1205
+ # type: () -> bool
1206
+ """Return ``True`` if the package is installed."""
1207
+ return (self._pkg.current_ver is not None)
1208
+
1209
+ @property
1210
+ def is_upgradable(self):
1211
+ # type: () -> bool
1212
+ """Return ``True`` if the package is upgradable."""
1213
+ return (self.is_installed and
1214
+ self._pcache._depcache.is_upgradable(self._pkg))
1215
+
1216
+ @property
1217
+ def is_auto_removable(self):
1218
+ # type: () -> bool
1219
+ """Return ``True`` if the package is no longer required.
1220
+
1221
+ If the package has been installed automatically as a dependency of
1222
+ another package, and if no packages depend on it anymore, the package
1223
+ is no longer required.
1224
+ """
1225
+ return ((self.is_installed or self.marked_install) and
1226
+ self._pcache._depcache.is_garbage(self._pkg))
1227
+
1228
+ @property
1229
+ def is_auto_installed(self):
1230
+ # type: () -> bool
1231
+ """Return whether the package is marked as automatically installed."""
1232
+ return self._pcache._depcache.is_auto_installed(self._pkg)
1233
+ # sizes
1234
+
1235
+ @property
1236
+ def installed_files(self):
1237
+ # type: () -> List[str]
1238
+ """Return a list of files installed by the package.
1239
+
1240
+ Return a list of unicode names of the files which have
1241
+ been installed by this package
1242
+ """
1243
+ for name in self.name, self.fullname:
1244
+ path = "/var/lib/dpkg/info/%s.list" % name
1245
+ try:
1246
+ with open(path, "rb") as file_list:
1247
+ return file_list.read().decode("utf-8").split(u"\n")
1248
+ except EnvironmentError:
1249
+ continue
1250
+
1251
+ return []
1252
+
1253
+ def get_changelog(self, uri=None, cancel_lock=None):
1254
+ # type: (Optional[str], Optional[threading.Event]) -> str
1255
+ """
1256
+ Download the changelog of the package and return it as unicode
1257
+ string.
1258
+
1259
+ The parameter *uri* refers to the uri of the changelog file. It may
1260
+ contain multiple named variables which will be substitued. These
1261
+ variables are (src_section, prefix, src_pkg, src_ver). An example is
1262
+ the Ubuntu changelog::
1263
+
1264
+ "http://changelogs.ubuntu.com/changelogs/pool" \\
1265
+ "/%(src_section)s/%(prefix)s/%(src_pkg)s" \\
1266
+ "/%(src_pkg)s_%(src_ver)s/changelog"
1267
+
1268
+ The parameter *cancel_lock* refers to an instance of threading.Event,
1269
+ which if set, prevents the download.
1270
+ """
1271
+ # Return a cached changelog if available
1272
+ if self._changelog != u"":
1273
+ return self._changelog
1274
+
1275
+ if not self.candidate:
1276
+ return _("The list of changes is not available")
1277
+
1278
+ if uri is None:
1279
+ if self.candidate.origins[0].origin == "Debian":
1280
+ uri = "http://packages.debian.org/changelogs/pool" \
1281
+ "/%(src_section)s/%(prefix)s/%(src_pkg)s" \
1282
+ "/%(src_pkg)s_%(src_ver)s/changelog"
1283
+ elif self.candidate.origins[0].origin == "Ubuntu":
1284
+ uri = "http://changelogs.ubuntu.com/changelogs/pool" \
1285
+ "/%(src_section)s/%(prefix)s/%(src_pkg)s" \
1286
+ "/%(src_pkg)s_%(src_ver)s/changelog"
1287
+ else:
1288
+ res = _("The list of changes is not available")
1289
+ if isinstance(res, str):
1290
+ return res
1291
+ else:
1292
+ return res.decode("utf-8")
1293
+
1294
+ # get the src package name
1295
+ src_pkg = self.candidate.source_name
1296
+
1297
+ # assume "main" section
1298
+ src_section = "main"
1299
+ # use the section of the candidate as a starting point
1300
+ section = self.candidate.section
1301
+
1302
+ # get the source version
1303
+ src_ver = self.candidate.source_version
1304
+
1305
+ try:
1306
+ # try to get the source version of the pkg, this differs
1307
+ # for some (e.g. libnspr4 on ubuntu)
1308
+ # this feature only works if the correct deb-src are in the
1309
+ # sources.list otherwise we fall back to the binary version number
1310
+ src_records = apt_pkg.SourceRecords()
1311
+ except SystemError:
1312
+ pass
1313
+ else:
1314
+ while src_records.lookup(src_pkg):
1315
+ if not src_records.version:
1316
+ continue
1317
+ if self.candidate.source_version == src_records.version:
1318
+ # Direct match, use it and do not do more lookups.
1319
+ src_ver = src_records.version
1320
+ section = src_records.section
1321
+ break
1322
+ if apt_pkg.version_compare(src_records.version, src_ver) > 0:
1323
+ # The version is higher, it seems to match.
1324
+ src_ver = src_records.version
1325
+ section = src_records.section
1326
+
1327
+ section_split = section.split("/", 1)
1328
+ if len(section_split) > 1:
1329
+ src_section = section_split[0]
1330
+ del section_split
1331
+
1332
+ # lib is handled special
1333
+ prefix = src_pkg[0]
1334
+ if src_pkg.startswith("lib"):
1335
+ prefix = "lib" + src_pkg[3]
1336
+
1337
+ # stip epoch
1338
+ src_ver_split = src_ver.split(":", 1)
1339
+ if len(src_ver_split) > 1:
1340
+ src_ver = "".join(src_ver_split[1:])
1341
+ del src_ver_split
1342
+
1343
+ uri = uri % {"src_section": src_section,
1344
+ "prefix": prefix,
1345
+ "src_pkg": src_pkg,
1346
+ "src_ver": src_ver}
1347
+
1348
+ timeout = socket.getdefaulttimeout()
1349
+
1350
+ # FIXME: when python2.4 vanishes from the archive,
1351
+ # merge this into a single try..finally block (pep 341)
1352
+ try:
1353
+ try:
1354
+ # Set a timeout for the changelog download
1355
+ socket.setdefaulttimeout(2)
1356
+
1357
+ # Check if the download was canceled
1358
+ if cancel_lock and cancel_lock.is_set():
1359
+ return u""
1360
+ # FIXME: python3.2: Should be closed manually
1361
+ changelog_file = urlopen(uri)
1362
+ # do only get the lines that are new
1363
+ changelog = u""
1364
+ regexp = "^%s \\((.*)\\)(.*)$" % (re.escape(src_pkg))
1365
+ while True:
1366
+ # Check if the download was canceled
1367
+ if cancel_lock and cancel_lock.is_set():
1368
+ return u""
1369
+ # Read changelog line by line
1370
+ line_raw = changelog_file.readline()
1371
+ if not line_raw:
1372
+ break
1373
+ # The changelog is encoded in utf-8, but since there isn't
1374
+ # any http header, urllib2 seems to treat it as ascii
1375
+ line = line_raw.decode("utf-8")
1376
+
1377
+ #print line.encode('utf-8')
1378
+ match = re.match(regexp, line)
1379
+ if match:
1380
+ # strip epoch from installed version
1381
+ # and from changelog too
1382
+ installed = getattr(self.installed, 'version', None)
1383
+ if installed and ":" in installed:
1384
+ installed = installed.split(":", 1)[1]
1385
+ changelog_ver = match.group(1)
1386
+ if changelog_ver and ":" in changelog_ver:
1387
+ changelog_ver = changelog_ver.split(":", 1)[1]
1388
+
1389
+ if (installed and apt_pkg.version_compare(
1390
+ changelog_ver, installed) <= 0):
1391
+ break
1392
+ # EOF (shouldn't really happen)
1393
+ changelog += line
1394
+
1395
+ # Print an error if we failed to extract a changelog
1396
+ if len(changelog) == 0:
1397
+ changelog = _("The list of changes is not available")
1398
+ if not isinstance(changelog, str):
1399
+ changelog = changelog.decode("utf-8")
1400
+ self._changelog = changelog
1401
+
1402
+ except HTTPError:
1403
+ if self.candidate.origins[0].origin == "Ubuntu":
1404
+ res = _("The list of changes is not available yet.\n\n"
1405
+ "Please use "
1406
+ "http://launchpad.net/ubuntu/+source/%s/"
1407
+ "%s/+changelog\n"
1408
+ "until the changes become available or try again "
1409
+ "later.") % (src_pkg, src_ver)
1410
+ else:
1411
+ res = _("The list of changes is not available")
1412
+ if isinstance(res, str):
1413
+ return res
1414
+ else:
1415
+ return res.decode("utf-8")
1416
+ except (IOError, BadStatusLine):
1417
+ res = _("Failed to download the list of changes. \nPlease "
1418
+ "check your Internet connection.")
1419
+ if isinstance(res, str):
1420
+ return res
1421
+ else:
1422
+ return res.decode("utf-8")
1423
+ finally:
1424
+ socket.setdefaulttimeout(timeout)
1425
+ return self._changelog
1426
+
1427
+ @property
1428
+ def versions(self):
1429
+ # type: () -> VersionList
1430
+ """Return a VersionList() object for all available versions.
1431
+
1432
+ .. versionadded:: 0.7.9
1433
+ """
1434
+ return VersionList(self)
1435
+
1436
+ @property
1437
+ def is_inst_broken(self):
1438
+ # type: () -> bool
1439
+ """Return True if the to-be-installed package is broken."""
1440
+ return self._pcache._depcache.is_inst_broken(self._pkg)
1441
+
1442
+ @property
1443
+ def is_now_broken(self):
1444
+ # type: () -> bool
1445
+ """Return True if the installed package is broken."""
1446
+ return self._pcache._depcache.is_now_broken(self._pkg)
1447
+
1448
+ @property
1449
+ def has_config_files(self):
1450
+ # type: () -> bool
1451
+ """Checks whether the package is is the config-files state."""
1452
+ return self. _pkg.current_state == apt_pkg.CURSTATE_CONFIG_FILES
1453
+
1454
+ # depcache actions
1455
+
1456
+ def mark_keep(self):
1457
+ # type: () -> None
1458
+ """Mark a package for keep."""
1459
+ self._pcache.cache_pre_change()
1460
+ self._pcache._depcache.mark_keep(self._pkg)
1461
+ self._pcache.cache_post_change()
1462
+
1463
+ def mark_delete(self, auto_fix=True, purge=False):
1464
+ # type: (bool, bool) -> None
1465
+ """Mark a package for deletion.
1466
+
1467
+ If *auto_fix* is ``True``, the resolver will be run, trying to fix
1468
+ broken packages. This is the default.
1469
+
1470
+ If *purge* is ``True``, remove the configuration files of the package
1471
+ as well. The default is to keep the configuration.
1472
+ """
1473
+ self._pcache.cache_pre_change()
1474
+ self._pcache._depcache.mark_delete(self._pkg, purge)
1475
+ # try to fix broken stuffsta
1476
+ if auto_fix and self._pcache._depcache.broken_count > 0:
1477
+ fix = apt_pkg.ProblemResolver(self._pcache._depcache)
1478
+ fix.clear(self._pkg)
1479
+ fix.protect(self._pkg)
1480
+ fix.remove(self._pkg)
1481
+ fix.resolve()
1482
+ self._pcache.cache_post_change()
1483
+
1484
+ def mark_install(self, auto_fix=True, auto_inst=True, from_user=True):
1485
+ # type: (bool, bool, bool) -> None
1486
+ """Mark a package for install.
1487
+
1488
+ If *autoFix* is ``True``, the resolver will be run, trying to fix
1489
+ broken packages. This is the default.
1490
+
1491
+ If *autoInst* is ``True``, the dependencies of the packages will be
1492
+ installed automatically. This is the default.
1493
+
1494
+ If *fromUser* is ``True``, this package will not be marked as
1495
+ automatically installed. This is the default. Set it to False if you
1496
+ want to be able to automatically remove the package at a later stage
1497
+ when no other package depends on it.
1498
+ """
1499
+ self._pcache.cache_pre_change()
1500
+ self._pcache._depcache.mark_install(self._pkg, auto_inst, from_user)
1501
+ # try to fix broken stuff
1502
+ if auto_fix and self._pcache._depcache.broken_count > 0:
1503
+ fixer = apt_pkg.ProblemResolver(self._pcache._depcache)
1504
+ fixer.clear(self._pkg)
1505
+ fixer.protect(self._pkg)
1506
+ fixer.resolve(True)
1507
+ self._pcache.cache_post_change()
1508
+
1509
+ def mark_upgrade(self, from_user=True):
1510
+ # type: (bool) -> None
1511
+ """Mark a package for upgrade."""
1512
+ if self.is_upgradable:
1513
+ auto = self.is_auto_installed
1514
+ self.mark_install(from_user=from_user)
1515
+ self.mark_auto(auto)
1516
+ else:
1517
+ # FIXME: we may want to throw a exception here
1518
+ sys.stderr.write(("MarkUpgrade() called on a non-upgradeable pkg: "
1519
+ "'%s'\n") % self._pkg.name)
1520
+
1521
+ def mark_auto(self, auto=True):
1522
+ # type: (bool) -> None
1523
+ """Mark a package as automatically installed.
1524
+
1525
+ Call this function to mark a package as automatically installed. If the
1526
+ optional parameter *auto* is set to ``False``, the package will not be
1527
+ marked as automatically installed anymore. The default is ``True``.
1528
+ """
1529
+ self._pcache._depcache.mark_auto(self._pkg, auto)
1530
+
1531
+ def commit(self, fprogress, iprogress):
1532
+ # type: (AcquireProgress, InstallProgress) -> None
1533
+ """Commit the changes.
1534
+
1535
+ The parameter *fprogress* refers to a apt_pkg.AcquireProgress() object,
1536
+ like apt.progress.text.AcquireProgress().
1537
+
1538
+ The parameter *iprogress* refers to an InstallProgress() object, as
1539
+ found in apt.progress.base.
1540
+ """
1541
+ self._pcache._depcache.commit(fprogress, iprogress)
1542
+
1543
+
1544
+ @no_type_check
1545
+ def _test():
1546
+ """Self-test."""
1547
+ print("Self-test for the Package modul")
1548
+ import random
1549
+ apt_pkg.init()
1550
+ progress = apt.progress.text.OpProgress()
1551
+ cache = apt.Cache(progress)
1552
+ pkg = cache["apt-utils"]
1553
+ print("Name: %s " % pkg.name)
1554
+ print("ID: %s " % pkg.id)
1555
+ print("Priority (Candidate): %s " % pkg.candidate.priority)
1556
+ print("Priority (Installed): %s " % pkg.installed.priority)
1557
+ print("Installed: %s " % pkg.installed.version)
1558
+ print("Candidate: %s " % pkg.candidate.version)
1559
+ print("CandidateDownloadable: %s" % pkg.candidate.downloadable)
1560
+ print("CandidateOrigins: %s" % pkg.candidate.origins)
1561
+ print("SourcePkg: %s " % pkg.candidate.source_name)
1562
+ print("Section: %s " % pkg.section)
1563
+ print("Summary: %s" % pkg.candidate.summary)
1564
+ print("Description (formatted) :\n%s" % pkg.candidate.description)
1565
+ print("Description (unformatted):\n%s" % pkg.candidate.raw_description)
1566
+ print("InstalledSize: %s " % pkg.candidate.installed_size)
1567
+ print("PackageSize: %s " % pkg.candidate.size)
1568
+ print("Dependencies: %s" % pkg.installed.dependencies)
1569
+ print("Recommends: %s" % pkg.installed.recommends)
1570
+ for dep in pkg.candidate.dependencies:
1571
+ print(",".join("%s (%s) (%s) (%s)" % (o.name, o.version, o.relation,
1572
+ o.pre_depend) for o in dep.or_dependencies))
1573
+ print("arch: %s" % pkg.candidate.architecture)
1574
+ print("homepage: %s" % pkg.candidate.homepage)
1575
+ print("rec: ", pkg.candidate.record)
1576
+
1577
+ print(cache["2vcard"].get_changelog())
1578
+ for i in True, False:
1579
+ print("Running install on random upgradable pkgs with AutoFix: ", i)
1580
+ for pkg in cache:
1581
+ if pkg.is_upgradable:
1582
+ if random.randint(0, 1) == 1:
1583
+ pkg.mark_install(i)
1584
+ print("Broken: %s " % cache._depcache.broken_count)
1585
+ print("InstCount: %s " % cache._depcache.inst_count)
1586
+
1587
+ print()
1588
+ # get a new cache
1589
+ for i in True, False:
1590
+ print("Randomly remove some packages with AutoFix: %s" % i)
1591
+ cache = apt.Cache(progress)
1592
+ for name in cache.keys():
1593
+ if random.randint(0, 1) == 1:
1594
+ try:
1595
+ cache[name].mark_delete(i)
1596
+ except SystemError:
1597
+ print("Error trying to remove: %s " % name)
1598
+ print("Broken: %s " % cache._depcache.broken_count)
1599
+ print("DelCount: %s " % cache._depcache.del_count)
1600
+
1601
+
1602
+ # self-test
1603
+ if __name__ == "__main__":
1604
+ _test()
build/lib.linux-x86_64-cpython-310/apt/progress/__init__.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # apt/progress/__init__.py - Initialization file for apt.progress.
2
+ #
3
+ # Copyright (c) 2009 Julian Andres Klode <[email protected]>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License as
7
+ # published by the Free Software Foundation; either version 2 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
+ # USA
19
+ """Progress reporting.
20
+
21
+ This package provides progress reporting for the python-apt package. The module
22
+ 'base' provides classes with no output, and the module 'text' provides classes
23
+ for terminals, etc.
24
+ """
25
+
26
+ from __future__ import print_function
27
+
28
+ from typing import Sequence
29
+
30
+
31
+ __all__ = [] # type: Sequence[str]
build/lib.linux-x86_64-cpython-310/apt/progress/base.py ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # apt/progress/base.py - Base classes for progress reporting.
2
+ #
3
+ # Copyright (C) 2009 Julian Andres Klode <[email protected]>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License as
7
+ # published by the Free Software Foundation; either version 2 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
+ # USA
19
+ # pylint: disable-msg = R0201
20
+ """Base classes for progress reporting.
21
+
22
+ Custom progress classes should inherit from these classes. They can also be
23
+ used as dummy progress classes which simply do nothing.
24
+ """
25
+ from __future__ import print_function
26
+
27
+ import errno
28
+ import fcntl
29
+ import io
30
+ import os
31
+ import re
32
+ import select
33
+ import sys
34
+
35
+ from typing import Optional, Union
36
+
37
+ import apt_pkg
38
+
39
+ __all__ = ['AcquireProgress', 'CdromProgress', 'InstallProgress', 'OpProgress']
40
+
41
+
42
+ class AcquireProgress(object):
43
+ """Monitor object for downloads controlled by the Acquire class.
44
+
45
+ This is an mostly abstract class. You should subclass it and implement the
46
+ methods to get something useful.
47
+ """
48
+
49
+ current_bytes = current_cps = fetched_bytes = last_bytes = total_bytes \
50
+ = 0.0
51
+ current_items = elapsed_time = total_items = 0
52
+
53
+ def done(self, item):
54
+ # type: (apt_pkg.AcquireItemDesc) -> None
55
+ """Invoked when an item is successfully and completely fetched."""
56
+
57
+ def fail(self, item):
58
+ # type: (apt_pkg.AcquireItemDesc) -> None
59
+ """Invoked when an item could not be fetched."""
60
+
61
+ def fetch(self, item):
62
+ # type: (apt_pkg.AcquireItemDesc) -> None
63
+ """Invoked when some of the item's data is fetched."""
64
+
65
+ def ims_hit(self, item):
66
+ # type: (apt_pkg.AcquireItemDesc) -> None
67
+ """Invoked when an item is confirmed to be up-to-date.
68
+
69
+ Invoked when an item is confirmed to be up-to-date. For instance,
70
+ when an HTTP download is informed that the file on the server was
71
+ not modified.
72
+ """
73
+
74
+ def media_change(self, media, drive):
75
+ # type: (str, str) -> bool
76
+ """Prompt the user to change the inserted removable media.
77
+
78
+ The parameter 'media' decribes the name of the media type that
79
+ should be changed, whereas the parameter 'drive' should be the
80
+ identifying name of the drive whose media should be changed.
81
+
82
+ This method should not return until the user has confirmed to the user
83
+ interface that the media change is complete. It must return True if
84
+ the user confirms the media change, or False to cancel it.
85
+ """
86
+ return False
87
+
88
+ def pulse(self, owner):
89
+ # type: (apt_pkg.Acquire) -> bool
90
+ """Periodically invoked while the Acquire process is underway.
91
+
92
+ This method gets invoked while the Acquire progress given by the
93
+ parameter 'owner' is underway. It should display information about
94
+ the current state.
95
+
96
+ This function returns a boolean value indicating whether the
97
+ acquisition should be continued (True) or cancelled (False).
98
+ """
99
+ return True
100
+
101
+ def start(self):
102
+ # type: () -> None
103
+ """Invoked when the Acquire process starts running."""
104
+ # Reset all our values.
105
+ self.current_bytes = 0.0
106
+ self.current_cps = 0.0
107
+ self.current_items = 0
108
+ self.elapsed_time = 0
109
+ self.fetched_bytes = 0.0
110
+ self.last_bytes = 0.0
111
+ self.total_bytes = 0.0
112
+ self.total_items = 0
113
+
114
+ def stop(self):
115
+ # type: () -> None
116
+ """Invoked when the Acquire process stops running."""
117
+
118
+
119
+ class CdromProgress(object):
120
+ """Base class for reporting the progress of adding a cdrom.
121
+
122
+ Can be used with apt_pkg.Cdrom to produce an utility like apt-cdrom. The
123
+ attribute 'total_steps' defines the total number of steps and can be used
124
+ in update() to display the current progress.
125
+ """
126
+
127
+ total_steps = 0
128
+
129
+ def ask_cdrom_name(self):
130
+ # type: () -> Optional[str]
131
+ """Ask for the name of the cdrom.
132
+
133
+ If a name has been provided, return it. Otherwise, return None to
134
+ cancel the operation.
135
+ """
136
+
137
+ def change_cdrom(self):
138
+ # type: () -> bool
139
+ """Ask for the CD-ROM to be changed.
140
+
141
+ Return True once the cdrom has been changed or False to cancel the
142
+ operation.
143
+ """
144
+
145
+ def update(self, text, current):
146
+ # type: (str, int) -> None
147
+ """Periodically invoked to update the interface.
148
+
149
+ The string 'text' defines the text which should be displayed. The
150
+ integer 'current' defines the number of completed steps.
151
+ """
152
+
153
+
154
+ class InstallProgress(object):
155
+ """Class to report the progress of installing packages."""
156
+
157
+ child_pid, percent, select_timeout, status = 0, 0.0, 0.1, ""
158
+
159
+ def __init__(self):
160
+ # type: () -> None
161
+ (self.statusfd, self.writefd) = os.pipe()
162
+ # These will leak fds, but fixing this safely requires API changes.
163
+ self.write_stream = os.fdopen(self.writefd, "w") # type: io.TextIOBase
164
+ self.status_stream = os.fdopen(self.statusfd, "r") # type: io.TextIOBase # noqa
165
+ fcntl.fcntl(self.statusfd, fcntl.F_SETFL, os.O_NONBLOCK)
166
+
167
+ def start_update(self):
168
+ # type: () -> None
169
+ """(Abstract) Start update."""
170
+
171
+ def finish_update(self):
172
+ # type: () -> None
173
+ """(Abstract) Called when update has finished."""
174
+
175
+ def __enter__(self):
176
+ # type: () -> InstallProgress
177
+ return self
178
+
179
+ def __exit__(self, type, value, traceback):
180
+ # type: (object, object, object) -> None
181
+ self.write_stream.close()
182
+ self.status_stream.close()
183
+
184
+ def error(self, pkg, errormsg):
185
+ # type: (str, str) -> None
186
+ """(Abstract) Called when a error is detected during the install."""
187
+
188
+ def conffile(self, current, new):
189
+ # type: (str, str) -> None
190
+ """(Abstract) Called when a conffile question from dpkg is detected."""
191
+
192
+ def status_change(self, pkg, percent, status):
193
+ # type: (str, float, str) -> None
194
+ """(Abstract) Called when the APT status changed."""
195
+
196
+ def dpkg_status_change(self, pkg, status):
197
+ # type: (str, str) -> None
198
+ """(Abstract) Called when the dpkg status changed."""
199
+
200
+ def processing(self, pkg, stage):
201
+ # type: (str, str) -> None
202
+ """(Abstract) Sent just before a processing stage starts.
203
+
204
+ The parameter 'stage' is one of "upgrade", "install"
205
+ (both sent before unpacking), "configure", "trigproc", "remove",
206
+ "purge". This method is used for dpkg only.
207
+ """
208
+
209
+ def run(self, obj):
210
+ # type: (Union[apt_pkg.PackageManager, Union[bytes, str]]) -> int
211
+ """Install using the object 'obj'.
212
+
213
+ This functions runs install actions. The parameter 'obj' may either
214
+ be a PackageManager object in which case its do_install() method is
215
+ called or the path to a deb file.
216
+
217
+ If the object is a PackageManager, the functions returns the result
218
+ of calling its do_install() method. Otherwise, the function returns
219
+ the exit status of dpkg. In both cases, 0 means that there were no
220
+ problems.
221
+ """
222
+ pid = self.fork()
223
+ if pid == 0:
224
+ try:
225
+ # PEP-446 implemented in Python 3.4 made all descriptors
226
+ # CLOEXEC, but we need to be able to pass writefd to dpkg
227
+ # when we spawn it
228
+ os.set_inheritable(self.writefd, True)
229
+ except AttributeError: # if we don't have os.set_inheritable()
230
+ pass
231
+ # pm.do_install might raise a exception,
232
+ # when this happens, we need to catch
233
+ # it, otherwise os._exit() is not run
234
+ # and the execution continues in the
235
+ # parent code leading to very confusing bugs
236
+ try:
237
+ os._exit(obj.do_install(self.write_stream.fileno())) # type: ignore # noqa
238
+ except AttributeError:
239
+ os._exit(os.spawnlp(os.P_WAIT, "dpkg", "dpkg", "--status-fd",
240
+ str(self.write_stream.fileno()), "-i",
241
+ obj)) # type: ignore # noqa
242
+ except Exception as e:
243
+ sys.stderr.write("%s\n" % e)
244
+ os._exit(apt_pkg.PackageManager.RESULT_FAILED)
245
+
246
+ self.child_pid = pid
247
+ res = self.wait_child()
248
+ return os.WEXITSTATUS(res)
249
+
250
+ def fork(self):
251
+ # type: () -> int
252
+ """Fork."""
253
+ return os.fork()
254
+
255
+ def update_interface(self):
256
+ # type: () -> None
257
+ """Update the interface."""
258
+ try:
259
+ line = self.status_stream.readline()
260
+ except IOError as err:
261
+ # resource temporarly unavailable is ignored
262
+ if err.errno != errno.EAGAIN and err.errno != errno.EWOULDBLOCK:
263
+ print(err.strerror)
264
+ return
265
+
266
+ pkgname = status = status_str = percent = base = ""
267
+
268
+ if line.startswith('pm'):
269
+ try:
270
+ (status, pkgname, percent, status_str) = line.split(":", 3)
271
+ except ValueError:
272
+ # silently ignore lines that can't be parsed
273
+ return
274
+ elif line.startswith('status'):
275
+ try:
276
+ (base, pkgname, status, status_str) = line.split(":", 3)
277
+ except ValueError:
278
+ (base, pkgname, status) = line.split(":", 2)
279
+ elif line.startswith('processing'):
280
+ (status, status_str, pkgname) = line.split(":", 2)
281
+ self.processing(pkgname.strip(), status_str.strip())
282
+
283
+ # Always strip the status message
284
+ pkgname = pkgname.strip()
285
+ status_str = status_str.strip()
286
+ status = status.strip()
287
+
288
+ if status == 'pmerror' or status == 'error':
289
+ self.error(pkgname, status_str)
290
+ elif status == 'conffile-prompt' or status == 'pmconffile':
291
+ match = re.match("\\s*\'(.*)\'\\s*\'(.*)\'.*", status_str)
292
+ if match:
293
+ self.conffile(match.group(1), match.group(2))
294
+ elif status == "pmstatus":
295
+ # FIXME: Float comparison
296
+ if float(percent) != self.percent or status_str != self.status:
297
+ self.status_change(pkgname, float(percent), status_str.strip())
298
+ self.percent = float(percent)
299
+ self.status = status_str.strip()
300
+ elif base == "status":
301
+ self.dpkg_status_change(pkgname, status)
302
+
303
+ def wait_child(self):
304
+ # type: () -> int
305
+ """Wait for child progress to exit.
306
+
307
+ This method is responsible for calling update_interface() from time to
308
+ time. It exits once the child has exited. The return values is the
309
+ full status returned from os.waitpid() (not only the return code).
310
+ """
311
+ (pid, res) = (0, 0)
312
+ while True:
313
+ try:
314
+ select.select([self.status_stream], [], [],
315
+ self.select_timeout)
316
+ except select.error as error:
317
+ (errno_, _errstr) = error.args
318
+ if errno_ != errno.EINTR:
319
+ raise
320
+
321
+ self.update_interface()
322
+ try:
323
+ (pid, res) = os.waitpid(self.child_pid, os.WNOHANG)
324
+ if pid == self.child_pid:
325
+ break
326
+ except OSError as err:
327
+ if err.errno == errno.ECHILD:
328
+ break
329
+ if err.errno != errno.EINTR:
330
+ raise
331
+
332
+ return res
333
+
334
+
335
+ class OpProgress(object):
336
+ """Monitor objects for operations.
337
+
338
+ Display the progress of operations such as opening the cache."""
339
+
340
+ major_change, op, percent, subop = False, "", 0.0, ""
341
+
342
+ def update(self, percent=None):
343
+ # type: (Optional[float]) -> None
344
+ """Called periodically to update the user interface.
345
+
346
+ You may use the optional argument 'percent' to set the attribute
347
+ 'percent' in this call.
348
+ """
349
+ if percent is not None:
350
+ self.percent = percent
351
+
352
+ def done(self):
353
+ # type: () -> None
354
+ """Called once an operation has been completed."""
build/lib.linux-x86_64-cpython-310/apt/progress/text.py ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2009 Julian Andres Klode <[email protected]>
2
+ #
3
+ # This program is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU General Public License as
5
+ # published by the Free Software Foundation; either version 2 of the
6
+ # License, or (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program; if not, write to the Free Software
15
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
16
+ # USA
17
+ """Progress reporting for text interfaces."""
18
+ from __future__ import print_function
19
+
20
+ import io
21
+ import os
22
+ import signal
23
+ import sys
24
+
25
+ import types
26
+ from typing import Callable, Optional, Union
27
+
28
+
29
+ import apt_pkg
30
+ from apt.progress import base
31
+
32
+
33
+ __all__ = ['AcquireProgress', 'CdromProgress', 'OpProgress']
34
+
35
+
36
+ def _(msg):
37
+ # type: (str) -> str
38
+ """Translate the message, also try apt if translation is missing."""
39
+ res = apt_pkg.gettext(msg)
40
+ if res == msg:
41
+ res = apt_pkg.gettext(msg, "apt")
42
+ return res
43
+
44
+
45
+ class TextProgress(object):
46
+ """Internal Base class for text progress classes."""
47
+
48
+ def __init__(self, outfile=None):
49
+ # type: (Optional[io.TextIOBase]) -> None
50
+ self._file = outfile or sys.stdout
51
+ self._width = 0
52
+
53
+ def _write(self, msg, newline=True, maximize=False):
54
+ # type: (str, bool, bool) -> None
55
+ """Write the message on the terminal, fill remaining space."""
56
+ self._file.write("\r")
57
+ self._file.write(msg)
58
+
59
+ # Fill remaining stuff with whitespace
60
+ if self._width > len(msg):
61
+ self._file.write((self._width - len(msg)) * ' ')
62
+ elif maximize: # Needed for OpProgress.
63
+ self._width = max(self._width, len(msg))
64
+ if newline:
65
+ self._file.write("\n")
66
+ else:
67
+ #self._file.write("\r")
68
+ self._file.flush()
69
+
70
+
71
+ class OpProgress(base.OpProgress, TextProgress):
72
+ """Operation progress reporting.
73
+
74
+ This closely resembles OpTextProgress in libapt-pkg.
75
+ """
76
+
77
+ def __init__(self, outfile=None):
78
+ # type: (Optional[io.TextIOBase]) -> None
79
+ TextProgress.__init__(self, outfile)
80
+ base.OpProgress.__init__(self)
81
+ self.old_op = ""
82
+
83
+ def update(self, percent=None):
84
+ # type: (Optional[float]) -> None
85
+ """Called periodically to update the user interface."""
86
+ base.OpProgress.update(self, percent)
87
+ if self.major_change and self.old_op:
88
+ self._write(self.old_op)
89
+ self._write("%s... %i%%\r" % (self.op, self.percent), False, True)
90
+ self.old_op = self.op
91
+
92
+ def done(self):
93
+ # type: () -> None
94
+ """Called once an operation has been completed."""
95
+ base.OpProgress.done(self)
96
+ if self.old_op:
97
+ self._write(_("%c%s... Done") % ('\r', self.old_op), True, True)
98
+ self.old_op = ""
99
+
100
+
101
+ class AcquireProgress(base.AcquireProgress, TextProgress):
102
+ """AcquireProgress for the text interface."""
103
+
104
+ def __init__(self, outfile=None):
105
+ # type: (Optional[io.TextIOBase]) -> None
106
+ TextProgress.__init__(self, outfile)
107
+ base.AcquireProgress.__init__(self)
108
+ self._signal = None # type: Union[Callable[[int, Optional[types.FrameType]], None], int, signal.Handlers, None] # noqa
109
+ self._width = 80
110
+ self._id = 1
111
+
112
+ def start(self):
113
+ # type: () -> None
114
+ """Start an Acquire progress.
115
+
116
+ In this case, the function sets up a signal handler for SIGWINCH, i.e.
117
+ window resize signals. And it also sets id to 1.
118
+ """
119
+ base.AcquireProgress.start(self)
120
+ self._signal = signal.signal(signal.SIGWINCH, self._winch)
121
+ # Get the window size.
122
+ self._winch()
123
+ self._id = 1
124
+
125
+ def _winch(self, *dummy):
126
+ # type: (object) -> None
127
+ """Signal handler for window resize signals."""
128
+ if hasattr(self._file, "fileno") and os.isatty(self._file.fileno()):
129
+ import fcntl
130
+ import termios
131
+ import struct
132
+ buf = fcntl.ioctl(self._file, termios.TIOCGWINSZ, 8 * b' ') # noqa
133
+ dummy, col, dummy, dummy = struct.unpack('hhhh', buf)
134
+ self._width = col - 1 # 1 for the cursor
135
+
136
+ def ims_hit(self, item):
137
+ # type: (apt_pkg.AcquireItemDesc) -> None
138
+ """Called when an item is update (e.g. not modified on the server)."""
139
+ base.AcquireProgress.ims_hit(self, item)
140
+ line = _('Hit ') + item.description
141
+ if item.owner.filesize:
142
+ line += ' [%sB]' % apt_pkg.size_to_str(item.owner.filesize)
143
+ self._write(line)
144
+
145
+ def fail(self, item):
146
+ # type: (apt_pkg.AcquireItemDesc) -> None
147
+ """Called when an item is failed."""
148
+ base.AcquireProgress.fail(self, item)
149
+ if item.owner.status == item.owner.STAT_DONE:
150
+ self._write(_("Ign ") + item.description)
151
+ else:
152
+ self._write(_("Err ") + item.description)
153
+ self._write(" %s" % item.owner.error_text)
154
+
155
+ def fetch(self, item):
156
+ # type: (apt_pkg.AcquireItemDesc) -> None
157
+ """Called when some of the item's data is fetched."""
158
+ base.AcquireProgress.fetch(self, item)
159
+ # It's complete already (e.g. Hit)
160
+ if item.owner.complete:
161
+ return
162
+ item.owner.id = self._id
163
+ self._id += 1
164
+ line = _("Get:") + "%s %s" % (item.owner.id, item.description)
165
+ if item.owner.filesize:
166
+ line += (" [%sB]" % apt_pkg.size_to_str(item.owner.filesize))
167
+
168
+ self._write(line)
169
+
170
+ def pulse(self, owner):
171
+ # type: (apt_pkg.Acquire) -> bool
172
+ """Periodically invoked while the Acquire process is underway.
173
+
174
+ Return False if the user asked to cancel the whole Acquire process."""
175
+ base.AcquireProgress.pulse(self, owner)
176
+ # only show progress on a tty to not clutter log files etc
177
+ if (hasattr(self._file, "fileno") and
178
+ not os.isatty(self._file.fileno())):
179
+ return True
180
+
181
+ # calculate progress
182
+ percent = (((self.current_bytes + self.current_items) * 100.0) /
183
+ float(self.total_bytes + self.total_items))
184
+
185
+ shown = False
186
+ tval = '%i%%' % percent
187
+ end = ""
188
+ if self.current_cps:
189
+ eta = int(float(self.total_bytes - self.current_bytes) /
190
+ self.current_cps)
191
+ end = " %sB/s %s" % (apt_pkg.size_to_str(self.current_cps),
192
+ apt_pkg.time_to_str(eta))
193
+
194
+ for worker in owner.workers:
195
+ val = ''
196
+ if not worker.current_item:
197
+ if worker.status:
198
+ val = ' [%s]' % worker.status
199
+ if len(tval) + len(val) + len(end) >= self._width:
200
+ break
201
+ tval += val
202
+ shown = True
203
+ continue
204
+ shown = True
205
+
206
+ if worker.current_item.owner.id:
207
+ val += " [%i %s" % (worker.current_item.owner.id,
208
+ worker.current_item.shortdesc)
209
+ else:
210
+ val += ' [%s' % worker.current_item.description
211
+ if worker.current_item.owner.active_subprocess:
212
+ val += ' %s' % worker.current_item.owner.active_subprocess
213
+
214
+ val += ' %sB' % apt_pkg.size_to_str(worker.current_size)
215
+
216
+ # Add the total size and percent
217
+ if worker.total_size and not worker.current_item.owner.complete:
218
+ val += "/%sB %i%%" % (
219
+ apt_pkg.size_to_str(worker.total_size),
220
+ worker.current_size * 100.0 / worker.total_size)
221
+
222
+ val += ']'
223
+
224
+ if len(tval) + len(val) + len(end) >= self._width:
225
+ # Display as many items as screen width
226
+ break
227
+ else:
228
+ tval += val
229
+
230
+ if not shown:
231
+ tval += _(" [Working]")
232
+
233
+ if self.current_cps:
234
+ tval += (self._width - len(end) - len(tval)) * ' ' + end
235
+
236
+ self._write(tval, False)
237
+ return True
238
+
239
+ def media_change(self, medium, drive):
240
+ # type: (str, str) -> bool
241
+ """Prompt the user to change the inserted removable media."""
242
+ base.AcquireProgress.media_change(self, medium, drive)
243
+ self._write(_("Media change: please insert the disc labeled\n"
244
+ " '%s'\n"
245
+ "in the drive '%s' and press enter\n") % (medium, drive))
246
+ return input() not in ('c', 'C')
247
+
248
+ def stop(self):
249
+ # type: () -> None
250
+ """Invoked when the Acquire process stops running."""
251
+ base.AcquireProgress.stop(self)
252
+ # Trick for getting a translation from apt
253
+ self._write((_("Fetched %sB in %s (%sB/s)\n") % (
254
+ apt_pkg.size_to_str(self.fetched_bytes),
255
+ apt_pkg.time_to_str(self.elapsed_time),
256
+ apt_pkg.size_to_str(self.current_cps))).rstrip("\n"))
257
+
258
+ # Delete the signal again.
259
+ import signal
260
+ signal.signal(signal.SIGWINCH, self._signal)
261
+
262
+
263
+ class CdromProgress(base.CdromProgress, TextProgress):
264
+ """Text CD-ROM progress."""
265
+
266
+ def ask_cdrom_name(self):
267
+ # type: () -> Optional[str]
268
+ """Ask the user to provide a name for the disc."""
269
+ base.CdromProgress.ask_cdrom_name(self)
270
+ self._write(_("Please provide a name for this medium, such as "
271
+ "'Debian 2.1r1 Disk 1'"), False)
272
+ try:
273
+ return str(input(":"))
274
+ except KeyboardInterrupt:
275
+ return None
276
+
277
+ def update(self, text, current):
278
+ # type: (str, int) -> None
279
+ """Set the current progress."""
280
+ base.CdromProgress.update(self, text, current)
281
+ if text:
282
+ self._write(text, False)
283
+
284
+ def change_cdrom(self):
285
+ # type: () -> bool
286
+ """Ask the user to change the CD-ROM."""
287
+ base.CdromProgress.change_cdrom(self)
288
+ self._write(_("Please insert an installation medium and press enter"),
289
+ False)
290
+ try:
291
+ return bool(input() == '')
292
+ except KeyboardInterrupt:
293
+ return False
build/lib.linux-x86_64-cpython-310/apt/py.typed ADDED
File without changes
build/lib.linux-x86_64-cpython-310/apt/utils.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2009 Canonical
2
+ #
3
+ # Authors:
4
+ # Michael Vogt
5
+ #
6
+ # This program is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU General Public License as
8
+ # published by the Free Software Foundation; either version 2 of the
9
+ # License, or (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but WITHOUT
12
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
+ # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
+ # details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along with
17
+ # this program; if not, write to the Free Software Foundation, Inc.,
18
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ from __future__ import print_function
20
+
21
+ import datetime
22
+ import os
23
+
24
+ from typing import Optional, Tuple
25
+
26
+ import apt
27
+ import apt_pkg
28
+
29
+
30
+ def get_maintenance_end_date(release_date, m_months):
31
+ # type: (datetime.datetime, int) -> Tuple[int, int]
32
+ """
33
+ get the (year, month) tuple when the maintenance for the distribution
34
+ ends. Needs the data of the release and the number of months that
35
+ its is supported as input
36
+ """
37
+ # calc end date
38
+ years = m_months // 12
39
+ months = m_months % 12
40
+ support_end_year = (release_date.year + years +
41
+ (release_date.month + months) // 12)
42
+ support_end_month = (release_date.month + months) % 12
43
+ # special case: this happens when e.g. doing 2010-06 + 18 months
44
+ if support_end_month == 0:
45
+ support_end_month = 12
46
+ support_end_year -= 1
47
+ return (support_end_year, support_end_month)
48
+
49
+
50
+ def get_release_date_from_release_file(path):
51
+ # type: (str) -> Optional[int]
52
+ """
53
+ return the release date as time_t for the given release file
54
+ """
55
+ if not path or not os.path.exists(path):
56
+ return None
57
+
58
+ with os.fdopen(apt_pkg.open_maybe_clear_signed_file(path)) as data:
59
+ tag = apt_pkg.TagFile(data)
60
+ section = next(tag)
61
+ if "Date" not in section:
62
+ return None
63
+ date = section["Date"]
64
+ return apt_pkg.str_to_time(date)
65
+
66
+
67
+ def get_release_filename_for_pkg(cache, pkgname, label, release):
68
+ # type: (apt.Cache, str, str, str) -> Optional[str]
69
+ " get the release file that provides this pkg "
70
+ if pkgname not in cache:
71
+ return None
72
+ pkg = cache[pkgname]
73
+ ver = None
74
+ # look for the version that comes from the repos with
75
+ # the given label and origin
76
+ for aver in pkg._pkg.version_list:
77
+ if aver is None or aver.file_list is None:
78
+ continue
79
+ for ver_file, _index in aver.file_list:
80
+ # print verFile
81
+ if (ver_file.origin == label and
82
+ ver_file.label == label and
83
+ ver_file.archive == release):
84
+ ver = aver
85
+ if not ver:
86
+ return None
87
+ indexfile = cache._list.find_index(ver.file_list[0][0])
88
+ for metaindex in cache._list.list:
89
+ for m in metaindex.index_files:
90
+ if (indexfile and
91
+ indexfile.describe == m.describe and
92
+ indexfile.is_trusted):
93
+ dirname = apt_pkg.config.find_dir("Dir::State::lists")
94
+ for relfile in ['InRelease', 'Release']:
95
+ name = (apt_pkg.uri_to_filename(metaindex.uri) +
96
+ "dists_%s_%s" % (metaindex.dist, relfile))
97
+ if os.path.exists(dirname + name):
98
+ return dirname + name
99
+ return None
build/lib.linux-x86_64-cpython-310/apt_inst.cpython-310-x86_64-linux-gnu.so ADDED
Binary file (570 kB). View file
 
build/lib.linux-x86_64-cpython-310/apt_pkg.cpython-310-x86_64-linux-gnu.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e286d7b1d0eef9ff2bad33d6c7344a1f37178a8acaf77afeeccc60ab23cb5496
3
+ size 3906184
build/lib.linux-x86_64-cpython-310/aptsources/__init__.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import print_function
2
+
3
+ import apt_pkg
4
+
5
+
6
+ # init the package system, but do not re-initialize config
7
+ if "APT" not in apt_pkg.config:
8
+ apt_pkg.init_config()
9
+ apt_pkg.init_system()
build/lib.linux-x86_64-cpython-310/aptsources/distinfo.py ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # distinfo.py - provide meta information for distro repositories
2
+ #
3
+ # Copyright (c) 2005 Gustavo Noronha Silva <[email protected]>
4
+ # Copyright (c) 2006-2007 Sebastian Heinlein <[email protected]>
5
+ #
6
+ # Authors: Gustavo Noronha Silva <[email protected]>
7
+ # Sebastian Heinlein <[email protected]>
8
+ #
9
+ # This program is free software; you can redistribute it and/or
10
+ # modify it under the terms of the GNU General Public License as
11
+ # published by the Free Software Foundation; either version 2 of the
12
+ # License, or (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program; if not, write to the Free Software
21
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22
+ # USA
23
+
24
+ from __future__ import print_function
25
+
26
+ import csv
27
+ import errno
28
+ import logging
29
+ import os
30
+ from subprocess import Popen, PIPE
31
+ import re
32
+
33
+ import apt_pkg
34
+
35
+ from apt_pkg import gettext as _
36
+
37
+
38
+ def _expand_template(template, csv_path):
39
+ """Expand the given template.
40
+
41
+ A template file consists of a header, followed by paragraphs
42
+ of templated suites, followed by a footer. A templated suite
43
+ is any paragraph where the Suite field contains {.
44
+
45
+ This function expands all templated suites using the information
46
+ found in the CSV file supplied by distro-info-data.
47
+
48
+ It yields lines of template info.
49
+ """
50
+
51
+ known_suites = set()
52
+
53
+ # Copy out any header, and gather all hardcoded suites
54
+ with apt_pkg.TagFile(template) as tmpl:
55
+ for section in tmpl:
56
+ if "X-Exclude-Suites" in section:
57
+ known_suites.update(section["X-Exclude-Suites"].split(", "))
58
+ if "Suite" in section:
59
+ if "{" in section["Suite"]:
60
+ break
61
+
62
+ known_suites.add(section["Suite"])
63
+
64
+ yield from str(section).splitlines()
65
+ else:
66
+ # We did not break, so we did copy all of them
67
+ return
68
+
69
+ for section in tmpl:
70
+ if "Suite" in section:
71
+ known_suites.add(section["Suite"])
72
+
73
+ with open(csv_path) as csv_object:
74
+ releases = reversed(list(csv.DictReader(csv_object)))
75
+
76
+ # Perform template substitution on the middle of the list
77
+ for rel in releases:
78
+ if rel["series"] in known_suites:
79
+ continue
80
+ yield ""
81
+ rel["version"] = rel["version"].replace(" LTS", "")
82
+ with apt_pkg.TagFile(template) as tmpl:
83
+ for section in tmpl:
84
+ # Only work on template sections, this skips head and tails
85
+ if "Suite" not in section or "{" not in section["Suite"]:
86
+ continue
87
+ if "X-Version" in section:
88
+ # Version requirements. Maybe should be made nicer
89
+ ver = rel["version"]
90
+ if any(
91
+ (field.startswith("le") and
92
+ apt_pkg.version_compare(field[3:], ver) < 0) or
93
+ (field.startswith("ge") and
94
+ apt_pkg.version_compare(field[3:], ver) > 0)
95
+ for field in section["X-Version"].split(", ")):
96
+ continue
97
+
98
+ for line in str(section).format(**rel).splitlines():
99
+ if line.startswith("X-Version"):
100
+ continue
101
+ yield line
102
+
103
+ # Copy out remaining suites
104
+ with apt_pkg.TagFile(template) as tmpl:
105
+ # Skip the head again, we don't want to copy it twice
106
+ for section in tmpl:
107
+ if "Suite" in section and "{" in section["Suite"]:
108
+ break
109
+
110
+ for section in tmpl:
111
+ # Ignore any template parts and copy the rest out,
112
+ # this is the inverse of the template substitution loop
113
+ if "Suite" in section and "{" in section["Suite"]:
114
+ continue
115
+
116
+ yield from str(section).splitlines()
117
+
118
+
119
+ class Template(object):
120
+
121
+ def __init__(self):
122
+ self.name = None
123
+ self.child = False
124
+ self.parents = [] # ref to parent template(s)
125
+ self.match_name = None
126
+ self.description = None
127
+ self.base_uri = None
128
+ self.type = None
129
+ self.components = []
130
+ self.children = []
131
+ self.match_uri = None
132
+ self.mirror_set = {}
133
+ self.distribution = None
134
+ self.available = True
135
+ self.official = True
136
+
137
+ def has_component(self, comp):
138
+ ''' Check if the distribution provides the given component '''
139
+ return comp in (c.name for c in self.components)
140
+
141
+ def is_mirror(self, url):
142
+ ''' Check if a given url of a repository is a valid mirror '''
143
+ proto, hostname, dir = split_url(url)
144
+ if hostname in self.mirror_set:
145
+ return self.mirror_set[hostname].has_repository(proto, dir)
146
+ else:
147
+ return False
148
+
149
+
150
+ class Component(object):
151
+
152
+ def __init__(self, name, desc=None, long_desc=None, parent_component=None):
153
+ self.name = name
154
+ self.description = desc
155
+ self.description_long = long_desc
156
+ self.parent_component = parent_component
157
+
158
+ def get_parent_component(self):
159
+ return self.parent_component
160
+
161
+ def set_parent_component(self, parent):
162
+ self.parent_component = parent
163
+
164
+ def get_description(self):
165
+ if self.description_long is not None:
166
+ return self.description_long
167
+ elif self.description is not None:
168
+ return self.description
169
+ else:
170
+ return None
171
+
172
+ def set_description(self, desc):
173
+ self.description = desc
174
+
175
+ def set_description_long(self, desc):
176
+ self.description_long = desc
177
+
178
+ def get_description_long(self):
179
+ return self.description_long
180
+
181
+
182
+ class Mirror(object):
183
+ ''' Storage for mirror related information '''
184
+
185
+ def __init__(self, proto, hostname, dir, location=None):
186
+ self.hostname = hostname
187
+ self.repositories = []
188
+ self.add_repository(proto, dir)
189
+ self.location = location
190
+
191
+ def add_repository(self, proto, dir):
192
+ self.repositories.append(Repository(proto, dir))
193
+
194
+ def get_repositories_for_proto(self, proto):
195
+ return [r for r in self.repositories if r.proto == proto]
196
+
197
+ def has_repository(self, proto, dir):
198
+ if dir is None:
199
+ return False
200
+ for r in self.repositories:
201
+ if r.proto == proto and dir in r.dir:
202
+ return True
203
+ return False
204
+
205
+ def get_repo_urls(self):
206
+ return [r.get_url(self.hostname) for r in self.repositories]
207
+
208
+ def get_location(self):
209
+ return self.location
210
+
211
+ def set_location(self, location):
212
+ self.location = location
213
+
214
+
215
+ class Repository(object):
216
+
217
+ def __init__(self, proto, dir):
218
+ self.proto = proto
219
+ self.dir = dir
220
+
221
+ def get_info(self):
222
+ return self.proto, self.dir
223
+
224
+ def get_url(self, hostname):
225
+ return "%s://%s/%s" % (self.proto, hostname, self.dir)
226
+
227
+
228
+ def split_url(url):
229
+ ''' split a given URL into the protocoll, the hostname and the dir part '''
230
+ split = re.split(":*\\/+", url, maxsplit=2)
231
+ while len(split) < 3:
232
+ split.append(None)
233
+ return split
234
+
235
+
236
+ class DistInfo(object):
237
+
238
+ def __init__(self, dist=None, base_dir="/usr/share/python-apt/templates"):
239
+ self.metarelease_uri = ''
240
+ self.templates = []
241
+ self.arch = apt_pkg.config.find("APT::Architecture")
242
+
243
+ location = None
244
+ match_loc = re.compile(r"^#LOC:(.+)$")
245
+ match_mirror_line = re.compile(
246
+ r"^(#LOC:.+)|(((http)|(ftp)|(rsync)|(file)|(mirror)|(https))://"
247
+ r"[A-Za-z0-9/\.:\-_@]+)$")
248
+ #match_mirror_line = re.compile(r".+")
249
+
250
+ if not dist:
251
+ try:
252
+ dist = Popen(["lsb_release", "-i", "-s"],
253
+ universal_newlines=True,
254
+ stdout=PIPE).communicate()[0].strip()
255
+ except (OSError, IOError) as exc:
256
+ if exc.errno != errno.ENOENT:
257
+ logging.warning(
258
+ 'lsb_release failed, using defaults:' % exc)
259
+ dist = "Debian"
260
+
261
+ self.dist = dist
262
+
263
+ map_mirror_sets = {}
264
+
265
+ dist_fname = "%s/%s.info" % (base_dir, dist)
266
+ csv_fname = "/usr/share/distro-info/{}.csv".format(dist.lower())
267
+
268
+ template = None
269
+ component = None
270
+ for line in _expand_template(dist_fname, csv_fname):
271
+ tokens = line.split(':', 1)
272
+ if len(tokens) < 2:
273
+ continue
274
+ field = tokens[0].strip()
275
+ value = tokens[1].strip()
276
+ if field == 'ChangelogURI':
277
+ self.changelogs_uri = _(value)
278
+ elif field == 'MetaReleaseURI':
279
+ self.metarelease_uri = value
280
+ elif field == 'Suite':
281
+ self.finish_template(template, component)
282
+ component = None
283
+ template = Template()
284
+ template.name = value
285
+ template.distribution = dist
286
+ template.match_name = "^%s$" % value
287
+ elif field == 'MatchName':
288
+ template.match_name = value
289
+ elif field == 'ParentSuite':
290
+ template.child = True
291
+ for nanny in self.templates:
292
+ # look for parent and add back ref to it
293
+ if nanny.name == value:
294
+ template.parents.append(nanny)
295
+ nanny.children.append(template)
296
+ elif field == 'Available':
297
+ template.available = apt_pkg.string_to_bool(value)
298
+ elif field == 'Official':
299
+ template.official = apt_pkg.string_to_bool(value)
300
+ elif field == 'RepositoryType':
301
+ template.type = value
302
+ elif field == 'BaseURI' and not template.base_uri:
303
+ template.base_uri = value
304
+ elif field == 'BaseURI-%s' % self.arch:
305
+ template.base_uri = value
306
+ elif field == 'MatchURI' and not template.match_uri:
307
+ template.match_uri = value
308
+ elif field == 'MatchURI-%s' % self.arch:
309
+ template.match_uri = value
310
+ elif (field == 'MirrorsFile' or
311
+ field == 'MirrorsFile-%s' % self.arch):
312
+ # Make the path absolute.
313
+ value = os.path.isabs(value) and value or \
314
+ os.path.abspath(os.path.join(base_dir, value))
315
+ if value not in map_mirror_sets:
316
+ mirror_set = {}
317
+ try:
318
+ with open(value) as value_f:
319
+ mirror_data = list(filter(
320
+ match_mirror_line.match,
321
+ [x.strip() for x in value_f]))
322
+ except Exception:
323
+ print("WARNING: Failed to read mirror file")
324
+ mirror_data = []
325
+ for line in mirror_data:
326
+ if line.startswith("#LOC:"):
327
+ location = match_loc.sub(r"\1", line)
328
+ continue
329
+ (proto, hostname, dir) = split_url(line)
330
+ if hostname in mirror_set:
331
+ mirror_set[hostname].add_repository(proto, dir)
332
+ else:
333
+ mirror_set[hostname] = Mirror(
334
+ proto, hostname, dir, location)
335
+ map_mirror_sets[value] = mirror_set
336
+ template.mirror_set = map_mirror_sets[value]
337
+ elif field == 'Description':
338
+ template.description = _(value)
339
+ elif field == 'Component':
340
+ if (component and not
341
+ template.has_component(component.name)):
342
+ template.components.append(component)
343
+ component = Component(value)
344
+ elif field == 'CompDescription':
345
+ component.set_description(_(value))
346
+ elif field == 'CompDescriptionLong':
347
+ component.set_description_long(_(value))
348
+ elif field == 'ParentComponent':
349
+ component.set_parent_component(value)
350
+ self.finish_template(template, component)
351
+ template = None
352
+ component = None
353
+
354
+ def finish_template(self, template, component):
355
+ " finish the current tempalte "
356
+ if not template:
357
+ return
358
+ # reuse some properties of the parent template
359
+ if template.match_uri is None and template.child:
360
+ for t in template.parents:
361
+ if t.match_uri:
362
+ template.match_uri = t.match_uri
363
+ break
364
+ if template.mirror_set == {} and template.child:
365
+ for t in template.parents:
366
+ if t.match_uri:
367
+ template.mirror_set = t.mirror_set
368
+ break
369
+ if component and not template.has_component(component.name):
370
+ template.components.append(component)
371
+ component = None
372
+ # the official attribute is inherited
373
+ for t in template.parents:
374
+ template.official = t.official
375
+ self.templates.append(template)
376
+
377
+
378
+ if __name__ == "__main__":
379
+ d = DistInfo("Ubuntu", "/usr/share/python-apt/templates")
380
+ logging.info(d.changelogs_uri)
381
+ for template in d.templates:
382
+ logging.info("\nSuite: %s" % template.name)
383
+ logging.info("Desc: %s" % template.description)
384
+ logging.info("BaseURI: %s" % template.base_uri)
385
+ logging.info("MatchURI: %s" % template.match_uri)
386
+ if template.mirror_set != {}:
387
+ logging.info("Mirrors: %s" % list(template.mirror_set.keys()))
388
+ for comp in template.components:
389
+ logging.info(" %s -%s -%s" % (comp.name,
390
+ comp.description,
391
+ comp.description_long))
392
+ for child in template.children:
393
+ logging.info(" %s" % child.description)