hive-nectar 0.0.2__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 hive-nectar might be problematic. Click here for more details.

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