1
2
3
4
5
6 """
7 couchdb.resource
8 ~~~~~~~~~~~~~~~~~~~~~~
9
10 This module providess a common interface for all CouchDB request. This
11 module makes HTTP request using :mod:`httplib2` module or :mod:`pycurl`
12 if available. Just use set transport argument for this.
13
14 Example:
15
16 >>> resource = CouchdbResource()
17 >>> info = resource.get()
18 >>> info['couchdb']
19 u'Welcome'
20
21 """
22 import base64
23 import re
24
25 from restkit import Resource, ClientResponse
26 from restkit.errors import ResourceError, RequestFailed, RequestError
27 from restkit.util import url_quote
28
29 from . import __version__
30 from .exceptions import ResourceNotFound, ResourceConflict, \
31 PreconditionFailed
32 from .utils import json
33
34 USER_AGENT = 'couchdbkit/%s' % __version__
35
36 RequestFailed = RequestFailed
39
40 @property
41 - def json_body(self):
42 body = self.body_string()
43
44
45 try:
46 return json.loads(body)
47 except ValueError:
48 return body
49
52
53 - def __init__(self, uri="http://127.0.0.1:5984", **client_opts):
54 """Constructor for a `CouchdbResource` object.
55
56 CouchdbResource represent an HTTP resource to CouchDB.
57
58 @param uri: str, full uri to the server.
59 """
60 client_opts['response_class'] = CouchDBResponse
61
62 Resource.__init__(self, uri=uri, **client_opts)
63 self.safe = ":/%"
64
65 - def copy(self, path=None, headers=None, **params):
66 """ add copy to HTTP verbs """
67 return self.request('COPY', path=path, headers=headers, **params)
68
69 - def request(self, method, path=None, payload=None, headers=None, **params):
70 """ Perform HTTP call to the couchdb server and manage
71 JSON conversions, support GET, POST, PUT and DELETE.
72
73 Usage example, get infos of a couchdb server on
74 http://127.0.0.1:5984 :
75
76
77 import couchdbkit.CouchdbResource
78 resource = couchdbkit.CouchdbResource()
79 infos = resource.request('GET')
80
81 @param method: str, the HTTP action to be performed:
82 'GET', 'HEAD', 'POST', 'PUT', or 'DELETE'
83 @param path: str or list, path to add to the uri
84 @param data: str or string or any object that could be
85 converted to JSON.
86 @param headers: dict, optional headers that will
87 be added to HTTP request.
88 @param raw: boolean, response return a Response object
89 @param params: Optional parameterss added to the request.
90 Parameterss are for example the parameters for a view. See
91 `CouchDB View API reference
92 <http://wiki.apache.org/couchdb/HTTP_view_API>`_ for example.
93
94 @return: tuple (data, resp), where resp is an `httplib2.Response`
95 object and data a python object (often a dict).
96 """
97
98 headers = headers or {}
99 headers.setdefault('Accept', 'application/json')
100 headers.setdefault('User-Agent', USER_AGENT)
101
102 if payload is not None:
103
104 if not hasattr(payload, 'read') and not isinstance(payload, basestring):
105 payload = json.dumps(payload).encode('utf-8')
106 headers.setdefault('Content-Type', 'application/json')
107
108 params = encode_params(params)
109 try:
110 resp = Resource.request(self, method, path=path,
111 payload=payload, headers=headers, **params)
112
113 except ResourceError, e:
114 msg = getattr(e, 'msg', '')
115 if e.response and msg:
116 if e.response.headers.get('content-type') == 'application/json':
117 try:
118 msg = json.loads(msg)
119 except ValueError:
120 pass
121
122 if type(msg) is dict:
123 error = msg.get('reason')
124 else:
125 error = msg
126
127 if e.status_int == 404:
128 raise ResourceNotFound(error, http_code=404,
129 response=e.response)
130
131 elif e.status_int == 409:
132 raise ResourceConflict(error, http_code=409,
133 response=e.response)
134 elif e.status_int == 412:
135 raise PreconditionFailed(error, http_code=412,
136 response=e.response)
137 else:
138 raise
139 except:
140 raise
141
142 return resp
143
145 """ encode parameters in json if needed """
146 _params = {}
147 if params:
148 for name, value in params.items():
149 if name in ('key', 'startkey', 'endkey'):
150 value = json.dumps(value)
151 elif value is None:
152 continue
153 elif not isinstance(value, basestring):
154 value = json.dumps(value)
155 _params[name] = value
156 return _params
157
159 if docid.startswith('/'):
160 docid = docid[1:]
161 if docid.startswith('_design'):
162 docid = '_design/%s' % url_quote(docid[8:], safe='')
163 else:
164 docid = url_quote(docid, safe='')
165 return docid
166
167 re_sp = re.compile('\s')
169 for k, v in attachments.iteritems():
170 if v.get('stub', False):
171 continue
172 else:
173 v['data'] = re_sp.sub('', base64.b64encode(v['data']))
174 return attachments
175