@@ -36,7 +36,11 @@ def is_restriction(document: dict) -> bool:
3636
3737
3838def is_single_cardinality_restriction (document : dict ) -> bool :
39- return document .get ("owl:cardinality" ) == 1
39+ return is_restriction (document ) and document .get ("owl:cardinality" ) == 1
40+
41+
42+ def is_emptyable_cardinality_restriction (document : dict ) -> bool :
43+ return is_restriction (document ) and document .get ("owl:minCardinality" ) == 0
4044
4145
4246def is_property (document : dict ) -> bool :
@@ -114,6 +118,83 @@ def get_domains(_property: dict) -> Iterator[dict]:
114118 yield domain
115119
116120
121+ def add_method (
122+ module ,
123+ class_def ,
124+ step_class ,
125+ properties ,
126+ is_path_step ,
127+ single_properties ,
128+ method_name ,
129+ ):
130+ mapping : List [Tuple [ast .Constant , ast .Expr ]] = [
131+ (ast .Constant ("@type" ), ast .Constant (step_class ["@id" ]))
132+ ]
133+ is_method : bool = False
134+ sorted_properties = sorted (
135+ properties ,
136+ key = lambda _property : ""
137+ if _property ["@id" ] == "linkedql:from"
138+ else _property ["@id" ],
139+ )
140+ positional_args = []
141+ kwonlyargs = []
142+ for _property in sorted_properties :
143+ argument_name = remove_linked_ql (_property ["@id" ])
144+ if argument_name == "from" :
145+ is_method = True
146+ positional_args .append (ast .arg (arg = "self" , annotation = None ))
147+ value = ast .Attribute (value = ast .Name (id = "self" ), attr = "step" )
148+ mapping .append ((ast .Constant (_property ["@id" ]), value ))
149+ continue
150+ mapping .append ((ast .Constant (_property ["@id" ]), ast .Name (argument_name )))
151+ _type = range_to_type (_property ["rdfs:range" ])
152+ if _property ["@id" ] not in single_properties :
153+ _type = ast .Subscript (
154+ value = ast .Attribute (value = ast .Name (id = "typing" ), attr = "List" ),
155+ slice = ast .Index (value = _type ),
156+ )
157+ arg = ast .arg (arg = argument_name , annotation = _type )
158+ if len (properties ) <= 2 :
159+ positional_args .append (arg )
160+ else :
161+ kwonlyargs .append (arg )
162+ args = ast .arguments (
163+ args = positional_args ,
164+ vararg = None ,
165+ kwonlyargs = kwonlyargs or None ,
166+ kw_defaults = [],
167+ kwarg = None ,
168+ defaults = [],
169+ )
170+ keys , values = zip (* mapping )
171+ step_dict = ast .Dict (keys = keys , values = values )
172+ return_type = ast .Name ("Path" if is_path_step else "FinalPath" )
173+ returns = ast .Constant ("Path" ) if is_path_step else ast .Name (id = "FinalPath" )
174+ comment = step_class ["rdfs:comment" ]
175+ if is_method :
176+ docstring_indent = " " * 8
177+ else :
178+ docstring_indent = " " * 4
179+ docstring = ast .Expr (
180+ ast .Constant (f"\n { docstring_indent + comment } \n { docstring_indent } " )
181+ )
182+ function_def = ast .FunctionDef (
183+ name = method_name ,
184+ args = args ,
185+ returns = returns ,
186+ decorator_list = [],
187+ body = [
188+ docstring ,
189+ ast .Return (value = ast .Call (func = return_type , args = [step_dict ], keywords = [])),
190+ ],
191+ )
192+ if is_method :
193+ class_def .body .append (function_def )
194+ else :
195+ module .body .append (function_def )
196+
197+
117198def generate (module_path : Path ) -> str :
118199 module = astor .code_to_ast .parse_file (header_path )
119200 # print(astor.dump_tree(tree))
@@ -138,87 +219,46 @@ def generate(module_path: Path) -> str:
138219
139220 for step_class in step_classes :
140221 single_properties : Set [str ] = set ()
222+ emptyable_properties : Set [str ] = set ()
141223 is_path_step : bool = False
142224 for super_class in normalize_list (step_class ["rdfs:subClassOf" ]):
143225 if super_class ["@id" ] == "linkedql:PathStep" :
144226 is_path_step = True
145- if is_restriction (super_class ) and is_single_cardinality_restriction (
146- super_class
147- ):
227+ if is_single_cardinality_restriction (super_class ):
148228 _property = super_class ["owl:onProperty" ]
149229 single_properties .add (_property ["@id" ])
230+ if is_emptyable_cardinality_restriction (super_class ):
231+ _property = super_class ["owl:onProperty" ]
232+ emptyable_properties .add (_property ["@id" ])
150233 method_name = normalize_keywords (
151234 snake_case .convert (remove_linked_ql (step_class ["@id" ]))
152235 )
153- is_method : bool = False
154- mapping : List [Tuple [ast .Constant , ast .Expr ]] = [
155- (ast .Constant ("@type" ), ast .Constant (step_class ["@id" ]))
156- ]
157236 properties = properties_by_domain .get (step_class ["@id" ], [])
158- sorted_properties = sorted (
237+ add_method (
238+ module ,
239+ class_def ,
240+ step_class ,
159241 properties ,
160- key = lambda _property : ""
161- if _property [ "@id" ] == "linkedql:from"
162- else _property [ "@id" ] ,
242+ is_path_step ,
243+ single_properties ,
244+ method_name ,
163245 )
164- positional_args = []
165- kwonlyargs = []
166- for _property in sorted_properties :
167- argument_name = remove_linked_ql (_property ["@id" ])
168- if argument_name == "from" :
169- is_method = True
170- positional_args .append (ast .arg (arg = "self" , annotation = None ))
171- value = ast .Attribute (value = ast .Name (id = "self" ), attr = "step" )
172- mapping .append ((ast .Constant (_property ["@id" ]), value ))
173- continue
174- mapping .append ((ast .Constant (_property ["@id" ]), ast .Name (argument_name )))
175- _type = range_to_type (_property ["rdfs:range" ])
176- if _property ["@id" ] not in single_properties :
177- _type = ast .Subscript (
178- value = ast .Attribute (value = ast .Name (id = "typing" ), attr = "List" ),
179- slice = ast .Index (value = _type ),
180- )
181- arg = ast .arg (arg = argument_name , annotation = _type )
182- if len (properties ) <= 2 :
183- positional_args .append (arg )
184- else :
185- kwonlyargs .append (arg )
186- args = ast .arguments (
187- args = positional_args ,
188- vararg = None ,
189- kwonlyargs = kwonlyargs or None ,
190- kw_defaults = [],
191- kwarg = None ,
192- defaults = [],
193- )
194- keys , values = zip (* mapping )
195- step_dict = ast .Dict (keys = keys , values = values )
196- return_type = ast .Name ("Path" if is_path_step else "FinalPath" )
197- returns = ast .Constant ("Path" ) if is_path_step else ast .Name (id = "FinalPath" )
198- comment = step_class ["rdfs:comment" ]
199- if is_method :
200- docstring_indent = " " * 8
201- else :
202- docstring_indent = " " * 4
203- docstring = ast .Expr (
204- ast .Constant (f"\n { docstring_indent + comment } \n { docstring_indent } " )
205- )
206- function_def = ast .FunctionDef (
207- name = method_name ,
208- args = args ,
209- returns = returns ,
210- decorator_list = [],
211- body = [
212- docstring ,
213- ast .Return (
214- value = ast .Call (func = return_type , args = [step_dict ], keywords = [])
215- ),
216- ],
217- )
218- if is_method :
219- class_def .body .append (function_def )
220- else :
221- module .body .append (function_def )
246+ if "linkedql:from" in emptyable_properties :
247+ properties = [
248+ _property
249+ for _property in properties
250+ if _property ["@id" ] != "linkedql:from"
251+ ]
252+ add_method (
253+ module ,
254+ class_def ,
255+ step_class ,
256+ properties ,
257+ is_path_step ,
258+ single_properties ,
259+ method_name ,
260+ )
261+
222262 generated_code = astor .to_source (module )
223263 with module_path .open ("w+" ) as file :
224264 file .write (generated_code )
0 commit comments