winipedia-utils 0.1.62__py3-none-any.whl → 0.2.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.

Potentially problematic release.


This version of winipedia-utils might be problematic. Click here for more details.

Files changed (91) hide show
  1. winipedia_utils/concurrent/concurrent.py +245 -242
  2. winipedia_utils/concurrent/multiprocessing.py +130 -115
  3. winipedia_utils/concurrent/multithreading.py +93 -93
  4. winipedia_utils/consts.py +23 -23
  5. winipedia_utils/data/__init__.py +1 -1
  6. winipedia_utils/data/dataframe/__init__.py +1 -0
  7. winipedia_utils/data/dataframe/cleaning.py +378 -0
  8. winipedia_utils/data/structures/__init__.py +1 -0
  9. winipedia_utils/data/structures/dicts.py +16 -0
  10. winipedia_utils/django/__init__.py +24 -24
  11. winipedia_utils/django/bulk.py +538 -538
  12. winipedia_utils/django/command.py +334 -334
  13. winipedia_utils/django/database.py +289 -289
  14. winipedia_utils/git/__init__.py +1 -1
  15. winipedia_utils/git/gitignore/__init__.py +1 -1
  16. winipedia_utils/git/gitignore/gitignore.py +136 -136
  17. winipedia_utils/git/pre_commit/__init__.py +1 -1
  18. winipedia_utils/git/pre_commit/config.py +70 -70
  19. winipedia_utils/git/pre_commit/hooks.py +109 -109
  20. winipedia_utils/git/pre_commit/run_hooks.py +49 -49
  21. winipedia_utils/iterating/__init__.py +1 -1
  22. winipedia_utils/iterating/iterate.py +29 -29
  23. winipedia_utils/logging/ansi.py +6 -6
  24. winipedia_utils/logging/config.py +64 -64
  25. winipedia_utils/logging/logger.py +26 -26
  26. winipedia_utils/modules/class_.py +119 -119
  27. winipedia_utils/modules/function.py +101 -103
  28. winipedia_utils/modules/module.py +379 -379
  29. winipedia_utils/modules/package.py +390 -390
  30. winipedia_utils/oop/mixins/meta.py +333 -331
  31. winipedia_utils/oop/mixins/mixin.py +37 -37
  32. winipedia_utils/os/__init__.py +1 -1
  33. winipedia_utils/os/os.py +63 -63
  34. winipedia_utils/projects/__init__.py +1 -1
  35. winipedia_utils/projects/poetry/__init__.py +1 -1
  36. winipedia_utils/projects/poetry/config.py +91 -91
  37. winipedia_utils/projects/poetry/poetry.py +31 -31
  38. winipedia_utils/projects/project.py +48 -48
  39. winipedia_utils/pyside/__init__.py +1 -1
  40. winipedia_utils/pyside/core/__init__.py +1 -1
  41. winipedia_utils/pyside/core/py_qiodevice.py +476 -476
  42. winipedia_utils/pyside/ui/__init__.py +1 -1
  43. winipedia_utils/pyside/ui/base/__init__.py +1 -1
  44. winipedia_utils/pyside/ui/base/base.py +180 -180
  45. winipedia_utils/pyside/ui/pages/__init__.py +1 -1
  46. winipedia_utils/pyside/ui/pages/base/__init__.py +1 -1
  47. winipedia_utils/pyside/ui/pages/base/base.py +92 -92
  48. winipedia_utils/pyside/ui/pages/browser.py +26 -26
  49. winipedia_utils/pyside/ui/pages/player.py +85 -85
  50. winipedia_utils/pyside/ui/widgets/__init__.py +1 -1
  51. winipedia_utils/pyside/ui/widgets/browser.py +243 -243
  52. winipedia_utils/pyside/ui/widgets/clickable_widget.py +57 -57
  53. winipedia_utils/pyside/ui/widgets/media_player.py +430 -423
  54. winipedia_utils/pyside/ui/widgets/notification.py +78 -78
  55. winipedia_utils/pyside/ui/windows/__init__.py +1 -1
  56. winipedia_utils/pyside/ui/windows/base/__init__.py +1 -1
  57. winipedia_utils/pyside/ui/windows/base/base.py +49 -49
  58. winipedia_utils/resources/__init__.py +1 -1
  59. winipedia_utils/resources/svgs/__init__.py +1 -1
  60. winipedia_utils/resources/svgs/download_arrow.svg +2 -2
  61. winipedia_utils/resources/svgs/exit_fullscreen_icon.svg +5 -5
  62. winipedia_utils/resources/svgs/fullscreen_icon.svg +2 -2
  63. winipedia_utils/resources/svgs/menu_icon.svg +3 -3
  64. winipedia_utils/resources/svgs/pause_icon.svg +3 -3
  65. winipedia_utils/resources/svgs/play_icon.svg +16 -16
  66. winipedia_utils/resources/svgs/plus_icon.svg +23 -23
  67. winipedia_utils/resources/svgs/svg.py +15 -15
  68. winipedia_utils/security/__init__.py +1 -1
  69. winipedia_utils/security/cryptography.py +29 -29
  70. winipedia_utils/security/keyring.py +70 -70
  71. winipedia_utils/setup.py +47 -47
  72. winipedia_utils/testing/assertions.py +23 -23
  73. winipedia_utils/testing/convention.py +177 -177
  74. winipedia_utils/testing/create_tests.py +291 -291
  75. winipedia_utils/testing/fixtures.py +28 -28
  76. winipedia_utils/testing/tests/base/fixtures/__init__.py +1 -1
  77. winipedia_utils/testing/tests/base/fixtures/fixture.py +6 -6
  78. winipedia_utils/testing/tests/base/fixtures/scopes/class_.py +33 -33
  79. winipedia_utils/testing/tests/base/fixtures/scopes/function.py +7 -7
  80. winipedia_utils/testing/tests/base/fixtures/scopes/module.py +31 -31
  81. winipedia_utils/testing/tests/base/fixtures/scopes/package.py +7 -7
  82. winipedia_utils/testing/tests/base/fixtures/scopes/session.py +312 -312
  83. winipedia_utils/testing/tests/base/utils/utils.py +82 -82
  84. winipedia_utils/testing/tests/conftest.py +32 -32
  85. winipedia_utils/text/string.py +126 -126
  86. {winipedia_utils-0.1.62.dist-info → winipedia_utils-0.2.0.dist-info}/METADATA +5 -4
  87. winipedia_utils-0.2.0.dist-info/RECORD +103 -0
  88. {winipedia_utils-0.1.62.dist-info → winipedia_utils-0.2.0.dist-info}/WHEEL +1 -1
  89. {winipedia_utils-0.1.62.dist-info → winipedia_utils-0.2.0.dist-info/licenses}/LICENSE +21 -21
  90. winipedia_utils/data/dataframe.py +0 -7
  91. winipedia_utils-0.1.62.dist-info/RECORD +0 -100
