IncludeCPP 4.2.2__py3-none-any.whl → 4.5.2__py3-none-any.whl

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.
Files changed (33) hide show
  1. includecpp/CHANGELOG.md +104 -115
  2. includecpp/DOCUMENTATION.md +208 -355
  3. includecpp/__init__.py +1 -1
  4. includecpp/__init__.pyi +1 -4
  5. includecpp/cli/commands.py +1220 -27
  6. includecpp/core/cpp_api_extensions.pyi +204 -200
  7. includecpp/core/cssl/CSSL_DOCUMENTATION.md +1505 -1467
  8. includecpp/core/cssl/__init__.py +317 -0
  9. includecpp/core/cssl/cpp/build/api.pyd +0 -0
  10. includecpp/core/cssl/cpp/build/cssl_core.pyi +323 -0
  11. includecpp/core/cssl/cpp/build/libgcc_s_seh-1.dll +0 -0
  12. includecpp/core/cssl/cpp/build/libstdc++-6.dll +0 -0
  13. includecpp/core/cssl/cpp/build/libwinpthread-1.dll +0 -0
  14. includecpp/core/cssl/cpp/cssl_core.cp +108 -0
  15. includecpp/core/cssl/cpp/cssl_lexer.hpp +280 -0
  16. includecpp/core/cssl/cssl_builtins.py +245 -20
  17. includecpp/core/cssl/cssl_compiler.py +448 -0
  18. includecpp/core/cssl/cssl_optimizer.py +833 -0
  19. includecpp/core/cssl/cssl_parser.py +945 -40
  20. includecpp/core/cssl/cssl_runtime.py +751 -38
  21. includecpp/core/cssl/cssl_syntax.py +17 -0
  22. includecpp/core/cssl/cssl_types.py +321 -0
  23. includecpp/core/cssl_bridge.py +36 -2
  24. includecpp/generator/parser.cpp +38 -14
  25. includecpp/vscode/cssl/package.json +15 -0
  26. includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +134 -2
  27. includecpp-4.5.2.dist-info/METADATA +277 -0
  28. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/RECORD +32 -23
  29. includecpp-4.2.2.dist-info/METADATA +0 -1008
  30. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/WHEEL +0 -0
  31. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/entry_points.txt +0 -0
  32. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/licenses/LICENSE +0 -0
  33. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,448 @@
