Merge lp:~jderose/usercouch/docs into lp:usercouch
- docs
- Merge into trunk
Proposed by
Jason Gerard DeRose
Status: | Merged |
---|---|
Approved by: | James Raymond |
Approved revision: | 60 |
Merged at revision: | 47 |
Proposed branch: | lp:~jderose/usercouch/docs |
Merge into: | lp:usercouch |
Diff against target: |
737 lines (+614/-17) 12 files modified
.bzrignore (+1/-0) debian/control (+27/-6) debian/python3-usercouch-doc.doc-base (+8/-0) debian/python3-usercouch-doc.install (+1/-0) debian/rules (+8/-8) doc/Makefile (+153/-0) doc/conf.py (+35/-0) doc/index.rst (+49/-0) doc/tutorial.rst (+154/-0) doc/usercouch.rst (+127/-0) doc/usercouch_misc.rst (+45/-0) usercouch/__init__.py (+6/-3) |
To merge this branch: | bzr merge lp:~jderose/usercouch/docs |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Raymond | Approve | ||
Review via email: mp+120495@code.launchpad.net |
Commit message
Description of the change
Adds a first pass at sphinx documentation. The API docs are a bit rough still, but I think the tutorial is in good shape and can quickly get newcomers productive.
To post a comment you must log in.
Revision history for this message
James Raymond (jamesmr) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file '.bzrignore' |
2 | --- .bzrignore 1970-01-01 00:00:00 +0000 |
3 | +++ .bzrignore 2012-08-21 03:50:43 +0000 |
4 | @@ -0,0 +1,1 @@ |
5 | +_build |
6 | |
7 | === modified file 'debian/control' |
8 | --- debian/control 2012-08-13 09:16:29 +0000 |
9 | +++ debian/control 2012-08-21 03:50:43 +0000 |
10 | @@ -4,6 +4,7 @@ |
11 | Maintainer: Jason Gerard DeRose <jderose@novacut.com> |
12 | Build-Depends: debhelper (>= 9), |
13 | python3-all (>= 3.2), |
14 | + python3-sphinx (>= 1.1), |
15 | couchdb-bin (>= 1.2.0) |
16 | Standards-Version: 3.9.3 |
17 | X-Python3-Version: >= 3.2 |
18 | @@ -12,7 +13,7 @@ |
19 | Package: python3-usercouch |
20 | Architecture: all |
21 | Depends: ${python3:Depends}, ${misc:Depends}, couchdb-bin (>= 1.2.0) |
22 | -Suggests: python3-microfiber |
23 | +Suggests: python3-microfiber, python3-usercouch-doc |
24 | Description: starts per-user CouchDB instances for fun, profit, unit testing |
25 | UserCouch is a Python3 library for starting per-user CouchDB instances, |
26 | including throw-away instances for unit testing. It's easy: |
27 | @@ -21,8 +22,28 @@ |
28 | from microfiber import Database |
29 | tmp = TempCouch() |
30 | env = tmp.bootstrap() |
31 | - db = Database('example', env) |
32 | - db.put(None) # Create the database |
33 | - db.post({'_id': 'foo'}) # Create a document |
34 | - . |
35 | - Also see Microfiber: https://launchpad.net/microfiber |
36 | + db = Database('mydb', env) |
37 | + db.put(None) # Create the database |
38 | + db.post({'_id': 'mydoc'}) # Create a document |
39 | + . |
40 | + Also see Microfiber: https://launchpad.net/microfiber |
41 | + |
42 | +Package: python3-usercouch-doc |
43 | +Architecture: all |
44 | +Section: doc |
45 | +Depends: ${sphinxdoc:Depends}, ${misc:Depends} |
46 | +Suggests: python3-usercouch |
47 | +Description: documentation for python3-usercouch |
48 | + UserCouch is a Python3 library for starting per-user CouchDB instances, |
49 | + including throw-away instances for unit testing. It's easy: |
50 | + . |
51 | + from usercouch.misc import TempCouch |
52 | + from microfiber import Database |
53 | + tmp = TempCouch() |
54 | + env = tmp.bootstrap() |
55 | + db = Database('mydb', env) |
56 | + db.put(None) # Create the database |
57 | + db.post({'_id': 'mydoc'}) # Create a document |
58 | + . |
59 | + Also see Microfiber: https://launchpad.net/microfiber |
60 | + |
61 | |
62 | === added file 'debian/python3-usercouch-doc.doc-base' |
63 | --- debian/python3-usercouch-doc.doc-base 1970-01-01 00:00:00 +0000 |
64 | +++ debian/python3-usercouch-doc.doc-base 2012-08-21 03:50:43 +0000 |
65 | @@ -0,0 +1,8 @@ |
66 | +Document: usercouch |
67 | +Title: UserCouch documentation |
68 | +Abstract: Documentation for UserCouch. |
69 | +Section: Programming |
70 | + |
71 | +Format: HTML |
72 | +Index: /usr/share/doc/python3-usercouch-doc/html/index.html |
73 | +Files: /usr/share/doc/python3-usercouch-doc/html/*.html |
74 | |
75 | === added file 'debian/python3-usercouch-doc.install' |
76 | --- debian/python3-usercouch-doc.install 1970-01-01 00:00:00 +0000 |
77 | +++ debian/python3-usercouch-doc.install 2012-08-21 03:50:43 +0000 |
78 | @@ -0,0 +1,1 @@ |
79 | +doc/_build/html/* usr/share/doc/python3-usercouch-doc/html |
80 | |
81 | === modified file 'debian/rules' |
82 | --- debian/rules 2012-08-13 09:14:59 +0000 |
83 | +++ debian/rules 2012-08-21 03:50:43 +0000 |
84 | @@ -1,22 +1,23 @@ |
85 | #!/usr/bin/make -f |
86 | |
87 | -PYTHON3=$(shell py3versions -vr) |
88 | - |
89 | %: |
90 | - dh $@ --with=python3 |
91 | - |
92 | -test-python%: |
93 | - python$* setup.py test |
94 | + dh $@ --with=python3 --with=sphinxdoc |
95 | |
96 | override_dh_auto_clean: |
97 | - rm -rf build/ |
98 | + rm -rf build/ doc/_build/ |
99 | |
100 | override_dh_auto_build: |
101 | + sphinx-build -b html doc/ doc/_build/html/ |
102 | set -ex; for python in $(shell py3versions -r); do \ |
103 | $$python setup.py build \ |
104 | --executable=/usr/bin/python3; \ |
105 | done |
106 | |
107 | +override_dh_auto_test: |
108 | + set -ex; for python in $(shell py3versions -r); do \ |
109 | + $$python setup.py test; \ |
110 | + done |
111 | + |
112 | override_dh_auto_install: |
113 | set -ex; for python in $(shell py3versions -r); do \ |
114 | $$python setup.py install \ |
115 | @@ -24,4 +25,3 @@ |
116 | --root=$(CURDIR)/debian/python3-usercouch; \ |
117 | done |
118 | |
119 | -override_dh_auto_test: $(PYTHON3:%=test-python%) |
120 | |
121 | === added directory 'doc' |
122 | === added file 'doc/Makefile' |
123 | --- doc/Makefile 1970-01-01 00:00:00 +0000 |
124 | +++ doc/Makefile 2012-08-21 03:50:43 +0000 |
125 | @@ -0,0 +1,153 @@ |
126 | +# Makefile for Sphinx documentation |
127 | +# |
128 | + |
129 | +# You can set these variables from the command line. |
130 | +SPHINXOPTS = |
131 | +SPHINXBUILD = sphinx-build |
132 | +PAPER = |
133 | +BUILDDIR = _build |
134 | + |
135 | +# Internal variables. |
136 | +PAPEROPT_a4 = -D latex_paper_size=a4 |
137 | +PAPEROPT_letter = -D latex_paper_size=letter |
138 | +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . |
139 | +# the i18n builder cannot share the environment and doctrees with the others |
140 | +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . |
141 | + |
142 | +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext |
143 | + |
144 | +help: |
145 | + @echo "Please use \`make <target>' where <target> is one of" |
146 | + @echo " html to make standalone HTML files" |
147 | + @echo " dirhtml to make HTML files named index.html in directories" |
148 | + @echo " singlehtml to make a single large HTML file" |
149 | + @echo " pickle to make pickle files" |
150 | + @echo " json to make JSON files" |
151 | + @echo " htmlhelp to make HTML files and a HTML help project" |
152 | + @echo " qthelp to make HTML files and a qthelp project" |
153 | + @echo " devhelp to make HTML files and a Devhelp project" |
154 | + @echo " epub to make an epub" |
155 | + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" |
156 | + @echo " latexpdf to make LaTeX files and run them through pdflatex" |
157 | + @echo " text to make text files" |
158 | + @echo " man to make manual pages" |
159 | + @echo " texinfo to make Texinfo files" |
160 | + @echo " info to make Texinfo files and run them through makeinfo" |
161 | + @echo " gettext to make PO message catalogs" |
162 | + @echo " changes to make an overview of all changed/added/deprecated items" |
163 | + @echo " linkcheck to check all external links for integrity" |
164 | + @echo " doctest to run all doctests embedded in the documentation (if enabled)" |
165 | + |
166 | +clean: |
167 | + -rm -rf $(BUILDDIR)/* |
168 | + |
169 | +html: |
170 | + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html |
171 | + @echo |
172 | + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." |
173 | + |
174 | +dirhtml: |
175 | + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml |
176 | + @echo |
177 | + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." |
178 | + |
179 | +singlehtml: |
180 | + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml |
181 | + @echo |
182 | + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." |
183 | + |
184 | +pickle: |
185 | + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle |
186 | + @echo |
187 | + @echo "Build finished; now you can process the pickle files." |
188 | + |
189 | +json: |
190 | + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json |
191 | + @echo |
192 | + @echo "Build finished; now you can process the JSON files." |
193 | + |
194 | +htmlhelp: |
195 | + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp |
196 | + @echo |
197 | + @echo "Build finished; now you can run HTML Help Workshop with the" \ |
198 | + ".hhp project file in $(BUILDDIR)/htmlhelp." |
199 | + |
200 | +qthelp: |
201 | + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp |
202 | + @echo |
203 | + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ |
204 | + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" |
205 | + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/UserCouch.qhcp" |
206 | + @echo "To view the help file:" |
207 | + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/UserCouch.qhc" |
208 | + |
209 | +devhelp: |
210 | + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp |
211 | + @echo |
212 | + @echo "Build finished." |
213 | + @echo "To view the help file:" |
214 | + @echo "# mkdir -p $$HOME/.local/share/devhelp/UserCouch" |
215 | + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/UserCouch" |
216 | + @echo "# devhelp" |
217 | + |
218 | +epub: |
219 | + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub |
220 | + @echo |
221 | + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." |
222 | + |
223 | +latex: |
224 | + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex |
225 | + @echo |
226 | + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." |
227 | + @echo "Run \`make' in that directory to run these through (pdf)latex" \ |
228 | + "(use \`make latexpdf' here to do that automatically)." |
229 | + |
230 | +latexpdf: |
231 | + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex |
232 | + @echo "Running LaTeX files through pdflatex..." |
233 | + $(MAKE) -C $(BUILDDIR)/latex all-pdf |
234 | + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." |
235 | + |
236 | +text: |
237 | + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text |
238 | + @echo |
239 | + @echo "Build finished. The text files are in $(BUILDDIR)/text." |
240 | + |
241 | +man: |
242 | + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man |
243 | + @echo |
244 | + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." |
245 | + |
246 | +texinfo: |
247 | + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo |
248 | + @echo |
249 | + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." |
250 | + @echo "Run \`make' in that directory to run these through makeinfo" \ |
251 | + "(use \`make info' here to do that automatically)." |
252 | + |
253 | +info: |
254 | + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo |
255 | + @echo "Running Texinfo files through makeinfo..." |
256 | + make -C $(BUILDDIR)/texinfo info |
257 | + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." |
258 | + |
259 | +gettext: |
260 | + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale |
261 | + @echo |
262 | + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." |
263 | + |
264 | +changes: |
265 | + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes |
266 | + @echo |
267 | + @echo "The overview file is in $(BUILDDIR)/changes." |
268 | + |
269 | +linkcheck: |
270 | + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck |
271 | + @echo |
272 | + @echo "Link check complete; look for any errors in the above output " \ |
273 | + "or in $(BUILDDIR)/linkcheck/output.txt." |
274 | + |
275 | +doctest: |
276 | + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest |
277 | + @echo "Testing of doctests in the sources finished, look at the " \ |
278 | + "results in $(BUILDDIR)/doctest/output.txt." |
279 | |
280 | === added directory 'doc/_static' |
281 | === added directory 'doc/_templates' |
282 | === added file 'doc/conf.py' |
283 | --- doc/conf.py 1970-01-01 00:00:00 +0000 |
284 | +++ doc/conf.py 2012-08-21 03:50:43 +0000 |
285 | @@ -0,0 +1,35 @@ |
286 | +import sys |
287 | +from os import path |
288 | + |
289 | +tree = path.dirname(path.dirname(path.abspath(__file__))) |
290 | +sys.path.insert(0, tree) |
291 | + |
292 | +import usercouch |
293 | + |
294 | + |
295 | +# Project info |
296 | +project = 'UserCouch' |
297 | +copyright = '2012, Novacut Inc' |
298 | +version = usercouch.__version__[:5] |
299 | +release = usercouch.__version__ |
300 | + |
301 | + |
302 | +# General config |
303 | +needs_sphinx = '1.1' |
304 | +extensions = [ |
305 | + 'sphinx.ext.autodoc', |
306 | + 'sphinx.ext.doctest', |
307 | + 'sphinx.ext.coverage', |
308 | +] |
309 | +templates_path = ['_templates'] |
310 | +source_suffix = '.rst' |
311 | +master_doc = 'index' |
312 | +exclude_patterns = ['_build'] |
313 | +pygments_style = 'sphinx' |
314 | + |
315 | + |
316 | +# HTML config |
317 | +html_theme = 'default' |
318 | +html_static_path = ['_static'] |
319 | +htmlhelp_basename = 'UserCouchdoc' |
320 | + |
321 | |
322 | === added file 'doc/index.rst' |
323 | --- doc/index.rst 1970-01-01 00:00:00 +0000 |
324 | +++ doc/index.rst 2012-08-21 03:50:43 +0000 |
325 | @@ -0,0 +1,49 @@ |
326 | +UserCouch |
327 | +========= |
328 | + |
329 | +`UserCouch`_ is a Python3 library for starting per-user `CouchDB`_ instances, |
330 | +including throw-away instances for unit testing. It's especially easy to use |
331 | +with `Microfiber`_, for example: |
332 | + |
333 | +>>> from usercouch.misc import TempCouch |
334 | +>>> from microfiber import Database |
335 | +>>> tmpcouch = TempCouch() |
336 | +>>> env = tmpcouch.bootstrap() |
337 | +>>> db = Database('mydb', env) |
338 | +>>> db.put(None) # Create the database |
339 | +{'ok': True} |
340 | +>>> db.post({'_id': 'mydoc'}) # Create a document |
341 | +{'rev': '1-967a00dff5e02add41819138abb3284d', 'ok': True, 'id': 'mydoc'} |
342 | + |
343 | +UserCouch is being developed as part of the `Novacut`_ project. UserCouch |
344 | +packages are available for Ubuntu in the `Novacut Stable Releases PPA`_ and the |
345 | +`Novacut Daily Builds PPA`_. |
346 | + |
347 | +If you have questions or need help getting started with UserCouch, please stop |
348 | +by the `#novacut`_ IRC channel on freenode. |
349 | + |
350 | +UserCouch is licensed `LGPLv3+`_. |
351 | + |
352 | + |
353 | +Contents: |
354 | + |
355 | +.. toctree:: |
356 | + :maxdepth: 2 |
357 | + |
358 | + tutorial |
359 | + usercouch |
360 | + usercouch_misc |
361 | + |
362 | + |
363 | + |
364 | +.. _`UserCouch`: https://launchpad.net/usercouch |
365 | +.. _`CouchDB`: http://couchdb.apache.org/ |
366 | +.. _`Microfiber`: https://launchpad.net/microfiber |
367 | +.. _`LGPLv3+`: http://www.gnu.org/licenses/lgpl-3.0.html |
368 | + |
369 | + |
370 | +.. _`Novacut`: https://wiki.ubuntu.com/Novacut |
371 | +.. _`Novacut Stable Releases PPA`: https://launchpad.net/~novacut/+archive/stable |
372 | +.. _`Novacut Daily Builds PPA`: https://launchpad.net/~novacut/+archive/daily |
373 | +.. _`#novacut`: http://webchat.freenode.net/?channels=novacut |
374 | + |
375 | |
376 | === added file 'doc/tutorial.rst' |
377 | --- doc/tutorial.rst 1970-01-01 00:00:00 +0000 |
378 | +++ doc/tutorial.rst 2012-08-21 03:50:43 +0000 |
379 | @@ -0,0 +1,154 @@ |
380 | +UserCouch Tutorial |
381 | +================== |
382 | + |
383 | +.. py:currentmodule:: usercouch |
384 | + |
385 | +To create a :class:`UserCouch` instance, you must supply the *basedir* |
386 | +directory in which all the `CouchDB`_ data will be stored. For example: |
387 | + |
388 | +>>> from usercouch import UserCouch |
389 | +>>> mycouch = UserCouch('/home/jderose/.usercouch') |
390 | + |
391 | +Then call :meth:`UserCouch.bootstrap()` to create the one-time configuration |
392 | +and start CouchDB: |
393 | + |
394 | +>>> env = mycouch.bootstrap() |
395 | + |
396 | +The returned *env* will be a ``dict`` with an extensible environment |
397 | +following the same conventions as `Microfiber`_. |
398 | + |
399 | +Because this is a per-user CouchDB instance, a random port is chosen when you |
400 | +call :meth:`UserCouch.bootstrap()`, and *env* will contain this port: |
401 | + |
402 | +>>> env['port'] |
403 | +53206 |
404 | + |
405 | +The *env* also contains the HTTP URL of the CouchDB instance: |
406 | + |
407 | +>>> env['url'] |
408 | +'http://localhost:53206/' |
409 | + |
410 | +By default, :meth:`UserCouch.bootstrap()` will configure CouchDB for basic |
411 | +HTTP auth, using a random username and password each time: |
412 | + |
413 | +>>> env['basic'] |
414 | +{'username': '72UT4WBTH3HFGT4S5OMYXGWA', 'password': 'WP5DUTBRQRYXFYKGZQ4MPDHB'} |
415 | + |
416 | +Normally the CouchDB process will be automatically killed for you when the |
417 | +:class:`UserCouch` instance is garbage collected. However, in certain |
418 | +circumstances, you may need to manually call :meth:`UserCouch.kill()`. |
419 | + |
420 | + |
421 | + |
422 | +Bootstrap Options |
423 | +----------------- |
424 | + |
425 | +The :meth:`UserCouch.bootstrap()` *auth* kwarg can be ``'open'``, ``'basic'``, |
426 | +or ``'oauth'``. As noted above, it defaults to ``'basic'``. |
427 | + |
428 | +If you use ``auth='open'``, you'll get an *env* similar to this: |
429 | + |
430 | +>>> { |
431 | +... 'port': 41505, |
432 | +... 'url': 'http://localhost:41505/', |
433 | +... } |
434 | + |
435 | +If you use ``auth='basic'``, you'll get an *env* similar to this: |
436 | + |
437 | +>>> { |
438 | +... 'port': 57910, |
439 | +... 'url': 'http://localhost:57910/', |
440 | +... 'basic': { |
441 | +... 'username': 'BKBTG7MX5Z6CTWHBOBXOX63S', |
442 | +... 'password': 'YGQQRSDMIF6GTZ6JMETWPUUE', |
443 | +... }, |
444 | +... } |
445 | + |
446 | +If you use ``auth='oauth'``, you'll get an *env* similar to this: |
447 | + |
448 | +>>> { |
449 | +... 'port': 56618 |
450 | +... 'url': 'http://localhost:56618/', |
451 | +... 'basic': { |
452 | +... 'username': 'MAO5VQIKCJWS7NGGMV2IYC7S', |
453 | +... 'password': 'A7RDFDAMUFFFBP72VWSGK5QD', |
454 | +... }, |
455 | +... 'oauth': { |
456 | +... 'consumer_key': 'MDWS6LVY4N7TSBKCNW4UWMVW', |
457 | +... 'consumer_secret': 'DA2TGMAUTRASC67ZZPVJAXYY', |
458 | +... 'token': 'PU7WWZNC3RJDX3CAOW3Q6TZW', |
459 | +... 'token_secret': 'H7XPTS2QHKYFQ4Z35NSKF3FR', |
460 | +... }, |
461 | +... } |
462 | + |
463 | + |
464 | + |
465 | +The Lockfile |
466 | +------------ |
467 | + |
468 | +The :class:`UserCouch` instance will store all the CouchDB data within the |
469 | +*basedir* you provide. To prevent multiple :class:`UserCouch` instances from |
470 | +starting multiple CouchDB instances pointing at the same database files, a |
471 | +lockfile is used. |
472 | + |
473 | +If the lock cannot be aquired, a :exc:`LockError` is raised: |
474 | + |
475 | +>>> mycouch2 = UserCouch('/home/jderose/.usercouch') |
476 | +Traceback (most recent call last): |
477 | + ... |
478 | +usercouch.LockError: cannot acquire exclusive lock on '/home/jderose/.usercouch/lockfile' |
479 | + |
480 | +Note that it's perfectly fine for multiple :class:`UserCouch` instances to be running |
481 | +simultaneously as long as each uses its own *basedir*. |
482 | + |
483 | + |
484 | + |
485 | +Unit Testing |
486 | +------------ |
487 | + |
488 | +.. py:currentmodule:: usercouch.misc |
489 | + |
490 | +When unit testing or experimenting with CouchDB, it's handy to have throw-away |
491 | +CouchDB instances. That way your tests start with CouchDB in a known state, |
492 | +plus you can't accidentally hose your production data. |
493 | + |
494 | +The :mod:`usercouch.misc` module contains two classes aimed at unit testing. |
495 | + |
496 | +The first is the :class:`TempCouch` class, which you can use like this: |
497 | + |
498 | +>>> from usercouch.misc import TempCouch |
499 | +>>> tmpcouch = TempCouch() |
500 | +>>> env = tmpcouch.bootstrap() |
501 | + |
502 | +:class:`TempCouch` is a :class:`usercouch.UserCouch` subclass that creates a |
503 | +one-time temporary directory to be used as the *basedir*. When the |
504 | +:class:`TempCouch` instance is garbage collected, this temporary directory |
505 | +(and any files it contains) are automatically deleted. |
506 | + |
507 | +The second is the :class:`CouchTestCase` class. It's a ``unittest.TestCase`` |
508 | +subclass with ``setUp()`` and ``tearDown()`` methods that create and destroy |
509 | +a :class:`TempCouch` instance for each test. |
510 | + |
511 | +The typical :class:`CouchTestCase` pattern looks like this: |
512 | + |
513 | +>>> from usercouch.misc import CouchTestCase |
514 | +>>> from microfiber import Database |
515 | +>>> |
516 | +>>> class TestFoo(CouchTestCase): |
517 | +... def test_bar(self): |
518 | +... db = Database('mydb', self.env) |
519 | +... self.assertEqual(db.put(None), {'ok': True}) |
520 | +... |
521 | +... def test_baz(self): |
522 | +... db = Database('mydb', self.env) |
523 | +... self.assertEqual(db.put(None), {'ok': True}) |
524 | +... |
525 | + |
526 | +Because a new :class:`TempCouch` is created by ``setUp()`` prior to running |
527 | +each test method, both the ``test_bar()`` and ``test_baz()`` tests will pass. |
528 | + |
529 | + |
530 | + |
531 | +.. _`Microfiber`: https://launchpad.net/microfiber |
532 | +.. _`CouchDB`: http://couchdb.apache.org/ |
533 | + |
534 | |
535 | === added file 'doc/usercouch.rst' |
536 | --- doc/usercouch.rst 1970-01-01 00:00:00 +0000 |
537 | +++ doc/usercouch.rst 2012-08-21 03:50:43 +0000 |
538 | @@ -0,0 +1,127 @@ |
539 | +:mod:`usercouch` API Reference |
540 | +============================== |
541 | + |
542 | +.. py:module:: usercouch |
543 | + :synopsis: Start per-user CouchDB instances for fun, profit, unit testing |
544 | + |
545 | + |
546 | +Exceptions |
547 | +---------- |
548 | + |
549 | +.. exception:: LockError(lockfile) |
550 | + |
551 | + Raised when lock cannot be acquired when creating a :class:`UserCouch`. |
552 | + |
553 | + .. attribute:: lockfile |
554 | + |
555 | + The path of the lockfile |
556 | + |
557 | + |
558 | + |
559 | +:class:`UserCouch` class |
560 | +------------------------ |
561 | + |
562 | +.. class:: UserCouch(basedir) |
563 | + |
564 | + Starts a per-user CouchDB instance. |
565 | + |
566 | + For example: |
567 | + |
568 | + >>> mycouch = UserCouch('/home/jderose/.usercouch') |
569 | + >>> env = mycouch.bootstrap() |
570 | + |
571 | + .. attribute:: basedir |
572 | + |
573 | + The directory provided when instance was created. |
574 | + |
575 | + .. method:: bootstrap(auth='basic', overrides=None) |
576 | + |
577 | + Create the one-time configuration and start CouchDB. |
578 | + |
579 | + *auth* must be ``'open'``, ``'basic'``, or ``'oauth'``. |
580 | + |
581 | + The return value is an *env* dictionary that follows the |
582 | + `Microfiber`_ conventions. |
583 | + |
584 | + .. method:: start() |
585 | + |
586 | + Start (or re-start) CouchDB. |
587 | + |
588 | + .. method:: kill() |
589 | + |
590 | + Kill the CouchDB process. |
591 | + |
592 | + Normally this method will be called automatically when the |
593 | + :class:`UserCouch` instance is garbage collected, but in certain |
594 | + circumstances you may need to explicitly call it. |
595 | + |
596 | + .. method:: isalive() |
597 | + |
598 | + Make an HTTP request to see if the CouchDB server is alive. |
599 | + |
600 | + .. method:: check() |
601 | + |
602 | + Test if the CouchDB server is alive, restart it if not. |
603 | + |
604 | + .. method:: crash() |
605 | + |
606 | + Terminate the CouchDB process to simulate a CouchDB crash. |
607 | + |
608 | + |
609 | + |
610 | +Helper functions |
611 | +---------------- |
612 | + |
613 | +.. function:: random_b32(numbytes=15) |
614 | + |
615 | + Return a random 120-bit base32-encoded random string. |
616 | + |
617 | + The ``str`` will be 24-characters long, URL and file-system safe. For |
618 | + example: |
619 | + |
620 | + >>> random_b32() |
621 | + '6NOLCDV3EQCPJDL43STIZIHN' |
622 | + |
623 | + |
624 | +.. function:: random_oauth() |
625 | + |
626 | + Return a ``dict`` containing random OAuth 1a tokens. |
627 | + |
628 | + For example: |
629 | + |
630 | + >>> random_oauth() |
631 | + { |
632 | + 'consumer_key': 'YXOIWEJOQW4VRGNNEGT6SQYN', |
633 | + 'consumer_secret': '6KFO4Y4OZQT3YGJ4ZUYOR5I2', |
634 | + 'token': 'DADIN54ILMCASM2W6S77Q2KW', |
635 | + 'token_secret': '6T2BFYDJLES7LPFNJOFPEBQO' |
636 | + } |
637 | + |
638 | + |
639 | +.. function:: random_salt() |
640 | + |
641 | + Return a 128-bit hex-encoded random salt for use by :func:`couch_hashed()`. |
642 | + |
643 | + For example: |
644 | + |
645 | + >>> random_salt() |
646 | + 'da52c844db4b8bd88ebb96d72542457a' |
647 | + |
648 | + |
649 | +.. function:: couch_hashed(password, salt) |
650 | + |
651 | + Hash *password* using *salt*. |
652 | + |
653 | + This returns a CouchDB-style hashed password to be used in the session.ini |
654 | + file. For example: |
655 | + |
656 | + >>> couch_hashed('secret', 'da52c844db4b8bd88ebb96d72542457a') |
657 | + '-hashed-ddf425840fd7f81cc45d9e9f5aa484d1f60964a9,da52c844db4b8bd88ebb96d72542457a' |
658 | + |
659 | + Typically :class:`UserCouch` is used with a per-session random password, |
660 | + so this function means that the clear-text of the password is only stored |
661 | + in memory, is never written to disk. |
662 | + |
663 | + |
664 | + |
665 | +.. _`Microfiber`: https://launchpad.net/microfiber |
666 | |
667 | === added file 'doc/usercouch_misc.rst' |
668 | --- doc/usercouch_misc.rst 1970-01-01 00:00:00 +0000 |
669 | +++ doc/usercouch_misc.rst 2012-08-21 03:50:43 +0000 |
670 | @@ -0,0 +1,45 @@ |
671 | +:mod:`usercouch.misc` API Reference |
672 | +=================================== |
673 | + |
674 | +.. py:module:: usercouch.misc |
675 | + :synopsis: Misc helpers for unit testing |
676 | + |
677 | +The :mod:`usercouch.misc` module provides some helper classes to make it easy |
678 | +to use good CouchDB unit testing idioms. |
679 | + |
680 | + |
681 | +:class:`TempCouch` class |
682 | +------------------------ |
683 | + |
684 | +.. class:: TempCouch |
685 | + |
686 | + A throw-away CouchDB that stores files in a temporary directory. |
687 | + |
688 | + |
689 | + |
690 | +:class:`CouchTestCase` class |
691 | +----------------------------- |
692 | + |
693 | +If subclasses need to provide their own ``setUp()`` or ``tearDown()`` methods, |
694 | +be sure to call the super methods. |
695 | + |
696 | +.. class:: CouchTestCase |
697 | + |
698 | + Base-class for CouchDB using unit tests. |
699 | + |
700 | + .. attribute:: tmpcouch |
701 | + |
702 | + The :class:`TempCouch` instance created by :meth:`CouchTestCase.setUp()`. |
703 | + |
704 | + .. attribute:: env |
705 | + |
706 | + The *env* returned by :meth:`usercouch.UserCouch.bootstrap()`. |
707 | + |
708 | + .. method:: setUp() |
709 | + |
710 | + Create and bootstrap a :class:`TempCouch` instance. |
711 | + |
712 | + .. method:: tearDown() |
713 | + |
714 | + Destroy the :class:`TempCouch` instance. |
715 | + |
716 | |
717 | === modified file 'usercouch/__init__.py' |
718 | --- usercouch/__init__.py 2012-07-15 05:41:06 +0000 |
719 | +++ usercouch/__init__.py 2012-08-21 03:50:43 +0000 |
720 | @@ -122,11 +122,14 @@ |
721 | Hash *password* using *salt*. |
722 | |
723 | This returns a CouchDB-style hashed password to be use in the session.ini |
724 | - file. |
725 | + file. For example: |
726 | + |
727 | + >>> couch_hashed('secret', 'da52c844db4b8bd88ebb96d72542457a') |
728 | + '-hashed-ddf425840fd7f81cc45d9e9f5aa484d1f60964a9,da52c844db4b8bd88ebb96d72542457a' |
729 | |
730 | Typically `UserCouch` is used with a per-session random password, so this |
731 | - function means that the clear-text of the password is only stored in memory, |
732 | - is never written to disk. |
733 | + function means that the clear-text of the password is only stored in |
734 | + memory, is never written to disk. |
735 | """ |
736 | assert len(salt) == 32 |
737 | data = (password + salt).encode('utf-8') |