@@ -1,78 +1,78 @@
1
- """Notification module.
2
-
3
- This module contains functions to show notifications.
4
- """
5
-
6
- from pyqttoast import Toast, ToastIcon, ToastPosition # type: ignore[import-untyped]
7
- from PySide6.QtWidgets import QApplication
8
-
9
- from winipedia_utils.text.string import value_to_truncated_string
10
-
11
- Toast.setPosition(ToastPosition.TOP_MIDDLE)
12
-
13
-
14
- class Notification(Toast): # type: ignore[misc]
15
- """Notification class."""
16
-
17
- def __init__(
18
- self,
19
- title: str,
20
- text: str,
21
- icon: ToastIcon = ToastIcon.INFORMATION,
22
- duration: int = 10000,
23
- ) -> None:
24
- """Initialize the notification.
25
-
26
- The notification is shown in the top middle of the screen.
27
-
28
- Args:
29
- title: The title of the notification.
30
- text: The text of the notification.
31
- icon: The icon of the notification. Defaults to INFORMATION.
32
- duration: The duration of the notification in milliseconds.
33
- Defaults to 10000.
34
- """
35
- super().__init__(QApplication.activeWindow())
36
- self.setDuration(duration)
37
- self.setIcon(icon)
38
- self.set_title(title)
39
- self.set_text(text)
40
-
41
- def set_title(self, title: str) -> None:
42
- """Set the title of the notification.
43
-
44
- Truncates the title to fit within half the window width before setting it.
45
-
46
- Args:
47
- title: The title text to set.
48
- """
49
- title = self.str_to_half_window_width(title)
50
- self.setTitle(title)
51
-
52
- def set_text(self, text: str) -> None:
53
- """Set the text of the notification.
54
-
55
- Truncates the text to fit within half the window width before setting it.
56
-
57
- Args:
58
- text: The notification text to set.
59
- """
60
- text = self.str_to_half_window_width(text)
61
- self.setText(text)
62
-
63
- def str_to_half_window_width(self, string: str) -> str:
64
- """Truncate the string to the width of the active window.
65
-
66
- Calculates half the width of the active window and truncates the string
67
- to fit within that width. Falls back to 500 pixels if no active window.
68
-
69
- Args:
70
- string: The string to truncate.
71
-
72
- Returns:
73
- The truncated string that fits within half the window width.
74
- """
75
- main_window = QApplication.activeWindow()
76
- width = main_window.width() / 2 if main_window is not None else 500
77
- width = int(width)
78
- return value_to_truncated_string(string, width)
1
+ """Notification module.
2
+
3
+ This module contains functions to show notifications.
4
+ """
5
+
6
+ from pyqttoast import Toast, ToastIcon, ToastPosition # type: ignore[import-untyped]
7
+ from PySide6.QtWidgets import QApplication
8
+
9
+ from winipedia_utils.text.string import value_to_truncated_string
10
+
11
+ Toast.setPosition(ToastPosition.TOP_MIDDLE)
12
+
13
+
14
+ class Notification(Toast): # type: ignore[misc]
15
+ """Notification class."""
16
+
17
+ def __init__(
18
+ self,
19
+ title: str,
20
+ text: str,
21
+ icon: ToastIcon = ToastIcon.INFORMATION,
22
+ duration: int = 10000,
23
+ ) -> None:
24
+ """Initialize the notification.
25
+
26
+ The notification is shown in the top middle of the screen.
27
+
28
+ Args:
29
+ title: The title of the notification.
30
+ text: The text of the notification.
31
+ icon: The icon of the notification. Defaults to INFORMATION.
32
+ duration: The duration of the notification in milliseconds.
33
+ Defaults to 10000.
34
+ """
35
+ super().__init__(QApplication.activeWindow())
36
+ self.setDuration(duration)
37
+ self.setIcon(icon)
38
+ self.set_title(title)
39
+ self.set_text(text)
40
+
41
+ def set_title(self, title: str) -> None:
42
+ """Set the title of the notification.
43
+
44
+ Truncates the title to fit within half the window width before setting it.
45
+
46
+ Args:
47
+ title: The title text to set.
48
+ """
49
+ title = self.str_to_half_window_width(title)
50
+ self.setTitle(title)
51
+
52
+ def set_text(self, text: str) -> None:
53
+ """Set the text of the notification.
54
+
55
+ Truncates the text to fit within half the window width before setting it.
56
+
57
+ Args:
58
+ text: The notification text to set.
59
+ """
60
+ text = self.str_to_half_window_width(text)
61
+ self.setText(text)
62
+
63
+ def str_to_half_window_width(self, string: str) -> str:
64
+ """Truncate the string to the width of the active window.
65
+
66
+ Calculates half the width of the active window and truncates the string
67
+ to fit within that width. Falls back to 500 pixels if no active window.
68
+
69
+ Args:
70
+ string: The string to truncate.
71
+
72
+ Returns:
73
+ The truncated string that fits within half the window width.
74
+ """
75
+ main_window = QApplication.activeWindow()
76
+ width = main_window.width() / 2 if main_window is not None else 500
77
+ width = int(width)
78
+ return value_to_truncated_string(string, width)
@@ -1 +1 @@
1
- """__init__ module."""
1
+ """__init__ module."""
@@ -1 +1 @@
1
- """__init__ module."""
1
+ """__init__ module."""
@@ -1,49 +1,49 @@
1
- """Base window module.
2
-
3
- This module contains the base window class for the VideoVault application.
4
- """
5
-
6
- from abc import abstractmethod
7
-
8
- from PySide6.QtWidgets import QMainWindow, QStackedWidget
9
-
10
- from winipedia_utils.pyside.ui.base.base import Base as BaseUI
11
- from winipedia_utils.pyside.ui.pages.base.base import Base as BasePage
12
-
13
-
14
- class Base(BaseUI, QMainWindow):
15
- """Base window class for the VideoVault application."""
16
-
17
- @classmethod
18
- @abstractmethod
19
- def get_all_page_classes(cls) -> list[type[BasePage]]:
20
- """Get all page classes."""
21
-
22
- @classmethod
23
- @abstractmethod
24
- def get_start_page_cls(cls) -> type[BasePage]:
25
- """Get the start page class."""
26
-
27
- def base_setup(self) -> None:
28
- """Get the Qt object of the UI."""
29
- self.setWindowTitle(self.get_display_name())
30
-
31
- self.stack = QStackedWidget()
32
- self.setCentralWidget(self.stack)
33
-
34
- self.make_pages()
35
-
36
- self.set_start_page()
37
-
38
- def make_pages(self) -> None:
39
- """Get the pages to add to the window."""
40
- for page_cls in self.get_all_page_classes():
41
- page_cls(base_window=self)
42
-
43
- def set_start_page(self) -> None:
44
- """Set the start page."""
45
- self.set_current_page(self.get_start_page_cls())
46
-
47
- def add_page(self, page: BasePage) -> None:
48
- """Add the pages to the window."""
49
- self.stack.addWidget(page)
1
+ """Base window module.
2
+
3
+ This module contains the base window class for the VideoVault application.
4
+ """
5
+
6
+ from abc import abstractmethod
7
+
8
+ from PySide6.QtWidgets import QMainWindow, QStackedWidget
9
+
10
+ from winipedia_utils.pyside.ui.base.base import Base as BaseUI
11
+ from winipedia_utils.pyside.ui.pages.base.base import Base as BasePage
12
+
13
+
14
+ class Base(BaseUI, QMainWindow):
15
+ """Base window class for the VideoVault application."""
16
+
17
+ @classmethod
18
+ @abstractmethod
19
+ def get_all_page_classes(cls) -> list[type[BasePage]]:
20
+ """Get all page classes."""
21
+
22
+ @classmethod
23
+ @abstractmethod
24
+ def get_start_page_cls(cls) -> type[BasePage]:
25
+ """Get the start page class."""
26
+
27
+ def base_setup(self) -> None:
28
+ """Get the Qt object of the UI."""
29
+ self.setWindowTitle(self.get_display_name())
30
+
31
+ self.stack = QStackedWidget()
32
+ self.setCentralWidget(self.stack)
33
+
34
+ self.make_pages()
35
+
36
+ self.set_start_page()
37
+
38
+ def make_pages(self) -> None:
39
+ """Get the pages to add to the window."""
40
+ for page_cls in self.get_all_page_classes():
41
+ page_cls(base_window=self)
42
+
43
+ def set_start_page(self) -> None:
44
+ """Set the start page."""
45
+ self.set_current_page(self.get_start_page_cls())
46
+
47
+ def add_page(self, page: BasePage) -> None:
48
+ """Add the pages to the window."""
49
+ self.stack.addWidget(page)
@@ -1 +1 @@
1
- """__init__ module."""
1
+ """__init__ module."""
@@ -1 +1 @@
1
- """__init__ module."""
1
+ """__init__ module."""
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="24" height="24">
2
- <path d="M12 3v12m0 0l-5-5m5 5l5-5M5 21h14" stroke="black" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="24" height="24">
2
+ <path d="M12 3v12m0 0l-5-5m5 5l5-5M5 21h14" stroke="black" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
3
3
  </svg>
