From b14fc3aaf1eca3656f5368a08c7ae0af9e6f387f Mon Sep 17 00:00:00 2001 From: "Tim.Mitchell" Date: Thu, 20 Jul 2023 10:06:18 +1200 Subject: [PATCH] 97: add support for 3.12. Drop support for 3.7 --- README.rst | 2 +- pure_interface/interface.py | 37 ++++++++++--------------------------- setup.cfg | 2 +- tox.ini | 2 +- 4 files changed, 13 insertions(+), 30 deletions(-) diff --git a/README.rst b/README.rst index 5062c58..3745e23 100644 --- a/README.rst +++ b/README.rst @@ -15,7 +15,7 @@ Features * ``Interface.adapt()`` can return an implementation wrapper that provides *only* the attributes and methods defined by ``Interface``. * Warns if ``provided_by`` did a structural type check when inheritance would work. -* Supports python 3.7+ +* Supports python 3.8+ A note on the name ------------------ diff --git a/pure_interface/interface.py b/pure_interface/interface.py index 568ebfd..a2faae8 100644 --- a/pure_interface/interface.py +++ b/pure_interface/interface.py @@ -192,8 +192,10 @@ def _is_empty_function(func: Any, unwrap: bool = False) -> bool: return True if byte_code in (b'd\x01\x00S', b'd\x01S\x00') and code_obj.co_consts[1] is None: return True + if byte_code == b'y\x00' and code_obj.co_consts[0] is None: # RETURN_CONST in 3.12+ + return True # convert bytes to instructions - instructions = _get_instructions(code_obj) + instructions = list(dis.get_instructions(code_obj)) if len(instructions) < 2: return True # this never happens if instructions[0].opname == 'GEN_START': @@ -206,6 +208,11 @@ def _is_empty_function(func: Any, unwrap: bool = False) -> bool: instructions.pop(0) if instructions[0].opname == 'POP_TOP': instructions.pop(0) + # All generator functions end with these 2 opcodes in 3.12+ + if (len(instructions) > 2 and + instructions[-2].opname == 'CALL_INTRINSIC_1' and + instructions[-1].opname == 'RERAISE'): + instructions = instructions[:-2] # remove last 2 instructions if instructions[0].opname == 'RESUME': instructions.pop(0) if instructions[0].opname == 'NOP': @@ -215,6 +222,8 @@ def _is_empty_function(func: Any, unwrap: bool = False) -> bool: if not (instruction.opname == 'LOAD_CONST' and code_obj.co_consts[instruction.arg] is None): # TOS is None return False # return is not None instructions = instructions[:-2] + if instructions[-1].opname == 'RETURN_CONST' and instructions[-1].argval is None: # returns constant + instructions.pop(-1) if len(instructions) == 0: return True # look for raise NotImplementedError @@ -228,32 +237,6 @@ def _is_empty_function(func: Any, unwrap: bool = False) -> bool: return False -_Instruction = collections.namedtuple('_Instruction', ('opcode', 'opname', 'arg', 'argval')) - - -def _get_instructions(code_obj: Any) -> Union[List[_Instruction], List[dis.Instruction]]: - if hasattr(dis, 'get_instructions'): - return list(dis.get_instructions(code_obj)) - - instructions = [] - instruction = None - for byte in code_obj.co_code: - if instruction is None: - instruction = [byte] - else: - instruction.append(byte) - if instruction[0] < dis.HAVE_ARGUMENT or len(instruction) == 3: - op_code = instruction[0] - op_name = dis.opname[op_code] - if instruction[0] < dis.HAVE_ARGUMENT: - instructions.append(_Instruction(op_code, op_name, None, None)) - else: - arg = instruction[1] - instructions.append(_Instruction(op_code, op_name, arg, arg)) - instruction = None - return instructions - - def _is_descriptor(obj: Any) -> bool: # in our context we only care about __get__ return hasattr(obj, '__get__') diff --git a/setup.cfg b/setup.cfg index 9b21208..e8f9316 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,11 +15,11 @@ classifiers = Intended Audience :: Developers Topic :: Software Development :: Libraries License :: OSI Approved :: MIT License - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 license = MIT license_files = LICENSE diff --git a/tox.ini b/tox.ini index 95e9cdf..76a4459 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py37, py38, py39, py310, py311, mypy +envlist = py38, py39, py310, py311, py312, mypy [testenv] commands =