IncludeCPP 3.3.20__py3-none-any.whl → 3.4.8__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.
includecpp/__init__.py CHANGED
@@ -1,58 +1,59 @@
1
- from .core.cpp_api import CppApi
2
- import warnings
3
-
4
- __version__ = "3.3.20"
5
- __all__ = ["CppApi"]
6
-
7
- # Module-level cache for C++ modules
8
- _api_instance = None
9
- _loaded_modules = {}
10
-
11
- def _get_api():
12
- """Get or create singleton CppApi instance."""
13
- global _api_instance
14
- if _api_instance is None:
15
- _api_instance = CppApi()
16
- return _api_instance
17
-
18
- def __getattr__(name: str):
19
- """Enable: from includecpp import fast_list
20
-
21
- This hook is called when Python cannot find an attribute in this module.
22
- It allows dynamic C++ module loading via the import system.
23
- """
24
- if name.startswith('_'):
25
- raise AttributeError(f"module 'includecpp' has no attribute '{name}'")
26
-
27
- if name in _loaded_modules:
28
- return _loaded_modules[name]
29
-
30
- api = _get_api()
31
-
32
- if name not in api.registry:
33
- available = list(api.registry.keys())
34
- raise AttributeError(
35
- f"Module '{name}' not found. "
36
- f"Available: {available}. "
37
- f"Run 'includecpp rebuild' first."
38
- )
39
-
40
- if api.need_update(name):
41
- warnings.warn(
42
- f"Module '{name}' source files changed. "
43
- f"Run 'includecpp rebuild' to update.",
44
- UserWarning
45
- )
46
-
47
- module = api.include(name)
48
- _loaded_modules[name] = module
49
- return module
50
-
51
- def __dir__():
52
- """List available attributes including C++ modules."""
53
- base = ['CppApi', '__version__']
54
- try:
55
- api = _get_api()
56
- return sorted(set(base + list(api.registry.keys())))
57
- except Exception:
58
- return base
1
+ from .core.cpp_api import CppApi
2
+ from .core import cssl_bridge as CSSL
3
+ import warnings
4
+
5
+ __version__ = "3.4.8"
6
+ __all__ = ["CppApi", "CSSL"]
7
+
8
+ # Module-level cache for C++ modules
9
+ _api_instance = None
10
+ _loaded_modules = {}
11
+
12
+ def _get_api():
13
+ """Get or create singleton CppApi instance."""
14
+ global _api_instance
15
+ if _api_instance is None:
16
+ _api_instance = CppApi()
17
+ return _api_instance
18
+
19
+ def __getattr__(name: str):
20
+ """Enable: from includecpp import fast_list
21
+
22
+ This hook is called when Python cannot find an attribute in this module.
23
+ It allows dynamic C++ module loading via the import system.
24
+ """
25
+ if name.startswith('_'):
26
+ raise AttributeError(f"module 'includecpp' has no attribute '{name}'")
27
+
28
+ if name in _loaded_modules:
29
+ return _loaded_modules[name]
30
+
31
+ api = _get_api()
32
+
33
+ if name not in api.registry:
34
+ available = list(api.registry.keys())
35
+ raise AttributeError(
36
+ f"Module '{name}' not found. "
37
+ f"Available: {available}. "
38
+ f"Run 'includecpp rebuild' first."
39
+ )
40
+
41
+ if api.need_update(name):
42
+ warnings.warn(
43
+ f"Module '{name}' source files changed. "
44
+ f"Run 'includecpp rebuild' to update.",
45
+ UserWarning
46
+ )
47
+
48
+ module = api.include(name)
49
+ _loaded_modules[name] = module
50
+ return module
51
+
52
+ def __dir__():
53
+ """List available attributes including C++ modules."""
54
+ base = ['CppApi', 'CSSL', '__version__']
55
+ try:
56
+ api = _get_api()
57
+ return sorted(set(base + list(api.registry.keys())))
58
+ except Exception:
59
+ return base
@@ -65,7 +65,7 @@ def _safe_echo(text, **kwargs):
65
65
  ('╔', '+'), ('╗', '+'), ('╚', '+'), ('╝', '+'),
