| 538 | |
| 539 | |
| 540 | class ScapyExt: |
| 541 | __slots__ = ["specs", "name", "version", "bash_completions"] |
| 542 | |
| 543 | class MODE(Enum): |
| 544 | LAYERS = "layers" |
| 545 | CONTRIB = "contrib" |
| 546 | MODULES = "modules" |
| 547 | |
| 548 | @dataclass |
| 549 | class ScapyExtSpec: |
| 550 | fullname: str |
| 551 | mode: 'ScapyExt.MODE' |
| 552 | spec: Any |
| 553 | default: bool |
| 554 | |
| 555 | def __init__(self): |
| 556 | self.specs: Dict[str, 'ScapyExt.ScapyExtSpec'] = {} |
| 557 | self.bash_completions = {} |
| 558 | |
| 559 | def config(self, name, version): |
| 560 | self.name = name |
| 561 | self.version = version |
| 562 | |
| 563 | def register(self, name, mode, path, default=None): |
| 564 | assert mode in self.MODE, "mode must be one of ScapyExt.MODE !" |
| 565 | fullname = f"scapy.{mode.value}.{name}" |
| 566 | spec = importlib.util.spec_from_file_location( |
| 567 | fullname, |
| 568 | str(path), |
| 569 | ) |
| 570 | spec = self.ScapyExtSpec( |
| 571 | fullname=fullname, |
| 572 | mode=mode, |
| 573 | spec=spec, |
| 574 | default=default or False, |
| 575 | ) |
| 576 | if default is None: |
| 577 | spec.default = bool(importlib.util.find_spec(spec.fullname)) |
| 578 | self.specs[fullname] = spec |
| 579 | |
| 580 | def register_bashcompletion(self, script: pathlib.Path): |
| 581 | self.bash_completions[script.name] = script |
| 582 | |
| 583 | def __repr__(self): |
| 584 | return "<ScapyExt %s %s (%s specs)>" % ( |
| 585 | self.name, |
| 586 | self.version, |
| 587 | len(self.specs), |
| 588 | ) |
| 589 | |
| 590 | |
| 591 | class ExtsManager(importlib.abc.MetaPathFinder): |