132 lines
4.7 KiB
Python
132 lines
4.7 KiB
Python
"""
|
|
Generate an OpenERP module skeleton.
|
|
"""
|
|
|
|
import functools
|
|
import keyword
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
import jinja2
|
|
|
|
# FIXME: add logging
|
|
def run(args):
|
|
env = jinja2.Environment(loader=jinja2.PackageLoader(
|
|
'openerpcommand', 'templates'))
|
|
env.filters['snake'] = snake
|
|
args.dependency = 'web' if args.controller else 'base'
|
|
|
|
module_name = snake(args.module)
|
|
module = functools.partial(
|
|
os.path.join, args.modules_dir, module_name)
|
|
|
|
if args.controller is True:
|
|
args.controller = module_name
|
|
|
|
if args.model is True:
|
|
args.model = module_name
|
|
|
|
if os.path.exists(module()):
|
|
message = "The path `%s` already exists." % module()
|
|
die(message)
|
|
|
|
dump(env, '__openerp__.jinja2', module('__openerp__.py'), config=args)
|
|
dump(env, '__init__.jinja2', module('__init__.py'), modules=[
|
|
args.controller and 'controllers',
|
|
args.model and 'models'
|
|
])
|
|
dump(env, 'ir.model.access.jinja2', module('security', 'ir.model.access.csv'), config=args)
|
|
|
|
if args.controller:
|
|
controller_module = snake(args.controller)
|
|
dump(env, '__init__.jinja2', module('controllers', '__init__.py'), modules=[controller_module])
|
|
dump(env, 'controllers.jinja2',
|
|
module('controllers', '%s.py' % controller_module),
|
|
config=args)
|
|
|
|
if args.model:
|
|
model_module = snake(args.model)
|
|
dump(env, '__init__.jinja2', module('models', '__init__.py'), modules=[model_module])
|
|
dump(env, 'models.jinja2', module('models', '%s.py' % model_module), config=args)
|
|
|
|
def add_parser(subparsers):
|
|
parser = subparsers.add_parser('scaffold',
|
|
description='Generate an OpenERP module skeleton.')
|
|
parser.add_argument('module', metavar='MODULE',
|
|
help='the name of the generated module')
|
|
parser.add_argument('modules_dir', metavar='DIRECTORY', type=directory,
|
|
help="Modules directory in which the new module should be generated")
|
|
|
|
controller = parser.add_mutually_exclusive_group()
|
|
controller.add_argument('--controller', type=identifier,
|
|
help="The name of the controller to generate")
|
|
controller.add_argument('--no-controller', dest='controller',
|
|
action='store_const', const=None, help="Do not generate a controller")
|
|
|
|
model = parser.add_mutually_exclusive_group()
|
|
model.add_argument('--model', type=identifier,
|
|
help="The name of the model to generate")
|
|
model.add_argument('--no-model', dest='model',
|
|
action='store_const', const=None, help="Do not generate a model")
|
|
|
|
mod = parser.add_argument_group("Module information",
|
|
"these are added to the module metadata and displayed on e.g. "
|
|
"apps.openerp.com. For company-backed modules, the company "
|
|
"information should be used")
|
|
mod.add_argument('--name', dest='author_name', default="",
|
|
help="Name of the module author")
|
|
mod.add_argument('--website', dest='author_website', default="",
|
|
help="Website of the module author")
|
|
mod.add_argument('--category', default="Uncategorized",
|
|
help="Broad categories to which the module belongs, used for "
|
|
"filtering within OpenERP and on apps.openerp.com."
|
|
"Defaults to %(default)s")
|
|
mod.add_argument('--summary', default="",
|
|
help="Short (1 phrase/line) summary of the module's purpose, used as "
|
|
"subtitle on modules listing or apps.openerp.com")
|
|
|
|
parser.set_defaults(run=run, controller=True, model=True)
|
|
|
|
def snake(s):
|
|
""" snake cases ``s``
|
|
|
|
:param str s:
|
|
:return: str
|
|
"""
|
|
# insert a space before each uppercase character preceded by a
|
|
# non-uppercase letter
|
|
s = re.sub(r'(?<=[^A-Z])\B([A-Z])', r' \1', s)
|
|
# lowercase everything, split on whitespace and join
|
|
return '_'.join(s.lower().split())
|
|
|
|
def dump(env, template, dest, **kwargs):
|
|
outdir = os.path.dirname(dest)
|
|
if not os.path.exists(outdir):
|
|
os.makedirs(outdir)
|
|
env.get_template(template).stream(**kwargs).dump(dest)
|
|
# add trailing newline which jinja removes
|
|
with open(dest, 'a') as f:
|
|
f.write('\n')
|
|
|
|
def identifier(s):
|
|
if keyword.iskeyword(s):
|
|
die("%s is a Python keyword and can not be used as a name" % s)
|
|
if not re.match('[A-Za-z_][A-Za-z0-9_]*', s):
|
|
die("%s is not a valid Python identifier" % s)
|
|
return s
|
|
|
|
def directory(p):
|
|
expanded = os.path.abspath(
|
|
os.path.expanduser(
|
|
os.path.expandvars(p)))
|
|
if not os.path.exists(expanded):
|
|
os.makedirs(expanded)
|
|
if not os.path.isdir(expanded):
|
|
die("%s exists but is not a directory" % p)
|
|
return expanded
|
|
|
|
def die(message, code=1):
|
|
print >>sys.stderr, message
|
|
sys.exit(code)
|