77from hermes .commands .harvest .base import HermesHarvestCommand , HermesHarvestPlugin
88
99class TomlHarvestSettings (BaseModel ):
10+ """Settings class for this plugin"""
1011 filename : str = 'pyproject.toml'
1112
1213
1314class TomlHarvestPlugin (HermesHarvestPlugin ):
15+ """Base class for the hermes plugin that harvests .toml files"""
16+
1417 settings_class = TomlHarvestSettings
1518 table_with_mapping = {
1619 "project" : [
@@ -27,75 +30,136 @@ class TomlHarvestPlugin(HermesHarvestPlugin):
2730 allowed_keys_for_person = ["givenName" , "lastName" , "email" , "@id" , "@type" ]
2831
2932 def __call__ (self , command : HermesHarvestCommand ):
33+ #set the working directory to the correct location
3034 path = command .args .path
3135 old_path = pathlib .Path .cwd ()
3236 if path != old_path :
3337 os .chdir (path )
3438
39+ #harvesting the data from the .toml file specified in the Settings class
3540 data = self .read_from_toml (command .settings .toml .filename )
3641
42+ #resetting the working directory
3743 if path != old_path :
3844 os .chdir (old_path )
3945
46+ #returning the harvested data and some metadata
4047 return data , {"filename" : command .settings .toml .filename }
4148
4249 @classmethod
4350 def read_from_toml (cls , file ):
51+ #load the toml file as a dictionary
4452 data = toml .load (file )
53+
4554 ret_data = {}
55+
56+ #iterate over each table
57+ #read it's information and store it according to the mapping
58+ #if more than one table existis raise an error as
59+ #the information could be overlapping and there should only be one table
4660 for table , mapping in cls .table_with_mapping .items ():
4761 table = data .get (table )
4862 if not table is None :
4963 if len (ret_data .keys ()) != 0 :
5064 raise ValueError ("Both project and tool.poetry table exist." )
65+ #read the data from the table
5166 ret_data = cls .read_from_one_table (table , mapping )
5267
68+ #return the result
5369 return ret_data
5470
5571 @classmethod
5672 def read_from_one_table (cls , table , mapping ):
5773 ret_data = {}
74+
75+ #iterate over each mapping
5876 for (field1 , field2 ) in mapping :
59- if not table .get (field2 ) is None :
60- if field2 == "requires-python" :
61- ret_data [field1 ] = "Python " + table [field2 ]
62- elif field1 in ["author" , "maintainer" ]:
63- temp = cls .handle_person_in_unknown_format (table [field2 ])
64- if isinstance (temp , dict ) and len (temp .keys ()) > 0 :
65- temp ["@type" ] = "Person"
77+ if not table .get (field2 ) is None :
78+ #if this field exists
79+ #some cases need additional processing
80+ if field2 == "requires-python" :
81+ #add python to the python version number for the runtime platform
82+ ret_data [field1 ] = "Python " + table [field2 ]
83+
84+ elif field1 in ["author" , "maintainer" ]:
85+ #the integrity of the format of the person(s) is assured
86+ temp = cls .handle_person_in_unknown_format (table [field2 ])
87+
88+ #check if it is one person in the right format or none
89+ if isinstance (temp , dict ) and len (temp .keys ()) > 0 :
90+ #store the persons data and add the @type field
91+ temp ["@type" ] = "Person"
92+ ret_data [field1 ] = temp
93+
94+ #check if how many persons are in the list
95+ elif isinstance (temp , list ):
96+ if len (temp ) > 1 :
97+ #add for every person the @type field
98+ for person in temp :
99+ person ["@type" ] = "Person"
100+
101+ #store the data of the persons
66102 ret_data [field1 ] = temp
67- elif isinstance (temp , list ):
68- if len (temp ) > 1 :
69- for person in temp :
70- person ["@type" ] = "Person"
71- ret_data [field1 ] = temp
72- elif len (temp ) == 1 :
73- temp [0 ]["@type" ] = "Person"
74- ret_data [field1 ] = temp [0 ]
75- else :
76- ret_data [field1 ] = table [field2 ]
103+
104+ elif len (temp ) == 1 :
105+ #store the persons data and add the @type field
106+ temp [0 ]["@type" ] = "Person"
107+ ret_data [field1 ] = temp [0 ]
108+
109+ else :
110+ #add the data of a field that needs no processing
111+ ret_data [field1 ] = table [field2 ]
112+
113+ else :
114+ #if it doesn't exist
115+ continue
116+
117+ #return the important data of the table
77118 return ret_data
78119
79120 @classmethod
80121 def handle_person_in_unknown_format (cls , persons ):
122+ #check wheter it is one or are more persons
81123 if isinstance (persons , list ):
124+ #in case of potentially at least two persons
82125 return_list = []
126+ #for each person
83127 for person in persons :
128+
129+ #check if the datatype is correct
84130 if isinstance (person , dict ):
131+ #remove all attributes that aren't allowed
85132 temp = cls .remove_forbidden_keys (person )
133+ #if this leads to the dataset losing all values don't add it to the return list
86134 if len (temp .keys ()) > 0 :
87135 return_list .append (temp )
136+
88137 else :
138+ #if the person isn't a dictionary raise an Error
89139 raise ValueError ("A person must be a dict." )
140+
141+ #return the person(s)
90142 return return_list
143+
144+ #if it is only one or no person
145+ #check for the right datatype
91146 if isinstance (persons , dict ):
147+ #if it is correct return the person with all forbidden keys
148+ #the 'person' may be an empty dictionary if all keys are incorrect
92149 return cls .remove_forbidden_keys (persons )
150+
151+ #raise an error if the persons data is not in the right format
93152 raise ValueError ("A person must be a dict." )
94153
95154 @classmethod
96155 def remove_forbidden_keys (cls , person ):
156+ #the keys are extracted as the dictionary may be resized
97157 keys = list (person .keys ())
158+
159+ #check for every key if it is allowed and if not remove it
98160 for key in keys :
99161 if not key in cls .allowed_keys_for_person :
100162 del person [key ]
163+
164+ #return the persons data
101165 return person
0 commit comments