Revision e3fd7f91

b/pithos/lib/client.py
4 4
import json
5 5
import types
6 6
import socket
7
import pithos.api.faults
8

  
9
ERROR_CODES = {304:'Not Modified',
10
               400:'Bad Request',
11
               401:'Unauthorized',
12
               404:'Not Found',
13
               409:'Conflict',
14
               411:'Length Required',
15
               412:'Precondition Failed',
16
               416:'Range Not Satisfiable',
17
               422:'Unprocessable Entity',
18
               503:'Service Unavailable'}
7 19

  
8 20
class Fault(Exception):
9
    def __init__(self, data=''):
21
    def __init__(self, data='', status=None):
22
        if data == '' and status in ERROR_CODES.keys():
23
            data = ERROR_CODES[status]
10 24
        Exception.__init__(self, data)
11 25
        self.data = data
12

  
26
        self.status = status
13 27

  
14 28
class Client(object):
15 29
    def __init__(self, host, account, api='v1', verbose=False, debug=False):
......
20 34
        self.api = api
21 35
        self.verbose = verbose or debug
22 36
        self.debug = debug
23
    
37

  
24 38
    def _chunked_transfer(self, path, method='PUT', f=stdin, headers=None,
25 39
                          blocksize=1024):
26 40
        http = HTTPConnection(self.host)
......
58 72
        
59 73
        # get response
60 74
        resp = http.getresponse()
75
        
61 76
        headers = dict(resp.getheaders())
62 77
        
63 78
        if self.verbose:
......
71 86
            print data
72 87
            print
73 88
        
89
        if data:
90
            assert data[-1] == '\n'
91
        #remove trailing enter
92
        data = data and data[:-1] or data
93
        
94
        if int(resp.status) in ERROR_CODES.keys():
95
            raise Fault(data, int(resp.status))
96
        
74 97
        return resp.status, headers, data
75 98

  
76 99
    def req(self, method, path, body=None, headers=None, format='text',
......
96 119
            kwargs['body'] = body
97 120
            kwargs['headers']['Content-Type'] = 'application/octet-stream'
98 121
        #print '****', method, full_path, kwargs
99
        #TODO catch socket.error
100
        conn.request(method, full_path, **kwargs)
122
        try:
123
            conn.request(method, full_path, **kwargs)
124
        except socket.error, e:
125
            raise Fault(status=503)
126
            
101 127
        resp = conn.getresponse()
102 128
        headers = dict(resp.getheaders())
103 129
        
......
117 143
        #remove trailing enter
118 144
        data = data and data[:-1] or data
119 145
        
146
        if int(resp.status) in ERROR_CODES.keys():
147
            raise Fault(data, int(resp.status))
148
        
149
        #print '*',  resp.status, headers, data
120 150
        return resp.status, headers, data
121 151

  
122 152
    def delete(self, path, format='text'):
......
147 177

  
148 178
    def _get_metadata(self, path, prefix=None, params=None):
149 179
        status, headers, data = self.head(path, params=params)
150
        if status == '404':
151
            return None
152 180
        prefixlen = prefix and len(prefix) or 0
153 181
        meta = {}
154 182
        for key, val in headers.items():
......
189 217
        if status == 202:
190 218
            return False
191 219
        elif status != 201:
192
            raise Fault(data)
220
            raise Fault(data, int(status))
193 221
        return True
194 222

  
195 223
    def delete_container(self, container):
......
222 250
        path = '/%s/%s' % (container, object)
223 251
        if not chunked and f != stdin:
224 252
            data = f and f.read() or None
225
            self.put(path, data, headers=headers)
253
            return self.put(path, data, headers=headers)
226 254
        else:
227
            self._chunked_transfer(path, 'PUT', f, headers=headers,
255
            return self._chunked_transfer(path, 'PUT', f, headers=headers,
228 256
                                   blocksize=1024)
229 257

  
230 258
    def update_object(self, container, object, f=stdin, chunked=False,
b/tools/store
57 57
        
58 58
        self.parser = parser
59 59
        self.args = args
60
    
60

  
61 61
    def add_options(self, parser):
62 62
        pass
63 63

  
......
105 105
            self.list_objects(container)
106 106
        else:
107 107
            self.list_containers()
108
    
108

  
109 109
    def list_containers(self):
110 110
        params = {'limit':self.limit, 'marker':self.marker}
111 111
        headers = {'IF_MODIFIED_SINCE':self.if_modified_since,
......
148 148
                          default=False, help='show metadata until that date')
149 149
        parser.add_option('--format', action='store', dest='format',
150 150
                          default='%d/%m/%Y', help='format to parse until date')
151
        parser.add_option('--version', action='store', dest='version',
152
                          default=None, help='show specific version \
153
                                  (applies only for objects)')
151 154

  
152 155
    def execute(self, path=''):
153 156
        container, sep, object = path.partition('/')
......
263 266
                          dest='manifest', default=None,
264 267
                          help='use for large file support')
265 268
        parser.add_option('--touch', action='store_true',
266
                          dest='touch', default=True,
269
                          dest='touch', default=False,
267 270
                          help='create object with zero data')
268 271

  
269 272
    def execute(self, path, srcpath='-', *args):
......
334 337
@cli_command('update')
335 338
class UpdateObject(Command):
336 339
    syntax = '<container>/<object> path [key=val] [...]'
337
    description = 'update object metadata/data'
340
    description = 'update object metadata/data (default mode: append)'
338 341
    
339 342
    def add_options(self, parser):
340 343
        parser.add_option('-a', action='store_true', dest='append',
341
                          default=None, help='append data')
344
                          default=True, help='append data')
342 345
        parser.add_option('--start', action='store',
343 346
                          dest='start',
344 347
                          default=None, help='range of data to be updated')
......
356 359
                          dest='manifest', default=None,
357 360
                          help='use for large file support')
358 361

  
359
    def execute(self, path, srcpath, *args):
362
    def execute(self, path, srcpath='-', *args):
360 363
        headers = {}
361 364
        if self.manifest:
362 365
            headers['X_OBJECT_MANIFEST'] = self.manifest
......
451 454
    
452 455
    try:
453 456
        cmd.execute(*cmd.args)
454
    except TypeError:
457
    except TypeError, e:
458
        print e
455 459
        cmd.parser.usage = '%%prog %s [options] %s' % (name, cmd.syntax)
456 460
        cmd.parser.print_help()
457 461
        exit(1)
458 462
    except Fault, f:
459
        print f.data
463
        print f.status, f.data
460 464

  
461 465
if __name__ == '__main__':
462 466
    main()

Also available in: Unified diff