ConsoleFramework 2.0.1__tar.gz → 2.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -67,15 +67,39 @@ class Render:
67
67
  field = [[default_char for _ in range(width)] for _ in range(height)]
68
68
  if points:
69
69
  for item in points:
70
- if len(item) == 3:
71
- x, y, char = item
70
+ if len(item) >= 3:
71
+ x, y, char = item[0], item[1], item[2]
72
72
  if 0 <= x < width and 0 <= y < height:
73
- field[y][x] = char
73
+ if len(item) == 4:
74
+ color = item[3]
75
+ field[y][x] = f"{color}{char}{Colors.RESET}"
76
+ elif len(item) == 5:
77
+ color, bg = item[3], item[4]
78
+ field[y][x] = f"{color}{bg}{char}{Colors.RESET}"
79
+ else:
80
+ field[y][x] = char
74
81
  for row in field:
75
82
  print(''.join(row))
76
-
77
83
  @staticmethod
78
- def get_line_points(x1, y1, x2, y2, char='#'):
84
+ def _apply_color(points, color=None, bg=None):
85
+ if color is None and bg is None:
86
+ return points
87
+ result = []
88
+ for p in points:
89
+ if len(p) == 3:
90
+ x, y, char = p
91
+ if color and bg:
92
+ result.append((x, y, char, color, bg))
93
+ elif color:
94
+ result.append((x, y, char, color))
95
+ else:
96
+ result.append((x, y, char, Colors.BG_BLACK + bg))
97
+ else:
98
+ result.append(p)
99
+ return result
100
+
101
+ @staticmethod
102
+ def get_line_points(x1, y1, x2, y2, char='#', color=None, bg=None):
79
103
  points = []
80
104
  dx = abs(x2 - x1)
81
105
  dy = abs(y2 - y1)
@@ -93,29 +117,29 @@ class Render:
93
117
  if e2 < dx:
94
118
  err += dx
95
119
  y1 += sy
96
- return points
97
-
120
+ return Render._apply_color(points, color, bg)
121
+
98
122
  @staticmethod
99
- def get_circle_points(cx, cy, r, char='#'):
123
+ def get_circle_points(cx, cy, r, char='#', color=None, bg=None):
100
124
  points = []
101
125
  for angle in range(0, 360, 5):
102
126
  rad = angle * math.pi / 180
103
127
  x = int(cx + r * math.cos(rad))
104
128
  y = int(cy + r * math.sin(rad))
105
129
  points.append((x, y, char))
106
- return points
107
-
130
+ return Render._apply_color(points, color, bg)
131
+
108
132
  @staticmethod
109
- def get_filled_circle_points(cx, cy, r, char='#'):
133
+ def get_filled_circle_points(cx, cy, r, char='#', color=None, bg=None):
110
134
  points = []
111
135
  for y in range(cy - r, cy + r + 1):
112
136
  for x in range(cx - r, cx + r + 1):
113
137
  if (x - cx) ** 2 + (y - cy) ** 2 <= r ** 2:
114
138
  points.append((x, y, char))
115
- return points
116
-
139
+ return Render._apply_color(points, color, bg)
140
+
117
141
  @staticmethod
118
- def get_rect_points(x1, y1, x2, y2, char='#'):
142
+ def get_rect_points(x1, y1, x2, y2, char='#', color=None, bg=None):
119
143
  points = []
120
144
  for x in range(x1, x2 + 1):
121
145
  points.append((x, y1, char))
@@ -123,26 +147,37 @@ class Render:
123
147
  for y in range(y1, y2 + 1):
124
148
  points.append((x1, y, char))
125
149
  points.append((x2, y, char))
126
- return points
127
-
150
+ return Render._apply_color(points, color, bg)
151
+
128
152
  @staticmethod
129
- def get_fill_rect_points(x1, y1, x2, y2, char='#'):
153
+ def get_fill_rect_points(x1, y1, x2, y2, char='#', color=None, bg=None):
130
154
  points = []
131
155
  for y in range(y1, y2 + 1):
132
156
  for x in range(x1, x2 + 1):
133
157
  points.append((x, y, char))
134
- return points
135
-
158
+ return Render._apply_color(points, color, bg)
159
+
136
160
  @staticmethod