@@ -1,7 +1,7 @@
1
1
  <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
- <svg width="800px" height="800px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
3
- <path d="M1 6L6 6L6 1L4.2 1L4.2 4.2L1 4.2L1 6Z" fill="#000000"/>
4
- <path d="M15 10L10 10L10 15L11.8 15L11.8 11.8L15 11.8L15 10Z" fill="#000000"/>
5
- <path d="M6 15L6 10L1 10L1 11.8L4.2 11.8L4.2 15L6 15Z" fill="#000000"/>
6
- <path d="M10 1L10 6L15 6L15 4.2L11.8 4.2L11.8 1L10 1Z" fill="#000000"/>
2
+ <svg width="800px" height="800px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
3
+ <path d="M1 6L6 6L6 1L4.2 1L4.2 4.2L1 4.2L1 6Z" fill="#000000"/>
4
+ <path d="M15 10L10 10L10 15L11.8 15L11.8 11.8L15 11.8L15 10Z" fill="#000000"/>
5
+ <path d="M6 15L6 10L1 10L1 11.8L4.2 11.8L4.2 15L6 15Z" fill="#000000"/>
6
+ <path d="M10 1L10 6L15 6L15 4.2L11.8 4.2L11.8 1L10 1Z" fill="#000000"/>
7
7
  </svg>
