Open-AutoTools 0.0.5__tar.gz → 0.0.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. {open_autotools-0.0.5 → open_autotools-0.0.7/Open_AutoTools.egg-info}/PKG-INFO +13 -2
  2. {open_autotools-0.0.5 → open_autotools-0.0.7}/Open_AutoTools.egg-info/SOURCES.txt +4 -0
  3. {open_autotools-0.0.5 → open_autotools-0.0.7}/Open_AutoTools.egg-info/requires.txt +1 -0
  4. {open_autotools-0.0.5/Open_AutoTools.egg-info → open_autotools-0.0.7}/PKG-INFO +13 -2
  5. {open_autotools-0.0.5 → open_autotools-0.0.7}/README.md +11 -1
  6. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autocaps/core.py +1 -1
  7. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autocolor/core.py +1 -1
  8. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autoconvert/commands.py +1 -1
  9. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autoconvert/conversion/convert_image.py +10 -0
  10. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autoconvert/conversion/convert_video.py +3 -2
  11. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autoconvert/core.py +1 -1
  12. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autoip/core.py +8 -5
  13. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autolower/core.py +1 -1
  14. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autounit/core.py +1 -1
  15. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autozip/commands.py +1 -1
  16. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autozip/core.py +3 -3
  17. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/cli.py +84 -33
  18. open_autotools-0.0.7/autotools/tool_registry.py +19 -0
  19. open_autotools-0.0.7/autotools/utils/__init__.py +14 -0
  20. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/utils/commands.py +59 -11
  21. open_autotools-0.0.7/autotools/utils/loading.py +41 -0
  22. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/utils/performance.py +1 -1
  23. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/utils/requirements.py +1 -1
  24. open_autotools-0.0.7/autotools/utils/updates.py +116 -0
  25. {open_autotools-0.0.5 → open_autotools-0.0.7}/requirements.txt +1 -0
  26. {open_autotools-0.0.5 → open_autotools-0.0.7}/setup.py +14 -20
  27. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autocaps/test_autocaps_core.py +8 -0
  28. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autocolor/test_autocolor_core.py +7 -0
  29. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoconvert/unit/test_convert_core.py +2 -0
  30. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoconvert/unit/test_convert_image.py +49 -0
  31. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autolower/test_autolower_core.py +8 -0
  32. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autounit/test_autounit_core.py +8 -0
  33. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/utils/test_commands_discovery.py +36 -0
  34. open_autotools-0.0.7/tests/autotools/utils/test_loading.py +99 -0
  35. open_autotools-0.0.7/tests/autotools/utils/test_updates.py +287 -0
  36. open_autotools-0.0.7/tests/autotools/utils/test_utils_init.py +20 -0
  37. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/utils/test_version.py +9 -0
  38. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/test_cli.py +129 -21
  39. open_autotools-0.0.7/tests/test_docker_benchmark_runner.py +222 -0
  40. open_autotools-0.0.5/autotools/utils/__init__.py +0 -5
  41. open_autotools-0.0.5/autotools/utils/loading.py +0 -24
  42. open_autotools-0.0.5/autotools/utils/updates.py +0 -38
  43. open_autotools-0.0.5/tests/autotools/utils/test_updates.py +0 -95
  44. {open_autotools-0.0.5 → open_autotools-0.0.7}/LICENSE +0 -0
  45. {open_autotools-0.0.5 → open_autotools-0.0.7}/MANIFEST.in +0 -0
  46. {open_autotools-0.0.5 → open_autotools-0.0.7}/Open_AutoTools.egg-info/dependency_links.txt +0 -0
  47. {open_autotools-0.0.5 → open_autotools-0.0.7}/Open_AutoTools.egg-info/entry_points.txt +0 -0
  48. {open_autotools-0.0.5 → open_autotools-0.0.7}/Open_AutoTools.egg-info/top_level.txt +0 -0
  49. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/__init__.py +0 -0
  50. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autocaps/__init__.py +0 -0
  51. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autocaps/commands.py +0 -0
  52. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autocolor/__init__.py +0 -0
  53. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autocolor/commands.py +0 -0
  54. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autoconvert/__init__.py +0 -0
  55. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autoconvert/conversion/__init__.py +0 -0
  56. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autoconvert/conversion/convert_audio.py +0 -0
  57. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autoconvert/conversion/convert_text.py +0 -0
  58. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autoip/__init__.py +0 -0
  59. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autoip/commands.py +0 -0
  60. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autolower/__init__.py +0 -0
  61. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autolower/commands.py +0 -0
  62. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autonote/__init__.py +0 -0
  63. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autonote/commands.py +0 -0
  64. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autonote/core.py +0 -0
  65. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autopassword/__init__.py +0 -0
  66. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autopassword/commands.py +0 -0
  67. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autopassword/core.py +0 -0
  68. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autotest/__init__.py +0 -0
  69. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autotest/commands.py +0 -0
  70. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autotodo/__init__.py +0 -0
  71. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autotodo/commands.py +0 -0
  72. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autotodo/core.py +0 -0
  73. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autounit/__init__.py +0 -0
  74. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autounit/commands.py +0 -0
  75. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/autozip/__init__.py +0 -0
  76. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/utils/smoke.py +0 -0
  77. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/utils/text.py +0 -0
  78. {open_autotools-0.0.5 → open_autotools-0.0.7}/autotools/utils/version.py +0 -0
  79. {open_autotools-0.0.5 → open_autotools-0.0.7}/pyproject.toml +0 -0
  80. {open_autotools-0.0.5 → open_autotools-0.0.7}/setup.cfg +0 -0
  81. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/__init__.py +0 -0
  82. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autocaps/__init__.py +0 -0
  83. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autocaps/test_autocaps_integration.py +0 -0
  84. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autocolor/__init__.py +0 -0
  85. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autocolor/test_autocolor_integration.py +0 -0
  86. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoconvert/__init__.py +0 -0
  87. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoconvert/conftest.py +0 -0
  88. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoconvert/integration/__init__.py +0 -0
  89. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoconvert/integration/test_convert_commands.py +0 -0
  90. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoconvert/unit/__init__.py +0 -0
  91. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoconvert/unit/test_convert_audio.py +0 -0
  92. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoconvert/unit/test_convert_text.py +0 -0
  93. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoconvert/unit/test_convert_video.py +0 -0
  94. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoip/__init__.py +0 -0
  95. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoip/test_autoip_core.py +0 -0
  96. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autoip/test_autoip_integration.py +0 -0
  97. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autolower/__init__.py +0 -0
  98. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autolower/test_autolower_integration.py +0 -0
  99. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autonote/__init__.py +0 -0
  100. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autonote/conftest.py +0 -0
  101. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autonote/test_autonote_core.py +0 -0
  102. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autonote/test_autonote_integration.py +0 -0
  103. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autopassword/test_autopassword_core.py +0 -0
  104. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autopassword/test_autopassword_integration.py +0 -0
  105. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autotest/__init__.py +0 -0
  106. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autotest/test_autotest_core.py +0 -0
  107. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autotodo/__init__.py +0 -0
  108. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autotodo/conftest.py +0 -0
  109. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autotodo/test_autotodo_coverage.py +0 -0
  110. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autotodo/test_autotodo_file_operations.py +0 -0
  111. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autotodo/test_autotodo_integration.py +0 -0
  112. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autotodo/test_autotodo_operations.py +0 -0
  113. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autotodo/test_autotodo_sections.py +0 -0
  114. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autotodo/test_autotodo_tasks.py +0 -0
  115. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autotodo/test_autotodo_utils.py +0 -0
  116. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autounit/__init__.py +0 -0
  117. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autounit/test_autounit_integration.py +0 -0
  118. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autozip/__init__.py +0 -0
  119. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autozip/test_autozip_core.py +0 -0
  120. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/autozip/test_autozip_integration.py +0 -0
  121. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/utils/__init__.py +0 -0
  122. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/utils/test_performance.py +0 -0
  123. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/utils/test_requirements.py +0 -0
  124. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/utils/test_smoke.py +0 -0
  125. {open_autotools-0.0.5 → open_autotools-0.0.7}/tests/autotools/utils/test_text.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Open-AutoTools
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: A suite of automated tools accessible via CLI with a simple `autotools` command
5
5
  Home-page: https://github.com/BabylooPro/Open-AutoTools
