Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import re 

2 

3from itertools import product 

4 

5from .. import Provider as BarcodeProvider 

6 

7 

8class Provider(BarcodeProvider): 

9 # Source of GS1 country codes: https://gs1.org/standards/id-keys/company-prefix 

10 local_prefixes = ( 

11 *product((0,), range(10)), 

12 *product((1,), range(4)), 

13 ) 

14 

15 upc_e_base_pattern = re.compile(r'^\d{6}$') 

16 upc_ae_pattern1 = re.compile( 

17 r'^(?P<number_system_digit>[01])' # The first digit must be 0 or 1 

18 r'(?=\d{11}$)' # followed by 11 digits of which 

19 r'(?P<mfr_code>\d{2})' # the first 2 digits make up the manufacturer code, 

20 r'(?:(?P<extra>[012])0{4})' # if immediately followed by 00000, 10000, or 20000, 

21 r'(?P<product_code>\d{3})' # a 3-digit product code, 

22 r'(?P<check_digit>\d)$', # and finally a check digit. 

23 ) 

24 upc_ae_pattern2 = re.compile( 

25 r'^(?P<number_system_digit>[01])' # The first digit must be 0 or 1 

26 r'(?=\d{11}$)' # followed by 11 digits of which 

27 r'(?P<mfr_code>\d{3,4}?)' # the first 3 or 4 digits make up the manufacturer code, 

28 r'(?:0{5})' # if immediately followed by 00000, 

29 r'(?P<product_code>\d{1,2})' # a 2-digit or single digit product code, 

30 r'(?P<check_digit>\d)$', # and finally a check digit. 

31 ) 

32 upc_ae_pattern3 = re.compile( 

33 r'^(?P<number_system_digit>[01])' # The first digit must be 0 or 1 

34 r'(?=\d{11}$)' # followed by 11 digits of which 

35 r'(?P<mfr_code>\d{5})' # the first 5 digits make up the manufacturer code, 

36 r'(?:0{4}(?P<extra>[5-9]))' # if immediately followed by 0000 and a 5, 6, 7, 8, or 9, 

37 r'(?P<check_digit>\d)$', # and finally a check digit. 

38 ) 

39 

40 def ean13(self, leading_zero=None, prefixes=()): 

41 """Generate an EAN-13 barcode. 

42 

43 If ``leading_zero`` is ``True``, the leftmost digit of the barcode will be set 

44 to ``0``. If ``False``, the leftmost digit cannot be ``0``. If ``None`` (default), 

45 the leftmost digit can be any digit. 

46 

47 If ``prefixes`` are specified, the result will begin with one of the sequence in ``prefixes``. 

48 This option overrides the option ``leading_zero`` 

49 

50 .. note:: 

51 

52 EAN-13 barcode that starts with a zero can be converted to UPC-A 

53 by dropping the leading zero. This may cause problems with readers that treat 

54 all of these code as UPC-A codes and drop the first digit when reading it. 

55 

56 You can set the argument ``prefixes`` ( or ``leading_zero`` for convenience) explicitly 

57 to avoid or force the generated barcode to start with a zero. 

58 

59 You can also generate actual UPC-A barcode with 

60 :meth:`upc_a() <faker.providers.barcode.en_US.Provider.upc_a>`. 

61 

62 This method uses :meth:`ean() <faker.providers.barcode.en_US.Provider.ean>` under the 

63 hood with the ``length`` argument explicitly set to ``13``. 

64 

65 :sample: 

66 :sample: leading_zero=False 

67 :sample: leading_zero=True 

68 :sample: prefixes=('00',) 

69 :sample: prefixes=('45', '49') 

70 """ 

71 if not prefixes: 

72 if leading_zero is True: 

73 prefixes = ((0,),) 

74 elif leading_zero is False: 

75 prefixes = ((self.random_int(1, 9),),) 

76 

77 return super().ean13(prefixes=prefixes) 

78 