137
- def get_triangle_points(x1, y1, x2, y2, x3, y3, char='#'):
161
+ def get_text_points(x, y, text, char=None, color=None, bg=None):
162
+ points = []
163
+ if char is None:
164
+ for i, ch in enumerate(text):
165
+ points.append((x + i, y, ch))
166
+ else:
167
+ for i in range(len(text)):
168
+ points.append((x + i, y, char))
169
+ return Render._apply_color(points, color, bg)
170
+
171
+ @staticmethod
172
+ def get_triangle_points(x1, y1, x2, y2, x3, y3, char='#', color=None, bg=None):
138
173
  points = []
139
174
  points.extend(Render.get_line_points(x1, y1, x2, y2, char))
140
175
  points.extend(Render.get_line_points(x2, y2, x3, y3, char))
141
176
  points.extend(Render.get_line_points(x3, y3, x1, y1, char))
142
- return points
143
-
177
+ return Render._apply_color(points, color, bg)
178
+
144
179
  @staticmethod
145
- def get_filled_triangle_points(x1, y1, x2, y2, x3, y3, char='#'):
180
+ def get_filled_triangle_points(x1, y1, x2, y2, x3, y3, char='#', color=None, bg=None):
146
181
  points = []
147
182
  min_x = min(x1, x2, x3)
148
183
  max_x = max(x1, x2, x3)
@@ -152,19 +187,19 @@ class Render:
152
187
  for x in range(min_x, max_x + 1):
153
188
  if Render._point_in_triangle(x, y, x1, y1, x2, y2, x3, y3):
154
189
  points.append((x, y, char))
155
- return points
156
-
190
+ return Render._apply_color(points, color, bg)
191
+
157
192
  @staticmethod
158
- def get_heart_points(cx, cy, size=5, char='#'):
193
+ def get_heart_points(cx, cy, size=5, char='#', color=None, bg=None):
159
194
  points = []
160
195
  for y in range(-size, size + 1):
161
196
  for x in range(-size, size + 1):
162
197
  if ((x * x + y * y - size * size) ** 3 <= size * size * x * x * y * y * 0.5):
163
198
  points.append((cx + x, cy - y, char))
164
- return points
165
-
199
+ return Render._apply_color(points, color, bg)
200
+
166
201
  @staticmethod
167
- def get_star_points(cx, cy, radius=5, points_count=5, char='*'):
202
+ def get_star_points(cx, cy, radius=5, points_count=5, char='*', color=None, bg=None):
168
203
  points = []
169
204
  angle = -math.pi / 2
170
205
  step = math.pi / points_count
@@ -184,18 +219,28 @@ class Render:
184
219
  p3 = outer_points[(i + 1) % points_count]
185
220
  points.extend(Render.get_line_points(int(p1[0]), int(p1[1]), int(p2[0]), int(p2[1]), char))
186
221
  points.extend(Render.get_line_points(int(p2[0]), int(p2[1]), int(p3[0]), int(p3[1]), char))
187
- return points
188
-
222
+ return Render._apply_color(points, color, bg)
223
+
224
+ @staticmethod
225
+ def get_bezier_points(x1, y1, x2, y2, x3, y3, steps=20, char='#', color=None, bg=None):
226
+ points = []
227
+ for t in range(steps + 1):
228
+ t = t / steps
229
+ x = (1 - t) ** 2 * x1 + 2 * (1 - t) * t * x2 + t ** 2 * x3
230
+ y = (1 - t) ** 2 * y1 + 2 * (1 - t) * t * y2 + t ** 2 * y3
231
+ points.append((int(x), int(y), char))
232
+ return Render._apply_color(points, color, bg)
233
+
189
234
  @staticmethod
190
- def get_wave_points(cx, cy, amplitude=5, length=20, char='~'):
235
+ def get_wave_points(cx, cy, amplitude=5, length=20, char='~', color=None, bg=None):
191
236
  points = []
192
237
  for x in range(length):
193
238
  y = cy + int(amplitude * math.sin(x * 0.5))
194
239
  points.append((cx + x, y, char))
195
- return points
196
-
240
+ return Render._apply_color(points, color, bg)
241
+
197
242
  @staticmethod
198
- def get_spiral_points(cx, cy, turns=3, radius=10, step=0.1, char='*'):
243
+ def get_spiral_points(cx, cy, turns=3, radius=10, step=0.1, char='*', color=None, bg=None):
199
244
  points = []
200
245
  angle = 0
201
246
  r = 0
@@ -205,28 +250,18 @@ class Render:
205
250
  points.append((x, y, char))
206
251
  angle += step
207
252
  r += step * 2