1
+ """
2
+ CSSL Compiler Configuration - Special handling for CSSL C++ acceleration.
3
+
4
+ CSSL is a special case in IncludeCPP:
5
+ - Pre-built modules are bundled in the PyPI package for instant use
6
+ - If bundled module doesn't match user's platform, it rebuilds ONCE
7
+ - Global compiler/platform info stored in %AppData%/IncludeCPP/general.json
8
+ - IncludeCPP works normally for everything else
9
+
10
+ This module handles:
11
+ - Global compiler detection and caching
12
+ - Platform detection for module matching
13
+ - Auto-rebuild to bundled folder if needed
14
+ """
15
+
16
+ import os
17
+ import sys
18
+ import json
19
+ import platform
20
+ import subprocess
21
+ import shutil
22
+ from pathlib import Path
23
+ from typing import Optional, Dict, Any, List
24
+
25
+
26
+ # =============================================================================
27
+ # Global IncludeCPP Config (shared across all projects)
28
+ # =============================================================================
29
+
30
+ def get_includecpp_config_dir() -> Path:
31
+ """
32
+ Get global IncludeCPP config directory.
33
+
34
+ Returns:
35
+ Windows: %APPDATA%/IncludeCPP/
36
+ Linux/macOS: ~/.config/IncludeCPP/
37
+ """
38
+ if sys.platform == 'win32':
39
+ base = Path(os.environ.get('APPDATA', Path.home() / 'AppData' / 'Roaming'))
40
+ else:
41
+ base = Path.home() / '.config'
42
+
43
+ config_dir = base / 'IncludeCPP'
44
+ config_dir.mkdir(parents=True, exist_ok=True)
45
+ return config_dir
46
+
47
+
48
+ def get_global_config_path() -> Path:
49
+ """Get path to global IncludeCPP config file."""
50
+ return get_includecpp_config_dir() / 'general.json'
51
+
52
+
53
+ def get_cssl_bundled_dir() -> Path:
54
+ """
55
+ Get the bundled output directory for CSSL modules.
56
+ This is inside the PyPI package so users get pre-built modules.
57
+
58
+ Returns:
59
+ Path to includecpp/core/cssl/cpp/build/
60
+ """
61
+ cssl_dir = Path(__file__).parent
62
+ build_dir = cssl_dir / 'cpp' / 'build'
63
+ build_dir.mkdir(parents=True, exist_ok=True)
64
+ return build_dir
65
+
66
+
67
+ # Legacy function for compatibility
68
+ def get_cssl_config_dir() -> Path:
69
+ """Legacy: Get CSSL config directory. Now uses global config."""
70
+ return get_includecpp_config_dir()
71
+
72
+
73
+ def get_cssl_config_path() -> Path:
74
+ """Legacy: Get CSSL config path. Now uses global config."""
75
+ return get_global_config_path()
76
+
77
+
78
+ def get_cssl_build_dir() -> Path:
79
+ """
80
+ Get build directory for CSSL modules.
81
+ CSSL is special: builds go to the PyPI bundled folder.
82
+ """
83
+ return get_cssl_bundled_dir()
84
+
85
+
86
+ # =============================================================================
87
+ # Global Config Manager
88
+ # =============================================================================
89
+
90
+ class GlobalConfig:
91
+ """
92
+ Global IncludeCPP configuration.
93
+
94
+ Stores compiler, platform info in %AppData%/IncludeCPP/general.json
95
+ Shared across all IncludeCPP projects.
96
+ """
97
+
98
+ _instance = None
99
+ _config = None
100
+
101
+ def __new__(cls):
102
+ if cls._instance is None:
103
+ cls._instance = super().__new__(cls)
104
+ return cls._instance
105
+
106
+ def __init__(self):
107
+ if GlobalConfig._config is None:
108
+ self.config_path = get_global_config_path()
109
+ GlobalConfig._config = self._load_config()
110
+
111
+ @property
112
+ def config(self) -> Dict[str, Any]:
113
+ return GlobalConfig._config
114
+
115
+ @config.setter
116
+ def config(self, value: Dict[str, Any]):
117
+ GlobalConfig._config = value
118
+
119
+ def _load_config(self) -> Dict[str, Any]:
120
+ """Load config from file, or return empty dict if not exists."""
121
+ if self.config_path.exists():
122
+ try:
123
+ return json.loads(self.config_path.read_text(encoding='utf-8'))
124
+ except (json.JSONDecodeError, OSError):
125
+ pass
126
+ return {}
127
+
128
+ def _save_config(self):
129
+ """Save config to file."""
130
+ try:
131
+ self.config_path.write_text(
132
+ json.dumps(self.config, indent=2),
133
+ encoding='utf-8'
134
+ )
135
+ except OSError:
136
+ pass
137
+
138
+ def detect_compiler(self) -> Optional[str]:
139
+ """
140
+ Detect available C++ compiler.
141
+ Checks: g++, clang++, cl (MSVC on Windows)
142
+ """
143
+ compilers = ['g++', 'clang++']
144
+ if sys.platform == 'win32':
145
+ compilers.append('cl')
146
+
147
+ for compiler in compilers:
148
+ if shutil.which(compiler):
149
+ try:
150
+ result = subprocess.run(
151
+ [compiler, '--version'],
152
+ capture_output=True,
153
+ timeout=5
154
+ )
155
+ if result.returncode == 0:
156
+ return compiler
157
+ except (subprocess.SubprocessError, OSError):
158
+ continue
159
+ return None
160
+
161
+ def detect_compiler_version(self, compiler: str) -> Optional[str]:
162
+ """Get compiler version string."""
163
+ try:
164
+ result = subprocess.run(
165
+ [compiler, '--version'],
166
+ capture_output=True,
167
+ text=True,
168
+ timeout=5
169
+ )
170
+ if result.returncode == 0:
171
+ return result.stdout.split('\n')[0].strip()
172
+ except (subprocess.SubprocessError, OSError):
173
+ pass
174
+ return None
175
+
176
+ def detect_platform(self) -> Dict[str, str]:
177
+ """Detect platform info for module matching."""
178
+ return {
179
+ 'system': platform.system(),
180
+ 'machine': platform.machine(),
181
+ 'python_version': f"{sys.version_info.major}{sys.version_info.minor}",
182
+ 'python_full_version': platform.python_version(),
183
+ 'platform': sys.platform,
184
+ }
185
+
186
+ def get_module_suffix(self) -> str:
187
+ """Get expected module suffix for current platform."""
188
+ info = self.detect_platform()
189
+ py_ver = info['python_version']
190
+
191
+ if info['platform'] == 'win32':
192
+ return f".cp{py_ver}-win_amd64.pyd"
193
+ elif info['platform'] == 'linux':
194
+ return f".cpython-{py_ver}-x86_64-linux-gnu.so"
195
+ elif info['platform'] == 'darwin':
196
+ arch = 'arm64' if info['machine'] == 'arm64' else 'x86_64'
197
+ return f".cpython-{py_ver}-{arch}-darwin.so"
198
+ return ".pyd" if info['platform'] == 'win32' else ".so"
199
+
200
+ def setup(self) -> Dict[str, Any]:
201
+ """
202
+ First-run setup - detect and store compiler/platform info.
203
+ Called automatically on first import.
204
+ """
205
+ if self.config.get('initialized'):
206
+ return self.config
207
+
208
+ compiler = self.detect_compiler()
209
+ compiler_version = self.detect_compiler_version(compiler) if compiler else None
210
+ platform_info = self.detect_platform()
211
+
212
+ self.config = {
213
+ 'initialized': True,
214
+ 'compiler': compiler,
215
+ 'compiler_version': compiler_version,
216
+ 'platform': platform_info,
217
+ 'module_suffix': self.get_module_suffix(),
218
+ 'can_compile': compiler is not None,
219
+ }
220
+
221
+ self._save_config()
222
+ return self.config
223
+
224
+ def refresh(self) -> Dict[str, Any]:
225
+ """Force refresh of compiler/platform detection."""
226
+ self.config['initialized'] = False
227
+ return self.setup()
228
+
229
+
230
+ # =============================================================================
231
+ # CSSL-Specific Compiler Config (wraps GlobalConfig)
232
+ # =============================================================================
233
+
234
+ class CSSLCompilerConfig:
235
+ """
236
+ CSSL-specific compiler configuration.
237
+
238
+ CSSL is special:
239
+ - Uses global config from %AppData%/IncludeCPP/general.json
240
+ - Builds output to PyPI bundled folder (includecpp/core/cssl/cpp/build/)
241
+ - Auto-rebuilds if bundled module doesn't match platform
242
+ """
243
+
244
+ def __init__(self):
245
+ self.global_config = GlobalConfig()
246
+ self.config_path = get_global_config_path()
247
+
248
+ @property
249
+ def config(self) -> Dict[str, Any]:
250
+ return self.global_config.config
251
+
252
+ def _load_config(self) -> Dict[str, Any]:
253
+ return self.global_config._load_config()
254
+
255
+ def _save_config(self):
256
+ self.global_config._save_config()
257
+
258
+ def detect_compiler(self) -> Optional[str]:
259
+ return self.global_config.detect_compiler()
260
+
261
+ def detect_compiler_version(self, compiler: str) -> Optional[str]:
262
+ return self.global_config.detect_compiler_version(compiler)
263
+
264
+ def detect_platform(self) -> Dict[str, str]:
265
+ return self.global_config.detect_platform()
266
+
267
+ def get_prebuilt_suffix(self) -> str:
268
+ return self.global_config.get_module_suffix()
269
+
270
+ def get_all_possible_suffixes(self) -> List[str]:
271
+ """Get all possible module suffixes for current platform."""
272
+ info = self.detect_platform()
273
+ py_ver = info['python_version']
274
+
275
+ suffixes = [self.get_prebuilt_suffix()]
276
+
277
+ if info['platform'] == 'win32':
278
+ suffixes.extend(['.pyd', f'.cp{py_ver}-win32.pyd'])
279
+ elif info['platform'] == 'linux':
280
+ suffixes.extend(['.so', f'.cpython-{py_ver}-linux-gnu.so'])
281
+ elif info['platform'] == 'darwin':
282
+ suffixes.extend(['.so', '.dylib'])
283
+
284
+ return suffixes
285
+
286
+ def setup_first_run(self) -> Dict[str, Any]:
287
+ """Setup global config on first run."""
288
+ return self.global_config.setup()
289
+
290
+ def refresh(self) -> Dict[str, Any]:
291
+ """Force refresh of config."""
292
+ return self.global_config.refresh()
293
+
294
+ def can_compile(self) -> bool:
295
+ """Check if compilation is possible."""
296
+ if not self.config.get('initialized'):
297
+ self.setup_first_run()
298
+ return self.config.get('can_compile', False)
299
+
300
+ def get_compiler(self) -> Optional[str]:
301
+ """Get configured compiler."""
302
+ if not self.config.get('initialized'):
303
+ self.setup_first_run()
304
+ return self.config.get('compiler')
305
+
306
+ def get_platform_info(self) -> Dict[str, str]:
307
+ """Get stored platform info."""
308
+ if not self.config.get('initialized'):
309
+ self.setup_first_run()
310
+ return self.config.get('platform', self.detect_platform())
311
+
312
+ def get_build_dir(self) -> Path:
313
+ """Get CSSL build directory (bundled in PyPI folder)."""
314
+ return get_cssl_bundled_dir()
315
+
316
+
317
+ # =============================================================================
318
+ # CSSL Module Compilation
319
+ # =============================================================================
320
+
321
+ def compile_cssl_core(force: bool = False) -> Optional[Path]:
322
+ """
323
+ Compile cssl_core module to the bundled PyPI folder.
324
+
325
+ CSSL builds go directly to includecpp/core/cssl/cpp/build/
326
+ so they're bundled with the package for other users.
327
+
328
+ Args:
329
+ force: If True, rebuild even if module exists
330
+
331
+ Returns:
332
+ Path to compiled module, or None if failed
333
+ """
334
+ config = CSSLCompilerConfig()
335
+ config.setup_first_run()
336
+
337
+ if not config.can_compile():
338
+ return None
339
+
340
+ # Output to bundled folder
341
+ build_dir = get_cssl_bundled_dir()
342
+ suffix = config.get_prebuilt_suffix()
343
+ output_path = build_dir / f'cssl_core{suffix}'
344
+
345
+ # Check if already built
346
+ if output_path.exists() and not force:
347
+ return output_path
348
+
349
+ # Get source directory
350
+ cssl_dir = Path(__file__).parent
351
+ cpp_dir = cssl_dir / 'cpp'
352
+
353
+ if not (cpp_dir / 'cpp.proj').exists():
354
+ return None
355
+
356
+ # Build using includecpp with output to bundled folder
357
+ try:
358
+ # IncludeCPP builds to AppData by default, so we build then copy
359
+ result = subprocess.run(
360
+ [sys.executable, '-m', 'includecpp', 'rebuild', '--clean'],
361
+ cwd=cpp_dir,
362
+ capture_output=True,
363
+ text=True,
364
+ timeout=300
365
+ )
366
+
367
+ if result.returncode == 0:
368
+ # Find built module and copy to bundled folder
369
+ appdata = Path(os.environ.get('APPDATA', ''))
370
+ icpp_build = appdata / 'cssl_core-g-build-proj' / 'bindings'
371
+
372
+ # Copy api.pyd to bundled folder
373
+ api_path = icpp_build / 'api.pyd'
374
+ if not api_path.exists():
375
+ api_path = icpp_build / 'api.so'
376
+
377
+ if api_path.exists():
378
+ # Copy as api.pyd (IncludeCPP format)
379
+ dest = build_dir / api_path.name
380
+ shutil.copy2(api_path, dest)
381
+
382
+ # Also copy as cssl_core.{suffix} for direct loading
383
+ shutil.copy2(api_path, output_path)
384
+ return output_path
385
+
386
+ except (subprocess.SubprocessError, OSError):
387
+ pass
388
+
389
+ return None
390
+
391
+
392
+ def get_cssl_core_path() -> Optional[Path]:
393
+ """
394
+ Get path to cssl_core module.
395
+
396
+ CSSL special handling:
397
+ 1. Check bundled folder in PyPI package
398
+ 2. If not found and can compile, rebuild to bundled folder
399
+
400
+ Returns:
401
+ Path to module, or None if not found
402
+ """
403
+ config = CSSLCompilerConfig()
404
+ config.setup_first_run()
405
+
406
+ suffixes = config.get_all_possible_suffixes()
407
+ build_dir = get_cssl_bundled_dir()
408
+
409
+ # Check for cssl_core.{suffix}
410
+ for suffix in suffixes:
411
+ module_path = build_dir / f'cssl_core{suffix}'
412
+ if module_path.exists():
413
+ return module_path
414
+
415
+ # Check for api.pyd (IncludeCPP format)
416
+ api_path = build_dir / 'api.pyd'
417
+ if not api_path.exists():
418
+ api_path = build_dir / 'api.so'
419
+ if api_path.exists():
420
+ return api_path
421
+
422
+ return None
423
+
424
+
425
+ def ensure_cssl_module() -> Optional[Path]:
426
+ """
427
+ Ensure CSSL C++ module is available.
428
+
429
+ If bundled module doesn't exist or doesn't match platform,
430
+ automatically rebuild to the bundled folder (ONCE).
431
+
432
+ Returns:
433
+ Path to module, or None if unavailable
434
+ """
435
+ # Check if bundled module exists
436
+ module_path = get_cssl_core_path()
437
+ if module_path:
438
+ return module_path
439
+
440
+ # No bundled module - try to compile
441
+ config = CSSLCompilerConfig()
442
+ config.setup_first_run()
443
+
444
+ if config.can_compile():
445
+ # Rebuild to bundled folder
446
+ return compile_cssl_core(force=True)
447
+
448
+ return None