anoy 0.1.3__tar.gz → 0.2.1__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.4
2
2
  Name: anoy
3
- Version: 0.1.3
3
+ Version: 0.2.1
4
4
  Summary: This is a library that provides simple type checking for YAML.
5
5
  Author-email: masaniki <masaniki.software@gmail.com>
6
6
  License-Expression: MIT
@@ -143,7 +143,7 @@ When there is an issue with the annotation YAML, `anoy` outputs as follows.
143
143
  ... (omission) ...
144
144
  src.anoyModule.anoyErrors.ConfigurationYamlError: `@Auther` is not defined.
145
145
  ```
146
- # For Developpers
146
+ # For Developers
147
147
 
148
148
  ## Testing
149
149
 
@@ -152,3 +152,25 @@ This project uses `pytest`.
152
152
  If you want to test, put in following command.
153
153
 
154
154
  `pytest tests\unit\test_dictTraversal.py`
155
+
156
+ ## Next To Do
157
+
158
+ - [ ] test caseを全てのdata型に拡大。
159
+ - [ ] `!Float`のtest.
160
+ - [ ] `!AnnoMap`のtest.
161
+ - [ ] `!FreeMap`のtest.
162
+
163
+ ## Ideas
164
+
165
+ ```
166
+ - key01
167
+ - key02
168
+ - key03
169
+ ```
170
+
171
+ ```
172
+ - {key01:value01}
173
+ - {key02:value02}
174
+ - {key03:value03}
175
+ ```
176
+ を等価に扱うsystemが欲しい。
@@ -122,7 +122,7 @@ When there is an issue with the annotation YAML, `anoy` outputs as follows.
122
122
  ... (omission) ...
123
123
  src.anoyModule.anoyErrors.ConfigurationYamlError: `@Auther` is not defined.
124
124
  ```
125
- # For Developpers
125
+ # For Developers
126
126
 
127
127
  ## Testing
128
128
 
@@ -131,3 +131,25 @@ This project uses `pytest`.
131
131
  If you want to test, put in following command.
132
132
 
133
133
  `pytest tests\unit\test_dictTraversal.py`
