Coverage for faststream / _internal / utils / nuid.py: 87%

35 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-08 01:48 +0000

1# Copyright 2016-2018 The NATS Authors 

2# Licensed under the Apache License, Version 2.0 (the "License"); 

3# you may not use this file except in compliance with the License. 

4# You may obtain a copy of the License at 

5# 

6# http://www.apache.org/licenses/LICENSE-2.0 

7# 

8# Unless required by applicable law or agreed to in writing, software 

9# distributed under the License is distributed on an "AS IS" BASIS, 

10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

11# See the License for the specific language governing permissions and 

12# limitations under the License. 

13 

14from __future__ import annotations 

15 

16from random import Random 

17from secrets import randbelow, token_bytes 

18from sys import maxsize as max_int 

19 

20DIGITS = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 

21BASE = 62 

22PREFIX_LENGTH = 12 

23SEQ_LENGTH = 10 

24MAX_SEQ = BASE**10 

25MIN_INC = 33 

26MAX_INC = 333 

27INC = MAX_INC - MIN_INC 

28TOTAL_LENGTH = PREFIX_LENGTH + SEQ_LENGTH 

29 

30 

31class NUID: 

32 """NUID created is a utility to create a new id. 

33 

34 NUID is an implementation of the approach for fast generation 

35 of unique identifiers used for inboxes in NATS. 

36 """ 

37 

38 def __init__(self) -> None: 

39 self._prand = Random(randbelow(max_int)) # nosec B311 # noqa: S311 

40 self._seq = self._prand.randint(0, MAX_SEQ) 

41 self._inc = MIN_INC + self._prand.randint(BASE + 1, INC) 

42 self._prefix = bytearray() 

43 self.randomize_prefix() 

44 

45 def next(self) -> bytearray: 

46 """Next returns the next unique identifier.""" 

47 self._seq += self._inc 

48 if self._seq >= MAX_SEQ: 48 ↛ 49line 48 didn't jump to line 49 because the condition on line 48 was never true

49 self.randomize_prefix() 

50 self.reset_sequential() 

51 

52 l_seq = self._seq 

53 prefix = self._prefix[:] 

54 suffix = bytearray(SEQ_LENGTH) 

55 for i in reversed(range(SEQ_LENGTH)): 

56 suffix[i] = DIGITS[int(l_seq) % BASE] 

57 l_seq //= BASE 

58 

59 prefix.extend(suffix) 

60 return prefix 

61 

62 def randomize_prefix(self) -> None: 

63 random_bytes = token_bytes(PREFIX_LENGTH) 

64 self._prefix = bytearray(DIGITS[c % BASE] for c in random_bytes) 

65 

66 def reset_sequential(self) -> None: 

67 self._seq = self._prand.randint(0, MAX_SEQ) 

68 self._inc = MIN_INC + self._prand.randint(0, INC)