IncludeCPP 2.4.2__tar.gz → 2.4.4__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.
Files changed (33) hide show
  1. {includecpp-2.4.2 → includecpp-2.4.4}/IncludeCPP.egg-info/PKG-INFO +1 -1
  2. {includecpp-2.4.2 → includecpp-2.4.4}/PKG-INFO +1 -1
  3. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/__init__.py +1 -1
  4. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/cli/commands.py +302 -20
  5. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/core/build_manager.py +139 -21
  6. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/generator/parser.cpp +61 -3
  7. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/generator/parser.h +6 -0
  8. {includecpp-2.4.2 → includecpp-2.4.4}/pyproject.toml +1 -1
  9. {includecpp-2.4.2 → includecpp-2.4.4}/setup.py +1 -1
  10. {includecpp-2.4.2 → includecpp-2.4.4}/IncludeCPP.egg-info/SOURCES.txt +0 -0
  11. {includecpp-2.4.2 → includecpp-2.4.4}/IncludeCPP.egg-info/dependency_links.txt +0 -0
  12. {includecpp-2.4.2 → includecpp-2.4.4}/IncludeCPP.egg-info/entry_points.txt +0 -0
  13. {includecpp-2.4.2 → includecpp-2.4.4}/IncludeCPP.egg-info/requires.txt +0 -0
  14. {includecpp-2.4.2 → includecpp-2.4.4}/IncludeCPP.egg-info/top_level.txt +0 -0
  15. {includecpp-2.4.2 → includecpp-2.4.4}/LICENSE +0 -0
  16. {includecpp-2.4.2 → includecpp-2.4.4}/MANIFEST.in +0 -0
  17. {includecpp-2.4.2 → includecpp-2.4.4}/README.md +0 -0
  18. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/__init__.pyi +0 -0
  19. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/__main__.py +0 -0
  20. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/cli/__init__.py +0 -0
  21. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/cli/config_parser.py +0 -0
  22. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/core/__init__.py +0 -0
  23. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/core/cpp_api.py +0 -0
  24. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/core/cpp_api.pyi +0 -0
  25. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/core/error_formatter.py +0 -0
  26. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/core/exceptions.py +0 -0
  27. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/core/path_discovery.py +0 -0
  28. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/generator/__init__.py +0 -0
  29. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/generator/type_resolver.cpp +0 -0
  30. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/generator/type_resolver.h +0 -0
  31. {includecpp-2.4.2 → includecpp-2.4.4}/includecpp/templates/cpp.proj.template +0 -0
  32. {includecpp-2.4.2 → includecpp-2.4.4}/requirements.txt +0 -0
  33. {includecpp-2.4.2 → includecpp-2.4.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IncludeCPP
3
- Version: 2.4.2
3
+ Version: 2.4.4
4
4
  Summary: Professional C++ Python bindings with type-generic templates, pystubs and native threading
5
5
  Home-page: https://github.com/includecpp/includecpp
6
6
  Author: IncludeCPP Team
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IncludeCPP
3
- Version: 2.4.2
3
+ Version: 2.4.4
4
4
  Summary: Professional C++ Python bindings with type-generic templates, pystubs and native threading
5
5
  Home-page: https://github.com/includecpp/includecpp
6
6
  Author: IncludeCPP Team
@@ -1,4 +1,4 @@
1
1
  from .core.cpp_api import CppApi
2
2
 
3
- __version__ = "2.4.2"
3
+ __version__ = "2.4.4"
4
4
  __all__ = ["CppApi"]
@@ -1268,14 +1268,159 @@ def minstall(module_name, list_all):
1268
1268
  ctx.invoke(install, module_name=module_name, list_all=list_all)
1269
1269
 
1270
1270
  @cli.command()
1271
- def update():
1272
- """Update IncludeCPP package to latest version from PyPI."""
1271
+ @click.argument('target_version', required=False)
1272
+ @click.option('--version', 'show_version', is_flag=True, help='Show current installed version')
1273
+ @click.option('--all', 'list_all', is_flag=True, help='List all available versions from PyPI')
1274
+ def update(target_version, show_version, list_all):
1275
+ """Update IncludeCPP package from PyPI.
1276
+
1277
+ Usage:
1278
+ includecpp update Upgrade to latest version
1279
+ includecpp update --version Show current version
1280
+ includecpp update --all List all available versions
1281
+ includecpp update X.X.X Install specific version
1282
+ """
1273
1283
  import subprocess
1274
1284
  import urllib.request
1275
1285
  import json
1276
1286
 
1287
+ from .. import __version__ as current_version
1288
+
1289
+ # --version: Show current version
1290
+ if show_version:
1291
+ click.echo("=" * 60)
1292
+ click.secho("IncludeCPP Version Info", fg='cyan', bold=True)
1293
+ click.echo("=" * 60)
1294
+ click.echo()
1295
+ click.echo(f" Installed version: ", nl=False)
1296
+ click.secho(f"{current_version}", fg='green', bold=True)
1297
+ click.echo()
1298
+ click.echo("=" * 60)
1299
+ return
1300
+
1301
+ # --all: List all PyPI versions
1302
+ if list_all:
1303
+ click.echo("=" * 60)
1304
+ click.secho("Available IncludeCPP Versions", fg='cyan', bold=True)
1305
+ click.echo("=" * 60)
1306
+ click.echo(f"Source: https://pypi.org/project/IncludeCPP/")
1307
+ click.echo()
1308
+
1309
+ try:
1310
+ click.echo(" Fetching version list from PyPI...", nl=False)
1311
+ with urllib.request.urlopen("https://pypi.org/pypi/IncludeCPP/json") as response:
1312
+ data = json.loads(response.read())
1313
+ click.secho(" OK", fg='green')
1314
+ click.echo()
1315
+
1316
+ # Get all versions from releases
1317
+ versions = list(data.get('releases', {}).keys())
1318
+ latest_version = data['info']['version']
1319
+
1320
+ if not versions:
1321
+ click.secho(" No versions found.", fg='yellow')
1322
+ else:
1323
+ # Sort versions (newest first)
1324
+ from packaging.version import Version, InvalidVersion
1325
+ valid_versions = []
1326
+ for v in versions:
1327
+ try:
1328
+ valid_versions.append((Version(v), v))
1329
+ except InvalidVersion:
1330
+ valid_versions.append((Version("0.0.0"), v))
1331
+ valid_versions.sort(reverse=True, key=lambda x: x[0])
1332
+
1333
+ click.secho(f"Found {len(versions)} version(s):", fg='green', bold=True)
1334
+ click.echo()
1335
+
1336
+ for _, v in valid_versions:
1337
+ if v == current_version and v == latest_version:
1338
+ click.echo(f" {_BULLET} {v} ", nl=False)
1339
+ click.secho("[installed] [latest]", fg='green', bold=True)
1340
+ elif v == current_version:
1341
+ click.echo(f" {_BULLET} {v} ", nl=False)
1342
+ click.secho("[installed]", fg='cyan')
1343
+ elif v == latest_version:
1344
+ click.echo(f" {_BULLET} {v} ", nl=False)
1345
+ click.secho("[latest]", fg='yellow')
1346
+ else:
1347
+ click.echo(f" {_BULLET} {v}")
1348
+
1349
+ click.echo()
1350
+ click.echo("Install a specific version with:")
1351
+ click.secho(" includecpp update <version>", fg='cyan')
1352
+
1353
+ except Exception as e:
1354
+ click.secho(f" FAILED", fg='red', err=True)
1355
+ click.echo()
1356
+ click.secho(f"Could not fetch version list: {e}", fg='red', err=True)
1357
+
1358
+ click.echo("=" * 60)
1359
+ return
1360
+
1361
+ # Install specific version
1362
+ if target_version:
1363
+ click.echo("=" * 60)
1364
+ click.secho(f"Installing IncludeCPP {target_version}", fg='cyan', bold=True)
1365
+ click.echo("=" * 60)
1366
+ click.echo()
1367
+
1368
+ # Verify version exists on PyPI
1369
+ try:
1370
+ click.echo(" Verifying version on PyPI...", nl=False)
1371
+ with urllib.request.urlopen("https://pypi.org/pypi/IncludeCPP/json") as response:
1372
+ data = json.loads(response.read())
1373
+ available_versions = list(data.get('releases', {}).keys())
1374
+
1375
+ if target_version not in available_versions:
1376
+ click.secho(" NOT FOUND", fg='red')
1377
+ click.echo()
1378
+ click.secho(f"Version '{target_version}' does not exist on PyPI.", fg='red', err=True)
1379
+ click.echo("Use 'includecpp update --all' to see available versions.")
1380
+ click.echo("=" * 60)
1381
+ return
1382
+
1383
+ click.secho(" OK", fg='green')
1384
+
1385
+ except Exception as e:
1386
+ click.secho(f" FAILED ({e})", fg='red', err=True)
1387
+ click.echo("=" * 60)
1388
+ return
1389
+
1390
+ click.echo(f" Current version: {current_version}")
1391
+ click.echo(f" Target version: {target_version}")
1392
+ click.echo()
1393
+
1394
+ if current_version == target_version:
1395
+ click.secho(f"Version {target_version} is already installed!", fg='green', bold=True)
1396
+ click.echo("=" * 60)
1397
+ return
1398
+
1399
+ click.echo(f" Installing IncludeCPP=={target_version}...", nl=False)
1400
+ result = subprocess.run(
1401
+ [sys.executable, "-m", "pip", "install", f"IncludeCPP=={target_version}"],
1402
+ capture_output=True,
1403
+ text=True
1404
+ )
1405
+
1406
+ if result.returncode == 0:
1407
+ click.secho(" OK", fg='green')
1408
+ click.echo()
1409
+ click.secho(f"Successfully installed IncludeCPP {target_version}!", fg='green', bold=True)
1410
+ click.echo("Restart your terminal to use the new version.")
1411
+ else:
1412
+ click.secho(" FAILED", fg='red', err=True)
1413
+ click.echo()
1414
+ click.secho("Installation failed!", fg='red', err=True, bold=True)
1415
+ if result.stderr:
1416
+ click.echo(result.stderr[:500])
1417
+
1418
+ click.echo("=" * 60)
1419
+ return
1420
+
1421
+ # Default: Upgrade to latest
1277
1422
  click.echo("=" * 60)
1278
- click.secho("Checking for IncludeCPP updates", fg='cyan', bold=True)
1423
+ click.secho("Checking for IncludeCPP Updates", fg='cyan', bold=True)
1279
1424
  click.echo("=" * 60)
1280
1425
  click.echo()
1281
1426
 
@@ -1284,9 +1429,8 @@ def update():
1284
1429
  with urllib.request.urlopen("https://pypi.org/pypi/IncludeCPP/json") as response:
1285
1430
  data = json.loads(response.read())
1286
1431
  latest_version = data['info']['version']
1287
- click.secho(f" OK", fg='green')
1432
+ click.secho(" OK", fg='green')
1288
1433
 
1289
- from .. import __version__ as current_version
1290
1434
  click.echo()
1291
1435
  click.echo(f" Current version: {current_version}")
1292
1436
  click.echo(f" Latest version: {latest_version}")
@@ -1297,8 +1441,7 @@ def update():
1297
1441
  click.echo("=" * 60)
1298
1442
  return
1299
1443
 
1300
- click.secho(f"Upgrading to version {latest_version}...", fg='cyan', bold=True)
1301
- click.echo()
1444
+ click.echo(f" Upgrading to version {latest_version}...", nl=False)
1302
1445
 
1303
1446
  result = subprocess.run(
1304
1447
  [sys.executable, "-m", "pip", "install", "--upgrade", "IncludeCPP"],
@@ -1307,18 +1450,89 @@ def update():
1307
1450
  )
1308
1451
 
1309
1452
  if result.returncode == 0:
1453
+ click.secho(" OK", fg='green')
1310
1454
  click.echo()
1311
1455
  click.secho(f"Successfully upgraded to IncludeCPP {latest_version}!", fg='green', bold=True)
1312
1456
  click.echo("Restart your terminal to use the new version.")
1313
1457
  else:
1458
+ click.secho(" FAILED", fg='red', err=True)
1459
+ click.echo()
1314
1460
  click.secho("Upgrade failed!", fg='red', err=True, bold=True)
1315
- click.echo(result.stderr)
1461
+ if result.stderr:
1462
+ click.echo(result.stderr[:500])
1316
1463
 
1317
1464
  except Exception as e:
1465
+ click.secho(f" FAILED", fg='red', err=True)
1466
+ click.echo()
1318
1467
  click.secho(f"Failed to check for updates: {e}", fg='red', err=True)
1319
1468
 
1320
1469
  click.echo("=" * 60)
1321
1470
 
1471
+
1472
+ @cli.command()
1473
+ def reboot():
1474
+ """Reinstall IncludeCPP (uninstall + install current version).
1475
+
1476
+ This command reinstalls the currently installed version without upgrading.
1477
+ Useful for fixing corrupted installations or resetting to a clean state.
1478
+ """
1479
+ import subprocess
1480
+
1481
+ from .. import __version__ as current_version
1482
+
1483
+ click.echo("=" * 60)
1484
+ click.secho("IncludeCPP Reboot", fg='cyan', bold=True)
1485
+ click.echo("=" * 60)
1486
+ click.echo()
1487
+ click.echo(f" Current version: {current_version}")
1488
+ click.echo()
1489
+ click.secho("This will reinstall IncludeCPP without changing the version.", fg='yellow')
1490
+ click.echo()
1491
+
1492
+ # Step 1: Uninstall
1493
+ click.echo(f" [1/2] Uninstalling IncludeCPP...", nl=False)
1494
+ result = subprocess.run(
1495
+ [sys.executable, "-m", "pip", "uninstall", "IncludeCPP", "-y"],
1496
+ capture_output=True,
1497
+ text=True
1498
+ )
1499
+
1500
+ if result.returncode != 0:
1501
+ click.secho(" FAILED", fg='red', err=True)
1502
+ click.echo()
1503
+ click.secho("Uninstall failed!", fg='red', err=True, bold=True)
1504
+ if result.stderr:
1505
+ click.echo(result.stderr[:500])
1506
+ click.echo("=" * 60)
1507
+ return
1508
+
1509
+ click.secho(" OK", fg='green')
1510
+
1511
+ # Step 2: Reinstall same version
1512
+ click.echo(f" [2/2] Installing IncludeCPP=={current_version}...", nl=False)
1513
+ result = subprocess.run(
1514
+ [sys.executable, "-m", "pip", "install", f"IncludeCPP=={current_version}"],
1515
+ capture_output=True,
1516
+ text=True
1517
+ )
1518
+
1519
+ if result.returncode == 0:
1520
+ click.secho(" OK", fg='green')
1521
+ click.echo()
1522
+ click.secho(f"Successfully reinstalled IncludeCPP {current_version}!", fg='green', bold=True)
1523
+ click.echo("Restart your terminal to use the reinstalled version.")
1524
+ else:
1525
+ click.secho(" FAILED", fg='red', err=True)
1526
+ click.echo()
1527
+ click.secho("Reinstall failed!", fg='red', err=True, bold=True)
1528
+ if result.stderr:
1529
+ click.echo(result.stderr[:500])
1530
+ click.echo()
1531
+ click.echo("Try manually installing with:")
1532
+ click.secho(f" pip install IncludeCPP=={current_version}", fg='cyan')
1533
+
1534
+ click.echo("=" * 60)
1535
+
1322
1536
  @cli.command()
1323
1537
  @click.argument('plugin_name')
1324
1538
  @click.argument('files', nargs=-1, required=True)
@@ -1393,10 +1607,58 @@ def plugin(plugin_name, files, private):
1393
1607
  return class_body[public_start:public_start + next_access.start()]
1394
1608
  return class_body[public_start:]
1395
1609
 
1610
+ def extract_param_types(params_str):
1611
+ """Extract parameter types from a parameter string like 'double x, double y'."""
1612
+ if not params_str or params_str.strip() == '':
1613
+ return []
1614
+
1615
+ types = []
1616
+ # Split by comma, but be careful with template types like std::vector<int, alloc>
1617
+ depth = 0
1618
+ current = ''
1619
+ for char in params_str:
1620
+ if char in '<(':
1621
+ depth += 1
1622
+ current += char
1623
+ elif char in '>)':
1624
+ depth -= 1
1625
+ current += char
1626
+ elif char == ',' and depth == 0:
1627
+ types.append(current.strip())
1628
+ current = ''
1629
+ else:
1630
+ current += char
1631
+ if current.strip():
1632
+ types.append(current.strip())
1633
+
1634
+ # Extract just the type from each parameter (remove variable name)
1635
+ result = []
1636
+ for param in types:
1637
+ param = param.strip()
1638
+ if not param:
1639
+ continue
1640
+ # Remove default value if present
1641
+ if '=' in param:
1642
+ param = param.split('=')[0].strip()
1643
+ # Find the type: everything before the last word (variable name)
1644
+ # Handle cases like "const std::string& name" -> "const std::string&"
1645
+ parts = param.rsplit(None, 1)
1646
+ if len(parts) == 1:
1647
+ # Just a type, no name (e.g., in declaration "void foo(int)")
1648
+ result.append(parts[0])
1649
+ else:
1650
+ # Check if last part is a pointer/reference suffix attached to type
1651
+ type_part = parts[0]
1652
+ # Handle cases where * or & is attached to variable name
1653
+ if parts[1].startswith('*') or parts[1].startswith('&'):
1654
+ type_part = parts[0] + parts[1][0]
1655
+ result.append(type_part)
1656
+ return result
1657
+
1396
1658
  def extract_methods(public_section, class_name):
1397
1659
  """Extract method names from public section, handling inline bodies."""
1398
1660
  methods = set()
1399
- constructors = 0
1661
+ constructor_signatures = [] # v2.4.3: List of (param_types) tuples
1400
1662
 
1401
1663
  # Method pattern that handles return types, const, and inline bodies
1402
1664
  # Match: [modifiers] return_type method_name(params) [const] [{ body } | ;]
@@ -1418,8 +1680,7 @@ def plugin(plugin_name, files, private):
1418
1680
 
1419
1681
  # Skip if method name is the class name (it's a constructor)
1420
1682
  if method_name == class_name:
1421
- constructors += 1
1422
- continue
1683
+ continue # Will be handled by dedicated constructor pattern
1423
1684
 
1424
1685
  # Skip destructors
1425
1686
  if method_name.startswith('~'):
@@ -1431,12 +1692,17 @@ def plugin(plugin_name, files, private):
1431
1692
 
1432
1693
  methods.add(method_name)
1433
1694
 
1434
- # Also look for explicit constructors
1435
- constructor_pattern = rf'\b{re.escape(class_name)}\s*\([^)]*\)'
1436
- constructor_matches = re.findall(constructor_pattern, public_section)
1437
- constructors = max(constructors, len(constructor_matches))
1695
+ # v2.4.3: Extract constructor signatures with parameter types
1696
+ constructor_pattern = re.compile(
1697
+ rf'(?:explicit\s+)?{re.escape(class_name)}\s*\(([^)]*)\)',
1698
+ re.MULTILINE
1699
+ )
1700
+ for match in constructor_pattern.finditer(public_section):
1701
+ params_str = match.group(1).strip()
1702
+ param_types = extract_param_types(params_str)
1703
+ constructor_signatures.append(tuple(param_types))
1438
1704
 
1439
- return methods, constructors
1705
+ return methods, constructor_signatures
1440
1706
 
1441
1707
  all_files = cpp_files + h_files
1442
1708
 
@@ -1470,12 +1736,17 @@ def plugin(plugin_name, files, private):
1470
1736
  public_section = extract_public_section(class_body, is_struct)
1471
1737
 
1472
1738
  if class_name not in classes:
1473
- classes[class_name] = {'methods': set(), 'constructors': 0}
1739
+ classes[class_name] = {'methods': set(), 'constructors': []}
1474
1740
 
1475
1741
  # Extract methods from public section
1476
- methods, constructors = extract_methods(public_section, class_name)
1742
+ methods, constructor_sigs = extract_methods(public_section, class_name)
1477
1743
  classes[class_name]['methods'].update(methods)
1478
- classes[class_name]['constructors'] = max(classes[class_name]['constructors'], constructors)
1744
+ # v2.4.3: Merge constructor signatures (avoid duplicates)
1745
+ existing_ctors = set(classes[class_name]['constructors'])
1746
+ for sig in constructor_sigs:
1747
+ if sig not in existing_ctors:
1748
+ classes[class_name]['constructors'].append(sig)
1749
+ existing_ctors.add(sig)
1479
1750
 
1480
1751
  # Find free functions (not inside class bodies)
1481
1752
  # First, remove all class bodies from content to avoid matching class methods
@@ -1524,7 +1795,18 @@ def plugin(plugin_name, files, private):
1524
1795
  cls_info = classes[cls_name]
1525
1796
  f.write(f' {plugin_name} CLASS({cls_name}) {{\n')
1526
1797
 
1527
- if cls_info['constructors'] > 0:
1798
+ # v2.4.3: Write all constructor overloads with parameter types
1799
+ if cls_info['constructors']:
1800
+ for ctor_params in cls_info['constructors']:
1801
+ if ctor_params:
1802
+ # Parametrized constructor
1803
+ params_str = ', '.join(ctor_params)
1804
+ f.write(f' CONSTRUCTOR({params_str})\n')
1805
+ else:
1806
+ # Default constructor
1807
+ f.write(f' CONSTRUCTOR()\n')
1808
+ else:
1809
+ # No constructors found, add default
1528
1810
  f.write(f' CONSTRUCTOR()\n')
1529
1811
 
1530
1812
  for method in sorted(cls_info['methods']):
@@ -579,10 +579,46 @@ endif()
579
579
  else:
580
580
  f.write(f' """C++ class: {class_name_inner}"""\n\n')
581
581
 
582
- # Constructor
583
- f.write(' def __init__(self, *args: Any, **kwargs: Any) -> None:\n')
584
- f.write(f' """Initialize {class_name_inner} instance"""\n')
585
- f.write(' ...\n\n')
582
+ # v2.4.3: Constructor overloads with parameter types
583
+ constructors = cls.get('constructors', [])
584
+ if constructors and len(constructors) > 1:
585
+ # Multiple constructors - use @overload
586
+ for ctor in constructors:
587
+ param_types = ctor.get('params', [])
588
+ f.write(' @overload\n')
589
+ if param_types:
590
+ param_list = ['self']
591
+ for i, ptype in enumerate(param_types):
592
+ py_type = self._cpp_to_python_type(ptype)
593
+ param_list.append(f'arg{i}: {py_type}')
594
+ params_str = ', '.join(param_list)
595
+ f.write(f' def __init__({params_str}) -> None: ...\n')
596
+ else:
597
+ f.write(f' def __init__(self) -> None: ...\n')
598
+ f.write('\n')
599
+ # Actual implementation signature
600
+ f.write(' def __init__(self, *args: Any, **kwargs: Any) -> None:\n')
601
+ f.write(f' """Initialize {class_name_inner} instance"""\n')
602
+ f.write(' ...\n\n')
603
+ elif constructors and len(constructors) == 1:
604
+ # Single constructor
605
+ param_types = constructors[0].get('params', [])
606
+ if param_types:
607
+ param_list = ['self']
608
+ for i, ptype in enumerate(param_types):
609
+ py_type = self._cpp_to_python_type(ptype)
610
+ param_list.append(f'arg{i}: {py_type}')
611
+ params_str = ', '.join(param_list)
612
+ f.write(f' def __init__({params_str}) -> None:\n')
613
+ else:
614
+ f.write(f' def __init__(self) -> None:\n')
615
+ f.write(f' """Initialize {class_name_inner} instance"""\n')
616
+ f.write(' ...\n\n')
617
+ else:
618
+ # Fallback - generic constructor
619
+ f.write(' def __init__(self, *args: Any, **kwargs: Any) -> None:\n')
620
+ f.write(f' """Initialize {class_name_inner} instance"""\n')
621
+ f.write(' ...\n\n')
586
622
 
587
623
  # Generate methods
588
624
  methods = cls.get('methods', [])
@@ -645,6 +681,52 @@ endif()
645
681
 
646
682
  f.write('\n\n')
647
683
 
684
+ # v2.4.3: Generate CppApi class with overloaded include() methods
685
+ # This is the KEY to making VSCode autocomplete work for module.Class
686
+ f.write('# CppApi with typed include() overloads for each module\n')
687
+ f.write('class CppApi:\n')
688
+ f.write(' """C++ API Manager with typed module loading.\n\n')
689
+ f.write(' The include() method returns a module wrapper with full type hints\n')
690
+ f.write(' for VSCode/PyCharm autocomplete support.\n')
691
+ f.write(' """\n\n')
692
+
693
+ f.write(' def __init__(self, project_root: Optional[str] = None, auto_update: bool = True) -> None:\n')
694
+ f.write(' """Initialize CppApi.\n\n')
695
+ f.write(' Args:\n')
696
+ f.write(' project_root: Path to project root (default: auto-detect)\n')
697
+ f.write(' auto_update: Whether to auto-rebuild on source changes\n')
698
+ f.write(' """\n')
699
+ f.write(' ...\n\n')
700
+
701
+ # Generate overloaded include() methods for each module
702
+ for module_name, _ in modules.items():
703
+ wrapper_class = f"{module_name.capitalize()}ModuleWrapper"
704
+ f.write(' @overload\n')
705
+ f.write(f' def include(self, module_name: str = "{module_name}", auto_update: Optional[bool] = None) -> {wrapper_class}: ...\n\n')
706
+
707
+ # Fallback overload for unknown modules
708
+ f.write(' @overload\n')
709
+ f.write(' def include(self, module_name: str, auto_update: Optional[bool] = None) -> Any: ...\n\n')
710
+
711
+ # Actual implementation signature
712
+ f.write(' def include(self, module_name: str, auto_update: Optional[bool] = None) -> Any:\n')
713
+ f.write(' """Load a C++ module.\n\n')
714
+ f.write(' Args:\n')
715
+ f.write(' module_name: Name of the module to load\n')
716
+ f.write(' auto_update: Override auto-update setting for this module\n\n')
717
+ f.write(' Returns:\n')
718
+ f.write(' ModuleWrapper with access to C++ classes, functions, and structs\n')
719
+ f.write(' """\n')
720
+ f.write(' ...\n\n')
721
+
722
+ f.write(' def rebuild(self, verbose: bool = False) -> bool:\n')
723
+ f.write(' """Rebuild all C++ modules."""\n')
724
+ f.write(' ...\n\n')
725
+
726
+ f.write(' def list_modules(self) -> List[str]:\n')
727
+ f.write(' """List available modules."""\n')
728
+ f.write(' ...\n')
729
+
648
730
  if verbose:
649
731
  print(f"Generated VSCode IntelliSense stubs: {pyi_file}")
650
732
 
@@ -728,25 +810,61 @@ endif()
728
810
  else:
729
811
  f.write(f' """C++ class: {class_name}"""\n\n')
730
812
 
731
- # Constructor with parameters if available
732
- constructor_params = cls.get('constructor_params', [])
733
- if constructor_params:
734
- param_list = ['self']
735
- for param in constructor_params:
736
- param_name = param.get('name', 'arg')
737
- param_type = self._cpp_to_python_type(param.get('type', 'Any'))
738
- param_default = param.get('default', None)
739
- if param_default:
740
- py_default = self._convert_cpp_default(param_default, param_type)
741
- param_list.append(f'{param_name}: {param_type} = {py_default}')
813
+ # v2.4.3: Constructor overloads with parameter types
814
+ constructors = cls.get('constructors', [])
815
+ if constructors and len(constructors) > 1:
816
+ # Multiple constructors - use @overload
817
+ for ctor in constructors:
818
+ param_types = ctor.get('params', [])
819
+ f.write(' @overload\n')
820
+ if param_types:
821
+ param_list = ['self']
822
+ for i, ptype in enumerate(param_types):
823
+ py_type = self._cpp_to_python_type(ptype)
824
+ param_list.append(f'arg{i}: {py_type}')
825
+ params_str = ', '.join(param_list)
826
+ f.write(f' def __init__({params_str}) -> None: ...\n')
742
827
  else:
743
- param_list.append(f'{param_name}: {param_type}')
744
- params_str = ', '.join(param_list)
745
- f.write(f' def __init__({params_str}) -> None:\n')
746
- else:
828
+ f.write(f' def __init__(self) -> None: ...\n')
829
+ f.write('\n')
830
+ # Actual implementation signature
747
831
  f.write(f' def __init__(self, *args: Any, **kwargs: Any) -> None:\n')
748
- f.write(f' """Initialize {class_name} instance."""\n')
749
- f.write(' ...\n\n')
832
+ f.write(f' """Initialize {class_name} instance."""\n')
833
+ f.write(' ...\n\n')
834
+ elif constructors and len(constructors) == 1:
835
+ # Single constructor
836
+ param_types = constructors[0].get('params', [])
837
+ if param_types:
838
+ param_list = ['self']
839
+ for i, ptype in enumerate(param_types):
840
+ py_type = self._cpp_to_python_type(ptype)
841
+ param_list.append(f'arg{i}: {py_type}')
842
+ params_str = ', '.join(param_list)
843
+ f.write(f' def __init__({params_str}) -> None:\n')
844
+ else:
845
+ f.write(f' def __init__(self) -> None:\n')
846
+ f.write(f' """Initialize {class_name} instance."""\n')
847
+ f.write(' ...\n\n')
848
+ else:
849
+ # Fallback: legacy format or no constructor info
850
+ constructor_params = cls.get('constructor_params', [])
851
+ if constructor_params:
852
+ param_list = ['self']
853
+ for param in constructor_params:
854
+ param_name = param.get('name', 'arg')
855
+ param_type = self._cpp_to_python_type(param.get('type', 'Any'))
856
+ param_default = param.get('default', None)
857
+ if param_default:
858
+ py_default = self._convert_cpp_default(param_default, param_type)
859
+ param_list.append(f'{param_name}: {param_type} = {py_default}')
860
+ else:
861
+ param_list.append(f'{param_name}: {param_type}')
862
+ params_str = ', '.join(param_list)
863
+ f.write(f' def __init__({params_str}) -> None:\n')
864
+ else:
865
+ f.write(f' def __init__(self, *args: Any, **kwargs: Any) -> None:\n')
866
+ f.write(f' """Initialize {class_name} instance."""\n')
867
+ f.write(' ...\n\n')
750
868
 
751
869
  # Methods
752
870
  methods = cls.get('methods', [])
@@ -395,7 +395,26 @@ ModuleDescriptor API::parse_cp_file(const std::string& filepath) {
395
395
  std::string mtrim = trim(mline);
396
396
  if (mtrim.empty()) continue;
397
397
 
398
- if (mtrim.find("METHOD") != std::string::npos) {
398
+ // v2.4.3: Parse CONSTRUCTOR(type1, type2, ...) for parametrized constructors
399
+ if (mtrim.find("CONSTRUCTOR") != std::string::npos) {
400
+ size_t c_start = mtrim.find('(');
401
+ size_t c_end = mtrim.rfind(')');
402
+ if (c_start != std::string::npos && c_end != std::string::npos) {
403
+ std::string params_str = mtrim.substr(c_start + 1, c_end - c_start - 1);
404
+ ConstructorInfo ctor;
405
+ if (!params_str.empty()) {
406
+ auto params = split(params_str, ',');
407
+ for (auto& p : params) {
408
+ std::string param_type = trim(p);
409
+ if (!param_type.empty()) {
410
+ ctor.param_types.push_back(param_type);
411
+ }
412
+ }
413
+ }
414
+ cb.constructors.push_back(ctor);
415
+ }
416
+ }
417
+ else if (mtrim.find("METHOD") != std::string::npos) {
399
418
  size_t m_start = mtrim.find('(');
400
419
  size_t m_end = mtrim.find(')');
401
420
  if (m_start != std::string::npos && m_end != std::string::npos) {
@@ -594,7 +613,25 @@ std::string API::generate_class_bindings(const ClassBinding& cls, const ModuleDe
594
613
 
595
614
  code << " py::class_<" << cls.class_name << ">("
596
615
  << mod.module_name << "_module, \"" << cls.class_name << "\")\n";
597
- code << " .def(py::init<>())";
616
+
617
+ // v2.4.3: Generate all constructor overloads from CONSTRUCTOR() entries
618
+ if (cls.constructors.empty()) {
619
+ // Backward compatibility: default constructor if none specified
620
+ code << " .def(py::init<>())";
621
+ } else {
622
+ bool first = true;
623
+ for (const auto& ctor : cls.constructors) {
624
+ if (!first) code << "\n";
625
+ first = false;
626
+
627
+ code << " .def(py::init<";
628
+ for (size_t i = 0; i < ctor.param_types.size(); ++i) {
629
+ if (i > 0) code << ", ";
630
+ code << ctor.param_types[i];
631
+ }
632
+ code << ">())";
633
+ }
634
+ }
598
635
 
599
636
  // Bind Initialize static method (factory method)
600
637
  code << "\n .def_static(\"Initialize\", []() { return " << cls.class_name << "(); })";
@@ -1141,7 +1178,7 @@ std::string API::generate_registry_json(const std::vector<ModuleDescriptor>& mod
1141
1178
  }
1142
1179
  json << " ],\n";
1143
1180
 
1144
- // Add classes with methods and documentation
1181
+ // Add classes with methods, constructors, and documentation
1145
1182
  json << " \"classes\": [\n";
1146
1183
  for (size_t j = 0; j < mod.classes.size(); ++j) {
1147
1184
  const auto& cls = mod.classes[j];
@@ -1150,6 +1187,27 @@ std::string API::generate_registry_json(const std::vector<ModuleDescriptor>& mod
1150
1187
  if (!cls.documentation.empty()) {
1151
1188
  json << ",\n \"doc\": \"" << replace_all(cls.documentation, "\"", "\\\"") << "\"";
1152
1189
  }
1190
+
1191
+ // v2.4.3: Add constructor signatures
1192
+ json << ",\n \"constructors\": [\n";
1193
+ if (cls.constructors.empty()) {
1194
+ // Default constructor
1195
+ json << " {\"params\": []}\n";
1196
+ } else {
1197
+ for (size_t k = 0; k < cls.constructors.size(); ++k) {
1198
+ const auto& ctor = cls.constructors[k];
1199
+ json << " {\"params\": [";
1200
+ for (size_t p = 0; p < ctor.param_types.size(); ++p) {
1201
+ json << "\"" << ctor.param_types[p] << "\"";
1202
+ if (p < ctor.param_types.size() - 1) json << ", ";
1203
+ }
1204
+ json << "]}";
1205
+ if (k < cls.constructors.size() - 1) json << ",";
1206
+ json << "\n";
1207
+ }
1208
+ }
1209
+ json << " ]";
1210
+
1153
1211
  json << ",\n \"methods\": [\n";
1154
1212
  for (size_t k = 0; k < cls.methods.size(); ++k) {
1155
1213
  const auto& method = cls.methods[k];
@@ -93,12 +93,18 @@ struct FieldInfo {
93
93
  std::string documentation;
94
94
  };
95
95
 
96
+ // v2.4.3: Constructor parameter types
97
+ struct ConstructorInfo {
98
+ std::vector<std::string> param_types; // e.g., ["double", "double"] for Vector2D(double, double)
99
+ };
100
+
96
101
  struct ClassBinding {
97
102
  std::string module_name;
98
103
  std::string class_name;
99
104
  std::vector<std::pair<std::string, std::string>> params;
100
105
  std::vector<std::string> methods; // Method names to bind
101
106
  std::vector<std::string> fields; // Field names to bind
107
+ std::vector<ConstructorInfo> constructors; // v2.4.3: Constructor overloads
102
108
  bool auto_bind_all; // Bind all methods automatically
103
109
  std::string documentation; // Class documentation from DOC()
104
110
  std::map<std::string, std::string> method_docs; // Method-specific docs: method_name -> doc
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "IncludeCPP"
7
- version = "2.4.2"
7
+ version = "2.4.4"
8
8
  description = "Professional C++ Python bindings with type-generic templates, pystubs and native threading"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -6,7 +6,7 @@ long_description = (this_directory / "README.md").read_text(encoding="utf-8")
6
6
 
7
7
  setup(
8
8
  name="IncludeCPP",
9
- version="2.4.2",
9
+ version="2.4.4",
10
10
  author="IncludeCPP Team",
11
11
  author_email="contact@includecpp.dev",
12
12
  description="Professional C++ Python bindings with type-generic templates and native threading",
File without changes
File without changes
File without changes
File without changes
File without changes