@@ -173,33 +173,67 @@ def permissive_nhs_number(warn_on_test_numbers: bool = False):
173173 return type ("NHSNumber" , (NHSNumber , * NHSNumber .__bases__ ), dict_ )
174174
175175
176- # TODO: Make the spacing configurable. Not all downstream consumers want a single space
177176class Postcode (types .ConstrainedStr ):
178177 """Postcode constrained string"""
179178
180179 regex : re .Pattern = POSTCODE_REGEX
181180 strip_whitespace = True
181+ apply_normalize = True
182182
183183 @staticmethod
184- def normalize (postcode : str ) -> Optional [str ]:
184+ def normalize (_postcode : str ) -> Optional [str ]:
185185 """Strips internal and external spaces"""
186- postcode = postcode .replace (" " , "" )
187- if not postcode or postcode .lower () in NULL_POSTCODES :
186+ _postcode = _postcode .replace (" " , "" )
187+ if not _postcode or _postcode .lower () in NULL_POSTCODES :
188188 return None
189- postcode = postcode .replace (" " , "" )
190- return " " .join ((postcode [0 :- 3 ], postcode [- 3 :])).upper ()
189+ _postcode = _postcode .replace (" " , "" )
190+ return " " .join ((_postcode [0 :- 3 ], _postcode [- 3 :])).upper ()
191191
192192 @classmethod
193193 def validate (cls , value : str ) -> Optional [str ]: # type: ignore
194194 """Validates the given postcode"""
195- stripped = cls .normalize (value )
196- if not stripped :
195+ if cls .apply_normalize and value :
196+ value = cls .normalize (value ) # type: ignore
197+
198+ if not value :
197199 return None
198200
199- if not cls .regex .match (stripped ):
201+ if not cls .regex .match (value ):
200202 raise ValueError ("Invalid Postcode submitted" )
201203
202- return stripped
204+ return value
205+
206+
207+ @lru_cache ()
208+ @validate_arguments
209+ def postcode (
210+ # pylint: disable=R0913
211+ strip_whitespace : Optional [bool ] = True ,
212+ to_upper : Optional [bool ] = False ,
213+ to_lower : Optional [bool ] = False ,
214+ strict : Optional [bool ] = False ,
215+ min_length : Optional [int ] = None ,
216+ max_length : Optional [int ] = None ,
217+ curtail_length : Optional [int ] = None ,
218+ regex : Optional [str ] = POSTCODE_REGEX , # type: ignore
219+ apply_normalize : Optional [bool ] = True ,
220+ ) -> type [Postcode ]:
221+ """Return a formatted date class with a set date format
222+ and timezone treatment.
223+
224+ """
225+ dict_ = Postcode .__dict__ .copy ()
226+ dict_ ["strip_whitespace" ] = strip_whitespace
227+ dict_ ["to_upper" ] = to_upper
228+ dict_ ["to_lower" ] = to_lower
229+ dict_ ["strict" ] = strict
230+ dict_ ["min_length" ] = min_length
231+ dict_ ["max_length" ] = max_length
232+ dict_ ["curtail_length" ] = curtail_length
233+ dict_ ["regex" ] = regex
234+ dict_ ["apply_normalize" ] = apply_normalize
235+
236+ return type ("Postcode" , (Postcode , * Postcode .__bases__ ), dict_ )
203237
204238
205239class OrgID (_SimpleRegexValidator ):
0 commit comments