6
6
  Author: BabylooPro
@@ -26,6 +26,7 @@ Requires-Dist: speedtest-cli>=2.1.3
26
26
  Requires-Dist: psutil>=5.9.0
27
27
  Requires-Dist: cryptography>=42.0.2
28
28
  Requires-Dist: Pillow>=10.0.0
29
+ Requires-Dist: pillow-heif>=0.16.0
29
30
  Requires-Dist: pydub>=0.25.1
30
31
  Requires-Dist: moviepy>=1.0.3
31
32
  Requires-Dist: pint>=0.23
@@ -63,14 +64,24 @@ Dynamic: summary
63
64
 
64
65
  [PYPI_BADGE]: https://badge.fury.io/py/Open-AutoTools.svg
65
66
  [PYPI_URL]: https://pypi.org/project/Open-AutoTools/
67
+
68
+ [PYPI_DOWNLOADS_BADGE]: https://img.shields.io/pypi/dm/Open-AutoTools
69
+ [PYPI_DOWNLOADS_URL]: https://pypi.org/project/Open-AutoTools/
70
+
66
71
  [PYTHON_BADGE]: https://img.shields.io/badge/Python-3.10+-blue.svg
67
72
  [PYTHON_URL]: https://www.python.org/downloads/
73
+
68
74
  [CHANGELOG_BADGE]: https://img.shields.io/badge/CHANGELOG-red.svg
69
75
  [CHANGELOG_URL]: CHANGELOG.md
76
+
70
77
  [TODO_BADGE]: https://img.shields.io/badge/TODO-purple.svg
71
78
  [TODO_URL]: TODO.md
72
79
 
73
- [![PyPI][PYPI_BADGE]][PYPI_URL] [![Python][PYTHON_BADGE]][PYTHON_URL] [![CHANGELOG][CHANGELOG_BADGE]][CHANGELOG_URL] [![TODO][TODO_BADGE]][TODO_URL]
80
+ [![PyPI][PYPI_BADGE]][PYPI_URL]
81
+ [![Python][PYTHON_BADGE]][PYTHON_URL]
82
+ [![Downloads][PYPI_DOWNLOADS_BADGE]][PYPI_DOWNLOADS_URL]
83
+ [![CHANGELOG][CHANGELOG_BADGE]][CHANGELOG_URL]
84
+ [![TODO][TODO_BADGE]][TODO_URL]
74
85
 
