~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/__init__.py

  • Committer: Martin Pool
  • Date: 2007-04-24 05:02:04 UTC
  • mfrom: (2449 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2450.
  • Revision ID: mbp@sourcefrog.net-20070424050204-bfkc1qiq0axt5f14
Merge trunk & fix NEWS conflict

Show diffs side-by-side

added added

removed removed

Lines of Context:
63
63
    mutter,
64
64
    warning,
65
65
    )
66
 
 
67
 
# {prefix: [transport_classes]}
68
 
# Transports are inserted onto the list LIFO and tried in order; as a result
69
 
# transports provided by plugins are tried first, which is usually what we
70
 
# want.
71
 
_protocol_handlers = {
72
 
}
73
 
 
74
 
def register_transport(prefix, klass, override=DEPRECATED_PARAMETER):
75
 
    """Register a transport that can be used to open URLs
76
 
 
77
 
    Normally you should use register_lazy_transport, which defers loading the
78
 
    implementation until it's actually used, and so avoids pulling in possibly
79
 
    large implementation libraries.
80
 
    """
81
 
    # Note that this code runs very early in library setup -- trace may not be
82
 
    # working, etc.
83
 
    global _protocol_handlers
84
 
    if deprecated_passed(override):
85
 
        warnings.warn("register_transport(override) is deprecated")
86
 
    _protocol_handlers.setdefault(prefix, []).insert(0, klass)
87
 
 
88
 
 
89
 
def register_lazy_transport(scheme, module, classname):
90
 
    """Register lazy-loaded transport class.
91
 
 
92
 
    When opening a URL with the given scheme, load the module and then
93
 
    instantiate the particular class.  
94
 
 
95
 
    If the module raises DependencyNotPresent when it's imported, it is
96
 
    skipped and another implementation of the protocol is tried.  This is
97
 
    intended to be used when the implementation depends on an external
98
 
    implementation that may not be present.  If any other error is raised, it
99
 
    propagates up and the attempt to open the url fails.
100
 
 
101
 
    :param scheme: The url scheme part, eg "ftp://"
102
 
    """
103
 
    # TODO: If no implementation of a protocol is available because of missing
104
 
    # dependencies, we should perhaps show the message about what dependency
105
 
    # was missing.
106
 
    def _loader(base):
107
 
        mod = __import__(module, globals(), locals(), [classname])
108
 
        klass = getattr(mod, classname)
109
 
        return klass(base)
110
 
    _loader.module = module
111
 
    register_transport(scheme, _loader)
112
 
 
113
 
 
114
 
def unregister_transport(scheme, factory):
115
 
    """Unregister a transport."""
116
 
    _protocol_handlers[scheme].remove(factory)
117
 
    if _protocol_handlers[scheme] == []:
118
 
        del _protocol_handlers[scheme]
 
66
from bzrlib import registry
119
67
 
120
68
 
121
69
def _get_protocol_handlers():
122
70
    """Return a dictionary of {urlprefix: [factory]}"""
123
 
    return _protocol_handlers
 
71
    return transport_list_registry
124
72
 
125
73
 
126
74
def _set_protocol_handlers(new_handlers):
128
76
 
129
77
    WARNING this will remove all build in protocols. Use with care.
130
78
    """
131
 
    global _protocol_handlers
132
 
    _protocol_handlers = new_handlers
 
79
    global transport_list_registry
 
80
    transport_list_registry = new_handlers
133
81
 
134
82
 
135
83
def _clear_protocol_handlers():
136
 
    global _protocol_handlers
137
 
    _protocol_handlers = {}
 
84
    global transport_list_registry
 
85
    transport_list_registry = TransportListRegistry()
138
86
 
139
87
 
140
88
def _get_transport_modules():
141
89
    """Return a list of the modules providing transports."""
142
90
    modules = set()
143
 
    for prefix, factory_list in _protocol_handlers.items():
 
91
    for prefix, factory_list in transport_list_registry.iteritems():
144
92
        for factory in factory_list:
145
 
            if factory.__module__ == "bzrlib.transport":
146
 
                # this is a lazy load transport, because no real ones
147
 
                # are directly in bzrlib.transport
148
 
                modules.add(factory.module)
 
93
            if hasattr(factory, "_module_name"):
 
94
                modules.add(factory._module_name)
149
95
            else:
150
 
                modules.add(factory.__module__)
 
96
                modules.add(factory._obj.__module__)
151
97
    # Add chroot directly, because there is not handler registered for it.
152
98
    modules.add('bzrlib.transport.chroot')
153
99
    result = list(modules)
155
101
    return result
156
102
 
157
103
 
 
104
class TransportListRegistry(registry.Registry):
 
105
    """A registry which simplifies tracking available Transports.
 
