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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pylodstorage
3
- Version: 0.0.7
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
  [![pypi](https://img.shields.io/pypi/pyversions/pyLoDStorage)](https://pypi.org/project/pyLoDStorage/)
17
+ [![PyPI Status](https://img.shields.io/pypi/v/pyLoDStorage.svg)](https://pypi.python.org/pypi/pyLoDStorage/)
17
18
  [![Github Actions Build](https://github.com/WolfgangFahl/pyLoDStorage/workflows/Build/badge.svg?branch=master)](https://github.com/WolfgangFahl/pyLoDStorage/actions?query=workflow%3ABuild+branch%3Amaster)
18
19
  [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyLoDStorage.svg)](https://github.com/WolfgangFahl/pyLoDStorage/issues)
19
20
  [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyLoDStorage.svg)](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
  [![pypi](https://img.shields.io/pypi/pyversions/pyLoDStorage)](https://pypi.org/project/pyLoDStorage/)
5
+ [![PyPI Status](https://img.shields.io/pypi/v/pyLoDStorage.svg)](https://pypi.python.org/pypi/pyLoDStorage/)
5
6
  [![Github Actions Build](https://github.com/WolfgangFahl/pyLoDStorage/workflows/Build/badge.svg?branch=master)](https://github.com/WolfgangFahl/pyLoDStorage/actions?query=workflow%3ABuild+branch%3Amaster)
6
7
  [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyLoDStorage.svg)](https://github.com/WolfgangFahl/pyLoDStorage/issues)
7
8
  [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyLoDStorage.svg)](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.7
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
  [![pypi](https://img.shields.io/pypi/pyversions/pyLoDStorage)](https://pypi.org/project/pyLoDStorage/)
17
+ [![PyPI Status](https://img.shields.io/pypi/v/pyLoDStorage.svg)](https://pypi.python.org/pypi/pyLoDStorage/)
17
18
  [![Github Actions Build](https://github.com/WolfgangFahl/pyLoDStorage/workflows/Build/badge.svg?branch=master)](https://github.com/WolfgangFahl/pyLoDStorage/actions?query=workflow%3ABuild+branch%3Amaster)
18
19
  [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyLoDStorage.svg)](https://github.com/WolfgangFahl/pyLoDStorage/issues)
19
20
  [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyLoDStorage.svg)](https://github.com/WolfgangFahl/pyLoDStorage/issues/?q=is%3Aissue+is%3Aclosed)
@@ -1,8 +1,10 @@
1
1
  README.md
2
2
  setup.py
3
3
  lodstorage/__init__.py
4
+ lodstorage/jsonable.py
4
5
  lodstorage/sample.py
5
6
  lodstorage/sql.py
7
+ lodstorage/types.py
6
8
  lodstorage/uml.py
7
9
  pylodstorage.egg-info/PKG-INFO
8
10
  pylodstorage.egg-info/SOURCES.txt
@@ -11,7 +11,7 @@ with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f:
11
11
 
12
12
  setup(
13
13
  name='pylodstorage',
14
- version='0.0.7',
14
+ version='0.0.9',
15
15
 
16
16
  packages=['lodstorage',],
17
17
  author='Wolfgang Fahl',
File without changes