Skip to content
Browse files

first pep8 run

Some long line lengths still exist, but I want
to avoid too heavily refactoring/moving bits
around for accomodating those just yet.

Once we have better test coverage, I will get
it all < 80 char line lengths.
  • Loading branch information...
1 parent 7c07ef3 commit 8756ba7a5f35b074d5a46df852280d0d41ffc039 @BigBlueHat BigBlueHat committed Dec 16, 2013
View
4 Couchapp.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
from couchapp.dispatch import run
if __name__ == '__main__':
- run()
+ run()
View
4 couchapp/__init__.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
version_info = (1, 0, 1)
-__version__ = ".".join(map(str, version_info))
+__version__ = ".".join(map(str, version_info))
View
5 couchapp/autopush/__init__.py
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
-DEFAULT_UPDATE_DELAY = 5 # update delay in seconds
-
+DEFAULT_UPDATE_DELAY = 5 # update delay in seconds
View
14 couchapp/autopush/command.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
import logging
@@ -12,12 +12,14 @@
from couchapp.localdoc import document
if sys.platform == "win32" or os.name == "nt":
- from couchapp.autopush.winwatcher import WinCouchappWatcher as CouchappWatcher
+ from couchapp.autopush.winwatcher import WinCouchappWatcher as \
+ CouchappWatcher
else:
from couchapp.autopush.watcher import CouchappWatcher
log = logging.getLogger(__name__)
+
def autopush(conf, path, *args, **opts):
doc_path = None
dest = None
@@ -26,21 +28,19 @@ def autopush(conf, path, *args, **opts):
if args:
dest = args[0]
else:
- doc_path = os.path.normpath(os.path.join(os.getcwd(),
- args[0]))
+ doc_path = os.path.normpath(os.path.join(os.getcwd(), args[0]))
dest = args[1]
if doc_path is None:
raise AppError("You aren't in a couchapp.")
conf.update(doc_path)
- doc = document(doc_path, create=False,
- docid=opts.get('docid'))
+ doc = document(doc_path, create=False, docid=opts.get('docid'))
dbs = conf.get_dbs(dest)
update_delay = int(opts.get('update_delay', DEFAULT_UPDATE_DELAY))
noatomic = opts.get('no_atomic', False)
watcher = CouchappWatcher(doc, dbs, update_delay=update_delay,
- noatomic=noatomic)
+ noatomic=noatomic)
watcher.run()
View
10 couchapp/autopush/handler.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
import logging
@@ -16,10 +16,11 @@
log = logging.getLogger(__name__)
+
class CouchappEventHandler(FileSystemEventHandler):
- def __init__(self, doc, dbs, update_delay=DEFAULT_UPDATE_DELAY,
- noatomic=False):
+ def __init__(self, doc, dbs, update_delay=DEFAULT_UPDATE_DELAY,
+ noatomic=False):
super(CouchappEventHandler, self).__init__()
self.update_delay = update_delay
@@ -49,8 +50,7 @@ def maybe_update(self):
diff = time.time() - self.last_update
if diff >= self.update_delay:
log.info("synchronize changes")
- self.doc.push(self.dbs, noatomic=self.noatomic,
- noindex=True)
+ self.doc.push(self.dbs, noatomic=self.noatomic, noindex=True)
self.last_update = None
def dispatch(self, ev):
View
29 couchapp/autopush/watcher.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
import logging
@@ -20,23 +20,22 @@
class CouchappWatcher(object):
SIG_QUEUE = []
- SIGNALS = map(
- lambda x: getattr(signal, "SIG%s" % x),
- "QUIT INT TERM".split())
+ SIGNALS = map(lambda x: getattr(signal, "SIG%s" % x),
+ "QUIT INT TERM".split())
- SIG_NAMES = dict(
- (getattr(signal, name), name[3:].lower()) \
- for name in dir(signal) \
- if name[:3] == "SIG" and name[3] != "_")
+ SIG_NAMES = dict((getattr(signal, name), name[3:].lower())
+ for name in dir(signal)
+ if name[:3] == "SIG" and name[3] != "_")
- def __init__(self, doc, dbs, update_delay=DEFAULT_UPDATE_DELAY,
- noatomic=False):
+ def __init__(self, doc, dbs, update_delay=DEFAULT_UPDATE_DELAY,
+ noatomic=False):
self.doc_path = absolute_path(doc.docdir)
self.event_handler = CouchappEventHandler(doc, dbs,
- update_delay=update_delay, noatomic=noatomic)
+ update_delay=update_delay,
+ noatomic=noatomic)
self.observer = Observer()
self.observer.schedule(self.event_handler,
- self.doc_path, recursive=True)
+ self.doc_path, recursive=True)
def init_signals(self):
"""\
@@ -72,7 +71,7 @@ def run(self):
try:
sig = self.SIG_QUEUE.pop(0) if len(self.SIG_QUEUE) else None
if sig is None:
- self.event_handler.maybe_update()
+ self.event_handler.maybe_update()
elif sig in self.SIG_NAMES:
signame = self.SIG_NAMES.get(sig)
handler = getattr(self, "handle_%s" % signame, None)
@@ -89,6 +88,6 @@ def run(self):
return 0
except Exception, e:
log.info("unhandled exception in main loop:\n%s" %
- traceback.format_exc())
- return -1
+ traceback.format_exc())
+ return -1
self.observer.join()
View
15 couchapp/autopush/winwatcher.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
import logging
@@ -13,15 +13,17 @@
log = logging.getLogger(__name__)
+
class WinCouchappWatcher(object):
- def __init__(self, doc, dbs, update_delay=DEFAULT_UPDATE_DELAY,
- noatomic=False):
+ def __init__(self, doc, dbs, update_delay=DEFAULT_UPDATE_DELAY,
+ noatomic=False):
self.doc_path = absolute_path(doc.docdir)
self.event_handler = CouchappEventHandler(doc, dbs,
- update_delay=update_delay, noatomic=noatomic)
+ update_delay=update_delay,
+ noatomic=noatomic)
self.observer = Observer()
- self.observer.schedule(self.event_handler,
- self.doc_path, recursive=True)
+ self.observer.schedule(self.event_handler, self.doc_path,
+ recursive=True)
def run(self):
log.info("Starting to listen changes in '%s'", self.doc_path)
@@ -33,4 +35,3 @@ def run(self):
except (SystemExit, KeyboardInterrupt):
self.observer.stop()
self.observer.join()
-
View
202 couchapp/client.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
from __future__ import with_statement
@@ -25,31 +25,33 @@
from restkit.filters import OAuthFilter
from couchapp import __version__
-from couchapp.errors import ResourceNotFound, ResourceConflict,\
-PreconditionFailed, RequestFailed, BulkSaveError, Unauthorized, \
-InvalidAttachment, AppError
+from couchapp.errors import ResourceNotFound, ResourceConflict, \
+ PreconditionFailed, RequestFailed, BulkSaveError, Unauthorized, \
+ InvalidAttachment, AppError
from couchapp.util import json
USER_AGENT = "couchapp/%s" % __version__
aliases = {
'id': '_id',
- 'rev': '_rev'
+ 'rev': '_rev'
}
UNKNOWN_VERSION = tuple()
logger = logging.getLogger(__name__)
+
class CouchdbResponse(ClientResponse):
-
+
@property
def json_body(self):
try:
return json.loads(self.body_string())
except ValueError:
return self.body
+
class CouchdbResource(Resource):
def __init__(self, uri="http://127.0.0.1:5984", **client_opts):
@@ -60,44 +62,44 @@ def __init__(self, uri="http://127.0.0.1:5984", **client_opts):
@param uri: str, full uri to the server.
"""
client_opts['response_class'] = CouchdbResponse
-
+
Resource.__init__(self, uri=uri, **client_opts)
self.safe = ":/%"
-
+
def copy(self, path=None, headers=None, **params):
""" add copy to HTTP verbs """
return self.request('COPY', path=path, headers=headers, **params)
-
+
def request(self, method, path=None, payload=None, headers=None,
- params_dict=None, **params):
- """ Perform HTTP call to the couchdb server and manage
+ params_dict=None, **params):
+ """ Perform HTTP call to the couchdb server and manage
JSON conversions, support GET, POST, PUT and DELETE.
-
- Usage example, get infos of a couchdb server on
+
+ Usage example, get infos of a couchdb server on
http://127.0.0.1:5984 :
import couchdbkit.CouchdbResource
resource = couchdbkit.CouchdbResource()
infos = resource.request('GET')
- @param method: str, the HTTP action to be performed:
+ @param method: str, the HTTP action to be performed:
'GET', 'HEAD', 'POST', 'PUT', or 'DELETE'
@param path: str or list, path to add to the uri
@param data: str or string or any object that could be
converted to JSON.
@param headers: dict, optional headers that will
be added to HTTP request.
@param raw: boolean, response return a Response object
- @param params: Optional parameterss added to the request.
- Parameterss are for example the parameters for a view. See
- `CouchDB View API reference
+ @param params: Optional parameterss added to the request.
+ Parameterss are for example the parameters for a view. See
+ `CouchDB View API reference
<http://wiki.apache.org/couchdb/HTTP_view_API>`_ for example.
-
- @return: tuple (data, resp), where resp is an `httplib2.Response`
+
+ @return: tuple (data, resp), where resp is an `httplib2.Response`
object and data a python object (often a dict).
"""
-
+
headers = headers or {}
headers.setdefault('Accept', 'application/json')
headers.setdefault('User-Agent', USER_AGENT)
@@ -109,7 +111,7 @@ def request(self, method, path=None, payload=None, headers=None,
try:
return Resource.request(self, method, path=path,
- payload=payload, headers=headers, **params)
+ payload=payload, headers=headers, **params)
except ResourceError, e:
msg = getattr(e, 'msg', '')
if e.response and msg:
@@ -118,105 +120,106 @@ def request(self, method, path=None, payload=None, headers=None,
msg = json.loads(str(msg))
except ValueError:
pass
-
+
if type(msg) is dict:
error = msg.get('reason')
else:
error = msg
if e.status_int == 404:
raise ResourceNotFound(error, http_code=404,
- response=e.response)
+ response=e.response)
elif e.status_int == 409:
raise ResourceConflict(error, http_code=409,
- response=e.response)
+ response=e.response)
elif e.status_int == 412:
raise PreconditionFailed(error, http_code=412,
- response=e.response)
-
+ response=e.response)
+
elif e.status_int in (401, 403):
raise Unauthorized(e)
else:
raise RequestFailed(str(e))
except Exception, e:
raise RequestFailed("unknown error [%s]" % str(e))
-
+
+
def couchdb_version(server_uri):
res = CouchdbResource(server_uri)
-
+
try:
resp = res.get()
except Exception, e:
return UNKNOWN_VERSION
-
+
version = resp.json_body["version"]
t = []
for p in version.split("."):
try:
t.append(int(p))
except ValueError:
continue
-
- return tuple(t)
+
+ return tuple(t)
+
class Uuids(object):
-
+
def __init__(self, uri, max_uuids=1000, **client_opts):
- self.res = CouchdbResource( uri=uri, **client_opts)
+ self.res = CouchdbResource(uri=uri, **client_opts)
self._uuids = []
self.max_uuids = max_uuids
-
+
def next(self):
if not self._uuids:
self.fetch_uuids()
self._uuids, res = self._uuids[:-1], self._uuids[-1]
return res
-
+
def __iter__(self):
return self
-
+
def fetch_uuids(self):
count = self.max_uuids - len(self._uuids)
resp = self.res.get('/_uuids', count=count)
self._uuids += resp.json_body['uuids']
+
class Database(object):
""" Object that abstract access to a CouchDB database
A Database object can act as a Dict object.
"""
-
+
def __init__(self, uri, create=True, **client_opts):
if uri.endswith("/"):
uri = uri[:-1]
self.raw_uri = uri
if uri.startswith("desktopcouch://"):
if not desktopcouch:
- raise AppError("Desktopcouch isn't available on this"+
- "machine. You can't access to %s" % uri)
+ raise AppError("Desktopcouch isn't available on this" +
+ "machine. You can't access to %s" % uri)
uri = "http://localhost:%s/%s" % (
desktopcouch.find_port(), uri[15:])
ctx = local_files.DEFAULT_CONTEXT
oauth_tokens = local_files.get_oauth_tokens(ctx)
consumer = oauth.Consumer(oauth_tokens["consumer_key"],
- oauth_tokens["consumer_secret"])
+ oauth_tokens["consumer_secret"])
token = oauth.Token(oauth_tokens["token"],
- oauth_tokens["token_secret"])
-
+ oauth_tokens["token_secret"])
+
oauth_filter = OAuthFilter("*", consumer, token)
filters = client_opts.get("filters") or []
filters.append(oauth_filter)
client_opts["filters"] = filters
-
self.res = CouchdbResource(uri=uri, **client_opts)
self.server_uri, self.dbname = uri.rsplit('/', 1)
-
+
self.uuids = Uuids(self.server_uri, **client_opts)
-
-
+
if create:
# create the db
try:
@@ -226,7 +229,7 @@ def __init__(self, uri, create=True, **client_opts):
def delete(self):
self.res.delete()
-
+
def info(self):
"""
Get database information
@@ -236,13 +239,13 @@ def info(self):
@return: dict
"""
return self.res.get().json_body
-
+
def all_docs(self, **params):
"""
return all_docs
"""
return self.view('_all_docs', **params)
-
+
def open_doc(self, docid, wrapper=None, **params):
"""Open document from database
@@ -253,24 +256,24 @@ def open_doc(self, docid, wrapper=None, **params):
@param wrapper: callable. function that takes dict as a param.
Used to wrap an object.
@params params: Other params to pass to the uri (or headers)
-
+
@return: dict, representation of CouchDB document as
a dict.
"""
resp = self.res.get(escape_docid(docid), **params)
-
+
if wrapper is not None:
if not callable(wrapper):
raise TypeError("wrapper isn't a callable")
return wrapper(resp.json_body)
return resp.json_body
-
+
def save_doc(self, doc, encode=False, force_update=False, **params):
""" Save a document. It will use the `_id` member of the document
or request a new uuid from CouchDB. IDs are attached to
documents on the client side because POST has the curious property of
being automatically retried by proxies in the event of network
- segmentation and lost responses.
+ segmentation and lost responses.
@param doc: dict. doc is updated
with doc '_id' and '_rev' properties returned
@@ -280,14 +283,14 @@ def save_doc(self, doc, encode=False, force_update=False, **params):
@param encode: Encode attachments if needed (depends on couchdb version)
@return: new doc with updated revision an id
- """
+ """
if '_attachments' in doc and encode:
doc['_attachments'] = encode_attachments(doc['_attachments'])
-
+
headers = params.get('headers', {})
headers.setdefault('Content-Type', 'application/json')
params['headers'] = headers
-
+
if '_id' in doc:
docid = escape_docid(doc['_id'])
try:
@@ -305,15 +308,15 @@ def save_doc(self, doc, encode=False, force_update=False, **params):
resp = self.res.put(doc['_id'], payload=json_doc, **params)
except ResourceConflict:
resp = self.res.post(payload=json_doc, **params)
-
+
json_res = resp.json_body
doc1 = {}
for a, n in aliases.items():
if a in json_res:
doc1[n] = json_res[a]
doc.update(doc1)
return doc
-
+
def last_rev(self, docid):
""" Get last revision from docid (the '_rev' member)
@param docid: str, undecoded document id.
@@ -328,141 +331,141 @@ def last_rev(self, docid):
doc = self.open_doc(docid)
return doc['_rev']
-
def delete_doc(self, id_or_doc):
""" Delete a document
@param id_or_doc: docid string or document dict
-
+
"""
if isinstance(id_or_doc, types.StringType):
docid = id_or_doc
- resp = self.res.delete(escape_docid(id_or_doc),
- rev=self.last_rev(id_or_doc))
+ resp = self.res.delete(escape_docid(id_or_doc),
+ rev=self.last_rev(id_or_doc))
else:
docid = id_or_doc.get('_id')
if not docid:
raise ValueError('Not valid doc to delete (no doc id)')
rev = id_or_doc.get('_rev', self.last_rev(docid))
resp = self.res.delete(escape_docid(docid), rev=rev)
return resp.json_body
-
+
def save_docs(self, docs, all_or_nothing=False, use_uuids=True):
""" Bulk save. Modify Multiple Documents With a Single Request
@param docs: list of docs
@param use_uuids: add _id in doc who don't have it already set.
- @param all_or_nothing: In the case of a power failure, when the database
+ @param all_or_nothing: In the case of a power failure, when the database
restarts either all the changes will have been saved or none of them.
However, it does not do conflict checking, so the documents will
- @return doc lists updated with new revision or raise BulkSaveError
+ @return doc lists updated with new revision or raise BulkSaveError
exception. You can access to doc created and docs in error as properties
of this exception.
"""
-
+
def is_id(doc):
return '_id' in doc
-
+
if use_uuids:
noids = []
for k, g in itertools.groupby(docs, is_id):
if not k:
noids = list(g)
-
+
for doc in noids:
nextid = self.uuids.next()
if nextid:
doc['_id'] = nextid
-
- payload = { "docs": docs }
+
+ payload = {"docs": docs}
if all_or_nothing:
payload["all-or-nothing"] = True
-
+
# update docs
res = self.res.post('/_bulk_docs', payload=json.dumps(payload),
- headers={'Content-Type': 'application/json'})
-
+ headers={'Content-Type': 'application/json'})
+
json_res = res.json_body
errors = []
for i, r in enumerate(json_res):
if 'error' in r:
doc1 = docs[i]
- doc1.update({'_id': r['id'],
- '_rev': r['rev']})
+ doc1.update({'_id': r['id'],
+ '_rev': r['rev']})
errors.append(doc1)
else:
- docs[i].update({'_id': r['id'],
+ docs[i].update({'_id': r['id'],
'_rev': r['rev']})
-
+
if errors:
raise BulkSaveError(docs, errors)
-
+
def delete_docs(self, docs, all_or_nothing=False, use_uuids=True):
""" multiple doc delete."""
for doc in docs:
doc['_deleted'] = True
- return self.save_docs(docs, all_or_nothing=all_or_nothing,
- use_uuids=use_uuids)
+ return self.save_docs(docs, all_or_nothing=all_or_nothing,
+ use_uuids=use_uuids)
def fetch_attachment(self, id_or_doc, name, headers=None):
""" get attachment in a document
@param id_or_doc: str or dict, doc id or document dict
@param name: name of attachment default: default result
@param header: optionnal headers (like range)
-
+
@return: `couchdbkit.resource.CouchDBResponse` object
"""
if isinstance(id_or_doc, basestring):
docid = id_or_doc
else:
docid = id_or_doc['_id']
-
- return self.res.get("%s/%s" % (escape_docid(docid), name), headers=headers)
-
+
+ return self.res.get("%s/%s" % (escape_docid(docid), name),
+ headers=headers)
+
def put_attachment(self, doc, content=None, name=None, headers=None):
""" Add attachement to a document. All attachments are streamed.
@param doc: dict, document object
@param content: string, iterator, fileobj
@param name: name or attachment (file name).
- @param headers: optionnal headers like `Content-Length`
+ @param headers: optionnal headers like `Content-Length`
or `Content-Type`
@return: updated document object
"""
headers = {}
content = content or ""
-
+
if name is None:
if hasattr(content, "name"):
name = content.name
else:
- raise InvalidAttachment(
- 'You should provid a valid attachment name')
+ raise InvalidAttachment('You should provid a valid ' +
+ 'attachment name')
name = util.url_quote(name, safe="")
- res = self.res.put("%s/%s" % (escape_docid(doc['_id']), name),
- payload=content, headers=headers, rev=doc['_rev'])
+ res = self.res.put("%s/%s" % (escape_docid(doc['_id']), name),
+ payload=content, headers=headers, rev=doc['_rev'])
json_res = res.json_body
-
+
if 'ok' in json_res:
return doc.update(self.open_doc(doc['_id']))
return False
-
+
def delete_attachment(self, doc, name):
""" delete attachement to the document
-
+
@param doc: dict, document object in python
@param name: name of attachement
@return: updated document object
"""
name = util.url_quote(name, safe="")
- self.res.delete("%s/%s" % (escape_docid(doc['_id']), name),
+ self.res.delete("%s/%s" % (escape_docid(doc['_id']), name),
rev=doc['_rev']).json_body
return doc.update(self.open_doc(doc['_id']))
-
+
def view(self, view_name, **params):
try:
dname, vname = view_name.split("/")
@@ -476,6 +479,7 @@ def view(self, view_name, **params):
return self.res.get(path, **params).json_body
+
def encode_params(params):
""" encode parameters in json if needed """
_params = {}
@@ -487,8 +491,9 @@ def encode_params(params):
or not isinstance(value, basestring):
value = json.dumps(value).encode('utf-8')
_params[name] = value
- return _params
-
+ return _params
+
+
def escape_docid(docid):
if docid.startswith('/'):
docid = docid[1:]
@@ -497,7 +502,8 @@ def escape_docid(docid):
else:
docid = util.url_quote(docid, safe='')
return docid
-
+
+
def encode_attachments(attachments):
for k, v in attachments.iteritems():
if v.get('stub', False):
View
46 couchapp/clone_app.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
from __future__ import with_statement
@@ -26,10 +26,11 @@ def _replace_slash(name):
def _replace_slash(name):
return name
+
def clone(source, dest=None, rev=None):
"""
Clone an application from a design_doc given.
-
+
:attr design_doc: dict, the design doc retrieved from couchdb
if something was wrong.
"""
@@ -39,24 +40,22 @@ def clone(source, dest=None, rev=None):
except ValueError:
raise AppError("%s isn't a valid source" % source)
-
if not dest:
dest = docid
-
+
path = os.path.normpath(os.path.join(os.getcwd(), dest))
if not os.path.exists(path):
os.makedirs(path)
- db = client.Database(dburl[:-1], create=False)
+ db = client.Database(dburl[:-1], create=False)
if not rev:
doc = db.open_doc("_design/%s" % docid)
else:
doc = db.open_doc("_design/%s" % docid, rev=rev)
docid = doc['_id']
-
-
+
metadata = doc.get('couchapp', {})
-
+
# get manifest
manifest = metadata.get('manifest', {})
@@ -71,7 +70,7 @@ def clone(source, dest=None, rev=None):
for filename in manifest:
logger.debug("clone property: %s" % filename)
filepath = os.path.join(path, filename)
- if filename.endswith('/'):
+ if filename.endswith('/'):
if not os.path.isdir(filepath):
os.makedirs(filepath)
elif filename == "couchapp.json":
@@ -95,12 +94,11 @@ def clone(source, dest=None, rev=None):
except KeyError:
break
-
if isinstance(content, basestring):
_ref = md5(util.to_bytestring(content)).hexdigest()
if objects and _ref in objects:
content = objects[_ref]
-
+
if content.startswith('base64-encoded;'):
content = base64.b64decode(content[15:])
@@ -113,7 +111,7 @@ def clone(source, dest=None, rev=None):
filedir = os.path.dirname(filepath)
if not os.path.isdir(filedir):
os.makedirs(filedir)
-
+
util.write(filepath, content)
# remove the key from design doc
@@ -124,12 +122,11 @@ def clone(source, dest=None, rev=None):
del temp[key2]
break
temp = temp[key2]
-
-
+
# second pass for missing key or in case
# manifest isn't in app
for key in doc.iterkeys():
- if key.startswith('_'):
+ if key.startswith('_'):
continue
elif key in ('couchapp'):
app_meta = copy.deepcopy(doc['couchapp'])
@@ -153,17 +150,15 @@ def clone(source, dest=None, rev=None):
if not os.path.isdir(vs_item_dir):
os.makedirs(vs_item_dir)
for func_name, func in vs_item.iteritems():
- filename = os.path.join(vs_item_dir, '%s.js' %
- func_name)
+ filename = os.path.join(vs_item_dir, '%s.js' % func_name)
util.write(filename, func)
logger.warning("clone view not in manifest: %s" % filename)
elif key in ('shows', 'lists', 'filter', 'update'):
showpath = os.path.join(path, key)
if not os.path.isdir(showpath):
os.makedirs(showpath)
for func_name, func in doc[key].iteritems():
- filename = os.path.join(showpath, '%s.js' %
- func_name)
+ filename = os.path.join(showpath, '%s.js' % func_name)
util.write(filename, func)
logger.warning(
"clone show or list not in manifest: %s" % filename)
@@ -185,7 +180,7 @@ def clone(source, dest=None, rev=None):
value = base64.b64decode(content[15:])
util.write(fieldpath, value)
else:
- util.write_json(fieldpath + '.json', value)
+ util.write_json(fieldpath + '.json', value)
else:
value = doc[key]
if not isinstance(value, basestring):
@@ -195,32 +190,33 @@ def clone(source, dest=None, rev=None):
# save id
idfile = os.path.join(path, '_id')
util.write(idfile, doc['_id'])
-
+
util.write_json(os.path.join(path, '.couchapprc'), {})
if '_attachments' in doc: # process attachments
attachdir = os.path.join(path, '_attachments')
if not os.path.isdir(attachdir):
os.makedirs(attachdir)
-
+
for filename in doc['_attachments'].iterkeys():
if filename.startswith('vendor'):
attach_parts = util.split_path(filename)
vendor_attachdir = os.path.join(path, attach_parts.pop(0),
- attach_parts.pop(0), '_attachments')
+ attach_parts.pop(0),
+ '_attachments')
filepath = os.path.join(vendor_attachdir, *attach_parts)
else:
filepath = os.path.join(attachdir, filename)
filepath = _replace_slash(filepath)
currentdir = os.path.dirname(filepath)
if not os.path.isdir(currentdir):
os.makedirs(currentdir)
-
+
if signatures.get(filename) != util.sign(filepath):
resp = db.fetch_attachment(docid, filename)
with open(filepath, 'wb') as f:
for chunk in resp.body_stream():
f.write(chunk)
logger.debug("clone attachment: %s" % filename)
-
+
logger.info("%s cloned in %s" % (source, dest))
View
96 couchapp/commands.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
import logging
@@ -29,17 +29,19 @@ def hook(conf, path, hook_type, *args, **kwargs):
if hasattr(h, 'hook'):
h.hook(path, hook_type, *args, **kwargs)
+
def init(conf, path, *args, **opts):
if not args:
dest = os.getcwd()
else:
dest = os.path.normpath(os.path.join(os.getcwd(), args[0]))
-
+
if dest is None:
raise AppError("Unknown dest")
-
+
document(dest, True)
+
def push(conf, path, *args, **opts):
export = opts.get('export', False)
noatomic = opts.get('no_atomic', False)
@@ -62,28 +64,28 @@ def push(conf, path, *args, **opts):
dest = args[1]
if doc_path is None:
raise AppError("You aren't in a couchapp.")
-
+
conf.update(doc_path)
- doc = document(doc_path, create=False,
- docid=opts.get('docid'))
+ doc = document(doc_path, create=False, docid=opts.get('docid'))
if export:
if opts.get('output'):
util.write_json(opts.get('output'), str(doc))
else:
print str(doc)
return 0
dbs = conf.get_dbs(dest)
-
- hook(conf, doc_path, "pre-push", dbs=dbs)
+
+ hook(conf, doc_path, "pre-push", dbs=dbs)
doc.push(dbs, noatomic, browse, force)
hook(conf, doc_path, "post-push", dbs=dbs)
-
+
docspath = os.path.join(doc_path, '_docs')
if os.path.exists(docspath):
pushdocs(conf, docspath, dest, *args, **opts)
return 0
+
def pushapps(conf, source, dest, *args, **opts):
export = opts.get('export', False)
noatomic = opts.get('no_atomic', False)
@@ -93,8 +95,8 @@ def pushapps(conf, source, dest, *args, **opts):
source = os.path.normpath(os.path.join(os.getcwd(), source))
for d in os.listdir(source):
appdir = os.path.join(source, d)
- if os.path.isdir(appdir) and os.path.isfile(os.path.join(appdir,
- '.couchapprc')):
+ if os.path.isdir(appdir) and \
+ os.path.isfile(os.path.join(appdir, '.couchapprc')):
doc = document(appdir)
hook(conf, appdir, "pre-push", dbs=dbs, pushapps=True)
if export or not noatomic:
@@ -125,11 +127,12 @@ def pushapps(conf, source, dest, *args, **opts):
doc['_rev'] = db.last_rev(doc['_id'])
docs1.append(doc)
except ResourceNotFound:
- pass
+ pass
if docs1:
db.save_docs(docs1)
return 0
-
+
+
def pushdocs(conf, source, dest, *args, **opts):
export = opts.get('export', False)
noatomic = opts.get('no_atomic', False)
@@ -194,21 +197,23 @@ def pushdocs(conf, source, dest, *args, **opts):
doc['_rev'] = db.last_rev(doc['_id'])
docs1.append(doc)
except ResourceNotFound:
- pass
+ pass
if docs1:
db.save_docs(docs1)
return 0
-
+
+
def clone(conf, source, *args, **opts):
if len(args) > 0:
dest = args[0]
else:
- dest = None
+ dest = None
hook(conf, dest, "pre-clone", source=source)
clone_app.clone(source, dest, rev=opts.get('rev'))
hook(conf, dest, "post-clone", source=source)
return 0
+
def startapp(conf, *args, **opts):
if len(args) < 1:
raise AppError("Can't start an app, name or path is missing")
@@ -217,24 +222,25 @@ def startapp(conf, *args, **opts):
name = args[0]
dest = os.path.normpath(os.path.join(os.getcwd(), ".", name))
elif len(args) == 2:
-
+
name = args[1]
dest = os.path.normpath(os.path.join(args[0], args[1]))
if os.path.isfile(os.path.join(dest, ".couchapprc")):
- raise AppError("can't create an app at '%s'. One already exists"
- "here" % dest)
+ raise AppError("can't create an app at '%s'. One already exists here" %
+ dest)
generator.generate(dest, "startapp", name, **opts)
return 0
+
def generate(conf, path, *args, **opts):
dest = path
if len(args) < 1:
raise AppError("Can't generate function, name or path is missing")
-
+
if len(args) == 1:
- kind="app"
+ kind = "app"
name = args[0]
elif len(args) == 2:
kind = args[0]
@@ -243,19 +249,20 @@ def generate(conf, path, *args, **opts):
kind = args[0]
dest = args[1]
name = args[2]
-
+
if dest is None:
if kind == "app":
dest = os.path.normpath(os.path.join(os.getcwd(), ".", name))
opts['create'] = True
else:
raise AppError("You aren't in a couchapp.")
-
- hook(conf, dest, "pre-generate")
+
+ hook(conf, dest, "pre-generate")
generator.generate(dest, kind, name, **opts)
hook(conf, dest, "post-generate")
return 0
-
+
+
def vendor(conf, path, *args, **opts):
if len(args) < 1:
raise AppError("missing command")
@@ -267,28 +274,28 @@ def vendor(conf, path, *args, **opts):
raise AppError("missing source")
if len(args) == 1:
source = args.pop(0)
-
+
elif len(args) > 1:
dest = args.pop(0)
source = args.pop(0)
-
+
if dest is None:
raise AppError("You aren't in a couchapp.")
-
+
dest = os.path.normpath(os.path.join(os.getcwd(), dest))
hook(conf, dest, "pre-vendor", source=source, action="install")
vendor_install(conf, dest, source, *args, **opts)
hook(conf, dest, "post-vendor", source=source, action="install")
else:
vendorname = None
if len(args) == 1:
- vendorname=args.pop(0)
+ vendorname = args.pop(0)
elif len(args) >= 2:
dest = args.pop(0)
- vendorname=args.pop(0)
+ vendorname = args.pop(0)
if dest is None:
raise AppError("You aren't in a couchapp.")
-
+
dest = os.path.normpath(os.path.join(os.getcwd(), dest))
hook(conf, dest, "pre-vendor", name=vendorname, action="update")
vendor_update(conf, dest, vendorname, *args, **opts)
@@ -308,27 +315,28 @@ def browse(conf, path, *args, **opts):
dest = args[1]
if doc_path is None:
raise AppError("You aren't in a couchapp.")
-
+
conf.update(doc_path)
- doc = document(doc_path, create=False,
- docid=opts.get('docid'))
+ doc = document(doc_path, create=False, docid=opts.get('docid'))
dbs = conf.get_dbs(dest)
doc.browse(dbs)
+
def version(conf, *args, **opts):
from couchapp import __version__
-
+
print "Couchapp (version %s)" % __version__
print "Copyright 2008-2010 Benoît Chesneau <benoitc@e-engura.org>"
- print "Licensed under the Apache License, Version 2.0."
+ print "Licensed under the Apache License, Version 2.0."
print ""
if opts.get('help', False):
usage(conf, *args, **opts)
-
+
return 0
-
+
+
def usage(conf, *args, **opts):
if opts.get('version', False):
version(conf, *args, **opts)
@@ -356,11 +364,13 @@ def usage(conf, *args, **opts):
max_opt = max(cmd_options, key=lambda o: len(get_switch_str(o)))
max_opt_len = len(get_switch_str(max_opt))
for opt in cmd_options:
- print "\t\t%-*s %s" % (max_opt_len, get_switch_str(opt), opt[3])
+ print "\t\t%-*s %s" % (max_opt_len, get_switch_str(opt),
+ opt[3])
print ""
print ""
return 0
+
def get_switch_str(opt):
"""
Output just the '-r, --rev [VAL]' part of the option string.
@@ -391,11 +401,11 @@ def get_switch_str(opt):
('b', 'browse', False, "open the couchapp in the browser"),
('', 'force', False, "force attachments sending")
]
-
+
table = {
- "init":
- (init,
- [],
+ "init":
+ (init,
+ [],
"[COUCHAPPDIR]"),
"push":
(push,
View
53 couchapp/config.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
import os
@@ -11,61 +11,60 @@
class Config(object):
- """ main object to read configuration from ~/.couchapp.conf or
+ """ main object to read configuration from ~/.couchapp.conf or
.couchapprc/couchapp.json in the couchapp folder.
"""
DEFAULT_SERVER_URI = "http://127.0.0.1:5984"
-
+
DEFAULTS = dict(
- env = {},
- extensions = [],
- hooks = {}
-
+ env={},
+ extensions=[],
+ hooks={}
)
-
+
def __init__(self):
self.rc_path = util.rcpath()
self.global_conf = self.load(self.rc_path, self.DEFAULTS)
self.local_conf = {}
self.app_dir = util.findcouchapp(os.getcwd())
if self.app_dir:
self.local_conf = self.load_local(self.app_dir)
-
+
self.conf = self.global_conf.copy()
self.conf.update(self.local_conf)
def load(self, path, default=None):
""" load config """
conf = default
-
+
if isinstance(path, basestring):
paths = [path]
else:
paths = path
-
+
for p in paths:
if os.path.isfile(p):
try:
new_conf = util.read_json(p, use_environment=True,
- raise_on_error=True)
+ raise_on_error=True)
except ValueError:
raise AppError("Error while reading %s" % p)
conf.update(new_conf)
-
+
return conf
-
+
def load_local(self, app_path):
""" load local config """
paths = []
for fname in ['couchapp.json', '.couchapprc']:
paths.append(os.path.join(app_path, fname))
return self.load(paths, {})
-
+
def update(self, path):
self.conf = self.global_conf.copy()
self.local_conf.update(self.load_local(path))
- self.conf.update(self.local_conf)
-
+ self.conf.update(self.local_conf)
+
def get(self, key, default=None):
try:
return getattr(self, key)
@@ -79,22 +78,22 @@ def __getitem__(self, key):
except AttributeError:
pass
return self.conf[key]
-
+
def __getattr__(self, key):
try:
getattr(super(Config, self), key)
except AttributeError:
if key in self.conf:
return self.conf[key]
raise
-
+
def __contains__(self, key):
return (key in self.conf)
-
+
def __iter__(self):
for k in list(self.conf.keys()):
yield self[k]
-
+
@property
def extensions(self):
""" load extensions from conf """
@@ -105,7 +104,7 @@ def extensions(self):
script = util.load_py(uri, self)
extensions_list.append(script)
return extensions_list
-
+
@property
def hooks(self):
hooks = {}
@@ -117,7 +116,7 @@ def hooks(self):
scripts.append(util.hook_uri(uri, self))
hooks[hooktype] = scripts
return hooks
-
+
# TODO: add oauth management
def get_dbs(self, db_string=None):
db_string = db_string or ''
@@ -137,14 +136,15 @@ def get_dbs(self, db_string=None):
dburls = "%s/%s" % (self.DEFAULT_SERVER_URI, db_string)
if db_string in env:
dburls = env[db_string].get('db', dburls)
-
+
if isinstance(dburls, basestring):
dburls = [dburls]
- use_proxy = os.environ.get("http_proxy", "") != "" or os.environ.get("https_proxy", "") != ""
+ use_proxy = os.environ.get("http_proxy", "") != "" or \
+ os.environ.get("https_proxy", "") != ""
return [Database(dburl, use_proxy=use_proxy) for dburl in dburls]
-
+
def get_app_name(self, dbstring=None, default=None):
env = self.conf.get('env', {})
if not dbstring.startswith("http://"):
@@ -155,4 +155,3 @@ def get_app_name(self, dbstring=None, default=None):
elif not dbstring and 'default' in env:
return env['default'].get('name', default)
return default
-
View
68 couchapp/dispatch.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
import logging
@@ -13,37 +13,40 @@
logger = logging.getLogger(__name__)
+
class NullHandler(logging.Handler):
""" null log handler """
def emit(self, record):
pass
+
def set_logging(level=2):
"""
- Set level of logging, and choose where to display/save logs
+ Set level of logging, and choose where to display/save logs
(file or standard output).
"""
handler = logging.StreamHandler()
logger_ = logging.getLogger('couchapp')
logger_.setLevel(level * 10)
format = r"%(asctime)s [%(levelname)s] %(message)s"
datefmt = r"%Y-%m-%d %H:%M:%S"
-
+
handler.setFormatter(logging.Formatter(format, datefmt))
logger_.addHandler(handler)
-
+
+
def set_logging_level(level=2):
logger_ = logging.getLogger('couchapp')
logger_.setLevel(level * 10)
-
+
def run():
sys.exit(dispatch(sys.argv[1:]))
-
-
+
+
def dispatch(args):
set_logging()
-
+
try:
return _dispatch(args)
except AppError, e:
@@ -52,27 +55,28 @@ def dispatch(args):
logger.info("keyboard interrupt")
except Exception, e:
import traceback
-
+
logger.critical("%s\n\n%s" % (str(e), traceback.format_exc()))
return -1
-
+
+
def _dispatch(args):
conf = Config()
# update commands
for mod in conf.extensions:
cmdtable = getattr(mod, 'cmdtable', {})
commands.table.update(cmdtable)
-
+
cmd, globalopts, opts, args = _parse(args)
-
+
if globalopts["help"]:
del globalopts["help"]
return commands.usage(conf, *args, **globalopts)
elif globalopts["version"]:
del globalopts["version"]
return commands.version(conf, *args, **globalopts)
-
+
verbose = 2
if globalopts["debug"]:
verbose = 1
@@ -82,16 +86,16 @@ def _dispatch(args):
verbose = 1
elif globalopts["quiet"]:
verbose = 0
-
+
set_logging_level(verbose)
if cmd is None:
raise CommandLineError("unknown command")
fun = commands.table[cmd][0]
if cmd in commands.incouchapp:
- return fun(conf, conf.app_dir, *args, **opts)
-
- return fun(conf, *args, **opts)
+ return fun(conf, conf.app_dir, *args, **opts)
+
+ return fun(conf, *args, **opts)
def _parse(args):
@@ -101,7 +105,7 @@ def _parse(args):
args = parseopts(args, commands.globalopts, options)
except getopt.GetoptError, e:
raise CommandLineError(str(e))
-
+
if args:
cmd, args = args[0], args[1:]
if cmd in commands.table:
@@ -111,48 +115,50 @@ def _parse(args):
else:
cmd = "help"
cmdopts = list(commands.table[cmd][1])
-
+
for opt in commands.globalopts:
cmdopts.append((opt[0], opt[1], options[opt[1]], opt[3]))
-
- try:
+
+ try:
args = parseopts(args, cmdopts, cmdoptions)
except getopt.GetoptError, e:
raise CommandLineError((cmd, e))
-
+
for opt in cmdoptions.keys():
if opt in options:
options[opt] = cmdoptions[opt]
del cmdoptions[opt]
-
+
return cmd, options, cmdoptions, args
-
+
def parseopts(args, options, state):
namelist = []
shortlist = ''
argmap = {}
defmap = {}
-
+
for short, name, default, comment in options:
oname = name
name = name.replace('-', '_')
argmap['-' + short] = argmap['--' + oname] = name
defmap[name] = default
-
+
if isinstance(default, list):
state[name] = default[:]
else:
state[name] = default
-
+
if not (default is None or default is True or default is False):
- if short: short += ':'
- if oname: oname += '='
+ if short:
+ short += ':'
+ if oname:
+ oname += '='
if short:
shortlist += short
if name:
namelist.append(oname)
-
+
opts, args = getopt.getopt(args, shortlist, namelist)
for opt, val in opts:
name = argmap[opt]
@@ -165,5 +171,5 @@ def parseopts(args, options, state):
state[name].append(val)
elif t is type(None) or t is type(False):
state[name] = True
-
+
return args
View
38 couchapp/errors.py
@@ -1,48 +1,60 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
from restkit import ResourceError
+
class AppError(Exception):
""" raised when a application error appear """
-
+
+
class MacroError(Exception):
""" raised for macro errors"""
-
+
+
class VendorError(Exception):
""" vendor error """
-
+
+
class ResourceNotFound(ResourceError):
""" raised when a resource not found on CouchDB"""
-
+
+
class ResourceConflict(ResourceError):
""" raised when a conflict occured"""
+
class PreconditionFailed(ResourceError):
- """ precondition failed error """
-
-class RequestFailed(Exception):
+ """ precondition failed error """
+
+
+class RequestFailed(Exception):
""" raised when an http error occurs"""
-
+
+
class Unauthorized(Exception):
""" raised when not authorized to access to CouchDB"""
+
class CommandLineError(Exception):
""" error when a bad command line is passed"""
-
+
+
class BulkSaveError(Exception):
""" error raised when therer are conflicts in bulk save"""
-
+
def ___init__(self, docs, errors):
Exception.__init__(self)
self.docs = docs
self.errors = errors
-
+
+
class ScriptError(Exception):
""" exception raised in external script"""
-
+
+
class InvalidAttachment(Exception):
""" raised when attachment is invalid (bad size, ct, ..)"""
View
109 couchapp/generator.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
from __future__ import with_statement
@@ -19,22 +19,20 @@
logger = logging.getLogger(__name__)
-DEFAULT_APP_TREE = [
- '_attachments',
- 'lists',
- 'shows',
- 'updates',
- 'views']
+DEFAULT_APP_TREE = ['_attachments',
+ 'lists',
+ 'shows',
+ 'updates',
+ 'views']
def start_app(path):
try:
os.makedirs(path)
except OSError, e:
errno, message = e
- raise AppError("Can't create a CouchApp in %s: %s" % (
- path, message))
-
+ raise AppError("Can't create a CouchApp in %s: %s" % (path, message))
+
for n in DEFAULT_APP_TREE:
tp = os.path.join(path, n)
os.makedirs(tp)
@@ -43,18 +41,19 @@ def start_app(path):
if not os.path.isfile(fid):
with open(fid, 'wb') as f:
f.write('_design/%s' % os.path.split(path)[1])
-
+
doc = localdoc.document(path, create=True)
logger.info("%s created." % path)
+
def generate_app(path, template=None, create=False):
- """ Generates a CouchApp in app_dir
-
+ """ Generates a CouchApp in app_dir
+
:attr verbose: boolean, default False
- :return: boolean, dict. { 'ok': True } if ok, { 'ok': False, 'error': message }
+ :return: boolean, dict. { 'ok': True } if ok, { 'ok': False, 'error': message }
if something was wrong.
"""
-
+
TEMPLATES = ['app']
prefix = ''
if template is not None:
@@ -63,13 +62,12 @@ def generate_app(path, template=None, create=False):
os.makedirs(path)
except OSError, e:
errno, message = e
- raise AppError("Can't create a CouchApp in %s: %s" % (
- path, message))
-
+ raise AppError("Can't create a CouchApp in %s: %s" % (path, message))
+
for n in DEFAULT_APP_TREE:
tp = os.path.join(path, n)
os.makedirs(tp)
-
+
for t in TEMPLATES:
appdir = path
if prefix:
@@ -82,24 +80,25 @@ def generate_app(path, template=None, create=False):
if os.path.exists(location):
t = os.path.join(prefix, t)
break
-
+
copy_helper(appdir, t)
-
+
# add vendor
vendor_dir = os.path.join(appdir, 'vendor')
os.makedirs(vendor_dir)
copy_helper(vendor_dir, '', tname="vendor")
-
+
fid = os.path.join(appdir, '_id')
if not os.path.isfile(fid):
with open(fid, 'wb') as f:
f.write('_design/%s' % os.path.split(appdir)[1])
-
+
if create:
doc = localdoc.document(path, create=True)
-
+
logger.info("%s generated." % path)
-
+
+
def generate_function(path, kind, name, template=None):
functions_path = ['functions']
if template:
@@ -128,27 +127,27 @@ def generate_function(path, kind, name, template=None):
return
elif kind == "spatial":
path = os.path.join(path, "spatial")
- functions = [("spatial.js", "%s.js" % name )]
+ functions = [("spatial.js", "%s.js" % name)]
else:
path = os.path.join(path, "%ss" % kind)
- functions = [('%s.js' % kind, "%s.js" % name )]
+ functions = [('%s.js' % kind, "%s.js" % name)]
try:
os.makedirs(path)
except:
pass
-
+
for template, target in functions:
target_path = os.path.join(path, target)
root_path = [template_dir] + functions_path + [template]
root = os.path.join(*root_path)
try:
shutil.copy2(root, target_path)
except:
- logger.warning("%s not found in %s" % (template,
- os.path.join(*root_path[:-1])))
+ logger.warning("%s not found in %s" %
+ (template, os.path.join(*root_path[:-1])))
else:
raise AppError("Defaults templates not found. Check your install.")
-
+
def copy_helper(path, directory, tname="templates"):
""" copy helper used to generate an app"""
@@ -163,7 +162,7 @@ def copy_helper(path, directory, tname="templates"):
os.makedirs(path)
except:
pass
-
+
for root, dirs, files in os.walk(templatedir):
rel = relpath(root, templatedir)
if rel == ".":
@@ -175,44 +174,46 @@ def copy_helper(path, directory, tname="templates"):
except:
continue
for f in files:
- shutil.copy2(os.path.join(root, f), os.path.join(target_path,
- f))
+ shutil.copy2(os.path.join(root, f),
+ os.path.join(target_path, f))
else:
raise AppError(
- "Can't create a CouchApp in %s: default template not found." % (
- path))
-
+ "Can't create a CouchApp in %s: default template not found." %
+ (path))
+
+
def find_template_dir(name, directory=''):
paths = ['%s' % name, os.path.join('..', name)]
- if hasattr(sys, 'frozen'): # py2exe
+ if hasattr(sys, 'frozen'): # py2exe
modpath = sys.executable
elif sys.platform == "win32" or os.name == "nt":
- modpath = os.path.join(sys.prefix, "Lib", "site-packages",
- "couchapp", "templates")
+ modpath = os.path.join(sys.prefix, "Lib", "site-packages", "couchapp",
+ "templates")
else:
modpath = __file__
-
+
if sys.platform != "win32" and os.name != "nt":
- default_locations = [
+ default_locations = [
"/usr/share/couchapp/templates/%s" % directory,
"/usr/local/share/couchapp/templates/%s" % directory,
"/opt/couchapp/templates/%s" % directory]
else:
default_locations = []
- default_locations.extend([os.path.join(os.path.dirname(modpath), p,
- directory) for p in paths])
+ default_locations.extend([os.path.join(os.path.dirname(modpath), p,
+ directory) for p in paths])
if sys.platform == "darwin":
home = os.path.expanduser('~'),
data_path = "%s/Library/Application Support/Couchapp" % home
- default_locations.extend(["%s/%s/%s" % (data_path, p, directory) \
- for p in paths])
-
+ default_locations.extend(["%s/%s/%s" % (data_path, p, directory)
+ for p in paths])
+
if directory:
for user_location in user_path():
- default_locations.append(os.path.join(user_location, name, directory))
+ default_locations.append(os.path.join(user_location, name,
+ directory))
found = False
for location in default_locations:
@@ -223,19 +224,21 @@ def find_template_dir(name, directory=''):
if found:
return template_dir
return False
-
+
+
def generate(path, kind, name, **opts):
if kind not in ['startapp', 'app', 'view', 'list', 'show', 'filter',
- 'function', 'vendor', 'update', 'spatial']:
+ 'function', 'vendor', 'update', 'spatial']:
raise AppError(
"Can't generate %s in your couchapp. generator is unknown" % kind)
if kind == "app":
- generate_app(path, template=opts.get("template"),
- create=opts.get('create', False))
+ generate_app(path, template=opts.get("template"),
+ create=opts.get('create', False))
elif kind == "startapp":
start_app(path)
else:
if name is None:
- raise AppError("Can't generate %s function, name is missing" % kind)
+ raise AppError("Can't generate %s function, name is missing" %
+ kind)
generate_function(path, kind, name, opts.get("template"))
View
42 couchapp/hooks/compress/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# This file is part of couchapp released under the Apache 2 license.
+# This file is part of couchapp released under the Apache 2 license.
# See the NOTICE for more information.
import logging
@@ -13,19 +13,20 @@
logger = logging.getLogger(__name__)
+
class Compress(object):
-
+
def __init__(self, path):
self.appdir = path
self.attach_dir = os.path.join(path, '_attachments')
self.conf = Config()
self.conf.update(path)
-
+
def is_hook(self):
if not 'compress' in self.conf:
return False
return True
-
+
def compress_css(self, css):
re_url = re.compile('url\s*\(([^\s"].*)\)')
@@ -34,9 +35,9 @@ def compress_css(self, css):
def replace_url(mo):
""" make sure urls are relative to css path """
- css_url = mo.group(0)[4:].strip(")").replace("'", "").replace('"','')
- css_path = os.path.join(os.path.dirname(src_fpath),
- css_url)
+ css_url = mo.group(0)[4:].strip(")").replace("'", "").replace('"',
+ '')
+ css_path = os.path.join(os.path.dirname(src_fpath), css_url)
rel_path = util.relpath(css_path, fname_dir)
return "url(%s)" % rel_path
@@ -51,16 +52,16 @@ def replace_url(mo):
src_fpath = os.path.join(self.appdir, src_fname)
if os.path.exists(src_fpath):
- content_css = str(compress_css.CSSParser(
- util.read(src_fpath)))
+ content_css = \
+ str(compress_css.CSSParser(util.read(src_fpath)))
content_css = re_url.sub(replace_url, content_css)
output_css += content_css
logger.debug("Merging %s in %s" % (src_fname, fname))
if not os.path.isdir(fname_dir):
os.makedirs(fname_dir)
util.write(dest_path, output_css)
-
+
def compress_js(self, backend, js):
logger.info("compress js with %s " % backend.__about__)
@@ -74,42 +75,43 @@ def compress_js(self, backend, js):
src_fpath = os.path.join(self.appdir, src_fname)
if os.path.isfile(src_fpath):
output_js += "/* %s */\n" % src_fpath
- output_js += util.read(src_fpath)
+ output_js += util.read(src_fpath)
logger.debug("merging %s in %s" % (src_fname, fname))
if not os.path.isdir(fname_dir):
os.makedirs(fname_dir)
output_js = backend.compress(output_js)
util.write(dest_path, output_js)
-
+
def run(self):
conf = self.conf
actions = conf.get('compress', {})
if 'css' in actions:
self.compress_css(actions['css'])
-
+
if 'js' in actions:
if 'js_compressor' in conf['compress']:
modname = conf['compress']['js_compressor']