Mercurial > public > mercurial-scm > hg
comparison mercurial/util.py @ 38797:8751d1e2a7ff
util: create a context manager to handle timing
The context manager is pulled out of the timed decorator function, and
refactored to provide a stats instance, with added tests.
author | Martijn Pieters <mj@zopatista.com> |
---|---|
date | Wed, 01 Aug 2018 16:05:41 +0200 |
parents | 27391d74aaa2 |
children | 9d49bb117dde |
comparison
equal
deleted
inserted
replaced
38796:f956dc7217fc | 38797:8751d1e2a7ff |
---|---|
34 import time | 34 import time |
35 import traceback | 35 import traceback |
36 import warnings | 36 import warnings |
37 import zlib | 37 import zlib |
38 | 38 |
39 from .thirdparty import ( | |
40 attr, | |
41 ) | |
39 from . import ( | 42 from . import ( |
40 encoding, | 43 encoding, |
41 error, | 44 error, |
42 i18n, | 45 i18n, |
43 node as nodemod, | 46 node as nodemod, |
2872 (100, 0.000000001, _('%.1f ns')), | 2875 (100, 0.000000001, _('%.1f ns')), |
2873 (10, 0.000000001, _('%.2f ns')), | 2876 (10, 0.000000001, _('%.2f ns')), |
2874 (1, 0.000000001, _('%.3f ns')), | 2877 (1, 0.000000001, _('%.3f ns')), |
2875 ) | 2878 ) |
2876 | 2879 |
2877 _timenesting = [0] | 2880 @attr.s |
2881 class timedcmstats(object): | |
2882 """Stats information produced by the timedcm context manager on entering.""" | |
2883 | |
2884 # the starting value of the timer as a float (meaning and resulution is | |
2885 # platform dependent, see util.timer) | |
2886 start = attr.ib(default=attr.Factory(lambda: timer())) | |
2887 # the number of seconds as a floating point value; starts at 0, updated when | |
2888 # the context is exited. | |
2889 elapsed = attr.ib(default=0) | |
2890 # the number of nested timedcm context managers. | |
2891 level = attr.ib(default=1) | |
2892 | |
2893 def __str__(self): | |
2894 return timecount(self.elapsed) if self.elapsed else '<unknown>' | |
2895 | |
2896 @contextlib.contextmanager | |
2897 def timedcm(): | |
2898 """A context manager that produces timing information for a given context. | |
2899 | |
2900 On entering a timedcmstats instance is produced. | |
2901 | |
2902 This context manager is reentrant. | |
2903 | |
2904 """ | |
2905 # track nested context managers | |
2906 timedcm._nested += 1 | |
2907 timing_stats = timedcmstats(level=timedcm._nested) | |
2908 try: | |
2909 yield timing_stats | |
2910 finally: | |
2911 timing_stats.elapsed = timer() - timing_stats.start | |
2912 timedcm._nested -= 1 | |
2913 | |
2914 timedcm._nested = 0 | |
2878 | 2915 |
2879 def timed(func): | 2916 def timed(func): |
2880 '''Report the execution time of a function call to stderr. | 2917 '''Report the execution time of a function call to stderr. |
2881 | 2918 |
2882 During development, use as a decorator when you need to measure | 2919 During development, use as a decorator when you need to measure |
2886 def foo(a, b, c): | 2923 def foo(a, b, c): |
2887 pass | 2924 pass |
2888 ''' | 2925 ''' |
2889 | 2926 |
2890 def wrapper(*args, **kwargs): | 2927 def wrapper(*args, **kwargs): |
2891 start = timer() | 2928 with timedcm() as time_stats: |
2892 indent = 2 | 2929 result = func(*args, **kwargs) |
2893 _timenesting[0] += indent | 2930 stderr = procutil.stderr |
2894 try: | 2931 stderr.write('%s%s: %s\n' % ( |
2895 return func(*args, **kwargs) | 2932 ' ' * time_stats.level * 2, func.__name__, time_stats)) |
2896 finally: | 2933 return result |
2897 elapsed = timer() - start | |
2898 _timenesting[0] -= indent | |
2899 stderr = procutil.stderr | |
2900 stderr.write('%s%s: %s\n' % | |
2901 (' ' * _timenesting[0], func.__name__, | |
2902 timecount(elapsed))) | |
2903 return wrapper | 2934 return wrapper |
2904 | 2935 |
2905 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30), | 2936 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30), |
2906 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1)) | 2937 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1)) |
2907 | 2938 |