106
 
 
107
    A registration of a new protocol requires two step:
 
108
    1) register the prefix with the function register_transport( )
 
109
    2) register the protocol provider with the function
 
110
    register_transport_provider( ) ( and the "lazy" variant )
 
111
 
 
112
    This in needed because:
 
113
    a) a single provider can support multple protcol ( like the ftp
 
114
    privider which supports both the ftp:// and the aftp:// protocols )
 
115
    b) a single protocol can have multiple providers ( like the http://
 
116
    protocol which is supported by both the urllib and pycurl privider )
 
117
    """
 
118
 
 
119
    def register_transport_provider(self, key, obj):
 
120
        self.get(key).insert(0, registry._ObjectGetter(obj))
 
121
 
 
122
    def register_lazy_transport_provider(self, key, module_name, member_name):
 
123
        self.get(key).insert(0, 
 
124
                registry._LazyObjectGetter(module_name, member_name))
 
125
 
 
126
    def register_transport(self, key, help=None, info=None):
 
127
        self.register(key, [], help, info)
 
128
 
 
129
    def set_default_transport(self, key=None):
 
130
        """Return either 'key' or the default key if key is None"""
 
131
        self._default_key = key
 
132
 
 
133
 
 
134
transport_list_registry = TransportListRegistry( )
 
135
 
 
136
 
 
137
def register_transport_proto(prefix, help=None, info=None):
 
138
    transport_list_registry.register_transport(prefix, help, info)
 
139
 
 
140
 
 
141
def register_lazy_transport(prefix, module, classname):
 
142
    if not prefix in transport_list_registry:
 
143
        register_transport_proto(prefix)
 
144
    transport_list_registry.register_lazy_transport_provider(prefix, module, classname)
 
145
 
 
146
 
 
147
def register_transport(prefix, klass, override=DEPRECATED_PARAMETER):
 
148
    if not prefix in transport_list_registry:
 
149
        register_transport_proto(prefix)
 
150
    transport_list_registry.register_transport_provider(prefix, klass)
 
151
 
 
152
 
158
153
def register_urlparse_netloc_protocol(protocol):
159
154
    """Ensure that protocol is setup to be used with urlparse netloc parsing."""
160
155
    if protocol not in urlparse.uses_netloc:
161
156
        urlparse.uses_netloc.append(protocol)
162
157
 
163
158
 
 
159
def unregister_transport(scheme, factory):
 
160
    """Unregister a transport."""
 
161
    l = transport_list_registry.get(scheme)
 
162
    for i in l:
 
163
        o = i.get_obj( )
 
164
        if o == factory:
 
165
            transport_list_registry.get(scheme).remove(i)
 
166
            break
 
167
    if len(l) == 0:
 
168
        transport_list_registry.remove(scheme)
 
169
 
 
170
 
 
171
 
164
172
def split_url(url):
165
173
    # TODO: jam 20060606 urls should only be ascii, or they should raise InvalidURL
166
174
    if isinstance(url, unicode):
403
411
        This function will only be defined for Transports which have a
404
412
        physical local filesystem representation.
405
413
        """
406
 
        # TODO: jam 20060426 Should this raise NotLocalUrl instead?
407
 
        raise errors.TransportNotPossible('This is not a LocalTransport,'
408
 
            ' so there is no local representation for a path')
 
414
        raise errors.NotLocalUrl(self.abspath(relpath))
 
415
 
409
416
 
410
417
    def has(self, relpath):
411
418
        """Does the file relpath exist?
1021
1028
 
1022
1029
    base is either a URL or a directory name.  
1023
1030
    """
1024
 
    global _protocol_handlers
 
1031
 
1025
1032
    if base is None:
1026
1033
        base = '.'
1027
1034
    last_err = None
1045
1052
        base = convert_path_to_url(base,
1046
1053
            'URLs must be properly escaped (protocol: %s)')
1047
1054
    
1048
 
    for proto, factory_list in _protocol_handlers.iteritems():
 
1055
    for proto, factory_list in transport_list_registry.iteritems():
1049
1056
        if proto is not None and base.startswith(proto):
1050
1057
            t, last_err = _try_transport_factories(base, factory_list)
1051
1058
            if t:
1056
1063
    base = convert_path_to_url(base, 'Unsupported protocol: %s')
1057
1064
 
1058
1065
    # The default handler is the filesystem handler, stored as protocol None
1059
 
    return _try_transport_factories(base, _protocol_handlers[None])[0]
1060
 
 
1061
 
 
 
1066
    return _try_transport_factories(base,
 
1067
                    transport_list_registry.get(None))[0]
 
1068
                                                   
