2424import calendar
2525from re import compile as re_compile
2626from re import IGNORECASE
27- from string import whitespace as whitespace_string
2827
2928__author__ = "Brett Cannon"
3029__email__ = "drifty@bigfoot.com"
3332
3433RegexpType = type (re_compile ('' ))
3534
35+ def _getlang ():
36+ # Figure out what the current language is set to.
37+ current_lang = locale .getlocale (locale .LC_TIME )[0 ]
38+ if current_lang :
39+ return current_lang
40+ else :
41+ current_lang = locale .getdefaultlocale ()[0 ]
42+ if current_lang :
43+ return current_lang
44+ else :
45+ return ''
3646
3747class LocaleTime (object ):
3848 """Stores and handles locale-specific information related to time.
@@ -285,19 +295,9 @@ def __calc_timezone(self):
285295 self .__timezone = self .__pad (time .tzname , 0 )
286296
287297 def __calc_lang (self ):
288- # Set self.__lang by using locale.getlocale() or
289- # locale.getdefaultlocale(). If both turn up empty, set the attribute
290- # to ''. This is to stop calls to this method and to make sure
291- # strptime() can produce an re object correctly.
292- current_lang = locale .getlocale (locale .LC_TIME )[0 ]
293- if current_lang :
294- self .__lang = current_lang
295- else :
296- current_lang = locale .getdefaultlocale ()[0 ]
297- if current_lang :
298- self .__lang = current_lang
299- else :
300- self .__lang = ''
298+ # Set self.__lang by using __getlang().
299+ self .__lang = _getlang ()
300+
301301
302302
303303class TimeRE (dict ):
@@ -382,8 +382,8 @@ def sorter(a, b):
382382 def pattern (self , format ):
383383 """Return re pattern for the format string."""
384384 processed_format = ''
385- for whitespace in whitespace_string :
386- format = format . replace ( whitespace , r '\s*' )
385+ whitespace_replacement = re_compile ( '\s+' )
386+ format = whitespace_replacement . sub ( '\s*' , format )
387387 while format .find ('%' ) != - 1 :
388388 directive_index = format .index ('%' )+ 1
389389 processed_format = "%s%s%s" % (processed_format ,
@@ -394,15 +394,31 @@ def pattern(self, format):
394394
395395 def compile (self , format ):
396396 """Return a compiled re object for the format string."""
397- format = "(?#%s)%s" % (self .locale_time .lang ,format )
398397 return re_compile (self .pattern (format ), IGNORECASE )
399398
399+ # Cached TimeRE; probably only need one instance ever so cache it for performance
400+ _locale_cache = TimeRE ()
401+ # Cached regex objects; same reason as for TimeRE cache
402+ _regex_cache = dict ()
400403
401404def strptime (data_string , format = "%a %b %d %H:%M:%S %Y" ):
402405 """Return a time struct based on the input data and the format string."""
403- locale_time = LocaleTime ()
404- compiled_re = TimeRE (locale_time ).compile (format )
405- found = compiled_re .match (data_string )
406+ global _locale_cache
407+ global _regex_cache
408+ locale_time = _locale_cache .locale_time
409+ # If the language changes, caches are invalidated, so clear them
410+ if locale_time .lang != _getlang ():
411+ _locale_cache = TimeRE ()
412+ _regex_cache .clear ()
413+ format_regex = _regex_cache .get (format )
414+ if not format_regex :
415+ # Limit regex cache size to prevent major bloating of the module;
416+ # The value 5 is arbitrary
417+ if len (_regex_cache ) > 5 :
418+ _regex_cache .clear ()
419+ format_regex = _locale_cache .compile (format )
420+ _regex_cache [format ] = format_regex
421+ found = format_regex .match (data_string )
406422 if not found :
407423 raise ValueError ("time data did not match format" )
408424 year = 1900
0 commit comments