def init(conf, *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') if opts['empty'] and opts['template']: raise AppError('option "empty" cannot use with "template"') if util.iscouchapp(dest): raise AppError("can't create an app at '{0}'. " "One already exists here.".format(dest)) if util.findcouchapp(dest): raise AppError("can't create an app inside another app '{0}'.".format( util.findcouchapp(dest))) # ``couchapp init -e dest`` if opts['empty']: document(dest, create=True) # ``couchapp init -t template_name dest`` elif opts['template']: generator.init_template(dest, template=opts['template']) # ``couchapp init dest`` else: generator.init_basic(dest) logger.info('{0} created.'.format(dest)) 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" name = args[0] elif len(args) == 2: kind = args[0] name = args[1] elif len(args) >= 3: 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") generator.generate(dest, kind, name, **opts) hook(conf, dest, "post-generate") return 0
def generate(path, kind, name, **opts): if kind not in [ 'view', 'list', 'show', 'filter', 'function', 'vendor', 'update', 'spatial' ]: raise AppError("Can't generate {0} in your couchapp. " 'generator is unknown'.format(kind)) if name is None: raise AppError("Can't generate {0} function, " "name is missing".format(kind)) generate_function(path, kind, name, opts.get("template"))
def generate(path, kind, name, **opts): if kind not in ['startapp', 'app', 'view', 'list', 'show', 'filter', '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)) elif kind == "startapp": start_app(path) else: if name is None: raise AppError("Can't generate %s function, name is missing" % kind) generate_function(path, kind, name, opts.get("template"))
def generate_function(path, kind, name, template=None): functions_path = ['functions'] if template: functions_path = [] _relpath = os.path.join(*template.split('/')) template_dir = find_template_dir("templates", _relpath) else: template_dir = find_template_dir("templates") if template_dir: functions = [] if kind == "view": path = os.path.join(path, "%ss" % kind, name) if os.path.exists(path): raise AppError("The view %s already exists" % name) functions = [('map.js', 'map.js'), ('reduce.js', 'reduce.js')] elif kind == "function": functions = [('%s.js' % name, '%s.js' % name)] elif kind == "vendor": app_dir = os.path.join(path, "vendor", name) try: os.makedirs(app_dir) except: pass targetpath = os.path.join(*template.split('/')) copy_helper(path, targetpath) return elif kind == "spatial": path = os.path.join(path, "spatial") functions = [("spatial.js", "%s.js" % name)] else: path = os.path.join(path, "%ss" % kind) 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]))) else: raise AppError("Defaults templates not found. Check your install.")
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) 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"]) token = oauth.Token(oauth_tokens["token"], 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: self.res.head() except ResourceNotFound: self.res.put()
def autopush(conf, path, *args, **opts): doc_path = None dest = None if len(args) < 2: doc_path = path if args: dest = args[0] else: 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')) 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) watcher.run()
def copy_helper(path, directory, tname="templates"): """ copy helper used to generate an app""" if tname == "vendor": tname = os.path.join("templates", tname) templatedir = find_template_dir(tname, directory) if templatedir: if directory == "vendor": path = os.path.join(path, directory) try: os.makedirs(path) except: pass for root, dirs, files in os.walk(templatedir): rel = relpath(root, templatedir) if rel == ".": rel = "" target_path = os.path.join(path, rel) for d in dirs: try: os.makedirs(os.path.join(target_path, d)) except: continue for f in files: 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))
def setup(self): self.noatomic = self.opts.get('no_atomic', False) self.update_delay = self.opts.get('update_delay', 60) dest = None doc_path = None if len(self.args) < 2: doc_path = self.path if self.args: dest = self.args[0] else: doc_path = os.path.normpath(os.path.join(os.getcwd(), self.args[0])) dest = self.args[1] if doc_path is None: raise AppError("You aren't in a couchapp.") self.conf.update(doc_path) self.doc_path = doc_path self.doc = document(doc_path, create=False, docid=self.opts.get('docid')) self.dbs = self.conf.get_dbs(dest) ignorefile = os.path.join(self.doc_path, '.couchappignore') if os.path.exists(ignorefile): # A .couchappignore file is a json file containing a # list of regexps for things to skip self.ignores = json.load(open(ignorefile, 'r'))
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))
def setup_dir(path, require_empty=True): ''' If dir exists, check it empty or not. If dir does not exist, make one. ''' isdir = os.path.isdir(path) if isdir and not require_empty: return elif isdir and require_empty and is_empty_dir(path): return elif isdir and require_empty and not is_empty_dir(path): raise AppError("dir '{0}' is not empty".format(path)) elif os.path.exists(path) and not isdir: raise AppError("'{0}': File exists".format(path)) os.mkdir(path)
def startapp(conf, *args, **opts): if len(args) < 1: raise AppError("Can't start an app, name or path is missing") if len(args) == 1: 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) generator.generate(dest, "startapp", name, **opts) return 0
def generate(path, kind, name, **opts): kinds = ('view', 'list', 'show', 'filter', 'function', 'vendor', 'update', 'spatial') if kind not in kinds: raise AppError("Can't generate '{0}' in your couchapp. " 'generator is unknown.'.format(kind)) if kind == 'vendor': return generate_vendor(path, name, opts.get('template', 'default')) generate_function(path, kind, name, opts.get('template', 'default'))
def generate(conf, path, *args, **opts): ''' :param path: result of util.findcouchapp ''' dest = path if len(args) < 1: raise AppError("Can't generate function, name or path is missing") if len(args) == 1: kind = "app" name = args[0] elif len(args) == 2: kind = args[0] name = args[1] elif len(args) >= 3: kind = args[0] dest = args[1] name = args[2] if kind == 'app': # deprecated warning logger.warning('"genrate app" will be deprecated in future release. ' 'Please use "init -t TEMPLATE" instead.') args = (dest, ) if dest is not None else tuple() kwargs = { 'template': opts['template'] if opts['template'] else 'default', 'empty': False } return init(conf, *args, **kwargs) if dest is None: raise AppError("You aren't in a couchapp.") hook(conf, dest, "pre-generate") generator.generate(dest, kind, name, **opts) hook(conf, dest, "post-generate") return 0
def __init__(self, source, dest=None, rev=None): self.source = source self.dest = dest self.rev = rev # init self.docid & self.dburl try: self.dburl, self.docid = self.source.split('_design/') except ValueError: raise AppError("{0} isn't a valid source".format(self.source)) if not self.dest: self.dest = self.docid # init self.path self.init_path() # init self.db related vars here # affected: # - self.docid # - self.doc self.init_db() # init metadata self.init_metadata() # create files from manifest self.setup_manifest() # second pass for missing key or in case # manifest isn't in app self.setup_missing() # create couchapp.json self.setup_couchapp_json() # save id self.setup_id() # setup .couchapprc self.setup_couchapprc() # process attachments self.setup_attachments() logger.info("{src} cloned in {dest}".format(src=self.source, dest=self.dest))
def browse_url(self, url): if url.startswith("desktopcouch://"): if not desktopcouch: raise AppError("Desktopcouch isn't available on this"+ "machine. You can't access to %s" % url) ctx = local_files.DEFAULT_CONTEXT bookmark_file = os.path.join(ctx.db_dir, "couchdb.html") try: username, password = re.findall("<!-- !!([^!]+)!!([^!]+)!! -->", open(bookmark_file).read())[-1] except ValueError: raise IOError("Bookmark file is corrupt."+ "Username/password are missing.") url = "http://%s:%s@localhost:%s/%s" % (username, password, desktopcouch.find_port(), url[15:]) webbrowser.open_new_tab(url)
def generate_app(path, template=None, create=False): """ Generates a CouchApp in app_dir :attr verbose: boolean, default False :return: boolean, dict. { 'ok': True } if ok, { 'ok': False, 'error': message } if something was wrong. """ TEMPLATES = ['app'] prefix = '' if template is not None: prefix = os.path.join(*template.split('/')) try: os.makedirs(path) except OSError, e: errno, message = e raise AppError("Can't create a CouchApp in %s: %s" % (path, message))
def browse(conf, path, *args, **opts): if len(args) == 0: dest = path doc_path = '.' else: doc_path = path dest = args[0] doc_path = os.path.normpath(os.path.join(os.getcwd(), doc_path)) if not util.iscouchapp(doc_path): raise AppError("Dir '{0}' is not a couchapp.".format(doc_path)) conf.update(doc_path) doc = document(doc_path, create=False, docid=opts.get('docid')) dbs = conf.get_dbs(dest) doc.browse(dbs)
def push(conf, path, *args, **opts): export = opts.get('export', False) noatomic = opts.get('no_atomic', False) browse = opts.get('browse', False) force = opts.get('force', False) dest = None doc_path = None if len(args) < 2: if export: if path is None and args: doc_path = args[0] else: doc_path = path else: doc_path = path if args: dest = args[0] else: 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')) if export: if opts.get('output'): util.write_json(opts.get('output'), doc) else: print(doc.to_json()) return 0 dbs = conf.get_dbs(dest) 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
from couchapp.errors import AppError from couchapp.localdoc import document from couchapp.util import json try: import pyinotify except ImportError: pyinotify = None try: import eventlet eventlet.monkey_patch(os=False) except ImportError: raise AppError("""Eventlet isn't installed. Install it with the command: pip install eventlet """) log = logging.getLogger(__name__) def close_on_exec(fd): flags = fcntl.fcntl(fd, fcntl.F_GETFD) flags |= fcntl.FD_CLOEXEC fcntl.fcntl(fd, fcntl.F_SETFD, flags) def set_non_blocking(fd): flags = fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK fcntl.fcntl(fd, fcntl.F_SETFL, flags)
def generate_function(path, func_type, name, template='default'): ''' Generate function from template :param path: the app dir :param func_type: function type. e.g. ``view``, ``show``. :param name: the function name :param template: the template set The big picture of template dir is discribed in :py:func:`~couchapp.generate.init_template`. Here we show the detail structure of ``functions`` dir:: functions/ filter.js list.js map.js reduce.js show.js spatial.js update.js validate_doc_update.js ... myfunc.js ''' tmpl_name = os.path.join(*template.split('/')) tmpl_dir = find_template_dir(tmpl_name, tmpl_type='functions', raise_error=True) file_list = [] # [(src, dest), ...] empty_dir = False if func_type == 'view': dir_ = os.path.join(path, 'views', name) empty_dir = True file_list.append(('map.js', 'map.js')) file_list.append(('reduce.js', 'reduce.js')) elif func_type in ('filter', 'list', 'show', 'update'): dir_ = os.path.join(path, '{0}s'.format(func_type)) file_list.append(('{0}.js'.format(func_type), '{0}.js'.format(name))) elif func_type == 'function': # user defined function dir_ = path file_list.append(('{0}.js'.format(name), '{0}.js'.format(name))) elif func_type == 'spatial': dir_ = os.path.join(path, 'spatial') file_list.append(('spatial.js', '{0}.js'.format(name))) elif func_type == 'validate_doc_update': dir_ = path file_list.append(('validate_doc_update.js', 'validate_doc_update.js')) else: raise AppError('unrecognized function type "{0}"'.format(func_type)) setup_dir(dir_, require_empty=empty_dir) for src, dest in file_list: full_src = os.path.join(tmpl_dir, src) full_dest = os.path.join(dir_, dest) try: copy2(full_src, full_dest) except Error: logger.warning('function "%s" not found in "%s"', src, tmpl_dir) else: logger.debug('function "%s" generated successfully', dest) logger.info('enjoy the %s function, "%s"!', func_type, 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. """ try: dburl, docid = source.split('_design/') 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) 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', {}) # get signatures signatures = metadata.get('signatures', {}) # get objects refs objects = metadata.get('objects', {}) # create files from manifest if manifest: for filename in manifest: logger.debug("clone property: %s" % filename) filepath = os.path.join(path, filename) if filename.endswith('/'): if not os.path.isdir(filepath): os.makedirs(filepath) elif filename == "couchapp.json": continue else: parts = util.split_path(filename) fname = parts.pop() v = doc while 1: try: for key in parts: v = v[key] except KeyError: break # remove extension last_key, ext = os.path.splitext(fname) # make sure key exist try: content = v[last_key] 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:]) if fname.endswith('.json'): content = util.json.dumps(content).encode('utf-8') del v[last_key] # make sure file dir have been created filedir = os.path.dirname(filepath) if not os.path.isdir(filedir): os.makedirs(filedir) util.write(filepath, content) # remove the key from design doc temp = doc for key2 in parts: if key2 == key: if not temp[key2]: 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('_'): continue elif key in ('couchapp'): app_meta = copy.deepcopy(doc['couchapp']) if 'signatures' in app_meta: del app_meta['signatures'] if 'manifest' in app_meta: del app_meta['manifest'] if 'objects' in app_meta: del app_meta['objects'] if 'length' in app_meta: del app_meta['length'] if app_meta: couchapp_file = os.path.join(path, 'couchapp.json') util.write_json(couchapp_file, app_meta) elif key in ('views'): vs_dir = os.path.join(path, key) if not os.path.isdir(vs_dir): os.makedirs(vs_dir) for vsname, vs_item in doc[key].iteritems(): vs_item_dir = os.path.join(vs_dir, vsname) 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) util.write(filename, func) logger.warning("clone view not in manifest: %s" % filename) elif key in ('shows', 'lists', 'filter', 'updates'): 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) util.write(filename, func) logger.warning("clone show or list not in manifest: %s" % filename) else: filedir = os.path.join(path, key) if os.path.exists(filedir): continue else: logger.warning("clone property not in manifest: %s" % key) if isinstance(doc[key], ( list, tuple, )): util.write_json(filedir + ".json", doc[key]) elif isinstance(doc[key], dict): if not os.path.isdir(filedir): os.makedirs(filedir) for field, value in doc[key].iteritems(): fieldpath = os.path.join(filedir, field) if isinstance(value, basestring): if value.startswith('base64-encoded;'): value = base64.b64decode(content[15:]) util.write(fieldpath, value) else: util.write_json(fieldpath + '.json', value) else: value = doc[key] if not isinstance(value, basestring): value = str(value) util.write(filedir, value) # 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') 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))
def find_template_dir(tmpl_name='default', tmpl_type='', raise_error=False): ''' Find template dir for different platform :param tmpl_name: The template name under ``templates``. It can be empty string. If it is set to ``default``, we will use consider the tmpl_name as empty. e.g. ``mytmpl`` mentioned in the docstring of :py:func:`~couchapp.generate.init_template` :param tmpl_type: the type of template. e.g. 'app', 'functions', 'vendor' :param bool raise_error: raise ``AppError`` if not found :return: the absolute path or ``None`` if not found We will check the ``<search path>/templates/<tmpl_name>/<tmpl_type>`` is dir or not. The first matched win. For posix platform, the search locations are following: - ~/.couchapp/ - <module dir path>/ - <module dir path>/../ - /usr/share/couchapp/ - /usr/local/share/couchapp/ - /opt/couchapp/ For darwin (OSX) platform, we have some extra search locations: - ${HOME}/Library/Application Support/Couchapp/ For windows with standlone binary (py2exe): - <executable dir path>/ - <executable dir path>/../ For windows with python interpreter: - ${USERPROFILE}/.couchapp/ - <module dir path>/ - <module dir path>/../ - <python prefix>/Lib/site-packages/couchapp/ ..versionchanged:: 1.1 ''' if tmpl_type and tmpl_type not in TEMPLATE_TYPES: raise AppError('invalid template type "{0}"'.format(tmpl_type)) if tmpl_name == 'default': tmpl_name = '' modpath = os.path.dirname(__file__) search_paths = user_path() + [ modpath, os.path.join(modpath, '..'), ] if os.name == 'posix': search_paths.extend([ '/usr/share/couchapp', '/usr/local/share/couchapp', '/opt/couchapp', ]) elif is_py2exe(): search_paths.append(os.path.dirname(sys.executable)) elif is_windows(): search_paths.append( os.path.join(sys.prefix, 'Lib', 'site-packages', 'couchapp') ) # extra path for darwin if sys.platform.startswith('darwin'): search_paths.append( os.path.expanduser('~/Library/Application Support/Couchapp') ) # the first win! for path in search_paths: path = os.path.normpath(path) path = os.path.join(path, 'templates', tmpl_name, tmpl_type) if os.path.isdir(path): logger.debug('template path match: "{0}"'.format(path)) return path logger.debug('template search path: "{0}" not found'.format(path)) if raise_error: logger.info('please use "-d" to checkout search paths.') raise AppError('template "{0}/{1}" not found.'.format( tmpl_name, tmpl_type)) return None
def init_template(path, template='default'): ''' Generates a CouchApp via template :param str path: the app dir :param str template: the templates set name. In following example, it is ``mytmpl``. We expect template dir has following structure:: templates/ app/ functions/ vendor/ mytmpl/ app/ functions/ vendor/ vuejs/ myvue/ app/ functions/ vendor/ vueform/ app/ functions/ vendor/ The ``templates/app`` will be used as default app template. ``templates/functions`` and ``templates/vender`` are default, also. And we can create a dir ``mytmpl`` as custom template set. The template set name can be nested, e.g. ``vuejs/myvue``. ..versionadded:: 1.1 ''' if template in TEMPLATE_TYPES: raise AppError('template name connot be {0}.'.format(TEMPLATE_TYPES)) tmpl_name = os.path.normpath(template) if template else '' # copy ``<template set>/app`` src_dir = find_template_dir(tmpl_name, 'app', raise_error=True) copy_helper(src_dir, path) # construct basic dirs setup_dirs((os.path.join(path, n) for n in DEFAULT_APP_TREE), require_empty=False) # add vendor src_dir = find_template_dir(tmpl_name, tmpl_type='vendor') if src_dir is None: logger.debug('vendor not found in template set "{0}". ' 'fallback to default vendor.'.format(tmpl_name)) src_dir = find_template_dir(tmpl_type='vendor') vendor_dir = os.path.join(path, 'vendor') copy_helper(src_dir, vendor_dir) save_id(path, '_design/{0}'.format(os.path.split(path)[-1])) localdoc.document(path, create=True)
def test_dispatch_AppError(_dispatch, logger): args = ['strange'] _dispatch.side_effect = AppError('some error') assert dispatch.dispatch(args) == -1 _dispatch.assert_called_with(args)