pyLodStorage 0.0.7__tar.gz → 0.0.9__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.
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/PKG-INFO +2 -1
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/README.md +1 -0
- pylodstorage-0.0.9/lodstorage/jsonable.py +198 -0
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/lodstorage/sample.py +9 -1
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/lodstorage/sql.py +9 -0
- pylodstorage-0.0.9/lodstorage/types.py +108 -0
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/pylodstorage.egg-info/PKG-INFO +2 -1
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/pylodstorage.egg-info/SOURCES.txt +2 -0
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/setup.py +1 -1
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/lodstorage/__init__.py +0 -0
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/lodstorage/uml.py +0 -0
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/pylodstorage.egg-info/dependency_links.txt +0 -0
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/pylodstorage.egg-info/top_level.txt +0 -0
- {pylodstorage-0.0.7 → pylodstorage-0.0.9}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pylodstorage
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
4
4
|
Summary: python List of Dict (Table) Storage library
|
|
5
5
|
Home-page: https://github.com/WolfgangFahl/pyLodStorage
|
|
6
6
|
Author: Wolfgang Fahl
|
|
@@ -14,6 +14,7 @@ Description: # pyLoDStorage
|
|
|
14
14
|
python List of Dict (Table) Storage library
|
|
15
15
|
|
|
16
16
|
[](https://pypi.org/project/pyLoDStorage/)
|
|
17
|
+
[](https://pypi.python.org/pypi/pyLoDStorage/)
|
|
17
18
|
[](https://github.com/WolfgangFahl/pyLoDStorage/actions?query=workflow%3ABuild+branch%3Amaster)
|
|
18
19
|
[](https://github.com/WolfgangFahl/pyLoDStorage/issues)
|
|
19
20
|
[](https://github.com/WolfgangFahl/pyLoDStorage/issues/?q=is%3Aissue+is%3Aclosed)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
python List of Dict (Table) Storage library
|
|
3
3
|
|
|
4
4
|
[](https://pypi.org/project/pyLoDStorage/)
|
|
5
|
+
[](https://pypi.python.org/pypi/pyLoDStorage/)
|
|
5
6
|
[](https://github.com/WolfgangFahl/pyLoDStorage/actions?query=workflow%3ABuild+branch%3Amaster)
|
|
6
7
|
[](https://github.com/WolfgangFahl/pyLoDStorage/issues)
|
|
7
8
|
[](https://github.com/WolfgangFahl/pyLoDStorage/issues/?q=is%3Aissue+is%3Aclosed)
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
'''This module has a class JSONAble for serialization of tables/list of dicts to and from JSON encoding
|
|
2
|
+
|
|
3
|
+
Created on 2020-09-03
|
|
4
|
+
|
|
5
|
+
@author: wf
|
|
6
|
+
'''
|
|
7
|
+
import json
|
|
8
|
+
import datetime
|
|
9
|
+
import re
|
|
10
|
+
|
|
11
|
+
class JSONAble(object):
|
|
12
|
+
'''
|
|
13
|
+
mixin to allow classes to be JSON serializable see
|
|
14
|
+
|
|
15
|
+
- https://stackoverflow.com/questions/3768895/how-to-make-a-class-json-serializable
|
|
16
|
+
|
|
17
|
+
'''
|
|
18
|
+
indent=4
|
|
19
|
+
|
|
20
|
+
'''
|
|
21
|
+
regular expression to be used for conversion from singleQuote to doubleQuote
|
|
22
|
+
see https://stackoverflow.com/a/50257217/1497139
|
|
23
|
+
'''
|
|
24
|
+
singleQuoteRegex = re.compile('(?<!\\\\)\'')
|
|
25
|
+
|
|
26
|
+
def __init__(self):
|
|
27
|
+
'''
|
|
28
|
+
Constructor
|
|
29
|
+
'''
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def singleQuoteToDoubleQuote(singleQuoted,useRegex=False):
|
|
33
|
+
'''
|
|
34
|
+
convert a single quoted string to a double quoted one
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
singleQuoted (str): a single quoted string e.g.
|
|
38
|
+
|
|
39
|
+
.. highlight:: json
|
|
40
|
+
|
|
41
|
+
{'cities': [{'name': "Upper Hell's Gate"}]}
|
|
42
|
+
|
|
43
|
+
useRegex (boolean): True if a regular expression shall be used for matching
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
string: the double quoted version of the string
|
|
47
|
+
|
|
48
|
+
Note:
|
|
49
|
+
see
|
|
50
|
+
- https://stackoverflow.com/questions/55600788/python-replace-single-quotes-with-double-quotes-but-leave-ones-within-double-q
|
|
51
|
+
|
|
52
|
+
'''
|
|
53
|
+
if useRegex:
|
|
54
|
+
doubleQuoted=JSONAble.singleQuoteToDoubleQuoteUsingRegex(singleQuoted)
|
|
55
|
+
else:
|
|
56
|
+
doubleQuoted=JSONAble.singleQuoteToDoubleQuoteUsingBracketLoop(singleQuoted)
|
|
57
|
+
return doubleQuoted
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def singleQuoteToDoubleQuoteUsingRegex(singleQuoted):
|
|
61
|
+
"""
|
|
62
|
+
convert a single quoted string to a double quoted one using a regular expression
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
singleQuoted(string): a single quoted string e.g. {'cities': [{'name': "Upper Hell's Gate"}]}
|
|
66
|
+
useRegex(boolean): True if a regular expression shall be used for matching
|
|
67
|
+
Returns:
|
|
68
|
+
string: the double quoted version of the string e.g.
|
|
69
|
+
Note:
|
|
70
|
+
see https://stackoverflow.com/a/50257217/1497139
|
|
71
|
+
"""
|
|
72
|
+
doubleQuoted=JSONAble.singleQuoteRegex.sub('\"', singleQuoted)
|
|
73
|
+
return doubleQuoted
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def singleQuoteToDoubleQuoteUsingBracketLoop(singleQuoted):
|
|
77
|
+
"""
|
|
78
|
+
convert a single quoted string to a double quoted one using a regular expression
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
singleQuoted(string): a single quoted string e.g. {'cities': [{'name': "Upper Hell's Gate"}]}
|
|
82
|
+
useRegex(boolean): True if a regular expression shall be used for matching
|
|
83
|
+
Returns:
|
|
84
|
+
string: the double quoted version of the string e.g.
|
|
85
|
+
Note:
|
|
86
|
+
see https://stackoverflow.com/a/63862387/1497139
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
cList=list(singleQuoted)
|
|
90
|
+
inDouble=False;
|
|
91
|
+
inSingle=False;
|
|
92
|
+
for i,c in enumerate(cList):
|
|
93
|
+
#print ("%d:%s %r %r" %(i,c,inSingle,inDouble))
|
|
94
|
+
if c=="'":
|
|
95
|
+
if not inDouble:
|
|
96
|
+
inSingle=not inSingle
|
|
97
|
+
cList[i]='"'
|
|
98
|
+
elif c=='"':
|
|
99
|
+
inDouble=not inDouble
|
|
100
|
+
inSingle=False
|
|
101
|
+
doubleQuoted="".join(cList)
|
|
102
|
+
return doubleQuoted
|
|
103
|
+
|
|
104
|
+
def fromJson(self,jsonStr,types=None):
|
|
105
|
+
'''
|
|
106
|
+
initialize me from the given JSON string
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
jsonStr(str): the JSON string
|
|
110
|
+
fixType(Types): the types to be fixed
|
|
111
|
+
'''
|
|
112
|
+
data=json.loads(jsonStr)
|
|
113
|
+
if types is not None:
|
|
114
|
+
types.fixTypes(data)
|
|
115
|
+
self.__dict__=data
|
|
116
|
+
|
|
117
|
+
def toJsonAbleValue(self,v):
|
|
118
|
+
'''
|
|
119
|
+
return the JSON able value of the given value v
|
|
120
|
+
Args:
|
|
121
|
+
v(object): the value to convert
|
|
122
|
+
'''
|
|
123
|
+
# objects have __dict__ hash tables which can be JSON-converted
|
|
124
|
+
if (hasattr(v, "__dict__")):
|
|
125
|
+
return v.__dict__
|
|
126
|
+
elif isinstance(v,datetime.datetime):
|
|
127
|
+
return v.isoformat()
|
|
128
|
+
elif isinstance(v,datetime.date):
|
|
129
|
+
return v.isoformat()
|
|
130
|
+
else:
|
|
131
|
+
return ""
|
|
132
|
+
|
|
133
|
+
def toJSON(self):
|
|
134
|
+
'''
|
|
135
|
+
Returns:
|
|
136
|
+
a recursive JSON dump of the dicts of my objects
|
|
137
|
+
'''
|
|
138
|
+
jsonStr=json.dumps(self, default=lambda v: self.toJsonAbleValue(v),
|
|
139
|
+
sort_keys=True, indent=JSONAble.indent)
|
|
140
|
+
return jsonStr
|
|
141
|
+
|
|
142
|
+
def getJSONValue(self,v):
|
|
143
|
+
'''
|
|
144
|
+
get the value of the given v as JSON
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
v(object): the value to get
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
the the value making sure objects are return as dicts
|
|
151
|
+
'''
|
|
152
|
+
if (hasattr(v, "asJSON")):
|
|
153
|
+
return v.asJSON(asString=False)
|
|
154
|
+
elif type(v) is dict:
|
|
155
|
+
return self.reprDict(v)
|
|
156
|
+
elif type(v) is list:
|
|
157
|
+
vlist=[]
|
|
158
|
+
for vitem in v:
|
|
159
|
+
vlist.append(self.getJSONValue(vitem))
|
|
160
|
+
return vlist
|
|
161
|
+
elif isinstance(v,datetime.datetime):
|
|
162
|
+
return v.isoformat()
|
|
163
|
+
elif isinstance(v,datetime.date):
|
|
164
|
+
return v.isoformat()
|
|
165
|
+
elif isinstance(v,bool):
|
|
166
|
+
# convert True,False to -> true,false
|
|
167
|
+
return str(v).lower()
|
|
168
|
+
else:
|
|
169
|
+
return v
|
|
170
|
+
|
|
171
|
+
def reprDict(self,srcDict):
|
|
172
|
+
'''
|
|
173
|
+
get the given srcDict as new dict with fields being converted with getJSONValue
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
scrcDict(dict): the source dictionary
|
|
177
|
+
|
|
178
|
+
Returns
|
|
179
|
+
dict: the converted dictionary
|
|
180
|
+
'''
|
|
181
|
+
d = dict()
|
|
182
|
+
for a, v in srcDict.items():
|
|
183
|
+
d[a]=self.getJSONValue(v)
|
|
184
|
+
return d
|
|
185
|
+
|
|
186
|
+
def asJSON(self,asString=True):
|
|
187
|
+
'''
|
|
188
|
+
recursively return my dict elements
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
asString(boolean): if True return my result as a string
|
|
192
|
+
'''
|
|
193
|
+
jsonDict=self.reprDict(self.__dict__)
|
|
194
|
+
if asString:
|
|
195
|
+
jsonStr=str(jsonDict)
|
|
196
|
+
jsonStr = JSONAble.singleQuoteToDoubleQuote(jsonStr)
|
|
197
|
+
return jsonStr
|
|
198
|
+
return jsonDict
|
|
@@ -5,6 +5,7 @@ Created on 2020-08-24
|
|
|
5
5
|
'''
|
|
6
6
|
import urllib.request
|
|
7
7
|
import json
|
|
8
|
+
from lodstorage.jsonable import JSONAble
|
|
8
9
|
from datetime import date, datetime
|
|
9
10
|
|
|
10
11
|
class Sample(object):
|
|
@@ -68,4 +69,11 @@ class Sample(object):
|
|
|
68
69
|
person['ofAge']=age>=18
|
|
69
70
|
person['lastmodified']=datetime.now()
|
|
70
71
|
return listOfDicts
|
|
71
|
-
|
|
72
|
+
|
|
73
|
+
class Royals(JSONAble):
|
|
74
|
+
def __init__(self):
|
|
75
|
+
self.royals=Sample.getRoyals()
|
|
76
|
+
|
|
77
|
+
class Cities(JSONAble):
|
|
78
|
+
def __init__(self):
|
|
79
|
+
self.cities=Sample.getCities()
|
|
@@ -82,8 +82,13 @@ class SQLDB(object):
|
|
|
82
82
|
'''
|
|
83
83
|
debugInfo=""
|
|
84
84
|
if not executeMany:
|
|
85
|
+
# shall we shoe the details of the record (which might be a security risk)
|
|
85
86
|
if self.errorDebug:
|
|
87
|
+
# show details of record
|
|
86
88
|
debugInfo="\nrecord #%d=%s" % (index,repr(record))
|
|
89
|
+
else:
|
|
90
|
+
# show only index
|
|
91
|
+
debugInfo="\nrecord #%d" % index
|
|
87
92
|
return debugInfo
|
|
88
93
|
|
|
89
94
|
|
|
@@ -124,6 +129,10 @@ class SQLDB(object):
|
|
|
124
129
|
raise Exception("%s\nfailed: error binding column '%s'%s" % (insertCmd,columnName,debugInfo))
|
|
125
130
|
else:
|
|
126
131
|
raise ie
|
|
132
|
+
except Exception as ex:
|
|
133
|
+
debugInfo=self.getDebugInfo(record, index, executeMany)
|
|
134
|
+
msg="%s\nfailed:%s%s" % (insertCmd,str(ex),debugInfo)
|
|
135
|
+
raise Exception(msg)
|
|
127
136
|
|
|
128
137
|
def query(self,sqlQuery):
|
|
129
138
|
'''
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Created on 2020-09-12
|
|
3
|
+
|
|
4
|
+
@author: wf
|
|
5
|
+
'''
|
|
6
|
+
import datetime
|
|
7
|
+
import sys
|
|
8
|
+
class Types(object):
|
|
9
|
+
'''
|
|
10
|
+
Types
|
|
11
|
+
|
|
12
|
+
holds entity meta Info
|
|
13
|
+
|
|
14
|
+
:ivar name(string): entity name = table name
|
|
15
|
+
'''
|
|
16
|
+
|
|
17
|
+
def __init__(self,name):
|
|
18
|
+
'''
|
|
19
|
+
Constructor
|
|
20
|
+
'''
|
|
21
|
+
self.name=name
|
|
22
|
+
self.typeMap={}
|
|
23
|
+
|
|
24
|
+
def addType(self,listName,field,valueType):
|
|
25
|
+
'''
|
|
26
|
+
add the python type for the given field to the typeMap
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
listName(string): the name of the list of the field
|
|
30
|
+
field(string): the name of the field
|
|
31
|
+
|
|
32
|
+
valueType(type): the python type of the field
|
|
33
|
+
'''
|
|
34
|
+
if listName not in self.typeMap:
|
|
35
|
+
self.typeMap[listName]={}
|
|
36
|
+
typeMap=self.typeMap[listName]
|
|
37
|
+
if not field in typeMap:
|
|
38
|
+
typeMap[field]=valueType
|
|
39
|
+
|
|
40
|
+
def getTypes(self,listName,sampleRecords,limit=10):
|
|
41
|
+
'''
|
|
42
|
+
determine the types for the given sample records
|
|
43
|
+
'''
|
|
44
|
+
for sampleRecord in sampleRecords[:limit]:
|
|
45
|
+
for key,value in sampleRecord.items():
|
|
46
|
+
valueType=None
|
|
47
|
+
if value is None:
|
|
48
|
+
if len(sampleRecords)==1:
|
|
49
|
+
print("Warning sampleRecord field %s is None - using string as type" % key)
|
|
50
|
+
valueType=str
|
|
51
|
+
else:
|
|
52
|
+
valueType=type(value)
|
|
53
|
+
if valueType == str:
|
|
54
|
+
pass
|
|
55
|
+
elif valueType == int:
|
|
56
|
+
pass
|
|
57
|
+
elif valueType == float:
|
|
58
|
+
pass
|
|
59
|
+
elif valueType == bool:
|
|
60
|
+
pass
|
|
61
|
+
elif valueType == datetime.date:
|
|
62
|
+
pass
|
|
63
|
+
elif valueType== datetime.datetime:
|
|
64
|
+
pass
|
|
65
|
+
else:
|
|
66
|
+
if valueType is not None:
|
|
67
|
+
msg="warning: unsupported type %s for field %s " % (str(valueType),key)
|
|
68
|
+
print(msg)
|
|
69
|
+
if valueType is not None:
|
|
70
|
+
self.addType(listName,key,valueType)
|
|
71
|
+
|
|
72
|
+
def fixTypes(self,data):
|
|
73
|
+
'''
|
|
74
|
+
fix the types in the given data structure
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
data(dict): a dict representation returned from storage
|
|
78
|
+
with list of Dicts as entries
|
|
79
|
+
'''
|
|
80
|
+
for listName in self.typeMap:
|
|
81
|
+
if listName in data:
|
|
82
|
+
self.fixListOfDicts(self.typeMap[listName],data[listName])
|
|
83
|
+
|
|
84
|
+
def fixListOfDicts(self,typeMap,listOfDicts):
|
|
85
|
+
'''
|
|
86
|
+
fix the type in the given list of Dicts
|
|
87
|
+
'''
|
|
88
|
+
for record in listOfDicts:
|
|
89
|
+
for keyValue in record.items():
|
|
90
|
+
key,value=keyValue
|
|
91
|
+
if key in typeMap:
|
|
92
|
+
valueType=typeMap[key]
|
|
93
|
+
if valueType==bool:
|
|
94
|
+
b= value in ['True','TRUE','true']
|
|
95
|
+
record[key]=b
|
|
96
|
+
elif valueType==datetime.date:
|
|
97
|
+
dt=datetime.datetime.strptime(value,"%Y-%m-%d")
|
|
98
|
+
record[key]=dt.date()
|
|
99
|
+
elif valueType==datetime.datetime:
|
|
100
|
+
# see https://stackoverflow.com/questions/127803/how-do-i-parse-an-iso-8601-formatted-date
|
|
101
|
+
if sys.version_info >= (3, 7):
|
|
102
|
+
dtime=datetime.datetime.fromisoformat(value)
|
|
103
|
+
else:
|
|
104
|
+
dtime=datetime.datetime.strptime(value,"%Y-%m-%dT%H:%M:%S.%f")
|
|
105
|
+
record[key]=dtime
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pylodstorage
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
4
4
|
Summary: python List of Dict (Table) Storage library
|
|
5
5
|
Home-page: https://github.com/WolfgangFahl/pyLodStorage
|
|
6
6
|
Author: Wolfgang Fahl
|
|
@@ -14,6 +14,7 @@ Description: # pyLoDStorage
|
|
|
14
14
|
python List of Dict (Table) Storage library
|
|
15
15
|
|
|
16
16
|
[](https://pypi.org/project/pyLoDStorage/)
|
|
17
|
+
[](https://pypi.python.org/pypi/pyLoDStorage/)
|
|
17
18
|
[](https://github.com/WolfgangFahl/pyLoDStorage/actions?query=workflow%3ABuild+branch%3Amaster)
|
|
18
19
|
[](https://github.com/WolfgangFahl/pyLoDStorage/issues)
|
|
19
20
|
[](https://github.com/WolfgangFahl/pyLoDStorage/issues/?q=is%3Aissue+is%3Aclosed)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|