hive-nectar 0.2.9__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.
Files changed (87) hide show
  1. hive_nectar-0.2.9.dist-info/METADATA +194 -0
  2. hive_nectar-0.2.9.dist-info/RECORD +87 -0
  3. hive_nectar-0.2.9.dist-info/WHEEL +4 -0
  4. hive_nectar-0.2.9.dist-info/entry_points.txt +2 -0
  5. hive_nectar-0.2.9.dist-info/licenses/LICENSE.txt +23 -0
  6. nectar/__init__.py +37 -0
  7. nectar/account.py +5076 -0
  8. nectar/amount.py +553 -0
  9. nectar/asciichart.py +303 -0
  10. nectar/asset.py +122 -0
  11. nectar/block.py +574 -0
  12. nectar/blockchain.py +1242 -0
  13. nectar/blockchaininstance.py +2590 -0
  14. nectar/blockchainobject.py +263 -0
  15. nectar/cli.py +5937 -0
  16. nectar/comment.py +1552 -0
  17. nectar/community.py +854 -0
  18. nectar/constants.py +95 -0
  19. nectar/discussions.py +1437 -0
  20. nectar/exceptions.py +152 -0
  21. nectar/haf.py +381 -0
  22. nectar/hive.py +630 -0
  23. nectar/imageuploader.py +114 -0
  24. nectar/instance.py +113 -0
  25. nectar/market.py +876 -0
  26. nectar/memo.py +542 -0
  27. nectar/message.py +379 -0
  28. nectar/nodelist.py +309 -0
  29. nectar/price.py +603 -0
  30. nectar/profile.py +74 -0
  31. nectar/py.typed +0 -0
  32. nectar/rc.py +333 -0
  33. nectar/snapshot.py +1024 -0
  34. nectar/storage.py +62 -0
  35. nectar/transactionbuilder.py +659 -0
  36. nectar/utils.py +630 -0
  37. nectar/version.py +3 -0
  38. nectar/vote.py +722 -0
  39. nectar/wallet.py +472 -0
  40. nectar/witness.py +728 -0
  41. nectarapi/__init__.py +12 -0
  42. nectarapi/exceptions.py +126 -0
  43. nectarapi/graphenerpc.py +596 -0
  44. nectarapi/node.py +194 -0
  45. nectarapi/noderpc.py +79 -0
  46. nectarapi/openapi.py +107 -0
  47. nectarapi/py.typed +0 -0
  48. nectarapi/rpcutils.py +98 -0
  49. nectarapi/version.py +3 -0
  50. nectarbase/__init__.py +15 -0
  51. nectarbase/ledgertransactions.py +106 -0
  52. nectarbase/memo.py +242 -0
  53. nectarbase/objects.py +521 -0
  54. nectarbase/objecttypes.py +21 -0
  55. nectarbase/operationids.py +102 -0
  56. nectarbase/operations.py +1357 -0
  57. nectarbase/py.typed +0 -0
  58. nectarbase/signedtransactions.py +89 -0
  59. nectarbase/transactions.py +11 -0
  60. nectarbase/version.py +3 -0
  61. nectargraphenebase/__init__.py +27 -0
  62. nectargraphenebase/account.py +1121 -0
  63. nectargraphenebase/aes.py +49 -0
  64. nectargraphenebase/base58.py +197 -0
  65. nectargraphenebase/bip32.py +575 -0
  66. nectargraphenebase/bip38.py +110 -0
  67. nectargraphenebase/chains.py +15 -0
  68. nectargraphenebase/dictionary.py +2 -0
  69. nectargraphenebase/ecdsasig.py +309 -0
  70. nectargraphenebase/objects.py +130 -0
  71. nectargraphenebase/objecttypes.py +8 -0
  72. nectargraphenebase/operationids.py +5 -0
  73. nectargraphenebase/operations.py +25 -0
  74. nectargraphenebase/prefix.py +13 -0
  75. nectargraphenebase/py.typed +0 -0
  76. nectargraphenebase/signedtransactions.py +221 -0
  77. nectargraphenebase/types.py +557 -0
  78. nectargraphenebase/unsignedtransactions.py +288 -0
  79. nectargraphenebase/version.py +3 -0
  80. nectarstorage/__init__.py +57 -0
  81. nectarstorage/base.py +317 -0
  82. nectarstorage/exceptions.py +15 -0
  83. nectarstorage/interfaces.py +244 -0
  84. nectarstorage/masterpassword.py +237 -0
  85. nectarstorage/py.typed +0 -0
  86. nectarstorage/ram.py +27 -0
  87. nectarstorage/sqlite.py +343 -0