@@ -1,4 +1,4 @@
1
1
  <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
- <svg width="800px" height="800px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
3
- <path fill-rule="evenodd" clip-rule="evenodd" d="M10 15H15V10H13.2V13.2H10V15ZM6 15V13.2H2.8V10H1V15H6ZM10 2.8H12.375H13.2V6H15V1H10V2.8ZM6 1V2.8H2.8V6H1V1H6Z" fill="#000000"/>
2
+ <svg width="800px" height="800px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
3
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10 15H15V10H13.2V13.2H10V15ZM6 15V13.2H2.8V10H1V15H6ZM10 2.8H12.375H13.2V6H15V1H10V2.8ZM6 1V2.8H2.8V6H1V1H6Z" fill="#000000"/>
4
4
  </svg>
@@ -1,4 +1,4 @@
1
- <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
- <svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
3
- <path d="M4 6H20M4 12H20M4 18H20" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
1
+ <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
+ <svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
3
+ <path d="M4 6H20M4 12H20M4 18H20" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
4
4
  </svg>
@@ -1,4 +1,4 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
2
- <rect x="4" y="4" width="6" height="16"/>
3
- <rect x="14" y="4" width="6" height="16"/>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
2
+ <rect x="4" y="4" width="6" height="16"/>
3
+ <rect x="14" y="4" width="6" height="16"/>
4
4
  </svg>
