bluer-objects 6.186.1__tar.gz → 6.201.1__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.

Potentially problematic release.


This version of bluer-objects might be problematic. Click here for more details.

Files changed (184) hide show
  1. {bluer_objects-6.186.1/bluer_objects.egg-info → bluer_objects-6.201.1}/PKG-INFO +2 -2
  2. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/README.md +1 -1
  3. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/alias.sh +1 -1
  4. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/help.sh +3 -2
  5. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/storage_public_upload.sh +7 -0
  6. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/storage_upload_download.sh +8 -1
  7. bluer_objects-6.186.1/bluer_objects/.abcli/tests/url_is_accessible.sh → bluer_objects-6.201.1/bluer_objects/.abcli/tests/web_is_accessible.sh +3 -3
  8. bluer_objects-6.201.1/bluer_objects/.abcli/tests/web_where_am_ai.sh +5 -0
  9. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/upload.sh +22 -0
  10. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/url.sh +4 -4
  11. {bluer_objects-6.186.1/bluer_objects/.abcli/url → bluer_objects-6.201.1/bluer_objects/.abcli/web}/is_accessible.sh +2 -2
  12. bluer_objects-6.201.1/bluer_objects/.abcli/web/where_am_i.sh +5 -0
  13. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/README/functions.py +4 -3
  14. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/__init__.py +1 -1
  15. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/functions.py +2 -2
  16. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/upload.py +1 -1
  17. bluer_objects-6.186.1/bluer_objects/help/url.py → bluer_objects-6.201.1/bluer_objects/help/web.py +16 -1
  18. bluer_objects-6.201.1/bluer_objects/logger/image.py +91 -0
  19. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/storage/WebDAV.py +4 -2
  20. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/storage/WebDAVrequest.py +4 -2
  21. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/storage/WebDAVzip.py +4 -2
  22. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/storage/__init__.py +2 -0
  23. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/storage/__main__.py +7 -0
  24. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/storage/base.py +2 -1
  25. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/storage/s3.py +22 -9
  26. bluer_objects-6.201.1/bluer_objects/tests/test_log_image_grid.py +29 -0
  27. bluer_objects-6.186.1/bluer_objects/tests/test_url_is_accessible.py → bluer_objects-6.201.1/bluer_objects/tests/test_web_is_accessible.py +1 -1
  28. {bluer_objects-6.186.1/bluer_objects/url → bluer_objects-6.201.1/bluer_objects/web}/__main__.py +1 -1
  29. {bluer_objects-6.186.1/bluer_objects/url → bluer_objects-6.201.1/bluer_objects/web}/functions.py +0 -2
  30. {bluer_objects-6.186.1 → bluer_objects-6.201.1/bluer_objects.egg-info}/PKG-INFO +2 -2
  31. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects.egg-info/SOURCES.txt +11 -7
  32. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/setup.py +1 -1
  33. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/LICENSE +0 -0
  34. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/MANIFEST.in +0 -0
  35. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/abcli.sh +0 -0
  36. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/actions.sh +0 -0
  37. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/aka.sh +0 -0
  38. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/bluer_objects.sh +0 -0
  39. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/clone.sh +0 -0
  40. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/create_test_asset.sh +0 -0
  41. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/download.sh +0 -0
  42. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/file.sh +0 -0
  43. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/gif.sh +0 -0
  44. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/host.sh +0 -0
  45. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/ls.sh +0 -0
  46. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/metadata/get.sh +0 -0
  47. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/metadata/post.sh +0 -0
  48. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/metadata.sh +0 -0
  49. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/browse.sh +0 -0
  50. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/cache.sh +0 -0
  51. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/deploy.sh +0 -0
  52. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/list_registered_models.sh +0 -0
  53. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/lock/lock.sh +0 -0
  54. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/lock/unlock.sh +0 -0
  55. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/lock.sh +0 -0
  56. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/log_artifacts.sh +0 -0
  57. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/log_run.sh +0 -0
  58. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/run.sh +0 -0
  59. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/tags/clone.sh +0 -0
  60. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/tags/get.sh +0 -0
  61. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/tags/search.sh +0 -0
  62. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/tags/set.sh +0 -0
  63. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/tags.sh +0 -0
  64. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/test.sh +0 -0
  65. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow/transition.sh +0 -0
  66. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/mlflow.sh +0 -0
  67. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/object.sh +0 -0
  68. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/select.sh +0 -0
  69. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/storage/clear.sh +0 -0
  70. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/storage/download_file.sh +0 -0
  71. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/storage/exists.sh +0 -0
  72. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/storage/list.sh +0 -0
  73. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/storage/rm.sh +0 -0
  74. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/storage/status.sh +0 -0
  75. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/storage.sh +0 -0
  76. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/README.sh +0 -0
  77. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/clone.sh +0 -0
  78. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/create_test_asset.sh +0 -0
  79. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/gif.sh +0 -0
  80. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/host.sh +0 -0
  81. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/ls.sh +0 -0
  82. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/metadata.sh +0 -0
  83. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/mlflow_cache.sh +0 -0
  84. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/mlflow_lock.sh +0 -0
  85. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/mlflow_logging.sh +0 -0
  86. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/mlflow_tags.sh +0 -0
  87. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/mlflow_test.sh +0 -0
  88. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/.abcli/tests/version.sh +0 -0
  89. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/README/__init__.py +0 -0
  90. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/README/items.py +0 -0
  91. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/__main__.py +0 -0
  92. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/config.env +0 -0
  93. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/env.py +0 -0
  94. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/file/__init__.py +0 -0
  95. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/file/__main__.py +0 -0
  96. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/file/classes.py +0 -0
  97. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/file/functions.py +0 -0
  98. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/file/load.py +0 -0
  99. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/file/save.py +0 -0
  100. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/graphics/__init__.py +0 -0
  101. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/graphics/__main__.py +0 -0
  102. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/graphics/frame.py +0 -0
  103. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/graphics/gif.py +0 -0
  104. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/graphics/screen.py +0 -0
  105. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/graphics/signature.py +0 -0
  106. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/graphics/text.py +0 -0
  107. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/__init__.py +0 -0
  108. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/__main__.py +0 -0
  109. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/clone.py +0 -0
  110. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/create_test_asset.py +0 -0
  111. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/download.py +0 -0
  112. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/gif.py +0 -0
  113. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/host.py +0 -0
  114. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/ls.py +0 -0
  115. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/metadata.py +0 -0
  116. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/mlflow/__init__.py +0 -0
  117. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/mlflow/cache.py +0 -0
  118. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/mlflow/lock.py +0 -0
  119. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/help/mlflow/tags.py +0 -0
  120. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/host/__init__.py +0 -0
  121. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/host/__main__.py +0 -0
  122. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/host/functions.py +0 -0
  123. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/logger/__init__.py +0 -0
  124. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/logger/matrix.py +0 -0
  125. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/markdown.py +0 -0
  126. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/metadata/__init__.py +0 -0
  127. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/metadata/__main__.py +0 -0
  128. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/metadata/enums.py +0 -0
  129. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/metadata/get.py +0 -0
  130. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/metadata/post.py +0 -0
  131. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/__init__.py +0 -0
  132. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/__main__.py +0 -0
  133. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/cache.py +0 -0
  134. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/lock/__init__.py +0 -0
  135. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/lock/__main__.py +0 -0
  136. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/lock/functions.py +0 -0
  137. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/logging.py +0 -0
  138. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/models.py +0 -0
  139. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/objects.py +0 -0
  140. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/runs.py +0 -0
  141. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/tags.py +0 -0
  142. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/mlflow/testing.py +0 -0
  143. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/objects.py +0 -0
  144. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/path.py +0 -0
  145. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/sample.env +0 -0
  146. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/testing/__init__.py +0 -0
  147. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/testing/__main__.py +0 -0
  148. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/testing/functions.py +0 -0
  149. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/__init__.py +0 -0
  150. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_README.py +0 -0
  151. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_env.py +0 -0
  152. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_file_download.py +0 -0
  153. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_file_load_save.py +0 -0
  154. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_file_load_save_text.py +0 -0
  155. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_fullname.py +0 -0
  156. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_graphics.py +0 -0
  157. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_graphics_frame.py +0 -0
  158. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_graphics_gif.py +0 -0
  159. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_graphics_screen.py +0 -0
  160. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_graphics_signature.py +0 -0
  161. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_graphics_text.py +0 -0
  162. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_logger.py +0 -0
  163. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_logger_matrix.py +0 -0
  164. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_markdown.py +0 -0
  165. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_metadata.py +0 -0
  166. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_mlflow.py +0 -0
  167. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_mlflow_lock.py +0 -0
  168. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_objects.py +0 -0
  169. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_path.py +0 -0
  170. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_storage.py +0 -0
  171. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_storage_base.py +0 -0
  172. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_storage_s3.py +0 -0
  173. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_storage_webdav_request.py +0 -0
  174. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_storage_webdav_zip.py +0 -0
  175. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_testing.py +0 -0
  176. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/tests/test_version.py +0 -0
  177. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects/urls.py +0 -0
  178. {bluer_objects-6.186.1/bluer_objects/url → bluer_objects-6.201.1/bluer_objects/web}/__init__.py +0 -0
  179. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects.egg-info/dependency_links.txt +0 -0
  180. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects.egg-info/requires.txt +0 -0
  181. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/bluer_objects.egg-info/top_level.txt +0 -0
  182. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/pyproject.toml +0 -0
  183. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/requirements.txt +0 -0
  184. {bluer_objects-6.186.1 → bluer_objects-6.201.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bluer_objects
3
- Version: 6.186.1
3
+ Version: 6.201.1
4
4
  Summary: 🌀 Object management in Bash.
5
5
  Home-page: https://github.com/kamangir/bluer-objects
6
6
  Author: Arash Abadpour (Kamangir)
@@ -64,6 +64,6 @@ pip install bluer-objects
64
64
 
65
65
  [![pylint](https://github.com/kamangir/bluer-objects/actions/workflows/pylint.yml/badge.svg)](https://github.com/kamangir/bluer-objects/actions/workflows/pylint.yml) [![pytest](https://github.com/kamangir/bluer-objects/actions/workflows/pytest.yml/badge.svg)](https://github.com/kamangir/bluer-objects/actions/workflows/pytest.yml) [![bashtest](https://github.com/kamangir/bluer-objects/actions/workflows/bashtest.yml/badge.svg)](https://github.com/kamangir/bluer-objects/actions/workflows/bashtest.yml) [![PyPI version](https://img.shields.io/pypi/v/bluer-objects.svg)](https://pypi.org/project/bluer-objects/) [![PyPI - Downloads](https://img.shields.io/pypi/dd/bluer-objects)](https://pypistats.org/packages/bluer-objects)
66
66
 
67
- built by 🌀 [`bluer README`](https://github.com/kamangir/bluer-objects/tree/main/bluer_objects/README), based on 🌀 [`bluer_objects-6.186.1`](https://github.com/kamangir/bluer-objects).
67
+ built by 🌀 [`bluer README`](https://github.com/kamangir/bluer-objects/tree/main/bluer_objects/README), based on 🌀 [`bluer_objects-6.201.1`](https://github.com/kamangir/bluer-objects).
68
68
 
69
69
  built by 🌀 [`blueness-3.118.1`](https://github.com/kamangir/blueness).
@@ -30,4 +30,4 @@ pip install bluer-objects
30
30
 
31
31
  [![pylint](https://github.com/kamangir/bluer-objects/actions/workflows/pylint.yml/badge.svg)](https://github.com/kamangir/bluer-objects/actions/workflows/pylint.yml) [![pytest](https://github.com/kamangir/bluer-objects/actions/workflows/pytest.yml/badge.svg)](https://github.com/kamangir/bluer-objects/actions/workflows/pytest.yml) [![bashtest](https://github.com/kamangir/bluer-objects/actions/workflows/bashtest.yml/badge.svg)](https://github.com/kamangir/bluer-objects/actions/workflows/bashtest.yml) [![PyPI version](https://img.shields.io/pypi/v/bluer-objects.svg)](https://pypi.org/project/bluer-objects/) [![PyPI - Downloads](https://img.shields.io/pypi/dd/bluer-objects)](https://pypistats.org/packages/bluer-objects)
32
32
 
33
- built by 🌀 [`bluer README`](https://github.com/kamangir/bluer-objects/tree/main/bluer_objects/README), based on 🌀 [`bluer_objects-6.186.1`](https://github.com/kamangir/bluer-objects).
33
+ built by 🌀 [`bluer README`](https://github.com/kamangir/bluer-objects/tree/main/bluer_objects/README), based on 🌀 [`bluer_objects-6.201.1`](https://github.com/kamangir/bluer-objects).
@@ -39,4 +39,4 @@ alias @storage=bluer_ai_storage
39
39
 
40
40
  alias @upload=bluer_objects_upload
41
41
 
42
- alias @url=bluer_objects_url
42
+ alias @web=bluer_objects_web
@@ -77,8 +77,9 @@ function test_bluer_objects_help() {
77
77
  \
78
78
  "@upload" \
79
79
  \
80
- "@url" \
81
- "@url is_accessible" \
80
+ "@web" \
81
+ "@web is_accessible" \
82
+ "@web where_am_i" \
82
83
  \
83
84
  "@wait" \
84
85
  \
@@ -11,8 +11,15 @@ function test_bluer_objects_storage_public_upload() {
11
11
  $object_name \
12
12
  --depth 1
13
13
  [[ $? -ne 0 ]] && return 1
14
+ bluer_ai_hr
14
15
 
15
16
  bluer_objects_upload \
16
17
  public \
17
18
  $object_name
19
+ [[ $? -ne 0 ]] && return 1
20
+ bluer_ai_hr
21
+
22
+ bluer_objects_upload \
23
+ public,zip \
24
+ $object_name
18
25
  }
@@ -8,7 +8,8 @@ function test_bluer_objects_storage_upload_download() {
8
8
  mkdir -pv $object_path
9
9
 
10
10
  bluer_objects_create_test_asset \
11
- $object_name
11
+ $object_name \
12
+ --depth 1
12
13
  [[ $? -ne 0 ]] && return 1
13
14
  bluer_ai_hr
14
15
 
@@ -59,4 +60,10 @@ function test_bluer_objects_storage_upload_download() {
59
60
  bluer_objects_download \
60
61
  - \
61
62
  $object_name
63
+ [[ $? -ne 0 ]] && return 1
64
+ bluer_ai_hr
65
+
66
+ bluer_objects_upload \
67
+ zip \
68
+ $object_name
62
69
  }
@@ -1,7 +1,7 @@
1
1
  #! /usr/bin/env bash
2
2
 
3
- function test_bluer_objects_url_is_accessible() {
4
- local output=$(bluer_objects_url_is_accessible void)
3
+ function test_bluer_objects_web_is_accessible() {
4
+ local output=$(bluer_objects_web_is_accessible void)
5
5
  [[ "$output" -ne 0 ]] &&
6
6
  return 1
7
7
 
@@ -9,7 +9,7 @@ function test_bluer_objects_url_is_accessible() {
9
9
  [[ "$abcli_is_github_workflow" == true ]] &&
10
10
  url="https://cnn.com"
11
11
 
12
- output=$(bluer_objects_url_is_accessible $url)
12
+ output=$(bluer_objects_web_is_accessible $url)
13
13
  [[ "$output" -ne 1 ]] &&
14
14
  return 1
15
15
 
@@ -0,0 +1,5 @@
1
+ #! /usr/bin/env bash
2
+
3
+ function test_bluer_objects_web_where_am_i() {
4
+ bluer_objects_web_where_am_i
5
+ }
@@ -4,12 +4,34 @@ function bluer_objects_upload() {
4
4
  local options=$1
5
5
  local filename=$(bluer_ai_option "$options" filename)
6
6
  local public=$(bluer_ai_option_int "$options" public 0)
7
+ local do_zip=$(bluer_ai_option_int "$options" zip 0)
7
8
 
8
9
  local object_name=$(bluer_ai_clarify_object $2 .)
9
10
  local object_path=$ABCLI_OBJECT_ROOT/$object_name
10
11
 
11
12
  rm -rf $object_path/auxiliary
12
13
 
14
+ if [[ "$do_zip" == 1 ]]; then
15
+ local zip_filename=$ABCLI_OBJECT_ROOT/$object_name.tar.gz
16
+
17
+ [[ -f "$zip_filename" ]] &&
18
+ rm -v $zip_filename
19
+
20
+ bluer_ai_eval - \
21
+ tar -czvf \
22
+ $zip_filename \
23
+ -C $ABCLI_OBJECT_ROOT $object_name
24
+ [[ $? -ne 0 ]] && return 1
25
+
26
+ python3 -m bluer_objects.storage \
27
+ upload \
28
+ --object_name $object_name \
29
+ --zip 1 \
30
+ --public $public
31
+
32
+ return
33
+ fi
34
+
13
35
  python3 -m bluer_objects.storage \
14
36
  upload \
15
37
  --object_name $object_name \
@@ -1,15 +1,15 @@
1
1
  #! /usr/bin/env bash
2
2
 
3
- function bluer_objects_url() {
3
+ function bluer_objects_web() {
4
4
  local task=$1
5
5
 
6
- local function_name=bluer_objects_url_$task
6
+ local function_name=bluer_objects_web_$task
7
7
  if [[ $(type -t $function_name) == "function" ]]; then
8
8
  $function_name "${@:2}"
9
9
  return
10
10
  fi
11
11
 
12
- python3 -m bluer_objects.url "$@"
12
+ python3 -m bluer_objects.web "$@"
13
13
  }
14
14
 
15
- bluer_ai_source_caller_suffix_path /url
15
+ bluer_ai_source_caller_suffix_path /web
@@ -1,13 +1,13 @@
1
1
  #! /usr/bin/env bash
2
2
 
3
- function bluer_objects_url_is_accessible() {
3
+ function bluer_objects_web_is_accessible() {
4
4
  local url=$1
5
5
  if [[ -z "$url" ]]; then
6
6
  bluer_ai_log_error "url not found."
7
7
  return 1
8
8
  fi
9
9
 
10
- python3 -m bluer_objects.url \
10
+ python3 -m bluer_objects.web \
11
11
  is_accessible \
12
12
  --url $url
13
13
  }
@@ -0,0 +1,5 @@
1
+ #! /usr/bin/env bash
2
+
3
+ function bluer_objects_web_where_am_i() {
4
+ curl ifconfig.co/json
5
+ }
@@ -6,7 +6,7 @@ from blueness import module
6
6
 
7
7
  from bluer_objects import NAME as MY_NAME, ICON as MY_ICON
8
8
  from bluer_objects.metadata import get_from_object
9
- from bluer_objects import file
9
+ from bluer_objects import file, env
10
10
  from bluer_objects import markdown
11
11
  from bluer_objects.logger import logger
12
12
 
@@ -148,9 +148,10 @@ def build(
148
148
  template_line = " ".join(
149
149
  [
150
150
  (
151
- "[{}]({}/{}.tar.gz)".format(
151
+ "[{}](https://{}.{}/{}.tar.gz)".format(
152
152
  token.split(":::")[1].strip(),
153
- "TBA",
153
+ env.S3_PUBLIC_STORAGE_BUCKET,
154
+ env.S3_STORAGE_ENDPOINT_URL.split("https://", 1)[1],
154
155
  token.split(":::")[1].strip(),
155
156
  )
156
157
  if token.startswith("object:::")
@@ -4,7 +4,7 @@ ICON = "🌀"
4
4
 
5
5
  DESCRIPTION = f"{ICON} Object management in Bash."
6
6
 
7
- VERSION = "6.186.1"
7
+ VERSION = "6.201.1"
8
8
 
9
9
  REPO_NAME = "bluer-objects"
10
10
 
@@ -10,7 +10,7 @@ from bluer_objects.help.ls import help_ls
10
10
  from bluer_objects.help.metadata import help_functions as help_metadata
11
11
  from bluer_objects.help.mlflow import help_functions as help_mlflow
12
12
  from bluer_objects.help.upload import help_upload
13
- from bluer_objects.help.url import help_functions as help_url
13
+ from bluer_objects.help.web import help_functions as help_web
14
14
 
15
15
  help_functions = generic_help_functions(plugin_name=ALIAS)
16
16
 
@@ -25,6 +25,6 @@ help_functions.update(
25
25
  "metadata": help_metadata,
26
26
  "mlflow": help_mlflow,
27
27
  "upload": help_upload,
28
- "url": help_url,
28
+ "web": help_web,
29
29
  }
30
30
  )
@@ -10,7 +10,7 @@ def help_upload(
10
10
  options = "".join(
11
11
  [
12
12
  "filename=<filename>",
13
- xtra(",public", mono=mono),
13
+ xtra(",public,zip", mono=mono),
14
14
  ]
15
15
  )
16
16
 
@@ -9,7 +9,7 @@ def help_is_accessible(
9
9
  ) -> str:
10
10
  return show_usage(
11
11
  [
12
- "@url",
12
+ "@web",
13
13
  "is_accessible",
14
14
  "<url>",
15
15
  ],
@@ -18,6 +18,21 @@ def help_is_accessible(
18
18
  )
19
19
 
20
20
 
21
+ def help_where_am_i(
22
+ tokens: List[str],
23
+ mono: bool,
24
+ ) -> str:
25
+ return show_usage(
26
+ [
27
+ "@web",
28
+ "where_am_i",
29
+ ],
30
+ "where am I?",
31
+ mono=mono,
32
+ )
33
+
34
+
21
35
  help_functions = {
22
36
  "is_accessible": help_is_accessible,
37
+ "where_am_i": help_where_am_i,
23
38
  }
@@ -0,0 +1,91 @@
1
+ import matplotlib.pyplot as plt
2
+ from typing import Dict, Any, Union, List
3
+ import pandas as pd
4
+ import random
5
+ import os
6
+
7
+ from bluer_objects import file, objects
8
+ from bluer_objects.graphics.signature import sign_filename
9
+
10
+
11
+ def log_image_grid(
12
+ items: Union[
13
+ Dict[str, Dict[str, Any]],
14
+ pd.DataFrame,
15
+ ],
16
+ filename: str,
17
+ rows: int = 3,
18
+ cols: int = 5,
19
+ log: bool = True,
20
+ scale: int = 2,
21
+ shuffle: bool = False,
22
+ header: List[str] = [],
23
+ footer: List[str] = [],
24
+ relative_path: bool = False,
25
+ ) -> bool:
26
+ if isinstance(items, pd.DataFrame):
27
+ items = items.to_dict("records")
28
+
29
+ while len(items) < rows * cols:
30
+ items += [{"pass": True}]
31
+ if shuffle:
32
+ random.shuffle(items)
33
+ items = items[: rows * cols]
34
+
35
+ if relative_path:
36
+ root_path = file.path(filename)
37
+ for item in items:
38
+ if item.get("filename", ""):
39
+ item["filename"] = os.path.join(
40
+ root_path,
41
+ item["filename"],
42
+ )
43
+
44
+ _, axes = plt.subplots(
45
+ rows,
46
+ cols,
47
+ figsize=(
48
+ scale * cols,
49
+ scale * rows,
50
+ ),
51
+ )
52
+ axes = axes.flatten()
53
+
54
+ for i, item in enumerate(items):
55
+ if item.get("pass", False):
56
+ axes[i].axis("off")
57
+ continue
58
+
59
+ if item.get("filename", ""):
60
+ success, item["image"] = file.load_image(
61
+ item.get("filename", ""),
62
+ log=log,
63
+ )
64
+ if not success:
65
+ return False
66
+
67
+ ax = axes[i]
68
+ image = item["image"]
69
+ ax.imshow(
70
+ image,
71
+ cmap="gray" if image.ndim == 2 else None,
72
+ )
73
+ ax.set_title(
74
+ item.get("title", f"#{i}"),
75
+ fontsize=10,
76
+ )
77
+ ax.axis("off")
78
+
79
+ plt.tight_layout()
80
+
81
+ if not file.save_fig(
82
+ filename,
83
+ log=log,
84
+ ):
85
+ return False
86
+
87
+ return sign_filename(
88
+ filename,
89
+ [" | ".join(objects.signature("grid.png") + header)],
90
+ [" | ".join(footer)],
91
+ )
@@ -87,10 +87,11 @@ class WebDAVInterface(StorageInterface):
87
87
  object_name: str,
88
88
  filename: str = "",
89
89
  public: bool = False,
90
+ zip: bool = False,
90
91
  log: bool = True,
91
92
  ) -> bool:
92
- if public:
93
- logger.error("public not supported.")
93
+ if public or zip:
94
+ logger.error("public/zip upload not supported.")
94
95
  return False
95
96
 
96
97
  if filename:
@@ -117,5 +118,6 @@ class WebDAVInterface(StorageInterface):
117
118
  object_name=object_name,
118
119
  filename=filename,
119
120
  public=public,
121
+ zip=zip,
120
122
  log=log,
121
123
  )
@@ -279,10 +279,11 @@ class WebDAVRequestInterface(StorageInterface):
279
279
  object_name: str,
280
280
  filename: str = "",
281
281
  public: bool = False,
282
+ zip: bool = False,
282
283
  log: bool = True,
283
284
  ) -> bool:
284
- if public:
285
- logger.error("public not supported.")
285
+ if public or zip:
286
+ logger.error("public/zip upload not supported.")
286
287
  return False
287
288
 
288
289
  if filename:
@@ -321,6 +322,7 @@ class WebDAVRequestInterface(StorageInterface):
321
322
  object_name=object_name,
322
323
  filename=filename,
323
324
  public=public,
325
+ zip=zip,
324
326
  log=log,
325
327
  )
326
328
 
@@ -126,10 +126,11 @@ class WebDAVzipInterface(StorageInterface):
126
126
  object_name: str,
127
127
  filename: str = "",
128
128
  public: bool = False,
129
+ zip: bool = False,
129
130
  log: bool = True,
130
131
  ) -> bool:
131
- if public:
132
- logger.error("public not supported.")
132
+ if public or zip:
133
+ logger.error("public/zip upload not supported.")
133
134
  return False
134
135
 
135
136
  object_path = objects.object_path(object_name=object_name)
@@ -155,5 +156,6 @@ class WebDAVzipInterface(StorageInterface):
155
156
  return super().upload(
156
157
  object_name=object_name,
157
158
  public=public,
159
+ zip=zip,
158
160
  log=log,
159
161
  )
@@ -57,11 +57,13 @@ def upload(
57
57
  object_name: str,
58
58
  filename: str = "",
59
59
  public: bool = False,
60
+ zip: bool = False,
60
61
  log: bool = True,
61
62
  ) -> bool:
62
63
  return interface.upload(
63
64
  object_name=object_name,
64
65
  filename=filename,
65
66
  public=public,
67
+ zip=zip,
66
68
  log=log,
67
69
  )
@@ -30,6 +30,12 @@ parser.add_argument(
30
30
  default=0,
31
31
  help="0 | 1",
32
32
  )
33
+ parser.add_argument(
34
+ "--zip",
35
+ type=int,
36
+ default=0,
37
+ help="0 | 1",
38
+ )
33
39
  parser.add_argument(
34
40
  "--where",
35
41
  type=str,
@@ -86,6 +92,7 @@ elif args.task == "upload":
86
92
  success = storage.upload(
87
93
  object_name=args.object_name,
88
94
  filename=args.filename,
95
+ zip=args.zip == 1,
89
96
  public=args.public == 1,
90
97
  )
91
98
  else:
@@ -67,6 +67,7 @@ class StorageInterface:
67
67
  object_name: str,
68
68
  filename: str = "",
69
69
  public: bool = False,
70
+ zip: bool = False,
70
71
  log: bool = True,
71
72
  ) -> bool:
72
73
  if log:
@@ -74,7 +75,7 @@ class StorageInterface:
74
75
  "{}.upload {}{}{}".format(
75
76
  self.__class__.__name__,
76
77
  object_name,
77
- f"/{filename}" if filename else "",
78
+ ".tar.gz" if zip else f"/{filename}" if filename else "",
78
79
  " [public]" if public else "",
79
80
  )
80
81
  )
@@ -1,4 +1,5 @@
1
1
  import boto3
2
+ import os
2
3
  from botocore.exceptions import ClientError
3
4
  import glob
4
5
  from typing import Tuple, List
@@ -7,6 +8,7 @@ from tqdm import tqdm
7
8
  from functools import reduce
8
9
 
9
10
  from bluer_objects.storage.base import StorageInterface
11
+ from bluer_objects.env import ABCLI_OBJECT_ROOT
10
12
  from bluer_objects import env, file, path
11
13
  from bluer_objects import objects
12
14
  from bluer_objects.logger import logger
@@ -226,12 +228,20 @@ class S3Interface(StorageInterface):
226
228
  object_name: str,
227
229
  filename: str = "",
228
230
  public: bool = False,
231
+ zip: bool = False,
229
232
  log: bool = True,
230
233
  ) -> bool:
231
- if filename:
232
- local_path = objects.path_of(
233
- object_name=object_name,
234
- filename=filename,
234
+ if filename or zip:
235
+ local_path = (
236
+ os.path.join(
237
+ ABCLI_OBJECT_ROOT,
238
+ f"{object_name}.tar.gz",
239
+ )
240
+ if zip
241
+ else objects.path_of(
242
+ object_name=object_name,
243
+ filename=filename,
244
+ )
235
245
  )
236
246
 
237
247
  bucket_name = (
@@ -252,20 +262,22 @@ class S3Interface(StorageInterface):
252
262
  bucket.put_object(
253
263
  ACL="public-read" if public else "private",
254
264
  Body=fp,
255
- Key=f"{object_name}/{filename}",
265
+ Key=(
266
+ f"{object_name}.tar.gz"
267
+ if zip
268
+ else f"{object_name}/{filename}"
269
+ ),
256
270
  )
257
271
  except ClientError as e:
258
272
  logger.error(e)
259
273
  return False
260
274
 
261
275
  if public:
262
-
263
276
  logger.info(
264
- "🔗 https://{}.{}/{}/{}".format(
277
+ "🔗 https://{}.{}/{}".format(
265
278
  bucket_name,
266
279
  env.S3_STORAGE_ENDPOINT_URL.split("https://", 1)[1],
267
- object_name,
268
- filename,
280
+ f"{object_name}.tar.gz" if zip else f"{object_name}/{filename}",
269
281
  )
270
282
  )
271
283
 
@@ -273,6 +285,7 @@ class S3Interface(StorageInterface):
273
285
  object_name=object_name,
274
286
  filename=filename,
275
287
  public=public,
288
+ zip=zip,
276
289
  log=log,
277
290
  )
278
291
 
@@ -0,0 +1,29 @@
1
+ from bluer_objects import objects
2
+ from bluer_objects.testing import create_test_asset
3
+ from bluer_objects.logger.image import log_image_grid
4
+
5
+
6
+ def test_log_image_grid():
7
+ object_name = objects.unique_object("test_log_image_grid")
8
+
9
+ depth = 10
10
+
11
+ assert create_test_asset(
12
+ object_name=object_name,
13
+ depth=depth,
14
+ )
15
+
16
+ assert log_image_grid(
17
+ [
18
+ {
19
+ "filename": objects.path_of(
20
+ object_name=object_name,
21
+ filename=f"test-{suffix:02d}.png",
22
+ )
23
+ for suffix in range(depth)
24
+ }
25
+ ],
26
+ objects.path_of(object_name=object_name, filename="image_grid.png"),
27
+ rows=2,
28
+ cols=5,
29
+ )
@@ -1,4 +1,4 @@
1
- from bluer_objects.url.functions import is_accessible
1
+ from bluer_objects.web.functions import is_accessible
2
2
  from bluer_ai.env import abcli_is_github_workflow
3
3
 
4
4
 
@@ -4,7 +4,7 @@ from blueness import module
4
4
  from blueness.argparse.generic import sys_exit
5
5
 
6
6
  from bluer_objects import NAME
7
- from bluer_objects.url.functions import is_accessible
7
+ from bluer_objects.web.functions import is_accessible
8
8
  from bluer_objects.logger import logger
9
9
 
10
10
  NAME = module.name(__file__, NAME)
@@ -1,7 +1,5 @@
1
1
  import requests
2
2
 
3
- from bluer_objects.logger import logger
4
-
5
3
 
6
4
  def is_accessible(url) -> bool:
7
5
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bluer_objects
3
- Version: 6.186.1
3
+ Version: 6.201.1
4
4
  Summary: 🌀 Object management in Bash.
5
5
  Home-page: https://github.com/kamangir/bluer-objects
6
6
  Author: Arash Abadpour (Kamangir)
@@ -64,6 +64,6 @@ pip install bluer-objects
64
64
 
65
65
  [![pylint](https://github.com/kamangir/bluer-objects/actions/workflows/pylint.yml/badge.svg)](https://github.com/kamangir/bluer-objects/actions/workflows/pylint.yml) [![pytest](https://github.com/kamangir/bluer-objects/actions/workflows/pytest.yml/badge.svg)](https://github.com/kamangir/bluer-objects/actions/workflows/pytest.yml) [![bashtest](https://github.com/kamangir/bluer-objects/actions/workflows/bashtest.yml/badge.svg)](https://github.com/kamangir/bluer-objects/actions/workflows/bashtest.yml) [![PyPI version](https://img.shields.io/pypi/v/bluer-objects.svg)](https://pypi.org/project/bluer-objects/) [![PyPI - Downloads](https://img.shields.io/pypi/dd/bluer-objects)](https://pypistats.org/packages/bluer-objects)
66
66
 
67
- built by 🌀 [`bluer README`](https://github.com/kamangir/bluer-objects/tree/main/bluer_objects/README), based on 🌀 [`bluer_objects-6.186.1`](https://github.com/kamangir/bluer-objects).
67
+ built by 🌀 [`bluer README`](https://github.com/kamangir/bluer-objects/tree/main/bluer_objects/README), based on 🌀 [`bluer_objects-6.201.1`](https://github.com/kamangir/bluer-objects).
68
68
 
69
69
  built by 🌀 [`blueness-3.118.1`](https://github.com/kamangir/blueness).