2020
2121
2222class _SettingNotFoundError (Exception ):
23- ...
23+ pass
2424
2525
2626class UndefinedValue :
@@ -49,12 +49,14 @@ def __repr__(self):
4949
5050
5151def _import_module (path : str , / ) -> types .ModuleType :
52+ """Import a module from a provided path."""
5253 package , _ , module_name = path .rpartition ("." )
5354
5455 return importlib .import_module (name = module_name , package = package )
5556
5657
5758def _import_class (path : str , / ) -> Type :
59+ """Import a class from a provided path."""
5860 module_path , _ , class_name = path .rpartition ("." )
5961 package , _ , module_name = module_path .rpartition ("." )
6062 module = importlib .import_module (name = module_name , package = package )
@@ -63,6 +65,10 @@ def _import_class(path: str, /) -> Type:
6365
6466
6567def _raise_on_set_attribute (self , attr_name , value : Any ):
68+ """
69+ Raise AttributeError when an attribute is set on the settings instance.
70+ Except the attr_name ends with _ATTR_RESOLVED_POSTFIX.
71+ """
6672 if not attr_name .endswith (_ATTR_RESOLVED_POSTFIX ):
6773 raise AttributeError (f"Can't set attribute { attr_name } " )
6874 self .__dict__ [attr_name ] = value
@@ -98,30 +104,38 @@ def _class_decorator(cls: Type[_T]) -> Type[_T]:
98104 type_hints = get_annotations (cls )
99105
100106 for attr_name , value in inspect .getmembers (cls ):
107+ # Skip dunder attributes and methods.
101108 if (
102109 attr_name .startswith ("__" ) and attr_name .endswith ("__" )
103110 ) or not attr_name .isupper ():
104111 continue
105112
113+ # Define names for the "real" attribute and the resolved value
106114 hidden_attr_name = f"_{ attr_name } "
107115 resolved_attr_name = f"_{ attr_name } { _ATTR_RESOLVED_POSTFIX } "
108116
117+ # Property getter which substitutes the class member and handles
118+ # overriding of settings via settings.py.
109119 def getter (
110120 self ,
111121 attr_name : str = attr_name ,
112122 hidden_attr_name : str = hidden_attr_name ,
113123 resolved_attr_name : str = resolved_attr_name ,
114124 ) -> Any :
125+ # Check if the setting was resolved previously and return that
126+ # already resolved value.
115127 value = getattr (self , resolved_attr_name )
116128 if value is not UndefinedValue :
117129 return value
118130
119131 annotation = type_hints .get (attr_name )
132+ # Check if the setting is overridden via settings.py.
120133 try :
121134 value = django_settings_getter (attr_name )
122135 except _SettingNotFoundError :
123136 value = getattr (self , hidden_attr_name )
124137
138+ # Perform checks and "magical" behaviours.
125139 if isinstance (value , UndefinedValue ):
126140 raise ImproperlyConfigured (
127141 f"{ attr_name !r} needs to be configured in your settings module"
@@ -131,6 +145,7 @@ def getter(
131145 elif _check_type (annotation ) and isinstance (value , str ):
132146 value = _import_class (value )
133147
148+ # Store the resolved setting in the instance.
134149 setattr (self , resolved_attr_name , value )
135150
136151 return value
0 commit comments