Allow http URLs for interface names.
[zeroinstall.git] / model.py
blob6db43f2a1d28a71fa93376bb2683c68959b2c544
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__ = ['arch', 'upstream_stability', 'user_stability',
68 'version', 'size', 'dependencies', '_cached',
69 'id']
71 def __init__(self, id):
72 """id can be a local path (string starting with /) or a manifest hash (eg "sha1=XXX")"""
73 assert id
74 self.id = id
75 self.size = None
76 self.version = None
77 self.user_stability = None
78 self.upstream_stability = None
79 self.arch = None
80 self._cached = None
81 self.dependencies = {} # URI -> Dependency
83 def get_stability(self):
84 return self.user_stability or self.upstream_stability or testing
86 def get_version(self):
87 return '.'.join(map(str, self.version))
89 def __str__(self):
90 return self.id
92 def __cmp__(self, other):
93 return cmp(other.version, self.version)
95 class Interface(object):
96 """An Interface represents some contract of behaviour."""
97 __slots__ = ['uri', 'implementations', 'name', 'description', 'summary',
98 'stability_policy', 'last_updated', 'last_modified', 'last_checked']
100 # stability_policy:
101 # Implementations at this level or higher are preferred.
102 # Lower levels are used only if there is no other choice.
104 # last_updated:
105 # The time we last updated the cached information (the time is stored
106 # in the cache). None means we haven't even read the cache yet.
108 def __init__(self, uri):
109 assert uri
110 assert uri.startswith('/') or uri.startswith('http:')
111 self.uri = uri
112 self.reset()
114 def reset(self):
115 self.implementations = {} # Path -> Implementation
116 self.name = None
117 self.last_updated = None
118 self.summary = None
119 self.description = None
120 self.stability_policy = None
121 self.last_modified = None
122 self.last_checked = None
124 def get_name(self):
125 return self.name or '(' + os.path.basename(self.uri) + ')'
127 def __repr__(self):
128 return "<Interface %s>" % self.uri
130 def get_impl(self, id):
131 if id not in self.implementations:
132 self.implementations[id] = Implementation(id)
133 return self.implementations[id]
135 def set_stability_policy(self, new):
136 assert new is None or isinstance(new, Stability)
137 self.stability_policy = new
139 def escape(uri):
140 "Convert each space to %20, etc"
141 import re
142 return re.sub('[^-_.a-zA-Z0-9]',
143 lambda match: '%%%02x' % ord(match.group(0)),
144 uri.encode('utf-8'))
146 class SafeException(Exception):
147 pass
149 try:
150 _cache = os.readlink('/uri/0install/.lazyfs-cache')
151 except:
152 _cache = None
153 def cache_for(path):
154 prefix = '/uri/0install/'
155 if path.startswith(prefix) and _cache:
156 return os.path.join(_cache, path[len(prefix):])
157 return path