annet 0.7__tar.gz → 0.8__tar.gz

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.

Files changed (136) hide show
  1. {annet-0.7/annet.egg-info → annet-0.8}/PKG-INFO +1 -1
  2. {annet-0.7 → annet-0.8}/annet/annlib/jsontools.py +23 -0
  3. {annet-0.7 → annet-0.8}/annet/gen.py +28 -2
  4. {annet-0.7 → annet-0.8}/annet/generators/__init__.py +77 -64
  5. {annet-0.7 → annet-0.8/annet.egg-info}/PKG-INFO +1 -1
  6. {annet-0.7 → annet-0.8}/AUTHORS +0 -0
  7. {annet-0.7 → annet-0.8}/LICENSE +0 -0
  8. {annet-0.7 → annet-0.8}/MANIFEST.in +0 -0
  9. {annet-0.7 → annet-0.8}/README.md +0 -0
  10. {annet-0.7 → annet-0.8}/annet/__init__.py +0 -0
  11. {annet-0.7 → annet-0.8}/annet/adapters/__init__.py +0 -0
  12. {annet-0.7 → annet-0.8}/annet/adapters/netbox/__init__.py +0 -0
  13. {annet-0.7 → annet-0.8}/annet/adapters/netbox/common/__init__.py +0 -0
  14. {annet-0.7 → annet-0.8}/annet/adapters/netbox/common/client.py +0 -0
  15. {annet-0.7 → annet-0.8}/annet/adapters/netbox/common/manufacturer.py +0 -0
  16. {annet-0.7 → annet-0.8}/annet/adapters/netbox/common/models.py +0 -0
  17. {annet-0.7 → annet-0.8}/annet/adapters/netbox/common/query.py +0 -0
  18. {annet-0.7 → annet-0.8}/annet/adapters/netbox/common/status_client.py +0 -0
  19. {annet-0.7 → annet-0.8}/annet/adapters/netbox/common/storage_opts.py +0 -0
  20. {annet-0.7 → annet-0.8}/annet/adapters/netbox/provider.py +0 -0
  21. {annet-0.7 → annet-0.8}/annet/adapters/netbox/v24/__init__.py +0 -0
  22. {annet-0.7 → annet-0.8}/annet/adapters/netbox/v24/api_models.py +0 -0
  23. {annet-0.7 → annet-0.8}/annet/adapters/netbox/v24/client.py +0 -0
  24. {annet-0.7 → annet-0.8}/annet/adapters/netbox/v24/storage.py +0 -0
  25. {annet-0.7 → annet-0.8}/annet/adapters/netbox/v37/__init__.py +0 -0
  26. {annet-0.7 → annet-0.8}/annet/adapters/netbox/v37/api_models.py +0 -0
  27. {annet-0.7 → annet-0.8}/annet/adapters/netbox/v37/client.py +0 -0
  28. {annet-0.7 → annet-0.8}/annet/adapters/netbox/v37/storage.py +0 -0
  29. {annet-0.7 → annet-0.8}/annet/annet.py +0 -0
  30. {annet-0.7 → annet-0.8}/annet/annlib/__init__.py +0 -0
  31. {annet-0.7 → annet-0.8}/annet/annlib/command.py +0 -0
  32. {annet-0.7 → annet-0.8}/annet/annlib/diff.py +0 -0
  33. {annet-0.7 → annet-0.8}/annet/annlib/errors.py +0 -0
  34. {annet-0.7 → annet-0.8}/annet/annlib/filter_acl.py +0 -0
  35. {annet-0.7 → annet-0.8}/annet/annlib/lib.py +0 -0
  36. {annet-0.7 → annet-0.8}/annet/annlib/netdev/__init__.py +0 -0
  37. {annet-0.7 → annet-0.8}/annet/annlib/netdev/db.py +0 -0
  38. {annet-0.7 → annet-0.8}/annet/annlib/netdev/devdb/__init__.py +0 -0
  39. {annet-0.7 → annet-0.8}/annet/annlib/netdev/devdb/data/devdb.json +0 -0
  40. {annet-0.7 → annet-0.8}/annet/annlib/netdev/views/__init__.py +0 -0
  41. {annet-0.7 → annet-0.8}/annet/annlib/netdev/views/dump.py +0 -0
  42. {annet-0.7 → annet-0.8}/annet/annlib/netdev/views/hardware.py +0 -0
  43. {annet-0.7 → annet-0.8}/annet/annlib/output.py +0 -0
  44. {annet-0.7 → annet-0.8}/annet/annlib/patching.py +0 -0
  45. {annet-0.7 → annet-0.8}/annet/annlib/rbparser/__init__.py +0 -0
  46. {annet-0.7 → annet-0.8}/annet/annlib/rbparser/acl.py +0 -0
  47. {annet-0.7 → annet-0.8}/annet/annlib/rbparser/deploying.py +0 -0
  48. {annet-0.7 → annet-0.8}/annet/annlib/rbparser/ordering.py +0 -0
  49. {annet-0.7 → annet-0.8}/annet/annlib/rbparser/platform.py +0 -0
  50. {annet-0.7 → annet-0.8}/annet/annlib/rbparser/syntax.py +0 -0
  51. {annet-0.7 → annet-0.8}/annet/annlib/rulebook/__init__.py +0 -0
  52. {annet-0.7 → annet-0.8}/annet/annlib/rulebook/common.py +0 -0
  53. {annet-0.7 → annet-0.8}/annet/annlib/tabparser.py +0 -0
  54. {annet-0.7 → annet-0.8}/annet/annlib/types.py +0 -0
  55. {annet-0.7 → annet-0.8}/annet/api/__init__.py +0 -0
  56. {annet-0.7 → annet-0.8}/annet/argparse.py +0 -0
  57. {annet-0.7 → annet-0.8}/annet/cli.py +0 -0
  58. {annet-0.7 → annet-0.8}/annet/cli_args.py +0 -0
  59. {annet-0.7 → annet-0.8}/annet/configs/context.yml +0 -0
  60. {annet-0.7 → annet-0.8}/annet/configs/logging.yaml +0 -0
  61. {annet-0.7 → annet-0.8}/annet/connectors.py +0 -0
  62. {annet-0.7 → annet-0.8}/annet/deploy.py +0 -0
  63. {annet-0.7 → annet-0.8}/annet/diff.py +0 -0
  64. {annet-0.7 → annet-0.8}/annet/executor.py +0 -0
  65. {annet-0.7 → annet-0.8}/annet/filtering.py +0 -0
  66. {annet-0.7 → annet-0.8}/annet/generators/common/__init__.py +0 -0
  67. {annet-0.7 → annet-0.8}/annet/generators/common/initial.py +0 -0
  68. {annet-0.7 → annet-0.8}/annet/hardware.py +0 -0
  69. {annet-0.7 → annet-0.8}/annet/implicit.py +0 -0
  70. {annet-0.7 → annet-0.8}/annet/lib.py +0 -0
  71. {annet-0.7 → annet-0.8}/annet/output.py +0 -0
  72. {annet-0.7 → annet-0.8}/annet/parallel.py +0 -0
  73. {annet-0.7 → annet-0.8}/annet/patching.py +0 -0
  74. {annet-0.7 → annet-0.8}/annet/reference.py +0 -0
  75. {annet-0.7 → annet-0.8}/annet/rulebook/__init__.py +0 -0
  76. {annet-0.7 → annet-0.8}/annet/rulebook/arista/__init__.py +0 -0
  77. {annet-0.7 → annet-0.8}/annet/rulebook/arista/iface.py +0 -0
  78. {annet-0.7 → annet-0.8}/annet/rulebook/aruba/__init__.py +0 -0
  79. {annet-0.7 → annet-0.8}/annet/rulebook/aruba/ap_env.py +0 -0
  80. {annet-0.7 → annet-0.8}/annet/rulebook/aruba/misc.py +0 -0
  81. {annet-0.7 → annet-0.8}/annet/rulebook/cisco/__init__.py +0 -0
  82. {annet-0.7 → annet-0.8}/annet/rulebook/cisco/iface.py +0 -0
  83. {annet-0.7 → annet-0.8}/annet/rulebook/cisco/misc.py +0 -0
  84. {annet-0.7 → annet-0.8}/annet/rulebook/cisco/vlandb.py +0 -0
  85. {annet-0.7 → annet-0.8}/annet/rulebook/common.py +0 -0
  86. {annet-0.7 → annet-0.8}/annet/rulebook/deploying.py +0 -0
  87. {annet-0.7 → annet-0.8}/annet/rulebook/huawei/__init__.py +0 -0
  88. {annet-0.7 → annet-0.8}/annet/rulebook/huawei/aaa.py +0 -0
  89. {annet-0.7 → annet-0.8}/annet/rulebook/huawei/bgp.py +0 -0
  90. {annet-0.7 → annet-0.8}/annet/rulebook/huawei/iface.py +0 -0
  91. {annet-0.7 → annet-0.8}/annet/rulebook/huawei/misc.py +0 -0
  92. {annet-0.7 → annet-0.8}/annet/rulebook/huawei/vlandb.py +0 -0
  93. {annet-0.7 → annet-0.8}/annet/rulebook/juniper/__init__.py +0 -0
  94. {annet-0.7 → annet-0.8}/annet/rulebook/nexus/__init__.py +0 -0
  95. {annet-0.7 → annet-0.8}/annet/rulebook/nexus/iface.py +0 -0
  96. {annet-0.7 → annet-0.8}/annet/rulebook/patching.py +0 -0
  97. {annet-0.7 → annet-0.8}/annet/rulebook/ribbon/__init__.py +0 -0
  98. {annet-0.7 → annet-0.8}/annet/rulebook/texts/arista.deploy +0 -0
  99. {annet-0.7 → annet-0.8}/annet/rulebook/texts/arista.order +0 -0
  100. {annet-0.7 → annet-0.8}/annet/rulebook/texts/arista.rul +0 -0
  101. {annet-0.7 → annet-0.8}/annet/rulebook/texts/aruba.deploy +0 -0
  102. {annet-0.7 → annet-0.8}/annet/rulebook/texts/aruba.order +0 -0
  103. {annet-0.7 → annet-0.8}/annet/rulebook/texts/aruba.rul +0 -0
  104. {annet-0.7 → annet-0.8}/annet/rulebook/texts/cisco.deploy +0 -0
  105. {annet-0.7 → annet-0.8}/annet/rulebook/texts/cisco.order +0 -0
  106. {annet-0.7 → annet-0.8}/annet/rulebook/texts/cisco.rul +0 -0
  107. {annet-0.7 → annet-0.8}/annet/rulebook/texts/huawei.deploy +0 -0
  108. {annet-0.7 → annet-0.8}/annet/rulebook/texts/huawei.order +0 -0
  109. {annet-0.7 → annet-0.8}/annet/rulebook/texts/huawei.rul +0 -0
  110. {annet-0.7 → annet-0.8}/annet/rulebook/texts/juniper.rul +0 -0
  111. {annet-0.7 → annet-0.8}/annet/rulebook/texts/nexus.deploy +0 -0
  112. {annet-0.7 → annet-0.8}/annet/rulebook/texts/nexus.order +0 -0
  113. {annet-0.7 → annet-0.8}/annet/rulebook/texts/nexus.rul +0 -0
  114. {annet-0.7 → annet-0.8}/annet/rulebook/texts/nokia.rul +0 -0
  115. {annet-0.7 → annet-0.8}/annet/rulebook/texts/pc.order +0 -0
  116. {annet-0.7 → annet-0.8}/annet/rulebook/texts/pc.rul +0 -0
  117. {annet-0.7 → annet-0.8}/annet/rulebook/texts/ribbon.deploy +0 -0
  118. {annet-0.7 → annet-0.8}/annet/rulebook/texts/ribbon.rul +0 -0
  119. {annet-0.7 → annet-0.8}/annet/rulebook/texts/routeros.order +0 -0
  120. {annet-0.7 → annet-0.8}/annet/rulebook/texts/routeros.rul +0 -0
  121. {annet-0.7 → annet-0.8}/annet/storage.py +0 -0
  122. {annet-0.7 → annet-0.8}/annet/tabparser.py +0 -0
  123. {annet-0.7 → annet-0.8}/annet/text_term_format.py +0 -0
  124. {annet-0.7 → annet-0.8}/annet/tracing.py +0 -0
  125. {annet-0.7 → annet-0.8}/annet/types.py +0 -0
  126. {annet-0.7 → annet-0.8}/annet.egg-info/SOURCES.txt +0 -0
  127. {annet-0.7 → annet-0.8}/annet.egg-info/dependency_links.txt +0 -0
  128. {annet-0.7 → annet-0.8}/annet.egg-info/entry_points.txt +0 -0
  129. {annet-0.7 → annet-0.8}/annet.egg-info/requires.txt +0 -0
  130. {annet-0.7 → annet-0.8}/annet.egg-info/top_level.txt +0 -0
  131. {annet-0.7 → annet-0.8}/annet_generators/__init__.py +0 -0
  132. {annet-0.7 → annet-0.8}/annet_generators/example/__init__.py +0 -0
  133. {annet-0.7 → annet-0.8}/annet_generators/example/lldp.py +0 -0
  134. {annet-0.7 → annet-0.8}/requirements.txt +0 -0
  135. {annet-0.7 → annet-0.8}/setup.cfg +0 -0
  136. {annet-0.7 → annet-0.8}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: annet