75
86
  Python CLI toolkit for everyday developer tasks. Boost productivity directly from your terminal.
76
87
 
@@ -12,6 +12,7 @@ Open_AutoTools.egg-info/requires.txt
12
12
  Open_AutoTools.egg-info/top_level.txt
13
13
  autotools/__init__.py
14
14
  autotools/cli.py
15
+ autotools/tool_registry.py
15
16
  autotools/autocaps/__init__.py
16
17
  autotools/autocaps/commands.py
17
18
  autotools/autocaps/core.py
@@ -60,6 +61,7 @@ autotools/utils/updates.py
60
61
  autotools/utils/version.py
61
62
  tests/__init__.py
62
63
  tests/test_cli.py
64
+ tests/test_docker_benchmark_runner.py
63
65
  tests/autotools/autocaps/__init__.py
64
66
  tests/autotools/autocaps/test_autocaps_core.py
65
67
  tests/autotools/autocaps/test_autocaps_integration.py
@@ -107,9 +109,11 @@ tests/autotools/autozip/test_autozip_core.py
107
109
  tests/autotools/autozip/test_autozip_integration.py
108
110
  tests/autotools/utils/__init__.py
109
111
  tests/autotools/utils/test_commands_discovery.py
112
+ tests/autotools/utils/test_loading.py
110
113
  tests/autotools/utils/test_performance.py
111
114
  tests/autotools/utils/test_requirements.py
112
115
  tests/autotools/utils/test_smoke.py
113
116
  tests/autotools/utils/test_text.py
114
117
  tests/autotools/utils/test_updates.py
118
+ tests/autotools/utils/test_utils_init.py
115
119
  tests/autotools/utils/test_version.py
@@ -8,6 +8,7 @@ speedtest-cli>=2.1.3
8
8
  psutil>=5.9.0
9
9
  cryptography>=42.0.2
10
10
  Pillow>=10.0.0
11
+ pillow-heif>=0.16.0
11
12
  pydub>=0.25.1
12
13
  moviepy>=1.0.3
13
14
  pint>=0.23
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Open-AutoTools
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: A suite of automated tools accessible via CLI with a simple `autotools` command
5
5
  Home-page: https://github.com/BabylooPro/Open-AutoTools
6
6
  Author: BabylooPro
@@ -26,6 +26,7 @@ Requires-Dist: speedtest-cli>=2.1.3
26
26
  Requires-Dist: psutil>=5.9.0
27
27
  Requires-Dist: cryptography>=42.0.2
28
28
  Requires-Dist: Pillow>=10.0.0
29
+ Requires-Dist: pillow-heif>=0.16.0
29
30
  Requires-Dist: pydub>=0.25.1
30
31
  Requires-Dist: moviepy>=1.0.3
31
32
  Requires-Dist: pint>=0.23
@@ -63,14 +64,24 @@ Dynamic: summary
63
64
 
64
65
  [PYPI_BADGE]: https://badge.fury.io/py/Open-AutoTools.svg
65
66
  [PYPI_URL]: https://pypi.org/project/Open-AutoTools/
67
+
68
+ [PYPI_DOWNLOADS_BADGE]: https://img.shields.io/pypi/dm/Open-AutoTools
69
+ [PYPI_DOWNLOADS_URL]: https://pypi.org/project/Open-AutoTools/
70
+
66
71
  [PYTHON_BADGE]: https://img.shields.io/badge/Python-3.10+-blue.svg
67
72
  [PYTHON_URL]: https://www.python.org/downloads/
73
+
68
74
  [CHANGELOG_BADGE]: https://img.shields.io/badge/CHANGELOG-red.svg
69
75
  [CHANGELOG_URL]: CHANGELOG.md
76
+
70
77
  [TODO_BADGE]: https://img.shields.io/badge/TODO-purple.svg
71
78
  [TODO_URL]: TODO.md
72
79
 
73
- [![PyPI][PYPI_BADGE]][PYPI_URL] [![Python][PYTHON_BADGE]][PYTHON_URL] [![CHANGELOG][CHANGELOG_BADGE]][CHANGELOG_URL] [![TODO][TODO_BADGE]][TODO_URL]
80
+ [![PyPI][PYPI_BADGE]][PYPI_URL]
81
+ [![Python][PYTHON_BADGE]][PYTHON_URL]
82
+ [![Downloads][PYPI_DOWNLOADS_BADGE]][PYPI_DOWNLOADS_URL]
83
+ [![CHANGELOG][CHANGELOG_BADGE]][CHANGELOG_URL]
84
+ [![TODO][TODO_BADGE]][TODO_URL]
74
85
 
75
86
  Python CLI toolkit for everyday developer tasks. Boost productivity directly from your terminal.
76
87
 
@@ -2,14 +2,24 @@
2
2
 
3
3
  [PYPI_BADGE]: https://badge.fury.io/py/Open-AutoTools.svg
4
4
  [PYPI_URL]: https://pypi.org/project/Open-AutoTools/
5
+
6
+ [PYPI_DOWNLOADS_BADGE]: https://img.shields.io/pypi/dm/Open-AutoTools
7
+ [PYPI_DOWNLOADS_URL]: https://pypi.org/project/Open-AutoTools/
8
+
5
9
  [PYTHON_BADGE]: https://img.shields.io/badge/Python-3.10+-blue.svg
