IncludeCPP 4.6.0__py3-none-any.whl → 4.9.3__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 (35) hide show
  1. includecpp/CHANGELOG.md +241 -0
  2. includecpp/__init__.py +89 -3
  3. includecpp/__init__.pyi +2 -1
  4. includecpp/cli/commands.py +1747 -266
  5. includecpp/cli/config_parser.py +1 -1
  6. includecpp/core/build_manager.py +64 -13
  7. includecpp/core/cpp_api_extensions.pyi +43 -270
  8. includecpp/core/cssl/CSSL_DOCUMENTATION.md +1799 -1445
  9. includecpp/core/cssl/cpp/build/api.pyd +0 -0
  10. includecpp/core/cssl/cpp/build/api.pyi +274 -0
  11. includecpp/core/cssl/cpp/build/cssl_core.pyi +0 -99
  12. includecpp/core/cssl/cpp/cssl_core.cp +2 -23
  13. includecpp/core/cssl/cssl_builtins.py +2116 -171
  14. includecpp/core/cssl/cssl_builtins.pyi +1324 -104
  15. includecpp/core/cssl/cssl_compiler.py +4 -1
  16. includecpp/core/cssl/cssl_modules.py +605 -6
  17. includecpp/core/cssl/cssl_optimizer.py +12 -1
  18. includecpp/core/cssl/cssl_parser.py +1048 -52
  19. includecpp/core/cssl/cssl_runtime.py +2041 -131
  20. includecpp/core/cssl/cssl_syntax.py +405 -277
  21. includecpp/core/cssl/cssl_types.py +5891 -1655
  22. includecpp/core/cssl_bridge.py +427 -4
  23. includecpp/core/error_catalog.py +54 -10
  24. includecpp/core/homeserver.py +1037 -0
  25. includecpp/generator/parser.cpp +203 -39
  26. includecpp/generator/parser.h +15 -1
  27. includecpp/templates/cpp.proj.template +1 -1
  28. includecpp/vscode/cssl/snippets/cssl.snippets.json +163 -0
  29. includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +87 -12
  30. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/METADATA +81 -10
  31. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/RECORD +35 -33
  32. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/WHEEL +1 -1
  33. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/entry_points.txt +0 -0
  34. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/licenses/LICENSE +0 -0
  35. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/top_level.txt +0 -0
@@ -405,7 +405,7 @@ class CsslLang:
405
405
  return 'cssl-pl'
406
406
  return 'cssl'
407
407
 
