Open-AutoTools 0.0.3rc5__py3-none-any.whl → 0.0.4rc1__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 (44) hide show
  1. autotools/autocaps/commands.py +3 -7
  2. autotools/autocaps/core.py +5 -4
  3. autotools/autoip/commands.py +6 -11
  4. autotools/autoip/core.py +151 -200
  5. autotools/autolower/commands.py +3 -7
  6. autotools/autolower/core.py +4 -3
  7. autotools/autopassword/commands.py +27 -33
  8. autotools/autopassword/core.py +32 -73
  9. autotools/autotest/__init__.py +2 -0
  10. autotools/autotest/commands.py +205 -0
  11. autotools/cli.py +123 -62
  12. autotools/utils/commands.py +13 -0
  13. autotools/utils/loading.py +14 -6
  14. autotools/utils/performance.py +392 -0
  15. autotools/utils/updates.py +30 -22
  16. autotools/utils/version.py +69 -63
  17. open_autotools-0.0.4rc1.dist-info/METADATA +103 -0
  18. open_autotools-0.0.4rc1.dist-info/RECORD +28 -0
  19. {Open_AutoTools-0.0.3rc5.dist-info → open_autotools-0.0.4rc1.dist-info}/WHEEL +1 -1
  20. {Open_AutoTools-0.0.3rc5.dist-info → open_autotools-0.0.4rc1.dist-info}/entry_points.txt +0 -3
  21. Open_AutoTools-0.0.3rc5.dist-info/METADATA +0 -317
  22. Open_AutoTools-0.0.3rc5.dist-info/RECORD +0 -44
  23. autotools/autocaps/tests/__init__.py +0 -1
  24. autotools/autocaps/tests/test_autocaps_core.py +0 -45
  25. autotools/autocaps/tests/test_autocaps_integration.py +0 -46
  26. autotools/autodownload/__init__.py +0 -0
  27. autotools/autodownload/commands.py +0 -38
  28. autotools/autodownload/core.py +0 -433
  29. autotools/autoip/tests/__init__.py +0 -1
  30. autotools/autoip/tests/test_autoip_core.py +0 -72
  31. autotools/autoip/tests/test_autoip_integration.py +0 -92
  32. autotools/autolower/tests/__init__.py +0 -1
  33. autotools/autolower/tests/test_autolower_core.py +0 -45
  34. autotools/autolower/tests/test_autolower_integration.py +0 -46
  35. autotools/autospell/__init__.py +0 -3
  36. autotools/autospell/commands.py +0 -123
  37. autotools/autospell/core.py +0 -222
  38. autotools/autotranslate/__init__.py +0 -3
  39. autotools/autotranslate/commands.py +0 -42
  40. autotools/autotranslate/core.py +0 -52
  41. autotools/test/__init__.py +0 -3
  42. autotools/test/commands.py +0 -118
  43. {Open_AutoTools-0.0.3rc5.dist-info → open_autotools-0.0.4rc1.dist-info/licenses}/LICENSE +0 -0
  44. {Open_AutoTools-0.0.3rc5.dist-info → open_autotools-0.0.4rc1.dist-info}/top_level.txt +0 -0
@@ -6,96 +6,55 @@ from cryptography.fernet import Fernet
6
6
  from cryptography.hazmat.primitives import hashes
7
7
  from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
8
8
 
9
- def generate_password(length=12, use_uppercase=True, use_numbers=True, use_special=True,
10
- min_special=1, min_numbers=1):
11
- """Generate a secure random password with specified requirements."""
12
-
13
- # DEFINE CHARACTER SETS
9
+ # GENERATES A SECURE RANDOM PASSWORD WITH SPECIFIED REQUIREMENTS
10
+ def generate_password(length=12, use_uppercase=True, use_numbers=True, use_special=True, min_special=1, min_numbers=1):
14
11
  lowercase = string.ascii_lowercase
15
12
  uppercase = string.ascii_uppercase if use_uppercase else ''
16
13
  numbers = string.digits if use_numbers else ''
17
14
  special = "!@#$%^&*()_+-=[]{}|;:,.<>?" if use_special else ''
18
-
19
- # COMBINE ALL ALLOWED CHARACTERS
20
15
  all_chars = lowercase + uppercase + numbers + special
21
-
22
- # ENSURE MINIMUM REQUIREMENTS
23
16
  password = []
24
17
  password.append(secrets.choice(lowercase))