79 def _convert_upc_a2e(self, upc_a): 

80 """ 

81 Convert a 12-digit UPC-A barcode to its 8-digit UPC-E equivalent. 

82 

83 Note that not all UPC-A barcodes can be converted. 

84 """ 

85 if not isinstance(upc_a, str): 

86 raise TypeError('`upc_a` is not a string') 

87 m1 = self.upc_ae_pattern1.match(upc_a) 

88 m2 = self.upc_ae_pattern2.match(upc_a) 

89 m3 = self.upc_ae_pattern3.match(upc_a) 

90 if not any([m1, m2, m3]): 

91 raise ValueError('`upc_a` has an invalid value') 

92 upc_e_template = '{number_system_digit}{mfr_code}{product_code}{extra}{check_digit}' 

93 if m1: 

94 upc_e = upc_e_template.format(**m1.groupdict()) 

95 elif m2: 

96 groupdict = m2.groupdict() 

97 groupdict['extra'] = str(len(groupdict.get('mfr_code'))) 

98 upc_e = upc_e_template.format(**groupdict) 

99 else: 

100 groupdict = m3.groupdict() 

101 groupdict['product_code'] = '' 

102 upc_e = upc_e_template.format(**groupdict) 

103 return upc_e 

104 

105 def _upc_ae(self, base=None, number_system_digit=None): 

106 """ 

107 Create a 12-digit UPC-A barcode that can be converted to UPC-E. 

108 

109 The expected value of ``base`` is a 6-digit string. If any other value is 

110 provided, this method will use a random 6-digit string instead. 

111 

112 The expected value of ``number_system_digit`` is the integer ``0`` or ``1``. 

113 If any other value is provided, this method will randomly choose from the two. 

114 

115 Please also view notes on `upc_a()` and `upc_e()` for more details. 

116 """ 

117 if isinstance(base, str) and self.upc_e_base_pattern.match(base): 

118 base = [int(x) for x in base] 

119 else: 

120 base = [self.random_int(0, 9) for _ in range(6)] 

121 if number_system_digit not in [0, 1]: 

122 number_system_digit = self.random_int(0, 1) 

123 

124 if base[-1] <= 2: 

125 code = base[:2] + base[-1:] + [0] * 4 + base[2:-1] 

126 elif base[-1] <= 4: 

127 code = base[:base[-1]] + [0] * 5 + base[base[-1]:-1] 

128 else: 

129 code = base[:5] + [0] * 4 + base[-1:] 

130 

131 code.insert(0, number_system_digit) 

132 weights = [3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3] 

133 weighted_sum = sum(x * y for x, y in zip(code, weights)) 

134 check_digit = (10 - weighted_sum % 10) % 10 

135 code.append(check_digit) 

136 return ''.join(str(x) for x in code) 

137 

138 def upc_a(self, upc_ae_mode=False, base=None, number_system_digit=None): 

139 """Generate a 12-digit UPC-A barcode. 

140 

141 The value of ``upc_ae_mode`` controls how barcodes will be generated. If ``False`` 

142 (default), barcodes are not guaranteed to have a UPC-E equivalent. In this mode, 

143 the method uses :meth:`ean13 <faker.providers.barcode.Provider.ean13>` under the hood, 

144 and the values of ``base`` and ``number_system_digit`` will be ignored. 

145 

146 If ``upc_ae_mode`` is ``True``, the resulting barcodes are guaranteed to have a UPC-E 

147 equivalent, and the values of ``base`` and ``number_system_digit`` will be used to 

148 control what is generated. 

149 

150 Under this mode, ``base`` is expected to have a 6-digit string value. If any other value 

151 is supplied, a random 6-digit string will be used instead. As for ``number_system_digit``, 

152 the expected value is a ``0`` or a ``1``. If any other value is provided, this method 

153 will randomly choose from the two. 

154 

155 .. important:: 

156 

157 When ``upc_ae_mode`` is enabled, you might encounter instances where different values 

158 of ``base`` (e.g. ``'120003'`` and ``'120004'``) produce the same UPC-A barcode. This 

159 is normal, and the reason lies within the whole conversion process. To learn more about 

160 this and what ``base`` and ``number_system_digit`` actually represent, please refer 

161 to :meth:`upc_e() <faker.providers.barcode.Provider.upc_e>`. 

162 

163 :sample: 

164 :sample: upc_ae_mode=True, number_system_digit=0 

165 :sample: upc_ae_mode=True, number_system_digit=1 

166 :sample: upc_ae_mode=True, base='123456', number_system_digit=0 

167 :sample: upc_ae_mode=True, base='120003', number_system_digit=0 

168 :sample: upc_ae_mode=True, base='120004', number_system_digit=0 

169 """ 

