3 from namespaces
import *
6 from logging
import debug
, info
9 #from logging import getLogger, DEBUG
10 #getLogger().setLevel(DEBUG)
12 _interfaces
= {} # URI -> Interface
16 return time
.strftime('%Y-%m-%d %H:%M:%S UTC', t
)
19 __slots__
= ['root', 'implementation', 'watchers',
20 'help_with_testing', 'network_use', 'updates']
22 def __init__(self
, root
):
23 assert isinstance(root
, (str, unicode))
25 self
.implementation
= {} # Interface -> Implementation
27 self
.help_with_testing
= False
28 self
.network_use
= network_full
31 path
= basedir
.load_first_config(config_site
, config_prog
, 'global')
33 config
= ConfigParser
.ConfigParser()
35 self
.help_with_testing
= config
.getboolean('global',
37 self
.network_use
= config
.get('global', 'network_use')
38 assert self
.network_use
in network_levels
40 def save_config(self
):
41 config
= ConfigParser
.ConfigParser()
42 config
.add_section('global')
44 config
.set('global', 'help_with_testing', self
.help_with_testing
)
45 config
.set('global', 'network_use', self
.network_use
)
47 path
= basedir
.save_config_path(config_site
, config_prog
)
48 path
= os
.path
.join(path
, 'global')
49 config
.write(file(path
+ '.new', 'w'))
50 os
.rename(path
+ '.new', path
)
52 def recalculate(self
):
53 self
.implementation
= {}
56 impl
= self
.get_best_implementation(iface
)
58 self
.implementation
[iface
] = impl
59 for d
in impl
.dependencies
.values():
60 process(self
.get_interface(d
.interface
))
61 process(self
.get_interface(self
.root
))
62 for w
in self
.watchers
: w()
64 def get_best_implementation(self
, iface
):
65 if not iface
.implementations
:
67 impls
= iface
.implementations
.values()
70 if self
.compare(iface
, x
, best
) < 0:
72 if self
.is_unusable(best
):
76 def compare(self
, interface
, b
, a
):
77 a_stab
= a
.get_stability()
78 b_stab
= b
.get_stability()
80 # Usable ones come first
81 r
= cmp(self
.is_unusable(b
), self
.is_unusable(a
))
84 # Preferred versions come first
85 r
= cmp(a_stab
== preferred
, b_stab
== preferred
)
88 if self
.network_use
!= network_full
:
89 r
= cmp(a
.get_cached(), b
.get_cached())
93 stab_policy
= interface
.stability_policy
95 if self
.help_with_testing
: stab_policy
= testing
96 else: stab_policy
= stable
98 if a_stab
>= stab_policy
: a_stab
= preferred
99 if b_stab
>= stab_policy
: b_stab
= preferred
101 r
= cmp(a_stab
, b_stab
)
104 r
= cmp(a
.version
, b
.version
)
107 if self
.network_use
!= network_full
:
108 r
= cmp(a
.get_cached(), b
.get_cached())
111 return cmp(a
.path
, b
.path
)
113 def get_ranked_implementations(self
, iface
):
114 impls
= iface
.implementations
.values()
115 impls
.sort(lambda a
, b
: self
.compare(iface
, a
, b
))
118 def is_unusable(self
, impl
):
119 if impl
.get_stability() <= buggy
:
121 if self
.network_use
== network_offline
and not impl
.get_cached():
125 def get_interface(self
, uri
):
126 """Get the interface for uri. If it's in the cache, read that.
127 If it's not in the cache or network use is full, start downloading
128 the latest version."""
131 assert isinstance(uri
, unicode)
133 if uri
not in _interfaces
:
134 # Haven't used this interface so far. Initialise from cache.
135 _interfaces
[uri
] = Interface(uri
)
136 self
.init_interface(_interfaces
[uri
])
138 if self
.network_use
== network_full
and not _interfaces
[uri
].uptodate
:
139 self
.begin_iface_download(_interfaces
[uri
])
141 return _interfaces
[uri
]
143 def init_interface(self
, iface
):
144 """We've just created a new Interface. Update from disk cache/network."""
145 debug("Created " + iface
.uri
)
146 cached
= reader
.update_from_cache(iface
)
148 if self
.network_use
!= network_offline
:
149 debug("Interface not cached and not off-line. Downloading...")
150 self
.begin_iface_download(iface
)
152 debug("Nothing known about interface, but we are off-line.")
154 def begin_iface_download(self
, interface
, force
= False):
155 dl
= download
.begin_download(interface
, force
)
158 return # Already in progress
160 # Calls update_interface_from_network eventually on success
161 self
.monitor_download(dl
)
163 def monitor_download(self
, dl
):
164 raise NotImplementedError("Abstract method")
166 def update_interface_from_network(self
, interface
, new_xml
):
167 """xml is the new XML (after the signature has been checked and
169 debug("Updating '%s' from network" % (interface
.name
or interface
.uri
))
170 assert interface
.uri
.startswith('/')
172 upstream_dir
= basedir
.save_config_path(config_site
, config_prog
, 'interfaces')
173 cached
= os
.path
.join(upstream_dir
, escape(interface
.uri
))
175 if os
.path
.exists(cached
):
176 old_xml
= file(cached
).read()
177 if old_xml
== new_xml
:
181 self
.confirm_diff(old_xml
, new_xml
, interface
.uri
)
183 stream
= file(cached
+ '.new', 'w')
184 stream
.write(new_xml
)
186 new_mtime
= reader
.check_readable(interface
.uri
, cached
+ '.new')
188 if interface
.last_modified
:
189 if new_mtime
< interface
.last_modified
:
190 raise SafeException("New interface's modification time is before old "
192 "\nOld time: " + pretty_time(interface
.last_modified
) +
193 "\nNew time: " + pretty_time(new_mtime
) +
194 "\nRefusing update (leaving new copy as " +
196 if new_mtime
== interface
.last_modified
:
197 raise SafeException("Interface has changed, but modification time "
198 "hasn't! Refusing update.")
199 os
.rename(cached
+ '.new', cached
)
200 debug("Saved as " + cached
)
202 interface
.uptodate
= True
203 reader
.update_from_cache(interface
)
206 def get_implementation(self
, interface
):
207 if not interface
.name
:
208 raise SafeException("We don't have enough information to "
209 "run this program yet. "
210 "Need to download:\n%s" % interface
.uri
)
212 return self
.implementation
[interface
]
214 if interface
.implementations
:
216 if self
.network_use
== network_offline
:
217 offline
= "\nThis may be because 'Network Use' is set to Off-line."
218 raise SafeException("No usable implementation found for '%s'.%s" %
219 (interface
.name
, offline
))
222 def walk_interfaces(self
):
225 impl
= self
.get_best_implementation(iface
)
227 for d
in impl
.dependencies
.values():
228 for idep
in walk(self
.get_interface(d
.interface
)):
230 return walk(self
.get_interface(self
.root
))
232 def check_signed_data(self
, download
, signed_data
):
233 """Downloaded data is a GPG-signed message. Check that the signature is trusted
234 and call self.update_interface_from_network() when done."""
236 from trust
import trust_db
237 data
, errors
, sigs
= gpg
.check_stream(signed_data
)
238 iface_xml
= data
.read()
240 if not self
.update_interface_if_trusted(download
.interface
, sigs
, iface_xml
):
241 self
.confirm_trust_keys(download
.interface
, sigs
, iface_xml
)
243 def update_interface_if_trusted(self
, interface
, sigs
, xml
):
246 self
.update_interface_from_network(interface
, xml
)
250 def confirm_trust_keys(self
, interface
, sigs
, iface_xml
):
251 """We don't trust any of the signatures yet. Ask the user.
252 When done, call update_interface_if_trusted()."""
254 valid_sigs
= [s
for s
in sigs
if isinstance(s
, gpg
.ValidSig
)]
256 raise SafeException('No valid signatures found')
258 print "\nInterface:", interface
.uri
259 print "The interface is correctly signed with the following keys:"
262 print "Do you want to trust all of these keys to sign interfaces?"
264 i
= raw_input("Trust all [Y/N] ")
267 raise SafeException('Not signed with a trusted key')
270 from trust
import trust_db
271 for key
in valid_sigs
:
272 print "Trusting", key
.fingerprint
273 trust_db
.trust_key(key
.fingerprint
)
275 if not self
.update_interface_if_trusted(interface
, sigs
, iface_xml
):
276 raise Exception('Bug: still not trusted!!')
278 def confirm_diff(self
, old
, new
, uri
):
280 diff
= difflib
.unified_diff(old
.split('\n'), new
.split('\n'), uri
, "",