| OLD | NEW |
| 1 # Copyright 2016 The LUCI Authors. All rights reserved. | 1 # Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 from __future__ import absolute_import | 5 from __future__ import absolute_import |
| 6 import bisect | 6 import bisect |
| 7 import collections | 7 import collections |
| 8 import contextlib | 8 import contextlib |
| 9 import copy | 9 import copy |
| 10 import json |
| 10 import keyword | 11 import keyword |
| 11 import re | 12 import re |
| 12 import types | 13 import types |
| 13 | 14 |
| 14 from functools import wraps | 15 from functools import wraps |
| 15 | 16 |
| 16 from .recipe_test_api import DisabledTestData, ModuleTestData | 17 from .recipe_test_api import DisabledTestData, ModuleTestData |
| 17 from .config import Single | 18 from .config import Single |
| 18 | 19 |
| 19 from .util import ModuleInjectionSite | 20 from .util import ModuleInjectionSite |
| (...skipping 868 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 888 regex = r'^[a-zA-Z][a-zA-Z0-9_]*$' if is_param_name else ( | 889 regex = r'^[a-zA-Z][a-zA-Z0-9_]*$' if is_param_name else ( |
| 889 r'^[a-zA-Z][.\w-]*$') | 890 r'^[a-zA-Z][.\w-]*$') |
| 890 return bool(re.match(regex, name)) | 891 return bool(re.match(regex, name)) |
| 891 | 892 |
| 892 def __init__(self, default, help, kind, name, property_type, module, | 893 def __init__(self, default, help, kind, name, property_type, module, |
| 893 param_name=None): | 894 param_name=None): |
| 894 """ | 895 """ |
| 895 Constructor for BoundProperty. | 896 Constructor for BoundProperty. |
| 896 | 897 |
| 897 Args: | 898 Args: |
| 898 default: The default value for this Property. Note: A default | 899 default: The default value for this Property. Must be JSON-encodable or |
| 899 value of None is allowed. To have no default value, omit | 900 PROPERTY_SENTINEL. |
| 900 this argument. | |
| 901 help: The help text for this Property. | 901 help: The help text for this Property. |
| 902 kind: The type of this Property. You can either pass in a raw python | 902 kind: The type of this Property. You can either pass in a raw python |
| 903 type, or a Config Type, using the recipe engine config system. | 903 type, or a Config Type, using the recipe engine config system. |
| 904 name: The name of this Property. | 904 name: The name of this Property. |
| 905 param_name: The name of the python function parameter this property | 905 param_name: The name of the python function parameter this property |
| 906 should be stored in. Can be used to allow for dotted property | 906 should be stored in. Can be used to allow for dotted property |
| 907 names, e.g. | 907 names, e.g. |
| 908 PROPERTIES = { | 908 PROPERTIES = { |
| 909 'foo.bar.bam': Property(param_name="bizbaz") | 909 'foo.bar.bam': Property(param_name="bizbaz") |
| 910 } | 910 } |
| 911 module: The module this Property is a part of. | 911 module: The module this Property is a part of. |
| 912 """ | 912 """ |
| 913 if not BoundProperty.legal_name(name): | 913 if not BoundProperty.legal_name(name): |
| 914 raise ValueError("Illegal name '{}'.".format(name)) | 914 raise ValueError("Illegal name '{}'.".format(name)) |
| 915 | 915 |
| 916 param_name = param_name or name | 916 param_name = param_name or name |
| 917 if not BoundProperty.legal_name(param_name, is_param_name=True): | 917 if not BoundProperty.legal_name(param_name, is_param_name=True): |
| 918 raise ValueError("Illegal param_name '{}'.".format(param_name)) | 918 raise ValueError("Illegal param_name '{}'.".format(param_name)) |
| 919 | 919 |
| 920 if default is not PROPERTY_SENTINEL: |
| 921 try: |
| 922 json.dumps(default) |
| 923 except: |
| 924 raise TypeError('default=%r is not json-encodable' % (default,)) |
| 925 |
| 920 self.__default = default | 926 self.__default = default |
| 921 self.__help = help | 927 self.__help = help |
| 922 self.__kind = kind | 928 self.__kind = kind |
| 923 self.__name = name | 929 self.__name = name |
| 924 self.__property_type = property_type | 930 self.__property_type = property_type |
| 925 self.__param_name = param_name | 931 self.__param_name = param_name |
| 926 self.__module = module | 932 self.__module = module |
| 927 | 933 |
| 928 @property | 934 @property |
| 929 def name(self): | 935 def name(self): |
| 930 return self.__name | 936 return self.__name |
| 931 | 937 |
| 932 @property | 938 @property |
| 933 def param_name(self): | 939 def param_name(self): |
| 934 return self.__param_name | 940 return self.__param_name |
| 935 | 941 |
| 936 @property | 942 @property |
| 937 def default(self): | 943 def default(self): |
| 938 return self.__default | 944 if self.__default is PROPERTY_SENTINEL: |
| 945 return self.__default |
| 946 return copy.deepcopy(self.__default) |
| 939 | 947 |
| 940 @property | 948 @property |
| 941 def kind(self): | 949 def kind(self): |
| 942 return self.__kind | 950 return self.__kind |
| 943 | 951 |
| 944 @property | 952 @property |
| 945 def help(self): | 953 def help(self): |
| 946 return self.__help | 954 return self.__help |
| 947 | 955 |
| 948 @property | 956 @property |
| (...skipping 11 matching lines...) Expand all Loading... |
| 960 Returns: | 968 Returns: |
| 961 The value to use for this property. Raises an error if | 969 The value to use for this property. Raises an error if |
| 962 this property has no valid interpretation. | 970 this property has no valid interpretation. |
| 963 """ | 971 """ |
| 964 if value is not PROPERTY_SENTINEL: | 972 if value is not PROPERTY_SENTINEL: |
| 965 if self.kind is not None: | 973 if self.kind is not None: |
| 966 # The config system handles type checking for us here. | 974 # The config system handles type checking for us here. |
| 967 self.kind.set_val(value) | 975 self.kind.set_val(value) |
| 968 return value | 976 return value |
| 969 | 977 |
| 970 if self.default is not PROPERTY_SENTINEL: | 978 if self.__default is not PROPERTY_SENTINEL: |
| 971 return self.default | 979 return self.default |
| 972 | 980 |
| 973 raise ValueError( | 981 raise ValueError( |
| 974 "No default specified and no value provided for '{}' from {} '{}'".format( | 982 "No default specified and no value provided for '{}' from {} '{}'".format( |
| 975 self.name, self.__property_type, self.module)) | 983 self.name, self.__property_type, self.module)) |
| 976 | 984 |
| 977 class Property(object): | 985 class Property(object): |
| 978 def __init__(self, default=PROPERTY_SENTINEL, help="", kind=None, | 986 def __init__(self, default=PROPERTY_SENTINEL, help="", kind=None, |
| 979 param_name=None): | 987 param_name=None): |
| 980 """ | 988 """ |
| 981 Constructor for Property. | 989 Constructor for Property. |
| 982 | 990 |
| 983 Args: | 991 Args: |
| 984 default: The default value for this Property. Note: A default | 992 default: The default value for this Property. Note: A default |
| 985 value of None is allowed. To have no default value, omit | 993 value of None is allowed. To have no default value, omit |
| 986 this argument. | 994 this argument. This must be a valid JSON-encodable object. |
| 987 help: The help text for this Property. | 995 help: The help text for this Property. |
| 988 kind: The type of this Property. You can either pass in a raw python | 996 kind: The type of this Property. You can either pass in a raw python |
| 989 type, or a Config Type, using the recipe engine config system. | 997 type, or a Config Type, using the recipe engine config system. |
| 990 """ | 998 """ |
| 999 if default is not PROPERTY_SENTINEL: |
| 1000 try: |
| 1001 json.dumps(default) |
| 1002 except: |
| 1003 raise TypeError('default=%r is not json-encodable' % (default,)) |
| 1004 |
| 991 self._default = default | 1005 self._default = default |
| 992 self.help = help | 1006 self.help = help |
| 993 self.param_name = param_name | 1007 self.param_name = param_name |
| 994 | 1008 |
| 995 if isinstance(kind, type): | 1009 if isinstance(kind, type): |
| 996 if kind in (str, unicode): | 1010 if kind in (str, unicode): |
| 997 kind = basestring | 1011 kind = basestring |
| 998 kind = Single(kind) | 1012 kind = Single(kind) |
| 999 self.kind = kind | 1013 self.kind = kind |
| 1000 | 1014 |
| 1001 def bind(self, name, property_type, module): | 1015 def bind(self, name, property_type, module): |
| 1002 """ | 1016 """ |
| 1003 Gets the BoundProperty version of this Property. Requires a name. | 1017 Gets the BoundProperty version of this Property. Requires a name. |
| 1004 """ | 1018 """ |
| 1005 return BoundProperty( | 1019 return BoundProperty( |
| 1006 self._default, self.help, self.kind, name, property_type, module, | 1020 self._default, self.help, self.kind, name, property_type, module, |
| 1007 self.param_name) | 1021 self.param_name) |
| 1008 | 1022 |
| 1009 class UndefinedPropertyException(TypeError): | 1023 class UndefinedPropertyException(TypeError): |
| 1010 pass | 1024 pass |
| 1011 | 1025 |
| OLD | NEW |