data-sourcerer 0.2.3__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {data_sourcerer-0.2.3.dist-info → data_sourcerer-0.4.0.dist-info}/METADATA +3 -1
- {data_sourcerer-0.2.3.dist-info → data_sourcerer-0.4.0.dist-info}/RECORD +50 -34
- sourcerer/__init__.py +1 -1
- sourcerer/domain/access_credentials/entities.py +3 -1
- sourcerer/domain/access_credentials/repositories.py +1 -1
- sourcerer/domain/storage/__init__.py +0 -0
- sourcerer/domain/storage/entities.py +27 -0
- sourcerer/domain/storage/repositories.py +31 -0
- sourcerer/domain/storage_provider/entities.py +1 -1
- sourcerer/infrastructure/access_credentials/repositories.py +3 -2
- sourcerer/infrastructure/access_credentials/services.py +9 -25
- sourcerer/infrastructure/db/models.py +33 -2
- sourcerer/infrastructure/storage/__init__.py +0 -0
- sourcerer/infrastructure/storage/repositories.py +72 -0
- sourcerer/infrastructure/storage/services.py +37 -0
- sourcerer/infrastructure/storage_provider/services/azure.py +1 -3
- sourcerer/infrastructure/storage_provider/services/gcp.py +2 -3
- sourcerer/infrastructure/storage_provider/services/s3.py +1 -2
- sourcerer/infrastructure/utils.py +2 -1
- sourcerer/presentation/di_container.py +15 -0
- sourcerer/presentation/screens/file_system_finder/main.py +5 -10
- sourcerer/presentation/screens/file_system_finder/widgets/file_system_navigator.py +16 -13
- sourcerer/presentation/screens/main/main.py +89 -9
- sourcerer/presentation/screens/main/messages/preview_request.py +1 -0
- sourcerer/presentation/screens/main/messages/select_storage_item.py +1 -0
- sourcerer/presentation/screens/main/mixins/resize_containers_watcher_mixin.py +2 -1
- sourcerer/presentation/screens/main/widgets/storage_content.py +197 -80
- sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py +99 -31
- sourcerer/presentation/screens/preview_content/main.py +216 -17
- sourcerer/presentation/screens/preview_content/styles.tcss +39 -4
- sourcerer/presentation/screens/preview_content/text_area_style.py +60 -0
- sourcerer/presentation/screens/provider_creds_list/main.py +38 -13
- sourcerer/presentation/screens/provider_creds_registration/main.py +10 -7
- sourcerer/presentation/screens/shared/modal_screens.py +37 -0
- sourcerer/presentation/screens/shared/widgets/spinner.py +57 -0
- sourcerer/presentation/screens/storage_action_progress/main.py +3 -5
- sourcerer/presentation/screens/storages_list/__init__.py +0 -0
- sourcerer/presentation/screens/storages_list/main.py +184 -0
- sourcerer/presentation/screens/storages_list/messages/__init__.py +0 -0
- sourcerer/presentation/screens/storages_list/messages/reload_storages_request.py +8 -0
- sourcerer/presentation/screens/storages_list/styles.tcss +55 -0
- sourcerer/presentation/screens/storages_registration/__init__.py +0 -0
- sourcerer/presentation/screens/storages_registration/main.py +100 -0
- sourcerer/presentation/screens/storages_registration/styles.tcss +41 -0
- sourcerer/presentation/settings.py +29 -16
- sourcerer/presentation/utils.py +9 -1
- sourcerer/settings.py +2 -0
- sourcerer/presentation/screens/shared/widgets/loader.py +0 -31
- {data_sourcerer-0.2.3.dist-info → data_sourcerer-0.4.0.dist-info}/WHEEL +0 -0
- {data_sourcerer-0.2.3.dist-info → data_sourcerer-0.4.0.dist-info}/entry_points.txt +0 -0
- {data_sourcerer-0.2.3.dist-info → data_sourcerer-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: data-sourcerer
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: Sourcerer is a terminal cloud storage navigator.
|
5
5
|
Author-email: Bohdana Kuzmenko <bohdana.kuzmenko.dev@gmail.com>
|
6
6
|
License: MIT
|
@@ -42,6 +42,8 @@ engineers to view and manage files across multiple cloud providers like
|
|
42
42
|
|
43
43
|
> Your terminal. Your storages. Your control.
|
44
44
|
|
45
|
+
[Demo page](https://the-impact-craft.github.io/sourcerer/)
|
46
|
+
|
45
47
|
---
|
46
48
|
|
47
49
|
## ✨ Features
|
@@ -1,11 +1,11 @@
|
|
1
|
-
sourcerer/__init__.py,sha256=
|
2
|
-
sourcerer/settings.py,sha256=
|
1
|
+
sourcerer/__init__.py,sha256=QELNZ0_i0crBFBzWe24aLrXu6z8WyDxq3mevvARdn2Q,585
|
2
|
+
sourcerer/settings.py,sha256=jIUcq9-_yYPxLS_8m1iKlkGCOg_pbhIjHXkR1LQmHQI,1463
|
3
3
|
sourcerer/utils.py,sha256=4jAlcofepAQMcD1cYDsC1ryGwBLxE9m7ckPS6CzDsCI,879
|
4
4
|
sourcerer/domain/__init__.py,sha256=rV21d-dD-e0q4EQ2LfWDSDLlrUOjnHnWBtWPujoue0o,556
|
5
5
|
sourcerer/domain/access_credentials/__init__.py,sha256=pFAwnr74uy09e7kubYsuaqzkVPkTA66dwjKzpIGQkAY,217
|
6
|
-
sourcerer/domain/access_credentials/entities.py,sha256=
|
6
|
+
sourcerer/domain/access_credentials/entities.py,sha256=m5hktJPmcwgG7vG-HKasA-gAd8US6jrZxGso4sCLZc8,2123
|
7
7
|
sourcerer/domain/access_credentials/exceptions.py,sha256=jo3vhGpIaEsDl68uV8JLDqPvfVZMi7H6MA0qsRL0QXQ,546
|
8
|
-
sourcerer/domain/access_credentials/repositories.py,sha256=
|
8
|
+
sourcerer/domain/access_credentials/repositories.py,sha256=hPlFnUDbR_W4WfiRRdsXvMQNfJ_3_Om1Wh7fvImepLo,2741
|
9
9
|
sourcerer/domain/access_credentials/services.py,sha256=HtTOBrDyh8Wz-dxYtcohPYu0qWxgnvzafGiXOXRu6Ww,2821
|
10
10
|
sourcerer/domain/file_system/__init__.py,sha256=5diScp5Q8Hw8e_4vqacVocQBw0KA7fPs_YERoNwSEzM,184
|
11
11
|
sourcerer/domain/file_system/entities.py,sha256=IeTu5VYxmBQMH7AcKfW1Muk4wUmgZ769eWwNyqeWRpw,2060
|
@@ -13,89 +13,105 @@ sourcerer/domain/file_system/exceptions.py,sha256=TairaMaLkSHgbiq3pImkmVZAACoVHy
|
|
13
13
|
sourcerer/domain/file_system/services.py,sha256=kQeEM1Lt28UBVT9uG4M2iFDSxSvQdaJw4QzM9giUdjY,2208
|
14
14
|
sourcerer/domain/shared/__init__.py,sha256=pkCn6PfBLIlYT5q4xWq3cNtOfbUrrePiH06TduLq6_o,148
|
15
15
|
sourcerer/domain/shared/entities.py,sha256=jJR1PhJBWrSeJyEFvauEYJ1UXG7BcT6pi2oFpmglu4I,497
|
16
|
+
sourcerer/domain/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
|
+
sourcerer/domain/storage/entities.py,sha256=bSWVFx4Hh7_RLCHVLN_U2Wg6XxeYUyzDGLUpbFuWTLo,687
|
18
|
+
sourcerer/domain/storage/repositories.py,sha256=v7RU8_X_HgFT3wn7Yc5m07aN7DEbsBMh8sRZSRSCTxU,1003
|
16
19
|
sourcerer/domain/storage_provider/__init__.py,sha256=P3RUH9LFkWez3ehCczgVbnbp0tZZepPeOQf9spsC8FQ,192
|
17
|
-
sourcerer/domain/storage_provider/entities.py,sha256=
|
20
|
+
sourcerer/domain/storage_provider/entities.py,sha256=koaY4M4nWeie9kXJrHLJ85fEvaLxOQJEq8Zl3vmGwMM,1931
|
18
21
|
sourcerer/domain/storage_provider/exceptions.py,sha256=6xK5r62Bhedx3vV0_i7Eu5ZG5IExxeiuaGHG5sX17i4,508
|
19
22
|
sourcerer/domain/storage_provider/services.py,sha256=Zm6nrKqQJW-9ZaqTp9wQaQuwMeH6hkLJZCkhQ_sTRF0,3997
|
20
23
|
sourcerer/infrastructure/__init__.py,sha256=HQoqA8S9Vx2dr1Eua86wu_YxwXyY6jqa4IfEoZJcXcQ,616
|
21
|
-
sourcerer/infrastructure/utils.py,sha256=
|
24
|
+
sourcerer/infrastructure/utils.py,sha256=_8dtobWxgrfHK7CUCFHq2AXS54KBwQQgDWWFxR21pFA,5198
|
22
25
|
sourcerer/infrastructure/access_credentials/__init__.py,sha256=7BSXnI9n59_PuGxHjOra6PG82R_6JlrU4S1tsJx4WGM,249
|
23
26
|
sourcerer/infrastructure/access_credentials/exceptions.py,sha256=usad48RTA7ub8AfnUs2EzbD-fiUwpmnRGjJluBjd3m4,1101
|
24
27
|
sourcerer/infrastructure/access_credentials/registry.py,sha256=YEjkbh5639j8LkAocozC8O_7t1pPcrJwbL1LCJhiGkY,3416
|
25
|
-
sourcerer/infrastructure/access_credentials/repositories.py,sha256=
|
26
|
-
sourcerer/infrastructure/access_credentials/services.py,sha256=
|
28
|
+
sourcerer/infrastructure/access_credentials/repositories.py,sha256=_GLT-d3yarQD9Rx4Y2EPYtFoWDeBIidixQW4MxXSZ2U,4472
|
29
|
+
sourcerer/infrastructure/access_credentials/services.py,sha256=QFU-eRKsl7P4Eg44i-A8BT6llVlT4vc6juvlJE39dq4,15720
|
27
30
|
sourcerer/infrastructure/db/__init__.py,sha256=sjx2F0aDnxej7-FAjhFJYIQQ5d2apmItkquKbvGYIBc,175
|
28
31
|
sourcerer/infrastructure/db/config.py,sha256=c3ubAxBWt-zDUwTe3pAgJ86-rw-LZZAZppuBRo93Xn8,1792
|
29
|
-
sourcerer/infrastructure/db/models.py,sha256=
|
32
|
+
sourcerer/infrastructure/db/models.py,sha256=XLkV7wpKZTPKCur32hpapT690O5UiILhYhTTr3dMw_U,2780
|
30
33
|
sourcerer/infrastructure/file_system/__init__.py,sha256=Swx8arwXeZ4E40ViDSpQglmUrIlpuEvZY6crcWpGC2g,219
|
31
34
|
sourcerer/infrastructure/file_system/exceptions.py,sha256=obx0NxVuhVzLgL5K2MA5d4rNImlRHJ4Gr-2RrIuKJqk,2944
|
32
35
|
sourcerer/infrastructure/file_system/services.py,sha256=J0XHv-zF8WblE6bzzEpKkqU_k3gefMj_OArWBRFtGHA,5649
|
36
|
+
sourcerer/infrastructure/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
|
+
sourcerer/infrastructure/storage/repositories.py,sha256=GTMxxdeRmnxqpLUq_FCX9i28tkUL6xYRSsv9rVYFTzM,2334
|
38
|
+
sourcerer/infrastructure/storage/services.py,sha256=wP37li_ts-By0Z_q5pizl__v-nRjFlkM7LlAJghujnc,1024
|
33
39
|
sourcerer/infrastructure/storage_provider/__init__.py,sha256=GONjDCsTmd6f_fF3lzxDAfLlyuSQDhO4bz0eF9a35tI,229
|
34
40
|
sourcerer/infrastructure/storage_provider/exceptions.py,sha256=acx3IIXD2yWlzLvD2asJBEpKEa6eJW31uKkzzr8MrR4,3336
|
35
41
|
sourcerer/infrastructure/storage_provider/registry.py,sha256=8dbRLOx1jLK_i18uuh_JnKvId9NJBECKg4nG9F_dFH4,2249
|
36
42
|
sourcerer/infrastructure/storage_provider/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
|
-
sourcerer/infrastructure/storage_provider/services/azure.py,sha256=
|
38
|
-
sourcerer/infrastructure/storage_provider/services/gcp.py,sha256=
|
39
|
-
sourcerer/infrastructure/storage_provider/services/s3.py,sha256=
|
43
|
+
sourcerer/infrastructure/storage_provider/services/azure.py,sha256=ZCglIQ0DQX1mYimsT4rS1tUvZtLbvpR_NZZzjGw7sB4,9757
|
44
|
+
sourcerer/infrastructure/storage_provider/services/gcp.py,sha256=vGqD7rrDZ4fepxIiY1owI4YTSCZWbRMmyrsuEssR24U,9059
|
45
|
+
sourcerer/infrastructure/storage_provider/services/s3.py,sha256=avRAtgZTobKnovfCG_lnW9xZOG77cnpDnZGazn85XHc,9239
|
40
46
|
sourcerer/presentation/__init__.py,sha256=kzOeaTpy9hm61MLl_nybdooRrawFUd1uEX4f3Y-84ZU,472
|
41
47
|
sourcerer/presentation/app.py,sha256=ROu3vSWzo6d8W30A9Zqi5zdLcVeHJsGLDJMLTKrthHE,1018
|
42
|
-
sourcerer/presentation/di_container.py,sha256=
|
43
|
-
sourcerer/presentation/settings.py,sha256=
|
44
|
-
sourcerer/presentation/utils.py,sha256=
|
48
|
+
sourcerer/presentation/di_container.py,sha256=KTDSWEtR_YTqJTOsyhwBBHqB9gqIkX-nu5a8Ks9UlDc,2215
|
49
|
+
sourcerer/presentation/settings.py,sha256=iR8oK62c3kzgN5w3h0sEQlXhE5AEwufk_pv3hjMODMo,1255
|
50
|
+
sourcerer/presentation/utils.py,sha256=9sDvwnXsycxHXvH0V_we5F6BzGyncGvWQ-vogaUqMBM,2979
|
45
51
|
sourcerer/presentation/screens/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
46
52
|
sourcerer/presentation/screens/critical_error/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
47
53
|
sourcerer/presentation/screens/critical_error/main.py,sha256=10Ip1InBzktwwM2ijKXBOkhvBXGorw1X8EGxdh75WZE,2208
|
48
54
|
sourcerer/presentation/screens/critical_error/styles.tcss,sha256=mURvbf0_npkRtzVBs2bVBybbyNK9cO_6Ar2Muk1Mpv8,604
|
49
|
-
sourcerer/presentation/screens/file_system_finder/main.py,sha256=
|
55
|
+
sourcerer/presentation/screens/file_system_finder/main.py,sha256=8V603I74iKoyMMChbl8XjpQAu614qpW0sbRlkCAxSjg,10221
|
50
56
|
sourcerer/presentation/screens/file_system_finder/styles.tcss,sha256=fZkdwXFsDkjXkaIskLxoQ_YHsLWKjgrn6hYseugg_68,718
|
51
57
|
sourcerer/presentation/screens/file_system_finder/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
52
|
-
sourcerer/presentation/screens/file_system_finder/widgets/file_system_navigator.py,sha256=
|
58
|
+
sourcerer/presentation/screens/file_system_finder/widgets/file_system_navigator.py,sha256=giQXiNEsSAmBV-YWc1eXcJDFUWxpCppv0oNxgvrLZxg,31070
|
53
59
|
sourcerer/presentation/screens/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
|
-
sourcerer/presentation/screens/main/main.py,sha256=
|
60
|
+
sourcerer/presentation/screens/main/main.py,sha256=4eCKA7ZCy7DPfpK-uFnwSdY7HIELQSHr2TrmGdPX0u4,24038
|
55
61
|
sourcerer/presentation/screens/main/styles.tcss,sha256=Ruv2vBKzM8njH7OS2TCpZqCmRVEp7XQLeBN4XhVB5AU,381
|
56
62
|
sourcerer/presentation/screens/main/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
63
|
sourcerer/presentation/screens/main/messages/delete_request.py,sha256=puaU1UtbDErfYi8ViPEawhSbycnUpwdE81zZfzlslNE,203
|
58
64
|
sourcerer/presentation/screens/main/messages/download_request.py,sha256=3urSTrvNbod1FrXfu-C1UDZqOu5D0OC-NJsKJhhprXE,205
|
59
|
-
sourcerer/presentation/screens/main/messages/preview_request.py,sha256
|
65
|
+
sourcerer/presentation/screens/main/messages/preview_request.py,sha256=-UVWFVr4vsB0LGj97MTT8pLbJ3eNJClMWvT6vQ4lqw8,198
|
60
66
|
sourcerer/presentation/screens/main/messages/refresh_storages_list_request.py,sha256=NxOBcUf5oKF1cMCcHZny8qG2Jv6zTdPmYeFV9qORHh8,136
|
61
67
|
sourcerer/presentation/screens/main/messages/resizing_rule.py,sha256=ws7lzS08h6qqeihF66XV5FsX26YkjQOje_4vgCw2mqI,332
|
62
|
-
sourcerer/presentation/screens/main/messages/select_storage_item.py,sha256=
|
68
|
+
sourcerer/presentation/screens/main/messages/select_storage_item.py,sha256=iZOwzOwFLhsr58WV01-NtTFr4LXKykz1i76nayLkCLw,269
|
63
69
|
sourcerer/presentation/screens/main/messages/uncheck_files_request.py,sha256=CMpYVwos1JHknqDcqru1hG8RJy5f1Lwjsxtrse1ZDGw,140
|
64
70
|
sourcerer/presentation/screens/main/messages/upload_request.py,sha256=KXPTliqdvRvtMwob5bQUuBDxCjWmFcSjt-eUgQoRv7g,185
|
65
71
|
sourcerer/presentation/screens/main/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
66
|
-
sourcerer/presentation/screens/main/mixins/resize_containers_watcher_mixin.py,sha256=
|
72
|
+
sourcerer/presentation/screens/main/mixins/resize_containers_watcher_mixin.py,sha256=LmB7ZQh6ngl-x3QsDBlcKqJFFm8bYW9FSBYMYRAEi_0,5696
|
67
73
|
sourcerer/presentation/screens/main/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
68
74
|
sourcerer/presentation/screens/main/widgets/gradient.py,sha256=Ow6oaX0tsFNbUKRh_e9ITph20_tKoQgqzM7rujwKmxs,1263
|
69
75
|
sourcerer/presentation/screens/main/widgets/resizing_rule.py,sha256=W4tAbSZlmAIytP1BNlSvBeiMGA7cD6l13IpxG2JYzxk,2088
|
70
|
-
sourcerer/presentation/screens/main/widgets/storage_content.py,sha256=
|
71
|
-
sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py,sha256=
|
76
|
+
sourcerer/presentation/screens/main/widgets/storage_content.py,sha256=7ngNzPSagFi2GzzYNzFQqq6DCppLil2taghJdkSube0,26278
|
77
|
+
sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py,sha256=83R7HximPBPIj-3J9oWnISv10RcCabTo5EVWmhO5lSk,7748
|
72
78
|
sourcerer/presentation/screens/preview_content/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
73
|
-
sourcerer/presentation/screens/preview_content/main.py,sha256=
|
74
|
-
sourcerer/presentation/screens/preview_content/styles.tcss,sha256=
|
79
|
+
sourcerer/presentation/screens/preview_content/main.py,sha256=UejOzmE6cl6HKTH_N0-spoByyTJRxZwI8-0nm3S8hi0,9111
|
80
|
+
sourcerer/presentation/screens/preview_content/styles.tcss,sha256=ESFpZgwZlehkrbCVhA45ODiXTpFuwqQPvQypsSjGxpQ,887
|
81
|
+
sourcerer/presentation/screens/preview_content/text_area_style.py,sha256=AOX8CG9A77gQIhF8GHTW45clPWrQcRKqu0KJcUzXrFU,2699
|
75
82
|
sourcerer/presentation/screens/provider_creds_list/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
76
|
-
sourcerer/presentation/screens/provider_creds_list/main.py,sha256=
|
83
|
+
sourcerer/presentation/screens/provider_creds_list/main.py,sha256=96BCZUi7ZLhZSCGeg8KaSeMdaQbh_BxwEZ1lW-9ZUp4,8822
|
77
84
|
sourcerer/presentation/screens/provider_creds_list/styles.tcss,sha256=_BXTWQw_LjPjbAG-V4YxxOeHn4GFShVjc4K1by-uVR0,956
|
78
85
|
sourcerer/presentation/screens/provider_creds_list/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
79
86
|
sourcerer/presentation/screens/provider_creds_list/messages/reload_credentials_request.py,sha256=zkyLFkXZHQMj5OsDU_IiInUnezHNWyvYn8yZLoJmJn0,134
|
80
87
|
sourcerer/presentation/screens/provider_creds_registration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
81
|
-
sourcerer/presentation/screens/provider_creds_registration/main.py,sha256=
|
88
|
+
sourcerer/presentation/screens/provider_creds_registration/main.py,sha256=CkGFef_JdMvcH0ve-XPGA_I8ZRTdzJNurvjUjZ0ZjLo,11422
|
82
89
|
sourcerer/presentation/screens/provider_creds_registration/styles.tcss,sha256=gd1SNeRoHTYwNzdGxK-2aDqNPeY5b2wFWajtoNn5--Y,612
|
83
90
|
sourcerer/presentation/screens/question/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
84
91
|
sourcerer/presentation/screens/question/main.py,sha256=7ogFbKrqP9deBYfgq7Bs6NkiSdy3whEu51mW_--TCIY,980
|
85
92
|
sourcerer/presentation/screens/question/styles.tcss,sha256=NT8Ty4opqYLG1-sal3m1us4M4LNhtTcQywlU-Al-v7o,396
|
86
93
|
sourcerer/presentation/screens/shared/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
87
94
|
sourcerer/presentation/screens/shared/containers.py,sha256=9Tkl5SbPNgycZlfp5Pq50pHnJqbP0EckmxayXPPuhFs,378
|
95
|
+
sourcerer/presentation/screens/shared/modal_screens.py,sha256=ycpqEy9M3Dh_q-EO44_YbIyDAb9lkF8ZCUUje5fh42c,1128
|
88
96
|
sourcerer/presentation/screens/shared/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
89
97
|
sourcerer/presentation/screens/shared/widgets/button.py,sha256=aGsJTwK04nTXarafkl8TgdeB9EGfRwn4gffoPvFFwiU,1580
|
90
98
|
sourcerer/presentation/screens/shared/widgets/labeled_input.py,sha256=OmaZjRJSg8LABvBvbN3LQ0s9M3-jp9X2i3yCVNbRhEk,2798
|
91
|
-
sourcerer/presentation/screens/shared/widgets/
|
99
|
+
sourcerer/presentation/screens/shared/widgets/spinner.py,sha256=Do_Gc32GKA9ibFhORjfiK0RM3a7bvhnJ1SvnCXQJsHY,1713
|
92
100
|
sourcerer/presentation/screens/storage_action_progress/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
93
|
-
sourcerer/presentation/screens/storage_action_progress/main.py,sha256=
|
101
|
+
sourcerer/presentation/screens/storage_action_progress/main.py,sha256=0nNSxKwZv-ly5uA1cpdZf6x-UO8xoF0poHwc9VcGMj4,17899
|
94
102
|
sourcerer/presentation/screens/storage_action_progress/styles.tcss,sha256=ffvDxRWhckE5tjEG4jwlhQNqeQsYpdF71104StWCGWU,818
|
103
|
+
sourcerer/presentation/screens/storages_list/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
104
|
+
sourcerer/presentation/screens/storages_list/main.py,sha256=tzgCH-nJODW4wX1TO8Z7Zj5EwFXl5IX5JjEjxy6_Ky0,6481
|
105
|
+
sourcerer/presentation/screens/storages_list/styles.tcss,sha256=BlvlL5M-WGtMZuyqxT37gGlAIi1AFfILMaLiL-ZwvIc,766
|
106
|
+
sourcerer/presentation/screens/storages_list/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
107
|
+
sourcerer/presentation/screens/storages_list/messages/reload_storages_request.py,sha256=PtwlWgPUP_ZcAid535fd4Py6AvKUVhaXbc0WMka4QD0,131
|
108
|
+
sourcerer/presentation/screens/storages_registration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
109
|
+
sourcerer/presentation/screens/storages_registration/main.py,sha256=xam1qrUgmh7kA1y7EEbUAO1znZRpd1FQ7igN7Q_4j50,3802
|
110
|
+
sourcerer/presentation/screens/storages_registration/styles.tcss,sha256=Yd78pkiaaShb30r5s6qlYlLxyB62DJNeAQqs_gMcP6k,601
|
95
111
|
sourcerer/presentation/themes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
96
112
|
sourcerer/presentation/themes/github_dark.py,sha256=9E1mEOr701nU-ZDSKBccMl3GYchroCEsxEVelm5oI-E,497
|
97
|
-
data_sourcerer-0.
|
98
|
-
data_sourcerer-0.
|
99
|
-
data_sourcerer-0.
|
100
|
-
data_sourcerer-0.
|
101
|
-
data_sourcerer-0.
|
113
|
+
data_sourcerer-0.4.0.dist-info/METADATA,sha256=xanVKbREZlNIs0m1ecxFpYZS-OzCqb9S0BQ7YdE94jw,2595
|
114
|
+
data_sourcerer-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
115
|
+
data_sourcerer-0.4.0.dist-info/entry_points.txt,sha256=CyD02GehPW6QuhR5oDY5tLLRHQ9qbPXe0v3aT1pK3N8,62
|
116
|
+
data_sourcerer-0.4.0.dist-info/licenses/LICENSE,sha256=HjZ7RAG3i6izxvitGfY4feHfvW5F8DPj5eF0YBSf2rI,1073
|
117
|
+
data_sourcerer-0.4.0.dist-info/RECORD,,
|
sourcerer/__init__.py
CHANGED
@@ -4,13 +4,14 @@ Access credentials entity classes.
|
|
4
4
|
This module defines data classes representing access credentials
|
5
5
|
used for authentication with various cloud providers.
|
6
6
|
"""
|
7
|
-
|
8
7
|
from datetime import datetime
|
9
8
|
|
10
9
|
import boto3
|
11
10
|
from azure.identity import ClientSecretCredential
|
12
11
|
from msgspec._core import Struct
|
13
12
|
|
13
|
+
from sourcerer.domain.storage.entities import Storage
|
14
|
+
|
14
15
|
|
15
16
|
class Credentials(Struct):
|
16
17
|
"""
|
@@ -35,6 +36,7 @@ class Credentials(Struct):
|
|
35
36
|
active: bool
|
36
37
|
created_at: datetime | None = None
|
37
38
|
updated_at: datetime | None = None
|
39
|
+
storages: list[Storage] = [] # noqa: RUF012
|
38
40
|
|
39
41
|
|
40
42
|
class Boto3Credentials(Struct):
|
@@ -56,7 +56,7 @@ class BaseCredentialsRepository(metaclass=ABCMeta):
|
|
56
56
|
raise NotImplementedError
|
57
57
|
|
58
58
|
@abstractmethod
|
59
|
-
def list(self, active_only: bool | None = None):
|
59
|
+
def list(self, active_only: bool | None = None) -> list[Credentials]:
|
60
60
|
"""List all credentials in the repository.
|
61
61
|
|
62
62
|
Args:
|
File without changes
|
@@ -0,0 +1,27 @@
|
|
1
|
+
"""
|
2
|
+
Storage provider entity classes.
|
3
|
+
|
4
|
+
This module defines data classes representing cloud storage entities
|
5
|
+
such as storage containers, files, folders, and permissions.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from datetime import datetime
|
9
|
+
|
10
|
+
from msgspec._core import Struct
|
11
|
+
|
12
|
+
|
13
|
+
class Storage(Struct):
|
14
|
+
"""
|
15
|
+
Represents a cloud storage container (bucket/container).
|
16
|
+
|
17
|
+
Attributes:
|
18
|
+
credentials_id (int): The ID of the associated credentials
|
19
|
+
name (str): The storage name/identifier (e.g., bucket name)
|
20
|
+
date_created (datetime): When the storage was created
|
21
|
+
"""
|
22
|
+
|
23
|
+
name: str
|
24
|
+
uuid: str
|
25
|
+
date_created: datetime
|
26
|
+
credentials_id: int | None = None
|
27
|
+
credentials_name: str | None = None
|
@@ -0,0 +1,31 @@
|
|
1
|
+
from abc import ABCMeta, abstractmethod
|
2
|
+
|
3
|
+
from sourcerer.domain.storage.entities import Storage
|
4
|
+
|
5
|
+
|
6
|
+
class BaseStoragesRepository(metaclass=ABCMeta):
|
7
|
+
@abstractmethod
|
8
|
+
def create(self, storage: Storage) -> None:
|
9
|
+
"""Create a new storage entry in the repository.
|
10
|
+
Args:
|
11
|
+
storage (Storage): The storage object to store
|
12
|
+
"""
|
13
|
+
raise NotImplementedError()
|
14
|
+
|
15
|
+
@abstractmethod
|
16
|
+
def list(self, provider_id: int | None = None) -> list[Storage]:
|
17
|
+
"""List all storage entries in the repository.
|
18
|
+
Args:
|
19
|
+
provider_id (int | None): The provider ID to filter by. If None, all entries are returned.
|
20
|
+
Returns:
|
21
|
+
List[Storage]: A list of storage entries
|
22
|
+
"""
|
23
|
+
raise NotImplementedError()
|
24
|
+
|
25
|
+
@abstractmethod
|
26
|
+
def delete(self, uuid: str) -> None:
|
27
|
+
"""Delete a storage entry by UUID.
|
28
|
+
Args:
|
29
|
+
uuid (str): The UUID of the storage entry to delete
|
30
|
+
"""
|
31
|
+
raise NotImplementedError()
|
@@ -46,7 +46,7 @@ class SQLAlchemyCredentialsRepository(BaseCredentialsRepository):
|
|
46
46
|
session.add(credentials)
|
47
47
|
session.commit()
|
48
48
|
|
49
|
-
def delete(self, uuid: str):
|
49
|
+
def delete(self, uuid: str) -> None:
|
50
50
|
"""
|
51
51
|
Delete credentials from the database by UUID.
|
52
52
|
|
@@ -78,7 +78,7 @@ class SQLAlchemyCredentialsRepository(BaseCredentialsRepository):
|
|
78
78
|
session.query(DBCredentials).filter(DBCredentials.uuid == uuid).first()
|
79
79
|
)
|
80
80
|
|
81
|
-
def list(self, active_only: bool | None = None):
|
81
|
+
def list(self, active_only: bool | None = None) -> list[Credentials]:
|
82
82
|
"""
|
83
83
|
List all credentials in the repository.
|
84
84
|
|
@@ -103,6 +103,7 @@ class SQLAlchemyCredentialsRepository(BaseCredentialsRepository):
|
|
103
103
|
credentials_type=credential.credentials_type,
|
104
104
|
credentials=credential.credentials,
|
105
105
|
active=credential.active,
|
106
|
+
storages=credential.storages,
|
106
107
|
)
|
107
108
|
for credential in credentials_query.all()
|
108
109
|
]
|
@@ -10,7 +10,6 @@ from abc import ABC
|
|
10
10
|
|
11
11
|
import boto3
|
12
12
|
from azure.identity import ClientSecretCredential
|
13
|
-
from dependency_injector.wiring import Provide
|
14
13
|
from google.cloud import storage
|
15
14
|
|
16
15
|
from sourcerer.domain.access_credentials.entities import (
|
@@ -33,7 +32,6 @@ from sourcerer.infrastructure.access_credentials.registry import (
|
|
33
32
|
access_credentials_method,
|
34
33
|
)
|
35
34
|
from sourcerer.infrastructure.utils import generate_uuid
|
36
|
-
from sourcerer.presentation.di_container import DiContainer
|
37
35
|
|
38
36
|
|
39
37
|
class CredentialsService:
|
@@ -44,21 +42,16 @@ class CredentialsService:
|
|
44
42
|
and deactivating credentials.
|
45
43
|
"""
|
46
44
|
|
47
|
-
def __init__(
|
48
|
-
self,
|
49
|
-
credentials_repo: BaseCredentialsRepository = Provide[
|
50
|
-
DiContainer.credentials_repository
|
51
|
-
],
|
52
|
-
):
|
45
|
+
def __init__(self, repository: BaseCredentialsRepository):
|
53
46
|
"""
|
54
47
|
Initialize the service with a credentials repository.
|
55
48
|
|
56
49
|
Args:
|
57
|
-
|
50
|
+
repository (BaseCredentialsRepository): Repository for storing credentials
|
58
51
|
"""
|
59
|
-
self.credentials_repo =
|
52
|
+
self.credentials_repo = repository
|
60
53
|
|
61
|
-
def list(self, active_only=False):
|
54
|
+
def list(self, active_only=False) -> list[Credentials]:
|
62
55
|
"""
|
63
56
|
List credentials.
|
64
57
|
|
@@ -119,12 +112,7 @@ class AccessCredentialsService(BaseAccessCredentialsService, ABC):
|
|
119
112
|
access credential service implementations.
|
120
113
|
"""
|
121
114
|
|
122
|
-
def __init__(
|
123
|
-
self,
|
124
|
-
credentials_repo: BaseCredentialsRepository = Provide[
|
125
|
-
DiContainer.credentials_repository
|
126
|
-
],
|
127
|
-
):
|
115
|
+
def __init__(self, credentials_repo: BaseCredentialsRepository):
|
128
116
|
"""
|
129
117
|
Initialize the service with a credentials repository.
|
130
118
|
|
@@ -408,11 +396,9 @@ class GCPCredentialsService(AccessCredentialsService):
|
|
408
396
|
return storage.Client.from_service_account_info(service_acc_info)
|
409
397
|
|
410
398
|
except json.JSONDecodeError as e:
|
411
|
-
raise CredentialsAuthError(f"Invalid credentials format: {
|
399
|
+
raise CredentialsAuthError(f"Invalid credentials format: {e}") from e
|
412
400
|
except Exception as e:
|
413
|
-
raise CredentialsAuthError(
|
414
|
-
f"Failed to authenticate with GCP: {str(e)}"
|
415
|
-
) from e
|
401
|
+
raise CredentialsAuthError(f"Failed to authenticate with GCP: {e}") from e
|
416
402
|
|
417
403
|
@classmethod
|
418
404
|
def auth_fields(cls) -> list[AuthField]:
|
@@ -484,11 +470,9 @@ class AzureClientSecretCredentialsService(AccessCredentialsService):
|
|
484
470
|
)
|
485
471
|
|
486
472
|
except json.JSONDecodeError as e:
|
487
|
-
raise CredentialsAuthError(f"Invalid credentials format: {
|
473
|
+
raise CredentialsAuthError(f"Invalid credentials format: {e}") from e
|
488
474
|
except Exception as e:
|
489
|
-
raise CredentialsAuthError(
|
490
|
-
f"Failed to authenticate with Azure: {str(e)}"
|
491
|
-
) from e
|
475
|
+
raise CredentialsAuthError(f"Failed to authenticate with Azure: {e}") from e
|
492
476
|
|
493
477
|
@classmethod
|
494
478
|
def auth_fields(cls) -> list[AuthField]:
|
@@ -7,8 +7,8 @@ and their relationships.
|
|
7
7
|
|
8
8
|
from datetime import datetime
|
9
9
|
|
10
|
-
from sqlalchemy import Boolean, Column, DateTime, Integer, String
|
11
|
-
from sqlalchemy.orm import declarative_base
|
10
|
+
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String
|
11
|
+
from sqlalchemy.orm import backref, declarative_base, relationship
|
12
12
|
from sqlalchemy_utils.types.encrypted.encrypted_type import EncryptedType
|
13
13
|
|
14
14
|
from sourcerer.settings import ENCRYPTION_KEY
|
@@ -45,3 +45,34 @@ class Credentials(Base):
|
|
45
45
|
active = Column(Boolean, default=True)
|
46
46
|
created_at = Column(DateTime, default=datetime.utcnow)
|
47
47
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
48
|
+
|
49
|
+
|
50
|
+
class Storage(Base):
|
51
|
+
"""
|
52
|
+
SQLAlchemy model for storing storage information.
|
53
|
+
|
54
|
+
This model represents the storage table in the database,
|
55
|
+
storing information about different storage containers.
|
56
|
+
|
57
|
+
Attributes:
|
58
|
+
id (int): Primary key
|
59
|
+
uuid (str): Unique identifier for the storage
|
60
|
+
name (str): Name of the storage
|
61
|
+
credentials_id (int): Foreign key referencing the credentials table
|
62
|
+
created_at (datetime): Timestamp when the storage was created
|
63
|
+
"""
|
64
|
+
|
65
|
+
__tablename__ = "storages"
|
66
|
+
id = Column(Integer, primary_key=True)
|
67
|
+
uuid = Column(String, unique=True, nullable=False)
|
68
|
+
name = Column(String, nullable=False)
|
69
|
+
credentials_id = Column(
|
70
|
+
Integer, ForeignKey("credentials.id", ondelete="CASCADE"), nullable=False
|
71
|
+
)
|
72
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
73
|
+
|
74
|
+
credentials = relationship(
|
75
|
+
"Credentials",
|
76
|
+
cascade="save-update",
|
77
|
+
backref=backref("storages", passive_deletes=True),
|
78
|
+
)
|
File without changes
|
@@ -0,0 +1,72 @@
|
|
1
|
+
from sourcerer.domain.storage.entities import Storage
|
2
|
+
from sourcerer.domain.storage.repositories import BaseStoragesRepository
|
3
|
+
from sourcerer.infrastructure.db.models import Storage as DBStorage
|
4
|
+
|
5
|
+
|
6
|
+
class SQLAlchemyStoragesRepository(BaseStoragesRepository):
|
7
|
+
def __init__(self, db):
|
8
|
+
"""
|
9
|
+
Initialize the repository with a database session factory.
|
10
|
+
|
11
|
+
Args:
|
12
|
+
db: Database session factory
|
13
|
+
"""
|
14
|
+
self.db = db
|
15
|
+
|
16
|
+
def create(self, storage: Storage) -> None:
|
17
|
+
"""
|
18
|
+
Create a new storage entity in the database.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
storage (Storage): The storage entity to be created
|
22
|
+
"""
|
23
|
+
entry = DBStorage(
|
24
|
+
uuid=storage.uuid,
|
25
|
+
name=storage.name,
|
26
|
+
credentials_id=storage.credentials_id,
|
27
|
+
created_at=storage.date_created,
|
28
|
+
)
|
29
|
+
with self.db() as session:
|
30
|
+
session.add(entry)
|
31
|
+
session.commit()
|
32
|
+
|
33
|
+
def list(self, provider_id: int | None = None) -> list[Storage]:
|
34
|
+
"""
|
35
|
+
List all storages, optionally filtered by provider ID.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
provider_id (int | None): The ID of the provider to filter by
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
list[Storage]: List of storage entities
|
42
|
+
"""
|
43
|
+
with self.db() as session:
|
44
|
+
query = session.query(DBStorage)
|
45
|
+
if provider_id is not None:
|
46
|
+
query = query.filter(DBStorage.credentials_id == provider_id)
|
47
|
+
return [
|
48
|
+
Storage(
|
49
|
+
name=storage.name,
|
50
|
+
uuid=storage.uuid,
|
51
|
+
credentials_id=storage.credentials_id,
|
52
|
+
date_created=storage.created_at,
|
53
|
+
credentials_name=storage.credentials and storage.credentials.name,
|
54
|
+
)
|
55
|
+
for storage in query.all()
|
56
|
+
]
|
57
|
+
|
58
|
+
def delete(self, uuid: str) -> None:
|
59
|
+
"""
|
60
|
+
Delete a storage entity by its UUID.
|
61
|
+
|
62
|
+
Args:
|
63
|
+
uuid (str): The UUID of the storage entity to be deleted
|
64
|
+
"""
|
65
|
+
with self.db() as session:
|
66
|
+
storage = (
|
67
|
+
session.query(DBStorage).filter(DBStorage.uuid == uuid).one_or_none()
|
68
|
+
)
|
69
|
+
if storage is None:
|
70
|
+
return
|
71
|
+
session.delete(storage)
|
72
|
+
session.commit()
|
@@ -0,0 +1,37 @@
|
|
1
|
+
from sourcerer.domain.storage.entities import Storage
|
2
|
+
from sourcerer.domain.storage.repositories import BaseStoragesRepository
|
3
|
+
|
4
|
+
|
5
|
+
class StoragesService:
|
6
|
+
def __init__(
|
7
|
+
self,
|
8
|
+
repository: BaseStoragesRepository,
|
9
|
+
):
|
10
|
+
self.repository = repository
|
11
|
+
|
12
|
+
def create(self, storage: Storage) -> None:
|
13
|
+
"""
|
14
|
+
Create a new storage entity.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
storage (Storage): The storage object to be created
|
18
|
+
"""
|
19
|
+
self.repository.create(storage)
|
20
|
+
|
21
|
+
def list(self, provider_id: int | None = None) -> list[Storage]:
|
22
|
+
"""
|
23
|
+
List all storage entities.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
provider_id (int|None, optional): If provided, filter storage entities by provider ID
|
27
|
+
"""
|
28
|
+
return self.repository.list(provider_id)
|
29
|
+
|
30
|
+
def delete(self, uuid: str) -> None:
|
31
|
+
"""
|
32
|
+
Delete a storage entity by its UUID.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
uuid (str): The UUID of the storage entity to be deleted
|
36
|
+
"""
|
37
|
+
self.repository.delete(uuid)
|
@@ -10,7 +10,6 @@ from collections.abc import Callable
|
|
10
10
|
from pathlib import Path
|
11
11
|
from typing import Any
|
12
12
|
|
13
|
-
import humanize
|
14
13
|
from azure.mgmt.storage import StorageManagementClient
|
15
14
|
from azure.storage.blob import BlobServiceClient
|
16
15
|
from platformdirs import user_downloads_dir
|
@@ -38,7 +37,6 @@ from sourcerer.infrastructure.utils import generate_uuid, is_text_file
|
|
38
37
|
|
39
38
|
@storage_provider(StorageProvider.AzureStorage)
|
40
39
|
class AzureStorageProviderService(BaseStorageProviderService):
|
41
|
-
|
42
40
|
def __init__(self, credentials: Any):
|
43
41
|
"""
|
44
42
|
Initialize the service with Azure credentials.
|
@@ -137,7 +135,7 @@ class AzureStorageProviderService(BaseStorageProviderService):
|
|
137
135
|
File(
|
138
136
|
generate_uuid(),
|
139
137
|
remaining_path,
|
140
|
-
size=
|
138
|
+
size=blob.size,
|
141
139
|
date_modified=blob.last_modified,
|
142
140
|
is_text=is_text_file(blob.name),
|
143
141
|
)
|
@@ -9,7 +9,6 @@ from collections.abc import Callable
|
|
9
9
|
from pathlib import Path
|
10
10
|
from typing import Any
|
11
11
|
|
12
|
-
import humanize
|
13
12
|
from platformdirs import user_downloads_dir
|
14
13
|
|
15
14
|
from sourcerer.domain.shared.entities import StorageProvider
|
@@ -136,7 +135,7 @@ class GCPStorageProviderService(BaseStorageProviderService):
|
|
136
135
|
File(
|
137
136
|
generate_uuid(),
|
138
137
|
blob.name[len(path) :],
|
139
|
-
size=
|
138
|
+
size=blob.size,
|
140
139
|
date_modified=blob.updated.date(),
|
141
140
|
is_text=is_text_file(blob.name),
|
142
141
|
)
|
@@ -150,7 +149,7 @@ class GCPStorageProviderService(BaseStorageProviderService):
|
|
150
149
|
|
151
150
|
except Exception as ex:
|
152
151
|
raise ListStorageItemsError(
|
153
|
-
f"Failed to list items in {storage}: {
|
152
|
+
f"Failed to list items in {storage}: {ex}"
|
154
153
|
) from ex
|
155
154
|
|
156
155
|
def read_storage_item(self, storage: str, key: str) -> str:
|
@@ -10,7 +10,6 @@ from itertools import groupby
|
|
10
10
|
from pathlib import Path
|
11
11
|
from typing import Any
|
12
12
|
|
13
|
-
import humanize
|
14
13
|
from platformdirs import user_downloads_dir
|
15
14
|
|
16
15
|
from sourcerer.domain.shared.entities import StorageProvider
|
@@ -173,7 +172,7 @@ class S3ProviderService(BaseStorageProviderService):
|
|
173
172
|
File(
|
174
173
|
generate_uuid(),
|
175
174
|
i.get("Key").replace(path, ""),
|
176
|
-
|
175
|
+
i.get("Size"),
|
177
176
|
is_text_file(i.get("Key")),
|
178
177
|
i.get("LastModified"),
|
179
178
|
)
|
@@ -8,6 +8,7 @@ import mimetypes
|
|
8
8
|
import secrets
|
9
9
|
import uuid
|
10
10
|
from pathlib import Path
|
11
|
+
from typing import ClassVar
|
11
12
|
|
12
13
|
from sourcerer.settings import TEXT_EXTENSIONS
|
13
14
|
|
@@ -83,7 +84,7 @@ class Singleton(type):
|
|
83
84
|
Metaclass that implements the singleton pattern, ensuring only one instance of a class exists.
|
84
85
|
"""
|
85
86
|
|
86
|
-
_instances = {}
|
87
|
+
_instances: ClassVar[dict["Singleton", type]] = {}
|
87
88
|
|
88
89
|
def __call__(cls, *args, **kwargs):
|
89
90
|
"""
|