Merge lp:~bjornt/landscape-client/apt-channel-api into lp:~landscape/landscape-client/trunk
- apt-channel-api
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Free Ekanayaka | ||||
Approved revision: | 384 | ||||
Merged at revision: | 366 | ||||
Proposed branch: | lp:~bjornt/landscape-client/apt-channel-api | ||||
Merge into: | lp:~landscape/landscape-client/trunk | ||||
Diff against target: |
304 lines (+194/-5) 5 files modified
landscape/lib/fs.py (+13/-0) landscape/lib/tests/test_fs.py (+23/-1) landscape/package/facade.py (+42/-0) landscape/package/tests/helpers.py (+1/-0) landscape/package/tests/test_facade.py (+115/-4) |
||||
To merge this branch: | bzr merge lp:~bjornt/landscape-client/apt-channel-api | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Free Ekanayaka (community) | Approve | ||
Alberto Donato (community) | Approve | ||
Review via email:
|
Commit message
Description of the change
Add support for adding and removing deb URLs in AptFacade.
add_channel_
add_channel() was omitted, since it's only used by SmartFacade
internally.
I filed bug 861345 for implementing add_channel_
to do some extra work to make sure apt can use the dir.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Free Ekanayaka (free.ekanayaka) wrote : | # |
Nice work, +1!
[1]
+ sources_file_path = sources_dir + "/landscape-
Please use os.path.join().
Also, wdyt of prefixing the file with an underscore to signal it's a "private" file? like "_landscape-
[2]
+ add_channel_
Please write it with epydoc syntax, "C{add_
[3]
+ self.facade.
+ "http://
It'd be nice to make the components parameter optional in add_channel_
[4]
+ If deb URLs have been added, a list of dict is returned with
+ information about the channels.
The second line is indented too much.
[5]
+ self.facade.
+ "http://
+ self.facade.
+ "http://
+ sources_list = SourcesList()
+ for entry in sources_list:
+ if "disabled" in entry.uri:
+ entry.set_
+ sources_list.save()
Doh, SourcesList() is nice and convenient, just too bad that it relies on global state! Anyway, good idea to use it, I didn't know or recall about it.
[6]
+ def reset_channels(
+ """Remove all the configured channels."""
+ sources_list = SourcesList()
+ for entry in sources_list:
+ entry.set_
The only use case for reset_channels() in the server code, canonical/
for entry in sources_list:
which I think would fit more with the "reset" term. If we need a "disable" behavior we could add a disable_channels() method.
Anyway, this is really minor, so your call.
- 385. By Björn Tillenius
-
Merge trunk.
- 386. By Björn Tillenius
-
Use os.path.join.
- 387. By Björn Tillenius
-
Use read_file().
- 388. By Björn Tillenius
-
Add append_file() and use it to get rid of with statements.
- 389. By Björn Tillenius
-
Rename .list file to make it more obvious that it's private.
- 390. By Björn Tillenius
-
Epydoc syntax.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Björn Tillenius (bjornt) wrote : | # |
On Wed, Sep 28, 2011 at 12:56:28PM -0000, Alberto Donato wrote:
> Review: Approve
>
> Nice, +1!
>
> #1:
> + sources_file_path = sources_dir + "/landscape-
>
> #2:
> + with open(path + "/Packages", "a") as packages:
>
> these should use os.path.join()
Sure.
> #3:
> + with open(list_filename, "r") as sources:
> + sources_contents = sources.read()
>
> you can use read_file() from landscape.lib.fs here (there are multiple
> tests with similar code)
Sure. I added append_file() as well, and replaced the with statements
that appended contents to files.
--
Björn Tillenius | https:/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Björn Tillenius (bjornt) wrote : | # |
On Wed, Sep 28, 2011 at 01:00:27PM -0000, Free Ekanayaka wrote:
> Review: Approve
>
> Nice work, +1!
>
> [1]
>
> + sources_file_path = sources_dir + "/landscape-
>
> Please use os.path.join().
Sure.
> Also, wdyt of prefixing the file with an underscore to signal it's a
> "private" file? like "_landscape-
> using a dot as prefix won't work because libapt ignores it).
Yes, that's a good idea.
> [2]
>
> + add_channel_
>
> Please write it with epydoc syntax, "C{add_
> the other occurrences.
Done.
> [3]
>
> + self.facade.
> + "http://
>
> It'd be nice to make the components parameter optional in
> add_channel_
> SmartFacade, but it should be compatible.
Sure, I made it optional
> [4]
>
> + If deb URLs have been added, a list of dict is returned with
> + information about the channels.
>
> The second line is indented too much.
Fixed.
> [5]
>
> + self.facade.
> + "http://
> + self.facade.
> + "http://
> + sources_list = SourcesList()
> + for entry in sources_list:
> + if "disabled" in entry.uri:
> + entry.set_
> + sources_list.save()
>
> Doh, SourcesList() is nice and convenient, just too bad that it relies on global state! Anyway, good idea to use it, I didn't know or recall about it.
>
> [6]
>
> + def reset_channels(
> + """Remove all the configured channels."""
> + sources_list = SourcesList()
> + for entry in sources_list:
> + entry.set_
>
> The only use case for reset_channels() in the server code,
> canonical/
> pre-canned hash->id maps. I think it's pretty safe to remove the entry
> entirely for that, like:
>
> for entry in sources_list:
> sources_
>
> which I think would fit more with the "reset" term. If we need a
> "disable" behavior we could add a disable_channels() method.
I agree. However, it doesn't quite work ;) I tried doing this, but for
some reason it doesn't remove the entries. So rather than spending time
on debuggin, I chose to simply disable them.
--
Björn Tillenius | https:/
Preview Diff
1 | === modified file 'landscape/lib/fs.py' | |||
2 | --- landscape/lib/fs.py 2010-04-01 08:28:22 +0000 | |||
3 | +++ landscape/lib/fs.py 2011-09-29 12:39:24 +0000 | |||
4 | @@ -14,6 +14,19 @@ | |||
5 | 14 | fd.close() | 14 | fd.close() |
6 | 15 | 15 | ||
7 | 16 | 16 | ||
8 | 17 | def append_file(path, content): | ||
9 | 18 | """Append a file with the given content. | ||
10 | 19 | |||
11 | 20 | The file is created, if it doesn't exist already. | ||
12 | 21 | |||
13 | 22 | @param path: The path to the file. | ||
14 | 23 | @param content: The content to be written in the file at the end. | ||
15 | 24 | """ | ||
16 | 25 | fd = open(path, "a") | ||
17 | 26 | fd.write(content) | ||
18 | 27 | fd.close() | ||
19 | 28 | |||
20 | 29 | |||
21 | 17 | def read_file(path, limit=None): | 30 | def read_file(path, limit=None): |
22 | 18 | """Return the content of the given file. | 31 | """Return the content of the given file. |
23 | 19 | 32 | ||
24 | 20 | 33 | ||
25 | === modified file 'landscape/lib/tests/test_fs.py' | |||
26 | --- landscape/lib/tests/test_fs.py 2011-07-05 05:09:11 +0000 | |||
27 | +++ landscape/lib/tests/test_fs.py 2011-09-29 12:39:24 +0000 | |||
28 | @@ -1,6 +1,8 @@ | |||
29 | 1 | import os | ||
30 | 2 | |||
31 | 1 | from landscape.tests.helpers import LandscapeTest | 3 | from landscape.tests.helpers import LandscapeTest |
32 | 2 | 4 | ||
34 | 3 | from landscape.lib.fs import read_file, touch_file | 5 | from landscape.lib.fs import append_file, read_file, touch_file |
35 | 4 | 6 | ||
36 | 5 | 7 | ||
37 | 6 | class ReadFileTest(LandscapeTest): | 8 | class ReadFileTest(LandscapeTest): |
38 | @@ -59,3 +61,23 @@ | |||
39 | 59 | touch_file(path) | 61 | touch_file(path) |
40 | 60 | touch_file(path) | 62 | touch_file(path) |
41 | 61 | self.assertFileContent(path, "") | 63 | self.assertFileContent(path, "") |
42 | 64 | |||
43 | 65 | |||
44 | 66 | class AppendFileTest(LandscapeTest): | ||
45 | 67 | |||
46 | 68 | def test_append_existing_file(self): | ||
47 | 69 | """ | ||
48 | 70 | The L{append_file} function appends contents to an existing file. | ||
49 | 71 | """ | ||
50 | 72 | existing_file = self.makeFile("foo bar") | ||
51 | 73 | append_file(existing_file, " baz") | ||
52 | 74 | self.assertFileContent(existing_file, "foo bar baz") | ||
53 | 75 | |||
54 | 76 | def test_append_no_file(self): | ||
55 | 77 | """ | ||
56 | 78 | The L{append_file} function creates a new file if one doesn't | ||
57 | 79 | exist already. | ||
58 | 80 | """ | ||
59 | 81 | new_file = os.path.join(self.makeDir(), "new_file") | ||
60 | 82 | append_file(new_file, "contents") | ||
61 | 83 | self.assertFileContent(new_file, "contents") | ||
62 | 62 | 84 | ||
63 | === modified file 'landscape/package/facade.py' | |||
64 | --- landscape/package/facade.py 2011-09-26 12:20:46 +0000 | |||
65 | +++ landscape/package/facade.py 2011-09-29 12:39:24 +0000 | |||
66 | @@ -1,3 +1,5 @@ | |||
67 | 1 | import os | ||
68 | 2 | |||
69 | 1 | from smart.transaction import ( | 3 | from smart.transaction import ( |
70 | 2 | Transaction, PolicyInstall, PolicyUpgrade, PolicyRemove, Failed) | 4 | Transaction, PolicyInstall, PolicyUpgrade, PolicyRemove, Failed) |
71 | 3 | from smart.const import INSTALL, REMOVE, UPGRADE, ALWAYS, NEVER | 5 | from smart.const import INSTALL, REMOVE, UPGRADE, ALWAYS, NEVER |
72 | @@ -5,7 +7,10 @@ | |||
73 | 5 | import smart | 7 | import smart |
74 | 6 | 8 | ||
75 | 7 | import apt | 9 | import apt |
76 | 10 | import apt_pkg | ||
77 | 11 | from aptsources.sourceslist import SourcesList | ||
78 | 8 | 12 | ||
79 | 13 | from landscape.lib.fs import append_file | ||
80 | 9 | from landscape.package.skeleton import build_skeleton | 14 | from landscape.package.skeleton import build_skeleton |
81 | 10 | 15 | ||
82 | 11 | 16 | ||
83 | @@ -51,6 +56,43 @@ | |||
84 | 51 | def reload_channels(self): | 56 | def reload_channels(self): |
85 | 52 | """Reload the channels and update the cache.""" | 57 | """Reload the channels and update the cache.""" |
86 | 53 | self._cache.open(None) | 58 | self._cache.open(None) |
87 | 59 | self._cache.update() | ||
88 | 60 | self._cache.open(None) | ||
89 | 61 | |||
90 | 62 | def add_channel_apt_deb(self, url, codename, components): | ||
91 | 63 | """Add a deb URL which points to a repository. | ||
92 | 64 | |||
93 | 65 | @param url: The base URL of the repository. | ||
94 | 66 | @param codename: The dist in the repository. | ||
95 | 67 | @param components: The components to be included. | ||
96 | 68 | """ | ||
97 | 69 | sources_dir = apt_pkg.config.find_dir("Dir::Etc::sourceparts") | ||
98 | 70 | sources_file_path = os.path.join( | ||
99 | 71 | sources_dir, "_landscape-internal-facade.list") | ||
100 | 72 | sources_line = "deb %s %s" % (url, codename) | ||
101 | 73 | if components: | ||
102 | 74 | sources_line += " %s" % " ".join(components) | ||
103 | 75 | sources_line += "\n" | ||
104 | 76 | append_file(sources_file_path, sources_line) | ||
105 | 77 | |||
106 | 78 | def get_channels(self): | ||
107 | 79 | """Return a list of channels configured. | ||
108 | 80 | |||
109 | 81 | A channel is a deb line in sources.list or sources.list.d. It's | ||
110 | 82 | represented by a dict with baseurl, distribution, components, | ||
111 | 83 | and type keys. | ||
112 | 84 | """ | ||
113 | 85 | sources_list = SourcesList() | ||
114 | 86 | return [{"baseurl": entry.uri, "distribution": entry.dist, | ||
115 | 87 | "components": " ".join(entry.comps), "type": entry.type} | ||
116 | 88 | for entry in sources_list if not entry.disabled] | ||
117 | 89 | |||
118 | 90 | def reset_channels(self): | ||
119 | 91 | """Remove all the configured channels.""" | ||
120 | 92 | sources_list = SourcesList() | ||
121 | 93 | for entry in sources_list: | ||
122 | 94 | entry.set_enabled(False) | ||
123 | 95 | sources_list.save() | ||
124 | 54 | 96 | ||
125 | 55 | 97 | ||
126 | 56 | class SmartFacade(object): | 98 | class SmartFacade(object): |
127 | 57 | 99 | ||
128 | === modified file 'landscape/package/tests/helpers.py' | |||
129 | --- landscape/package/tests/helpers.py 2011-09-29 10:08:01 +0000 | |||
130 | +++ landscape/package/tests/helpers.py 2011-09-29 12:39:24 +0000 | |||
131 | @@ -16,6 +16,7 @@ | |||
132 | 16 | # auto-create them, which causing the paths to be printed to stdout. | 16 | # auto-create them, which causing the paths to be printed to stdout. |
133 | 17 | test_case.dpkg_dir = self._create_sub_dir(test_case, "var/lib/dpkg") | 17 | test_case.dpkg_dir = self._create_sub_dir(test_case, "var/lib/dpkg") |
134 | 18 | self._create_sub_dir(test_case, "etc/apt") | 18 | self._create_sub_dir(test_case, "etc/apt") |
135 | 19 | self._create_sub_dir(test_case, "etc/apt/sources.list.d") | ||
136 | 19 | self._create_sub_dir(test_case, "var/cache/apt/archives/partial") | 20 | self._create_sub_dir(test_case, "var/cache/apt/archives/partial") |
137 | 20 | self._create_sub_dir(test_case, "var/lib/apt/lists/partial") | 21 | self._create_sub_dir(test_case, "var/lib/apt/lists/partial") |
138 | 21 | test_case.dpkg_status = os.path.join(test_case.dpkg_dir, "status") | 22 | test_case.dpkg_status = os.path.join(test_case.dpkg_dir, "status") |
139 | 22 | 23 | ||
140 | === modified file 'landscape/package/tests/test_facade.py' | |||
141 | --- landscape/package/tests/test_facade.py 2011-09-29 08:20:24 +0000 | |||
142 | +++ landscape/package/tests/test_facade.py 2011-09-29 12:39:24 +0000 | |||
143 | @@ -8,12 +8,15 @@ | |||
144 | 8 | from smart.cache import Provides | 8 | from smart.cache import Provides |
145 | 9 | from smart.const import NEVER, ALWAYS | 9 | from smart.const import NEVER, ALWAYS |
146 | 10 | 10 | ||
147 | 11 | from aptsources.sourceslist import SourcesList | ||
148 | 12 | |||
149 | 11 | from twisted.internet import reactor | 13 | from twisted.internet import reactor |
150 | 12 | from twisted.internet.defer import Deferred | 14 | from twisted.internet.defer import Deferred |
151 | 13 | from twisted.internet.utils import getProcessOutputAndValue | 15 | from twisted.internet.utils import getProcessOutputAndValue |
152 | 14 | 16 | ||
153 | 15 | import smart | 17 | import smart |
154 | 16 | 18 | ||
155 | 19 | from landscape.lib.fs import append_file, read_file | ||
156 | 17 | from landscape.package.facade import ( | 20 | from landscape.package.facade import ( |
157 | 18 | TransactionError, DependencyError, ChannelError, SmartError) | 21 | TransactionError, DependencyError, ChannelError, SmartError) |
158 | 19 | 22 | ||
159 | @@ -30,8 +33,7 @@ | |||
160 | 30 | 33 | ||
161 | 31 | def _add_system_package(self, name): | 34 | def _add_system_package(self, name): |
162 | 32 | """Add a package to the dpkg status file.""" | 35 | """Add a package to the dpkg status file.""" |
165 | 33 | with open(self.dpkg_status, "a") as status_file: | 36 | append_file(self.dpkg_status, textwrap.dedent("""\ |
164 | 34 | status_file.write(textwrap.dedent("""\ | ||
166 | 35 | Package: %s | 37 | Package: %s |
167 | 36 | Status: install ok installed | 38 | Status: install ok installed |
168 | 37 | Priority: optional | 39 | Priority: optional |
169 | @@ -46,10 +48,22 @@ | |||
170 | 46 | 48 | ||
171 | 47 | """ % name)) | 49 | """ % name)) |
172 | 48 | 50 | ||
173 | 51 | def _add_package_to_deb_dir(self, path, name, version="1.0"): | ||
174 | 52 | """Add fake package information to a directory. | ||
175 | 53 | |||
176 | 54 | There will only be basic information about the package | ||
177 | 55 | available, so that get_packages() have something to return. | ||
178 | 56 | There won't be an actual package in the dir. | ||
179 | 57 | """ | ||
180 | 58 | package_stanza = "Package: %(name)s\nVersion: %(version)s\n\n" | ||
181 | 59 | append_file( | ||
182 | 60 | os.path.join(path, "Packages"), | ||
183 | 61 | package_stanza % {"name": name, "version": version}) | ||
184 | 62 | |||
185 | 49 | def test_no_system_packages(self): | 63 | def test_no_system_packages(self): |
186 | 50 | """ | 64 | """ |
187 | 51 | If the dpkg status file is empty, not packages are reported by | 65 | If the dpkg status file is empty, not packages are reported by |
189 | 52 | get_packages(). | 66 | C{get_packages()}. |
190 | 53 | """ | 67 | """ |
191 | 54 | self.facade.reload_channels() | 68 | self.facade.reload_channels() |
192 | 55 | self.assertEqual([], self.facade.get_packages()) | 69 | self.assertEqual([], self.facade.get_packages()) |
193 | @@ -57,7 +71,7 @@ | |||
194 | 57 | def test_get_system_packages(self): | 71 | def test_get_system_packages(self): |
195 | 58 | """ | 72 | """ |
196 | 59 | If the dpkg status file contains some packages, those packages | 73 | If the dpkg status file contains some packages, those packages |
198 | 60 | are reported by get_packages(). | 74 | are reported by C{get_packages()}. |
199 | 61 | """ | 75 | """ |
200 | 62 | self._add_system_package("foo") | 76 | self._add_system_package("foo") |
201 | 63 | self._add_system_package("bar") | 77 | self._add_system_package("bar") |
202 | @@ -66,6 +80,103 @@ | |||
203 | 66 | ["bar", "foo"], | 80 | ["bar", "foo"], |
204 | 67 | sorted(package.name for package in self.facade.get_packages())) | 81 | sorted(package.name for package in self.facade.get_packages())) |
205 | 68 | 82 | ||
206 | 83 | def test_add_channel_apt_deb_without_components(self): | ||
207 | 84 | """ | ||
208 | 85 | C{add_channel_apt_deb()} adds a new deb URL to a file in | ||
209 | 86 | sources.list.d. | ||
210 | 87 | |||
211 | 88 | If no components are given, nothing is written after the dist. | ||
212 | 89 | """ | ||
213 | 90 | self.facade.add_channel_apt_deb( | ||
214 | 91 | "http://example.com/ubuntu", "lucid", None) | ||
215 | 92 | list_filename = ( | ||
216 | 93 | self.apt_root + | ||
217 | 94 | "/etc/apt/sources.list.d/_landscape-internal-facade.list") | ||
218 | 95 | sources_contents = read_file(list_filename) | ||
219 | 96 | self.assertEqual( | ||
220 | 97 | "deb http://example.com/ubuntu lucid\n", | ||
221 | 98 | sources_contents) | ||
222 | 99 | |||
223 | 100 | def test_add_channel_apt_deb_with_components(self): | ||
224 | 101 | """ | ||
225 | 102 | C{add_channel_apt_deb()} adds a new deb URL to a file in | ||
226 | 103 | sources.list.d. | ||
227 | 104 | |||
228 | 105 | If components are given, they are included after the dist. | ||
229 | 106 | """ | ||
230 | 107 | self.facade.add_channel_apt_deb( | ||
231 | 108 | "http://example.com/ubuntu", "lucid", ["main", "restricted"]) | ||
232 | 109 | list_filename = ( | ||
233 | 110 | self.apt_root + | ||
234 | 111 | "/etc/apt/sources.list.d/_landscape-internal-facade.list") | ||
235 | 112 | sources_contents = read_file(list_filename) | ||
236 | 113 | self.assertEqual( | ||
237 | 114 | "deb http://example.com/ubuntu lucid main restricted\n", | ||
238 | 115 | sources_contents) | ||
239 | 116 | |||
240 | 117 | def test_get_channels_with_no_channels(self): | ||
241 | 118 | """ | ||
242 | 119 | If no deb URLs have been added, C{get_channels()} returns an empty list. | ||
243 | 120 | """ | ||
244 | 121 | self.assertEqual([], self.facade.get_channels()) | ||
245 | 122 | |||
246 | 123 | def test_get_channels_with_channels(self): | ||
247 | 124 | """ | ||
248 | 125 | If deb URLs have been added, a list of dict is returned with | ||
249 | 126 | information about the channels. | ||
250 | 127 | """ | ||
251 | 128 | self.facade.add_channel_apt_deb( | ||
252 | 129 | "http://example.com/ubuntu", "lucid", ["main", "restricted"]) | ||
253 | 130 | self.assertEqual([{"baseurl": "http://example.com/ubuntu", | ||
254 | 131 | "distribution": "lucid", | ||
255 | 132 | "components": "main restricted", | ||
256 | 133 | "type": "deb"}], | ||
257 | 134 | self.facade.get_channels()) | ||
258 | 135 | |||
259 | 136 | def test_get_channels_with_disabled_channels(self): | ||
260 | 137 | """ | ||
261 | 138 | C{get_channels()} doesn't return disabled deb URLs. | ||
262 | 139 | """ | ||
263 | 140 | self.facade.add_channel_apt_deb( | ||
264 | 141 | "http://enabled.example.com/ubuntu", "lucid", ["main"]) | ||
265 | 142 | self.facade.add_channel_apt_deb( | ||
266 | 143 | "http://disabled.example.com/ubuntu", "lucid", ["main"]) | ||
267 | 144 | sources_list = SourcesList() | ||
268 | 145 | for entry in sources_list: | ||
269 | 146 | if "disabled" in entry.uri: | ||
270 | 147 | entry.set_enabled(False) | ||
271 | 148 | sources_list.save() | ||
272 | 149 | self.assertEqual([{"baseurl": "http://enabled.example.com/ubuntu", | ||
273 | 150 | "distribution": "lucid", | ||
274 | 151 | "components": "main", | ||
275 | 152 | "type": "deb"}], | ||
276 | 153 | self.facade.get_channels()) | ||
277 | 154 | |||
278 | 155 | def test_reset_channels(self): | ||
279 | 156 | """ | ||
280 | 157 | C{reset_channels()} disables all the configured deb URLs. | ||
281 | 158 | """ | ||
282 | 159 | self.facade.add_channel_apt_deb( | ||
283 | 160 | "http://1.example.com/ubuntu", "lucid", ["main", "restricted"]) | ||
284 | 161 | self.facade.add_channel_apt_deb( | ||
285 | 162 | "http://2.example.com/ubuntu", "lucid", ["main", "restricted"]) | ||
286 | 163 | self.facade.reset_channels() | ||
287 | 164 | self.assertEqual([], self.facade.get_channels()) | ||
288 | 165 | |||
289 | 166 | def test_reload_includes_added_channels(self): | ||
290 | 167 | """ | ||
291 | 168 | When reloading the channels, C{get_packages()} returns the packages | ||
292 | 169 | in the channel. | ||
293 | 170 | """ | ||
294 | 171 | deb_dir = self.makeDir() | ||
295 | 172 | self._add_package_to_deb_dir(deb_dir, "foo") | ||
296 | 173 | self._add_package_to_deb_dir(deb_dir, "bar") | ||
297 | 174 | self.facade.add_channel_apt_deb("file://%s" % deb_dir, "./", None) | ||
298 | 175 | self.facade.reload_channels() | ||
299 | 176 | self.assertEqual( | ||
300 | 177 | ["bar", "foo"], | ||
301 | 178 | sorted(package.name for package in self.facade.get_packages())) | ||
302 | 179 | |||
303 | 69 | 180 | ||
304 | 70 | class SmartFacadeTest(LandscapeTest): | 181 | class SmartFacadeTest(LandscapeTest): |
305 | 71 | 182 |
Nice, +1!
#1: internal- facade. list"
+ sources_file_path = sources_dir + "/landscape-
#2:
+ with open(path + "/Packages", "a") as packages:
these should use os.path.join()
#3:
+ with open(list_filename, "r") as sources:
+ sources_contents = sources.read()
you can use read_file() from landscape.lib.fs here (there are multiple tests with similar code)