gooderp18绿色标准版
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

151 行
4.7KB

  1. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  2. import argparse
  3. import os
  4. import re
  5. import sys
  6. from pathlib import Path
  7. import jinja2
  8. from . import Command
  9. class Scaffold(Command):
  10. """ Generates an Odoo module skeleton. """
  11. def run(self, cmdargs):
  12. # TODO: bash completion file
  13. parser = argparse.ArgumentParser(
  14. prog=f'{Path(sys.argv[0]).name} {self.name}',
  15. description=self.__doc__,
  16. epilog=self.epilog(),
  17. )
  18. parser.add_argument(
  19. '-t', '--template', type=template, default=template('default'),
  20. help="Use a custom module template, can be a template name or the"
  21. " path to a module template (default: %(default)s)")
  22. parser.add_argument('name', help="Name of the module to create")
  23. parser.add_argument(
  24. 'dest', default='.', nargs='?',
  25. help="Directory to create the module in (default: %(default)s)")
  26. if not cmdargs:
  27. sys.exit(parser.print_help())
  28. args = parser.parse_args(args=cmdargs)
  29. if args.template.id == 'l10n_payroll':
  30. name_split = args.name.split('-')
  31. params = {
  32. 'name': name_split[0],
  33. 'code': name_split[1]
  34. }
  35. else:
  36. params = {'name': args.name}
  37. args.template.render_to(
  38. snake(args.name),
  39. directory(args.dest, create=True),
  40. params=params,
  41. )
  42. def epilog(self):
  43. return "Built-in templates available are: %s" % ', '.join(
  44. d for d in os.listdir(builtins())
  45. if d != 'base'
  46. )
  47. builtins = lambda *args: os.path.join(
  48. os.path.abspath(os.path.dirname(__file__)),
  49. 'templates',
  50. *args)
  51. def snake(s):
  52. """ snake cases ``s``
  53. :param str s:
  54. :return: str
  55. """
  56. # insert a space before each uppercase character preceded by a
  57. # non-uppercase letter
  58. s = re.sub(r'(?<=[^A-Z])\B([A-Z])', r' \1', s)
  59. # lowercase everything, split on whitespace and join
  60. return '_'.join(s.lower().split())
  61. def pascal(s):
  62. return ''.join(
  63. ss.capitalize()
  64. for ss in re.sub(r'[_\s]+', ' ', s).split()
  65. )
  66. def directory(p, create=False):
  67. expanded = os.path.abspath(
  68. os.path.expanduser(
  69. os.path.expandvars(p)))
  70. if create and not os.path.exists(expanded):
  71. os.makedirs(expanded)
  72. if not os.path.isdir(expanded):
  73. die("%s is not a directory" % p)
  74. return expanded
  75. env = jinja2.Environment()
  76. env.filters['snake'] = snake
  77. env.filters['pascal'] = pascal
  78. class template(object):
  79. def __init__(self, identifier):
  80. # TODO: archives (zipfile, tarfile)
  81. self.id = identifier
  82. # is identifier a builtin?
  83. self.path = builtins(identifier)
  84. if os.path.isdir(self.path):
  85. return
  86. # is identifier a directory?
  87. self.path = identifier
  88. if os.path.isdir(self.path):
  89. return
  90. die("{} is not a valid module template".format(identifier))
  91. def __str__(self):
  92. return self.id
  93. def files(self):
  94. """ Lists the (local) path and content of all files in the template
  95. """
  96. for root, _, files in os.walk(self.path):
  97. for f in files:
  98. path = os.path.join(root, f)
  99. yield path, open(path, 'rb').read()
  100. def render_to(self, modname, directory, params=None):
  101. """ Render this module template to ``dest`` with the provided
  102. rendering parameters
  103. """
  104. # overwrite with local
  105. for path, content in self.files():
  106. path = env.from_string(path).render(params)
  107. local = os.path.relpath(path, self.path)
  108. # strip .template extension
  109. root, ext = os.path.splitext(local)
  110. if ext == '.template':
  111. local = root
  112. if self.id == "l10n_payroll":
  113. modname = f"l10n_{params['code']}_hr_payroll"
  114. dest = os.path.join(directory, modname, local)
  115. destdir = os.path.dirname(dest)
  116. if not os.path.exists(destdir):
  117. os.makedirs(destdir)
  118. with open(dest, 'wb') as f:
  119. if ext not in ('.py', '.xml', '.csv', '.js', '.rst', '.html', '.template'):
  120. f.write(content)
  121. else:
  122. env.from_string(content.decode('utf-8'))\
  123. .stream(params or {})\
  124. .dump(f, encoding='utf-8')
  125. f.write(b'\n')
  126. def die(message, code=1):
  127. print(message, file=sys.stderr)
  128. sys.exit(code)
  129. def warn(message):
  130. # ASK: shall we use logger ?
  131. print("WARNING:", message)
上海开阖软件有限公司 沪ICP备12045867号-1