66
66
  ('┌', '+'), ('┐', '+'), ('└', '+'), ('┘', '+'),
67
67
  ('═', '-'), ('─', '-'), ('║', '|'), ('│', '|'),
68
- ('✗', '[X]'), ('•', '*'),
68
+ ('✗', '[X]'), ('✓', '[OK]'), ('❌', '[X]'), ('•', '*'),
69
69
  ('→', '->'), ('▶', '>'), ('◆', '*'),
70
70
  ('\u2011', '-'), ('\u2010', '-'),
71
71
  ('\u2013', '-'), ('\u2014', '--'),
@@ -149,13 +149,13 @@ def _render_readme_with_colors(readme_text):
149
149
 
150
150
  def _show_changelog():
151
151
  """Extract and display changelog from README."""
152
- click.echo("=" * 70)
153
- click.secho("IncludeCPP Changelog", fg='cyan', bold=True)
154
- click.echo("=" * 70)
155
- click.echo()
152
+ _safe_echo("=" * 70)
153
+ _safe_echo("IncludeCPP Changelog", fg='cyan', bold=True)
154
+ _safe_echo("=" * 70)
155
+ _safe_echo("")
156
156
 
157
157
  try:
158
- click.echo(" Fetching changelog from PyPI...", nl=False)
158
+ _safe_echo(" Fetching changelog from PyPI...", nl=False)
159
159
  req = urllib.request.Request(
160
160
  "https://pypi.org/pypi/IncludeCPP/json",
161
161
  headers={"User-Agent": "IncludeCPP-CLI"}
@@ -166,12 +166,12 @@ def _show_changelog():
166
166
  data = json.loads(raw_data)
167
167
  description = data.get('info', {}).get('description', '')
168
168
  version = data.get('info', {}).get('version', 'unknown')
169
- click.secho(" OK", fg='green')
170
- click.echo()
169
+ _safe_echo(" OK", fg='green')
170
+ _safe_echo("")
171
171
 
172
172
  if description:
173
- click.secho(f"Current Version: {version}", fg='green', bold=True)
174
- click.echo()
173
+ _safe_echo(f"Current Version: {version}", fg='green', bold=True)
174
+ _safe_echo("")
175
175
 
176
176
  lines = description.split('\n')
177
177
  in_changelog = False
@@ -190,23 +190,23 @@ def _show_changelog():
190
190
  for line in changelog_lines:
191
191
  stripped = line.strip()
192
192
  if stripped.startswith('## '):
193
- click.secho(stripped[3:], fg='yellow', bold=True)
193
+ _safe_echo(stripped[3:], fg='yellow', bold=True)
194
194
  elif stripped.startswith('- '):
195
- click.echo(" " + stripped)
195
+ _safe_echo(" " + stripped)
196
196
  elif stripped:
197
- click.echo(" " + stripped)
197
+ _safe_echo(" " + stripped)
198
198
  else:
199
- click.secho("No changelog found in README.", fg='yellow')
199
+ _safe_echo("No changelog found in README.", fg='yellow')
200
200
  else:
201
- click.secho("No documentation available.", fg='yellow')
201
+ _safe_echo("No documentation available.", fg='yellow')
202
202
 
203
203
  except Exception as e:
204
- click.secho(" FAILED", fg='red')
205
- click.echo()
206
- click.secho("Could not fetch changelog: " + str(e), fg='red', err=True)
204
+ _safe_echo(" FAILED", fg='red')
205
+ _safe_echo("")
206
+ _safe_echo("Could not fetch changelog: " + str(e), fg='red', err=True)
207
207
 
208
- click.echo()
209
- click.echo("=" * 70)
208
+ _safe_echo("")
209
+ _safe_echo("=" * 70)
210
210
 
211
211
 
212
212
  @click.group(invoke_without_command=True)
@@ -4436,8 +4436,10 @@ def fix(module_name, all_modules, exclude, undo, auto_fix, verbose, use_ai):
4436
4436
  if var_decl:
4437
4437
  var_name = var_decl.group(1)
4438
4438
  # Check if variable appears again in rest of content
4439
+ # v3.4.1: Use word boundaries to avoid false positives from substring matches
4439
4440
  rest_content = '\n'.join(lines[i:])
4440
- if rest_content.count(var_name) <= 1:
4441
+ uses = len(re.findall(rf'\b{re.escape(var_name)}\b', rest_content))
4442
+ if uses <= 1:
4441
4443
  add_issue('info', 'Mistake', file_path, i, 'C003',
4442
4444
  f"Variable '{var_name}' may be unused",
4443
4445
  "Remove unused variable or mark as [[maybe_unused]]")
@@ -7133,6 +7135,383 @@ def cppy_types():
7133
7135
  click.echo("=" * 60)
7134
7136
 
7135
7137
 
7138
+ # ============================================================================
7139
+ # EXEC - Interactive Code Execution
7140
+ # ============================================================================
7141
+
7142
+ @cli.command()
7143
+ @click.argument('lang', type=click.Choice(['py', 'cpp', 'python', 'c++']))
7144
+ @click.argument('path', required=False, type=click.Path())
7145
+ @click.option('--all', 'import_all', is_flag=True, help='Import all available modules')
7146
+ def exec(lang, path, import_all):
7147
+ """Execute code interactively for quick testing.
7148
+
7149
+ Run Python or C++ code snippets without creating files.
7150
+ Perfect for testing your IncludeCPP modules quickly.
7151
+
7152
+ \b
7153
+ Usage:
7154
+ includecpp exec py # Interactive Python
7155
+ includecpp exec cpp # Interactive C++
7156
+ includecpp exec py mymodule # Auto-import mymodule
7157
+ includecpp exec py plugins/x.cp # Auto-import from plugin
7158
+ includecpp exec py --all # Import all modules
7159
+
7160
+ \b
7161
+ Controls:
7162
+ ENTER = Add new line
7163
+ Empty line = Execute code (press ENTER twice)
7164
+ CTRL+C = Cancel
7165
+
7166
+ \b
7167
+ Examples:
7168
+ $ includecpp exec py fast_math
7169
+ >>> x = fast_math.add(1, 2)
7170
+ >>> print(x)
7171
+ >>>
7172
+ 3
7173
+ """
7174
+ import sys
7175
+ import subprocess
7176
+ import tempfile
7177
+ from pathlib import Path as PathLib
7178
+
7179
+ # Normalize language
7180
+ is_python = lang in ('py', 'python')
7181
+ lang_name = 'Python' if is_python else 'C++'
7182
+
7183
+ # Build imports/includes
7184
+ imports = []
7185
+ includes = []
7186
+
7187
+ if import_all:
7188
+ # Import all available modules
7189
+ try:
7190
+ from ..core.cpp_api import CppApi
7191
+ api = CppApi()
7192
+ modules = list(api.registry.keys())
7193
+ if is_python:
7194
+ for mod in modules:
7195
+ imports.append(f'from includecpp import {mod}')
7196
+ if modules:
7197
+ click.secho(f"Auto-importing {len(modules)} modules: {', '.join(modules)}", fg='cyan')
7198
+ else:
7199
+ for mod in modules:
7200
+ includes.append(f'#include "{mod}.h"')
7201
+ if modules:
7202
+ click.secho(f"Auto-including {len(modules)} headers", fg='cyan')
7203
+ except Exception:
7204
+ click.secho("Warning: Could not load module registry", fg='yellow')
7205
+
7206
+ elif path:
7207
+ path_obj = PathLib(path)
7208
+ module_name = None
7209
+
7210
+ # Check if it's a .cp plugin file
7211
+ if path.endswith('.cp') or '/plugins/' in path or '\\plugins\\' in path:
7212
+ # Extract module name from plugin path
7213
+ module_name = path_obj.stem
7214
+ elif path_obj.exists():
7215
+ # It's a file path
7216
+ module_name = path_obj.stem
7217
+ else:
7218
+ # Assume it's a module name directly
7219
+ module_name = path
7220
+
7221
+ if module_name:
7222
+ if is_python:
7223
+ imports.append(f'from includecpp import {module_name}')
7224
+ click.secho(f"Auto-importing: {module_name}", fg='cyan')
7225
+ else:
7226
+ includes.append(f'#include "{module_name}.h"')
7227
+ click.secho(f"Auto-including: {module_name}.h", fg='cyan')
7228
+
7229
+ # Show header
7230
+ click.echo()
7231
+ click.secho(f"=== IncludeCPP {lang_name} REPL ===", fg='cyan', bold=True)
7232
+ click.echo("Enter code line by line. Press ENTER on empty line to execute.")
7233
+ click.echo("Press CTRL+C to cancel.")
7234
+ click.echo()
7235
+
7236
+ # Show pre-loaded imports
7237
+ if imports:
7238
+ click.secho("Pre-loaded:", fg='green')
7239
+ for imp in imports:
7240
+ click.echo(f" {imp}")
7241
+ click.echo()
7242
+
7243
+ if includes:
7244
+ click.secho("Pre-loaded:", fg='green')
7245
+ for inc in includes:
7246
+ click.echo(f" {inc}")
7247
+ click.echo()
7248
+
7249
+ # Collect code lines
7250
+ lines = []
7251
+ prompt = '>>> ' if is_python else 'cpp> '
7252
+ continuation = '... ' if is_python else ' > '
7253
+
7254
+ try:
7255
+ while True:
7256
+ try:
7257
+ # Determine prompt
7258
+ current_prompt = prompt if not lines else continuation
7259
+ line = input(current_prompt)
7260
+
7261
+ # Empty line = execute
7262
+ if not line.strip():
7263
+ if lines:
7264
+ break
7265
+ continue
7266
+
7267
+ lines.append(line)
7268
+
7269
+ except EOFError:
7270
+ break
7271
+
7272
+ except KeyboardInterrupt:
7273
+ click.echo()
7274
+ click.secho("Cancelled.", fg='yellow')
7275
+ return
7276
+
7277
+ if not lines:
7278
+ click.secho("No code entered.", fg='yellow')
7279
+ return
7280
+
7281
+ # Build full code
7282
+ code_lines = imports + [''] + lines if imports else lines
7283
+
7284
+ click.echo()
7285
+ click.secho("--- Output ---", fg='green')
7286
+
7287
+ if is_python:
7288
+ # Execute Python code
7289
+ full_code = '\n'.join(code_lines)
7290
+ try:
7291
+ # Use exec with captured output
7292
+ import io
7293
+ from contextlib import redirect_stdout, redirect_stderr
7294
+
7295
+ stdout_capture = io.StringIO()
7296
+ stderr_capture = io.StringIO()
7297
+
7298
+ # Create execution context
7299
+ exec_globals = {'__name__': '__main__'}
7300
+
7301
+ with redirect_stdout(stdout_capture), redirect_stderr(stderr_capture):
7302
+ exec(full_code, exec_globals)
7303
+
7304
+ stdout_val = stdout_capture.getvalue()
7305
+ stderr_val = stderr_capture.getvalue()
7306
+
7307
+ if stdout_val:
7308
+ click.echo(stdout_val, nl=False)
7309
+ if stderr_val:
7310
+ click.secho(stderr_val, fg='red', nl=False)
7311
+
7312
+ if not stdout_val and not stderr_val:
7313
+ click.secho("(no output)", fg='bright_black')
7314
+
7315
+ except Exception as e:
7316
+ click.secho(f"Error: {e}", fg='red')
7317
+
7318
+ else:
7319
+ # Execute C++ code
7320
+ # Build a complete C++ program
7321
+ cpp_code_lines = [
7322
+ '#include <iostream>',
7323
+ '#include <vector>',
7324
+ '#include <string>',
7325
+ '#include <algorithm>',
7326
+ '#include <cmath>',
7327
+ 'using namespace std;',
7328
+ ''
7329
+ ]
7330
+ cpp_code_lines.extend(includes)
7331
+ cpp_code_lines.append('')
7332
+ cpp_code_lines.append('int main() {')
7333
+
7334
+ # Indent user code
7335
+ for line in lines:
7336
+ cpp_code_lines.append(' ' + line)
7337
+
7338
+ cpp_code_lines.append(' return 0;')
7339
+ cpp_code_lines.append('}')
7340
+
7341
+ full_code = '\n'.join(cpp_code_lines)
7342
+
7343
+ # Write to temp file and compile
7344
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.cpp', delete=False, encoding='utf-8') as f:
7345
+ f.write(full_code)
7346
+ cpp_file = f.name
7347
+
7348
+ exe_file = cpp_file.replace('.cpp', '.exe' if sys.platform == 'win32' else '')
7349
+
7350
+ try:
7351
+ # Compile
7352
+ compile_cmd = ['g++', '-std=c++17', '-o', exe_file, cpp_file]
7353
+ result = subprocess.run(compile_cmd, capture_output=True, text=True)
7354
+
7355
+ if result.returncode != 0:
7356
+ click.secho("Compilation Error:", fg='red')
7357
+ click.echo(result.stderr)
7358
+ else:
7359
+ # Run
7360
+ run_result = subprocess.run([exe_file], capture_output=True, text=True, timeout=10)
7361
+ if run_result.stdout:
7362
+ click.echo(run_result.stdout, nl=False)
7363
+ if run_result.stderr:
7364
+ click.secho(run_result.stderr, fg='red', nl=False)
7365
+ if not run_result.stdout and not run_result.stderr:
7366
+ click.secho("(no output)", fg='bright_black')
7367
+
7368
+ except subprocess.TimeoutExpired:
7369
+ click.secho("Execution timed out (10s limit)", fg='red')
7370
+ except FileNotFoundError:
7371
+ click.secho("Error: g++ not found. Install a C++ compiler.", fg='red')
7372
+ except Exception as e:
7373
+ click.secho(f"Error: {e}", fg='red')
7374
+ finally:
7375
+ # Cleanup temp files
7376
+ import os
7377
+ try:
7378
+ os.unlink(cpp_file)
7379
+ if os.path.exists(exe_file):
7380
+ os.unlink(exe_file)
7381
+ except Exception:
7382
+ pass
7383
+
7384
+ click.echo()
7385
+ click.secho("--------------", fg='green')
7386
+
7387
+
7388
+ # ============================================================================
7389
+ # CSSL - Hidden Command Group
7390
+ # ============================================================================
7391
+
7392
+ @click.group(hidden=True)
7393
+ def cssl():
7394
+ """CSSL scripting commands."""
7395
+ pass
7396
+
7397
+
7398
+ @cssl.command(name='exec')
7399
+ @click.argument('path', required=False, type=click.Path())
7400
+ @click.option('--code', '-c', type=str, help='Execute code directly')
7401
+ def cssl_exec(path, code):
7402
+ """Execute CSSL code or file."""
7403
+ from pathlib import Path as PathLib
7404
+
7405
+ try:
7406
+ from ..core.cssl_bridge import CsslLang
7407
+ except ImportError as e:
7408
+ click.secho(f"CSSL runtime not available: {e}", fg='red')
7409
+ return
7410
+
7411
+ cssl_lang = CsslLang()
7412
+
7413
+ # Determine source
7414
+ if code:
7415
+ source = code
7416
+ elif path:
7417
+ path_obj = PathLib(path)
7418
+ if not path_obj.exists():
7419
+ click.secho(f"File not found: {path}", fg='red')
7420
+ return
7421
+ source = path_obj.read_text(encoding='utf-8')
7422
+ else:
7423
+ # Interactive mode
7424
+ click.secho("=== CSSL REPL ===", fg='magenta', bold=True)
7425
+ click.echo("Enter CSSL code. Empty line to execute. CTRL+C to cancel.")
7426
+ click.echo()
7427
+
7428
+ lines = []
7429
+ prompt = 'cssl> '
7430
+
7431
+ try:
7432
+ while True:
7433
+ try:
7434
+ line = input(prompt)
7435
+ if not line.strip():
7436
+ if lines:
7437
+ break
7438
+ continue
7439
+ lines.append(line)
7440
+ except EOFError:
7441
+ break
7442
+ except KeyboardInterrupt:
7443
+ click.echo()
7444
+ click.secho("Cancelled.", fg='yellow')
7445
+ return
7446
+
7447
+ if not lines:
7448
+ click.secho("No code entered.", fg='yellow')
7449
+ return
7450
+
7451
+ source = '\n'.join(lines)
7452
+
7453
+ # Execute
7454
+ click.secho("--- Output ---", fg='green')
7455
+ try:
7456
+ result = cssl_lang.exec(source)
7457
+
7458
+ # Print output buffer
7459
+ for line in cssl_lang.get_output():
7460
+ click.echo(line)
7461
+
7462
+ if result is not None:
7463
+ click.echo(f"Result: {result}")
7464
+
7465
+ except Exception as e:
7466
+ click.secho(f"CSSL Error: {e}", fg='red')
7467
+
7468
+ click.secho("--------------", fg='green')
7469
+
7470
+
7471
+ @cssl.command(name='makemodule')
7472
+ @click.argument('path', type=click.Path(exists=True))
7473
+ @click.option('--output', '-o', type=click.Path(), help='Output path for .cssl-mod')
7474
+ def cssl_makemodule(path, output):
7475
+ """Create a .cssl-mod module from Python/C++ file."""
7476
+ from pathlib import Path as PathLib
7477
+ import pickle
7478
+ import base64
7479
+
7480
+ path_obj = PathLib(path)
7481
+ suffix = path_obj.suffix.lower()
7482
+
7483
+ if suffix not in ('.py', '.cpp', '.cp', '.h'):
7484
+ click.secho(f"Unsupported file type: {suffix}", fg='red')
7485
+ click.echo("Supported: .py, .cpp, .cp, .h")
7486
+ return
7487
+
7488
+ # Read source
7489
+ source = path_obj.read_text(encoding='utf-8')
7490
+
7491
+ # Create module data
7492
+ module_data = {
7493
+ 'name': path_obj.stem,
7494
+ 'type': 'python' if suffix == '.py' else 'cpp',
7495
+ 'source': source,
7496
+ 'version': '1.0',
7497
+ }
7498
+
7499
+ # Encode as base64 pickle
7500
+ encoded = base64.b64encode(pickle.dumps(module_data)).decode('utf-8')
7501
+
7502
+ # Write .cssl-mod file
7503
+ out_path = PathLib(output) if output else path_obj.with_suffix('.cssl-mod')
7504
+ out_path.write_text(f"CSSLMOD1\n{encoded}", encoding='utf-8')
7505
+
7506
+ click.secho(f"Created: {out_path}", fg='green')
7507
+ click.echo(f" Name: {module_data['name']}")
7508
+ click.echo(f" Type: {module_data['type']}")
7509
+
7510
+
7511
+ # Register hidden cssl command group
7512
+ cli.add_command(cssl)
7513
+
7514
+
7136
7515
  # ============================================================================
7137
7516
  # Conditional Registration of Experimental Commands
7138
7517
  # ============================================================================