dictature 0.9.7__tar.gz → 0.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {dictature-0.9.7/src/dictature.egg-info → dictature-0.10.0}/PKG-INFO +2 -1
  2. {dictature-0.9.7 → dictature-0.10.0}/README.md +1 -0
  3. {dictature-0.9.7 → dictature-0.10.0}/pyproject.toml +1 -1
  4. dictature-0.10.0/src/dictature/backend/webdav.py +141 -0
  5. {dictature-0.9.7 → dictature-0.10.0/src/dictature.egg-info}/PKG-INFO +2 -1
  6. {dictature-0.9.7 → dictature-0.10.0}/src/dictature.egg-info/SOURCES.txt +1 -0
  7. {dictature-0.9.7 → dictature-0.10.0}/LICENSE +0 -0
  8. {dictature-0.9.7 → dictature-0.10.0}/setup.cfg +0 -0
  9. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/__init__.py +0 -0
  10. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/backend/__init__.py +0 -0
  11. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/backend/directory.py +0 -0
  12. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/backend/misp.py +0 -0
  13. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/backend/mock.py +0 -0
  14. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/backend/sqlite.py +0 -0
  15. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/dictature.py +0 -0
  16. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/transformer/__init__.py +0 -0
  17. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/transformer/aes.py +0 -0
  18. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/transformer/gzip.py +0 -0
  19. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/transformer/hmac.py +0 -0
  20. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/transformer/mock.py +0 -0
  21. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/transformer/passthrough.py +0 -0
  22. {dictature-0.9.7 → dictature-0.10.0}/src/dictature/transformer/pipeline.py +0 -0
  23. {dictature-0.9.7 → dictature-0.10.0}/src/dictature.egg-info/dependency_links.txt +0 -0
  24. {dictature-0.9.7 → dictature-0.10.0}/src/dictature.egg-info/top_level.txt +0 -0
  25. {dictature-0.9.7 → dictature-0.10.0}/tests/test_operations.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dictature
3
- Version: 0.9.7
3
+ Version: 0.10.0
4
4
  Summary: dictature -- A generic wrapper around dict-like interface with mulitple backends
5
5
  Author-email: Adam Hlavacek <git@adamhlavacek.com>
6
6
  Project-URL: Homepage, https://github.com/esoadamo/dictature
@@ -54,6 +54,7 @@ Currently, the following backends are supported:
54
54
  - `DictatureBackendDirectory`: stores the data in a directory as json files
55
55
  - `DictatureBackendSQLite`: stores the data in a SQLite database
56
56
  - `DictatureBackendMISP`: stores the data in a MISP instance
57
+ - `DictatureBackendWebdav`: stores data in a WebDav share as files
57
58
 
58
59
  ### Transformers
59
60
 
@@ -41,6 +41,7 @@ Currently, the following backends are supported:
41
41
  - `DictatureBackendDirectory`: stores the data in a directory as json files
42
42
  - `DictatureBackendSQLite`: stores the data in a SQLite database
43
43
  - `DictatureBackendMISP`: stores the data in a MISP instance
44
+ - `DictatureBackendWebdav`: stores data in a WebDav share as files
44
45
 
45
46
  ### Transformers
46
47
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dictature"
7
- version = "0.9.7"
7
+ version = "0.10.0"
8
8
  description = "dictature -- A generic wrapper around dict-like interface with mulitple backends"