408
- def run(self, path_or_code: str, *args) -> Any:
408
+ def run(self, path_or_code: str, *args, force_python: bool = False) -> Any:
409
409
  """
410
410
  Execute CSSL code or file.
411
411
 
@@ -415,6 +415,7 @@ class CsslLang:
415
415
  Args:
416
416
  path_or_code: Path to .cssl file or CSSL code string
417
417
  *args: Arguments to pass to the script (accessible via parameter.get())
418
+ force_python: Force Python interpreter (for full builtin support)
418
419
 
419
420
  Returns:
420
421
  Execution result. If parameter.return() was called, returns
@@ -444,9 +445,63 @@ class CsslLang:
444
445
  # Path too long or invalid - treat as code
445
446
  pass
446
447
 
448
+ # v4.6.5: Check for native/unative keywords
449
+ import re
450
+ has_native = bool(re.search(r'\bnative\b', source))
451
+ has_unative = bool(re.search(r'\bunative\b', source))
452
+
453
+ # v4.8.5: Python-only builtins (not available in C++ runtime)
454
+ # Auto-detect and use Python when these are present
455
+ PYTHON_ONLY_BUILTINS = {
456
+ # os/sys replacements
457
+ 'getcwd', 'chdir', 'mkdir', 'rmdir', 'rmfile', 'rename',
458
+ 'argv', 'argc', 'platform', 'version', 'exit',
459
+ # File operations
460
+ 'listdir', 'makedirs', 'removefile', 'removedir', 'copyfile', 'movefile',
461
+ 'readfile', 'writefile', 'appendfile', 'readlines', 'filesize',
462
+ 'pathexists', 'exists', 'isfile', 'isdir',
463
+ # Environment
464
+ 'env', 'setenv',
465
+ # Module imports
466
+ 'pyimport', 'cppimport', 'include', 'libinclude',
467
+ # Advanced features
468
+ 'initpy', 'initsh', 'appexec', 'createcmd',
469
+ # Instance reflection
470
+ 'instance_getMethods', 'instance_getClasses', 'instance_getVars',
471
+ 'instance_getAll', 'instance_call', 'instance_has', 'instance_type',
472
+ # Console/terminal functions
473
+ 'clear', 'input', 'color',
474
+ 'red', 'green', 'blue', 'yellow', 'cyan', 'magenta', 'white', 'black',
475
+ 'bold', 'dim', 'italic', 'underline', 'blink', 'reverse',
476
+ # v4.9.0: Memory introspection and snapshot (Python reflection)
477
+ 'memory', 'snapshot', 'address', 'reflect',
478
+ }
479
+
480
+ # Check if source uses any Python-only builtins
481
+ needs_python = any(
482
+ re.search(rf'\b{builtin}\s*\(', source)
483
+ for builtin in PYTHON_ONLY_BUILTINS
484
+ )
485
+
486
+ # Also detect module usage that requires Python runtime
487
+ has_python_modules = bool(re.search(r'\b(fmt|Console|Process|Config|Server)::', source))
488
+
489
+ # v4.9.3: Detect python:: namespace usage (parameter passing, pythonize, etc.)
490
+ has_python_namespace = bool(re.search(r'\bpython::', source))
491
+
492
+ # v4.9.0: Detect bit/byte/address type declarations (Python-only types)
493
+ has_binary_types = bool(re.search(r'\b(bit|byte|address)\s+\w+', source))
494
+
495
+ # unative forces Python execution (skip C++ entirely)
496
+ # force_python flag also skips C++ (for full builtin support like getcwd, listdir)
497
+ # Auto-detect Python-only builtins and use Python automatically
498
+ # v4.9.0: Also skip C++ for bit/byte types (Python-only)
499
+ # v4.9.3: Also skip C++ for python:: namespace usage
500
+ if has_unative or force_python or needs_python or has_python_modules or has_binary_types or has_python_namespace:
501
+ pass # Skip C++ block, go directly to Python execution
447
502
  # Try C++ accelerated execution first (375x faster)
448
503
  # Only use C++ for simple scripts without parameter passing
449
- if not args:
504
+ elif not args:
450
505
  try:
451
506
  from .cssl import run_cssl, run_cssl_file
452
507
  if is_file and file_path:
@@ -454,10 +509,17 @@ class CsslLang:
454
509
  else:
455
510
  return run_cssl(source)
456
511
  except Exception as cpp_error:
512
+ # native keyword forces C++ - no fallback allowed
513
+ if has_native:
514
+ raise RuntimeError(f"C++ execution failed (native mode): {cpp_error}") from cpp_error
515
+
457
516
  # Fall back to Python for unsupported features
458
- # v4.5.2: Also fall back on "unexpected token" - C++ parser doesn't support all syntax
517
+ # v4.8.5: Extended fallback triggers for advanced CSSL syntax
459
518
  error_msg = str(cpp_error).lower()
460
- fallback_triggers = ['unsupported', 'not implemented', 'unexpected token']
519
+ fallback_triggers = [
520
+ 'unsupported', 'not implemented', 'unexpected', 'expected',
521
+ 'syntax error', 'unknown identifier', 'undefined', 'not defined'
522
+ ]
461
523
  should_fallback = any(trigger in error_msg for trigger in fallback_triggers)
462
524
  if not should_fallback:
463
525
  # Real error - re-raise it
@@ -467,6 +529,10 @@ class CsslLang:
467
529
  # Python execution (for scripts with args or when C++ fails)
468
530
  runtime = self._get_runtime()
469
531
 
532
+ # v4.8.5: Strip unative directive before parsing (it's just a marker)
533
+ if has_unative:
534
+ source = re.sub(r'\bunative\s*;?\s*', '', source, count=1)
535
+
470
536
  # Set arguments in runtime scope
471
537
  from .cssl import Parameter
472
538
  param = Parameter(list(args))
@@ -1555,6 +1621,357 @@ def makemodule(
1555
1621
  return get_cssl().makemodule(main_script, payload_script, name, bind)
1556
1622
 
1557
1623
 
1624
+ # =============================================================================
1625
+ # v4.6.5: CsslWatcher - Live Python Instance Collection for CSSL Access
1626
+ # =============================================================================
1627
+
1628
+ # Global registry of active watchers
1629
+ _active_watchers: Dict[str, 'CsslWatcher'] = {}
1630
+
1631
+
1632
+ class CsslWatcher:
1633
+ """
1634
+ Live Python instance watcher that collects all active instances, classes,
1635
+ and functions from the Python scope and makes them available to CSSL.
1636
+
1637
+ Usage in Python:
1638
+ from includecpp.core.cssl_bridge import CsslWatcher
1639
+
1640
+ cwatcher = CsslWatcher(id="MyWatcher")
1641
+ cwatcher.start()
1642
+
1643
+ class Game:
1644
+ def __init__(self):
1645
+ self.score = 0
1646
+ def start(self):
1647
+ print("Game started!")
1648
+
1649
+ game = Game()
1650
+
1651
+ # ... later
1652
+ cwatcher.end()
1653
+
1654
+ Usage in CSSL:
1655
+ # Get all instances from a watcher
1656
+ all_instances = watcher::get("MyWatcher");
1657
+
1658
+ # Access Python class/instance
1659
+ pygameclass = all_instances['Game'];
1660
+ game_instance = all_instances['game'];
1661
+
1662
+ # Call Python methods
1663
+ game_instance.start();
1664
+
1665
+ # Bidirectional: CSSL can overwrite Python functions
1666
+ int start() : overwrites all_instances['Game.start'] {
1667
+ printl("Overwritten by CSSL!");
1668
+ return 0;
1669
+ }
1670
+ """
1671
+
1672
+ def __init__(self, id: str, auto_collect: bool = True, depth: int = 1):
1673
+ """
1674
+ Initialize a new CsslWatcher.
1675
+
1676
+ Args:
1677
+ id: Unique identifier for this watcher (used in watcher::get("id"))
1678
+ auto_collect: If True, automatically collect instances periodically
1679
+ depth: Stack frame depth to look for variables (1 = caller's scope)
1680
+ """
1681
+ self._id = id
1682
+ self._auto_collect = auto_collect
1683
+ self._depth = depth
1684
+ self._running = False
1685
+ self._thread: Optional[threading.Thread] = None
1686
+ self._lock = threading.Lock()
1687
+ self._instances: Dict[str, Any] = {}
1688
+ self._classes: Dict[str, type] = {}
1689
+ self._functions: Dict[str, Callable] = {}
1690
+ self._caller_frame = None
1691
+ self._caller_locals = {}
1692
+ self._caller_globals = {}
1693
+
1694
+ @property
1695
+ def id(self) -> str:
1696
+ """Get the watcher ID."""
1697
+ return self._id
1698
+
1699
+ def start(self) -> 'CsslWatcher':
1700
+ """
1701
+ Start the watcher. Collects instances from the caller's scope
1702
+ and registers this watcher globally.
1703
+
1704
+ Returns:
1705
+ self for chaining
1706
+ """
1707
+ import inspect
1708
+
1709
+ # Get caller's frame
1710
+ frame = inspect.currentframe()
1711
+ try:
1712
+ # Go up the stack to find the caller
1713
+ for _ in range(self._depth + 1):
1714
+ if frame.f_back:
1715
+ frame = frame.f_back
1716
+
1717
+ self._caller_frame = frame
1718
+ self._caller_locals = frame.f_locals
1719
+ self._caller_globals = frame.f_globals
1720
+ finally:
1721
+ del frame
1722
+
1723
+ # Initial collection
1724
+ self._collect_instances()
1725
+
1726
+ # Register globally
1727
+ with self._lock:
1728
+ _active_watchers[self._id] = self
1729
+ self._running = True
1730
+
1731
+ # Start background thread if auto_collect
1732
+ if self._auto_collect:
1733
+ self._thread = threading.Thread(
1734
+ target=self._background_collect,
1735
+ daemon=True,
1736
+ name=f"CsslWatcher-{self._id}"
1737
+ )
1738
+ self._thread.start()
1739
+
1740
+ return self
1741
+
1742
+ def end(self) -> None:
1743
+ """Stop the watcher and unregister it."""
1744
+ with self._lock:
1745
+ self._running = False
1746
+ if self._id in _active_watchers:
1747
+ del _active_watchers[self._id]
1748
+
1749
+ if self._thread and self._thread.is_alive():
1750
+ self._thread.join(timeout=1.0)
1751
+
1752
+ self._caller_frame = None
1753
+ self._caller_locals = {}
1754
+ self._caller_globals = {}
1755
+
1756
+ def stop(self) -> None:
1757
+ """Alias for end()."""
1758
+ self.end()
1759
+
1760
+ def _collect_instances(self) -> None:
1761
+ """Collect all instances, classes, and functions from the watched scope."""
1762
+ import inspect
1763
+
1764
+ with self._lock:
1765
+ # Combine locals and globals
1766
+ all_vars = {**self._caller_globals, **self._caller_locals}
1767
+
1768
+ for name, obj in all_vars.items():
1769
+ # Skip private/magic names and modules
1770
+ if name.startswith('_'):
1771
+ continue
1772
+ if inspect.ismodule(obj):
1773
+ continue
1774
+
1775
+ # Classify the object
1776
+ if inspect.isclass(obj):
1777
+ self._classes[name] = obj
1778
+ # Also collect class methods
1779
+ for method_name, method in inspect.getmembers(obj, predicate=inspect.isfunction):
1780
+ if not method_name.startswith('_'):
1781
+ self._functions[f"{name}.{method_name}"] = method
1782
+ elif callable(obj) and not isinstance(obj, type):
1783
+ self._functions[name] = obj
1784
+ elif hasattr(obj, '__class__') and not isinstance(obj, (int, float, str, bool, list, dict, tuple, set)):
1785
+ # It's an instance of a custom class
1786
+ self._instances[name] = obj
1787
+ # Also collect instance methods
1788
+ obj_class = obj.__class__
1789
+ for method_name in dir(obj):
1790
+ if not method_name.startswith('_'):
1791
+ try:
1792
+ method = getattr(obj, method_name)
1793
+ if callable(method):
1794
+ self._functions[f"{name}.{method_name}"] = method
1795
+ except Exception:
1796
+ pass
1797
+
1798
+ def _background_collect(self) -> None:
1799
+ """Background thread for periodic collection."""
1800
+ import time
1801
+ while self._running:
1802
+ time.sleep(0.5) # Collect every 500ms
1803
+ if self._running:
1804
+ try:
1805
+ self._collect_instances()
1806
+ except Exception:
1807
+ pass
1808
+
1809
+ def refresh(self) -> None:
1810
+ """Manually refresh the collected instances."""
1811
+ self._collect_instances()
1812
+
1813
+ def get_all(self) -> Dict[str, Any]:
1814
+ """
1815
+ Get all collected items as a dictionary.
1816
+
1817
+ Returns:
1818
+ Dict with all instances, classes, and functions
1819
+ """
1820
+ with self._lock:
1821
+ result = {}
1822
+ result.update(self._classes)
1823
+ result.update(self._instances)
1824
+ result.update(self._functions)
1825
+ return result
1826
+
1827
+ def get(self, path: str) -> Any:
1828
+ """
1829
+ Get a specific item by path.
1830
+
1831
+ Args:
1832
+ path: Name or dotted path like 'Game' or 'game.start'
1833
+
1834
+ Returns:
1835
+ The requested object or None
1836
+ """
1837
+ with self._lock:
1838
+ # Check direct matches first
1839
+ if path in self._classes:
1840
+ return self._classes[path]
1841
+ if path in self._instances:
1842
+ return self._instances[path]
1843
+ if path in self._functions:
1844
+ return self._functions[path]
1845
+
1846
+ # Handle dotted paths
1847
+ if '.' in path:
1848
+ parts = path.split('.', 1)
1849
+ base = parts[0]
1850
+ rest = parts[1]
1851
+
1852
+ # Get the base object
1853
+ obj = self._instances.get(base) or self._classes.get(base)
1854
+ if obj:
1855
+ try:
1856
+ # Navigate the path
1857
+ for part in rest.split('.'):
1858
+ obj = getattr(obj, part)
1859
+ return obj
1860
+ except AttributeError:
1861
+ pass
1862
+
1863
+ return None
1864
+
1865
+ def set(self, path: str, value: Any) -> bool:
1866
+ """
1867
+ Set/overwrite a value at the given path (bidirectional).
1868
+
1869
+ Args:
1870
+ path: Name or dotted path like 'Game.start'
1871
+ value: New value (function, class, or instance)
1872
+
1873
+ Returns:
1874
+ True if successful
1875
+ """
1876
+ with self._lock:
1877
+ if '.' in path:
1878
+ parts = path.rsplit('.', 1)
1879
+ base_path = parts[0]
1880
+ attr_name = parts[1]
1881
+
1882
+ # Get the base object
1883
+ obj = self.get(base_path)
1884
+ if obj:
1885
+ try:
1886
+ setattr(obj, attr_name, value)
1887
+ # Update our registry
1888
+ self._functions[path] = value
1889
+ return True
1890
+ except Exception:
1891
+ pass
1892
+ else:
1893
+ # Direct assignment to scope
1894
+ if callable(value) and not isinstance(value, type):
1895
+ self._functions[path] = value
1896
+ elif isinstance(value, type):
1897
+ self._classes[path] = value
1898
+ else:
1899
+ self._instances[path] = value
1900
+
1901
+ # Also update caller's scope
1902
+ if path in self._caller_locals:
1903
+ self._caller_locals[path] = value
1904
+ elif path in self._caller_globals:
1905
+ self._caller_globals[path] = value
1906
+
1907
+ return True
1908
+
1909
+ return False
1910
+
1911
+ def __getitem__(self, key: str) -> Any:
1912
+ """Dict-like access: watcher['Game']"""
1913
+ return self.get(key)
1914
+
1915
+ def __setitem__(self, key: str, value: Any) -> None:
1916
+ """Dict-like assignment: watcher['Game.start'] = new_func"""
1917
+ self.set(key, value)
1918
+
1919
+ def __contains__(self, key: str) -> bool:
1920
+ """Check if key exists: 'Game' in watcher"""
1921
+ return self.get(key) is not None
1922
+
1923
+ def __repr__(self) -> str:
1924
+ with self._lock:
1925
+ return (f"CsslWatcher(id='{self._id}', "
1926
+ f"classes={len(self._classes)}, "
1927
+ f"instances={len(self._instances)}, "
1928
+ f"functions={len(self._functions)}, "
1929
+ f"running={self._running})")
1930
+
1931
+
1932
+ def watcher_get(watcher_id: str) -> Optional[Dict[str, Any]]:
1933
+ """
1934
+ Get all instances from a watcher by ID.
1935
+ This is the Python-side implementation for watcher::get("id").
1936
+
1937
+ Args:
1938
+ watcher_id: The watcher's unique ID
1939
+
1940
+ Returns:
1941
+ Dict of all collected instances, classes, and functions
1942
+ """
1943
+ if watcher_id in _active_watchers:
1944
+ return _active_watchers[watcher_id].get_all()
1945
+ return None
1946
+
1947
+
1948
+ def watcher_set(watcher_id: str, path: str, value: Any) -> bool:
1949
+ """
1950
+ Set a value in a watcher (bidirectional overwrite).
1951
+
1952
+ Args:
1953
+ watcher_id: The watcher's unique ID
1954
+ path: Path to the item (e.g., 'Game.start')
1955
+ value: New value
1956
+
1957
+ Returns:
1958
+ True if successful
1959
+ """
1960
+ if watcher_id in _active_watchers:
1961
+ return _active_watchers[watcher_id].set(path, value)
1962
+ return False
1963
+
1964
+
1965
+ def get_watcher(watcher_id: str) -> Optional[CsslWatcher]:
1966
+ """Get a watcher instance by ID."""
1967
+ return _active_watchers.get(watcher_id)
1968
+
1969
+
1970
+ def list_watchers() -> List[str]:
1971
+ """List all active watcher IDs."""
1972
+ return list(_active_watchers.keys())
1973
+
1974
+
1558
1975
  # Export all
1559
1976
  __all__ = [
1560
1977
  'CsslLang',
@@ -1589,4 +2006,10 @@ __all__ = [
1589
2006
  'shared',
1590
2007
  'get_shared',
1591
2008
  'cleanup_shared',
2009
+ # v4.6.5: CsslWatcher for live Python instance access
2010
+ 'CsslWatcher',
2011
+ 'watcher_get',
2012
+ 'watcher_set',
2013
+ 'get_watcher',
2014
+ 'list_watchers',
1592
2015
  ]
@@ -10,6 +10,28 @@ from typing import Optional, Dict, Tuple
10
10
  # Error catalog: pattern -> (message, category)
11
11
  # Categories: beginner, compiler, linker, cmake, pybind11, runtime, plugin, import
12
12
  ERROR_CATALOG = {
13
+ # ========== PRIORITY: FILE LOCK ERRORS (checked FIRST) ==========
14
+ # These must be at the top to catch file-locked errors before other patterns match
15
+ "FILE_LOCKED_WIN32": {
16
+ "pattern": r"WinError 32|being used by another process|cannot access.*because.*used|process cannot access|PermissionError.*api\.pyd|Permission denied.*\.pyd",
17
+ "message": "Module file is locked by another process!\n\nThis usually means:\n - A compiled .exe using this module is still running\n - Another Python script has imported this module\n - Your IDE has the file open\n\nFix:\n 1. Close any running .exe that uses this module\n 2. Close Python REPL/scripts using this module\n 3. Restart your IDE if it imported the module\n 4. Then run 'includecpp rebuild' again",
18
+ "category": "import"
19
+ },
20
+ "FILE_LOCKED_COPY": {
21
+ "pattern": r"copy.*failed.*permission|cannot (copy|overwrite|write).*\.pyd|failed.*copy.*bindings",
22
+ "message": "Cannot overwrite module file - it's locked!\n\nThe compiled .pyd/.so file is in use by another process.\n\nFix:\n 1. Close any running .exe that uses this module\n 2. Close all Python processes that imported this module\n 3. Run 'includecpp rebuild' again",
23
+ "category": "import"
24
+ },
25
+ "FILE_LOCKED_LINKER": {
26
+ "pattern": r"cannot open output file.*\.pyd|cannot open.*api\.pyd|ld\.exe:.*cannot open|ld\.exe:.*Permission denied|ld:.*cannot open output|error: unable to open output file|collect2.*ld.*Permission denied|LINK :.*cannot open.*\.pyd",
27
+ "message": "Linker cannot write output file - it's locked!\n\nThe .pyd/.so file is being used by another process.\n\nFix:\n 1. Close any running .exe using this module\n 2. Close Python/IDE that imported this module\n 3. Run 'includecpp rebuild --clean'",
28
+ "category": "linker"
29
+ },
30
+ "PERMISSION_DENIED_PYD": {
31
+ "pattern": r"Permission denied.*\.pyd|Permission denied.*\.so|PermissionError.*bindings",
32
+ "message": "Permission denied writing module file!\n\nThe .pyd/.so file is locked (probably by a running process).\n\nFix: Close any .exe or Python script using this module, then rebuild.",
33
+ "category": "import"
34
+ },
13
35
  # ========== A. BEGINNER MISTAKES (1-20) ==========
14
36
  "MODULE_NOT_BUILT": {
15
37
  "pattern": r"Module '(\w+)'.*not found|No module named '(\w+)'",
@@ -208,11 +230,8 @@ ERROR_CATALOG = {
208
230
  "message": "Array index possibly out of bounds.\n\nFix: Check array indices",
209
231
  "category": "compiler"
210
232
  },
211
- "SIGNED_UNSIGNED": {
212
- "pattern": r"signed.*unsigned|comparison.*signed",
213
- "message": "Comparing signed and unsigned.\n\nFix: Cast one to match the other",
214
- "category": "compiler"
215
- },
233
+ # SIGNED_UNSIGNED removed in v4.6.6 - this is a warning, not an error
234
+ # pybind11 generates code that triggers this warning, we suppress it with -Wno-sign-compare
216
235
  "REDEFINITION": {
217
236
  "pattern": r"redefinition of ['\"`]([^'\"`]+)['\"`]",
218
237
  "message": "'{name}' defined twice.\n\nFix: Remove duplicate or use different names",
@@ -276,12 +295,12 @@ ERROR_CATALOG = {
276
295
  "category": "linker"
277
296
  },
278
297
  "MISSING_LIBSTDCPP": {
279
- "pattern": r"libstdc\+\+|cannot find.*stdc\+\+",
298
+ "pattern": r"cannot find -lstdc\+\+|ld:.*cannot find.*libstdc\+\+|error:.*libstdc\+\+\.so.*not found|/usr/lib.*libstdc\+\+.*not found",
280
299
  "message": "C++ runtime missing.\n\nFix: Install gcc-libs/libstdc++ package",
281
300
  "category": "linker"
282
301
  },
283
302
  "MISSING_LIBPYTHON": {
284
- "pattern": r"libpython|cannot find.*python",
303
+ "pattern": r"cannot find -lpython|cannot find.*libpython|error:.*libpython.*not found|ld:.*-lpython.*not found",
285
304
  "message": "Python dev libs missing.\n\nFix: Install python3-dev/python3-devel",
286
305
  "category": "linker"
287
306
  },
@@ -655,14 +674,39 @@ ERROR_CATALOG = {
655
674
  "message": "Module not in Python path.\n\nFix: Check PYTHONPATH or install location",
656
675
  "category": "import"
657
676
  },
677
+ "FILE_LOCKED_WINDOWS": {
678
+ "pattern": r"WinError 32|being used by another process|cannot access.*because.*used|process cannot access",
679
+ "message": "File is locked by another process!\n\nThis usually means:\n - A compiled .exe is still running\n - Another Python script is using this module\n - An IDE has the file open\n\nFix: Close any running .exe or Python process that uses this module,\n then run 'includecpp rebuild' again.",
680
+ "category": "import"
681
+ },
682
+ "FILE_LOCKED_LINUX": {
683
+ "pattern": r"ETXTBSY|EBUSY|Text file busy|Device or resource busy|\[Errno 16\]|\[Errno 26\]",
684
+ "message": "File is busy (in use by another process)!\n\nThis usually means:\n - A running process is using this shared library\n - Another Python script has imported this module\n\nFix: Close any process using this module, then run 'includecpp rebuild' again.",
685
+ "category": "import"
686
+ },
687
+ "MODULE_FILE_LOCKED": {
688
+ "pattern": r"(api\.pyd|\.pyd|\.so|\.dll).*(?:Permission|WinError|denied|locked|in use|busy)",
689
+ "message": "Module file (.pyd/.so/.dll) is locked!\n\nThe compiled module cannot be overwritten because it's in use.\n\nFix:\n 1. Close any running executable that uses this module\n 2. Close Python scripts/REPL using this module\n 3. Restart your IDE if it imported the module\n 4. Then run 'includecpp rebuild'",
690
+ "category": "import"
691
+ },
692
+ "PERMISSION_ERROR_WINDOWS": {
693
+ "pattern": r"WinError 5|WinError 13|Access is denied",
694
+ "message": "Permission denied (Windows).\n\nPossible causes:\n - File is locked by another process (close running .exe)\n - Antivirus blocking write access\n - Need administrator rights\n\nFix: Close any process using this file, or run as administrator.",
695
+ "category": "import"
696
+ },
697
+ "PERMISSION_ERROR_LINUX": {
698
+ "pattern": r"\[Errno 13\]|EACCES|Operation not permitted|\[Errno 1\]",
699
+ "message": "Permission denied (Linux/Mac).\n\nPossible causes:\n - File is locked by another process\n - Insufficient permissions\n - File owned by different user\n\nFix: Close any process using this file, check ownership, or use sudo.",
700
+ "category": "import"
701
+ },
658
702
  "PERMISSION_ERROR": {
659
703
  "pattern": r"permission denied|PermissionError|access.*denied",
660
- "message": "Permission denied.\n\nFix: Check file permissions or run as admin",
704
+ "message": "Permission denied.\n\nFix: Check file permissions or close processes using the file",
661
705
  "category": "import"
662
706
  },
663
707
  "FILE_LOCKED": {
664
- "pattern": r"file.*locked|file.*in use|being used.*another",
665
- "message": "File in use by another process.\n\nFix: Close other Python processes",
708
+ "pattern": r"file.*locked|file.*in use|being used.*another|file is busy",
709
+ "message": "File in use by another process.\n\nFix: Close Python processes or executables using this module",
666
710
  "category": "import"
667
711
  },
668
712
  "REGISTRY_MISSING": {