@@ -1,17 +1,17 @@
1
- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
- <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
- <svg width="800px" height="800px" viewBox="-3 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
4
-
5
- <title>play</title>
6
- <desc>Created with Sketch Beta.</desc>
7
- <defs>
8
-
9
- </defs>
10
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
11
- <g id="Icon-Set-Filled" sketch:type="MSLayerGroup" transform="translate(-419.000000, -571.000000)" fill="#000000">
12
- <path d="M440.415,583.554 L421.418,571.311 C420.291,570.704 419,570.767 419,572.946 L419,597.054 C419,599.046 420.385,599.36 421.418,598.689 L440.415,586.446 C441.197,585.647 441.197,584.353 440.415,583.554" id="play" sketch:type="MSShapeGroup">
13
-
14
- </path>
15
- </g>
16
- </g>
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg width="800px" height="800px" viewBox="-3 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
4
+
5
+ <title>play</title>
6
+ <desc>Created with Sketch Beta.</desc>
7
+ <defs>
8
+
9
+ </defs>
10
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
11
+ <g id="Icon-Set-Filled" sketch:type="MSLayerGroup" transform="translate(-419.000000, -571.000000)" fill="#000000">
12
+ <path d="M440.415,583.554 L421.418,571.311 C420.291,570.704 419,570.767 419,572.946 L419,597.054 C419,599.046 420.385,599.36 421.418,598.689 L440.415,586.446 C441.197,585.647 441.197,584.353 440.415,583.554" id="play" sketch:type="MSShapeGroup">
13
+
14
+ </path>
15
+ </g>
16
+ </g>
17
17
  </svg>
@@ -1,24 +1,24 @@
1
- <?xml version="1.0" ?>
2
-
3
- <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
4
- <svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
5
-
6
- <title/>
7
-
8
- <g id="Complete">
9
-
10
- <g data-name="add" id="add-2">
11
-
12
- <g>
13
-
14
- <line fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="19" y2="5"/>
15
-
16
- <line fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="5" x2="19" y1="12" y2="12"/>
17
-
18
- </g>
19
-
20
- </g>
21
-
22
- </g>
23
-
1
+ <?xml version="1.0" ?>
2
+
3
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
4
+ <svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
5
+
6
+ <title/>
7
+
8
+ <g id="Complete">
9
+
10
+ <g data-name="add" id="add-2">
11
+
12
+ <g>
13
+
14
+ <line fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="19" y2="5"/>
15
+
16
+ <line fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="5" x2="19" y1="12" y2="12"/>
17
+
18
+ </g>
19
+
20
+ </g>
21
+
22
+ </g>
23
+
24
24
  </svg>