9
9
  authors = [
10
10
  { name = "Adam Hlavacek", email = "git@adamhlavacek.com" }
@@ -0,0 +1,141 @@
1
+ import io
2
+ import posixpath
3
+ from typing import Iterable
4
+
5
+ try:
6
+ from webdav3.client import Client as WebdavClient
7
+ from webdav3.exceptions import (
8
+ WebDavException,
9
+ RemoteResourceNotFound,
10
+ RemoteParentNotFound,
11
+ MethodNotSupported,
12
+ )
13
+ except ImportError:
14
+ raise ImportError('Requires: pip install webdavclient3')
15
+
16
+ from .mock import DictatureTableMock, DictatureBackendMock, Value, ValueMode, ValueSerializer, ValueSerializerMode
17
+
18
+
19
+ class DictatureBackendWebdav(DictatureBackendMock):
20
+ def __init__(
21
+ self,
22
+ client: WebdavClient,
23
+ dir_prefix: str = 'db_',
24
+ item_prefix: str = 'item_'
25
+ ) -> None:
26
+ self.__client = client
27
+ self.__dir_prefix = dir_prefix
28
+ self.__item_prefix = item_prefix
29
+
30
+ def keys(self) -> Iterable[str]:
31
+ try:
32
+ resources = self.__client.list(get_info=True)
33
+
34
+ for resource_info in resources:
35
+ name = posixpath.basename(posixpath.dirname(resource_info.get('path')))
36
+ is_dir = resource_info.get('isdir', False)
37
+
38
+ if not name:
39
+ continue
40
+
41
+ if is_dir and name.startswith(self.__dir_prefix):
42
+ table_name_encoded = name[len(self.__dir_prefix):]
43
+ # noinspection PyProtectedMember
44
+ yield DictatureTableWebdav._filename_decode(table_name_encoded, suffix='')
45
+ except RemoteResourceNotFound:
46
+ pass
47
+
48
+ def table(self, name: str) -> 'DictatureTableMock':
49
+ return DictatureTableWebdav(self.__client, name, self.__dir_prefix, self.__item_prefix)
50
+
51
+
52
+ class DictatureTableWebdav(DictatureTableMock):
53
+ def __init__(self, client: WebdavClient, name: str, db_prefix: str, prefix: str) -> None:
54
+ self.__client = client
55
+ self.__encoded_name = self._filename_encode(name, suffix='')
56
+ self.__path = posixpath.join(db_prefix + self.__encoded_name)
57
+ self.__prefix = prefix
58
+ self.__name_serializer = ValueSerializer(mode=ValueSerializerMode.filename_only)
59
+ self.__value_serializer = ValueSerializer(mode=ValueSerializerMode.any_string)
60
+
61
+ def keys(self) -> Iterable[str]:
62
+ try:
63
+ resources = self.__client.list(self.__path, get_info=True)
64
+
65
+ for resource_info in resources:
66
+ name = posixpath.basename(resource_info.get('path'))
67
+ is_dir = resource_info.get('isdir', False)
68
+
69
+ if not name:
70
+ continue
71
+
72
+ if not is_dir and name.startswith(self.__prefix):
73
+ item_name_encoded = name[len(self.__prefix):]
74
+ yield self._filename_decode(item_name_encoded, suffix='.txt')
75
+
76
+ except RemoteResourceNotFound:
77
+ pass
78
+
79
+ def drop(self) -> None:
80
+ try:
81
+ self.__client.clean(self.__path)
82
+ except RemoteResourceNotFound:
83
+ pass
84
+
85
+ def create(self) -> None:
86
+ try:
87
+ if not self.__client.check(self.__path):
88
+ self.__client.mkdir(self.__path)
89
+ except MethodNotSupported as e:
90
+ if not self.__client.check(self.__path):
91
+ raise e
92
+
93
+ def set(self, item: str, value: Value) -> None:
94
+ item_path = self.__item_path(item)
95
+ self.create()
96
+
97
+ save_data: str = self.__value_serializer.serialize(value)
98
+ data_bytes = save_data.encode('utf-8')
99
+
100
+ with io.BytesIO(data_bytes) as buffer:
101
+ self.__client.upload_to(buffer, item_path)
102
+
103
+ def get(self, item: str) -> Value:
104
+ item_path = self.__item_path(item)
105
+ buffer = io.BytesIO()
106
+ try:
107
+ self.__client.download_from(buffer, item_path)
108
+ buffer.seek(0)
109
+ save_data = buffer.read().decode('utf-8')
110
+ except RemoteResourceNotFound:
111
+ raise KeyError(item)
112
+ return self.__value_serializer.deserialize(save_data)
113
+
114
+ def delete(self, item: str) -> None:
115
+ """
116
+ Deletes the file corresponding to the item. No error if not found.
117
+ Raises WebDavException for other deletion errors.
118
+ """
119
+ item_path = self.__item_path(item)
120
+ try:
121
+ self.__client.clean(item_path)
122
+ except RemoteResourceNotFound:
123
+ pass
124
+
125
+ def __item_path(self, item: str) -> str:
126
+ encoded_item_name = self.__prefix + self._filename_encode(item, suffix='.txt')
127
+ full_path = posixpath.join(self.__path, encoded_item_name)
128
+ return full_path
129
+
130
+ @staticmethod
131
+ def _filename_encode(name: str, suffix: str = '.txt') -> str:
132
+ return ValueSerializer(mode=ValueSerializerMode.filename_only).serialize(Value(
133
+ value=name,
134
+ mode=ValueMode.string.value
135
+ )) + suffix
136
+
137
+ @staticmethod
138
+ def _filename_decode(name: str, suffix: str = '.txt') -> str:
139
+ if suffix and name.endswith(suffix):
140
+ name = name[:-len(suffix)]
141
+ return ValueSerializer(mode=ValueSerializerMode.filename_only).deserialize(name).value
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dictature
3
- Version: 0.9.7
3
+ Version: 0.10.0
4
4
  Summary: dictature -- A generic wrapper around dict-like interface with mulitple backends
5
5
  Author-email: Adam Hlavacek <git@adamhlavacek.com>
6
6
  Project-URL: Homepage, https://github.com/esoadamo/dictature
@@ -54,6 +54,7 @@ Currently, the following backends are supported:
54
54
  - `DictatureBackendDirectory`: stores the data in a directory as json files
55
55
  - `DictatureBackendSQLite`: stores the data in a SQLite database
56
56
  - `DictatureBackendMISP`: stores the data in a MISP instance
57
+ - `DictatureBackendWebdav`: stores data in a WebDav share as files
57
58
 
58
59
  ### Transformers
59
60
 
@@ -12,6 +12,7 @@ src/dictature/backend/directory.py
12
12
  src/dictature/backend/misp.py
13
13
  src/dictature/backend/mock.py
14
14
  src/dictature/backend/sqlite.py
15
+ src/dictature/backend/webdav.py
15
16
  src/dictature/transformer/__init__.py
16
17
  src/dictature/transformer/aes.py
17
18
  src/dictature/transformer/gzip.py
File without changes
File without changes