6
10
  [PYTHON_URL]: https://www.python.org/downloads/
11
+
7
12
  [CHANGELOG_BADGE]: https://img.shields.io/badge/CHANGELOG-red.svg
8
13
  [CHANGELOG_URL]: CHANGELOG.md
14
+
9
15
  [TODO_BADGE]: https://img.shields.io/badge/TODO-purple.svg
10
16
  [TODO_URL]: TODO.md
11
17
 
12
- [![PyPI][PYPI_BADGE]][PYPI_URL] [![Python][PYTHON_BADGE]][PYTHON_URL] [![CHANGELOG][CHANGELOG_BADGE]][CHANGELOG_URL] [![TODO][TODO_BADGE]][TODO_URL]
18
+ [![PyPI][PYPI_BADGE]][PYPI_URL]
19
+ [![Python][PYTHON_BADGE]][PYTHON_URL]
20
+ [![Downloads][PYPI_DOWNLOADS_BADGE]][PYPI_DOWNLOADS_URL]
21
+ [![CHANGELOG][CHANGELOG_BADGE]][CHANGELOG_URL]
22
+ [![TODO][TODO_BADGE]][TODO_URL]
13
23
 
14
24
  Python CLI toolkit for everyday developer tasks. Boost productivity directly from your terminal.
15
25
 
@@ -4,5 +4,5 @@ import pyperclip
4
4
  def autocaps_transform(text):
5
5
  transformed_text = text.upper()
6
6
  try: pyperclip.copy(transformed_text)
7
- except pyperclip.PyperclipException: pass
7
+ except (pyperclip.PyperclipException, OSError): pass
8
8
  return transformed_text
@@ -94,6 +94,6 @@ def autocolor_convert(color_input, output_format='hex'):
94
94
  result = _format_output(r, g, b, alpha, output_format)
95
95
 
96
96
  try: pyperclip.copy(result)
97
- except pyperclip.PyperclipException: pass
97
+ except (pyperclip.PyperclipException, OSError): pass
98
98
 
99
99
  return result
@@ -10,7 +10,7 @@ TOOL_CATEGORY = 'Files'
10
10
 
11
11
  # SMOKE TEST CASES (USED BY 'autotools smoke')
12
12
  SMOKE_TESTS = [
13
- {'name': 'md-json', 'args': ['README.md', '/tmp/autoconvert-smoke.json']},
13
+ {'name': 'md-json', 'args': ['README.md', '.autotools-smoke/autoconvert-smoke.json']},
14
14
  ]
15
15
 
16
16
  # CLI COMMAND TO CONVERT FILES BETWEEN DIFFERENT FORMATS
@@ -6,11 +6,21 @@ from typing import Optional
6
6
  def convert_image(input_path: str, output_path: str, output_format: Optional[str] = None) -> bool:
7
7
  try:
8
8
  from PIL import Image
9
+ input_ext = Path(input_path).suffix[1:].lower()
10
+ if input_ext in ('heic', 'heif'):
11
+ try:
12
+ from pillow_heif import register_heif_opener
13
+ register_heif_opener()
14
+ except ImportError:
15
+ pass
9
16
 
10
17
  if not os.path.exists(input_path):
11
18
  raise FileNotFoundError(f"INPUT FILE NOT FOUND: {input_path}")
12
19
  if output_format is None: output_format = Path(output_path).suffix[1:].upper()
13
20
 
21
+ format_aliases = {'JPG': 'JPEG', 'TIF': 'TIFF'}
22
+ output_format = format_aliases.get(output_format.upper(), output_format.upper())
23
+
14
24
  with Image.open(input_path) as img:
15
25
  if output_format in ['JPG', 'JPEG'] and img.mode in ('RGBA', 'LA', 'P'):
16
26
  rgb_img = Image.new('RGB', img.size, (255, 255, 255))
@@ -1,17 +1,18 @@
1
1
  import os
2
+ import importlib
2
3
  from pathlib import Path
3
4
  from typing import Optional
4
5
 
5
6
  # CONVERTS VIDEO BETWEEN FORMATS
6
7
  def convert_video(input_path: str, output_path: str, output_format: Optional[str] = None) -> bool:
7
8
  try:
8
- from moviepy.editor import VideoFileClip
9
+ video_file_clip = importlib.import_module("moviepy.editor").VideoFileClip
9
10
 
10
11
  if not os.path.exists(input_path):
11
12
  raise FileNotFoundError(f"INPUT FILE NOT FOUND: {input_path}")
12
13
  if output_format is None: output_format = Path(output_path).suffix[1:].lower()
13
14
 
14
- with VideoFileClip(input_path) as video: video.write_videofile(output_path, codec='libx264', audio_codec='aac')
15
+ with video_file_clip(input_path) as video: video.write_videofile(output_path, codec='libx264', audio_codec='aac')
15
16
 
16
17
  return True
17
18
 
@@ -15,7 +15,7 @@ def detect_file_type(file_path: str) -> str:
15
15
  if ext in text_formats: return 'text'
16
16
 
17
17
  # IMAGE FORMATS
18
- image_formats = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff', 'tif', 'ico', 'svg']
18
+ image_formats = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff', 'tif', 'ico', 'svg', 'heic', 'heif']
19
19
  if ext in image_formats: return 'image'