134
+
135
+ ## Next To Do
136
+
137
+ - [ ] test caseを全てのdata型に拡大。
138
+ - [ ] `!Float`のtest.
139
+ - [ ] `!AnnoMap`のtest.
140
+ - [ ] `!FreeMap`のtest.
141
+
142
+ ## Ideas
143
+
144
+ ```
145
+ - key01
146
+ - key02
147
+ - key03
148
+ ```
149
+
150
+ ```
151
+ - {key01:value01}
152
+ - {key02:value02}
153
+ - {key03:value03}
154
+ ```
155
+ を等価に扱うsystemが欲しい。
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "anoy"
8
- version = "0.1.3"
8
+ version = "0.2.1"
9
9
  authors = [
10
10
  { name="masaniki", email="masaniki.software@gmail.com" }
11
11
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anoy
3
- Version: 0.1.3
3
+ Version: 0.2.1
4
4
  Summary: This is a library that provides simple type checking for YAML.
5
5
  Author-email: masaniki <masaniki.software@gmail.com>
6
6
  License-Expression: MIT
@@ -143,7 +143,7 @@ When there is an issue with the annotation YAML, `anoy` outputs as follows.
143
143
  ... (omission) ...
144
144
  src.anoyModule.anoyErrors.ConfigurationYamlError: `@Auther` is not defined.
145
145
  ```
146
- # For Developpers
146
+ # For Developers
147
147
 
148
148
  ## Testing
149
149
 
@@ -152,3 +152,25 @@ This project uses `pytest`.
152
152
  If you want to test, put in following command.
153
153
 
154
154
  `pytest tests\unit\test_dictTraversal.py`
155
+
156
+ ## Next To Do
157
+
158
+ - [ ] test caseを全てのdata型に拡大。
159
+ - [ ] `!Float`のtest.
160
+ - [ ] `!AnnoMap`のtest.
161
+ - [ ] `!FreeMap`のtest.
162
+
163
+ ## Ideas
164
+
165
+ ```
166
+ - key01
167
+ - key02
168
+ - key03
169
+ ```
170
+
171
+ ```
172
+ - {key01:value01}
173
+ - {key02:value02}
174
+ - {key03:value03}
175
+ ```
176
+ を等価に扱うsystemが欲しい。
@@ -0,0 +1,3 @@
1
+
2
+ from .dictTraversal import DictTraversal
3
+ from .anoyErrors import AnoyError, AnoyTypeError, ConfigYamlError
@@ -1,5 +1,5 @@
1
1
 
2
- class AnnotationYamlError(Exception):
2
+ class AnoyError(Exception):
3
3
  """
4
4
  @Summ: annotation yaml上のError。
5
5
  """
@@ -7,20 +7,20 @@ class AnnotationYamlError(Exception):
7
7
  super().__init__(*args)
8
8
 
9
9
 
10
- class AnnotationYamlTypeError(Exception):
10
+ class AnoyTypeError(Exception):
11
11
  """
12
12
  @Summ: annotation yaml上のdata型のError。
13
13
  """
14
- def __init__(self,fileName:str,type:str,path:list):
14
+ def __init__(self,type:str,fileName:str,path:list):
15
15
  super().__init__()
16
- self.fileName=fileName
17
16
  self.type=type
17
+ self.fileName=fileName
18
18
  self.path=path
19
19
 
20
20
  def __str__(self):
21
- return f"required {self.type} type at:\n {self.fileName}: {self.path}"
21
+ return f"{self.type} contradiction:\n {self.fileName}: {self.path}"
22
22
 
23
- class ConfigurationYamlError(Exception):
23
+ class ConfigYamlError(Exception):
24
24
  """
25
25
  @Summ: annotation yaml上のError。
26
26
  """
@@ -0,0 +1,642 @@
1
+ import yaml
2
+ from pathlib import Path
3
+
4
+ from .anoyErrors import AnoyError,ConfigYamlError,AnoyTypeError
5
+
6
+ class DictTraversal():
7
+ """
8
+ @Summ: 辞書型の中身を探索するclass.
9
+
10
+ @InsVars:
11
+ _configDict:
12
+ @Summ: config yamlを構文解析した後の値を格納する。
13
+ @Desc:
14
+ - !Childの値は{"!Child": {typeString(str):typOption(dict)}}という形式に直す。
15
+ - typeStringはdata型を表す文字列。
16
+ - typeOptionはdata型の詳細な設定を表すdict型である。
17
+ - つまり、str-format data typeもmap-format data typeに直すということ。
18
+ - map-format data typeが無いBool型は{"!Bool":{}}とする。
19
+ - annotation keyを使った否かを"isVisit" keyに記録する。
20
+ @Type: Dict
21
+ _visitQueue:
22
+ @Summ: 探索queue
23
+ @Desc: BFSなのでFIFO。
24
+ @Type: List
25
+ _pathQueue:
26
+ @Summ: 探索する要素の相対pathを格納する。
27
+ @Desc:
28
+ - visitQueueと要素番号を共有する。
29
+ - []でroot要素を表す。
30
+ @Type: List
31
+ _curFile:
32
+ @Summ: 現在探索中のANOY file名。
33
+ @ComeFrom: current file.
34
+ @Type: Str
35
+ _curPath:
36
+ @Summ: _curFile内での現在地。
37
+ @ComeFrom: current path/
38
+ @Type: List
39
+ """
40
+
41
+ def __init__(self,configDict:dict):
42
+ """
43
+ @Summ: constructor.
44
+ """
45
+ self._configDict=self.checkConfig(configDict)
46
+ print(self._configDict)
47
+ self._visitQueue=[]
48
+ self._pathQueue=[]
49
+ self._curFile=""
50
+ self._curPath=[]
51
+
52
+ def checkConfig(self,configDict:dict)->dict:
53
+ """
54
+ @Summ: config yamlの中身を構文解析する関数。
55
+
56
+ @Desc
57
+ - config yamlは、annotation keyかconfig keyの記述しか許さない。
58
+ - configDictに"isVisit" keyを追加し、annotation keyを使用したかを記録する。
59
+
60
+ @Args:
61
+ configDict:
62
+ @Summ: config yamlの中身。
63
+ @Type: Dict
64
+ @Returns:
65
+ @Summ: 型確認して、余計なものを取り除いたconfigDict。
66
+ @Type: dict
67
+ """
68
+ newConfigDict={} # 整形されたconfigDict
69
+ for annoKey in configDict.keys():
70
+ newAnnoValue={} #annotation keyに対応する値。
71
+ if(annoKey[0]!="@"):
72
+ raise ConfigYamlError(f"{annoKey} is invalid definition.")
73
+ valueDict=configDict[annoKey]
74
+ if(type(valueDict)!=dict):
75
+ raise ConfigYamlError(f"{annoKey} is invalid definition.")
76
+ for key,value in valueDict.items():
77
+ if(key[0]=="@"):
78
+ continue
79
+ elif(key=="!Parent"):
80
+ validConfParent=self.checkParent(annoKey,value)
81
+ newAnnoValue["!Parent"]=validConfParent
82
+ elif(key=="!Child"):
83
+ validConfChild=self.checkChild(annoKey,value)
84
+ newAnnoValue["!Child"]=validConfChild
85
+ else:
86
+ raise ConfigYamlError(f"{annoKey} is invalid definition.")
87
+ # isVisit keyの追加。
88
+ newAnnoValue["isVisit"]=False
89
+ newConfigDict[annoKey]=newAnnoValue
90
+ return newConfigDict
91
+
92
+ @classmethod
93
+ def checkParent(cls,annoKey,confParent):
94
+ """
95
+ @Summ: `!Parent`に対応する値を型確認する関数。
96
+
97
+ @Args:
98
+ annoKey:
99
+ @Summ: `!Parent`の親となるannotation key.
100
+ @Type: Str
101
+ confParent:
102
+ @Summ: `!Parent`の子。
103
+ @Type: Any
104
+ @Returns:
105
+ @Summ: `!Parent`のvalueとして有効な値。
106
+ @Type: List
107
+ """
108
+ if(type(confParent)!=list):
109
+ raise ConfigYamlError(f"{annoKey} is invalid definition.")
110
+ for item in confParent:
111
+ if(item is None):
112
+ continue
113
+ if(item[0]!="@"):
114
+ raise ConfigYamlError(f"{annoKey} is invalid definition.")
115
+ return confParent.copy()
116
+
117
+ @classmethod
118
+ def checkChild(cls,annoKey,confChild):
119
+ """
120
+ @Summ: `!Child`に対応する値を型確認する関数。
121
+
122
+ @Args:
123
+ annoKey:
124
+ @Summ: `!Child`の親となるannotation key.
125
+ @Type: Str
126
+ confChild:
127
+ @Summ: `!Child`の子。
128
+ @Type: Any
129
+ @Returns:
130
+ @Summ: `!Child`のvalueとして有効な値。
131
+ @Type: Dict
132
+ """
133
+ if(type(confChild)==str):
134
+ match confChild:
135
+ case "!Str":
136
+ return {"!Str":{"length":None,"min":None,"max":None}}
137
+ case "!Bool":
138
+ return {"!Bool":{}}
139
+ case "!Int":
140
+ return {"!Int":{"min":None,"max":None}}
141
+ case "!Float":
142
+ return {"!Float":{"min":None,"max":None}}
143
+ case "!List":
144
+ return {"!List":{"type":None,"length":None}}
145
+ case "!FreeMap":
146
+ return {"!FreeMap":{}}
147
+ case "!AnnoMap":
148
+ return {"!AnnoMap":[]}
149
+ case _:
150
+ raise ConfigYamlError(f"{annoKey} is invalid definition.")
151
+ elif(type(confChild)==dict):
152
+ confChildKey=list(confChild.keys())
153
+ if(len(confChildKey)!=1):
154
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
155
+ typeStr=confChildKey[0]
156
+ typeOption=confChild[typeStr]
157
+ match typeStr:
158
+ case "!Str":
159
+ if(type(typeOption)!=dict):
160
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
161
+ strLength=None
162
+ strMin=None
163
+ strMax=None
164
+ for strKey,strVal in typeOption.items():
165
+ match strKey:
166
+ case "length":
167
+ if(strMin is None and strMax is None):
168
+ strLength=strVal
169
+ else:
170
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
171
+ case "min":
172
+ if(strLength is None):
173
+ strMin=strVal
174
+ else:
175
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
176
+ case "max":
177
+ if(strLength is None):
178
+ strMax=strVal
179
+ else:
180
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
181
+ case _:
182
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
183
+ return {"!Str":{"length":strLength,"min":strMin,"max":strMax}}
184
+ case "!Int":
185
+ if(type(typeOption)!=dict):
186
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
187
+ intMin=None
188
+ intMax=None
189
+ for intKey,intVal in typeOption.items():
190
+ match intKey:
191
+ case "min":
192
+ intMin=intVal
193
+ case "max":
194
+ intMax=intVal
195
+ case _:
196
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
197
+ return {"!Int":{"min":intMin,"max":intMax}}
198
+ case "!Float":
199
+ if(type(typeOption)!=dict):
200
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
201
+ floatMin=None
202
+ floatMax=None
203
+ for floatKey,floatVal in typeOption.items():
204
+ match floatKey:
205
+ case "min":
206
+ floatMin=floatVal
207
+ case "max":
208
+ floatMax=floatVal
209
+ case _:
210
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
211
+ return {"!Float":{"min":floatMin,"max":floatMax}}
212
+ case "!Enum":
213
+ if(type(typeOption)!=list):
214
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
215
+ enumOption=[]
216
+ for item in typeOption:
217
+ if(type(item)==list):
218
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
219
+ elif(type(item)==dict):
220
+ keyList=list(item.keys())
221
+ if(len(keyList)!=1):
222
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
223
+ enumOption.append(keyList[0])
224
+ else:
225
+ enumOption.append(item)
226
+ return {"!Enum":enumOption}
227
+ case "!List":
228
+ if(type(typeOption)!=dict):
229
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
230
+ listType=None
231
+ listLength=None
232
+ for listKey,listVal in typeOption.items():
233
+ match listKey:
234
+ case "type":
235
+ listType=listVal
236
+ case "length":
237
+ listLength=listVal
238
+ case _:
239
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
240
+ return {"!List":{"type":listType,"length":listLength}}
241
+ case "!AnnoDict":
242
+ if(type(typeOption)!=list):
243
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
244
+ for i in len(typeOption):
245
+ item=typeOption[i]
246
+ if(item[0]!="@"):
247
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
248
+ return {"!AnnoDict":typeOption}
249
+ case _:
250
+ raise ConfigYamlError(f"`{annoKey}` has invalid definition.")
251
+ else:
252
+ raise ConfigYamlError(f"{annoKey} is invalid definition.")
253
+
254
+
255
+ def dirDFS(self,anoyPath:Path):
256
+ """
257
+ @Summ: directory内を深さ優先探索する関数。
258
+
259
+ @Desc:
260
+ - fileならばYAMLかどうかを確認して、内部のdict型を探索する。
261
+ - directoryならば、子要素を再帰的に探索する。
262
+
263
+ @Args:
264
+ anoyPath:
265
+ @Summ: 探索するfileやdirectoryのpath名。
266
+ @Type: Path
267
+ """
268
+ if(anoyPath.is_file()):
269
+ suffix=anoyPath.suffix
270
+ if(suffix==".yaml" or suffix==".yml" or suffix==".anoy"):
271
+ with open(anoyPath, mode="r", encoding="utf-8") as f:
272
+ anoyDict=yaml.safe_load(f)
273
+ self._curFile=anoyPath
274
+ self.dictBFS(anoyDict)
275
+ else:
276
+ for childPath in anoyPath.iterdir():
277
+ self.dirDFS(childPath)
278
+
279
+
280
+ def dictBFS(self,anoyDict:dict):
281
+ """
282
+ @Summ: anoyDictの中を幅優先探索を開始する関数。
283
+
284
+ @Desc:
285
+ - list型は単純に探索する。
286
+ - dict型は型確認しながら探索する。
287
+ - visitQueueには(key(str),value(any))のtupleを入れる。
288
+ - list型の時は、(key(int),value(any))になる。
289
+ @Args:
290
+ anoyDict:
291
+ @Summ: annotation yamlのdict型。
292
+ """
293
+ self._visitQueue=[(None,anoyDict)]
294
+ self._pathQueue=[[]]
295
+ while(True):
296
+ if(self._visitQueue==[]):
297
+ break
298
+ key,value=self._visitQueue.pop(0)
299
+ self._curPath=self._pathQueue.pop(0)
300
+ print(key,value)
301
+ print(self._curPath)
302
+ self.checkAnoy(key,value)
303
+
304
+ def checkAnoy(self,parentKey:str|None,childValue):
305
+ """
306
+ "@Summ": anoyの中身を探索する関数。
307
+
308
+ "@Desc":
309
+ - 型確認は"!Parent"と"!Child"の2つだ。
310
+ - parentKeyの`!Child`がchildValueを制限する。
311
+ - childValueの`!parent`がparentKeyを制限する。
312
+ - parentKeyがannotationKeyでない時は、"!Parent"も"!Child"も効力を発揮しないので無視。
313
+ - !Childが無い時は、childValue=Noneとして考える。
314
+ - `!Parent`による型確認は、childValueが`!AnnoMap`型の時のみ行われる。
315
+
316
+ "@Args":
317
+ parentKey:
318
+ "@Summ": 探索するdict型のkey。
319
+ "@Desc":
320
+ - nullは親要素が存在しないことを表す(つまりvalueがroot要素である)。
321
+ "@Type":
322
+ Union:
323
+ - Str
324
+ - null
325
+ childValue:
326
+ "@Summ": 探索するdict型のvalue。
327
+ "@Type": Any
328
+ "@Error":
329
+ - AnnotationYamlError
330
+ - AnnotationYamlTypeError
331
+ - ConfigurationYamlError
332
+ """
333
+ if(parentKey is None):
334
+ confChild=None #confChild=Noneの時は型確認をしない。
335
+ elif(type(parentKey)!=str):
336
+ confChild=None
337
+ elif(parentKey[0]=="@"):
338
+ confDictVal=self._configDict.get(parentKey)
339
+ if(confDictVal is None):
340
+ raise AnoyError(f"{parentKey} is not found.")
341
+ confChild=confDictVal.get("!Child")
342
+ else:
343
+ confChild=None
344
+ # anoyの型確認
345
+ if(confChild is None): #Noneの処理方法は不明。
346
+ # nestになるlistとdictだけ対処する。
347
+ if(type(childValue)==list):
348
+ for i in range(len(childValue)):
349
+ element=childValue[i]
350
+ newPath=self._curPath+[i]
351
+ self._visitQueue.append((i,element))
352
+ self._pathQueue.append(newPath)
353
+ elif(type(childValue)==dict):
354
+ for key,value in childValue.items():
355
+ newPath=self._curPath+[key]
356
+ self._visitQueue.append((key,value))
357
+ self._pathQueue.append(newPath)
358
+ return
359
+ typeStr=list(confChild.keys())[0]
360
+ typeOption=confChild[typeStr]
361
+ match typeStr:
362
+ case "!Str":
363
+ self.checkStr(childValue,**typeOption)
364
+ case "!Bool":
365
+ self.checkBool(childValue)
366
+ case "!Int":
367
+ self.checkInt(childValue,**typeOption)
368
+ case "!Float":
369
+ self.checkFloat(childValue,**typeOption)
370
+ case "!FreeMap":
371
+ self.checkFreeMap(childValue)
372
+ case "!AnnoMap":
373
+ self.checkAnnoMap(parentKey,childValue,typeOption)
374
+ case "!List":
375
+ self.checkList(parentKey,childValue,elementType=typeOption["type"],length=typeOption["length"])
376
+ case "!Enum":
377
+ self.checkEnum(childValue,typeOption)
378
+ case _:
379
+ raise ConfigYamlError(f"{parentKey} is invalid definition.")
380
+
381
+ def checkStr(self,anoyValue,length=None,min=None,max=None):
382
+ """
383
+ @Summ: !Str型を型確認する関数。
384
+
385
+ @Desc:
386
+ - <length>と<min>、<length>と<max>の両立は不可能であるが、この関数ではその確認を行わない。
387
+ - 呼び出し時にその確認を行うべきである。
388
+
389
+ @Args:
390
+ anoyValue:
391
+ @Summ: 型確認する値。
392
+ length:
393
+ @Summ: 文字列の長さ。
394
+ @Desc: min,maxとの両立は不可能。
395
+ min:
396
+ @Summ: 文字列の長さの最小値。
397
+ @Desc:
398
+ - lengthとの両立は不可能。
399
+ - min-1からerror.
400
+ max:
401
+ @Summ: 文字列の長さの最大値。
402
+ @Desc:
403
+ - lengthとの両立は不可能。
404
+ - max+1からerror.
405
+ """
406
+ if(type(anoyValue)==str):
407
+ if(length is not None):
408
+ if(len(anoyValue)==length):
409
+ return
410
+ else:
411
+ raise AnoyTypeError("!Str",self._curFile,self._curPath)
412
+ else:
413
+ if(min is not None):
414
+ if(len(anoyValue)<min):
415
+ raise AnoyTypeError("!Str",self._curFile,self._curPath)
416
+ if(max is not None):
417
+ if(max<len(anoyValue)):
418
+ raise AnoyTypeError("!Str",self._curFile,self._curPath)
419
+ return
420
+ else:
421
+ raise AnoyTypeError("!Str",self._curFile,self._curPath)
422
+
423
+ def checkBool(self,anoyValue):
424
+ """
425
+ @Summ: !Bool型を型確認する関数。
426
+
427
+ @Args:
428
+ anoyValue:
429
+ @Summ: 型確認する値。
430
+ """
431
+ if(type(anoyValue)!=bool):
432
+ raise AnoyTypeError("!Bool",self._curFile,self._curPath)
433
+
434
+ def checkInt(self,anoyValue,min=None,max=None):
435
+ """
436
+ @Summ: !Int型を型確認する関数。
437
+
438
+ @Args:
439
+ anoyValue:
440
+ @Summ: 型確認する値。
441
+ min:
442
+ @Summ: 最小値。
443
+ max:
444
+ @Summ: 最大値。
445
+ """
446
+ if(type(anoyValue)==int):
447
+ if(min is not None):
448
+ if(anoyValue<min):
449
+ raise AnoyTypeError("!Int",self._curFile,self._curPath)
450
+ if(max is not None):
451
+ if(max<anoyValue):
452
+ raise AnoyTypeError("!Int",self._curFile,self._curPath)
453
+ return
454
+ else:
455
+ raise AnoyTypeError("!Int",self._curFile,self._curPath)
456
+
457
+ def checkFloat(self,anoyValue,min=None,max=None):
458
+ """
459
+ @Summ: !Float型を型確認する関数。
460
+
461
+ @Args:
462
+ anoyValue:
463
+ @Summ: 型確認する値。
464
+ min:
465
+ @Summ: 最小値。
466
+ max:
467
+ @Summ: 最大値。
468
+ """
469
+ if(type(anoyValue)==float):
470
+ if(min is not None):
471
+ if(anoyValue<min):
472
+ raise AnoyTypeError("!Float",self._curFile,self._curPath)
473
+ if(max is not None):
474
+ if(max<anoyValue):
475
+ raise AnoyTypeError("!Float",self._curFile,self._curPath)
476
+ return
477
+ else:
478
+ raise AnoyTypeError("!Float",self._curFile,self._curPath)
479
+
480
+ def checkFreeMap(self,anoyValue):
481
+ """
482
+ @Summ: !FreeMap型を型確認する関数。
483
+
484
+ @Args:
485
+ anoyValue:
486
+ @Summ: 型確認する値。
487
+ """
488
+ if(type(anoyValue)==dict):
489
+ for key,value in anoyValue.items():
490
+ newPath=self._curPath+[key]
491
+ self._visitQueue.append((key,value))
492
+ self._pathQueue.append(newPath)
493
+ if(key[0]=="@"):
494
+ raise AnoyTypeError("!FreeMap",self._curFile,newPath)
495
+ else:
496
+ raise AnoyTypeError("!FreeMap",self._curFile,self._curPath)
497
+
498
+ def checkAnnoMap(self,parentKey,anoyValue,annoKeyList:list=[]):
499
+ """
500
+ @Summ: !FreeMap型を型確認する関数。
501
+
502
+ @Desc:
503
+ - <annoKeyList>は最低限必要なannotation keyのlistが入る。
504
+ - 最低限なので、<annoKeyList>以外のannotation keyも許容される。
505
+
506
+ @Args:
507
+ parentKey:
508
+ @Summ: 親要素のannotation key。
509
+ @Type: Str
510
+ anoyValue:
511
+ @Summ: 型確認する値。
512
+ annoKeyList:
513
+ @Summ: annotation keyのlist。
514
+ @Type: List
515
+ @Default: []
516
+ """
517
+ if(type(anoyValue)==dict):
518
+ for key,value in anoyValue.items():
519
+ newPath=self._curPath+[key]
520
+ self._visitQueue.append((key,value))
521
+ self._pathQueue.append(newPath)
522
+ # !Parentの確認。
523
+ configValue=self._configDict.get(key)
524
+ if(configValue is None):
525
+ raise AnoyError(f"{parentKey} is not found.")
526
+ confParent=configValue.get("!Parent")
527
+ if(confParent is not None):
528
+ if(parentKey not in confParent):
529
+ raise AnoyTypeError("!Parent",self._curFile,newPath)
530
+ if(annoKeyList!=[]):
531
+ if(key not in annoKeyList):
532
+ raise AnoyTypeError("!AnnoMap",self._curFile,self._curPath)
533
+ else:
534
+ raise AnoyTypeError("!AnnoMap",self._curFile,self._curPath)
535
+
536
+ def checkList(self,parentKey,anoyValue,elementType:str=None,length:int=None):
537
+ """
538
+ @Summ: !List型を型確認する関数。
539
+
540
+ @Desc:
541
+ - <typeOption>は最低限必要なannotation keyのlistが入る。
542
+ - 最低限なので、<typeOption>以外のannotation keyも許容される。
543
+
544
+ @Args:
545
+ parentKey:
546
+ @Summ: 親のkey。
547
+ @Type: Str
548
+ anoyValue:
549
+ @Summ: 型確認する値。
550
+ elementType:
551
+ @Summ: list型の子要素のdata型。
552
+ @Desc:
553
+ - [!Bool,!Str,!Int,!Float]を指定できる。
554
+ - Noneの時はdata型を確認しない。
555
+ @Type: Str
556
+ length:
557
+ @Summ: listの長さ
558
+ @Type: Int
559
+ """
560
+ if(type(anoyValue)==list):
561
+ if(length is not None):
562
+ if(length!=len(anoyValue)):
563
+ raise AnoyTypeError("!List",self._curFile,self._curPath)
564
+ for i in range(len(anoyValue)):
565
+ element=anoyValue[i]
566
+ newPath=self._curPath+[i]
567
+ if(elementType is not None):
568
+ match elementType:
569
+ case "!Str":
570
+ if(type(element)!=str):
571
+ raise AnoyTypeError("!List",self._curFile,newPath)
572
+ case "!Bool":
573
+ if(type(element)!=bool):
574
+ raise AnoyTypeError("!List",self._curFile,newPath)
575
+ case "!Int":
576
+ if(type(element)!=int):
577
+ raise AnoyTypeError("!List",self._curFile,newPath)
578
+ case "!Float":
579
+ if(type(element)!=float):
580
+ raise AnoyTypeError("!List",self._curFile,newPath)
581
+ case _:
582
+ raise ConfigYamlError(f"{parentKey} is invalid definition.")
583
+ self._visitQueue.append((i,element))
584
+ self._pathQueue.append(newPath)
585
+ else:
586
+ raise AnoyTypeError("!List",self._curFile,self._curPath)
587
+
588
+ def checkEnum(self,anoyValue,optionList:list):
589
+ """
590
+ @Summ: !Enum型を型確認する関数。
591
+
592
+ @Desc:
593
+ - 他の言語のUnion型の役割も兼ねている。
594
+ - 選択できるdata型は、[null,!Bool,!Str,!Int,!Float,!List,!FreeMap]である。
595
+ - 入れ子の下層までは確認しない(浅いdata型確認)。
596
+
597
+ @Args:
598
+ anoyValue:
599
+ @Summ: 型確認する値。
600
+ optionList:
601
+ @Summ: Enum型の選択肢を格納するlist型。
602
+ @Type: List
603
+ """
604
+ for i in range(len(optionList)):
605
+ option=optionList[i]
606
+ if(option is None and anoyValue is None):
607
+ return
608
+ match option:
609
+ case "!Str":
610
+ if(type(anoyValue)==str):
611
+ return
612
+ case "!Bool":
613
+ if(type(anoyValue)==bool):
614
+ return
615
+ case "!Int":
616
+ if(type(anoyValue)==int):
617
+ return
618
+ case "!Float":
619
+ if(type(anoyValue)==float):
620
+ return
621
+ case "!List":
622
+ if(type(anoyValue)==list):
623
+ return
624
+ case "!FreeMap":
625
+ if(type(anoyValue)==dict):
626
+ return
627
+ case _:
628
+ if(anoyValue==option):
629
+ return
630
+ raise AnoyTypeError("!Enum",self._curFile,self._curPath)
631
+
632
+
633
+ if(__name__=="__main__"):
634
+ configPath=r"C:\Users\tomot\Backup\sourcecode\python\projects\annotation_yaml\tests\unit\case01\config01.yaml"
635
+ anoyPath=r"C:\Users\tomot\Backup\sourcecode\python\projects\annotation_yaml\tests\unit\case01\int_false.yaml"
636
+ with open(configPath,mode="r",encoding="utf-8") as f:
637
+ configDict=yaml.safe_load(f)
638
+ with open(anoyPath,mode="r",encoding="utf-8") as f:
639
+ anoyDict=yaml.safe_load(f)
640
+ tree01=DictTraversal(configDict)
641
+ tree01.dictBFS(anoyDict)
642
+
@@ -5,7 +5,7 @@ import yaml
5
5
 
6
6
  from anoyModule import DictTraversal
7
7
 
8
- VERSION="v0.1.3"
8
+ VERSION="v0.2.1"
9
9
 
10
10
  def main():
11
11
  """
@@ -1,3 +0,0 @@
1
-
2
- from .dictTraversal import DictTraversal
3
- from .anoyErrors import AnnotationYamlError, AnnotationYamlTypeError, ConfigurationYamlError
@@ -1,219 +0,0 @@
1
- import yaml
2
- from pathlib import Path
3
-
4
- from .anoyErrors import AnnotationYamlError,ConfigurationYamlError,AnnotationYamlTypeError
5
-
6
- class DictTraversal():
7
- """
8
- @Summ: 辞書型の中身を探索するclass
9
-
10
- @InsVars:
11
- _configDict:
12
- @Summ: configuration yamlの中身。
13
- @Type: Dict
14
- _configVisit:
15
- @Summ: configuration yaml内のannotationKeyを使った否かを記録する変数。
16
- @Desc: {annotationKey(str):訪れた⇒True(bool)}
17
- @Type: Dict
18
- _visitQueue:
19
- @Summ: 探索queue
20
- @Desc: BFSなのでFIFO。
21
- @Type: List
22
- _pathQueue:
23
- @Summ: 探索する要素の相対pathを格納する。
24
- @Desc:
25
- - visitQueueと要素番号を共有する。
26
- - []でroot要素を表す。
27
- @Type: List
28
- _curAnoy:
29
- @Summ: 現在探索中のANOY file.
30
- @ComeFrom: current ANOY.
31
- @Type: Str
32
- """
33
- def __init__(self,configDict:dict):
34
- """
35
- @Summ: constructor.
36
- """
37
- self._configDict=configDict
38
- self._visitQueue=[]
39
- self._pathQueue=[]
40
- self._configVisit={key:False for key in configDict.keys()}
41
- self._curAnoy=""
42
-
43
-
44
- def dirDFS(self,anoyPath:Path):
45
- """
46
- @Summ: directory内を深さ優先探索する関数。
47
-
48
- @Desc:
49
- - fileならばYAMLかどうかを確認して、内部のdict型を探索する。
50
- - directoryならば、子要素を再帰的に探索する。
51
-
52
- @Args:
53
- anoyPath:
54
- @Summ: 探索するfileやdirectoryのpath名。
55
- @Type: Path
56
- """
57
- if(anoyPath.is_file()):
58
- suffix=anoyPath.suffix
59
- if(suffix==".yaml" or suffix==".yml" or suffix==".anoy"):
60
- with open(anoyPath, mode="r", encoding="utf-8") as f:
61
- anoyDict=yaml.safe_load(f)
62
- self._curAnoy=anoyPath
63
- self.dictBFS(anoyDict)
64
- else:
65
- for childPath in anoyPath.iterdir():
66
- self.dirDFS(childPath)
67
-
68
-
69
- def dictBFS(self,anoyDict:dict):
70
- """
71
- @Summ: anoyDictの中を幅優先探索を開始する関数。
72
-
73
- @Desc:
74
- - list型は単純に探索する。
75
- - dict型は型確認しながら探索する。
76
- - visitQueueには(key(str),value(any))のtupleを入れる。
77
- - list型の時は、(key(int),value(any))になる。
78
- @Args:
79
- anoyDict:
80
- @Summ: annotation yamlのdict型。
81
- """
82
- self._visitQueue=[(None,anoyDict)]
83
- self._pathQueue=[[]]
84
- while(True):
85
- if(self._visitQueue==[]):
86
- break
87
- key,value=self._visitQueue.pop(0)
88
- pathList=self._pathQueue.pop(0)
89
- # print(key,value)
90
- # print(pathList)
91
- self.typeCheck(key,value,pathList)
92
-
93
- def typeCheck(self,parentKey:str|None,childValue,path:list):
94
- """
95
- "@Summ": annoDict内を探索する関数。
96
-
97
- "@Desc":
98
- - 型確認は"!ParentKey"と"!ChildValue"の2つだ。
99
- - annotationKeyが親でない時は、"!ParentKey"も"!ChildValue"も効力を発揮しないので無視。
100
- - ただし、annoKeyがNoneの時は、"!ParentKey"が効力を発揮する場合がある。
101
- - !ChildValueが無い時は何もしない。
102
- - !ChildValueが無い時は、childValue=nullとして考える。
103
- - valueがanoyDict型の時のみ、!ParentKeyの型確認が行われる。
104
-
105
- "@Args":
106
- parentKey:
107
- "@Summ": 探索するdict型のkey。
108
- "@Desc":
109
- - nullは親要素が存在しないことを表す(つまりvalueがroot要素である)。
110
- "@Type":
111
- Union:
112
- - Str
113
- - null
114
- childValue:
115
- "@Summ": 探索するdict型のvalue。
116
- "@Type": Any
117
- path:
118
- @Summ: 今まで経由したkeyのlist。
119
- @Type: List
120
- "@Error":
121
- - AnnotationYamlError
122
- - AnnotationYamlTypeError
123
- - ConfigurationYamlError
124
- """
125
- if(parentKey is None):
126
- confChildVal=None #confChild=Noneの時は型確認をしない。
127
- elif(type(parentKey)!=str):
128
- confChildVal=None
129
- elif(parentKey[0]=="@"):
130
- confChild=self._configDict.get(parentKey)
131
- if(confChild is None):
132
- raise ConfigurationYamlError(f"`{parentKey}` is not defined.")
133
- confChildVal=confChild.get("!ChildValue")
134
- else:
135
- confChildVal=None
136
- if(type(confChildVal)==dict):
137
- #Enum型の型確認。
138
- # config yaml側の型確認。
139
- confKeyList=list(confChildVal.keys())
140
- if(len(confKeyList)!=1 or confKeyList[0]!="Enum"):
141
- raise ConfigurationYamlError(f"`{parentKey}` has invalid definition.")
142
- confValueList=confChildVal["Enum"]
143
- if(type(confValueList)!=list):
144
- raise ConfigurationYamlError(f"`{parentKey}` has invalid definition.")
145
- # annotaion yaml側の型確認。
146
- for i in range(len(confValueList)):
147
- if(childValue==confValueList[i]):
148
- return
149
- raise AnnotationYamlTypeError(str(self._curAnoy),"Enum",path)
150
- elif(type(childValue)==bool):
151
- if((confChildVal is None) or confChildVal=="Bool"):
152
- return
153
- else:
154
- raise AnnotationYamlTypeError(str(self._curAnoy),"Bool",path)
155
- elif(type(childValue)==str):
156
- if((confChildVal is None or confChildVal=="Str")):
157
- return
158
- else:
159
- raise AnnotationYamlTypeError(str(self._curAnoy),type="Str",path=path)
160
- elif(type(childValue)==int):
161
- if((confChildVal is None) or confChildVal=="Int"):
162
- return
163
- else:
164
- raise AnnotationYamlTypeError(str(self._curAnoy),"Int",path)
165
- elif(type(childValue)==float):
166
- if((confChildVal is None) or confChildVal=="Float"):
167
- return
168
- else:
169
- raise AnnotationYamlTypeError(str(self._curAnoy),"Float",path)
170
- elif(type(childValue)==list):
171
- if((confChildVal is None) or confChildVal=="List"):
172
- for i in range(len(childValue)):
173
- newPath=path+[i]
174
- self._visitQueue.append((i,childValue[i]))
175
- self._pathQueue.append(newPath)
176
- return
177
- else:
178
- raise AnnotationYamlTypeError(str(self._curAnoy),"List",path)
179
- elif(type(childValue)==dict):
180
- if(confChildVal is None):
181
- for key,childValue in childValue.items():
182
- newPath=path+[key]
183
- self._visitQueue.append((key,childValue))
184
- self._pathQueue.append(newPath)
185
- return
186
- elif(confChildVal=="FreeDict"):
187
- for key,childValue in childValue.items():
188
- newPath=path+[key]
189
- self._visitQueue.append((key,childValue))
190
- self._pathQueue.append(newPath)
191
- if(key[0]=="@"):
192
- raise AnnotationYamlTypeError(str(self._curAnoy),"FreeDict",path)
193
- return
194
- elif(confChildVal=="AnnoDict"):
195
- for key,childValue in childValue.items():
196
- newPath=path+[key]
197
- self._visitQueue.append((key,childValue))
198
- self._pathQueue.append(newPath)
199
- confParent=self._configDict[key].get("!ParentKey")
200
- if(key[0]!="@"):
201
- raise AnnotationYamlTypeError(str(self._curAnoy),"AnnoDict",path)
202
- if(confParent is None):
203
- pass
204
- elif(parentKey not in confParent):
205
- raise AnnotationYamlTypeError(str(self._curAnoy),"AnnoDict",path)
206
- return
207
- else:
208
- raise AnnotationYamlError(f" invalid value is found at:\n {str(self._curAnoy)}: `{path}`.")
209
-
210
- if(__name__=="__main__"):
211
- configPath=r"C:\Users\tomot\Backup\sourcecode\python\projects\annotation_yaml\tests\unit\case01\config01.yaml"
212
- anoyPath=r"C:\Users\tomot\Backup\sourcecode\python\projects\annotation_yaml\tests\unit\case01\int_false.yaml"
213
- with open(configPath,mode="r",encoding="utf-8") as f:
214
- configDict=yaml.safe_load(f)
215
- with open(anoyPath,mode="r",encoding="utf-8") as f:
216
- anoyDict=yaml.safe_load(f)
217
- tree01=DictTraversal(configDict)
218
- tree01.dictBFS(anoyDict)
219
-
File without changes
File without changes
File without changes