1062
1069
def do_catching_redirections(action, transport, redirected):
1063
1070
    """Execute an action with given transport catching redirections.
1064
1071
 
1105
1112
    last_err = None
1106
1113
    for factory in factory_list:
1107
1114
        try:
1108
 
            return factory(base), None
 
1115
            return factory.get_obj()(base), None
1109
1116
        except errors.DependencyNotPresent, e:
1110
1117
            mutter("failed to instantiate transport %r for %r: %r" %
1111
1118
                    (factory, base, e))
1220
1227
    def readv(self, name, offsets):
1221
1228
        self._calls.append((name, offsets))
1222
1229
        return self._adapted.readv(name, offsets)
1223
 
        
 
1230
 
1224
1231
 
1225
1232
# None is the default transport, for things with no url scheme
1226
 
register_lazy_transport(None, 'bzrlib.transport.local', 'LocalTransport')
 
1233
register_transport_proto('file://',
 
1234
            help="Access using the standard filesystem (default)")
1227
1235
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
 
1236
transport_list_registry.set_default_transport("file://")
 
1237
 
 
1238
register_transport_proto('sftp://',
 
1239
            help="Access using SFTP (most SSH servers provide SFTP).")
1228
1240
register_lazy_transport('sftp://', 'bzrlib.transport.sftp', 'SFTPTransport')
1229
1241
# Decorated http transport
 
1242
register_transport_proto('http+urllib://',
 
1243
#                help="Read-only access of branches exported on the web."
 
1244
            )
1230
1245
register_lazy_transport('http+urllib://', 'bzrlib.transport.http._urllib',
1231
1246
                        'HttpTransport_urllib')
 
1247
register_transport_proto('https+urllib://',
 
1248
#                help="Read-only access of branches exported on the web using SSL."
 
1249
            )
1232
1250
register_lazy_transport('https+urllib://', 'bzrlib.transport.http._urllib',
1233
1251
                        'HttpTransport_urllib')
 
1252
register_transport_proto('http+pycurl://',
 
1253
#                help="Read-only access of branches exported on the web."
 
1254
            )
1234
1255
register_lazy_transport('http+pycurl://', 'bzrlib.transport.http._pycurl',
1235
1256
                        'PyCurlTransport')
 
1257
register_transport_proto('https+pycurl://',
 
1258
#                help="Read-only access of branches exported on the web using SSL."
 
1259
            )
1236
1260
register_lazy_transport('https+pycurl://', 'bzrlib.transport.http._pycurl',
1237
1261
                        'PyCurlTransport')
1238
1262
# Default http transports (last declared wins (if it can be imported))
 
1263
register_transport_proto('http://',
 
1264
            help="Read-only access of branches exported on the web.")
 
1265
register_transport_proto('https://',
 
1266
            help="Read-only access of branches exported on the web using SSL.")
1239
1267
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
1240
1268
                        'HttpTransport_urllib')
1241
1269
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
1242
1270
                        'HttpTransport_urllib')
1243
1271
register_lazy_transport('http://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
1244
1272
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
 
1273
 
 
1274
register_transport_proto('ftp://',
 
1275
            help="Access using passive FTP.")
1245
1276
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
 
1277
register_transport_proto('aftp://',
 
1278
            help="Access using active FTP.")
1246
1279
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
 
1280
 
 
1281
register_transport_proto('memory://')
1247
1282
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
 
1283
register_transport_proto('chroot+')
 
1284
 
 
1285
register_transport_proto('readonly+',
 
1286
#              help="This modifier converts any transport to be readonly."
 
1287
            )
1248
1288
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
 
1289
register_transport_proto('fakenfs+')
1249
1290
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
 
1291
register_transport_proto('vfat+')
1250
1292
register_lazy_transport('vfat+',
1251
1293
                        'bzrlib.transport.fakevfat',
1252
1294
                        'FakeVFATTransportDecorator')
 
1295
register_transport_proto('bzr://',
 
1296
            help="Fast access using the Bazaar smart server.")
 
1297
 
1253
1298
register_lazy_transport('bzr://',
1254
1299
                        'bzrlib.transport.remote',
1255
1300
                        'RemoteTCPTransport')
 
1301
register_transport_proto('bzr+http://',
 
1302
#                help="Fast access using the Bazaar smart server over HTTP."
 
1303
             )
1256
1304
register_lazy_transport('bzr+http://',
1257
1305
                        'bzrlib.transport.remote',
1258
1306
                        'RemoteHTTPTransport')
 
1307
register_transport_proto('bzr+ssh://',
 
1308
            help="Fast access using the Bazaar smart server over SSH.")
1259
1309
register_lazy_transport('bzr+ssh://',
1260
1310
                        'bzrlib.transport.remote',
1261
1311
                        'RemoteSSHTransport')