20
20
 
21
21
  # AUDIO FORMATS
@@ -7,6 +7,9 @@ import speedtest
7
7
  import psutil
8
8
  from ..utils.text import is_ci_environment, mask_ipv4, mask_ipv6, mask_sensitive_info
9
9
 
10
+ GOOGLE_DNS_IPV4 = str(ipaddress.IPv4Address(0x08080808))
11
+ CLOUDFLARE_DNS_IPV4 = str(ipaddress.IPv4Address(0x01010101))
12
+
10
13
  # NORMALIZES psutil.net_if_addrs() OUTPUT TO A netifaces-LIKE STRUCTURE:
11
14
  def _psutil_addrs_to_family_map(addrs):
12
15
  family_map = {}
@@ -61,14 +64,14 @@ def get_public_ips():
61
64
  try:
62
65
  ips['ipv4'] = requests.get(service, timeout=2).text.strip()
63
66
  if ips['ipv4']: break
64
- except (requests.RequestException, requests.Timeout, requests.ConnectionError):
67
+ except requests.RequestException:
65
68
  continue
66
69
 
67
70
  for service in ipv6_services:
68
71
  try:
69
72
  ips['ipv6'] = requests.get(service, timeout=2).text.strip()
70
73
  if ips['ipv6']: break
71
- except (requests.RequestException, requests.Timeout, requests.ConnectionError):
74
+ except requests.RequestException:
72
75
  continue
73
76
 
74
77
  return ips
@@ -77,8 +80,8 @@ def get_public_ips():
77
80
  def test_connectivity():
78
81
  results = []