170 if upc_ae_mode is True: 

171 return self._upc_ae(base=base, number_system_digit=number_system_digit) 

172 else: 

173 ean13 = self.ean13(leading_zero=True) 

174 return ean13[1:] 

175 

176 def upc_e(self, base=None, number_system_digit=None, safe_mode=True): 

177 """Generate an 8-digit UPC-E barcode. 

178 

179 UPC-E barcodes can be expressed in 6, 7, or 8-digit formats, but this method uses the 

180 8 digit format, since it is trivial to convert to the other two formats. The first digit 

181 (starting from the left) is controlled by ``number_system_digit``, and it can only be a 

182 ``0`` or a ``1``. The last digit is the check digit that is inherited from the UPC-E barcode's 

183 UPC-A equivalent. The middle six digits are collectively referred to as the ``base`` (for a 

184 lack of a better term). 

185 

186 On that note, this method uses ``base`` and ``number_system_digit`` to first generate a 

187 UPC-A barcode for the check digit, and what happens next depends on the value of ``safe_mode``. 

188 The argument ``safe_mode`` exists, because there are some UPC-E values that share the same 

189 UPC-A equivalent. For example, any UPC-E barcode of the form ``abc0000d``, ``abc0003d``, and 

190 ``abc0004d`` share the same UPC-A value ``abc00000000d``, but that UPC-A value will only convert 

191 to ``abc0000d`` because of (a) how UPC-E is just a zero-suppressed version of UPC-A and (b) the 

192 rules around the conversion. 

193 

194 If ``safe_mode`` is ``True`` (default), this method performs another set of conversions to 

195 guarantee that the UPC-E barcodes generated can be converted to UPC-A, and that UPC-A 

196 barcode can be converted back to the original UPC-E barcode. Using the example above, even 

197 if the bases ``120003`` or ``120004`` are used, the resulting UPC-E barcode will always 

198 use the base ``120000``. 

199 

200 If ``safe_mode`` is ``False``, then the ``number_system_digit``, ``base``, and the computed 

201 check digit will just be concatenated together to produce the UPC-E barcode, and attempting 

202 to convert the barcode to UPC-A and back again to UPC-E will exhibit the behavior described 

203 above. 

204 

205 :sample: 

206 :sample: base='123456' 

207 :sample: base='123456', number_system_digit=0 

208 :sample: base='123456', number_system_digit=1 

209 :sample: base='120000', number_system_digit=0 

210 :sample: base='120003', number_system_digit=0 

211 :sample: base='120004', number_system_digit=0 

212 :sample: base='120000', number_system_digit=0, safe_mode=False 

213 :sample: base='120003', number_system_digit=0, safe_mode=False 

214 :sample: base='120004', number_system_digit=0, safe_mode=False 

215 """ 

216 if safe_mode is not False: 

217 upc_ae = self._upc_ae(base=base, number_system_digit=number_system_digit) 

218 return self._convert_upc_a2e(upc_ae) 

219 else: 

220 upc_ae = self._upc_ae(base=base, number_system_digit=number_system_digit) 

221 return upc_ae[0] + ''.join(str(x) for x in base) + upc_ae[-1]