@@ -1,15 +1,15 @@
1
- """utils for svgs."""
2
-
3
- from importlib.resources import as_file, files
4
- from pathlib import Path
5
- from types import ModuleType
6
-
7
- from winipedia_utils.resources import svgs
8
-
9
-
10
- def get_svg_path(svg_name: str, package: ModuleType | None = None) -> Path:
11
- """Get the path to a svg."""
12
- package = package or svgs
13
- svg_path = files(package) / f"{svg_name}.svg"
14
- with as_file(svg_path) as path:
15
- return path
1
+ """utils for svgs."""
2
+
3
+ from importlib.resources import as_file, files
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+ from winipedia_utils.resources import svgs
8
+
9
+
10
+ def get_svg_path(svg_name: str, package: ModuleType | None = None) -> Path:
11
+ """Get the path to a svg."""
12
+ package = package or svgs
13
+ svg_path = files(package) / f"{svg_name}.svg"
14
+ with as_file(svg_path) as path:
15
+ return path
@@ -1 +1 @@
1
- """__init__ module for winipedia_utils.security."""
1
+ """__init__ module for winipedia_utils.security."""
@@ -1,29 +1,29 @@
1
- """Cryptography utilities for secure data handling.
2
-
3
- This module provides utility functions for working with cryptography,
4
- including encryption and decryption using Fernet and AESGCM.
5
- These utilities help with secure data handling.
6
- """
7
-
8
- import os
9
-
10
- from cryptography.hazmat.primitives.ciphers.aead import AESGCM
11
-
12
- IV_LEN = 12
13
-
14
-
15
- def encrypt_with_aes_gcm(
16
- aes_gcm: AESGCM, data: bytes, aad: bytes | None = None
17
- ) -> bytes:
18
- """Encrypt data using AESGCM."""
19
- iv = os.urandom(IV_LEN)
20
- encrypted = aes_gcm.encrypt(iv, data, aad)
21
- return iv + encrypted
22
-
23
-
24
- def decrypt_with_aes_gcm(
25
- aes_gcm: AESGCM, data: bytes, aad: bytes | None = None
26
- ) -> bytes:
27
- """Decrypt data using AESGCM."""
28
- iv, encrypted = data[:IV_LEN], data[IV_LEN:]
29
- return aes_gcm.decrypt(iv, encrypted, aad)
1
+ """Cryptography utilities for secure data handling.
2
+
3
+ This module provides utility functions for working with cryptography,
4
+ including encryption and decryption using Fernet and AESGCM.
5
+ These utilities help with secure data handling.
6
+ """
7
+
8
+ import os
9
+
10
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
11
+
12
+ IV_LEN = 12
13
+
14
+
15
+ def encrypt_with_aes_gcm(
16
+ aes_gcm: AESGCM, data: bytes, aad: bytes | None = None
17
+ ) -> bytes:
18
+ """Encrypt data using AESGCM."""
19
+ iv = os.urandom(IV_LEN)
20
+ encrypted = aes_gcm.encrypt(iv, data, aad)
21
+ return iv + encrypted
22
+
23
+
24
+ def decrypt_with_aes_gcm(
25
+ aes_gcm: AESGCM, data: bytes, aad: bytes | None = None
26
+ ) -> bytes:
27
+ """Decrypt data using AESGCM."""
28
+ iv, encrypted = data[:IV_LEN], data[IV_LEN:]
29
+ return aes_gcm.decrypt(iv, encrypted, aad)
@@ -1,70 +1,70 @@
1
- """Keyring utilities for secure storage and retrieval of secrets.
2
-
3
- This module provides utility functions for working with keyring,
4
- including getting and creating secrets and fernets.
5
- These utilities help with secure storage and retrieval of secrets.
6
- """
7
-
8
- from base64 import b64decode, b64encode
9
- from collections.abc import Callable
10
-
11
- import keyring
12
- from cryptography.fernet import Fernet
13
- from cryptography.hazmat.primitives.ciphers.aead import AESGCM
14
-
15
-
16
- def get_or_create_fernet(service_name: str, username: str) -> tuple[Fernet, bytes]:
17
- """Get the app secret using keyring.
18
-
19
- If it does not exist, create it with a Fernet.
20
- """
21
- return get_or_create_key(
22
- service_name, username, Fernet, lambda: Fernet.generate_key()
23
- )
24
-
25
-
26
- def get_or_create_aes_gcm(service_name: str, username: str) -> tuple[AESGCM, bytes]:
27
- """Get the app secret using keyring.
28
-
29
- If it does not exist, create it with a AESGCM.
30
- """
31
- return get_or_create_key(
32
- service_name, username, AESGCM, lambda: AESGCM.generate_key(bit_length=256)
33
- )
34
-
35
-
36
- def get_or_create_key[T](
37
- service_name: str,
38
- username: str,
39
- key_class: Callable[[bytes], T],
40
- generate_key_func: Callable[..., bytes],
41
- ) -> tuple[T, bytes]:
42
- """Get the app secret using keyring.
43
-
44
- If it does not exist, create it with the generate_func.
45
- """
46
- key = get_key_as_str(service_name, username, key_class)
47
- if key is None:
48
- binary_key = generate_key_func()
49
- key = b64encode(binary_key).decode("ascii")
50
- modified_service_name = make_service_name(service_name, key_class)
51
- keyring.set_password(modified_service_name, username, key)
52
-
53
- binary_key = b64decode(key)
54
- return key_class(binary_key), binary_key
55
-
56
-
57
- def get_key_as_str[T](
58
- service_name: str, username: str, key_class: Callable[[bytes], T]
59
- ) -> str | None:
60
- """Get the app secret using keyring.
61
-
62
- If it does not exist, create it with the generate_func.
63
- """
64
- service_name = make_service_name(service_name, key_class)
65
- return keyring.get_password(service_name, username)
66
-
67
-
68
- def make_service_name[T](service_name: str, key_class: Callable[[bytes], T]) -> str:
69
- """Make a service name from a service name and a key class."""
70
- return f"{service_name}_{key_class.__name__}"
1
+ """Keyring utilities for secure storage and retrieval of secrets.
2
+
3
+ This module provides utility functions for working with keyring,
4
+ including getting and creating secrets and fernets.
5
+ These utilities help with secure storage and retrieval of secrets.
6
+ """
7
+
8
+ from base64 import b64decode, b64encode
9
+ from collections.abc import Callable
10
+
11
+ import keyring
12
+ from cryptography.fernet import Fernet
13
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
14
+
15
+
16
+ def get_or_create_fernet(service_name: str, username: str) -> tuple[Fernet, bytes]:
17
+ """Get the app secret using keyring.
18
+
19
+ If it does not exist, create it with a Fernet.
20
+ """
21
+ return get_or_create_key(
22
+ service_name, username, Fernet, lambda: Fernet.generate_key()
23
+ )
24
+
25
+
26
+ def get_or_create_aes_gcm(service_name: str, username: str) -> tuple[AESGCM, bytes]:
27
+ """Get the app secret using keyring.
28
+
29
+ If it does not exist, create it with a AESGCM.
30
+ """
31
+ return get_or_create_key(
32
+ service_name, username, AESGCM, lambda: AESGCM.generate_key(bit_length=256)
33
+ )
34
+
35
+
36
+ def get_or_create_key[T](
37
+ service_name: str,
38
+ username: str,
39
+ key_class: Callable[[bytes], T],
40
+ generate_key_func: Callable[..., bytes],
41
+ ) -> tuple[T, bytes]:
42
+ """Get the app secret using keyring.
43
+
44
+ If it does not exist, create it with the generate_func.
45
+ """
46
+ key = get_key_as_str(service_name, username, key_class)
47
+ if key is None:
48
+ binary_key = generate_key_func()
49
+ key = b64encode(binary_key).decode("ascii")
50
+ modified_service_name = make_service_name(service_name, key_class)
51
+ keyring.set_password(modified_service_name, username, key)
52
+
53
+ binary_key = b64decode(key)
54
+ return key_class(binary_key), binary_key
55
+
56
+
57
+ def get_key_as_str[T](
58
+ service_name: str, username: str, key_class: Callable[[bytes], T]
59
+ ) -> str | None:
60
+ """Get the app secret using keyring.
61
+
62
+ If it does not exist, create it with the generate_func.
63
+ """
64
+ service_name = make_service_name(service_name, key_class)
65
+ return keyring.get_password(service_name, username)
66
+
67
+
68
+ def make_service_name[T](service_name: str, key_class: Callable[[bytes], T]) -> str:
69
+ """Make a service name from a service name and a key class."""
70
+ return f"{service_name}_{key_class.__name__}"