使用Python exec执行多类脚本时出现NameError问题求助
问题:exec执行多类Python脚本时触发NameError
错误信息
执行exec(script)时出现如下错误:
exec(script) File "<string>", line 73, in <module> File "<string>", line 79, in EthernetFrame NameError: name 'EthernetFrameHeader' is not defined
问题描述
我有一个包含大量类定义的Python脚本,需要通过exec函数执行,但运行时触发上述错误。曾怀疑exec不支持多类代码,尝试多种方法均无效,请求解决。
待执行的脚本代码
import socket import struct from enum import Enum from typing import Callable class NetworkParseError(Exception): pass class NetworkLevel(Enum): DATALINK = 'DataLink layer' NETWORK = 'Network layer' TRANSPORT = 'Transport layer' APPLICATION = 'Application layer' class NetworkProtocol: def __init__(self, raw_data: bytes): self.raw_data = raw_data self.data_length = len(self.raw_data) def get_proto_info(self) -> str: raise NetworkParseError('Override function: %s in class: %s' % (self.get_proto_info.__name__, self.__class__.__name__)) def __str__(self) -> str: return self.get_proto_info() @staticmethod def get_format_address(raw_addr: bytes, *, sep: str = '', function: Callable = str.upper) -> str: return f'{sep}'.join(map(function, raw_addr)) def get_data(self, offset: int, total_length: int = None) -> bytes: if self.data_length: return self.raw_data[offset:] if total_length is None else self.raw_data[offset:total_length] else: raise NetworkParseError('No raw data in %s object' % self.__class__.__name__) def get_json_proto_header(self): raise NotImplementedError('Override function') class EthernetFrameHeader: def __init__(self, src_mac: bytes, dst_mac: bytes, encapsulated_proto: int) -> None: self.__src_mac: bytes = src_mac self.__dst_mac: bytes = dst_mac self.__encapsulated_proto: int = encapsulated_proto @property def format_src_mac(self) -> str: return NetworkProtocol.get_format_address(self.__src_mac, sep=':', function="{:02x}".format).upper() @property def format_dst_mac(self) -> str: return NetworkProtocol.get_format_address(self.__dst_mac, sep=':', function="{:02x}".format).upper() @property def encapsulated_proto(self) -> int: return socket.ntohs(self.__encapsulated_proto) @property def source_mac(self) -> bytes: return self.__src_mac @property def destination_mac(self) -> bytes: return self.__dst_mac def __str__(self) -> str: result = f'{self.__level.value}\tEthernetProtocol:\n' result += f'Src MAC: {self.format_src_mac}\tDst MAC: {self.format_dst_mac}\t' \ f'Eth Proto: {self.encapsulated_proto}\n' return result class EthernetFrame(NetworkProtocol): def __init__(self, raw_data: bytes): NetworkProtocol.__init__(self, raw_data) self.__header = self.__parse_data() self.__offset: int = 14 def __parse_data(self) -> EthernetFrameHeader: if self.data_length: try: dst_mac, src_mac, proto = struct.unpack('!6s6sH', self.raw_data[:14]) return EthernetFrameHeader(src_mac, dst_mac, proto) except struct.error: raise NetworkParseError("Incorrect Ethernet frame format") else: raise NetworkParseError(f"No {self.__class__.__name__} header") @property def header(self) -> EthernetFrameHeader: return self.__header @property def source_mac(self) -> bytes: return self.__header.source_mac @property def destination_mac(self) -> bytes: return self.__header.destination_mac @property def encapsulated_proto(self) -> int: return self.__header.encapsulated_proto def get_encapsulated_data(self) -> bytes: return NetworkProtocol.get_data(self, self.__offset) def get_proto_info(self) -> str: return str(self.__header) def get_json_proto_header(self): pass sniffer = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(3)) raw_data = sniffer.recvfrom(65535)[0] print(EthernetFrame(raw_data))
执行方式
... exec(script_code)
其中script_code包含上述完整脚本。
解决方法
exec完全支持执行包含多个类的代码,问题根源在脚本本身的bug:
1. 修复EthernetFrameHeader的__str__方法错误
在EthernetFrameHeader的__str__方法中,你使用了未定义的self.__level属性,这会导致实例化该类时抛出AttributeError,异常中断了类的初始化流程,最终表现为EthernetFrameHeader未定义的假象。
修复方案二选一:
- 方案一:在
EthernetFrameHeader的__init__方法中添加__level属性的初始化:def __init__(self, src_mac: bytes, dst_mac: bytes, encapsulated_proto: int) -> None: self.__src_mac: bytes = src_mac self.__dst_mac: bytes = dst_mac self.__encapsulated_proto: int = encapsulated_proto self.__level = NetworkLevel.DATALINK # 添加这行 - 方案二:直接修改
__str__方法中的对应行,使用枚举常量:def __str__(self) -> str: result = f'{NetworkLevel.DATALINK.value}\tEthernetProtocol:\n' # 修改这行 result += f'Src MAC: {self.format_src_mac}\tDst MAC: {self.format_dst_mac}\t' \ f'Eth Proto: {self.encapsulated_proto}\n' return result
2. 验证脚本正确性
先在普通Python环境中直接运行脚本(不用exec),会发现同样的错误,确认问题与exec无关。修复后再用exec执行即可正常运行。
内容的提问来源于stack exchange,提问作者KeisyKeij
相关产品推荐
相关产品推荐

