Added last-modified attribute.
[zeroinstall.git] / model.py
blob58b02a230105cb7ace65df275c26bc85d353a705
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 Implementation(object):
66 """An Implementation is a package which implements an Interface."""
67 __slots__ = ['path', 'arch', 'upstream_stability', 'user_stability',
68 'version', 'size', 'dependencies', '_cached']
70 def __init__(self, path):
71 assert path
72 self.path = path
73 self.size = None
74 self.version = None
75 self.user_stability = None
76 self.upstream_stability = None
77 self.arch = None
78 self._cached = None
79 self.dependencies = {} # URI -> Dependency
81 def get_stability(self):
82 return self.user_stability or self.upstream_stability or testing
84 def get_cached(self):
85 if self._cached is None:
86 self._cached = False
87 if os.path.exists(self.path):
88 cache_dir = cache_for(self.path)
89 for x in os.listdir(cache_dir):
90 if x[0] == '.': continue
91 if x == 'AppInfo.xml': continue
92 if os.path.isfile(os.path.join(cache_dir, x)):
93 self._cached = True
94 break
95 return self._cached
97 def get_version(self):
98 return '.'.join(map(str, self.version))
100 def __str__(self):
101 return self.path
103 def __cmp__(self, other):
104 return cmp(other.version, self.version)
106 class Interface(object):
107 """An Interface represents some contract of behaviour."""
108 __slots__ = ['uri', 'implementations', 'name', 'uptodate', 'description', 'summary',
109 'stability_policy', 'last_updated', 'last_modified']
111 # stability_policy:
112 # Implementations at this level or higher are preferred.
113 # Lower levels are used only if there is no other choice.
115 # last_updated:
116 # The time we last updated the cached information (the time is stored
117 # in the cache). None means we haven't even read the cache yet.
119 def __init__(self, uri):
120 assert uri
121 self.uri = uri
122 self.uptodate = False # Did we download this session?
123 self.reset()
125 def reset(self):
126 self.implementations = {} # Path -> Implementation
127 self.name = None
128 self.last_updated = None
129 self.summary = None
130 self.description = None
131 self.stability_policy = None
132 self.last_modified = None
134 def get_name(self):
135 return self.name or '(' + os.path.basename(self.uri) + ')'
137 def __repr__(self):
138 return "<Interface %s>" % self.uri
140 def get_impl(self, path):
141 if path not in self.implementations:
142 self.implementations[path] = Implementation(path)
143 return self.implementations[path]
145 def set_stability_policy(self, new):
146 assert new is None or isinstance(new, Stability)
147 self.stability_policy = new
149 def escape(uri):
150 "Convert each space to %20, etc"
151 import re
152 return re.sub('[^-_.a-zA-Z0-9]',
153 lambda match: '%%%02x' % ord(match.group(0)),
154 uri.encode('utf-8'))
156 class SafeException(Exception):
157 pass
159 try:
160 _cache = os.readlink('/uri/0install/.lazyfs-cache')
161 except:
162 _cache = None
163 def cache_for(path):
164 prefix = '/uri/0install/'
165 if path.startswith(prefix) and _cache:
166 return os.path.join(_cache, path[len(prefix):])
167 return path