99logger = logging .getLogger ("somesy" )
1010
1111
12+ class IgnoreKey :
13+ """Special marker to be passed for dropping a key from serialization."""
14+
15+
16+ FieldKeyMapping = Dict [str , Union [List [str ], IgnoreKey ]]
17+ """Type to be used for the dict passed as `direct_mappings`."""
18+
19+
1220class ProjectMetadataWriter (ABC ):
1321 """Base class for Project Metadata Output Wrapper.
1422
@@ -20,7 +28,7 @@ def __init__(
2028 path : Path ,
2129 * ,
2230 create_if_not_exists : Optional [bool ] = False ,
23- direct_mappings : Dict [ str , List [ str ]] = None ,
31+ direct_mappings : FieldKeyMapping = None ,
2432 ) -> None :
2533 """Initialize the Project Metadata Output Wrapper.
2634
@@ -85,31 +93,54 @@ def save(self, path: Optional[Path]) -> None:
8593 without destroying its other contents or structure.
8694 """
8795
88- def _get_property (self , key : Union [str , List [str ]]) -> Optional [Any ]:
96+ def _get_property (
97+ self , key : Union [str , List [str ]], * , remove : bool = False
98+ ) -> Optional [Any ]:
8999 """Get a property from the data.
90100
91101 Override this to e.g. rewrite the retrieved key
92102 (e.g. if everything relevant is in some subobject).
103+
104+ Args:
105+ key: Name of the key or sequence of multiple keys to retrieve the value.
106+ remove: If True, will remove the retrieved value and clean up the dict.
93107 """
94108 key_path = [key ] if isinstance (key , str ) else key
95109
96110 curr = self ._data
111+ seq = [curr ]
97112 for k in key_path :
98113 curr = curr .get (k )
114+ seq .append (curr )
99115 if curr is None :
100116 return None
101117
118+ if remove :
119+ seq .pop ()
120+ logger .debug ("remove in" )
121+ logger .debug (seq [- 1 ])
122+ del seq [- 1 ][key_path [- 1 ]] # remove leaf value
123+ # clean up the tree
124+ for key , dct in reversed (list (zip (key_path [:- 1 ], seq [:- 1 ]))):
125+ if not dct .get (key ):
126+ del dct [key ]
127+
102128 return curr
103129
104- def _set_property (self , key : Union [str , List [str ]], value : Any ) -> None :
130+ def _set_property (self , key : Union [str , List [str ], IgnoreKey ], value : Any ) -> None :
105131 """Set a property in the data.
106132
107133 Override this to e.g. rewrite the retrieved key
108134 (e.g. if everything relevant is in some subobject).
109135 """
110- if not value :
136+ if isinstance ( key , IgnoreKey ) :
111137 return
112138 key_path = [key ] if isinstance (key , str ) else key
139+
140+ if not value : # remove value and clean up the sub-dict
141+ self ._get_property (key_path , remove = True )
142+ return
143+
113144 # create path on the fly if needed
114145 curr = self ._data
115146 for key in key_path [:- 1 ]:
@@ -220,10 +251,12 @@ def sync(self, metadata: ProjectMetadata) -> None:
220251 )
221252
222253 self .license = metadata .license .value
223- if metadata .homepage :
224- self .homepage = str (metadata .homepage )
225- if metadata .repository :
226- self .repository = str (metadata .repository )
254+
255+ self .homepage = str (metadata .homepage ) if metadata .homepage else None
256+ self .repository = str (metadata .repository ) if metadata .repository else None
257+ self .documentation = (
258+ str (metadata .documentation ) if metadata .documentation else None
259+ )
227260
228261 @staticmethod
229262 @abstractmethod
@@ -326,16 +359,26 @@ def homepage(self) -> Optional[str]:
326359 return self ._get_property (self ._get_key ("homepage" ))
327360
328361 @homepage .setter
329- def homepage (self , homepage : Optional [str ]) -> None :
362+ def homepage (self , value : Optional [str ]) -> None :
330363 """Set the homepage url of the project."""
331- self ._set_property (self ._get_key ("homepage" ), homepage )
364+ self ._set_property (self ._get_key ("homepage" ), value )
332365
333366 @property
334367 def repository (self ) -> Optional [Union [str , dict ]]:
335368 """Return the repository url of the project."""
336369 return self ._get_property (self ._get_key ("repository" ))
337370
338371 @repository .setter
339- def repository (self , repository : Optional [Union [str , dict ]]) -> None :
372+ def repository (self , value : Optional [Union [str , dict ]]) -> None :
340373 """Set the repository url of the project."""
341- self ._set_property (self ._get_key ("repository" ), repository )
374+ self ._set_property (self ._get_key ("repository" ), value )
375+
376+ @property
377+ def documentation (self ) -> Optional [Union [str , dict ]]:
378+ """Return the documentation url of the project."""
379+ return self ._get_property (self ._get_key ("documentation" ))
380+
381+ @documentation .setter
382+ def documentation (self , value : Optional [Union [str , dict ]]) -> None :
383+ """Set the documentation url of the project."""
384+ self ._set_property (self ._get_key ("documentation" ), value )
0 commit comments