Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/compat.py: 61%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
5"""
6Compatibility module to various older versions of Python
7"""
9import base64
10import binascii
11import enum
12import struct
13import sys
15from typing import (
16 Any,
17 AnyStr,
18 Callable,
19 Optional,
20 TypeVar,
21 TYPE_CHECKING,
22 Union,
23)
25# Very important: will issue typing errors otherwise
26__all__ = [
27 # typing
28 'DecoratorCallable',
29 'Literal',
30 'Protocol',
31 'Self',
32 'UserDict',
33 # compat
34 'base64_bytes',
35 'bytes_base64',
36 'bytes_encode',
37 'bytes_hex',
38 'chb',
39 'hex_bytes',
40 'orb',
41 'plain_str',
42 'raw',
43 'StrEnum',
44]
46# Typing compatibility
48# Note:
49# supporting typing on multiple python versions is a nightmare.
50# we provide a FakeType class to be able to use types added on
51# later Python versions (since we run mypy on 3.14), on older
52# ones.
55# Import or create fake types
57def _FakeType(name, cls=object):
58 # type: (str, Optional[type]) -> Any
59 class _FT(object):
60 def __init__(self, name):
61 # type: (str) -> None
62 self.name = name
64 # make the objects subscriptable indefinitely
65 def __getitem__(self, item): # type: ignore
66 return cls
68 def __call__(self, *args, **kargs):
69 # type: (*Any, **Any) -> Any
70 if isinstance(args[0], str):
71 self.name = args[0]
72 return self
74 def __repr__(self):
75 # type: () -> str
76 return "<Fake typing.%s>" % self.name
77 return _FT(name)
80# Python 3.8 Only
81if sys.version_info >= (3, 8):
82 from typing import Literal
83 from typing import Protocol
84else:
85 Literal = _FakeType("Literal")
87 class Protocol:
88 pass
91# Python 3.9 Only
92if sys.version_info >= (3, 9):
93 from collections import UserDict
94else:
95 from collections import UserDict as _UserDict
96 UserDict = _FakeType("_UserDict", _UserDict)
99# Python 3.11 Only
100if sys.version_info >= (3, 11):
101 from typing import Self
102else:
103 Self = _FakeType("Self")
106# Python 3.11 Only
107if sys.version_info >= (3, 11):
108 from enum import StrEnum
109else:
110 class StrEnum(str, enum.Enum):
111 pass
114###########
115# Python3 #
116###########
118# https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
119DecoratorCallable = TypeVar("DecoratorCallable", bound=Callable[..., Any])
122# This is ugly, but we don't want to move raw() out of compat.py
123# and it makes it much clearer
124if TYPE_CHECKING:
125 from scapy.packet import Packet
128def raw(x):
129 # type: (Packet) -> bytes
130 """
131 Builds a packet and returns its bytes representation.
132 This function is and will always be cross-version compatible
133 """
134 return bytes(x)
137def bytes_encode(x):
138 # type: (Any) -> bytes
139 """Ensure that the given object is bytes. If the parameter is a
140 packet, raw() should be preferred.
142 """
143 if isinstance(x, str):
144 return x.encode()
145 return bytes(x)
148def plain_str(x):
149 # type: (Any) -> str
150 """Convert basic byte objects to str"""
151 if isinstance(x, bytes):
152 return x.decode(errors="backslashreplace")
153 return str(x)
156def chb(x):
157 # type: (int) -> bytes
158 """Same than chr() but encode as bytes."""
159 return struct.pack("!B", x)
162def orb(x):
163 # type: (Union[int, str, bytes]) -> int
164 """Return ord(x) when not already an int."""
165 if isinstance(x, int):
166 return x
167 return ord(x)
170def bytes_hex(x):
171 # type: (AnyStr) -> bytes
172 """Hexify a str or a bytes object"""
173 return binascii.b2a_hex(bytes_encode(x))
176def hex_bytes(x):
177 # type: (AnyStr) -> bytes
178 """De-hexify a str or a byte object"""
179 return binascii.a2b_hex(bytes_encode(x))
182def int_bytes(x, size):
183 # type: (int, int) -> bytes
184 """Convert an int to an arbitrary sized bytes string"""
185 return x.to_bytes(size, byteorder='big')
188def bytes_int(x):
189 # type: (bytes) -> int
190 """Convert an arbitrary sized bytes string to an int"""
191 return int.from_bytes(x, "big")
194def base64_bytes(x):
195 # type: (AnyStr) -> bytes
196 """Turn base64 into bytes"""
197 return base64.decodebytes(bytes_encode(x))
200def bytes_base64(x):
201 # type: (AnyStr) -> bytes
202 """Turn bytes into base64"""
203 return base64.encodebytes(bytes_encode(x)).replace(b'\n', b'')