Open-AutoTools 0.0.3rc1__py3-none-any.whl → 0.0.3rc3__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.
- {Open_AutoTools-0.0.3rc1.dist-info → Open_AutoTools-0.0.3rc3.dist-info}/METADATA +58 -67
- {Open_AutoTools-0.0.3rc1.dist-info → Open_AutoTools-0.0.3rc3.dist-info}/RECORD +22 -9
- autotools/autocaps/commands.py +17 -0
- autotools/autodownload/commands.py +38 -0
- autotools/autodownload/core.py +149 -50
- autotools/autoip/commands.py +29 -0
- autotools/autolower/commands.py +17 -0
- autotools/autopassword/commands.py +76 -0
- autotools/autopassword/core.py +1 -0
- autotools/autospell/commands.py +123 -0
- autotools/autotranslate/commands.py +42 -0
- autotools/cli.py +30 -502
- autotools/test/__init__.py +3 -0
- autotools/test/commands.py +120 -0
- autotools/utils/__init__.py +5 -0
- autotools/utils/loading.py +16 -0
- autotools/utils/updates.py +30 -0
- autotools/utils/version.py +74 -0
- {Open_AutoTools-0.0.3rc1.dist-info → Open_AutoTools-0.0.3rc3.dist-info}/LICENSE +0 -0
- {Open_AutoTools-0.0.3rc1.dist-info → Open_AutoTools-0.0.3rc3.dist-info}/WHEEL +0 -0
- {Open_AutoTools-0.0.3rc1.dist-info → Open_AutoTools-0.0.3rc3.dist-info}/entry_points.txt +0 -0
- {Open_AutoTools-0.0.3rc1.dist-info → Open_AutoTools-0.0.3rc3.dist-info}/top_level.txt +0 -0
autotools/cli.py
CHANGED
|
@@ -1,103 +1,32 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import click
|
|
3
|
-
import base64
|
|
4
|
-
import json as json_module
|
|
5
2
|
from importlib.metadata import version as get_version, PackageNotFoundError
|
|
3
|
+
import pkg_resources
|
|
4
|
+
import requests
|
|
6
5
|
from packaging.version import parse as parse_version
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
download_file,
|
|
12
|
-
validate_youtube_url
|
|
13
|
-
)
|
|
14
|
-
from autotools.autopassword.core import (
|
|
15
|
-
generate_password,
|
|
16
|
-
generate_encryption_key,
|
|
17
|
-
analyze_password_strength
|
|
18
|
-
)
|
|
6
|
+
from dotenv import load_dotenv
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
import base64
|
|
9
|
+
import json as json_module
|
|
19
10
|
from translate import Translator
|
|
20
|
-
from autotools.autotranslate.core import translate_text, get_supported_languages
|
|
21
11
|
import yt_dlp
|
|
22
|
-
from autotools import autodownload, autolower, autocaps, autoip
|
|
23
12
|
import argparse
|
|
24
|
-
from autotools.autospell.core import SpellChecker
|
|
25
13
|
from urllib.parse import urlparse
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
import
|
|
29
|
-
import
|
|
30
|
-
import
|
|
31
|
-
from
|
|
14
|
+
|
|
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
|
+
from .utils.version import print_version
|
|
32
26
|
|
|
33
27
|
# LOAD ENVIRONMENT VARIABLES FROM .ENV FILE
|
|
34
28
|
load_dotenv()
|
|
35
29
|
|
|
36
|
-
# VERSION CALLBACK
|
|
37
|
-
def print_version(ctx, param, value):
|
|
38
|
-
"""PRINT VERSION AND CHECK FOR UPDATES"""
|
|
39
|
-
|
|
40
|
-
# EXIT IF VERSION IS NOT REQUESTED
|
|
41
|
-
if not value or ctx.resilient_parsing:
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
# GET CURRENT VERSION
|
|
45
|
-
try:
|
|
46
|
-
pkg_version = get_version('Open-AutoTools')
|
|
47
|
-
click.echo(f"Open-AutoTools version {pkg_version}")
|
|
48
|
-
|
|
49
|
-
try:
|
|
50
|
-
# CHECK IF PACKAGE IS FROM TESTPYPI
|
|
51
|
-
is_testpypi = False
|
|
52
|
-
try:
|
|
53
|
-
import pkg_resources
|
|
54
|
-
dist = pkg_resources.get_distribution('Open-AutoTools')
|
|
55
|
-
is_testpypi = 'test.pypi.org' in dist.location
|
|
56
|
-
except:
|
|
57
|
-
pass
|
|
58
|
-
|
|
59
|
-
# DETERMINE PYPI URL BASED ON SOURCE
|
|
60
|
-
pypi_url = "https://test.pypi.org/pypi/Open-AutoTools/json" if is_testpypi else "https://pypi.org/pypi/Open-AutoTools/json"
|
|
61
|
-
|
|
62
|
-
# CHECK LATEST VERSION FROM PYPI
|
|
63
|
-
response = requests.get(pypi_url)
|
|
64
|
-
if response.status_code == 200:
|
|
65
|
-
data = response.json()
|
|
66
|
-
latest_version = data["info"]["version"]
|
|
67
|
-
# GET RELEASE DATE
|
|
68
|
-
releases = data["releases"]
|
|
69
|
-
if latest_version in releases and releases[latest_version]:
|
|
70
|
-
try:
|
|
71
|
-
upload_time = releases[latest_version][0]["upload_time"]
|
|
72
|
-
# TRY DIFFERENT DATE FORMATS
|
|
73
|
-
try:
|
|
74
|
-
published_date = datetime.strptime(upload_time, "%Y-%m-%dT%H:%M:%S")
|
|
75
|
-
except ValueError:
|
|
76
|
-
try:
|
|
77
|
-
published_date = datetime.strptime(upload_time, "%Y-%m-%dT%H:%M:%S.%fZ")
|
|
78
|
-
except ValueError:
|
|
79
|
-
published_date = datetime.strptime(upload_time, "%Y-%m-%d %H:%M:%S")
|
|
80
|
-
formatted_date = published_date.strftime("%d %B %Y at %H:%M:%S")
|
|
81
|
-
click.echo(f"Released: {formatted_date}")
|
|
82
|
-
except Exception:
|
|
83
|
-
# IF DATE PARSING FAILS, SKIP SHOWING THE DATE
|
|
84
|
-
pass
|
|
85
|
-
|
|
86
|
-
# PARSE VERSIONS FOR COMPARISON
|
|
87
|
-
current_parsed = parse_version(pkg_version)
|
|
88
|
-
latest_parsed = parse_version(latest_version)
|
|
89
|
-
|
|
90
|
-
# COMPARE VERSIONS AND PRINT UPDATE MESSAGE IF NEEDED
|
|
91
|
-
if latest_parsed > current_parsed:
|
|
92
|
-
update_cmd = "pip install --upgrade -i https://test.pypi.org/simple/ Open-AutoTools" if is_testpypi else "pip install --upgrade Open-AutoTools"
|
|
93
|
-
click.echo(click.style(f"\nUpdate available: v{latest_version}", fg='red', bold=True))
|
|
94
|
-
click.echo(click.style(f"Run '{update_cmd}' to update", fg='red'))
|
|
95
|
-
except Exception as e:
|
|
96
|
-
click.echo(f"Error checking updates: {str(e)}")
|
|
97
|
-
except PackageNotFoundError:
|
|
98
|
-
click.echo("Open-AutoTools version information not available")
|
|
99
|
-
ctx.exit()
|
|
100
|
-
|
|
101
30
|
# CLI FUNCTION DEFINITION
|
|
102
31
|
@click.group()
|
|
103
32
|
@click.option('--version', '--v', is_flag=True, callback=print_version,
|
|
@@ -112,7 +41,17 @@ def cli():
|
|
|
112
41
|
Run 'autotools COMMAND --help' for more information on each command."""
|
|
113
42
|
pass
|
|
114
43
|
|
|
115
|
-
#
|
|
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)
|
|
53
|
+
|
|
54
|
+
# MAIN COMMAND DEFINITION
|
|
116
55
|
@cli.command()
|
|
117
56
|
def autotools():
|
|
118
57
|
"""Display available commands and tool information."""
|
|
@@ -151,417 +90,6 @@ def autotools():
|
|
|
151
90
|
click.echo(click.style("\nUpdate Available:", fg='red', bold=True))
|
|
152
91
|
click.echo(update_msg)
|
|
153
92
|
|
|
154
|
-
|
|
155
|
-
"""CHECK IF AN UPDATE IS AVAILABLE AND RETURN UPDATE MESSAGE IF NEEDED"""
|
|
156
|
-
|
|
157
|
-
# GET CURRENT VERSION
|
|
158
|
-
try:
|
|
159
|
-
current_version = get_version('Open-AutoTools')
|
|
160
|
-
|
|
161
|
-
# CHECK IF PACKAGE IS FROM TESTPYPI
|
|
162
|
-
is_testpypi = False
|
|
163
|
-
try:
|
|
164
|
-
# ATTEMPT TO GET PACKAGE METADATA
|
|
165
|
-
import pkg_resources
|
|
166
|
-
dist = pkg_resources.get_distribution('Open-AutoTools')
|
|
167
|
-
is_testpypi = 'test.pypi.org' in dist.location
|
|
168
|
-
except:
|
|
169
|
-
pass
|
|
170
|
-
|
|
171
|
-
# DETERMINE PYPI URL BASED ON SOURCE
|
|
172
|
-
pypi_url = "https://test.pypi.org/pypi/Open-AutoTools/json" if is_testpypi else "https://pypi.org/pypi/Open-AutoTools/json"
|
|
173
|
-
|
|
174
|
-
# CHECK FOR UPDATES FROM PYPI
|
|
175
|
-
response = requests.get(pypi_url)
|
|
176
|
-
|
|
177
|
-
# CHECK IF RESPONSE IS SUCCESSFUL
|
|
178
|
-
if response.status_code == 200:
|
|
179
|
-
data = response.json()
|
|
180
|
-
latest_version = data["info"]["version"]
|
|
181
|
-
|
|
182
|
-
# PARSE VERSIONS FOR COMPARISON
|
|
183
|
-
current_parsed = parse_version(current_version)
|
|
184
|
-
latest_parsed = parse_version(latest_version)
|
|
185
|
-
|
|
186
|
-
# PRINT UPDATE MESSAGE IF NEEDED
|
|
187
|
-
if latest_parsed > current_parsed:
|
|
188
|
-
update_cmd = "pip install --upgrade -i https://test.pypi.org/simple/ Open-AutoTools" if is_testpypi else "pip install --upgrade Open-AutoTools"
|
|
189
|
-
return (
|
|
190
|
-
click.style(f"\nUpdate available: v{latest_version}", fg='red', bold=True) + "\n" +
|
|
191
|
-
click.style(f"Run '{update_cmd}' to update", fg='red')
|
|
192
|
-
)
|
|
193
|
-
except Exception as e:
|
|
194
|
-
# FOR DEBUGGING, LOG ERROR
|
|
195
|
-
print(f"Error checking updates: {str(e)}")
|
|
196
|
-
pass
|
|
197
|
-
return None
|
|
198
|
-
|
|
199
|
-
# AUTOCAPS COMMAND LINE INTERFACE FUNCTION DEFINITION
|
|
200
|
-
@cli.command()
|
|
201
|
-
@click.argument('text', nargs=-1)
|
|
202
|
-
def autocaps(text):
|
|
203
|
-
"""Convert text to UPPERCASE."""
|
|
204
|
-
result = autocaps_transform(" ".join(text))
|
|
205
|
-
click.echo(result)
|
|
206
|
-
|
|
207
|
-
# UPDATE CHECK AT THE END
|
|
208
|
-
update_msg = check_for_updates()
|
|
209
|
-
if update_msg:
|
|
210
|
-
click.echo(update_msg)
|
|
211
|
-
|
|
212
|
-
# AUTOLOWER CASE COMMAND LINE INTERFACE FUNCTION DEFINITION
|
|
213
|
-
@cli.command()
|
|
214
|
-
@click.argument('text', nargs=-1)
|
|
215
|
-
def autolower(text):
|
|
216
|
-
"""Convert text to lowercase."""
|
|
217
|
-
result = autolower_transform(" ".join(text))
|
|
218
|
-
click.echo(result)
|
|
219
|
-
|
|
220
|
-
# UPDATE CHECK AT THE END
|
|
221
|
-
update_msg = check_for_updates()
|
|
222
|
-
if update_msg:
|
|
223
|
-
click.echo(update_msg)
|
|
224
|
-
|
|
225
|
-
# AUTODOWNLOAD COMMAND LINE INTERFACE FUNCTION DEFINITION
|
|
226
|
-
@cli.command()
|
|
227
|
-
@click.argument('url')
|
|
228
|
-
@click.option('--format', '-f', type=click.Choice(['mp4', 'mp3'], case_sensitive=False),
|
|
229
|
-
default='mp4', help='Output file format')
|
|
230
|
-
@click.option('--quality', '-q', type=click.Choice(['best', '1440p', '1080p', '720p', '480p', '360p', '240p'],
|
|
231
|
-
case_sensitive=False), default='best', help='Video quality (mp4 only)')
|
|
232
|
-
def autodownload(url, format, quality):
|
|
233
|
-
"""Download videos from YouTube or files from any URL.
|
|
234
|
-
|
|
235
|
-
Supports YouTube video download with quality selection and format conversion (mp4/mp3).
|
|
236
|
-
For non-YouTube URLs, downloads the file directly."""
|
|
237
|
-
if "youtube.com" in url or "youtu.be" in url:
|
|
238
|
-
# VALIDATE YOUTUBE URL FIRST
|
|
239
|
-
if not validate_youtube_url(url):
|
|
240
|
-
click.echo("Invalid YouTube URL", err=True)
|
|
241
|
-
sys.exit(1)
|
|
242
|
-
download_youtube_video(url, format, quality)
|
|
243
|
-
else:
|
|
244
|
-
download_file(url)
|
|
245
|
-
|
|
246
|
-
# UPDATE CHECK AT THE END
|
|
247
|
-
update_msg = check_for_updates()
|
|
248
|
-
if update_msg:
|
|
249
|
-
click.echo(update_msg)
|
|
250
|
-
|
|
251
|
-
# AUTOPASSWORD COMMAND LINE INTERFACE FUNCTION DEFINITION
|
|
252
|
-
@cli.command()
|
|
253
|
-
@click.option('--length', '-l', default=12, help='Password length (default: 12)')
|
|
254
|
-
@click.option('--no-uppercase', '-u', is_flag=True, help='Exclude uppercase letters')
|
|
255
|
-
@click.option('--no-numbers', '-n', is_flag=True, help='Exclude numbers')
|
|
256
|
-
@click.option('--no-special', '-s', is_flag=True, help='Exclude special characters')
|
|
257
|
-
@click.option('--min-special', '-m', default=1, help='Minimum number of special characters')
|
|
258
|
-
@click.option('--min-numbers', '-d', default=1, help='Minimum number of numbers')
|
|
259
|
-
@click.option('--analyze', '-a', is_flag=True, help='Analyze password strength')
|
|
260
|
-
@click.option('--gen-key', '-g', is_flag=True, help='Generate encryption key')
|
|
261
|
-
@click.option('--password-key', '-p', help='Generate key from password')
|
|
262
|
-
def autopassword(length, no_uppercase, no_numbers, no_special,
|
|
263
|
-
min_special, min_numbers, analyze, gen_key, password_key):
|
|
264
|
-
"""Generate secure passwords and encryption keys."""
|
|
265
|
-
|
|
266
|
-
## HELPER FUNCTION TO SHOW PASSWORD/KEY ANALYSIS
|
|
267
|
-
def show_analysis(text, prefix=""):
|
|
268
|
-
"""Helper function to show password/key analysis"""
|
|
269
|
-
if analyze:
|
|
270
|
-
analysis = analyze_password_strength(text)
|
|
271
|
-
click.echo(f"\n{prefix}Strength Analysis:")
|
|
272
|
-
click.echo(f"Strength: {analysis['strength']}")
|
|
273
|
-
click.echo(f"Score: {analysis['score']}/5")
|
|
274
|
-
if analysis['suggestions']:
|
|
275
|
-
click.echo("\nSuggestions for improvement:")
|
|
276
|
-
for suggestion in analysis['suggestions']:
|
|
277
|
-
click.echo(f"- {suggestion}")
|
|
278
|
-
|
|
279
|
-
# GENERATE KEY
|
|
280
|
-
if gen_key:
|
|
281
|
-
key = generate_encryption_key()
|
|
282
|
-
key_str = key.decode()
|
|
283
|
-
click.echo(f"Encryption Key: {key_str}")
|
|
284
|
-
if analyze:
|
|
285
|
-
show_analysis(key_str, "Key ")
|
|
286
|
-
return
|
|
287
|
-
|
|
288
|
-
# GENERATE KEY FROM PASSWORD
|
|
289
|
-
if password_key:
|
|
290
|
-
key, salt = generate_encryption_key(password_key)
|
|
291
|
-
key_str = key.decode()
|
|
292
|
-
click.echo(f"Derived Key: {key_str}")
|
|
293
|
-
click.echo(f"Salt: {base64.b64encode(salt).decode()}")
|
|
294
|
-
if analyze:
|
|
295
|
-
click.echo("\nAnalyzing source password:")
|
|
296
|
-
show_analysis(password_key, "Password ")
|
|
297
|
-
click.echo("\nAnalyzing generated key:")
|
|
298
|
-
show_analysis(key_str, "Key ")
|
|
299
|
-
return
|
|
300
|
-
|
|
301
|
-
# GENERATE PASSWORD
|
|
302
|
-
password = generate_password(
|
|
303
|
-
length=length,
|
|
304
|
-
use_uppercase=not no_uppercase,
|
|
305
|
-
use_numbers=not no_numbers,
|
|
306
|
-
use_special=not no_special,
|
|
307
|
-
min_special=min_special,
|
|
308
|
-
min_numbers=min_numbers,
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
# SHOW PASSWORD
|
|
312
|
-
click.echo(f"Generated Password: {password}")
|
|
313
|
-
show_analysis(password, "Password ")
|
|
314
|
-
|
|
315
|
-
# UPDATE CHECK AT THE END
|
|
316
|
-
update_msg = check_for_updates()
|
|
317
|
-
if update_msg:
|
|
318
|
-
click.echo(update_msg)
|
|
319
|
-
|
|
320
|
-
# TRANSLATE COMMAND LINE INTERFACE FUNCTION DEFINITION
|
|
321
|
-
@cli.command()
|
|
322
|
-
@click.argument('text', required=False)
|
|
323
|
-
@click.option('--to', default='en', help='Target language (default: en)')
|
|
324
|
-
@click.option('--from', 'from_lang', help='Source language (default: auto-detect)')
|
|
325
|
-
@click.option('--list-languages', is_flag=True, help='List all supported languages')
|
|
326
|
-
@click.option('--copy', is_flag=True, help='Copy translation to clipboard')
|
|
327
|
-
@click.option('--detect', is_flag=True, help='Show detected source language')
|
|
328
|
-
@click.option('--output', '-o', type=click.Path(), help='Save translation to file')
|
|
329
|
-
def autotranslate(text: str, to: str, from_lang: str, list_languages: bool,
|
|
330
|
-
copy: bool, detect: bool, output: str):
|
|
331
|
-
"""Translate text to specified language.
|
|
332
|
-
|
|
333
|
-
Supports automatic language detection, multiple target languages,
|
|
334
|
-
clipboard operations and file output. Use --list-languages to see
|
|
335
|
-
all supported language codes."""
|
|
336
|
-
# LIST ALL SUPPORTED LANGUAGES
|
|
337
|
-
if list_languages:
|
|
338
|
-
click.echo("\nSupported Languages:")
|
|
339
|
-
for code, name in get_supported_languages().items():
|
|
340
|
-
click.echo(f"{code:<8} {name}")
|
|
341
|
-
return
|
|
342
|
-
|
|
343
|
-
# CHECK IF TEXT IS PROVIDED
|
|
344
|
-
if not text:
|
|
345
|
-
click.echo("Error: Please provide text to translate")
|
|
346
|
-
return
|
|
347
|
-
|
|
348
|
-
result = translate_text(text, to_lang=to, from_lang=from_lang,
|
|
349
|
-
copy=copy, detect_lang=detect, output=output)
|
|
350
|
-
click.echo(result)
|
|
351
|
-
|
|
352
|
-
# UPDATE CHECK AT THE END
|
|
353
|
-
update_msg = check_for_updates()
|
|
354
|
-
if update_msg:
|
|
355
|
-
click.echo(update_msg)
|
|
356
|
-
|
|
357
|
-
# AUTOIP COMMAND LINE INTERFACE FUNCTION DEFINITION
|
|
358
|
-
@cli.command()
|
|
359
|
-
@click.option('--test', '-t', is_flag=True, help='Run connectivity tests')
|
|
360
|
-
@click.option('--speed', '-s', is_flag=True, help='Run internet speed test')
|
|
361
|
-
@click.option('--monitor', '-m', is_flag=True, help='Monitor network traffic')
|
|
362
|
-
@click.option('--interval', '-i', default=1, help='Monitoring interval in seconds')
|
|
363
|
-
@click.option('--ports', '-p', is_flag=True, help='Check common ports status')
|
|
364
|
-
@click.option('--dns', '-d', is_flag=True, help='Show DNS servers')
|
|
365
|
-
@click.option('--location', '-l', is_flag=True, help='Show IP location info')
|
|
366
|
-
@click.option('--no-ip', '-n', is_flag=True, help='Hide IP addresses')
|
|
367
|
-
def autoip(test, speed, monitor, interval, ports, dns, location, no_ip):
|
|
368
|
-
"""Display network information and diagnostics.
|
|
369
|
-
|
|
370
|
-
Shows local and public IP addresses, runs network diagnostics,
|
|
371
|
-
performs speed tests, monitors traffic with custom intervals,
|
|
372
|
-
checks ports, displays DNS information and provides geolocation data."""
|
|
373
|
-
from autotools.autoip.core import run
|
|
374
|
-
output = run(test=test, speed=speed, monitor=monitor, interval=interval,
|
|
375
|
-
ports=ports, dns=dns, location=location, no_ip=no_ip)
|
|
376
|
-
click.echo(output)
|
|
377
|
-
|
|
378
|
-
# UPDATE CHECK AT THE END
|
|
379
|
-
update_msg = check_for_updates()
|
|
380
|
-
if update_msg:
|
|
381
|
-
click.echo(update_msg)
|
|
382
|
-
|
|
383
|
-
# AUTOSPELL COMMAND LINE INTERFACE FUNCTION DEFINITION
|
|
384
|
-
@cli.command()
|
|
385
|
-
@click.argument('texts', nargs=-1)
|
|
386
|
-
@click.option('--lang', '-l', default='auto', help='Language code (auto for detection)')
|
|
387
|
-
@click.option('--fix', '-f', is_flag=True, help='Auto-fix text and copy to clipboard')
|
|
388
|
-
@click.option('--copy', '-c', is_flag=True, help='Copy result to clipboard')
|
|
389
|
-
@click.option('--list-languages', is_flag=True, help='List supported languages')
|
|
390
|
-
@click.option('--json', '-j', is_flag=True, help='Output results as JSON')
|
|
391
|
-
@click.option('--ignore', '-i', multiple=True,
|
|
392
|
-
type=click.Choice(['spelling', 'grammar', 'style', 'punctuation']),
|
|
393
|
-
help='Error types to ignore')
|
|
394
|
-
@click.option('--interactive', '-n', is_flag=True,
|
|
395
|
-
help='Interactive mode - confirm each correction')
|
|
396
|
-
@click.option('--output', '-o', type=click.Path(),
|
|
397
|
-
help='Save corrections to file')
|
|
398
|
-
def autospell(texts: tuple, lang: str, fix: bool, copy: bool, list_languages: bool,
|
|
399
|
-
json: bool, ignore: tuple, interactive: bool, output: str):
|
|
400
|
-
"""Check and fix text for spelling, grammar, style, and punctuation errors.
|
|
401
|
-
|
|
402
|
-
Provides comprehensive text analysis with support for multiple languages,
|
|
403
|
-
interactive corrections, and various output formats (text/JSON).
|
|
404
|
-
Can ignore specific error types: spelling, grammar, style, or punctuation."""
|
|
405
|
-
checker = SpellChecker()
|
|
406
|
-
|
|
407
|
-
# LIST ALL SUPPORTED LANGUAGES
|
|
408
|
-
if list_languages:
|
|
409
|
-
languages = checker.get_supported_languages()
|
|
410
|
-
if json:
|
|
411
|
-
result = {'languages': languages}
|
|
412
|
-
click.echo(json_module.dumps(result, indent=2))
|
|
413
|
-
else:
|
|
414
|
-
click.echo("\nSupported Languages:")
|
|
415
|
-
for lang in languages:
|
|
416
|
-
click.echo(f"{lang['code']:<8} {lang['name']}")
|
|
417
|
-
return
|
|
418
|
-
|
|
419
|
-
# CHECK AND FIX SPELLING/GRAMMAR IN TEXT
|
|
420
|
-
for text in texts:
|
|
421
|
-
if not text:
|
|
422
|
-
click.echo("Error: Please provide text to check")
|
|
423
|
-
continue
|
|
424
|
-
|
|
425
|
-
# FIX SPELLING/GRAMMAR IN TEXT
|
|
426
|
-
if fix:
|
|
427
|
-
# CORRECT TEXT WITH SPELL CHECKER
|
|
428
|
-
corrected = checker.fix_text(text, lang, copy_to_clipboard=True,
|
|
429
|
-
ignore=ignore, interactive=interactive)
|
|
430
|
-
result = {'corrected_text': corrected} # RESULT TO RETURN
|
|
431
|
-
|
|
432
|
-
# OUTPUT RESULTS AS JSON
|
|
433
|
-
if json:
|
|
434
|
-
click.echo(json_module.dumps(result, indent=2))
|
|
435
|
-
else:
|
|
436
|
-
# LANGUAGE INFORMATION
|
|
437
|
-
check_result = checker.check_text(text, lang)
|
|
438
|
-
lang_info = check_result['language']
|
|
439
|
-
click.echo(f"\nLanguage detected: {lang_info['name']} ({lang_info['code']})")
|
|
440
|
-
click.echo(f"Confidence: {lang_info['confidence']:.2%}")
|
|
441
|
-
click.echo("\nCorrected text (copied to clipboard):")
|
|
442
|
-
click.echo(corrected)
|
|
443
|
-
|
|
444
|
-
# SAVE CORRECTIONS TO FILE
|
|
445
|
-
if output:
|
|
446
|
-
with open(output, 'w', encoding='utf-8') as f:
|
|
447
|
-
if json:
|
|
448
|
-
json_module.dump(result, f, indent=2)
|
|
449
|
-
else:
|
|
450
|
-
f.write(corrected)
|
|
451
|
-
else:
|
|
452
|
-
# CHECK SPELLING/GRAMMAR IN TEXT
|
|
453
|
-
check_result = checker.check_text(text, lang)
|
|
454
|
-
|
|
455
|
-
# OUTPUT RESULTS AS JSON
|
|
456
|
-
if json:
|
|
457
|
-
click.echo(json_module.dumps(check_result, indent=2))
|
|
458
|
-
else:
|
|
459
|
-
lang_info = check_result['language']
|
|
460
|
-
click.echo(f"\nLanguage detected: {lang_info['name']} ({lang_info['code']})")
|
|
461
|
-
click.echo(f"Confidence: {lang_info['confidence']:.2%}")
|
|
462
|
-
click.echo(f"Total errors found: {check_result['statistics']['total_errors']}")
|
|
463
|
-
|
|
464
|
-
# CORRECTIONS SUGGESTED
|
|
465
|
-
if check_result['corrections']:
|
|
466
|
-
click.echo("\nCorrections suggested:")
|
|
467
|
-
for i, corr in enumerate(check_result['corrections'], 1):
|
|
468
|
-
click.echo(f"\n{i}. [{corr['severity'].upper()}] {corr['message']}")
|
|
469
|
-
click.echo(f" Context: {corr['context']}")
|
|
470
|
-
if corr['replacements']:
|
|
471
|
-
click.echo(f" Suggestions: {', '.join(corr['replacements'][:3])}")
|
|
472
|
-
|
|
473
|
-
# SAVE CHECK RESULT TO FILE
|
|
474
|
-
if output:
|
|
475
|
-
with open(output, 'w', encoding='utf-8') as f:
|
|
476
|
-
if json:
|
|
477
|
-
json_module.dump(check_result, f, indent=2)
|
|
478
|
-
else:
|
|
479
|
-
# WRITE A HUMAN-READABLE REPORT
|
|
480
|
-
f.write(f"Language: {lang_info['name']} ({lang_info['code']})\n")
|
|
481
|
-
f.write(f"Confidence: {lang_info['confidence']:.2%}\n")
|
|
482
|
-
f.write(f"Total errors: {check_result['statistics']['total_errors']}\n\n")
|
|
483
|
-
|
|
484
|
-
# CORRECTIONS SUGGESTED
|
|
485
|
-
if check_result['corrections']:
|
|
486
|
-
f.write("Corrections suggested:\n")
|
|
487
|
-
for i, corr in enumerate(check_result['corrections'], 1):
|
|
488
|
-
f.write(f"\n{i}. [{corr['severity'].upper()}] {corr['message']}\n")
|
|
489
|
-
f.write(f" Context: {corr['context']}\n")
|
|
490
|
-
if corr['replacements']:
|
|
491
|
-
f.write(f" Suggestions: {', '.join(corr['replacements'][:3])}\n")
|
|
492
|
-
|
|
493
|
-
# UPDATE CHECK AT THE END
|
|
494
|
-
update_msg = check_for_updates()
|
|
495
|
-
if update_msg:
|
|
496
|
-
click.echo(update_msg)
|
|
497
|
-
|
|
498
|
-
# TEST COMMAND LINE INTERFACE FUNCTION DEFINITION
|
|
499
|
-
@cli.command()
|
|
500
|
-
@click.option('--unit', '-u', is_flag=True, help='Run only unit tests')
|
|
501
|
-
@click.option('--integration', '-i', is_flag=True, help='Run only integration tests')
|
|
502
|
-
@click.option('--no-cov', is_flag=True, help='Disable coverage report')
|
|
503
|
-
@click.option('--html', is_flag=True, help='Generate HTML coverage report')
|
|
504
|
-
@click.option('--module', '-m', help='Test specific module (e.g., autocaps, autolower)')
|
|
505
|
-
def test(unit, integration, no_cov, html, module):
|
|
506
|
-
"""Run test suite with various options."""
|
|
507
|
-
# CHECK IF PYTEST IS INSTALLED
|
|
508
|
-
try:
|
|
509
|
-
import pytest
|
|
510
|
-
import pytest_cov
|
|
511
|
-
except ImportError:
|
|
512
|
-
click.echo(click.style("\n❌ pytest and/or pytest-cov not found. Installing...", fg='yellow', bold=True))
|
|
513
|
-
try:
|
|
514
|
-
subprocess.run(['pip', 'install', 'pytest', 'pytest-cov'], check=True)
|
|
515
|
-
click.echo(click.style("✅ Successfully installed pytest and pytest-cov", fg='green', bold=True))
|
|
516
|
-
except subprocess.CalledProcessError as e:
|
|
517
|
-
click.echo(click.style(f"\n❌ Failed to install dependencies: {str(e)}", fg='red', bold=True))
|
|
518
|
-
sys.exit(1)
|
|
519
|
-
|
|
520
|
-
cmd = ['python', '-m', 'pytest', '-v'] # BASE COMMAND
|
|
521
|
-
|
|
522
|
-
# COVERAGE OPTIONS
|
|
523
|
-
if not no_cov:
|
|
524
|
-
cmd.extend(['--cov=autotools'])
|
|
525
|
-
if html:
|
|
526
|
-
cmd.extend(['--cov-report=html'])
|
|
527
|
-
else:
|
|
528
|
-
cmd.extend(['--cov-report=term-missing'])
|
|
529
|
-
|
|
530
|
-
# TEST SELECTION
|
|
531
|
-
test_path = 'autotools'
|
|
532
|
-
if module:
|
|
533
|
-
if unit and not integration:
|
|
534
|
-
cmd.append(f'autotools/{module}/tests/test_{module}_core.py')
|
|
535
|
-
elif integration and not unit:
|
|
536
|
-
cmd.append(f'autotools/{module}/tests/test_{module}_integration.py')
|
|
537
|
-
else:
|
|
538
|
-
cmd.append(f'autotools/{module}/tests')
|
|
539
|
-
|
|
540
|
-
# SHOW COMMAND BEING RUN
|
|
541
|
-
click.echo(click.style("\nRunning tests with command:", fg='blue', bold=True))
|
|
542
|
-
click.echo(" ".join(cmd))
|
|
543
|
-
click.echo()
|
|
544
|
-
|
|
545
|
-
# RUN TESTS
|
|
546
|
-
try:
|
|
547
|
-
result = subprocess.run(cmd, check=True)
|
|
548
|
-
if result.returncode == 0:
|
|
549
|
-
click.echo(click.style("\n✅ All tests passed!", fg='green', bold=True))
|
|
550
|
-
else:
|
|
551
|
-
click.echo(click.style("\n❌ Some tests failed!", fg='red', bold=True))
|
|
552
|
-
sys.exit(1)
|
|
553
|
-
except subprocess.CalledProcessError as e:
|
|
554
|
-
click.echo(click.style(f"\n❌ Tests failed with return code {e.returncode}", fg='red', bold=True))
|
|
555
|
-
sys.exit(1)
|
|
556
|
-
except Exception as e:
|
|
557
|
-
click.echo(click.style(f"\n❌ Error running tests: {str(e)}", fg='red', bold=True))
|
|
558
|
-
sys.exit(1)
|
|
559
|
-
|
|
560
|
-
# UPDATE CHECK AT THE END
|
|
561
|
-
update_msg = check_for_updates()
|
|
562
|
-
if update_msg:
|
|
563
|
-
click.echo(update_msg)
|
|
564
|
-
|
|
565
|
-
# MAIN FUNCTION TO RUN CLI
|
|
93
|
+
# ENTRY POINT
|
|
566
94
|
if __name__ == '__main__':
|
|
567
95
|
cli()
|
|
@@ -0,0 +1,120 @@
|
|
|
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
|
+
@click.command()
|
|
9
|
+
@click.option('--unit', '-u', is_flag=True, help='Run only unit tests')
|
|
10
|
+
@click.option('--integration', '-i', is_flag=True, help='Run only integration tests')
|
|
11
|
+
@click.option('--no-cov', is_flag=True, help='Disable coverage report')
|
|
12
|
+
@click.option('--html', is_flag=True, help='Generate HTML coverage report')
|
|
13
|
+
@click.option('--module', '-m', help='Test specific module (e.g., autocaps, autolower)')
|
|
14
|
+
def test(unit, integration, no_cov, html, module):
|
|
15
|
+
"""Run test suite with various options."""
|
|
16
|
+
# CHECK IF PYTEST IS INSTALLED
|
|
17
|
+
try:
|
|
18
|
+
import pytest
|
|
19
|
+
import pytest_cov
|
|
20
|
+
except ImportError:
|
|
21
|
+
click.echo(click.style("\n❌ pytest and/or pytest-cov not found. Installing...", fg='yellow', bold=True))
|
|
22
|
+
try:
|
|
23
|
+
subprocess.run(['pip', 'install', 'pytest', 'pytest-cov'], check=True)
|
|
24
|
+
click.echo(click.style("✅ Successfully installed pytest and pytest-cov", fg='green', bold=True))
|
|
25
|
+
except subprocess.CalledProcessError as e:
|
|
26
|
+
click.echo(click.style(f"\n❌ Failed to install dependencies: {str(e)}", fg='red', bold=True))
|
|
27
|
+
sys.exit(1)
|
|
28
|
+
|
|
29
|
+
# BASE COMMAND WITH ENHANCED VERBOSITY
|
|
30
|
+
cmd = [
|
|
31
|
+
'python', '-m', 'pytest', # PYTHON MODULE AND TEST COMMAND
|
|
32
|
+
'--capture=no', # SHOW PRINT STATEMENTS AND CAPTURED OUTPUT
|
|
33
|
+
'--full-trace', # SHOW FULL TRACEBACK
|
|
34
|
+
'-vv', # VERY VERBOSE OUTPUT
|
|
35
|
+
'--durations=0', # SHOW ALL TEST DURATIONS
|
|
36
|
+
'--showlocals', # SHOW LOCAL VARIABLES IN TRACEBACKS
|
|
37
|
+
'--log-cli-level=DEBUG', # SHOW DEBUG LOGS
|
|
38
|
+
'--tb=long', # LONG TRACEBACK STYLE
|
|
39
|
+
'-s' # SHORTCUT FOR --capture=no
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# COVERAGE OPTIONS
|
|
43
|
+
if not no_cov:
|
|
44
|
+
cmd.extend(['--cov=autotools'])
|
|
45
|
+
if html:
|
|
46
|
+
cmd.extend(['--cov-report=html'])
|
|
47
|
+
else:
|
|
48
|
+
cmd.extend(['--cov-report=term-missing'])
|
|
49
|
+
|
|
50
|
+
# TEST SELECTION
|
|
51
|
+
test_path = 'autotools'
|
|
52
|
+
if module:
|
|
53
|
+
if unit and not integration:
|
|
54
|
+
cmd.append(f'autotools/{module}/tests/test_{module}_core.py')
|
|
55
|
+
elif integration and not unit:
|
|
56
|
+
cmd.append(f'autotools/{module}/tests/test_{module}_integration.py')
|
|
57
|
+
else:
|
|
58
|
+
cmd.append(f'autotools/{module}/tests')
|
|
59
|
+
|
|
60
|
+
# SHOW COMMAND BEING RUN
|
|
61
|
+
click.echo(click.style("\nRunning tests with command:", fg='blue', bold=True))
|
|
62
|
+
click.echo(" ".join(cmd))
|
|
63
|
+
click.echo()
|
|
64
|
+
|
|
65
|
+
# RUN TESTS
|
|
66
|
+
try:
|
|
67
|
+
env = dict(os.environ)
|
|
68
|
+
env['PYTHONPATH'] = os.getcwd()
|
|
69
|
+
env['FORCE_COLOR'] = '1' # FORCE COLORS IN OUTPUT
|
|
70
|
+
|
|
71
|
+
process = subprocess.Popen(
|
|
72
|
+
cmd,
|
|
73
|
+
env=env,
|
|
74
|
+
stdout=subprocess.PIPE,
|
|
75
|
+
stderr=subprocess.STDOUT,
|
|
76
|
+
universal_newlines=True,
|
|
77
|
+
bufsize=1
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# READ AND PROCESS OUTPUT IN REAL-TIME
|
|
81
|
+
while True:
|
|
82
|
+
line = process.stdout.readline()
|
|
83
|
+
if not line and process.poll() is not None:
|
|
84
|
+
break
|
|
85
|
+
if line:
|
|
86
|
+
# CLEAN THE LINE
|
|
87
|
+
line = line.strip()
|
|
88
|
+
if line: # ONLY PROCESS NON-EMPTY LINES
|
|
89
|
+
if '::' in line and 'autotools/' in line:
|
|
90
|
+
# REMOVE PARENT DIRECTORY PATHS
|
|
91
|
+
line = line.split('autotools/')[-1].replace('/tests/', '/')
|
|
92
|
+
# REMOVE MODULE PARENT DIRECTORY
|
|
93
|
+
parts = line.split('/')
|
|
94
|
+
if len(parts) > 1:
|
|
95
|
+
line = parts[-1]
|
|
96
|
+
# REMOVE MULTIPLE SPACES AND DOTS
|
|
97
|
+
line = re.sub(r'\s+', ' ', line)
|
|
98
|
+
line = re.sub(r'\.+', '.', line)
|
|
99
|
+
# REMOVE EMPTY LINES WITH JUST DOTS OR SPACES
|
|
100
|
+
if line.strip('. '):
|
|
101
|
+
sys.stdout.write(line + '\n')
|
|
102
|
+
sys.stdout.flush()
|
|
103
|
+
|
|
104
|
+
process.wait()
|
|
105
|
+
if process.returncode == 0:
|
|
106
|
+
click.echo(click.style("\n✅ All tests passed!", fg='green', bold=True))
|
|
107
|
+
else:
|
|
108
|
+
click.echo(click.style("\n❌ Some tests failed!", fg='red', bold=True))
|
|
109
|
+
sys.exit(1)
|
|
110
|
+
except subprocess.CalledProcessError as e:
|
|
111
|
+
click.echo(click.style(f"\n❌ Tests failed with return code {e.returncode}", fg='red', bold=True))
|
|
112
|
+
sys.exit(1)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
click.echo(click.style(f"\n❌ Error running tests: {str(e)}", fg='red', bold=True))
|
|
115
|
+
sys.exit(1)
|
|
116
|
+
|
|
117
|
+
# UPDATE CHECK AT THE END
|
|
118
|
+
update_msg = check_for_updates()
|
|
119
|
+
if update_msg:
|
|
120
|
+
click.echo(update_msg)
|