annet 3.16.3__py3-none-any.whl → 3.17.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.

@@ -5,7 +5,7 @@ from typing import cast, List, Union, Iterable, Optional, TypedDict
5
5
  from annet.storage import Query
6
6
 
7
7
  FIELD_VALUE_SEPARATOR = ":"
8
- ALLOWED_GLOB_GROUPS = ["site", "tag", "role", "device_type", "status", "tenant"]
8
+ ALLOWED_GLOB_GROUPS = ["site", "tag", "role", "device_type", "status", "tenant", "asset_tag"]
9
9
 
10
10
 
11
11
  class Filter(TypedDict, total=False):
@@ -16,6 +16,7 @@ class Filter(TypedDict, total=False):
16
16
  device_type: list[str]
17
17
  status: list[str]
18
18
  tenant: list[str]
19
+ asset_tag: list[str]
19
20
 
20
21
 
21
22
  @dataclass
@@ -22,6 +22,7 @@ def storage_factory(opts: NetboxStorageOpts) -> Storage:
22
22
  "4.1": NetboxStorageV41,
23
23
  "4.2": NetboxStorageV42,
24
24
  "4.3": NetboxStorageV42,
25
+ "4.4": NetboxStorageV42,
25
26
  }
26
27
 
27
28
  status = None
annet/annlib/jsontools.py CHANGED
@@ -10,6 +10,7 @@ from typing import Any, Dict, Iterable, List, Optional
10
10
 
11
11
  import jsonpatch
12
12
  import jsonpointer
13
+ from ordered_set import OrderedSet
13
14
 
14
15
 
15
16
  def format_json(data: Any, stable: bool = False) -> str:
