Unpack archives to tmp dir.
[zeroinstall.git] / model.py
bloba2dbec9ca22f90353080ba799a67ebe6a3317dad
1 """In-memory representation of the dependency graph."""
3 import os
5 network_offline = 'off-line'
6 network_minimal = 'minimal'
7 network_full = 'full'
8 network_levels = (network_offline, network_minimal, network_full)
10 stability_levels = {} # Name -> Stability
12 class Stability(object):
13 __slots__ = ['level', 'name', 'description']
14 def __init__(self, level, name, description):
15 self.level = level
16 self.name = name
17 self.description = description
18 assert name not in stability_levels
19 stability_levels[name] = self
21 def __cmp__(self, other):
22 return cmp(self.level, other.level)
24 def __str__(self):
25 return self.name
27 insecure = Stability(0, 'insecure', 'This is a security risk')
28 buggy = Stability(5, 'buggy', 'Known to have serious bugs')
29 developer = Stability(10, 'developer', 'Work-in-progress - bugs likely')
30 testing = Stability(20, 'testing', 'Stability unknown - please test!')
31 stable = Stability(30, 'stable', 'Tested - no serious problems found')
32 preferred = Stability(40, 'preferred', 'Best of all - must be set manually')
34 class Restriction(object):
35 """A Restriction limits the allowed implementations of an Interface."""
37 class Binding(object):
38 """Information about how the choice of a Dependency is made known
39 to the application being run."""
41 class EnvironmentBinding(Binding):
42 __slots__ = ['name', 'insert']
44 def __init__(self, name, insert):
45 self.name = name
46 self.insert = insert
48 def __str__(self):
49 return "<environ %s += %s>" % (self.name, self.insert)
51 class Dependency(object):
52 """A Dependency indicates that an Implementation requires some additional
53 code to function, specified by another Interface."""
54 __slots__ = ['interface', 'restrictions', 'bindings']
56 def __init__(self, interface):
57 assert isinstance(interface, (str, unicode))
58 self.interface = interface
59 self.restrictions = []
60 self.bindings = []
62 def __str__(self):
63 return "<Dependency on %s; bindings: %d>" % (self.interface, len(self.bindings))
65 class DownloadSource(object):
66 """A DownloadSource provides a way to fetch an implementation."""
67 __slots__ = ['implementation', 'url', 'size']
69 def __init__(self, implementation, url, size):
70 assert url.startswith('http:') or url.startswith('/')
71 self.implementation = implementation
72 self.url = url
73 self.size = size
75 class Implementation(object):
76 """An Implementation is a package which implements an Interface."""
77 __slots__ = ['arch', 'upstream_stability', 'user_stability',
78 'version', 'size', 'dependencies', '_cached',
79 'id', 'download_sources']
81 def __init__(self, id):
82 """id can be a local path (string starting with /) or a manifest hash (eg "sha1=XXX")"""
83 assert id
84 self.id = id
85 self.size = None
86 self.version = None
87 self.user_stability = None
88 self.upstream_stability = None
89 self.arch = None
90 self._cached = None
91 self.dependencies = {} # URI -> Dependency
92 self.download_sources = [] # [DownloadSource]
94 def add_download_source(self, url, size):
95 self.download_sources.append(DownloadSource(self, url, size))
97 def get_stability(self):
98 return self.user_stability or self.upstream_stability or testing
100 def get_version(self):
101 return '.'.join(map(str, self.version))
103 def __str__(self):
104 return self.id
106 def __cmp__(self, other):
107 return cmp(other.version, self.version)
109 class Interface(object):
110 """An Interface represents some contract of behaviour."""
111 __slots__ = ['uri', 'implementations', 'name', 'description', 'summary',
112 'stability_policy', 'last_updated', 'last_modified', 'last_checked',
113 'main']
115 # stability_policy:
116 # Implementations at this level or higher are preferred.
117 # Lower levels are used only if there is no other choice.
119 # last_updated:
120 # The time we last updated the cached information (the time is stored
121 # in the cache). None means we haven't even read the cache yet.
123 def __init__(self, uri):
124 assert uri
125 assert uri.startswith('/') or uri.startswith('http:')
126 self.uri = uri
127 self.reset()
129 def reset(self):
130 self.implementations = {} # Path -> Implementation
131 self.name = None
132 self.last_updated = None
133 self.summary = None
134 self.description = None
135 self.stability_policy = None
136 self.last_modified = None
137 self.last_checked = None
138 self.main = None
140 def get_name(self):
141 return self.name or '(' + os.path.basename(self.uri) + ')'
143 def __repr__(self):
144 return "<Interface %s>" % self.uri
146 def get_impl(self, id):
147 if id not in self.implementations:
148 self.implementations[id] = Implementation(id)
149 return self.implementations[id]
151 def set_stability_policy(self, new):
152 assert new is None or isinstance(new, Stability)
153 self.stability_policy = new
155 def escape(uri):
156 "Convert each space to %20, etc"
157 import re
158 return re.sub('[^-_.a-zA-Z0-9]',
159 lambda match: '%%%02x' % ord(match.group(0)),
160 uri.encode('utf-8'))
162 class SafeException(Exception):
163 pass
165 try:
166 _cache = os.readlink('/uri/0install/.lazyfs-cache')
167 except:
168 _cache = None
169 def cache_for(path):
170 prefix = '/uri/0install/'
171 if path.startswith(prefix) and _cache:
172 return os.path.join(_cache, path[len(prefix):])
173 return path