3636        pass   # pragma: no cov 
3737
3838
39+ GRACE_PERIOD_CI  =  timedelta (hours = 1 )  # prevent version switch in the middle of a CI run 
40+ GRACE_PERIOD_MINOR  =  timedelta (days = 28 )
41+ UPDATE_PERIOD  =  timedelta (days = 14 )
42+ UPDATE_ABORTED_DELAY  =  timedelta (hours = 1 )
43+ 
44+ 
3945def  periodic_update (distribution , of_version , for_py_version , wheel , search_dirs , app_data , do_periodic_update , env ):
4046    if  do_periodic_update :
4147        handle_auto_update (distribution , for_py_version , wheel , search_dirs , app_data , env )
@@ -48,20 +54,20 @@ def _update_wheel(ver):
4854        return  updated_wheel 
4955
5056    u_log  =  UpdateLog .from_app_data (app_data , distribution , for_py_version )
51-     u_log_older_than_hour  =  now  -  u_log .completed  >  timedelta (hours = 1 ) if  u_log .completed  is  not None  else  False 
5257    if  of_version  is  None :
5358        for  _ , group  in  groupby (u_log .versions , key = lambda  v : v .wheel .version_tuple [0 :2 ]):
54-             version  =  next (group )  # use only latest patch version per minor, earlier assumed to be buggy 
55-             if  wheel  is  not None  and  Path (version .filename ).name  ==  wheel .name :
56-                 break 
57-             if  u_log .periodic  is  False  or  (u_log_older_than_hour  and  version .use (now )):
58-                 wheel  =  _update_wheel (version )
59-                 break 
60-     elif  u_log .periodic  is  False  or  u_log_older_than_hour :
59+             # use only latest patch version per minor, earlier assumed to be buggy 
60+             all_patches  =  list (group )
61+             ignore_grace_period_minor  =  any (version  for  version  in  all_patches  if  version .use (now ))
62+             for  version  in  all_patches :
63+                 if  wheel  is  not None  and  Path (version .filename ).name  ==  wheel .name :
64+                     return  wheel 
65+                 if  version .use (now , ignore_grace_period_minor ):
66+                     return  _update_wheel (version )
67+     else :
6168        for  version  in  u_log .versions :
6269            if  version .wheel .version  ==  of_version :
63-                 wheel  =  _update_wheel (version )
64-                 break 
70+                 return  _update_wheel (version )
6571
6672    return  wheel 
6773
@@ -88,41 +94,52 @@ def load_datetime(value):
8894
8995
9096class  NewVersion (object ):
91-     def  __init__ (self , filename , found_date , release_date ):
97+     def  __init__ (self , filename , found_date , release_date ,  source ):
9298        self .filename  =  filename 
9399        self .found_date  =  found_date 
94100        self .release_date  =  release_date 
101+         self .source  =  source 
95102
96103    @classmethod  
97104    def  from_dict (cls , dictionary ):
98105        return  cls (
99106            filename = dictionary ["filename" ],
100107            found_date = load_datetime (dictionary ["found_date" ]),
101108            release_date = load_datetime (dictionary ["release_date" ]),
109+             source = dictionary ["source" ],
102110        )
103111
104112    def  to_dict (self ):
105113        return  {
106114            "filename" : self .filename ,
107115            "release_date" : dump_datetime (self .release_date ),
108116            "found_date" : dump_datetime (self .found_date ),
117+             "source" : self .source ,
109118        }
110119
111-     def  use (self , now ):
112-         compare_from  =  self .release_date  or  self .found_date 
113-         return  now  -  compare_from  >=  timedelta (days = 28 )
120+     def  use (self , now , ignore_grace_period_minor = False , ignore_grace_period_ci = False ):
121+         if  self .source  ==  "manual" :
122+             return  True 
123+         elif  self .source  ==  "periodic" :
124+             if  self .found_date  <  now  -  GRACE_PERIOD_CI  or  ignore_grace_period_ci :
125+                 if  not  ignore_grace_period_minor :
126+                     compare_from  =  self .release_date  or  self .found_date 
127+                     return  now  -  compare_from  >=  GRACE_PERIOD_MINOR 
128+                 return  True 
129+         return  False 
114130
115131    def  __repr__ (self ):
116-         return  "{}(filename={}), found_date={}, release_date={})" .format (
132+         return  "{}(filename={}), found_date={}, release_date={}, source={} )" .format (
117133            self .__class__ .__name__ ,
118134            self .filename ,
119135            self .found_date ,
120136            self .release_date ,
137+             self .source ,
121138        )
122139
123140    def  __eq__ (self , other ):
124141        return  type (self ) ==  type (other ) and  all (
125-             getattr (self , k ) ==  getattr (other , k ) for  k  in  ["filename" , "release_date" , "found_date" ]
142+             getattr (self , k ) ==  getattr (other , k ) for  k  in  ["filename" , "release_date" , "found_date" ,  "source" ]
126143        )
127144
128145    def  __ne__ (self , other ):
@@ -170,12 +187,12 @@ def needs_update(self):
170187        if  self .completed  is  None :  # never completed 
171188            return  self ._check_start (now )
172189        else :
173-             if  now  -  self .completed  <=  timedelta ( days = 14 ) :
190+             if  now  -  self .completed  <=  UPDATE_PERIOD :
174191                return  False 
175192            return  self ._check_start (now )
176193
177194    def  _check_start (self , now ):
178-         return  self .started  is  None  or  now  -  self .started  >  timedelta ( hours = 1 ) 
195+         return  self .started  is  None  or  now  -  self .started  >  UPDATE_ABORTED_DELAY 
179196
180197
181198def  trigger_update (distribution , for_py_version , wheel , search_dirs , app_data , env , periodic ):
@@ -231,12 +248,24 @@ def _run_do_update(app_data, distribution, embed_filename, for_py_version, perio
231248    embed_update_log  =  app_data .embed_update_log (distribution , for_py_version )
232249    u_log  =  UpdateLog .from_dict (embed_update_log .read ())
233250    now  =  datetime .now ()
251+     if  periodic :
252+         source  =  "periodic" 
253+         # mark everything not updated manually as source "periodic" 
254+         for  version  in  u_log .versions :
255+             if  version .source  !=  "manual" :
256+                 version .source  =  source 
257+     else :
258+         source  =  "manual" 
259+         # mark everything as source "manual" 
260+         for  version  in  u_log .versions :
261+             version .source  =  source 
262+ 
234263    if  wheel_filename  is  not None :
235264        dest  =  wheelhouse  /  wheel_filename .name 
236265        if  not  dest .exists ():
237266            copy2 (str (wheel_filename ), str (wheelhouse ))
238267    last , last_version , versions  =  None , None , []
239-     while  last  is  None  or  not  last .use (now ):
268+     while  last  is  None  or  not  last .use (now ,  ignore_grace_period_ci = True ):
240269        download_time  =  datetime .now ()
241270        dest  =  acquire .download_wheel (
242271            distribution = distribution ,
@@ -250,7 +279,7 @@ def _run_do_update(app_data, distribution, embed_filename, for_py_version, perio
250279        if  dest  is  None  or  (u_log .versions  and  u_log .versions [0 ].filename  ==  dest .name ):
251280            break 
252281        release_date  =  release_date_for_wheel_path (dest .path )
253-         last  =  NewVersion (filename = dest .path .name , release_date = release_date , found_date = download_time )
282+         last  =  NewVersion (filename = dest .path .name , release_date = release_date , found_date = download_time ,  source = source )
254283        logging .info ("detected %s in %s" , last , datetime .now () -  download_time )
255284        versions .append (last )
256285        last_wheel  =  Wheel (Path (last .filename ))
0 commit comments