3
- Version: 0.7
3
+ Version: 0.8
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -87,3 +87,26 @@ def apply_patch(content: Optional[bytes], patch_bytes: bytes) -> bytes:
87
87
 
88
88
  new_contents = format_json(new_doc, stable=True).encode()
89
89
  return new_contents
90
+
91
+
92
+ def apply_acl_filters(content: Dict[str, Any], filters: List[str]) -> Dict[str, Any]:
93
+ result = {}
94
+ for f in filters:
95
+ pointer = jsonpointer.JsonPointer(f)
96
+
97
+ try:
98
+ part = pointer.get(copy.deepcopy(content))
99
+
100
+ sub_tree = result
101
+ for i in pointer.get_parts():
102
+ if i not in sub_tree:
103
+ sub_tree[i] = {}
104
+ sub_tree = sub_tree[i]
105
+
106
+ patch = jsonpatch.JsonPatch([{"op": "add", "path": f, "value": part}])
107
+ result = patch.apply(result)
108
+ except jsonpointer.JsonPointerException:
109
+ # no value found in new_fragment by the pointer, skip the ACL item
110
+ continue
111
+
112
+ return result
@@ -182,6 +182,7 @@ def _old_new_per_device(ctx: OldNewDeviceContext, device: Device, filterer: Filt
182
182
  if not ctx.args.no_acl:
183
183
  acl_rules = generators.compile_acl_text(res.acl_text(), device.hw.vendor)
184
184
  old = (old and patching.apply_acl(old, acl_rules))
185
+
185
186
  new = patching.apply_acl(
186
187
  new,
187
188
  acl_rules,
@@ -232,11 +233,29 @@ def _old_new_per_device(ctx: OldNewDeviceContext, device: Device, filterer: Filt
232
233
 
233
234
  entire_results = res.entire_results
234
235
  json_fragment_results = res.json_fragment_results
236
+ old_json_fragment_files = old_files.json_fragment_files
237
+
235
238
  new_files = res.new_files()
236
- new_json_fragment_files = res.new_json_fragment_files(old_files.json_fragment_files)
239
+ new_json_fragment_files = res.new_json_fragment_files(old_json_fragment_files)
240
+
237
241
  if ctx.args.acl_safe:
238
242
  safe_new_files = res.new_files(safe=True)
239
243
 
244
+ filters = build_filter_text(filterer, device, ctx.stdin, ctx.args, ctx.config).split("\n")
245
+
246
+ for file_name in new_json_fragment_files:
247
+ new_json_fragment_files = _update_json_config(
248
+ new_json_fragment_files,
249
+ file_name,
250
+ jsontools.apply_acl_filters(new_json_fragment_files[file_name][0], filters)
251
+ )
252
+ for file_name in old_json_fragment_files:
253
+ old_json_fragment_files = _update_json_config(
254
+ old_json_fragment_files,
255
+ file_name,
256
+ jsontools.apply_acl_filters(old_json_fragment_files[file_name][0], filters)
257
+ )
258
+
240
259
  if ctx.args.profile:
241
260
  perf = res.perf_mesures()
242
261
  combined_perf[ALL_GENS] = {"total": time.monotonic() - start}
@@ -253,7 +272,7 @@ def _old_new_per_device(ctx: OldNewDeviceContext, device: Device, filterer: Filt
253
272
  new_files=new_files,
254
273
  partial_result=partial_results,
255
274
  entire_result=entire_results,
256
- old_json_fragment_files=old_files.json_fragment_files,
275
+ old_json_fragment_files=old_json_fragment_files,
257
276
  new_json_fragment_files=new_json_fragment_files,
258
277
  json_fragment_result=json_fragment_results,
259
278
  implicit_rules=implicit_rules,
@@ -266,6 +285,13 @@ def _old_new_per_device(ctx: OldNewDeviceContext, device: Device, filterer: Filt
266
285
  )
267
286
 
268
287
 
288
+ def _update_json_config(json_files, file_name, new_config):
289
+ file = list(json_files[file_name])
290
+ file[0] = new_config
291
+ json_files[file_name] = tuple(file)
292
+ return json_files
293
+
294
+
269
295
  @dataclasses.dataclass
270
296
  class DeviceDownloadedFiles:
271
297
  # map file path to file content for entire generators
@@ -296,8 +296,8 @@ def run_partial_generators(gens: List["PartialGenerator"], run_args: GeneratorPa
296
296
  for gen in gens:
297
297
  try:
298
298
  result = _run_partial_generator(gen, run_args)
299
- except NotSupportedDevice:
300
- logger.info("generator %s is not supported for this device, skip generator for this devices!", gen)
299
+ except NotSupportedDevice as exc:
300
+ logger.info("generator %s raised unsupported error: %r", gen, exc)
301
301
  continue
302
302
 
303
303
  if not result:
@@ -317,13 +317,17 @@ def run_partial_generators(gens: List["PartialGenerator"], run_args: GeneratorPa
317
317
 
318
318
 
319
319
  @tracing.function(name="run_partial_generator")
320
- def _run_partial_generator(gen: "PartialGenerator", run_args: GeneratorPartialRunArgs) -> GeneratorPartialResult:
320
+ def _run_partial_generator(gen: "PartialGenerator", run_args: GeneratorPartialRunArgs) -> Optional[GeneratorPartialResult]:
321
321
  logger = get_logger(generator=_make_generator_ctx(gen))
322
322
  device = run_args.device
323
323
  output = ""
324
324
  config = odict()
325
325
  safe_config = odict()
326
326
 
327
+ if not gen.supports_device(device):
328
+ logger.info("generator %s is not supported for device %s", gen, device.hostname)
329
+ return None
330
+
327
331
  span = tracing_connector.get().get_current_span()
328
332
  if span:
329
333
  tracing_connector.get().set_device_attributes(span, run_args.device)
@@ -439,8 +443,8 @@ def run_file_generators(
439
443
  raise RuntimeError(f"Unknown generator class type: cls={gen.__class__} TYPE={gen.__class__.TYPE}")
440
444
  try:
441
445
  result = run_generator_fn(gen, device, storage)
442
- except NotSupportedDevice:
443
- logger.info("generator %s is not supported for this device", gen)
446
+ except NotSupportedDevice as exc:
447
+ logger.info("generator %s raised unsupported error: %r", gen, exc)
444
448
  continue
445
449
  if result:
446
450
  add_result_fn(result)
@@ -450,33 +454,34 @@ def run_file_generators(
450
454
 
451
455
  @tracing.function(min_duration="0.5")
452
456
  def _run_entire_generator(gen: "Entire", device: "Device", storage: Storage) -> Optional[GeneratorResult]:
457
+ logger = get_logger(generator=_make_generator_ctx(gen))
458
+ if not gen.supports_device(device):
459
+ logger.info("generator %s is not supported for device %s", gen, device.hostname)
460
+ return
461
+
453
462
  span = tracing_connector.get().get_current_span()
454
463
  if span:
455
464
  tracing_connector.get().set_device_attributes(span, device)
456
465
  tracing_connector.get().set_dimensions_attributes(span, gen, device)
457
466
 
458
- logger = get_logger(generator=_make_generator_ctx(gen))
459
467
  path = gen.path(device)
460
- if path:
461
- logger.info("Generating ENTIRE ...")
462
-
463
- with GeneratorPerfMesurer(gen, storage, trace_min_duration="0.5") as pm:
464
- output = gen(device)
465
-
466
- reload_cmds = gen.get_reload_cmds(device)
467
- prio = gen.prio
468
-
469
- return GeneratorEntireResult(
470
- name=gen.__class__.__name__,
471
- tags=gen.TAGS,
472
- path=path,
473
- output=output,
474
- reload=reload_cmds,
475
- prio=prio,
476
- perf=pm.last_result,
477
- is_safe=gen.is_safe(device),
478
- )
479
- return None
468
+ if not path:
469
+ raise RuntimeError("entire generator should return non-empty path")
470
+
471
+ logger.info("Generating ENTIRE ...")
472
+ with GeneratorPerfMesurer(gen, storage, trace_min_duration="0.5") as pm:
473
+ output = gen(device)
474
+
475
+ return GeneratorEntireResult(
476
+ name=gen.__class__.__name__,
477
+ tags=gen.TAGS,
478
+ path=path,
479
+ output=output,
480
+ reload=gen.get_reload_cmds(device),
481
+ prio=gen.prio,
482
+ perf=pm.last_result,
483
+ is_safe=gen.is_safe(device),
484
+ )
480
485
 
481
486
 
482
487
  def _make_generator_ctx(gen):
@@ -489,34 +494,36 @@ def _run_json_fragment_generator(
489
494
  storage: Storage,
490
495
  ) -> Optional[GeneratorResult]:
491
496
  logger = get_logger(generator=_make_generator_ctx(gen))
497
+ if not gen.supports_device(device):
498
+ logger.info("generator %s is not supported for device %s", gen, device.hostname)
499
+
492
500
  path = gen.path(device)
501
+ if not path:
502
+ raise RuntimeError("json fragment generator should return non-empty path")
493
503
 
494
504
  acl_item_or_list_of_items = gen.acl(device)
505
+ if not acl_item_or_list_of_items:
506
+ raise RuntimeError("json fragment generator should return non-empty acl")
495
507
  if isinstance(acl_item_or_list_of_items, list):
496
508
  acl = acl_item_or_list_of_items
497
509
  else:
498
510
  acl = [acl_item_or_list_of_items]
499
511
 
500
- if path:
501
- logger.info("Generating JSON_FRAGMENT ...")
502
-
503
- with GeneratorPerfMesurer(gen, storage) as pm:
504
- config = gen(device)
505
-
506
- reload_cmds = gen.get_reload_cmds(device)
507
-
508
- return GeneratorJSONFragmentResult(
509
- name=gen.__class__.__name__,
510
- tags=gen.TAGS,
511
- path=path,
512
- acl=acl,
513
- config=config,
514
- reload=reload_cmds,
515
- perf=pm.last_result,
516
- is_safe=gen.is_safe(device),
517
- reload_prio=gen.reload_prio,
518
- )
519
- return None
512
+ logger.info("Generating JSON_FRAGMENT ...")
513
+ with GeneratorPerfMesurer(gen, storage) as pm:
514
+ config = gen(device)
515
+ reload_cmds = gen.get_reload_cmds(device)
516
+ return GeneratorJSONFragmentResult(
517
+ name=gen.__class__.__name__,
518
+ tags=gen.TAGS,
519
+ path=path,
520
+ acl=acl,
521
+ config=config,
522
+ reload=reload_cmds,
523
+ perf=pm.last_result,
524
+ is_safe=gen.is_safe(device),
525
+ reload_prio=gen.reload_prio,
526
+ )
520
527
 
521
528
 
522
529
  def _get_generators(module_paths: Union[List[str], dict], storage, device=None):
@@ -587,7 +594,7 @@ class BaseGenerator:
587
594
  TYPE: str
588
595
  TAGS: list[str]
589
596
 
590
- def supports_vendor(self, vendor: str) -> bool: # pylint: disable=unused-argument
597
+ def supports_device(self, device: Device) -> bool: # pylint: disable=unused-argument
591
598
  return True
592
599
 
593
600
 
@@ -681,36 +688,36 @@ class PartialGenerator(TreeGenerator):
681
688
  self._annotations = []
682
689
  self._annotation_module = self.__class__.__module__ or ""
683
690
 
684
- def supports_vendor(self, vendor: str) -> bool:
691
+ def supports_device(self, device: Device) -> bool:
685
692
  if self.__class__.run is PartialGenerator.run:
686
- return hasattr(self, f"run_{vendor}")
693
+ return bool(self._get_vendor_func(device.hw.vendor, "run"))
687
694
  else:
688
695
  return True
689
696
 
690
697
  def acl(self, device):
691
- if hasattr(self, "acl_" + device.hw.vendor):
692
- return getattr(self, "acl_" + device.hw.vendor)(device)
698
+ acl_func = self._get_vendor_func(device.hw.vendor, "acl")
699
+ if acl_func:
700
+ return acl_func(device)
693
701
 
694
702
  def acl_safe(self, device):
695
- if hasattr(self, "acl_safe_" + device.hw.vendor):
696
- return getattr(self, "acl_safe_" + device.hw.vendor)(device)
703
+ acl_func = self._get_vendor_func(device.hw.vendor, "acl_safe")
704
+ if acl_func:
705
+ return acl_func(device)
697
706
 
698
707
  def run(self, device) -> Iterable[Union[str, tuple]]:
699
- if hasattr(self, "run_" + device.hw.vendor):
700
- return getattr(self, "run_" + device.hw.vendor)(device)
701
- logger = get_logger()
702
- logger.info(
703
- "generator %s is not supported for vendor %s",
704
- self,
705
- device.hw.vendor,
706
- )
707
- return iter(())
708
+ run_func = self._get_vendor_func(device.hw.vendor, "run")
709
+ if run_func:
710
+ return run_func(device)
708
711
 
709
712
  def get_user_runner(self, device):
710
713
  if self.__class__.run is not PartialGenerator.run:
711
714
  return self.run
712
- elif hasattr(self, "run_" + device.hw.vendor):
713
- return getattr(self, "run_" + device.hw.vendor)
715
+ return self._get_vendor_func(device.hw.vendor, "run")
716
+
717
+ def _get_vendor_func(self, vendor: str, func_name: str):
718
+ attr_name = f"{func_name}_{vendor}"
719
+ if hasattr(self, attr_name):
720
+ return getattr(self, attr_name)
714
721
  return None
715
722
 
716
723
  # =====
@@ -790,6 +797,9 @@ class Entire(BaseGenerator):
790
797
  self.prio = 100
791
798
  self.__device = None
792
799
 
800
+ def supports_device(self, device: Device):
801
+ return bool(self.path(device))
802
+
793
803
  def run(self, device) -> Union[None, str, Iterable[Union[str, tuple]]]:
794
804
  raise NotImplementedError
795
805
 
@@ -907,6 +917,9 @@ class JSONFragment(TreeGenerator):
907
917
  if not hasattr(self, "reload_prio"):
908
918
  self.reload_prio = 100
909
919
 
920
+ def supports_device(self, device: Device):
921
+ return bool(self.path(device))
922
+
910
923
  def path(self, device: Device) -> Optional[str]:
911
924
  raise NotImplementedError("Required PATH for JSON_FRAGMENT generator")
912
925
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: annet
3
- Version: 0.7
3
+ Version: 0.8
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes