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