25
- if use_uppercase:
26
- password.append(secrets.choice(uppercase))
27
- if use_numbers:
28
- password.extend(secrets.choice(numbers) for _ in range(min_numbers))
29
- if use_special:
30
- password.extend(secrets.choice(special) for _ in range(min_special))
31
-
32
- # FILL REST OF PASSWORD
18
+
19
+ if use_uppercase: password.append(secrets.choice(uppercase))
20
+ if use_numbers: password.extend(secrets.choice(numbers) for _ in range(min_numbers))
21
+ if use_special: password.extend(secrets.choice(special) for _ in range(min_special))
22
+
33
23
  remaining_length = length - len(password)
34
24
  password.extend(secrets.choice(all_chars) for _ in range(remaining_length))
35
-
36
- random.shuffle(password) # SHUFFLE PASSWORD
37
-
38
- return ''.join(password) ## RETURN PASSWORD
25
+ random.shuffle(password)
39
26
 
27
+ return ''.join(password)
28
+
29
+ # GENERATES ENCRYPTION KEY FOR FERNET SYMMETRIC ENCRYPTION
40
30
  def generate_encryption_key(password=None, salt=None):
41
- """Generate a strong encryption key using Fernet."""
42
- if not password:
43
- # GENERATE A RANDOM KEY
44
- return Fernet.generate_key()
45
-
46
- if not salt:
47
- salt = secrets.token_bytes(16)
48
-
49
- # DERIVE A KEY FROM PASSWORD AND SALT
50
- kdf = PBKDF2HMAC(
51
- algorithm=hashes.SHA256(),
52
- length=32,
53
- salt=salt,
54
- iterations=100000,
55
- )
56
-
57
- # ENCODE KEY IN BASE64
31
+ if not password: return Fernet.generate_key()
32
+ if not salt: salt = secrets.token_bytes(16)
33
+ kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000)
58
34
  key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
59
35
  return key, salt
60
36
 
37
+ # ANALYZES PASSWORD STRENGTH AND PROVIDES IMPROVEMENT SUGGESTIONS
61
38
  def analyze_password_strength(password):
62
- """Analyze password strength and return a score and suggestions."""
63
39
  score = 0
64
40
  suggestions = []
