val = self._render_format(fmt, prefix) if val not in used: return val raise ValueError("Could not generate unique invoice number after 50 attempts") def _render_format(self, fmt: str, prefix: str) -> str: from string import ascii_letters, digits upper = string.ascii_uppercase + string.digits lower = string.ascii_lowercase + string.digits digits_only = string.digits def rnd(chars, n): return "".join(self.rng.choice(chars) for _ in range(n)) def repl(match): token = match.group(1) if token == "prefix": return prefix if token == "date": return datetime.utcnow().strftime("%Y%m%d") if token.startswith("R"): # e.g. R4, R5 try: n = int(token[1:]) except ValueError: return token return rnd(upper, n) if token.startswith("r"): try: n = int(token[1:]) except ValueError: return token return rnd(lower, n) if token.startswith("d"): try: n = int(token[1:]) except ValueError: return token return rnd(digits_only, n) if token == "seq4": return rnd(digits_only, 4) if token.startswith("hash"): n = int(token[4:] or 8) return secrets.token_hex(n) return match.group(0) return re.sub(r"\{([A-Za-z0-9_]+)(\d*)\}", repl, fmt)