208
- return points
209
-
210
- @staticmethod
211
- def get_bezier_points(x1, y1, x2, y2, x3, y3, steps=20, char='#'):
212
- points = []
213
- for t in range(steps + 1):
214
- t = t / steps
215
- x = (1 - t) ** 2 * x1 + 2 * (1 - t) * t * x2 + t ** 2 * x3
216
- y = (1 - t) ** 2 * y1 + 2 * (1 - t) * t * y2 + t ** 2 * y3
217
- points.append((int(x), int(y), char))
218
- return points
219
-
253
+ return Render._apply_color(points, color, bg)
254
+
220
255
  @staticmethod
221
- def get_grid_points(x1, y1, x2, y2, step=2, char='+'):
256
+ def get_grid_points(x1, y1, x2, y2, step=2, char='+', color=None, bg=None):
222
257
  points = []
223
258
  for x in range(x1, x2 + 1, step):
224
259
  for y in range(y1, y2 + 1, step):
225
260
  points.append((x, y, char))
226
- return points
227
-
261
+ return Render._apply_color(points, color, bg)
262
+
228
263
  @staticmethod
229
- def get_noise_points(cx, cy, radius, count=50, char='.'):
264
+ def get_noise_points(cx, cy, radius, count=50, char='.', color=None, bg=None):
230
265
  points = []
231
266
  for _ in range(count):
232
267
  angle = random.uniform(0, 2 * math.pi)
@@ -234,8 +269,9 @@ class Render:
234
269
  x = int(cx + r * math.cos(angle))
235
270
  y = int(cy + r * math.sin(angle))
236
271
  points.append((x, y, char))
237
- return points
238
-
272
+ return Render._apply_color(points, color, bg)
273
+
274
+ # ---- 3D ----
239
275
  @staticmethod
240
276
  def _point_in_triangle(px, py, x1, y1, x2, y2, x3, y3):
241
277
  def sign(a, b, c):
@@ -247,7 +283,7 @@ class Render:
247
283
  has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0)
248
284
  has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0)
249
285
  return not (has_neg and has_pos)
250
-
286
+
251
287
  @staticmethod
252
288
  def _rotx(x, y, z, angle):
253
289
  rad = math.radians(angle)
@@ -256,7 +292,7 @@ class Render:
256
292
  y_new = y * cos_a - z * sin_a
257
293
  z_new = y * sin_a + z * cos_a
258
294
  return x, y_new, z_new
259
-
295
+
260
296
  @staticmethod
261
297
  def _roty(x, y, z, angle):
262
298
  rad = math.radians(angle)
@@ -265,7 +301,7 @@ class Render:
265
301
  x_new = x * cos_a + z * sin_a
266
302
  z_new = -x * sin_a + z * cos_a
267
303
  return x_new, y, z_new
268
-
304
+
269
305
  @staticmethod
270
306
  def _rotz(x, y, z, angle):
271
307
  rad = math.radians(angle)
@@ -274,7 +310,7 @@ class Render:
274
310
  x_new = x * cos_a - y * sin_a
275
311
  y_new = x * sin_a + y * cos_a
276
312
  return x_new, y_new, z
277
-
313
+
278
314
  @staticmethod
279
315
  def _project_3d(x, y, z, rotx, roty, rotz, scale, cx, cy):
280
316
  x, y, z = Render._rotx(x, y, z, rotx)
@@ -283,9 +319,9 @@ class Render:
283
319
  x2d = int(x * scale + cx)
284
320
  y2d = int(-y * scale + cy)
285
321
  return x2d, y2d
286
-
322
+
287
323
  @staticmethod