@@ -0,0 +1,343 @@
1
+ # Inspired by https://raw.githubusercontent.com/xeroc/python-graphenelib/master/graphenestorage/sqlite.py
2
+ import logging
3
+ import shutil
4
+ import sqlite3
5
+ import time
6
+ from datetime import datetime, timezone
7
+ from pathlib import Path
8
+ from typing import Any, Optional, Tuple, Union
9
+
10
+ from appdirs import user_data_dir
11
+
12
+ from .interfaces import StoreInterface
13
+
14
+ log = logging.getLogger(__name__)
15
+ timeformat = "%Y%m%d-%H%M%S"
16
+
17
+
18
+ class SQLiteFile:
19
+ """This class ensures that the user's data is stored in its OS
20
+ preotected user directory:
21
+
22
+ **OSX:**
23
+
24
+ * `~/Library/Application Support/<AppName>`
25
+
26
+ **Windows:**
27
+
28
+ * `C:\\Documents and Settings\\<User>\\Application Data\\Local Settings\\<AppAuthor>\\<AppName>`
29
+ * `C:\\Documents and Settings\\<User>\\Application Data\\<AppAuthor>\\<AppName>`
30
+
31
+ **Linux:**
32
+
33
+ * `~/.local/share/<AppName>`
34
+
35
+ Furthermore, it offers an interface to generated backups
36
+ in the `backups/` directory every now and then.
37
+
38
+ .. note:: The file name can be overwritten when providing a keyword
39
+ argument ``profile``.
40
+ """
41
+
42
+ data_dir: Path
43
+ storageDatabase: str
44
+ sqlite_file: Path
45
+
46
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
47
+ appauthor = "nectar"
48
+ appname = kwargs.get("appname", "nectar")
49
+ self.data_dir = Path(kwargs.get("data_dir", user_data_dir(appname, appauthor)))
50
+
51
+ if "profile" in kwargs:
52
+ self.storageDatabase = f"{kwargs['profile']}.sqlite"
53
+ else:
54
+ self.storageDatabase = f"{appname}.sqlite"
55
+
56
+ self.sqlite_file = self.data_dir / self.storageDatabase
57
+
58
+ """ Ensure that the directory in which the data is stored
59
+ exists
60
+ """
61
+ if self.data_dir.is_dir(): # pragma: no cover
62
+ return
63
+ else: # pragma: no cover
64
+ self.data_dir.mkdir(parents=True)
65
+
66
+ def sqlite3_backup(self, backupdir: Union[str, Path]) -> None:
67
+ """Create timestamped database copy"""
68
+ backup_path = Path(backupdir)
69
+ if not backup_path.is_dir():
70
+ backup_path.mkdir()
71
+ backup_file = backup_path / (
72
+ f"{Path(self.storageDatabase).stem}-{datetime.now(timezone.utc).strftime(timeformat)}"
73
+ )
74
+ self.sqlite3_copy(self.sqlite_file, backup_file)
75
+
76
+ def sqlite3_copy(self, src: Path, dst: Path) -> None:
77
+ """Copy sql file from src to dst"""
78
+ if not src.is_file():
79
+ return
80
+ connection = sqlite3.connect(str(self.sqlite_file))
81
+ try:
82
+ cursor = connection.cursor()
83
+ # Lock database before making a backup
84
+ cursor.execute("begin immediate")
85
+ # Make new backup file
86
+ shutil.copyfile(str(src), str(dst))
87
+ log.info(f"Creating {dst}...")
88
+ # Unlock database
89
+ connection.rollback()
90
+ finally:
91
+ connection.close()
92
+
93
+ def recover_with_latest_backup(self, backupdir: Union[str, Path] = "backups") -> None:
94
+ """Replace database with latest backup"""
95
+ file_date = 0
96
+ backup_path = Path(backupdir)
97
+ if not backup_path.is_dir():
98
+ # Treat string backupdir as relative to data_dir
99
+ backup_path = self.data_dir / str(backupdir)
100
+ if not backup_path.is_dir():
101
+ return
102
+ newest_backup_file = None
103
+ for backup_file in backup_path.iterdir():
104
+ if backup_file.stat().st_ctime > file_date:
105
+ if backup_file.is_file():
106
+ file_date = backup_file.stat().st_ctime
107
+ newest_backup_file = backup_file
108
+ if newest_backup_file is not None:
109
+ self.sqlite3_copy(newest_backup_file, self.sqlite_file)
110
+
111
+ def clean_data(self, backupdir: Union[str, Path] = "backups") -> None:
112
+ """Delete files older than 70 days"""
113
+ log.info("Cleaning up old backups")
114
+ # Allow either a Path or a directory name relative to data_dir
115
+ backup_path = Path(backupdir)
116
+ if not backup_path.is_dir():
117
+ backup_path = self.data_dir / str(backupdir)
118
+ if not backup_path.is_dir():
119
+ return
120
+ for backup_file in backup_path.iterdir():
121
+ if backup_file.stat().st_ctime < (time.time() - 70 * 86400):
122
+ if backup_file.is_file():
123
+ backup_file.unlink()
124
+ log.info(f"Deleting {backup_file}...")
125
+
126
+ def refreshBackup(self) -> None:
127
+ """Make a new backup"""
128
+ backupdir = self.data_dir / "backups"
129
+ self.sqlite3_backup(backupdir)
130
+ # Clean by logical name so clean_data resolves under data_dir correctly
131
+ self.clean_data("backups")
132
+
133
+
134
+ class SQLiteCommon:
135
+ """This class abstracts away common sqlite3 operations.
136
+
137
+ This class should not be used directly.
138
+
139
+ When inheriting from this class, the following instance members must
140
+ be defined:
141
+
142
+ * ``sqlite_file``: Path to the SQLite Database file
143
+ """
144
+
145
+ sqlite_file: Path
146
+
147
+ def sql_fetchone(self, query: Tuple[str, Tuple]) -> Optional[Tuple]:
148
+ connection = sqlite3.connect(str(self.sqlite_file))
149
+ try:
150
+ cursor = connection.cursor()
151
+ cursor.execute(*query)
152
+ result = cursor.fetchone()
153
+ finally:
154
+ connection.close()
155
+ return result
156
+
157
+ def sql_fetchall(self, query: Tuple[str, Tuple]) -> list:
158
+ connection = sqlite3.connect(str(self.sqlite_file))
159
+ try:
160
+ cursor = connection.cursor()
161
+ cursor.execute(*query)
162
+ results = cursor.fetchall()
163
+ finally:
164
+ connection.close()
165
+ return results
166
+
167
+ def sql_execute(self, query: Tuple[str, Tuple], lastid: bool = False) -> Optional[int]:
168
+ connection = sqlite3.connect(str(self.sqlite_file))
169
+ try:
170
+ cursor = connection.cursor()
171
+ cursor.execute(*query)
172
+ connection.commit()
173
+ except Exception:
174
+ connection.close()
175
+ raise
176
+ ret = None
177
+ try:
178
+ if lastid:
179
+ cursor = connection.cursor()
180
+ cursor.execute("SELECT last_insert_rowid();")
181
+ ret = cursor.fetchone()[0]
182
+ finally:
183
+ connection.close()
184
+ return ret
185
+
186
+
187
+ class SQLiteStore(SQLiteFile, SQLiteCommon, StoreInterface):
188
+ """The SQLiteStore deals with the sqlite3 part of storing data into a
189
+ database file.
190
+
191
+ .. note:: This module is limited to two columns and merely stores
192
+ key/value pairs into the sqlite database
193
+
194
+ On first launch, the database file as well as the tables are created
195
+ automatically.
196
+
197
+ When inheriting from this class, the following three class members must
198
+ be defined:
199
+
200
+ * ``__tablename__``: Name of the table
201
+ * ``__key__``: Name of the key column
202
+ * ``__value__``: Name of the value column
203
+ """
204
+
205
+ #:
206
+ __tablename__ = None
207
+ __key__ = None
208
+ __value__ = None
209
+
210
+ def __init__(self, *args, **kwargs):
211
+ #: Storage
212
+ SQLiteFile.__init__(self, *args, **kwargs)
213
+ StoreInterface.__init__(self, *args, **kwargs)
214
+ if self.__tablename__ is None or self.__key__ is None or self.__value__ is None:
215
+ raise ValueError("Values missing for tablename, key, or value!")
216
+ if not self.exists(): # pragma: no cover
217
+ self.create()
218
+
219
+ def _haveKey(self, key: str) -> bool:
220
+ """Is the key `key` available?"""
221
+ query = (
222
+ f"SELECT {self.__value__} FROM {self.__tablename__} WHERE {self.__key__}=?",
223
+ (key,),
224
+ )
225
+ return True if self.sql_fetchone(query) else False
226
+
227
+ def __setitem__(self, key: str, value: str) -> None:
228
+ """Sets an item in the store
229
+
230
+ :param str key: Key
231
+ :param str value: Value
232
+ """
233
+ if self._haveKey(key):
234
+ query = (
235
+ f"UPDATE {self.__tablename__} SET {self.__value__}=? WHERE {self.__key__}=?",
236
+ (value, key),
237
+ )
238
+ else:
239
+ query = (
240
+ f"INSERT INTO {self.__tablename__} ({self.__key__}, {self.__value__}) VALUES (?, ?)",
241
+ (key, value),
242
+ )
243
+ self.sql_execute(query)
244
+
245
+ def __getitem__(self, key: str) -> Optional[str]:
246
+ """Gets an item from the store as if it was a dictionary
247
+
248
+ :param str value: Value
249
+ """
250
+ query = (
251
+ f"SELECT {self.__value__} FROM {self.__tablename__} WHERE {self.__key__}=?",
252
+ (key,),
253
+ )
254
+ result = self.sql_fetchone(query)
255
+ if result:
256
+ return result[0]
257
+ else:
258
+ if key in self.defaults:
259
+ return self.defaults[key]
260
+ else:
261
+ return None
262
+
263
+ def __iter__(self):
264
+ """Iterates through the store"""
265
+ return iter(self.keys())
266
+
267
+ def keys(self):
268
+ query = (f"SELECT {self.__key__} from {self.__tablename__}", ())
269
+ key_list = [x[0] for x in self.sql_fetchall(query)]
270
+ return dict.fromkeys(key_list).keys()
271
+
272
+ def __len__(self) -> int:
273
+ """return lenght of store"""
274
+ query = (f"SELECT id from {self.__tablename__}", ())
275
+ return len(self.sql_fetchall(query))
276
+
277
+ def __contains__(self, key: object) -> bool:
278
+ """Tests if a key is contained in the store.
279
+
280
+ May test againsts self.defaults
281
+
282
+ :param str value: Value
283
+ """
284
+ key_str = str(key)
285
+ if self._haveKey(key_str) or key_str in self.defaults:
286
+ return True
287
+ else:
288
+ return False
289
+
290
+ def items(self):
291
+ """returns all items off the store as tuples"""
292
+ query = (f"SELECT {self.__key__}, {self.__value__} from {self.__tablename__}", ())
293
+ collected = {key: value for key, value in self.sql_fetchall(query)}
294
+ return collected.items()
295
+
296
+ def get(self, key: str, default: Any = None) -> Any:
297
+ """Return the key if exists or a default value
298
+
299
+ :param str value: Value
300
+ :param str default: Default value if key not present
301
+ """
302
+ if key in self:
303
+ return self.__getitem__(key)
304
+ else:
305
+ return default
306
+
307
+ # Specific for this library
308
+ def delete(self, key: str) -> None:
309
+ """Delete a key from the store
310
+
311
+ :param str value: Value
312
+ """
313
+ query = (
314
+ f"DELETE FROM {self.__tablename__} WHERE {self.__key__}=?",
315
+ (key,),
316
+ )
317
+ self.sql_execute(query)
318
+
319
+ def wipe(self) -> None:
320
+ """Wipe the store"""
321
+ query = (f"DELETE FROM {self.__tablename__}", ())
322
+ self.sql_execute(query)
323
+
324
+ def exists(self) -> bool:
325
+ """Check if the database table exists"""
326
+ query = (
327
+ "SELECT name FROM sqlite_master WHERE type='table' AND name=?",
328
+ (self.__tablename__,),
329
+ )
330
+ return True if self.sql_fetchone(query) else False
331
+
332
+ def create(self) -> None: # pragma: no cover
333
+ """Create the new table in the SQLite database"""
334
+ query = (
335
+ f"""
336
+ CREATE TABLE {self.__tablename__} (
337
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
338
+ {self.__key__} STRING(256),
339
+ {self.__value__} STRING(256)
340
+ )""",
341
+ (),
342
+ )
343
+ self.sql_execute(query)