Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +1 -0
- .gitlab-ci.yml +35 -0
- .travis.yml +14 -0
- AUTHORS +7 -0
- COPYING.GPL +340 -0
- Dockerfile +11 -0
- README.md +49 -17
- TODO +4 -0
- apt/__init__.py +38 -0
- apt/auth.py +309 -0
- apt/cache.py +1038 -0
- apt/cdrom.py +92 -0
- apt/debfile.py +868 -0
- apt/package.py +1604 -0
- apt/progress/__init__.py +31 -0
- apt/progress/base.py +354 -0
- apt/progress/text.py +293 -0
- apt/py.typed +0 -0
- apt/utils.py +99 -0
- aptsources/__init__.py +9 -0
- aptsources/distinfo.py +393 -0
- aptsources/distro.py +613 -0
- aptsources/sourceslist.py +516 -0
- build/data/templates/Blankon.info +370 -0
- build/data/templates/Blankon.mirrors +17 -0
- build/data/templates/Debian.info +95 -0
- build/data/templates/Debian.mirrors +371 -0
- build/data/templates/Kali.info +32 -0
- build/data/templates/Kali.mirrors +2 -0
- build/data/templates/Tanglu.info +42 -0
- build/data/templates/Tanglu.mirrors +3 -0
- build/data/templates/Ubuntu.info +223 -0
- build/data/templates/Ubuntu.mirrors +659 -0
- build/data/templates/gNewSense.info +55 -0
- build/data/templates/gNewSense.mirrors +489 -0
- build/lib.linux-x86_64-cpython-310/apt/__init__.py +38 -0
- build/lib.linux-x86_64-cpython-310/apt/auth.py +309 -0
- build/lib.linux-x86_64-cpython-310/apt/cache.py +1038 -0
- build/lib.linux-x86_64-cpython-310/apt/cdrom.py +92 -0
- build/lib.linux-x86_64-cpython-310/apt/debfile.py +868 -0
- build/lib.linux-x86_64-cpython-310/apt/package.py +1604 -0
- build/lib.linux-x86_64-cpython-310/apt/progress/__init__.py +31 -0
- build/lib.linux-x86_64-cpython-310/apt/progress/base.py +354 -0
- build/lib.linux-x86_64-cpython-310/apt/progress/text.py +293 -0
- build/lib.linux-x86_64-cpython-310/apt/py.typed +0 -0
- build/lib.linux-x86_64-cpython-310/apt/utils.py +99 -0
- build/lib.linux-x86_64-cpython-310/apt_inst.cpython-310-x86_64-linux-gnu.so +0 -0
- build/lib.linux-x86_64-cpython-310/apt_pkg.cpython-310-x86_64-linux-gnu.so +3 -0
- build/lib.linux-x86_64-cpython-310/aptsources/__init__.py +9 -0
- 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 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
-
|
8 |
-
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
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)
|