288
- def get_cube_3d_points(size=5, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#'):
324
+ def get_cube_3d_points(size=5, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
289
325
  s = size / 2
290
326
  vertices = [
291
327
  (-s, -s, -s), (s, -s, -s), (s, s, -s), (-s, s, -s),
@@ -305,7 +341,143 @@ class Render:
305
341
  x1, y1 = projected[edge[0]]
306
342
  x2, y2 = projected[edge[1]]
307
343
  points.extend(Render.get_line_points(x1, y1, x2, y2, char))
308
- return points
344
+ return Render._apply_color(points, color, bg)
345
+
346
+ @staticmethod
347
+ def get_pyramid_3d_points(size=5, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
348
+ s = size / 2
349
+ vertices = [
350
+ (-s, -s, -s), (s, -s, -s), (s, -s, s), (-s, -s, s),
351
+ (0, s, 0)
352
+ ]
353
+ edges = [
354
+ (0,1), (1,2), (2,3), (3,0),
355
+ (0,4), (1,4), (2,4), (3,4)
356
+ ]
357
+ points = []
358
+ projected = []
359
+ for v in vertices:
360
+ x2d, y2d = Render._project_3d(v[0], v[1], v[2], rotx, roty, rotz, scale, cx, cy)
361
+ projected.append((x2d, y2d))
362
+ for edge in edges:
363
+ x1, y1 = projected[edge[0]]
364
+ x2, y2 = projected[edge[1]]
365
+ points.extend(Render.get_line_points(x1, y1, x2, y2, char))
366
+ return Render._apply_color(points, color, bg)
367
+
368
+ @staticmethod
369
+ def get_cylinder_3d_points(radius=5, height=10, segments=12, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
370
+ points = []
371
+ top_points = []
372
+ bottom_points = []
373
+ for i in range(segments):
374
+ rad = i / segments * 2 * math.pi
375
+ x = radius * math.cos(rad)
376
+ z = radius * math.sin(rad)
377
+ top_points.append((x, height/2, z))
378
+ bottom_points.append((x, -height/2, z))
379
+ all_vertices = top_points + bottom_points
380
+ projected = []
381
+ for v in all_vertices:
382
+ x2d, y2d = Render._project_3d(v[0], v[1], v[2], rotx, roty, rotz, scale, cx, cy)
383
+ projected.append((x2d, y2d))
384
+ for i in range(segments):
385
+ j = (i + 1) % segments
386
+ x1, y1 = projected[i]
387
+ x2, y2 = projected[j]
388
+ points.extend(Render.get_line_points(x1, y1, x2, y2, char))
389
+ x1, y1 = projected[i + segments]
390
+ x2, y2 = projected[j + segments]
391
+ points.extend(Render.get_line_points(x1, y1, x2, y2, char))
392
+ x1, y1 = projected[i]
393
+ x2, y2 = projected[i + segments]
394
+ points.extend(Render.get_line_points(x1, y1, x2, y2, char))
395
+ return Render._apply_color(points, color, bg)
396
+
397
+ @staticmethod
398
+ def get_sphere_3d_points(radius=5, segments=12, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
399
+ points = []
400
+ for i in range(segments):
401
+ theta = i / segments * math.pi
402
+ for j in range(segments):
403
+ phi = j / segments * 2 * math.pi
404
+ x = radius * math.sin(theta) * math.cos(phi)
405
+ y = radius * math.cos(theta)
406
+ z = radius * math.sin(theta) * math.sin(phi)
407
+ x2d, y2d = Render._project_3d(x, y, z, rotx, roty, rotz, scale, cx, cy)
408
+ points.append((x2d, y2d, char))
409
+ return Render._apply_color(points, color, bg)
410
+
411
+ @staticmethod
412
+ def get_cone_3d_points(radius=5, height=10, segments=12, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
413
+ points = []
414
+ base_points = []
415
+ for i in range(segments):
416
+ rad = i / segments * 2 * math.pi
417
+ x = radius * math.cos(rad)
418
+ z = radius * math.sin(rad)
419
+ base_points.append((x, -height/2, z))
420
+ apex = (0, height/2, 0)
421
+ all_vertices = base_points + [apex]
422
+ projected = []
423
+ for v in all_vertices:
424
+ x2d, y2d = Render._project_3d(v[0], v[1], v[2], rotx, roty, rotz, scale, cx, cy)
425
+ projected.append((x2d, y2d))
426
+ for i in range(segments):
427
+ j = (i + 1) % segments
428
+ x1, y1 = projected[i]
429
+ x2, y2 = projected[j]
430
+ points.extend(Render.get_line_points(x1, y1, x2, y2, char))
431
+ x1, y1 = projected[i]
432
+ x2, y2 = projected[-1]
433
+ points.extend(Render.get_line_points(x1, y1, x2, y2, char))
434
+ return Render._apply_color(points, color, bg)
435
+
436
+ @staticmethod
437
+ def get_torus_3d_points(radius=8, tube=3, segments=16, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
438
+ points = []
439
+ for i in range(segments):
440
+ theta = i / segments * 2 * math.pi
441
+ for j in range(segments):
442
+ phi = j / segments * 2 * math.pi
443
+ x = (radius + tube * math.cos(phi)) * math.cos(theta)
444
+ y = tube * math.sin(phi)
445
+ z = (radius + tube * math.cos(phi)) * math.sin(theta)
446
+ x2d, y2d = Render._project_3d(x, y, z, rotx, roty, rotz, scale, cx, cy)
447
+ points.append((x2d, y2d, char))
448
+ return Render._apply_color(points, color, bg)
449
+
450
+ @staticmethod
451
+ def get_ico_sphere_3d_points(radius=5, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
452
+ points = []
453
+ phi = (1 + math.sqrt(5)) / 2
454
+ vertices = [
455
+ (-1, phi, 0), ( 1, phi, 0), (-1, -phi, 0), ( 1, -phi, 0),
456
+ ( 0, -1, phi), ( 0, 1, phi), ( 0, -1, -phi), ( 0, 1, -phi),
457
+ ( phi, 0, -1), ( phi, 0, 1), (-phi, 0, -1), (-phi, 0, 1)
458
+ ]
459
+ vertices = [(x * radius / 1.618, y * radius / 1.618, z * radius / 1.618) for x, y, z in vertices]
460
+ edges = [
461
+ (0,1), (0,4), (0,5), (0,10), (0,11),
462
+ (1,2), (1,5), (1,7), (1,8), (1,9),
463
+ (2,3), (2,6), (2,7), (2,10), (2,11),
464
+ (3,4), (3,6), (3,8), (3,9), (3,11),
465
+ (4,5), (4,9), (4,11),
466
+ (5,7), (5,9),
467
+ (6,7), (6,10),
468
+ (7,8),
469
+ (8,9), (8,10),
470
+ (10,11)
471
+ ]
472
+ projected = []
473
+ for v in vertices:
474
+ x2d, y2d = Render._project_3d(v[0], v[1], v[2], rotx, roty, rotz, scale, cx, cy)
475
+ projected.append((x2d, y2d))
476
+ for edge in edges:
477
+ x1, y1 = projected[edge[0]]
478
+ x2, y2 = projected[edge[1]]
479
+ points.extend(Render.get_line_points(x1, y1, x2, y2, char))
480
+ return Render._apply_color(points, color, bg)
309
481
 
310
482
  class Animate:
311
483
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ConsoleFramework
3
- Version: 2.0.1
3
+ Version: 2.0.3
4
4
  Summary: The best Console Library of the Python.
5
5
  Author: Suleiman
6
6
  Author-email: steal.apet@mail.ru
@@ -10,12 +10,14 @@ Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.6
12
12
  Description-Content-Type: text/plain
13
+ License-File: LICENSE
13
14
  Dynamic: author
14
15
  Dynamic: author-email
15
16
  Dynamic: classifier
16
17
  Dynamic: description
17
18
  Dynamic: description-content-type
18
19
  Dynamic: license
20
+ Dynamic: license-file
19
21
  Dynamic: requires-python
20
22
  Dynamic: summary
21
23
 
@@ -1,9 +1,8 @@
1
- MANIFEST.in
1
+ LICENSE
2
2
  setup.py
3
3
  ConsoleFramework/__init__.py
4
4
  ConsoleFramework.egg-info/PKG-INFO
5
5
  ConsoleFramework.egg-info/SOURCES.txt
6
6
  ConsoleFramework.egg-info/dependency_links.txt
7
7
  ConsoleFramework.egg-info/not-zip-safe
8
- ConsoleFramework.egg-info/top_level.txt
9
- consoleframework/__init__.py
8
+ ConsoleFramework.egg-info/top_level.txt
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Suleiman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ConsoleFramework
3
- Version: 2.0.1
3
+ Version: 2.0.3
4
4
  Summary: The best Console Library of the Python.
5
5
  Author: Suleiman
6
6
  Author-email: steal.apet@mail.ru
@@ -10,12 +10,14 @@ Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.6
12
12
  Description-Content-Type: text/plain
13
+ License-File: LICENSE
13
14
  Dynamic: author
14
15
  Dynamic: author-email
15
16
  Dynamic: classifier
16
17
  Dynamic: description
17
18
  Dynamic: description-content-type
18
19
  Dynamic: license
20
+ Dynamic: license-file
19
21
  Dynamic: requires-python
20
22
  Dynamic: summary
21
23
 
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="ConsoleFramework",
5
- version="2.0.1",
5
+ version="2.0.3",
6
6
  description="The best Console Library of the Python.",
7
7
  long_description="ConsoleFramework - Pure Python library for console rendering, animations, processes and more. In this module ONLY standart python, no other modules.",
8
8
  long_description_content_type="text/plain",
@@ -1,4 +0,0 @@
1
- include consoleframework/*
2
- include consoleframework/core.cpp
3
- include consoleframework/__init__.py
4
- recursive-include consoleframework *.py *.cpp