Skip to content

Commit 7852c9b

Browse files
committed
Fix CLIUtil overlap
1 parent cbb09c4 commit 7852c9b

2 files changed

Lines changed: 77 additions & 4 deletions

File tree

scapy/utils.py

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import collections
2020
import decimal
2121
import difflib
22+
import enum
2223
import gzip
2324
import inspect
2425
import locale
@@ -3566,7 +3567,38 @@ def whois(ip_address):
35663567
####################
35673568

35683569

3569-
class CLIUtil:
3570+
class _CLIUtilMetaclass(type):
3571+
class TYPE(enum.Enum):
3572+
COMMAND = 0
3573+
OUTPUT = 1
3574+
COMPLETE = 2
3575+
3576+
def __new__(cls, # type: Type[_CLIUtilMetaclass]
3577+
name, # type: str
3578+
bases, # type: Tuple[type, ...]
3579+
dct # type: Dict[str, Any]
3580+
):
3581+
# type: (...) -> Type[CLIUtil]
3582+
dct["commands"] = {
3583+
x.__name__: x
3584+
for x in dct.values()
3585+
if getattr(x, "cliutil_type", None) == _CLIUtilMetaclass.TYPE.COMMAND
3586+
}
3587+
dct["commands_output"] = {
3588+
x.cliutil_ref.__name__: x
3589+
for x in dct.values()
3590+
if getattr(x, "cliutil_type", None) == _CLIUtilMetaclass.TYPE.OUTPUT
3591+
}
3592+
dct["commands_complete"] = {
3593+
x.cliutil_ref.__name__: x
3594+
for x in dct.values()
3595+
if getattr(x, "cliutil_type", None) == _CLIUtilMetaclass.TYPE.COMPLETE
3596+
}
3597+
newcls = cast(Type['CLIUtil'], type.__new__(cls, name, bases, dct))
3598+
return newcls
3599+
3600+
3601+
class CLIUtil(metaclass=_CLIUtilMetaclass):
35703602
"""
35713603
Provides a Util class to easily create simple CLI tools in Scapy,
35723604
that can still be used as an API.
@@ -3594,6 +3626,14 @@ def _depcheck(self) -> None:
35943626
# provides completion to command
35953627
commands_complete: Dict[str, Callable[..., List[str]]] = {}
35963628

3629+
def __init__(self, cli: bool = True, debug: bool = False) -> None:
3630+
"""
3631+
DEV: overwrite
3632+
"""
3633+
if cli:
3634+
self._depcheck()
3635+
self.loop(debug=debug)
3636+
35973637
@staticmethod
35983638
def _inspectkwargs(func: DecoratorCallable) -> None:
35993639
"""
@@ -3655,7 +3695,7 @@ def addcommand(
36553695
Decorator to register a command
36563696
"""
36573697
def func(cmd: DecoratorCallable) -> DecoratorCallable:
3658-
cls.commands[cmd.__name__] = cmd
3698+
cmd.cliutil_type = _CLIUtilMetaclass.TYPE.COMMAND # type: ignore
36593699
cmd._spaces = spaces # type: ignore
36603700
cmd._globsupport = globsupport # type: ignore
36613701
cls._inspectkwargs(cmd)
@@ -3670,7 +3710,8 @@ def addoutput(cls, cmd: DecoratorCallable) -> Callable[[DecoratorCallable], Deco
36703710
Decorator to register a command output processor
36713711
"""
36723712
def func(processor: DecoratorCallable) -> DecoratorCallable:
3673-
cls.commands_output[cmd.__name__] = processor
3713+
processor.cliutil_type = _CLIUtilMetaclass.TYPE.OUTPUT # type: ignore
3714+
processor.cliutil_ref = cmd # type: ignore
36743715
cls._inspectkwargs(processor)
36753716
return processor
36763717
return func
@@ -3681,7 +3722,8 @@ def addcomplete(cls, cmd: DecoratorCallable) -> Callable[[DecoratorCallable], De
36813722
Decorator to register a command completor
36823723
"""
36833724
def func(processor: DecoratorCallable) -> DecoratorCallable:
3684-
cls.commands_complete[cmd.__name__] = processor
3725+
processor.cliutil_type = _CLIUtilMetaclass.TYPE.COMPLETE # type: ignore
3726+
processor.cliutil_ref = cmd # type: ignore
36853727
return processor
36863728
return func
36873729

test/regression.uts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4937,3 +4937,34 @@ def _test_fragleak(func, sr1, select, L3socket):
49374937

49384938
assert _test_fragleak(fragleak)
49394939
assert _test_fragleak(fragleak2)
4940+
4941+
+ CLIUtil
4942+
~ cliutil
4943+
4944+
= CLIUtil: define and check overlap
4945+
4946+
from scapy.layers.smbclient import smbclient
4947+
4948+
class CLI1(CLIUtil):
4949+
@CLIUtil.addcommand()
4950+
def shares(self):
4951+
return 1
4952+
@CLIUtil.addoutput(shares)
4953+
def shares_output(self, results):
4954+
print(results)
4955+
4956+
4957+
class CLI2(CLIUtil):
4958+
@CLIUtil.addcommand()
4959+
def shares(self):
4960+
return 2
4961+
@CLIUtil.addoutput(shares)
4962+
def shares_output(self, results):
4963+
print(results)
4964+
4965+
4966+
c1 = CLI1(cli=False)
4967+
c2 = CLI2(cli=False)
4968+
4969+
assert c1.shares() == 1
4970+
assert c2.shares() == 2

0 commit comments

Comments
 (0)