65
-
66
- # CHECK LENGTH
67
- if len(password) >= 12:
68
- score += 2
69
- elif len(password) >= 8:
70
- score += 1
71
- else:
72
- suggestions.append("Password should be at least 8 characters long")
73
-
74
- # CHECK FOR CHARACTER TYPES
75
- if any(c.isupper() for c in password):
76
- score += 1
77
- else:
78
- suggestions.append("Add uppercase letters")
79
-
80
- # CHECK FOR CHARACTER TYPES
81
- if any(c.islower() for c in password):
82
- score += 1
83
- else:
84
- suggestions.append("Add lowercase letters")
85
-
86
- # CHECK FOR CHARACTER TYPES
87
- if any(c.isdigit() for c in password):
88
- score += 1
89
- else:
90
- suggestions.append("Add numbers")
91
-
92
- # CHECK FOR CHARACTER TYPES
93
- if any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password):
94
- score += 1
95
- else:
96
- suggestions.append("Add special characters")
97
-
98
- # RETURN SCORE AND SUGGESTIONS
41
+
42
+ if len(password) >= 12: score += 2
43
+ elif len(password) >= 8: score += 1
44
+ else: suggestions.append("Password should be at least 8 characters long")
45
+
46
+ if any(c.isupper() for c in password): score += 1
47
+ else: suggestions.append("Add uppercase letters")
48
+
49
+ if any(c.islower() for c in password): score += 1
50
+ else: suggestions.append("Add lowercase letters")
51
+
52
+ if any(c.isdigit() for c in password): score += 1
53
+ else: suggestions.append("Add numbers")
54
+
55
+ if any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password): score += 1
56
+ else: suggestions.append("Add special characters")
57
+
99
58
  return {
100
59
  'score': score,
101
60
  'strength': ['Very Weak', 'Weak', 'Medium', 'Strong', 'Very Strong'][min(score, 4)],
@@ -0,0 +1,2 @@
1
+ from .commands import autotest
2
+ __all__ = ['autotest']
@@ -0,0 +1,205 @@
1
+ import click
2
+ import subprocess
3
+ import sys
4
+ import os
5
+ import re
6
+ from ..utils.updates import check_for_updates
7
+
8
+ # CLI COMMAND TO RUN TEST SUITE WITH PYTEST
9
+ @click.command()
10
+ @click.option('--unit', '-u', is_flag=True, help='Run only unit tests')
11
+ @click.option('--integration', '-i', is_flag=True, help='Run only integration tests')
12
+ @click.option('--no-cov', is_flag=True, help='Disable coverage report')
13
+ @click.option('--html', is_flag=True, help='Generate HTML coverage report')
14
+ @click.option('--module', '-m', help='Test specific module (e.g., autocaps, autolower)')
15
+ def autotest(unit, integration, no_cov, html, module):
16
+ _install_test_dependencies()
17
+
18
+ cmd = _build_test_command(unit, integration, no_cov, html, module)
19
+
20
+ click.echo(click.style("\nRunning tests with command:", fg='blue', bold=True))
21
+ click.echo(" ".join(cmd))
22
+ click.echo()
23
+
24
+ _run_test_process(cmd)
25
+
26
+ update_msg = check_for_updates()
27
+ if update_msg: click.echo(update_msg)
28
+
29
+ # INSTALLS TEST DEPENDENCIES IF MISSING BY RUNNING PIP INSTALL COMMAND
30
+ def _install_test_dependencies():
31
+ try:
32
+ import pytest
33
+ import pytest_cov
34
+ except ImportError:
35
+ click.echo(click.style("\n❌ pytest and/or pytest-cov not found. Installing...", fg='yellow', bold=True))
36
+ try:
37
+ subprocess.run(['pip', 'install', 'pytest', 'pytest-cov'], check=True)
38
+ click.echo(click.style("✅ Successfully installed pytest and pytest-cov", fg='green', bold=True))
39
+ except subprocess.CalledProcessError as e:
40
+ click.echo(click.style(f"\n❌ Failed to install dependencies: {str(e)}", fg='red', bold=True))
41
+ sys.exit(1)
42
+
43
+ # BUILDS THE TEST COMMAND ARGUMENTS BY ADDING THE CORRECT TEST PATH AND OPTIONS
44
+ def _build_test_command(unit, integration, no_cov, html, module):
45
+ cmd = [sys.executable, '-m', 'pytest', '-vv', '--capture=no', '--showlocals', '--log-cli-level=DEBUG', '-s']
46
+
47
+ if not no_cov:
48
+ if html: cmd.extend(['--cov-report=html', '--cov=autotools'])
49
+ else: cmd.extend(['--cov-report=term-missing', '--cov=autotools'])
50
+
51
+ if module:
52
+ test_path = f'tests/autotools/{module}'
53
+ if unit and not integration: test_path = f'{test_path}/unit'
54
+ elif integration and not unit: test_path = f'{test_path}/integration'
55
+ cmd.append(test_path)
56
+ else:
57
+ cmd.append('tests')
58
+
59
+ return cmd
60
+
61
+ # PROCESSES TEST OUTPUT LINE BY REMOVING UNNECESSARY CHARACTERS AND FORMATTING
62
+ def _process_test_output_line(line):
63
+ if not line: return None
64
+ line = line.strip()
65
+ if not line: return None
66
+
67
+ if '::' in line and 'autotools/' in line:
68
+ line = line.split('autotools/')[-1].replace('/tests/', '/')
69
+ parts = line.split('/')
70
+ if len(parts) > 1: line = parts[-1]
71
+
72
+ line = re.sub(r'\s+', ' ', line)
73
+ line = re.sub(r'\.+', '.', line)
74
+
75
+ if line.strip('. '): return line
76
+
77
+ return None
78
+
79
+ # EXTRACTS COVERAGE DATA FROM TOTAL LINE
80
+ def _parse_coverage_line(line):
81
+ parts = line.split()
82
+ try:
83
+ if len(parts) >= 4:
84
+ stmts = int(parts[1])
85
+ missed = int(parts[2])
86
+
87
+ # CHECK IF BRANCHES ARE PRESENT
88
+ if len(parts) >= 6 and parts[3].isdigit() and parts[4].isdigit():
89
+ branches = int(parts[3])
90
+ branch_partial = int(parts[4])
91
+ coverage_pct = float(parts[5].rstrip('%'))
92
+ return {
93
+ 'statements': stmts,
94
+ 'missed': missed,
95
+ 'branches': branches,
96
+ 'branch_partial': branch_partial,
97
+ 'coverage': coverage_pct
98
+ }
99
+
100
+ coverage_pct = float(parts[3].rstrip('%'))
101
+ return {'statements': stmts, 'missed': missed, 'coverage': coverage_pct}
102
+ except (ValueError, IndexError):
103
+ match = re.search(r'(\d+\.\d+)%', line)
104
+ if match: return {'coverage': float(match.group(1))}
105
+ return {}
106
+
107
+ # DETERMINES COLOR BASED ON COVERAGE PERCENTAGE
108
+ def _get_coverage_color(percentage):
109
+ if percentage >= 80: return 'green'
110
+ if percentage >= 60: return 'yellow'
111
+ return 'red'
112
+
113
+ # DISPLAYS COVERAGE METRICS
114
+ def _display_coverage_metrics(coverage_data):
115
+ if not coverage_data: return
116
+
117
+ click.echo()
118
+ click.echo(click.style("COVERAGE METRICS", fg='blue', bold=True))
119
+
120
+ # STATEMENTS COVERAGE
121
+ if 'statements' in coverage_data and 'missed' in coverage_data:
122
+ stmts = coverage_data['statements']
123
+ missed = coverage_data['missed']
124
+ covered = stmts - missed
125
+ stmts_pct = (covered / stmts * 100) if stmts > 0 else 0
126
+ color = _get_coverage_color(stmts_pct)
127
+ click.echo(click.style(f"Statements: {covered}/{stmts} ({stmts_pct:.2f}%)", fg=color, bold=True))
128
+
129
+ # BRANCHES COVERAGE
130
+ if 'branches' in coverage_data and coverage_data['branches'] > 0:
131
+ branches = coverage_data['branches']
132
+ branch_partial = coverage_data.get('branch_partial', 0)
133
+ branch_covered = branches - branch_partial
134
+ branch_pct = (branch_covered / branches * 100) if branches > 0 else 0
135
+ color = _get_coverage_color(branch_pct)
136
+ click.echo(click.style(f"Branches: {branch_covered}/{branches} ({branch_pct:.2f}%)", fg=color, bold=True))
137
+
138
+ # OVERALL COVERAGE
139
+ if 'coverage' in coverage_data:
140
+ overall = coverage_data['coverage']
141
+ color = _get_coverage_color(overall)
142
+ click.echo(click.style(f"Overall: {overall:.2f}%", fg=color, bold=True))
143
+
144
+ # RUNS THE TEST PROCESS AND CAPTURES OUTPUT
145
+ def _run_test_process(cmd):
146
+ try:
147
+ env = _prepare_test_environment()
148
+ process = _start_test_process(cmd, env)
149
+ coverage_data = _process_test_output(process)
150
+ _handle_test_result(process, coverage_data)
151
+ except subprocess.CalledProcessError as e:
152
+ click.echo(click.style(f"\n❌ TESTS FAILED WITH RETURN CODE {e.returncode}", fg='red', bold=True))
153
+ sys.exit(1)
154
+ except Exception as e:
155
+ click.echo(click.style(f"\n❌ ERROR RUNNING TESTS: {str(e)}", fg='red', bold=True))
156
+ sys.exit(1)
157
+
158
+ # PREPARES ENVIRONMENT FOR TEST PROCESS
159
+ def _prepare_test_environment():
160
+ env = dict(os.environ)
161
+ env['PYTHONPATH'] = os.getcwd()
162
+ env['FORCE_COLOR'] = '1'
163
+ return env
164
+
165
+ # STARTS THE TEST PROCESS
166
+ def _start_test_process(cmd, env):
167
+ return subprocess.Popen(
168
+ cmd,
169
+ env=env,
170
+ stdout=subprocess.PIPE,
171
+ stderr=subprocess.STDOUT,
172
+ universal_newlines=True,
173
+ bufsize=1
174
+ )
175
+
176
+ # PROCESSES TEST OUTPUT AND EXTRACTS COVERAGE DATA
177
+ def _process_test_output(process):
178
+ coverage_data = {}
179
+
180
+ while True:
181
+ line = process.stdout.readline()
182
+ if not line and process.poll() is not None: break
183
+
184
+ if 'TOTAL' in line and '%' in line:
185
+ coverage_data = _parse_coverage_line(line)
186
+ sys.stdout.write(line)
187
+ sys.stdout.flush()
188
+ continue
189
+
190
+ processed_line = _process_test_output_line(line)
191
+ if processed_line:
192
+ sys.stdout.write(processed_line + '\n')
193
+ sys.stdout.flush()
194
+
195
+ process.wait()
196
+ return coverage_data
197
+
198
+ # HANDLES TEST RESULT AND DISPLAYS COVERAGE
199
+ def _handle_test_result(process, coverage_data):
200
+ if process.returncode == 0:
201
+ click.echo(click.style("\n✅ ALL TESTS PASSED !", fg='green', bold=True))
202
+ _display_coverage_metrics(coverage_data)
203
+ else:
204
+ click.echo(click.style("\n❌ SOME TESTS FAILED!", fg='red', bold=True))
205
+ sys.exit(1)
autotools/cli.py CHANGED
@@ -1,95 +1,156 @@
1
1
  import click
2
- from importlib.metadata import version as get_version, PackageNotFoundError
3
- import pkg_resources
4
2
  import requests
5
- from packaging.version import parse as parse_version
6
- from dotenv import load_dotenv
7
- from datetime import datetime
8
3
  import base64
9
- import json as json_module
10
- from translate import Translator
11
- import yt_dlp
12
4
  import argparse
5
+ import json as json_module
6
+ import sys
7
+
8
+ from dotenv import load_dotenv
9
+ from datetime import datetime
13
10
  from urllib.parse import urlparse
11
+ from packaging.version import parse as parse_version
12
+ from importlib.metadata import version as get_version, PackageNotFoundError
14
13
 
15
- # IMPORT COMMANDS FROM EACH MODULE
16
- from .autocaps.commands import autocaps
17
- from .autolower.commands import autolower
18
- from .autodownload.commands import autodownload
19
- from .autopassword.commands import autopassword
20
- from .autotranslate.commands import autotranslate
21
- from .autoip.commands import autoip
22
- from .autospell.commands import autospell
23
- from .test.commands import test
24
- from .utils.updates import check_for_updates
25
14
  from .utils.version import print_version
15
+ from .utils.updates import check_for_updates
16
+ from .utils.commands import autocaps, autolower, autopassword, autoip, autotest
17
+ from .utils.performance import init_metrics, finalize_metrics, get_metrics, should_enable_metrics, track_step
26
18
 
27
- # LOAD ENVIRONMENT VARIABLES FROM .ENV FILE
28
19
  load_dotenv()
29
20
 
30
- # CLI FUNCTION DEFINITION
31
- @click.group()
32
- @click.option('--version', '--v', is_flag=True, callback=print_version,
21
+ # MAIN CLI ENTRY POINT - REGISTERS ALL COMMANDS AND HANDLES GLOBAL OPTIONS
22
+ @click.group(invoke_without_command=True)
23
+ @click.option('--version', '--v', is_flag=True, callback=lambda ctx, param, value: print_version(ctx, value),
33
24
  expose_value=False, is_eager=True, help='Show version and check for updates')
34
25
  @click.option('--help', '-h', is_flag=True, callback=lambda ctx, param, value:
35
26
  None if not value else (click.echo(ctx.get_help() + '\n' +
36
27
  (check_for_updates() or '')) or ctx.exit()),
37
28
  is_eager=True, expose_value=False, help='Show this message and exit.')
38
- def cli():
39
- """A suite of automated tools for various tasks.
29
+ @click.option('--perf', is_flag=True, help='Display performance metrics')
30
+ @click.pass_context
31
+ def cli(ctx, perf):
32
+ """
33
+ A suite of automated tools for various tasks:\n
34
+ - autocaps: Convert text to uppercase\n
35
+ - autolower: Convert text to lowercase\n
36
+ - autopassword: Generate secure passwords and encryption keys\n
37
+ - autoip: Display network information and run diagnostics\n
38
+ - test: Run the test suite\n
39
+ \n
40
+ Run 'autotools COMMAND --help' for more information on each command.\n
41
+ """
42
+
43
+ # INITIALIZE METRICS IF NEEDED
44
+ if should_enable_metrics(ctx):
45
+ init_metrics()
46
+ get_metrics().step_start('startup')
47
+ get_metrics().step_end()
48
+ get_metrics().end_startup()
40
49
 
41
- Run 'autotools COMMAND --help' for more information on each command."""
42
- pass
50
+ # IF NO COMMAND INVOKED, SHOW HELP
51
+ if ctx.invoked_subcommand is None:
52
+ autotools()
53
+ if should_enable_metrics(ctx):
54
+ get_metrics().end_process()
55
+ finalize_metrics(ctx)
43
56
 
44
- # REGISTER COMMANDS
45
- cli.add_command(autocaps)
46
- cli.add_command(autolower)
47
- cli.add_command(autodownload)
48
- cli.add_command(autopassword)
49
- cli.add_command(autotranslate)
50
- cli.add_command(autoip)
51
- cli.add_command(autospell)
52
- cli.add_command(test)
57
+ # EXECUTES COMMAND WITH PERFORMANCE TRACKING
58
+ def _execute_with_metrics(ctx, original_callback, *args, **kwargs):
59
+ metrics = get_metrics()
60
+ # REMOVE 'perf' FROM kwargs IF PRESENT (IT'S NOT PART OF THE ORIGINAL CALLBACK SIGNATURE)
61
+ kwargs.pop('perf', None)
62
+
63
+ if not should_enable_metrics(ctx): return original_callback(*args, **kwargs)
64
+
65
+ if metrics.process_start is None:
66
+ init_metrics()
67
+ get_metrics().end_startup()
68
+
69
+ metrics.start_command()
70
+ cmd_name = ctx.invoked_subcommand or ctx.command.name or 'unknown'
71
+ try:
72
+ with track_step(f'command_{cmd_name}'): result = original_callback(*args, **kwargs)
73
+ metrics.end_command()
74
+ return result
75
+ finally:
76
+ if metrics.process_end is None:
77
+ metrics.end_process()
78
+ finalize_metrics(ctx)
53
79
 
54
- # MAIN COMMAND DEFINITION
55
- @cli.command()
56
- def autotools():
57
- """Display available commands and tool information."""
58
- # SHOW COMMANDS LIST WITH BETTER FORMATTING
59
- ctx = click.get_current_context()
60
- commands = cli.list_commands(ctx)
80
+ # WRAPS COMMANDS WITH PERFORMANCE TRACKING
81
+ def _wrap_command_with_metrics(cmd):
82
+ import inspect
83
+ original_callback = cmd.callback
61
84
 
85
+ # ADD --perf OPTION TO THE COMMAND SO IT CAN BE USED DIRECTLY ON SUBCOMMANDS
86
+ has_perf_option = any(param.opts == ['--perf'] for param in cmd.params if isinstance(param, click.Option))
87
+ if not has_perf_option:
88
+ perf_option = click.Option(['--perf'], is_flag=True, help='Display performance metrics')
89
+ cmd.params.append(perf_option)
90
+
91
+ sig = inspect.signature(original_callback)
92
+ expects_ctx = 'ctx' in sig.parameters
93
+
94
+ if expects_ctx:
95
+ @click.pass_context
96
+ def wrapped_callback(ctx, *args, **kwargs):
97
+ return _execute_with_metrics(ctx, original_callback, ctx, *args, **kwargs)
98
+ else:
99
+ def wrapped_callback(*args, **kwargs):
100
+ ctx = click.get_current_context()
101
+ return _execute_with_metrics(ctx, original_callback, *args, **kwargs)
102
+
103
+ cmd.callback = wrapped_callback
104
+ return cmd
105
+
106
+ cli.add_command(_wrap_command_with_metrics(autocaps))
107
+ cli.add_command(_wrap_command_with_metrics(autolower))
108
+ cli.add_command(_wrap_command_with_metrics(autopassword))
109
+ cli.add_command(_wrap_command_with_metrics(autoip))
110
+ cli.add_command(_wrap_command_with_metrics(autotest), name='test')
111
+
112
+ # DISPLAYS COMMAND OPTIONS
113
+ def _display_command_options(cmd_obj):
114
+ if not hasattr(cmd_obj, 'params'): return
115
+ click.echo(click.style("\n Options:", fg='yellow'))
116
+ for param in cmd_obj.params:
117
+ if isinstance(param, click.Option):
118
+ opts = '/'.join(param.opts)
119
+ help_text = param.help or ''
120
+ click.echo(f" {click.style(opts, fg='yellow')}")
121
+ click.echo(f" {help_text}")
122
+
123
+ # DISPLAYS ALL AVAILABLE COMMANDS
124
+ def _display_commands(ctx, commands):
62
125
  click.echo(click.style("\nOpen-AutoTools Commands:", fg='blue', bold=True))
63
126
  for cmd in sorted(commands):
64
- if cmd != 'autotools':
65
- cmd_obj = cli.get_command(ctx, cmd)
66
- help_text = cmd_obj.help or cmd_obj.short_help or ''
67
- click.echo(f"\n{click.style(cmd, fg='green', bold=True)}")
68
- click.echo(f" {help_text}")
69
-
70
- # GET OPTIONS FOR EACH COMMAND
71
- if hasattr(cmd_obj, 'params'):
72
- click.echo(click.style("\n Options:", fg='yellow'))
73
- for param in cmd_obj.params:
74
- if isinstance(param, click.Option):
75
- opts = '/'.join(param.opts)
76
- help_text = param.help or ''
77
- click.echo(f" {click.style(opts, fg='yellow')}")
78
- click.echo(f" {help_text}")
127
+ if cmd == 'autotools': continue
128
+ cmd_obj = cli.get_command(ctx, cmd)
129
+ help_text = cmd_obj.help or cmd_obj.short_help or ''
130
+ click.echo(f"\n{click.style(cmd, fg='green', bold=True)}")
131
+ click.echo(f" {help_text}")
132
+ _display_command_options(cmd_obj)
79
133
 
80
- # SHOW USAGE EXAMPLES
134
+ # DISPLAYS USAGE EXAMPLES
135
+ def _display_usage_examples():
81
136
  click.echo(click.style("\nUsage Examples:", fg='blue', bold=True))
82
137
  click.echo(" autotools --help Show this help message")
83
138
  click.echo(" autotools --version Show version information")
84
139
  click.echo(" autotools COMMAND Run a specific command")
85
140
  click.echo(" autotools COMMAND --help Show help for a specific command")
86
141
 
87
- # CHECK FOR UPDATES
142
+ # DISPLAYS ALL AVAILABLE COMMANDS WITH THEIR OPTIONS AND USAGE EXAMPLES
143
+ @cli.command()
144
+ def autotools():
145
+ ctx = click.get_current_context()
146
+ commands = cli.list_commands(ctx)
147
+
148
+ _display_commands(ctx, commands)
149
+ _display_usage_examples()
150
+
88
151
  update_msg = check_for_updates()
89
152
  if update_msg:
90
153
  click.echo(click.style("\nUpdate Available:", fg='red', bold=True))
91
154
  click.echo(update_msg)
92
155
 
93
- # ENTRY POINT
94
- if __name__ == '__main__':
95
- cli()
156
+ if __name__ == '__main__': cli()
@@ -0,0 +1,13 @@
1
+ from ..autocaps.commands import autocaps
2
+ from ..autolower.commands import autolower
3
+ from ..autopassword.commands import autopassword
4
+ from ..autoip.commands import autoip
5
+ from ..autotest.commands import autotest
6
+
7
+ __all__ = [
8
+ 'autocaps',
9
+ 'autolower',
10
+ 'autopassword',
11
+ 'autoip',
12
+ 'autotest'
13
+ ]
@@ -1,16 +1,24 @@
1
+ import threading
1
2
  from halo import Halo
2
3
 
4
+ # PATCHES THREAD SET DAEMON METHOD TO PREVENT WARNINGS
5
+ def _patched_set_daemon(self, daemon):
6
+ self.daemon = daemon
7
+
8
+ _original_set_daemon = threading.Thread.setDaemon
9
+ threading.Thread.setDaemon = _patched_set_daemon
10
+
11
+ # CONTEXT MANAGER FOR DISPLAYING LOADING ANIMATION
3
12
  class LoadingAnimation:
4
- """A CONTEXT MANAGER FOR DISPLAYING A LOADING ANIMATION"""
13
+ # INITIALIZES SPINNER WITH CUSTOM ANIMATION FRAMES
5
14
  def __init__(self):
6
- self._spinner = Halo(spinner={
7
- 'interval': 200,
8
- 'frames': [' ', '. ', '.. ', '...'],
9
- })
15
+ self._spinner = Halo(spinner={'interval': 200, 'frames': [' ', '. ', '.. ', '...']})
10
16
 
17
+ # STARTS THE LOADING ANIMATION
11
18
  def __enter__(self):
12
19
  self._spinner.start()
13
20
  return self
14
21
 
22
+ # STOPS THE LOADING ANIMATION
15
23
  def __exit__(self, exc_type, exc_val, exc_tb):
16
- self._spinner.stop()
24
+ self._spinner.stop()