-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathrenderer.py
More file actions
155 lines (143 loc) · 5.77 KB
/
renderer.py
File metadata and controls
155 lines (143 loc) · 5.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# -*- coding: utf-8 -*-
# This file is part of the CloudBlue Connect connect-cli.
# Copyright (c) 2019-2025 CloudBlue. All Rights Reserved.
import fnmatch
import os
import shutil
import tempfile
from pathlib import Path
from string import Template
from jinja2 import Environment, FileSystemLoader, select_autoescape
from connect.cli.core.terminal import console
from connect.cli.plugins.project.utils import purge_dir
class BoilerplateRenderer:
def __init__(
self,
context,
template_folder,
output_dir,
overwrite=False,
pre_render=None,
post_render=None,
exclude=None,
):
self._validate_args(
context,
template_folder,
output_dir,
overwrite,
pre_render,
post_render,
exclude,
)
self.context = context
self.template_folder = template_folder
self.output_dir = output_dir
self.overwrite = overwrite
self.pre_render_function = pre_render
self.post_render_function = post_render
self.excluded_patterns = self._get_excluded_patterns(template_folder, exclude or [])
self.env = Environment(
loader=FileSystemLoader(
searchpath=template_folder,
),
extensions=['jinja2_time.TimeExtension'],
keep_trailing_newline=True,
autoescape=select_autoescape(),
)
@staticmethod
def _get_excluded_patterns(template_dir, exclude):
excludes = []
for pattern in exclude:
excludes.extend(
[str(p) for p in Path(os.path.join(template_dir, '${project_slug}')).glob(pattern)],
)
return excludes
@staticmethod
def _validate_args(
context,
template_folder,
output_dir,
overwrite,
pre_render,
post_render,
exclude,
):
if not isinstance(context, dict):
raise TypeError('The parameter context is invalid, it must be a dict.')
if not isinstance(template_folder, str) or not os.path.exists(template_folder):
raise TypeError(
'The parameter template_folder is invalid, it must be a valid path string.',
)
if not isinstance(output_dir, str):
raise TypeError('The parameter output_dir is invalid, it must be a valid path string.')
if not isinstance(overwrite, bool):
raise TypeError('The parameter overwrite is invalid, it must be bool type.')
if pre_render and not callable(pre_render):
raise TypeError('The parameter pre_render is invalid, it must be callable.')
if post_render and not callable(post_render):
raise TypeError('The parameter post_render is invalid, it must be callable.')
if exclude and not isinstance(exclude, list):
raise TypeError('The parameter exclude is invalid, it must be a list.')
def _create_directories(self, output_dir):
for element in Path(self.template_folder).rglob('*'):
directory = os.path.join(
output_dir,
os.path.relpath(str(element), self.template_folder),
)
directory = Template(directory).safe_substitute(self.context)
if element.is_dir() and not fnmatch.filter(self.excluded_patterns, element):
os.makedirs(directory, exist_ok=True)
console.print(
f'Folder {directory.replace(output_dir, "")}'
' created [bold green]\u2713[/bold green]',
)
def render(self):
if self.overwrite:
prj_dir = os.path.join(self.output_dir, self.context['project_slug'])
purge_dir(prj_dir)
console.print(f'Directory {prj_dir} deleted.')
with tempfile.TemporaryDirectory() as tmpdir:
self._create_directories(output_dir=tmpdir)
if self.pre_render_function:
self.pre_render_function(tmpdir, self.context)
console.print('pre_render_function executed [bold green]\u2713[/bold green]')
with console.status('[magenta]Generating files'):
templates = self.env.list_templates()
for template in templates:
self._render_template(template, tmpdir)
if self.post_render_function:
self.post_render_function(tmpdir, self.context)
console.print('post_render_function executed [bold green]\u2713[/bold green]')
for folder in os.listdir(tmpdir):
shutil.move(
os.path.join(
tmpdir,
folder,
),
self.output_dir,
)
console.print('moved generated tree to destination [bold green]\u2713[/bold green]')
console.print()
def _render_template(self, template_name, destination):
evaluated_template_path = Template(
str(template_name),
).safe_substitute(
self.context,
)
if not fnmatch.filter(
self.excluded_patterns,
os.path.join(self.template_folder, template_name),
):
file_destination = os.path.join(
destination,
evaluated_template_path[:-3],
)
template = self.env.get_template(template_name)
content = template.render(self.context)
with open(file_destination, 'w') as outstream:
outstream.write(f'{content.rstrip()}\n')
console.print(
f'File {file_destination.replace(destination, "")}'
' generated [bold green]\u2713[/bold green]',
)