Moved non-gui parts to their own dir.
[zeroinstall.git] / injector / model.py
blobd62f0b961a80a60bf67341cc05a4798ab115e257
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 get_interface(self):
63 return get_interface(self.interface)
65 def __str__(self):
66 return "<Dependency on %s; bindings: %d>" % (self.interface, len(self.bindings))
68 class Implementation(object):
69 """An Implementation is a package which implements an Interface."""
70 __slots__ = ['path', 'arch', 'upstream_stability', 'user_stability',
71 'version', 'size', 'dependencies', '_cached']
73 def __init__(self, path):
74 assert path
75 self.path = path
76 self.size = None
77 self.version = None
78 self.user_stability = None
79 self.upstream_stability = None
80 self.arch = None
81 self._cached = None
82 self.dependencies = {} # URI -> Dependency
84 def get_stability(self):
85 return self.user_stability or self.upstream_stability or testing
87 def get_cached(self):
88 if self._cached is None:
89 self._cached = False
90 if os.path.exists(self.path):
91 cache_dir = cache_for(self.path)
92 for x in os.listdir(cache_dir):
93 if x[0] == '.': continue
94 if x == 'AppInfo.xml': continue
95 if os.path.isfile(os.path.join(cache_dir, x)):
96 self._cached = True
97 break
98 return self._cached
100 def get_version(self):
101 return '.'.join(map(str, self.version))
103 def __str__(self):
104 return self.path
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', 'uptodate', 'description', 'summary',
112 'stability_policy', 'last_updated']
114 # stability_policy:
115 # Implementations at this level or higher are preferred.
116 # Lower levels are used only if there is no other choice.
118 # last_updated:
119 # The time we last updated the cached information (the time is stored
120 # in the cache). None means we haven't even read the cache yet.
122 def __init__(self, uri):
123 assert uri
124 self.uri = uri
125 self.implementations = {} # Path -> Implementation
126 self.name = None
127 self.last_updated = None
128 self.uptodate = False
129 self.summary = None
130 self.description = None
131 self.stability_policy = None
133 def get_name(self):
134 return self.name or '(' + os.path.basename(self.uri) + ')'
136 def __repr__(self):
137 return "<Interface %s>" % self.uri
139 def get_impl(self, path):
140 if path not in self.implementations:
141 self.implementations[path] = Implementation(path)
142 return self.implementations[path]
144 def set_stability_policy(self, new):
145 assert new is None or isinstance(new, Stability)
146 self.stability_policy = new
148 def changed(self):
149 for w in self.watchers(): w(self)
151 _interfaces = {} # URI -> Interface
153 def get_interface(uri):
154 if type(uri) == str:
155 uri = unicode(uri)
156 assert isinstance(uri, unicode)
157 if uri not in _interfaces:
158 _interfaces[uri] = Interface(uri)
159 return _interfaces[uri]
161 def escape(uri):
162 "Convert each space to %20, etc"
163 import re
164 return re.sub('[^-_.a-zA-Z0-9]',
165 lambda match: '%%%02x' % ord(match.group(0)),
166 uri.encode('utf-8'))
168 class SafeException(Exception):
169 pass
171 try:
172 _cache = os.readlink('/uri/0install/.lazyfs-cache')
173 except:
174 _cache = None
175 def cache_for(path):
176 prefix = '/uri/0install/'
177 if path.startswith(prefix) and _cache:
178 return os.path.join(_cache, path[len(prefix):])
179 return path