@@ -42,8 +43,7 @@ def apply_json_fragment(
42
43
 
43
44
  for pointer in new_pointers:
44
45
  new_value = pointer.get(new_fragment)
45
- _ensure_pointer_exists(full_new_config, pointer)
46
- pointer.set(full_new_config, new_value)
46
+ _pointer_set(pointer, full_new_config, new_value)
47
47
 
48
48
  # delete matched parts in old config whicn are not present in the new
49
49
  paths = {p.path for p in new_pointers}
@@ -56,36 +56,58 @@ def apply_json_fragment(
56
56
  return full_new_config
57
57
 
58
58
 
59
- def _ensure_pointer_exists(doc: Dict[str, Any], pointer: jsonpointer.JsonPointer) -> None:
59
+ def _pointer_set(pointer: jsonpointer.JsonPointer, doc: Any, value: Any) -> None:
60
60
  """
61
- Ensure that document has all pointer parts (if possible).
61
+ Resolve `pointer` against the `doc`, creating new elements if neccessary,
62
+ and set the target's value to `value`, all in place.
62
63
 
63
- This is workaround for errors of type:
64
+ If `pointer` in any it's part points to the non-existing key,
65
+ or if value at this point is `None`, new object will be created.
66
+ (See https://github.com/stefankoegl/python-json-pointer/issues/41)
64
67
 
65
- ```
66
- jsonpointer.JsonPointerException: member 'MY_PART' not found in {}
67
- ```
68
-
69
- See for details: https://github.com/stefankoegl/python-json-pointer/issues/41
68
+ If `pointer` in any it's part points to the index of next to be appended
69
+ element of the array, new document / `value` will be appended to that list.
70
70
  """
71
- parts_except_the_last = pointer.get_parts()[:-1]
72
- doc_pointer: Dict[str, Any] = doc
73
- for part in parts_except_the_last:
74
- if isinstance(doc_pointer, dict):
75
- if part not in doc_pointer or doc_pointer[part] is None:
76
- # create an empty object by the pointer part
77
- doc_pointer[part] = {}
78
-
79
- # follow the pointer to delve deeper
80
- doc_pointer = doc_pointer[part]
81
- else:
82
- # not a dict - cannot delve deeper
83
- break
71
+ if len(pointer.parts) == 0:
72
+ raise jsonpointer.JsonPointerException("Cannot set root in place")
73
+ *parts_expect_the_last, last_part = pointer.parts
74
+
75
+ for part in parts_expect_the_last:
76
+ key = pointer.get_part(doc, part)
77
+ if isinstance(doc, dict):
78
+ if doc.get(key, None) is None:
79
+ doc[key] = {}
80
+ elif isinstance(doc, list):
81
+ if key == len(doc):
82
+ doc.append({})
83
+ doc = doc[key]
84
+
85
+ key = pointer.get_part(doc, last_part)
86
+ if isinstance(doc, list) and key == len(doc):
87
+ doc.append(value)
88
+ else:
89
+ doc[key] = value
84
90
 
85
91
 
86
92
  def make_patch(old: Dict[str, Any], new: Dict[str, Any]) -> List[Dict[str, Any]]:
87
93
  """Generate a JSON patch by comparing the old document with the new one."""
88
- return sorted(jsonpatch.make_patch(old, new).patch, key=itemgetter("path"))
94
+ # NOTE: changing order of the patch operations (e.g. sorting)
95
+ # may interfere with the `move` logic.
96
+ # E.g.:
97
+ # ```python
98
+ # old = [["a", "b"], ["c", "d"]]
99
+ # new = [["d", "c"], ["b", "a"]]
100
+ # ```
101
+ # produces the following patch:
102
+ # ```json
103
+ # [{"op": "move", "path": "/0/0", "from": "/1/0"},
104
+ # {"op": "move", "path": "/1/0", "from": "/0/2"},
105
+ # {"op": "move", "path": "/0/0", "from": "/1/1"},
106
+ # {"op": "move", "path": "/1/1", "from": "/0/2"}]
107
+ # ```
108
+ # which relies on proper ordering to be correctly applied.
109
+ # See https://github.com/annetutil/annet/pull/452 for details.
110
+ return jsonpatch.make_patch(old, new).patch
89
111
 
90
112
 
91
113
  def apply_patch(content: Optional[bytes], patch_bytes: bytes) -> bytes:
@@ -175,12 +197,11 @@ def _apply_filters_to_json_pointers(
175
197
  pointers: Iterable[jsonpointer.JsonPointer],
176
198
  filters: Sequence[str], *,
177
199
  content: Any,
178
- ) -> list[jsonpointer.JsonPointer]:
200
+ ) -> Sequence[jsonpointer.JsonPointer]:
179
201
 
180
202
  """
181
- Takes a list of pointers, a list of filters and a document
182
- and returns a list of pointers that match at least one of the filters
183
- (if necessary, pointers may be deeper than from the input).
203
+ Takes a list of pointers, a list of filters and a document, and returns
204
+ a list of pointers that match at least one of the filters, preserving order.
184
205
 
185
206
  For example, given:
186
207
  pointers=["/foo", "/lorem/ipsum", "/lorem/dolor"],
@@ -205,7 +226,7 @@ def _apply_filters_to_json_pointers(
205
226
  ["/foo/bar/qux", "/lorem/ipsum", "/lorem/dolor"]
206
227
  """
207
228
 
208
- ret: set[jsonpointer.JsonPointer] = set()
229
+ ret = OrderedSet[jsonpointer.JsonPointer]()
209
230
  for filter_item in filters:
210
231
  filter_parts = jsonpointer.JsonPointer(filter_item).parts
211
232
  for pointer in pointers:
@@ -222,7 +243,4 @@ def _apply_filters_to_json_pointers(
222
243
  ret.update(map(pointer.join, _resolve_json_pointers(deeper_pattern, deeper_doc)))
223
244
  else:
224
245
  ret.add(pointer)
225
- # sort return value by some stable key, to decrease the chance
226
- # that some bug may lead to unstable output being produced
227
- # (since `type(ret) is set`)
228
- return sorted(ret, key=str)
246
+ return ret
annet/deploy.py CHANGED
@@ -193,7 +193,8 @@ def fill_cmd_params(rules: OrderedDict, cmd: Command):
193
193
  if rule:
194
194
  cmd_params = make_cmd_params(rule)
195
195
  cmd.questions = cmd_params.get("questions", None)
196
- cmd.timeout = cmd_params["timeout"]
196
+ if cmd.timeout is None:
197
+ cmd.timeout = cmd_params["timeout"]
197
198
 
198
199
 
199
200
  def apply_deploy_rulebook(hw: HardwareView, cmd_paths, do_finalize=True, do_commit=True):
@@ -14,7 +14,7 @@ class H3CVendor(AbstractVendor):
14
14
 
15
15
  before.add_cmd(Command("system-view"))
16
16
  if do_finalize:
17
- after.add_cmd(Command("save force", timeout=90))
17
+ after.add_cmd(Command("save force", timeout=90, read_timeout=90))
18
18
 
19
19
  return before, after
20
20
 
@@ -14,7 +14,7 @@ class JuniperVendor(AbstractVendor):
14
14
 
15
15
  before.add_cmd(Command("configure exclusive"))
16
16
  if do_commit:
17
- after.add_cmd(Command("commit", timeout=30))
17
+ after.add_cmd(Command("commit", timeout=30, read_timeout=30))
18
18
  after.add_cmd(Command("exit"))
19
19
 
20
20
  return before, after
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: annet
3
- Version: 3.16.3
3
+ Version: 3.17.1
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -20,8 +20,9 @@ Requires-Dist: valkit>=0.1.4
20
20
  Requires-Dist: yarl>=1.8.2
21
21
  Requires-Dist: adaptix==3.0.0b7
22
22
  Requires-Dist: dataclass-rest==0.4
23
+ Requires-Dist: ordered-set>=4.1.0
23
24
  Provides-Extra: netbox
24
- Requires-Dist: annetbox[sync]>=0.7.0; extra == "netbox"
25
+ Requires-Dist: annetbox[sync]>=0.8.0; extra == "netbox"
25
26
  Dynamic: home-page
26
27
  Dynamic: license
27
28
  Dynamic: license-file
@@ -5,7 +5,7 @@ annet/bgp_models.py,sha256=roSDKhxzcg9N8iEh9zI4Jww1vOObWcwRVzMl9FJnh1M,14046
5
5
  annet/cli.py,sha256=shq3hHzrTxFL3x1_zTOR43QHo0JYs8QSwyOvGtL86Co,12733
6
6
  annet/cli_args.py,sha256=d0WuGiZfe42-nMvurX1bOb3iUMg0TSK-hxk8J1THxSY,13728
7
7
  annet/connectors.py,sha256=aoiDVLPizx8CW2p8SAwGCzyO_WW8H9xc2aujbGC4bDg,4882
8
- annet/deploy.py,sha256=ZTZcomYm2KJkKMyG6XzZaAMPr-Tduwcv67BhsoLARkY,7673
8
+ annet/deploy.py,sha256=toIaR2QLvLJxpdeXoJhZzKdXfhn784ciTNZlMGT9huo,7709
9
9
  annet/deploy_ui.py,sha256=XsN1i7E9cNp1SAf1YBwhEBuwN91MvlNMSrLhnQrumA8,28672
10
10
  annet/diff.py,sha256=kD_2kxz5wc2TP10xj-BHs6IPq1yNKkXxIco8czjeC6M,9497
11
11
  annet/filtering.py,sha256=ZtqxPsKdV9reZoRxtQyBg22BqyMqd-2SotYcxZ-68AQ,903
@@ -28,13 +28,13 @@ annet/adapters/fetchers/stub/fetcher.py,sha256=FzpkIzPJBQVuNNSdXKoGzkALmIGMgo2gm
28
28
  annet/adapters/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  annet/adapters/file/provider.py,sha256=Raw5DzsGS8I9XcKNeMuk5O3dPxqeRSCNmuB5JwClRqY,7687
30
30
  annet/adapters/netbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- annet/adapters/netbox/provider.py,sha256=iUkG0Dc0wBeck1dQMysej-olFLugKW6r-GJVutjMUYs,2375
31
+ annet/adapters/netbox/provider.py,sha256=xxPVCLOFkiw56zK4iDKLYLCHJ0-5y3k325nZtGA0NPM,2408
32
32
  annet/adapters/netbox/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
33
  annet/adapters/netbox/common/adapter.py,sha256=1rR9PZAU72hIreozWFVb9CKpFVjfUH_i9kQEbaNiEUI,2639
34
34
  annet/adapters/netbox/common/client.py,sha256=PaxHG4W9H8_uunIwMBNYkLq4eQJYoO6p6gY-ciQs7Nc,2563
35
35
  annet/adapters/netbox/common/manufacturer.py,sha256=9jTfzwx5XmETrjSbIJu_FhNaByaUbGQE787c5rBor-4,1137
36
36
  annet/adapters/netbox/common/models.py,sha256=nMruBugAKNY8D63rqUnPYyMYGMIU-haxX0vnfxLGNWc,8990
37
- annet/adapters/netbox/common/query.py,sha256=0GeIiW_ttnESaNKHIziF61P-3DX0XD592UHQGn_1m60,1911
37
+ annet/adapters/netbox/common/query.py,sha256=mz5oOn5vjU-lCV48TedvtXZ14GtBXU3jpcXa_cNawWg,1949
38
38
  annet/adapters/netbox/common/status_client.py,sha256=POaqiQJ0jPcqUQH-X_fWHVnKB7TBYveNriaT0eNTlfI,769
39
39
  annet/adapters/netbox/common/storage_base.py,sha256=lMRiDkzQylpA5Fxa4B6CUOWzq2d1cpjeSDbFGp8jH7o,9787
40
40
  annet/adapters/netbox/common/storage_opts.py,sha256=8LCxX0m-Wd-jNZ2iRAzkBJnbDg-MuE7RS17uPxjZo3o,2066
@@ -55,7 +55,7 @@ annet/annlib/command.py,sha256=GsgzQ8oFiHFan1Tfyo340rWgfhyU4_sCKJGhBm2jG6Y,1258
55
55
  annet/annlib/diff.py,sha256=MZ6eQAU3cadQp8KaSE6uAYFtcfMDCIe_eNuVROnYkCk,4496
56
56
  annet/annlib/errors.py,sha256=jBcSFzY6Vj-FxR__vqjFm-87AwYQ0xHuAopTirii5AU,287
57
57
  annet/annlib/filter_acl.py,sha256=ZJvNpSwE5MzJS_sKLenQpZgTuM-IrngwcbukQRq90do,7195
58
- annet/annlib/jsontools.py,sha256=-vTVtOL6ZplXSb9f2doNtrocIjU80OH4ua0X94vpCl4,7348
58
+ annet/annlib/jsontools.py,sha256=licizwJye0HYm3YAOHinhNIom70DyW8rQtwodx7iCkM,8055
59
59
  annet/annlib/lib.py,sha256=lxmYrbeablFMhFtvFmADVpVShSFi9TN4gNWaBs_Ygm0,14952
60
60
  annet/annlib/output.py,sha256=g8d_LyNg5snywt0iPQuL9OFpoxKMdN59VW-vcrKitTQ,7674
61
61
  annet/annlib/patching.py,sha256=1bxlLDRx15GjB6aNk6kAx7t3WPPLHffYvSy7VLGnCig,21506
@@ -198,18 +198,18 @@ annet/vendors/library/arista.py,sha256=J4ltZ7sS_TgIECg2U7fizvxwfS4-s35Of0tNDNWWY
198
198
  annet/vendors/library/aruba.py,sha256=tvrSFSA43n0uelCv-NLQnqxO01d0y2mrfhncpOX7zoQ,1257
199
199
  annet/vendors/library/b4com.py,sha256=EABOc-w8XqFGI0hcGU5JjCfXAf9v_Uf4MVdFT5gLZ7Q,1462
200
200
  annet/vendors/library/cisco.py,sha256=3-hWdMaQPnUVFJzKX4gNwIIoGxrSqrGrrE0p7QvedyU,1176
201
- annet/vendors/library/h3c.py,sha256=yYvKXCrhznbiHybP5nTHR45TVYI08A4Ot_xQgVUgELU,1125
201
+ annet/vendors/library/h3c.py,sha256=e2RIvemtLH4S1gukYJJU4MNVEukPzaHBR7FF2xht-vY,1142
202
202
  annet/vendors/library/huawei.py,sha256=79RINBbuob8KJXQvULbEGVVaGhn3-LzCYuqWQ847eds,1261
203
203
  annet/vendors/library/iosxr.py,sha256=0eFPbiE0jry_IATDxK3-UsPPMkK2gs9-avR51v2HAWw,1185
204
- annet/vendors/library/juniper.py,sha256=BDljknxDJ0JxEXb3HCbZSC38BuA7OF5mtcQLLftXx_o,1289
204
+ annet/vendors/library/juniper.py,sha256=DuVpJ_Xa9PtYGC4k_dyHrLrQAnxbzGpZ9vbqymFbOAo,1306
205
205
  annet/vendors/library/nexus.py,sha256=naTtiaFzNt1YZmxUSR9UCa-z766r1oav0tPXinA2tho,1118
206
206
  annet/vendors/library/nokia.py,sha256=RhynYZpti5ftud88vaphLAA3ZrCEKdR8BESXWlUZIH8,1152
207
207
  annet/vendors/library/optixtrans.py,sha256=VdME69Ca4HAEgoaKN21fZxnmmsqqaxOe_HZjay9T8j0,632
208
208
  annet/vendors/library/pc.py,sha256=vfv31_NPi7M-4AUDL89UcpawK2E6xvCpELA209cd1ho,1086
209
209
  annet/vendors/library/ribbon.py,sha256=DDOBq-_-FL9dCxqXs2inEWZ-pvw-dJ-A-prA7cKMhec,1216
210
210
  annet/vendors/library/routeros.py,sha256=iQa7m_4wjuvcgBOI9gyZwlw1BvzJfOkvUbyoEk-NI9I,1254
211
- annet-3.16.3.dist-info/licenses/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
212
- annet-3.16.3.dist-info/licenses/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
211
+ annet-3.17.1.dist-info/licenses/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
212
+ annet-3.17.1.dist-info/licenses/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
213
213
  annet_generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
214
214
  annet_generators/example/__init__.py,sha256=OJ77uj8axc-FIyIu_Xdcnzmde3oQW5mk5qbODkhuVc8,355
215
215
  annet_generators/example/hostname.py,sha256=RloLzNVetEoWPLITzfJ13Nk3CC0yi-cZB1RTd6dnuhI,2541
@@ -222,8 +222,8 @@ annet_generators/rpl_example/generator.py,sha256=EWah19gOH8G-QyNyWqxCqdRi0BK7GbM
222
222
  annet_generators/rpl_example/items.py,sha256=HPgxScDvSqJPdz0c2SppDrH82DZYC4zUaniQwcWmh4A,1176
223
223
  annet_generators/rpl_example/mesh.py,sha256=z_WgfDZZ4xnyh3cSf75igyH09hGvtexEVwy1gCD_DzA,288
224
224
  annet_generators/rpl_example/route_policy.py,sha256=z6nPb0VDeQtKD1NIg9sFvmUxBD5tVs2frfNIuKdM-5c,2318
225
- annet-3.16.3.dist-info/METADATA,sha256=uYHvn35xiYFMhdKtk4gtGZX2jPG3MAlrWuzeJUjjMt8,816
226
- annet-3.16.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
227
- annet-3.16.3.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
228
- annet-3.16.3.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
229
- annet-3.16.3.dist-info/RECORD,,
225
+ annet-3.17.1.dist-info/METADATA,sha256=EQkXfefC9u4bnO2LEjZmSk1xAEHYwDpGx1tH1jcHdNk,850
226
+ annet-3.17.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
227
+ annet-3.17.1.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
228
+ annet-3.17.1.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
229
+ annet-3.17.1.dist-info/RECORD,,
File without changes