79
82
  test_hosts = {
80
- 'Google DNS': ('8.8.8.8', 53),
81
- 'CloudFlare DNS': ('1.1.1.1', 53),
83
+ 'Google DNS': (GOOGLE_DNS_IPV4, 53),
84
+ 'CloudFlare DNS': (CLOUDFLARE_DNS_IPV4, 53),
82
85
  'Google': ('google.com', 443),
83
86
  'Cloudflare': ('cloudflare.com', 443),
84
87
  'GitHub': ('github.com', 443),
@@ -150,7 +153,7 @@ def get_local_ip():
150
153
  # FALLBACK: UDP SOCKET TRICK
151
154
  try:
152
155
  s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
153
- s.connect(("8.8.8.8", 80))
156
+ s.connect((GOOGLE_DNS_IPV4, 80))
154
157
  ip = s.getsockname()[0]
155
158
  s.close()
156
159
  return ip
@@ -4,5 +4,5 @@ import pyperclip
4
4
  def autolower_transform(text):
5
5
  transformed_text = text.lower()
6
6
  try: pyperclip.copy(transformed_text)
7
- except pyperclip.PyperclipException: pass
7
+ except (pyperclip.PyperclipException, OSError): pass
8
8
  return transformed_text
@@ -23,7 +23,7 @@ def autounit_convert(value, from_unit, to_unit):
23
23
  output = f"{result_str} {to_unit}"
24
24
 
25
25
  try: pyperclip.copy(output)
26
- except pyperclip.PyperclipException: pass
26
+ except (pyperclip.PyperclipException, OSError): pass
27
27
 
28
28
  return output
29
29
 
@@ -9,7 +9,7 @@ TOOL_CATEGORY = 'Files'
9
9
 
10
10
  # SMOKE TEST CASES (USED BY 'autotools smoke')
11
11
  SMOKE_TESTS = [
12
- {'name': 'zip-readme', 'args': ['README.md', '--output', '/tmp/autozip-smoke.zip']},
12
+ {'name': 'zip-readme', 'args': ['README.md', '--output', '.autotools-smoke/autozip-smoke.zip']},
13
13
  ]
14
14
 
15
15
  # CLI COMMAND TO COMPRESS FILES AND DIRECTORIES
@@ -69,9 +69,9 @@ def _compress_tar_xz(source_paths, output_path, compression_level=6):
69
69
  # DETERMINES OUTPUT FORMAT FROM FILE EXTENSION
70
70
  def _get_format_from_extension(output_path):
71
71
  path_str = str(output_path).lower()
72
- if path_str.endswith('.tar.gz') or path_str.endswith('.tgz'): return 'tar.gz'
73
- elif path_str.endswith('.tar.bz2') or path_str.endswith('.tbz2'): return 'tar.bz2'
74
- elif path_str.endswith('.tar.xz') or path_str.endswith('.txz'): return 'tar.xz'
72
+ if path_str.endswith(('.tar.gz', '.tgz')): return 'tar.gz'
73
+ elif path_str.endswith(('.tar.bz2', '.tbz2')): return 'tar.bz2'
74
+ elif path_str.endswith(('.tar.xz', '.txz')): return 'tar.xz'
75
75
  elif path_str.endswith('.tar'): return 'tar'
76
76
  elif path_str.endswith('.zip'): return 'zip'
77
77
  else:
@@ -1,26 +1,89 @@
1
1
  import click
2
- import requests
3
- import base64
4
- import argparse
5
2
  import json as json_module
6
- import sys
7
3
  import os
8
4
 
9
- from dotenv import load_dotenv
10
- from datetime import datetime
11
- from urllib.parse import urlparse
12
- from packaging.version import parse as parse_version
13
- from importlib.metadata import version as get_version, PackageNotFoundError
14
-
15
- from .utils.version import print_version
16
- from .utils.updates import check_for_updates
17
- from .utils.commands import get_wrapped_tool_commands
18
- from .utils.performance import init_metrics, finalize_metrics, get_metrics, should_enable_metrics
19
-
20
- load_dotenv()
5
+ from .utils.commands import get_tool_names, get_wrapped_tool_command, get_wrapped_tool_commands
6
+ from .tool_registry import LAZY_TOOL_NAMES
7
+
8
+ def _public_tool_name(tool_name):
9
+ return 'test' if tool_name == 'autotest' else tool_name
10
+
11
+ def _internal_tool_name(public_name):
12
+ return 'autotest' if public_name == 'test' else public_name
13
+
14
+ def _load_dotenv_if_present():
15
+ if not os.path.exists('.env'): return
16
+ try:
17
+ from dotenv import load_dotenv
18
+ load_dotenv()
19
+ except ImportError:
20
+ pass
21
+
22
+ def print_version(ctx, value):
23
+ if not value or ctx.resilient_parsing: return
24
+ _load_dotenv_if_present()
25
+ from .utils.version import print_version as _print_version
26
+ return _print_version(ctx, value)
27
+
28
+ def check_for_updates():
29
+ from .utils.updates import check_for_updates as _check_for_updates
30
+ return _check_for_updates()
31
+
32
+ def _ctx_has_perf_flag(ctx):
33
+ current = ctx
34
+ while current:
35
+ if current.params.get('perf', False): return True
36
+ current = getattr(current, 'parent', None)
37
+ return False
38
+
39
+ def should_enable_metrics(ctx):
40
+ if not _ctx_has_perf_flag(ctx): return False
41
+ from .utils.performance import should_enable_metrics as _should_enable_metrics
42
+ return _should_enable_metrics(ctx)
43
+
44
+ def init_metrics():
45
+ from .utils.performance import init_metrics as _init_metrics
46
+ return _init_metrics()
47
+
48
+ def finalize_metrics(ctx):
49
+ from .utils.performance import finalize_metrics as _finalize_metrics
50
+ return _finalize_metrics(ctx)
51
+
52
+ def get_metrics():
53
+ from .utils.performance import get_metrics as _get_metrics
54
+ return _get_metrics()
55
+
56
+ # CLICK COMMAND PROXY USED BY console_scripts AND TESTS WITHOUT IMPORTING EVERY TOOL
57
+ class LazyToolCommand(click.Command):
58
+ def __init__(self, tool_name):
59
+ self.tool_name = tool_name
60
+ super().__init__(name=_public_tool_name(tool_name))
61
+
62
+ def _load_command(self):
63
+ cmd = get_wrapped_tool_command(self.tool_name)
64
+ if cmd is None:
65
+ raise click.ClickException(f"Unknown tool: {self.tool_name}")
66
+ return cmd
67
+
68
+ def main(self, *args, **kwargs):
69
+ return self._load_command().main(*args, **kwargs)
70
+
71
+ # LAZY GROUP: LISTS TOOLS WITHOUT IMPORTING THEM, IMPORTS ONLY THE SELECTED COMMAND
72
+ class LazyAutoToolsGroup(click.Group):
73
+ def list_commands(self, ctx):
74
+ commands = set(super().list_commands(ctx))
75
+ commands.update(_public_tool_name(tool_name) for tool_name in get_tool_names())
76
+ return sorted(commands)
77
+
78
+ def get_command(self, ctx, cmd_name):
79
+ cmd = super().get_command(ctx, cmd_name)
80
+ if cmd is not None: return cmd
81
+
82
+ tool_name = _internal_tool_name(cmd_name)
83
+ return get_wrapped_tool_command(tool_name)
21
84
 
22
85
  # MAIN CLI ENTRY POINT - REGISTERS ALL COMMANDS AND HANDLES GLOBAL OPTIONS
23
- @click.group(invoke_without_command=True)
86
+ @click.group(cls=LazyAutoToolsGroup, invoke_without_command=True)
24
87
  @click.option('--version', '--v', is_flag=True, callback=lambda ctx, param, value: print_version(ctx, value),
25
88
  expose_value=False, is_eager=True, help='Show version and check for updates')
26
89
  @click.option('--help', '-h', is_flag=True, callback=lambda ctx, param, value:
@@ -50,6 +113,7 @@ def cli(ctx, perf):
50
113
  """
51
114
 
52
115
  # INITIALIZE METRICS IF NEEDED
116
+ _load_dotenv_if_present()
53
117
  if should_enable_metrics(ctx):
54
118
  init_metrics()
55
119
  get_metrics().step_start('startup')
@@ -63,17 +127,6 @@ def cli(ctx, perf):
63
127
  get_metrics().end_process()
64
128
  finalize_metrics(ctx)
65
129
 
66
- # DISCOVER AND REGISTER ALL TOOL COMMANDS
67
- _wrapped_tool_commands = get_wrapped_tool_commands()
68
- for _tool_name in sorted(_wrapped_tool_commands):
69
- _cmd = _wrapped_tool_commands[_tool_name]
70
-
71
- if _tool_name == 'autotest': cli.add_command(_cmd, name='test')
72
- else: cli.add_command(_cmd)
73
-
74
- # EXPOSE TOOL COMMANDS FOR console_scripts ENTRYPOINTS (setup.py)
75
- for _tool_name, _cmd in _wrapped_tool_commands.items(): globals()[_tool_name] = _cmd
76
-
77
130
  # DISPLAYS COMMAND OPTIONS
78
131
  def _display_command_options(cmd_obj):
79
132
  if not hasattr(cmd_obj, 'params'): return
@@ -122,11 +175,7 @@ def autotools():
122
175
  @cli.command(name='list-tools')
123
176
  @click.option('--json', 'as_json', is_flag=True, help='OUTPUT JSON')
124
177
  def list_tools(as_json):
125
- tools = []
126
- for tool_name, cmd in get_wrapped_tool_commands().items():
127
- public_name = 'test' if tool_name == 'autotest' else (cmd.name or tool_name)
128
- tools.append(public_name)
129
-
178
+ tools = [_public_tool_name(tool_name) for tool_name in get_tool_names()]
130
179
  tools = sorted(set(tools))
131
180
  if as_json: click.echo(json_module.dumps(tools))
132
181
  else: click.echo("\n".join(tools))
@@ -157,4 +206,6 @@ def smoke(workdir, timeout, include_tools, exclude_tools, as_json, verbose):
157
206
  failed = [r for r in results if r.get('status') != 'OK']
158
207
  raise SystemExit(1 if failed else 0)
159
208
 
209
+ globals().update({tool_name: LazyToolCommand(tool_name) for tool_name in LAZY_TOOL_NAMES})
210
+
160
211
  if __name__ == '__main__': cli()
@@ -0,0 +1,19 @@
1
+ CONSOLE_TOOL_NAMES = (
2
+ 'autocaps',
3
+ 'autolower',
4
+ 'autopassword',
5
+ 'autoip',
6
+ 'autoconvert',
7
+ 'autocolor',
8
+ 'autounit',
9
+ 'autozip',
10
+ 'autotodo',
11
+ 'autonote',
12
+ )
13
+
14
+ LAZY_TOOL_NAMES = CONSOLE_TOOL_NAMES + ('autotest',)
15
+
16
+ CONSOLE_SCRIPT_ENTRY_POINTS = (
17
+ 'autotools=autotools.cli:cli',
18
+ *(f'{tool_name}=autotools.cli:{tool_name}' for tool_name in CONSOLE_TOOL_NAMES),
19
+ )
@@ -0,0 +1,14 @@
1
+ __all__ = ['LoadingAnimation', 'check_for_updates', 'print_version']
2
+
3
+ def __getattr__(name):
4
+ if name == 'LoadingAnimation':
5
+ from .loading import LoadingAnimation
6
+ return LoadingAnimation
7
+ if name == 'check_for_updates':
8
+ from .updates import check_for_updates
9
+ return check_for_updates
10
+ if name == 'print_version':
11
+ from .version import print_version
12
+ return print_version
13
+
14
+ raise AttributeError(f"module 'autotools.utils' has no attribute {name!r}")
@@ -5,10 +5,11 @@ import pkgutil
5
5
  from types import ModuleType
6
6
  from typing import Dict, Iterable, List, Tuple
7
7
 
8
- from .performance import init_metrics, finalize_metrics, get_metrics, should_enable_metrics, track_step
9
-
10
8
  __all__ = [
11
9
  'discover_tool_command_entries',
10
+ 'get_tool_names',
11
+ 'get_tool_command_entry',
12
+ 'get_wrapped_tool_command',
12
13
  'get_wrapped_tool_commands',
13
14
  'register_commands',
14
15
  'get_tool_category'
@@ -17,6 +18,34 @@ __all__ = [
17
18
  # PACKAGES THAT SHOULD NEVER BE TREATED AS TOOLS
18
19
  _EXCLUDED_TOOL_PACKAGES = {'utils', '__pycache__'}
19
20
 
21
+ def _ctx_has_perf_flag(ctx) -> bool:
22
+ current = ctx
23
+ while current:
24
+ if current.params.get('perf', False): return True
25
+ current = getattr(current, 'parent', None)
26
+ return False
27
+
28
+ def init_metrics():
29
+ from .performance import init_metrics as _init_metrics
30
+ return _init_metrics()
31
+
32
+ def finalize_metrics(ctx):
33
+ from .performance import finalize_metrics as _finalize_metrics
34
+ return _finalize_metrics(ctx)
35
+
36
+ def get_metrics():
37
+ from .performance import get_metrics as _get_metrics
38
+ return _get_metrics()
39
+
40
+ def should_enable_metrics(ctx):
41
+ if not _ctx_has_perf_flag(ctx): return False
42
+ from .performance import should_enable_metrics as _should_enable_metrics
43
+ return _should_enable_metrics(ctx)
44
+
45
+ def track_step(name: str):
46
+ from .performance import track_step as _track_step
47
+ return _track_step(name)
48
+
20
49
  # ITERATES ALL TOP-LEVEL TOOL PACKAGES (autotools/<tool>/)
21
50
  def _iter_tool_packages() -> Iterable[str]:
22
51
  import autotools as autotools_pkg
@@ -28,6 +57,10 @@ def _iter_tool_packages() -> Iterable[str]:
28
57
  if name == 'cli': continue
29
58
  yield name
30
59
 
60
+ # RETURNS DISCOVERED TOP-LEVEL TOOL PACKAGE NAMES WITHOUT IMPORTING COMMAND MODULES
61
+ def get_tool_names() -> List[str]:
62
+ return sorted(_iter_tool_packages())
63
+
31
64
  # IMPORTS autotools.<tool>.commands IF IT EXISTS
32
65
  def _import_tool_commands_module(tool_name: str) -> ModuleType | None:
33
66
  full_name = f'autotools.{tool_name}.commands'
@@ -64,17 +97,32 @@ def _select_command_for_tool(cmds: List[click.Command], tool_name: str, mod_name
64
97
  # DISCOVERS TOOL COMMANDS AS (MODULE, CLICK COMMAND) BY TOOL PACKAGE NAME
65
98
  def discover_tool_command_entries() -> Dict[str, Tuple[ModuleType, click.Command]]:
66
99
  entries: Dict[str, Tuple[ModuleType, click.Command]] = {}
67
- for tool_name in _iter_tool_packages():
68
- mod = _import_tool_commands_module(tool_name)
69
- if mod is None: continue
70
- cmds = _extract_click_commands(mod)
71
- if not cmds: continue
72
-
73
- selected = _select_command_for_tool(cmds, tool_name, mod.__name__)
74
- entries[tool_name] = (mod, selected)
100
+ for tool_name in get_tool_names():
101
+ entry = get_tool_command_entry(tool_name)
102
+ if entry is not None: entries[tool_name] = entry
75
103
 
76
104
  return entries
77
105
 
106
+ # RETURNS A SINGLE TOOL COMMAND ENTRY WITHOUT IMPORTING EVERY OTHER TOOL
107
+ def get_tool_command_entry(tool_name: str) -> Tuple[ModuleType, click.Command] | None:
108
+ if tool_name not in set(get_tool_names()): return None
109
+
110
+ mod = _import_tool_commands_module(tool_name)
111
+ if mod is None: return None
112
+ cmds = _extract_click_commands(mod)
113
+ if not cmds: return None
114
+
115
+ selected = _select_command_for_tool(cmds, tool_name, mod.__name__)
116
+ return mod, selected
117
+
118
+ # RETURNS ONE WRAPPED TOOL COMMAND BY PACKAGE NAME
119
+ def get_wrapped_tool_command(tool_name: str) -> click.Command | None:
120
+ entry = get_tool_command_entry(tool_name)
121
+ if entry is None: return None
122
+
123
+ _mod, cmd = entry
124
+ return _wrap_command_with_metrics(cmd)
125
+
78
126
  # RETURNS WRAPPED TOOL COMMANDS (USED BY CLI GROUP AND CONSOLE_SCRIPTS EXPORTS)
79
127
  def get_wrapped_tool_commands() -> Dict[str, click.Command]:
80
128
  wrapped: Dict[str, click.Command] = {}
@@ -84,10 +132,10 @@ def get_wrapped_tool_commands() -> Dict[str, click.Command]:
84
132
 
85
133
  # EXECUTES COMMAND WITH PERFORMANCE TRACKING
86
134
  def _execute_with_metrics(ctx, original_callback, *args, **kwargs):
87
- metrics = get_metrics()
88
135
  kwargs.pop('perf', None)
89
136
 
90
137
  if not should_enable_metrics(ctx): return original_callback(*args, **kwargs)
138
+ metrics = get_metrics()
91
139
  if metrics.process_start is None:
92
140
  init_metrics()
93
141
  get_metrics().end_startup()
@@ -0,0 +1,41 @@
1
+ import os
2
+ import sys
3
+ import threading
4
+
5
+ # PATCHES THREAD SET DAEMON METHOD TO PREVENT WARNINGS
6
+ def _patched_set_daemon(self, daemon):
7
+ self.daemon = daemon
8
+
9
+ _original_set_daemon = None
10
+
11
+ def _should_show_spinner():
12
+ if os.getenv('CI') or os.getenv('PYTEST_CURRENT_TEST'): return False
13
+ if os.getenv('TERM', '').lower() == 'dumb': return False
14
+ return bool(getattr(sys.stderr, 'isatty', lambda: False)())
15
+
16
+ def _create_spinner():
17
+ global _original_set_daemon
18
+ if _original_set_daemon is None:
19
+ _original_set_daemon = threading.Thread.daemon
20
+ threading.Thread.daemon = _patched_set_daemon
21
+
22
+ from halo import Halo
23
+ return Halo(spinner={'interval': 200, 'frames': [' ', '. ', '.. ', '...']})
24
+
25
+ # CONTEXT MANAGER FOR DISPLAYING LOADING ANIMATION
26
+ class LoadingAnimation:
27
+ # INITIALIZES SPINNER WITH CUSTOM ANIMATION FRAMES
28
+ def __init__(self):
29
+ self._spinner = None
30
+ self._enabled = _should_show_spinner()
31
+
32
+ # STARTS THE LOADING ANIMATION
33
+ def __enter__(self):
34
+ if self._enabled:
35
+ self._spinner = _create_spinner()
36
+ self._spinner.start()
37
+ return self
38
+
39
+ # STOPS THE LOADING ANIMATION
40
+ def __exit__(self, exc_type, exc_val, exc_tb):
41
+ if self._spinner is not None: self._spinner.stop()