evolver-tools 3.0.0__tar.gz → 3.1.0__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.
- {evolver_tools-3.0.0/src/evolver_tools.egg-info → evolver_tools-3.1.0}/PKG-INFO +2 -2
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/pyproject.toml +2 -2
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/cli.py +1 -1
- evolver_tools-3.1.0/src/evolver_tools/vendor/crypto_price.py +108 -0
- evolver_tools-3.1.0/src/evolver_tools/vendor/dice_roll.py +99 -0
- evolver_tools-3.1.0/src/evolver_tools/vendor/ip_location.py +108 -0
- evolver_tools-3.1.0/src/evolver_tools/vendor/text_stats.py +119 -0
- evolver_tools-3.1.0/src/evolver_tools/vendor/unit_convert.py +137 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0/src/evolver_tools.egg-info}/PKG-INFO +2 -2
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools.egg-info/SOURCES.txt +5 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/LICENSE +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/README.md +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/setup.cfg +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/autoreg.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/agent_b_tool.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/api_tester.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ascii_gen.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/audit_log.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/b64/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/b64/b64.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/backup.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/banner/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/banner/banner.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/banner.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/bookmark.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/cal_tool/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/cal_tool/cli.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/cert_check.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/changelog_gen/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/changelog_gen/changelog_gen.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/changelog_gen.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/chart_cli/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/chart_cli/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/checksum_dir.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/clipboard/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/clipboard/clipboard.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/colorize.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/colors/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/colors/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/config_validator.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/cron/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/cron/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/crontab_helper.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/csv_stats/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/csv_stats/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/csv_stats/analyzer.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/csv_stats/cli.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/db_schema.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/dep_graph.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/diff_csv.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/diff_tool/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/diff_tool/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/dirsize/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/disk_usage/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/disk_usage/disk_usage.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/dt_convert.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/env_manager.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/envcheck/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/excel2csv.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ff/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ff/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/figlet_cli.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/figlet_tool.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/file_encrypt.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/find_dups/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/find_dups/cli.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/firewall_rule.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/fmt/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/fmt/fmt.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/git_branch_cleaner.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/hashsum/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/hashsum/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/html2markdown.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/html2md.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/http_live/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/http_live/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/image_meta.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ini_parser/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ini_parser/ini_parser.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ipcalc/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ipcalc/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ipinfo/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ipinfo/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/join.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/joke.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/jq_lite/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/jq_lite/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/json2csv/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/json2csv/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/json_pretty/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/json_pretty/json_pretty.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/jsonql/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/jsonql/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/license_cli/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/license_cli/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/license_cli/cli.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/log_analyzer.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/log_tail.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/markdown_check/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/morse.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/nb/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/nb/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/net_speed.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/note_taker.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/otp_gen.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/passgen/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/password_strength.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/portcheck/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/portcheck/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/pr_tool/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/pr_tool/pr_tool.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/process_kill.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/progress_bar.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/project_doctor/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/project_doctor/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/qrcode.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/quote_tool/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/quote_tool/quote.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/rainbow.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/reminder.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ren/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ren/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/restore.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/scan_ports.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/service_check.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/shuffle.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/siege_lite/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/siege_lite/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/smellfinder/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/smellfinder/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/sort/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/sort/sort.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/spinner.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/split.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/split_tool/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/split_tool/split.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/sql2csv.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/sqlite_cli/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/sqlite_cli/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ssh_key_gen.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/ssl_check.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/stopwatch.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/sysmon/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/sysmon/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/timer/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/timer_pro/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/timer_pro/timer_pro.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/timer_pro.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/todo_cli.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/treedir/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/treedir/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/uniq_tool/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/uniq_tool/uniq.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/urlparse_tool/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/urlparse_tool/cli.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/uuid_tool/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/uuid_tool/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/weather_cli.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/web_summary/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/web_summary/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/wordcount/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/wordcount/__main__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/xml2json.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/yaml2json/__init__.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools/vendor/yaml2json/yaml2json.py +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools.egg-info/dependency_links.txt +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools.egg-info/entry_points.txt +0 -0
- {evolver_tools-3.0.0 → evolver_tools-3.1.0}/src/evolver_tools.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evolver-tools
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 3.1.0
|
|
4
|
+
Summary: 105 essential CLI tools - one pip install
|
|
5
5
|
Author: EVOLVER
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://evolver-dev.github.io/evolver-tools
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "evolver-tools"
|
|
7
|
-
version = "3.
|
|
8
|
-
description = "
|
|
7
|
+
version = "3.1.0"
|
|
8
|
+
description = "105 essential CLI tools - one pip install"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.8"
|
|
@@ -14,7 +14,7 @@ from evolver_tools.autoreg import auto_discover
|
|
|
14
14
|
def list_tools():
|
|
15
15
|
"""Display all available tools."""
|
|
16
16
|
tools = auto_discover()
|
|
17
|
-
print(f'\x1b[1;36m===== EVOLVER Tools v3.
|
|
17
|
+
print(f'\x1b[1;36m===== EVOLVER Tools v3.1.0 =====\x1b[0m')
|
|
18
18
|
print()
|
|
19
19
|
for name, info in sorted(tools.items()):
|
|
20
20
|
print(f' \033[1;33m{name:<18}\033[0m {info["desc"]}')
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Cryptocurrency price ticker using CoinGecko free API.
|
|
4
|
+
|
|
5
|
+
Fetches real-time cryptocurrency prices in a specified fiat currency.
|
|
6
|
+
Uses only Python stdlib (urllib, json, sys) — zero external dependencies.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python -m evolver_tools.vendor.crypto_price --coin bitcoin
|
|
10
|
+
python -m evolver_tools.vendor.crypto_price --coin ethereum --currency eur
|
|
11
|
+
python -m evolver_tools.vendor.crypto_price --list
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
import urllib.request
|
|
18
|
+
import urllib.error
|
|
19
|
+
|
|
20
|
+
TOOL_META = {
|
|
21
|
+
'name': 'crypto-price',
|
|
22
|
+
'func': 'main',
|
|
23
|
+
'desc': 'Cryptocurrency price ticker (CoinGecko)',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
COINGECKO_SIMPLE_URL = 'https://api.coingecko.com/api/v3/simple/price'
|
|
27
|
+
COINGECKO_LIST_URL = 'https://api.coingecko.com/api/v3/coins/list'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _api_get(url: str) -> dict:
|
|
31
|
+
"""Make a GET request to the CoinGecko API and return parsed JSON."""
|
|
32
|
+
req = urllib.request.Request(url, headers={'User-Agent': 'crypto-price/1.0'})
|
|
33
|
+
try:
|
|
34
|
+
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
35
|
+
return json.loads(resp.read().decode('utf-8'))
|
|
36
|
+
except urllib.error.HTTPError as e:
|
|
37
|
+
msg = e.read().decode('utf-8', errors='replace') if e.fp else ''
|
|
38
|
+
print(f'HTTP error {e.code}: {msg}', file=sys.stderr)
|
|
39
|
+
sys.exit(1)
|
|
40
|
+
except urllib.error.URLError as e:
|
|
41
|
+
print(f'Network error: {e.reason}', file=sys.stderr)
|
|
42
|
+
sys.exit(1)
|
|
43
|
+
except (json.JSONDecodeError, OSError, ValueError) as e:
|
|
44
|
+
print(f'Error parsing response: {e}', file=sys.stderr)
|
|
45
|
+
sys.exit(1)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def list_coins() -> None:
|
|
49
|
+
"""Fetch and display all available coins from CoinGecko."""
|
|
50
|
+
data = _api_get(COINGECKO_LIST_URL)
|
|
51
|
+
print(f'Total coins listed: {len(data)}\n')
|
|
52
|
+
# Show first 50 as a sample; user can filter with grep
|
|
53
|
+
for coin in data[:50]:
|
|
54
|
+
print(f'{coin["id"]:40s} {coin["symbol"]:10s} {coin["name"]}')
|
|
55
|
+
if len(data) > 50:
|
|
56
|
+
print(f'\n... and {len(data) - 50} more. Pipe through grep or use --coin <id>.')
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_price(coin: str, currency: str) -> None:
|
|
60
|
+
"""Fetch and display the price for a single coin."""
|
|
61
|
+
url = f'{COINGECKO_SIMPLE_URL}?ids={coin}&vs_currencies={currency}'
|
|
62
|
+
data = _api_get(url)
|
|
63
|
+
if coin not in data:
|
|
64
|
+
print(
|
|
65
|
+
f'Coin "{coin}" not found. Use --list to see available coins.',
|
|
66
|
+
file=sys.stderr,
|
|
67
|
+
)
|
|
68
|
+
sys.exit(1)
|
|
69
|
+
if currency not in data[coin]:
|
|
70
|
+
print(
|
|
71
|
+
f'Currency "{currency}" not available for {coin}.',
|
|
72
|
+
file=sys.stderr,
|
|
73
|
+
)
|
|
74
|
+
sys.exit(1)
|
|
75
|
+
price = data[coin][currency]
|
|
76
|
+
print(f'{coin}: {price:.8f} {currency.upper()}')
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def main() -> None:
|
|
80
|
+
"""Parse arguments and run the appropriate action."""
|
|
81
|
+
parser = argparse.ArgumentParser(
|
|
82
|
+
description='Cryptocurrency price ticker (CoinGecko)',
|
|
83
|
+
)
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
'--coin',
|
|
86
|
+
default='bitcoin',
|
|
87
|
+
help='Coin ID to look up (default: bitcoin)',
|
|
88
|
+
)
|
|
89
|
+
parser.add_argument(
|
|
90
|
+
'--currency',
|
|
91
|
+
default='usd',
|
|
92
|
+
help='Fiat currency symbol (default: usd)',
|
|
93
|
+
)
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
'--list',
|
|
96
|
+
action='store_true',
|
|
97
|
+
help='List all available coins and exit',
|
|
98
|
+
)
|
|
99
|
+
args = parser.parse_args()
|
|
100
|
+
|
|
101
|
+
if args.list:
|
|
102
|
+
list_coins()
|
|
103
|
+
else:
|
|
104
|
+
get_price(args.coin, args.currency)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == '__main__':
|
|
108
|
+
main()
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Zero-dependency CLI dice roller (d4, d6, d8, d10, d12, d20, d100)."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import random
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
TOOL_META = {
|
|
9
|
+
'name': 'dice-roll',
|
|
10
|
+
'func': 'main',
|
|
11
|
+
'desc': 'Dice roller (d4, d6, d8, d10, d12, d20, d100)',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def parse_dice_spec(spec: str) -> tuple[int, int]:
|
|
16
|
+
"""Parse a dice spec like '2d6' into (count, sides).
|
|
17
|
+
|
|
18
|
+
Raises ValueError on invalid format or unsupported die type.
|
|
19
|
+
"""
|
|
20
|
+
if 'd' not in spec:
|
|
21
|
+
raise ValueError(f"Invalid dice spec '{spec}': missing 'd' (e.g. 2d6)")
|
|
22
|
+
|
|
23
|
+
parts = spec.split('d', maxsplit=1)
|
|
24
|
+
count_str, sides_str = parts
|
|
25
|
+
|
|
26
|
+
if not sides_str:
|
|
27
|
+
raise ValueError(f"Invalid dice spec '{spec}': no sides after 'd'")
|
|
28
|
+
|
|
29
|
+
count = int(count_str) if count_str else 1
|
|
30
|
+
sides = int(sides_str)
|
|
31
|
+
|
|
32
|
+
valid_sides = {4, 6, 8, 10, 12, 20, 100}
|
|
33
|
+
if sides not in valid_sides:
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"Unsupported die type d{sides}. Supported: d4, d6, d8, d10, d12, d20, d100"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if count < 1:
|
|
39
|
+
raise ValueError(f"Number of dice must be at least 1, got {count}")
|
|
40
|
+
|
|
41
|
+
return count, sides
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def roll_dice(count: int, sides: int) -> list[int]:
|
|
45
|
+
"""Roll `count` dice with `sides` sides, returning a list of results."""
|
|
46
|
+
return [random.randint(1, sides) for _ in range(count)]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def format_results(results: list[int], sides: int) -> str:
|
|
50
|
+
"""Format a roll result as a human-readable string."""
|
|
51
|
+
total = sum(results)
|
|
52
|
+
parts = ', '.join(str(r) for r in results)
|
|
53
|
+
label = f"d{sides}"
|
|
54
|
+
return f"{label}: [{parts}] = {total}"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main(argv: list[str] | None = None) -> int:
|
|
58
|
+
"""CLI entry point. Parse args, roll dice, print results."""
|
|
59
|
+
parser = argparse.ArgumentParser(
|
|
60
|
+
description='Dice roller — roll any supported die type.',
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
'--dice',
|
|
64
|
+
type=str,
|
|
65
|
+
default='1d6',
|
|
66
|
+
help='Dice to roll, e.g. 2d6, 1d20, 3d10 (default: 1d6)',
|
|
67
|
+
)
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
'--rolls',
|
|
70
|
+
type=int,
|
|
71
|
+
default=1,
|
|
72
|
+
help='Number of times to roll (default: 1)',
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
args = parser.parse_args(argv)
|
|
76
|
+
|
|
77
|
+
if args.rolls < 1:
|
|
78
|
+
print(f"error: --rolls must be at least 1, got {args.rolls}", file=sys.stderr)
|
|
79
|
+
return 1
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
count, sides = parse_dice_spec(args.dice)
|
|
83
|
+
except ValueError as e:
|
|
84
|
+
print(f"error: {e}", file=sys.stderr)
|
|
85
|
+
return 1
|
|
86
|
+
|
|
87
|
+
for i in range(args.rolls):
|
|
88
|
+
results = roll_dice(count, sides)
|
|
89
|
+
line = format_results(results, sides)
|
|
90
|
+
if args.rolls > 1:
|
|
91
|
+
print(f"Roll #{i + 1}: {line}")
|
|
92
|
+
else:
|
|
93
|
+
print(line)
|
|
94
|
+
|
|
95
|
+
return 0
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == '__main__':
|
|
99
|
+
sys.exit(main())
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
GeoIP location lookup using ip-api.com (free, no API key required).
|
|
4
|
+
|
|
5
|
+
Queries the public ip-api.com API to determine the geographic location
|
|
6
|
+
of an IP address. Uses only Python stdlib (urllib, json, sys) —
|
|
7
|
+
zero external dependencies.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python -m evolver_tools.vendor.ip_location
|
|
11
|
+
python -m evolver_tools.vendor.ip_location 8.8.8.8
|
|
12
|
+
python -m evolver_tools.vendor.ip_location --json 1.1.1.1
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import json
|
|
17
|
+
import sys
|
|
18
|
+
import urllib.error
|
|
19
|
+
import urllib.request
|
|
20
|
+
|
|
21
|
+
TOOL_META = {
|
|
22
|
+
'name': 'ip-location',
|
|
23
|
+
'func': 'main',
|
|
24
|
+
'desc': 'GeoIP location lookup (ip-api.com)',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
API_BASE = 'http://ip-api.com/json/'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _api_get(ip: str) -> dict:
|
|
31
|
+
"""Query ip-api.com for the given IP and return parsed JSON."""
|
|
32
|
+
url = API_BASE + ip
|
|
33
|
+
req = urllib.request.Request(url, headers={'User-Agent': 'ip-location/1.0'})
|
|
34
|
+
try:
|
|
35
|
+
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
36
|
+
return json.loads(resp.read().decode('utf-8'))
|
|
37
|
+
except urllib.error.HTTPError as e:
|
|
38
|
+
msg = e.read().decode('utf-8', errors='replace') if e.fp else ''
|
|
39
|
+
print(f'HTTP error {e.code}: {msg}', file=sys.stderr)
|
|
40
|
+
sys.exit(1)
|
|
41
|
+
except urllib.error.URLError as e:
|
|
42
|
+
print(f'Network error: {e.reason}', file=sys.stderr)
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
except (json.JSONDecodeError, OSError, ValueError) as e:
|
|
45
|
+
print(f'Error parsing response: {e}', file=sys.stderr)
|
|
46
|
+
sys.exit(1)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def lookup(ip: str, as_json: bool = False) -> None:
|
|
50
|
+
"""Look up an IP address and display the result."""
|
|
51
|
+
data = _api_get(ip)
|
|
52
|
+
|
|
53
|
+
if data.get('status') != 'success':
|
|
54
|
+
msg = data.get('message', 'unknown error')
|
|
55
|
+
print(f'Lookup failed for "{ip}": {msg}', file=sys.stderr)
|
|
56
|
+
sys.exit(1)
|
|
57
|
+
|
|
58
|
+
if as_json:
|
|
59
|
+
print(json.dumps(data, indent=2, ensure_ascii=False))
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
fields = {
|
|
63
|
+
'IP': data.get('query', ip),
|
|
64
|
+
'Country': data.get('country', ''),
|
|
65
|
+
'Country Code': data.get('countryCode', ''),
|
|
66
|
+
'Region': data.get('regionName', ''),
|
|
67
|
+
'Region Code': data.get('region', ''),
|
|
68
|
+
'City': data.get('city', ''),
|
|
69
|
+
'ZIP': data.get('zip', ''),
|
|
70
|
+
'Latitude': str(data.get('lat', '')),
|
|
71
|
+
'Longitude': str(data.get('lon', '')),
|
|
72
|
+
'Timezone': data.get('timezone', ''),
|
|
73
|
+
'ISP': data.get('isp', ''),
|
|
74
|
+
'Organization': data.get('org', ''),
|
|
75
|
+
'AS': data.get('as', ''),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
max_key_len = max(len(k) for k in fields)
|
|
79
|
+
for key, val in fields.items():
|
|
80
|
+
if val:
|
|
81
|
+
print(f'{key:>{max_key_len + 1}} {val}')
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def main() -> None:
|
|
85
|
+
"""Parse arguments and run the lookup."""
|
|
86
|
+
parser = argparse.ArgumentParser(
|
|
87
|
+
description='GeoIP location lookup via ip-api.com',
|
|
88
|
+
)
|
|
89
|
+
parser.add_argument(
|
|
90
|
+
'ip',
|
|
91
|
+
nargs='?',
|
|
92
|
+
default='',
|
|
93
|
+
help='IP address to look up (default: auto, queries your own IP)',
|
|
94
|
+
)
|
|
95
|
+
parser.add_argument(
|
|
96
|
+
'--json',
|
|
97
|
+
action='store_true',
|
|
98
|
+
dest='as_json',
|
|
99
|
+
help='Output raw JSON response',
|
|
100
|
+
)
|
|
101
|
+
args = parser.parse_args()
|
|
102
|
+
|
|
103
|
+
ip = args.ip if args.ip else ''
|
|
104
|
+
lookup(ip, as_json=args.as_json)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == '__main__':
|
|
108
|
+
main()
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""text-stats — File/stdin text statistics analyzer.
|
|
3
|
+
|
|
4
|
+
Usage: text-stats [--file PATH]
|
|
5
|
+
cat file.txt | text-stats
|
|
6
|
+
|
|
7
|
+
Analyzes text and reports: word count, character count, line count,
|
|
8
|
+
sentence count, average word length, and estimated reading time.
|
|
9
|
+
Zero-dependency (stdlib only).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
import argparse
|
|
14
|
+
|
|
15
|
+
# Average reading speed: ~238 words per minute (common benchmark)
|
|
16
|
+
WPM = 238
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def count_sentences(text):
|
|
20
|
+
"""Count sentences by splitting on terminal punctuation."""
|
|
21
|
+
import re
|
|
22
|
+
sentences = re.split(r'[.!?]+', text)
|
|
23
|
+
return len([s for s in sentences if s.strip()])
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def count_words(text):
|
|
27
|
+
"""Count words (whitespace-delimited tokens containing letters)."""
|
|
28
|
+
words = text.split()
|
|
29
|
+
return len(words)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def count_chars(text):
|
|
33
|
+
"""Count characters including spaces, excluding newlines."""
|
|
34
|
+
return len(text)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def average_word_length(text):
|
|
38
|
+
"""Compute average word length (characters per word)."""
|
|
39
|
+
words = [w for w in text.split() if w.strip()]
|
|
40
|
+
if not words:
|
|
41
|
+
return 0.0
|
|
42
|
+
return sum(len(w) for w in words) / len(words)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def reading_time(word_count):
|
|
46
|
+
"""Estimate reading time in minutes (decimal)."""
|
|
47
|
+
return word_count / WPM
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def format_reading_time(minutes):
|
|
51
|
+
"""Format reading time as human-readable string."""
|
|
52
|
+
if minutes < 1:
|
|
53
|
+
secs = round(minutes * 60)
|
|
54
|
+
return f"{secs} sec"
|
|
55
|
+
mins = int(minutes)
|
|
56
|
+
secs = round((minutes - mins) * 60)
|
|
57
|
+
if secs == 0:
|
|
58
|
+
return f"{mins} min"
|
|
59
|
+
return f"{mins} min {secs} sec"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def main():
|
|
63
|
+
parser = argparse.ArgumentParser(
|
|
64
|
+
description="Text statistics analyzer — word/char/line/sentence counts, "
|
|
65
|
+
"avg word length, reading time."
|
|
66
|
+
)
|
|
67
|
+
parser.add_argument(
|
|
68
|
+
"--file", "-f",
|
|
69
|
+
type=str,
|
|
70
|
+
help="Path to input file (reads from stdin if omitted)"
|
|
71
|
+
)
|
|
72
|
+
args = parser.parse_args()
|
|
73
|
+
|
|
74
|
+
# Read input
|
|
75
|
+
if args.file:
|
|
76
|
+
try:
|
|
77
|
+
with open(args.file, "r", encoding="utf-8") as fh:
|
|
78
|
+
text = fh.read()
|
|
79
|
+
except FileNotFoundError:
|
|
80
|
+
print(f"Error: file not found: {args.file}", file=sys.stderr)
|
|
81
|
+
sys.exit(1)
|
|
82
|
+
except IOError as e:
|
|
83
|
+
print(f"Error reading file: {e}", file=sys.stderr)
|
|
84
|
+
sys.exit(1)
|
|
85
|
+
elif not sys.stdin.isatty():
|
|
86
|
+
text = sys.stdin.read()
|
|
87
|
+
else:
|
|
88
|
+
parser.print_help()
|
|
89
|
+
sys.exit(0)
|
|
90
|
+
|
|
91
|
+
# Compute statistics
|
|
92
|
+
chars = count_chars(text)
|
|
93
|
+
words = count_words(text)
|
|
94
|
+
lines = text.count("\n")
|
|
95
|
+
if lines == 0 and text:
|
|
96
|
+
lines = 1
|
|
97
|
+
sentences = count_sentences(text)
|
|
98
|
+
avg_wlen = average_word_length(text)
|
|
99
|
+
read_mins = reading_time(words)
|
|
100
|
+
read_str = format_reading_time(read_mins)
|
|
101
|
+
|
|
102
|
+
# Output
|
|
103
|
+
print(f"Characters: {chars}")
|
|
104
|
+
print(f"Words: {words}")
|
|
105
|
+
print(f"Lines: {lines}")
|
|
106
|
+
print(f"Sentences: {sentences}")
|
|
107
|
+
print(f"Avg word length: {avg_wlen:.2f}")
|
|
108
|
+
print(f"Reading time: {read_str} ({WPM} wpm)")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# === Auto-registration metadata ===
|
|
112
|
+
TOOL_META = {
|
|
113
|
+
"name": "text-stats",
|
|
114
|
+
"func": "main",
|
|
115
|
+
"desc": "Text statistics (words, chars, sentences, reading time)",
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if __name__ == "__main__":
|
|
119
|
+
main()
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""unit-convert — Convert between units of length, weight, temperature, and volume."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# --- Conversion tables ---
|
|
9
|
+
# All convert TO a common base unit, then FROM that base to the target.
|
|
10
|
+
|
|
11
|
+
# Length base: meters
|
|
12
|
+
_LENGTH = {
|
|
13
|
+
"m": 1.0,
|
|
14
|
+
"km": 1000.0,
|
|
15
|
+
"mi": 1609.344,
|
|
16
|
+
"ft": 0.3048,
|
|
17
|
+
"in": 0.0254,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Weight base: grams
|
|
21
|
+
_WEIGHT = {
|
|
22
|
+
"kg": 1000.0,
|
|
23
|
+
"g": 1.0,
|
|
24
|
+
"lb": 453.59237,
|
|
25
|
+
"oz": 28.349523125,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Volume base: liters
|
|
29
|
+
_VOLUME = {
|
|
30
|
+
"L": 1.0,
|
|
31
|
+
"ml": 0.001,
|
|
32
|
+
"gal": 3.785411784,
|
|
33
|
+
"cup": 0.2365882365,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Temperature: special handling via formulas
|
|
37
|
+
_CATEGORIES = {
|
|
38
|
+
"length": "length",
|
|
39
|
+
"weight": "weight",
|
|
40
|
+
"temp": "temp",
|
|
41
|
+
"volume": "volume",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
_UNIT_MAP = {}
|
|
45
|
+
for cat, tbl in [("length", _LENGTH), ("weight", _WEIGHT), ("volume", _VOLUME)]:
|
|
46
|
+
for u in tbl:
|
|
47
|
+
_UNIT_MAP[u] = cat
|
|
48
|
+
_UNIT_MAP.update({"C": "temp", "F": "temp", "K": "temp"})
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _convert_length(value, from_unit, to_unit):
|
|
52
|
+
base = value * _LENGTH[from_unit]
|
|
53
|
+
return base / _LENGTH[to_unit]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _convert_weight(value, from_unit, to_unit):
|
|
57
|
+
base = value * _WEIGHT[from_unit]
|
|
58
|
+
return base / _WEIGHT[to_unit]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _convert_volume(value, from_unit, to_unit):
|
|
62
|
+
base = value * _VOLUME[from_unit]
|
|
63
|
+
return base / _VOLUME[to_unit]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _convert_temp(value, from_unit, to_unit):
|
|
67
|
+
# Convert to Kelvin first
|
|
68
|
+
if from_unit == "C":
|
|
69
|
+
kelvin = value + 273.15
|
|
70
|
+
elif from_unit == "F":
|
|
71
|
+
kelvin = (value - 32) * 5 / 9 + 273.15
|
|
72
|
+
elif from_unit == "K":
|
|
73
|
+
kelvin = value
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError(f"Unknown temperature unit: {from_unit}")
|
|
76
|
+
# Convert from Kelvin to target
|
|
77
|
+
if to_unit == "C":
|
|
78
|
+
return kelvin - 273.15
|
|
79
|
+
elif to_unit == "F":
|
|
80
|
+
return (kelvin - 273.15) * 9 / 5 + 32
|
|
81
|
+
elif to_unit == "K":
|
|
82
|
+
return kelvin
|
|
83
|
+
else:
|
|
84
|
+
raise ValueError(f"Unknown temperature unit: {to_unit}")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
_CONVERTERS = {
|
|
88
|
+
"length": _convert_length,
|
|
89
|
+
"weight": _convert_weight,
|
|
90
|
+
"temp": _convert_temp,
|
|
91
|
+
"volume": _convert_volume,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def main():
|
|
96
|
+
parser = argparse.ArgumentParser(
|
|
97
|
+
description="Convert between units of length, weight, temperature, and volume."
|
|
98
|
+
)
|
|
99
|
+
parser.add_argument("value", type=float, help="Numeric value to convert")
|
|
100
|
+
parser.add_argument("from_unit", help="Source unit (e.g. m, kg, C, L)")
|
|
101
|
+
parser.add_argument("to_unit", help="Target unit (e.g. ft, lb, F, gal)")
|
|
102
|
+
parser.add_argument(
|
|
103
|
+
"-p", "--precision", type=int, default=6,
|
|
104
|
+
help="Number of decimal places (default: 6)"
|
|
105
|
+
)
|
|
106
|
+
args = parser.parse_args()
|
|
107
|
+
|
|
108
|
+
f, t = args.from_unit, args.to_unit
|
|
109
|
+
if f not in _UNIT_MAP:
|
|
110
|
+
print(f"Unknown unit: {f}", file=sys.stderr)
|
|
111
|
+
sys.exit(1)
|
|
112
|
+
if t not in _UNIT_MAP:
|
|
113
|
+
print(f"Unknown unit: {t}", file=sys.stderr)
|
|
114
|
+
sys.exit(1)
|
|
115
|
+
|
|
116
|
+
cat_f = _UNIT_MAP[f]
|
|
117
|
+
cat_t = _UNIT_MAP[t]
|
|
118
|
+
if cat_f != cat_t:
|
|
119
|
+
print(
|
|
120
|
+
f"Cannot convert {f} ({cat_f}) to {t} ({cat_t}) — incompatible categories",
|
|
121
|
+
file=sys.stderr,
|
|
122
|
+
)
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
|
|
125
|
+
result = _CONVERTERS[cat_f](args.value, f, t)
|
|
126
|
+
print(f"{result:.{args.precision}f}")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# === Auto-registration metadata ===
|
|
130
|
+
TOOL_META = {
|
|
131
|
+
"name": "unit-convert",
|
|
132
|
+
"func": "main",
|
|
133
|
+
"desc": "Unit converter (length, weight, temp, volume)",
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
main()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evolver-tools
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 3.1.0
|
|
4
|
+
Summary: 105 essential CLI tools - one pip install
|
|
5
5
|
Author: EVOLVER
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://evolver-dev.github.io/evolver-tools
|
|
@@ -24,8 +24,10 @@ src/evolver_tools/vendor/checksum_dir.py
|
|
|
24
24
|
src/evolver_tools/vendor/colorize.py
|
|
25
25
|
src/evolver_tools/vendor/config_validator.py
|
|
26
26
|
src/evolver_tools/vendor/crontab_helper.py
|
|
27
|
+
src/evolver_tools/vendor/crypto_price.py
|
|
27
28
|
src/evolver_tools/vendor/db_schema.py
|
|
28
29
|
src/evolver_tools/vendor/dep_graph.py
|
|
30
|
+
src/evolver_tools/vendor/dice_roll.py
|
|
29
31
|
src/evolver_tools/vendor/diff_csv.py
|
|
30
32
|
src/evolver_tools/vendor/dt_convert.py
|
|
31
33
|
src/evolver_tools/vendor/env_manager.py
|
|
@@ -38,6 +40,7 @@ src/evolver_tools/vendor/git_branch_cleaner.py
|
|
|
38
40
|
src/evolver_tools/vendor/html2markdown.py
|
|
39
41
|
src/evolver_tools/vendor/html2md.py
|
|
40
42
|
src/evolver_tools/vendor/image_meta.py
|
|
43
|
+
src/evolver_tools/vendor/ip_location.py
|
|
41
44
|
src/evolver_tools/vendor/join.py
|
|
42
45
|
src/evolver_tools/vendor/joke.py
|
|
43
46
|
src/evolver_tools/vendor/log_analyzer.py
|
|
@@ -62,8 +65,10 @@ src/evolver_tools/vendor/sql2csv.py
|
|
|
62
65
|
src/evolver_tools/vendor/ssh_key_gen.py
|
|
63
66
|
src/evolver_tools/vendor/ssl_check.py
|
|
64
67
|
src/evolver_tools/vendor/stopwatch.py
|
|
68
|
+
src/evolver_tools/vendor/text_stats.py
|
|
65
69
|
src/evolver_tools/vendor/timer_pro.py
|
|
66
70
|
src/evolver_tools/vendor/todo_cli.py
|
|
71
|
+
src/evolver_tools/vendor/unit_convert.py
|
|
67
72
|
src/evolver_tools/vendor/weather_cli.py
|
|
68
73
|
src/evolver_tools/vendor/xml2json.py
|
|
69
74
|
src/evolver_tools/vendor/b64/__init__.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|