annet 2.0.0__py3-none-any.whl → 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of annet might be problematic. Click here for more details.

@@ -28,11 +28,12 @@ def storage_factory(opts: NetboxStorageOpts) -> Storage:
28
28
 
29
29
  class NetboxProvider(StorageProvider, AdapterWithName, AdapterWithConfig):
30
30
  def __init__(self, url: Optional[str] = None, token: Optional[str] = None, insecure: bool = False,
31
- exact_host_filter: bool = False):
31
+ exact_host_filter: bool = False, threads: int = 1):
32
32
  self.url = url
33
33
  self.token = token
34
34
  self.insecure = insecure
35
35
  self.exact_host_filter = exact_host_filter
36
+ self.threads = threads
36
37
 
37
38
  @classmethod
38
39
  def with_config(cls, **kwargs: Dict[str, Any]) -> T:
@@ -32,7 +32,6 @@ def filter_config(acl: Acl, fmtr: tabparser.CommonFormatter, input_config: Input
32
32
  config = patching.apply_acl(config, acl, fatal_acl=False)
33
33
  config = fmtr.join(config)
34
34
  else:
35
- config = typing.cast(input_config, FileConfigTree)
36
35
  config = apply_acl_fileconfig(input_config, acl)
37
36
  return config
38
37
 
@@ -47,7 +46,6 @@ def filter_diff(acl: Acl, fmtr: tabparser.CommonFormatter, input_config: InputCo
47
46
  config = unshift_op(config)
48
47
  config = config.rstrip()
49
48
  else:
50
- config = typing.cast(input_config, FileConfigTree)
51
49
  config = apply_acl_fileconfig(input_config, acl)
52
50
  return config
53
51
 
@@ -108,17 +106,23 @@ def get_op(line: str) -> typing.Tuple[str, str, str]:
108
106
  indent = ""
109
107
  opidx = -1
110
108
  rowstart = 0
109
+
111
110
  for rowstart in range(len(line)):
111
+ if line[rowstart] != " " and line[0:rowstart].strip():
112
+ break
112
113
  if line[rowstart] not in diff_ops:
113
114
  break
115
+
114
116
  for opidx in range(rowstart):
115
117
  if line[opidx] != " ":
116
118
  break
119
+
117
120
  if opidx >= 0:
118
121
  op = line[opidx]
119
122
  indent = line[:opidx] + line[opidx + 1:rowstart]
120
123
  if op != " ":
121
124
  indent = indent + " "
125
+
122
126
  return op, indent, line[rowstart:]
123
127
 
124
128
 
@@ -1,3 +1,4 @@
1
+ import warnings
1
2
  from collections.abc import Callable
2
3
  from dataclasses import dataclass
3
4
  from dataclasses import field
@@ -14,6 +15,7 @@ class ThenField(str, Enum):
14
15
  large_community = "large_community"
15
16
  extcommunity_rt = "extcommunity_rt"
16
17
  extcommunity_soo = "extcommunity_soo"
18
+ extcommunity = "extcommunity"
17
19
  as_path = "as_path"
18
20
  local_pref = "local_pref"
19
21
  metric = "metric"
@@ -144,6 +146,7 @@ class StatementBuilder:
144
146
  self._added_as_path: list[int] = []
145
147
  self._community = CommunityActionValue()
146
148
  self._large_community = CommunityActionValue()
149
+ self._extcommunity = CommunityActionValue()
147
150
  self._extcommunity_rt = CommunityActionValue()
148
151
  self._extcommunity_soo = CommunityActionValue()
149
152
  self._as_path = AsPathActionValue()
@@ -165,12 +168,18 @@ class StatementBuilder:
165
168
  def large_community(self) -> CommunityActionBuilder:
166
169
  return CommunityActionBuilder(self._large_community)
167
170
 
171
+ @property
172
+ def extcommunity(self) -> CommunityActionBuilder:
173
+ return CommunityActionBuilder(self._extcommunity)
174
+
168
175
  @property
169
176
  def extcommunity_rt(self) -> CommunityActionBuilder:
177
+ warnings.warn("extcommunity_rt is deprecated, use extcommunity", DeprecationWarning, stacklevel=2)
170
178
  return CommunityActionBuilder(self._extcommunity_rt)
171
179
 
172
180
  @property
173
181
  def extcommunity_soo(self) -> CommunityActionBuilder:
182
+ warnings.warn("extcommunity_soo is deprecated, use extcommunity", DeprecationWarning, stacklevel=2)
174
183
  return CommunityActionBuilder(self._extcommunity_soo)
175
184
 
176
185
  def _set(self, field: str, value: ValueT) -> None:
@@ -244,7 +253,13 @@ class StatementBuilder:
244
253
  self._statement.then.append(SingleAction(
245
254
  field=ThenField.large_community,
246
255
  type=ActionType.CUSTOM,
247
- value=self._extcommunity_rt,
256
+ value=self._large_community,
257
+ ))
258
+ if self._extcommunity:
259
+ self._statement.then.append(SingleAction(
260
+ field=ThenField.extcommunity,
261
+ type=ActionType.CUSTOM,
262
+ value=self._extcommunity,
248
263
  ))
249
264
  if self._extcommunity_rt:
250
265
  self._statement.then.append(SingleAction(
@@ -256,7 +271,7 @@ class StatementBuilder:
256
271
  self._statement.then.append(SingleAction(
257
272
  field=ThenField.extcommunity_soo,
258
273
  type=ActionType.CUSTOM,
259
- value=self._extcommunity_rt,
274
+ value=self._extcommunity_soo,
260
275
  ))
261
276
  if self._as_path:
262
277
  self._statement.then.append(SingleAction(
@@ -1,4 +1,5 @@
1
1
  from abc import abstractmethod, ABC
2
+ from collections import defaultdict
2
3
  from collections.abc import Sequence
3
4
  from ipaddress import ip_interface
4
5
  from typing import Any, Literal, Iterable, Iterator, Optional, cast
@@ -12,7 +13,7 @@ from .aspath import get_used_as_path_filters
12
13
  from .community import get_used_united_community_lists
13
14
  from .entities import (
14
15
  AsPathFilter, IpPrefixList, CommunityList, CommunityLogic, CommunityType,
15
- mangle_united_community_list_name, PrefixListNameGenerator,
16
+ mangle_united_community_list_name, PrefixListNameGenerator, group_community_members,
16
17
  )
17
18
 
18
19
 
@@ -307,9 +308,44 @@ class CumulusPolicyGenerator(ABC):
307
308
  raise NotImplementedError("Replacing SOO extcommunity is not supported for Cumulus")
308
309
  for community_name in action.value.added:
309
310
  yield "set", "extcommunity soo", community_name, "additive"
310
- for community_name in action.value.removed:
311
+ if action.value.removed:
311
312
  raise NotImplementedError("SOO extcommunity remove is not supported for Cumulus")
312
313
 
314
+ def _cumulus_extcommunity_type_str(self, comm_type: CommunityType) -> str:
315
+ if comm_type is CommunityType.SOO:
316
+ return "soo"
317
+ elif comm_type is CommunityType.RT:
318
+ return "rt"
319
+ elif comm_type is CommunityType.LARGE:
320
+ raise ValueError("Large community is not subtype of extcommunity")
321
+ elif comm_type is CommunityType.BASIC:
322
+ raise ValueError("Basic community is not subtype of extcommunity")
323
+ else:
324
+ raise NotImplementedError(f"Community type {comm_type} is not supported on cumulus")
325
+
326
+ def _cumulus_then_extcommunity(
327
+ self,
328
+ communities: dict[str, CommunityList],
329
+ device: Any,
330
+ action: SingleAction[CommunityActionValue],
331
+ ):
332
+ if action.value.replaced is not None:
333
+ if action.value.added or action.value.removed:
334
+ raise NotImplementedError(
335
+ "Cannot set extcommunity together with add/delete on cumulus",
336
+ )
337
+ if not action.value.replaced:
338
+ yield "set", "extcommunity", "none"
339
+ return
340
+ members = group_community_members(communities, action.value.replaced)
341
+ for community_type, replaced_members in members.items():
342
+ type_str = self._cumulus_extcommunity_type_str(community_type)
343
+ yield "set", "extcommunity", type_str, *replaced_members
344
+ if action.value.added:
345
+ raise NotImplementedError("extcommunity add is not supported for Cumulus")
346
+ if action.value.removed:
347
+ raise NotImplementedError("extcommunity remove is not supported for Cumulus")
348
+
313
349
  def _cumulus_then_as_path(
314
350
  self,
315
351
  device: Any,
@@ -350,6 +386,9 @@ class CumulusPolicyGenerator(ABC):
350
386
  cast(SingleAction[CommunityActionValue], action),
351
387
  )
352
388
  return
389
+ if action.field == ThenField.extcommunity:
390
+ yield from self._cumulus_then_extcommunity(communities, device, action)
391
+ return
353
392
  if action.field == ThenField.extcommunity_rt:
354
393
  yield from self._cumulus_then_rt_community(
355
394
  communities,
@@ -1,4 +1,5 @@
1
1
  from ipaddress import IPv4Network, IPv6Network, ip_network
2
+ from collections import defaultdict
2
3
  from collections.abc import Sequence
3
4
  from dataclasses import dataclass
4
5
  from enum import Enum
@@ -124,3 +125,13 @@ class PrefixListNameGenerator:
124
125
  for x in orig_prefix.members
125
126
  ],
126
127
  )
128
+
129
+
130
+ def group_community_members(
131
+ all_communities: dict[str, CommunityList], communities: list[str],
132
+ ) -> dict[CommunityType, list[str]]:
133
+ members: dict[CommunityType, list[str]] = defaultdict(list)
134
+ for community_name in communities:
135
+ community = all_communities[community_name]
136
+ members[community.type].extend(community.members)
137
+ return members
@@ -12,7 +12,7 @@ from annet.rpl.statement_builder import AsPathActionValue, NextHopActionValue, T
12
12
  from annet.rpl_generators.entities import (
13
13
  arista_well_known_community,
14
14
  CommunityList, RDFilter, PrefixListNameGenerator, CommunityLogic, mangle_united_community_list_name,
15
- IpPrefixList,
15
+ IpPrefixList, group_community_members, CommunityType,
16
16
  )
17
17
 
18
18
 
@@ -245,6 +245,52 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
245
245
  if action.value.removed:
246
246
  raise NotImplementedError("Extcommunity_soo remove is not supported for huawei")
247
247
 
248
+ def _huawei_render_ext_community_members(
249
+ self, comm_type: CommunityType, members: list[str]
250
+ ) -> Sequence[Sequence[str]]:
251
+ if comm_type is CommunityType.SOO:
252
+ return "soo", *members
253
+ if comm_type is CommunityType.RT:
254
+ return [f"rt {member}" for member in members]
255
+ elif comm_type is CommunityType.LARGE:
256
+ raise ValueError("Large community is not subtype of extcommunity")
257
+ elif comm_type is CommunityType.BASIC:
258
+ raise ValueError("Basic community is not subtype of extcommunity")
259
+ else:
260
+ raise NotImplementedError(f"Community type {comm_type} is not supported on huawei")
261
+
262
+ def _huawei_then_extcommunity(
263
+ self,
264
+ communities: dict[str, CommunityList],
265
+ device: Any,
266
+ action: SingleAction[CommunityActionValue],
267
+ ):
268
+ if action.value.replaced is not None:
269
+ if action.value.added or action.value.removed:
270
+ raise NotImplementedError(
271
+ "Cannot set extcommunity together with add/delete on huawei",
272
+ )
273
+ if not action.value.replaced:
274
+ raise NotImplementedError(
275
+ "Cannot reset extcommunity on huawei",
276
+ )
277
+
278
+ members = group_community_members(communities, action.value.replaced)
279
+ for community_type, replaced_members in members.items():
280
+ if community_type is CommunityType.SOO:
281
+ raise NotImplementedError(
282
+ "Cannot set extcommunity soo on huawei",
283
+ )
284
+ rendered_memebers = self._huawei_render_ext_community_members(community_type, replaced_members)
285
+ yield "apply", "extcommunity", *rendered_memebers
286
+ if action.value.added:
287
+ members = group_community_members(communities, action.value.added)
288
+ for community_type, added_members in members.items():
289
+ rendered_memebers = self._huawei_render_ext_community_members(community_type, added_members)
290
+ yield "apply", "extcommunity", *rendered_memebers, "additive"
291
+ if action.value.removed:
292
+ raise NotImplementedError("Cannot remove extcommunity on huawei")
293
+
248
294
  def _huawei_then_as_path(
249
295
  self,
250
296
  device: Any,
@@ -283,6 +329,10 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
283
329
  yield from self._huawei_then_large_community(communities, device,
284
330
  cast(SingleAction[CommunityActionValue], action))
285
331
  return
332
+ if action.field == ThenField.extcommunity:
333
+ yield from self._huawei_then_extcommunity(communities, device,
334
+ cast(SingleAction[CommunityActionValue], action))
335
+ return
286
336
  if action.field == ThenField.extcommunity_rt:
287
337
  yield from self._huawei_then_extcommunity_rt(communities, device,
288
338
  cast(SingleAction[CommunityActionValue], action))
@@ -564,6 +614,51 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
564
614
  ]
565
615
  yield "set", "extcommunity", *members, "delete"
566
616
 
617
+ def _arista_extcommunity_type_str(self, comm_type: CommunityType) -> str:
618
+ if comm_type is CommunityType.SOO:
619
+ return "soo"
620
+ elif comm_type is CommunityType.RT:
621
+ return "rt"
622
+ elif comm_type is CommunityType.LARGE:
623
+ raise ValueError("Large community is not subtype of extcommunity")
624
+ elif comm_type is CommunityType.BASIC:
625
+ raise ValueError("Basic community is not subtype of extcommunity")
626
+ else:
627
+ raise NotImplementedError(f"Community type {comm_type} is not supported on arista")
628
+
629
+ def _arista_render_ext_community_members(
630
+ self, all_communities: dict[str, CommunityList], communities: list[str],
631
+ ) -> Iterator[str]:
632
+ for community_name in communities:
633
+ community = all_communities[community_name]
634
+ comm_type = self._arista_extcommunity_type_str(community.type)
635
+ for member in community.members:
636
+ yield f"{comm_type} {member}"
637
+
638
+ def _arista_then_extcommunity(
639
+ self,
640
+ communities: dict[str, CommunityList],
641
+ device: Any,
642
+ action: SingleAction[CommunityActionValue],
643
+ ):
644
+ if action.value.replaced is not None:
645
+ if action.value.added or action.value.removed:
646
+ raise NotImplementedError(
647
+ "Cannot set extcommunity together with add/delete on arista",
648
+ )
649
+ if not action.value.replaced:
650
+ yield "set", "extcommunity", "none"
651
+ return
652
+ members = list(self._arista_render_ext_community_members(communities, action.value.replaced))
653
+ yield "set extcommunity", *members
654
+ return
655
+ if action.value.added:
656
+ members = list(self._arista_render_ext_community_members(communities, action.value.added))
657
+ yield "set extcommunity", *members, "additive"
658
+ if action.value.removed:
659
+ members = list(self._arista_render_ext_community_members(communities, action.value.removed))
660
+ yield "set extcommunity", *members, "delete"
661
+
567
662
  def _arista_then_as_path(
568
663
  self,
569
664
  device: Any,
@@ -610,6 +705,9 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
610
705
  communities, device, cast(SingleAction[CommunityActionValue], action),
611
706
  )
612
707
  return
708
+ if action.field == ThenField.extcommunity:
709
+ yield from self._arista_then_extcommunity(communities, device, action)
710
+ return
613
711
  if action.field == ThenField.extcommunity_rt:
614
712
  yield from self._arista_then_extcommunity_rt(
615
713
  communities, device, cast(SingleAction[CommunityActionValue], action),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: annet
3
- Version: 2.0.0
3
+ Version: 2.0.1
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -30,7 +30,7 @@ annet/adapters/fetchers/stub/fetcher.py,sha256=FzpkIzPJBQVuNNSdXKoGzkALmIGMgo2gm
30
30
  annet/adapters/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  annet/adapters/file/provider.py,sha256=KyHuA5w9E4T3Lxeko8b5M7jRmu7yhTJVLqLkas4ue1A,6654
32
32
  annet/adapters/netbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
- annet/adapters/netbox/provider.py,sha256=3IrfZ6CfCxf-lTnJlIC2TQ8M_rDxOB_B7HGXZ92vAgA,1643
33
+ annet/adapters/netbox/provider.py,sha256=Y1WahJLNMIi_an3dY_m8h480CjyRTzKP5rqqDjFW_nc,1692
34
34
  annet/adapters/netbox/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  annet/adapters/netbox/common/client.py,sha256=PaxHG4W9H8_uunIwMBNYkLq4eQJYoO6p6gY-ciQs7Nc,2563
36
36
  annet/adapters/netbox/common/manufacturer.py,sha256=Y9kqU13q6fwYu0_HiotUKAy7OHFZngkC2s3s4IDAbDg,1745
@@ -46,7 +46,7 @@ annet/annlib/__init__.py,sha256=fT1l4xV5fqqg8HPw9HqmZVN2qwS8i6X1aIm2zGDjxKY,252
46
46
  annet/annlib/command.py,sha256=uuBddMQphtn8P5MO5kzIa8_QrtMns-k05VeKv1bcAuA,1043
47
47
  annet/annlib/diff.py,sha256=MZ6eQAU3cadQp8KaSE6uAYFtcfMDCIe_eNuVROnYkCk,4496
48
48
  annet/annlib/errors.py,sha256=jBcSFzY6Vj-FxR__vqjFm-87AwYQ0xHuAopTirii5AU,287
49
- annet/annlib/filter_acl.py,sha256=0w1VF6WcONiTYTQh0yWi6_j9rCTc_kMLAUMr0hbdkNU,7203
49
+ annet/annlib/filter_acl.py,sha256=mxDga-3SXqpPEsdQypTq0ScqUqP625lxX-DmJalEc3s,7170
50
50
  annet/annlib/jsontools.py,sha256=BS7s4rI8R9c_y3zz0zYl1l6con65oQ0MvfsC1dsXZts,5535
51
51
  annet/annlib/lib.py,sha256=lxmYrbeablFMhFtvFmADVpVShSFi9TN4gNWaBs_Ygm0,14952
52
52
  annet/annlib/output.py,sha256=_SjJ6G6bejvnTKqNHw6xeio0FT9oO3OIkLaOC3cEga4,7569
@@ -98,14 +98,14 @@ annet/rpl/match_builder.py,sha256=8hvkNWF29yIAt8cpS7P797ePm3ikZBZwrVQj7QcCsP8,47
98
98
  annet/rpl/policy.py,sha256=P1Kt-8fHFxEczeP-RwkK_wrGN0p7IR-hOApEd2vC55E,448
99
99
  annet/rpl/result.py,sha256=PHFn1zhDeqLBn07nkYw5vsoXew4nTwkklOwqvFWzBLg,141
100
100
  annet/rpl/routemap.py,sha256=SIyk73OzPp2oH_XwrDv2xczuY2Zt1VsJmB0TT5r7F5g,2593
101
- annet/rpl/statement_builder.py,sha256=sVGOYsCV0s_SFQUy2WtUyQqKy5H4MOfmRCJWGj-UOJ4,9403
101
+ annet/rpl/statement_builder.py,sha256=qKhLS34lygbhtbEIY-6jzs0Aisc6qmNc_iyk9iKPyHE,10076
102
102
  annet/rpl_generators/__init__.py,sha256=V4rAZlBaOUSjeQ5eCNmWeD7BSJLIwy0lKU_01grebpc,832
103
103
  annet/rpl_generators/aspath.py,sha256=kZakwPLfGGiXu9fC6I1z-pvy7Fe-4dy93_-lYcx39_4,2038
104
104
  annet/rpl_generators/community.py,sha256=SWpaOvoQUNISuRm41-IvGPFmntvgFv9ee4zegsMyeo0,11496
105
- annet/rpl_generators/cumulus_frr.py,sha256=OabE9Me0O0iYCl20JOq9khTNhV2tXTrB3hrx9ohI3xE,19651
106
- annet/rpl_generators/entities.py,sha256=gL_Ap7dz2ioTCbA-4DIZceeE-7-_Var5hItVz--bVcs,3343
105
+ annet/rpl_generators/cumulus_frr.py,sha256=hCUK3o3ndgKhpL92cE0CWpL--0OJYtDad6b7YfCAUyQ,21530
106
+ annet/rpl_generators/entities.py,sha256=uO78iG2zHAGra5DqzpfnBgoc6slHEc6wDLvlnoySvJc,3750
107
107
  annet/rpl_generators/execute.py,sha256=wS6e6fwcPWywsHB0gBMqZ17eF0s4YOBgDgwPB_cr5Rw,431
108
- annet/rpl_generators/policy.py,sha256=t0n4kRYT_cQIfPUXvQBfGiKwMF6GrpiHqs93KMq075s,32335
108
+ annet/rpl_generators/policy.py,sha256=j_2EtAsaxldhZZT4kRXKZacuAsGnHr3JSc2BMxr_Pow,37174
109
109
  annet/rpl_generators/prefix_lists.py,sha256=5jM5Xj0dtx5xF9ap-TgExs0ofRAzm0LN2j3aL5Ub_yc,4778
110
110
  annet/rpl_generators/rd.py,sha256=YGXgx1D2D0-pixgspXJzA6NvW8lx3AmHMxIY2l5rraI,1457
111
111
  annet/rulebook/__init__.py,sha256=oafL5HC8QHdkO9CH2q_fxohPMxOgjn-dNQa5kPjuqsA,3942
@@ -178,10 +178,10 @@ annet_generators/rpl_example/generator.py,sha256=zndIGfV4ZlTxPgAGYs7bMQvTc_tYScO
178
178
  annet_generators/rpl_example/items.py,sha256=d99HSXDHFjZq511EvGhIqRTWK3F4ZsCWfdUqFYQcyhE,772
179
179
  annet_generators/rpl_example/mesh.py,sha256=z_WgfDZZ4xnyh3cSf75igyH09hGvtexEVwy1gCD_DzA,288
180
180
  annet_generators/rpl_example/route_policy.py,sha256=z6nPb0VDeQtKD1NIg9sFvmUxBD5tVs2frfNIuKdM-5c,2318
181
- annet-2.0.0.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
182
- annet-2.0.0.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
183
- annet-2.0.0.dist-info/METADATA,sha256=9UifSXsrbaDmr4yZByRC-grykjjq-HGfyT32G1aDE6w,793
184
- annet-2.0.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
185
- annet-2.0.0.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
186
- annet-2.0.0.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
187
- annet-2.0.0.dist-info/RECORD,,
181
+ annet-2.0.1.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
182
+ annet-2.0.1.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
183
+ annet-2.0.1.dist-info/METADATA,sha256=x3cpmknanwf6tSpC5mM7i_dBbYAQZwc0f7Sn5zHNfZ0,793
184
+ annet-2.0.1.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
185
+ annet-2.0.1.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
186
+ annet-2.0.1.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
187
+ annet-2.0.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.2)
2
+ Generator: setuptools (76.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
File without changes
File without changes