Skip to content

Commit

Permalink
Merge branch 'main' into pythongh-105623
Browse files Browse the repository at this point in the history
  • Loading branch information
zhatt authored Jun 17, 2023
2 parents d3a1875 + 14d0126 commit b1d5ba1
Show file tree
Hide file tree
Showing 20 changed files with 247 additions and 154 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ jobs:
path: config.cache
key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }}
- name: Install Homebrew dependencies
run: brew install pkg-config openssl@1.1 xz gdbm tcl-tk
run: brew install pkg-config openssl@3.0 xz gdbm tcl-tk
- name: Configure CPython
run: |
GDBM_CFLAGS="-I$(brew --prefix gdbm)/include" \
Expand All @@ -241,7 +241,7 @@ jobs:
--config-cache \
--with-pydebug \
--prefix=/opt/python-dev \
--with-openssl="$(brew --prefix openssl@1.1)"
--with-openssl="$(brew --prefix openssl@3.0)"
- name: Build CPython
run: make -j4
- name: Display build info
Expand Down
35 changes: 26 additions & 9 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,12 @@ operation is being performed, so the intermediate analysis object isn't useful:
.. versionchanged:: 3.8
Added *jump* parameter.

.. versionchanged:: 3.13
If ``oparg`` is omitted (or ``None``), the stack effect is now returned
for ``oparg=0``. Previously this was an error for opcodes that use their
arg. It is also no longer an error to pass an integer ``oparg`` when
the ``opcode`` does not use it; the ``oparg`` in this case is ignored.


.. _bytecodes:

Expand Down Expand Up @@ -1421,23 +1427,34 @@ iterations of the loop.
.. versionadded:: 3.11


.. opcode:: MAKE_FUNCTION (flags)
.. opcode:: MAKE_FUNCTION

Pushes a new function object on the stack built from the code object at ``STACK[1]``.

.. versionchanged:: 3.10
Flag value ``0x04`` is a tuple of strings instead of dictionary

.. versionchanged:: 3.11
Qualified name at ``STACK[-1]`` was removed.

Pushes a new function object on the stack. From bottom to top, the consumed
stack must consist of values if the argument carries a specified flag value
.. versionchanged:: 3.13
Extra function attributes on the stack, signaled by oparg flags, were
removed. They now use :opcode:`SET_FUNCTION_ATTRIBUTE`.


.. opcode:: SET_FUNCTION_ATTRIBUTE (flag)

Sets an attribute on a function object. Expects the function at ``STACK[-1]``
and the attribute value to set at ``STACK[-2]``; consumes both and leaves the
function at ``STACK[-1]``. The flag determines which attribute to set:

* ``0x01`` a tuple of default values for positional-only and
positional-or-keyword parameters in positional order
* ``0x02`` a dictionary of keyword-only parameters' default values
* ``0x04`` a tuple of strings containing parameters' annotations
* ``0x08`` a tuple containing cells for free variables, making a closure
* the code associated with the function (at ``STACK[-1]``)

.. versionchanged:: 3.10
Flag value ``0x04`` is a tuple of strings instead of dictionary

.. versionchanged:: 3.11
Qualified name at ``STACK[-1]`` was removed.
.. versionadded:: 3.13


.. opcode:: BUILD_SLICE (argc)
Expand Down
13 changes: 0 additions & 13 deletions Include/internal/pycore_opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 1 addition & 25 deletions Include/internal/pycore_opcode_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ extern "C" {

#define IS_WITHIN_OPCODE_RANGE(opcode) \
(((opcode) >= 0 && (opcode) <= MAX_REAL_OPCODE) || \
IS_PSEUDO_OPCODE(opcode))

#define IS_JUMP_OPCODE(opcode) \
is_bit_set_in_table(_PyOpcode_Jump, opcode)
IS_PSEUDO_INSTR(opcode))

#define IS_BLOCK_PUSH_OPCODE(opcode) \
((opcode) == SETUP_FINALLY || \
Expand Down Expand Up @@ -55,27 +52,6 @@ extern "C" {
(opcode) == RAISE_VARARGS || \
(opcode) == RERAISE)

#define LOG_BITS_PER_INT 5
#define MASK_LOW_LOG_BITS 31

static inline int
is_bit_set_in_table(const uint32_t *table, int bitindex) {
/* Is the relevant bit set in the relevant word? */
/* 512 bits fit into 9 32-bits words.
* Word is indexed by (bitindex>>ln(size of int in bits)).
* Bit within word is the low bits of bitindex.
*/
if (bitindex >= 0 && bitindex < 512) {
uint32_t word = table[bitindex >> LOG_BITS_PER_INT];
return (word >> (bitindex & MASK_LOW_LOG_BITS)) & 1;
}
else {
return 0;
}
}

#undef LOG_BITS_PER_INT
#undef MASK_LOW_LOG_BITS

/* Flags used in the oparg for MAKE_FUNCTION */
#define MAKE_FUNCTION_DEFAULTS 0x01
Expand Down
19 changes: 0 additions & 19 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Lib/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -1051,7 +1051,8 @@ def visit_ClassDef(self, node):
self.fill("@")
self.traverse(deco)
self.fill("class " + node.name)
self._type_params_helper(node.type_params)
if hasattr(node, "type_params"):
self._type_params_helper(node.type_params)
with self.delimit_if("(", ")", condition = node.bases or node.keywords):
comma = False
for e in node.bases:
Expand Down Expand Up @@ -1083,7 +1084,8 @@ def _function_helper(self, node, fill_suffix):
self.traverse(deco)
def_str = fill_suffix + " " + node.name
self.fill(def_str)
self._type_params_helper(node.type_params)
if hasattr(node, "type_params"):
self._type_params_helper(node.type_params)
with self.delimit("(", ")"):
self.traverse(node.args)
if node.returns:
Expand Down
10 changes: 2 additions & 8 deletions Lib/test/test__opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,14 @@ def test_stack_effect(self):
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 1), -1)
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 3), -2)
self.assertRaises(ValueError, stack_effect, 30000)
self.assertRaises(ValueError, stack_effect, dis.opmap['BUILD_SLICE'])
self.assertRaises(ValueError, stack_effect, dis.opmap['POP_TOP'], 0)
# All defined opcodes
has_arg = dis.hasarg
for name, code in filter(lambda item: item[0] not in dis.deoptmap, dis.opmap.items()):
if code >= opcode.MIN_INSTRUMENTED_OPCODE:
continue
with self.subTest(opname=name):
if code not in has_arg:
stack_effect(code)
self.assertRaises(ValueError, stack_effect, code, 0)
else:
stack_effect(code, 0)
self.assertRaises(ValueError, stack_effect, code)
stack_effect(code)
stack_effect(code, 0)
# All not defined opcodes
for code in set(range(256)) - set(dis.opmap.values()):
with self.subTest(opcode=code):
Expand Down
29 changes: 29 additions & 0 deletions Lib/test/test_opcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,35 @@ def f():
self.assertFalse(f())


class TestCallCache(unittest.TestCase):
def test_too_many_defaults_0(self):
def f():
pass

f.__defaults__ = (None,)
for _ in range(1025):
f()

def test_too_many_defaults_1(self):
def f(x):
pass

f.__defaults__ = (None, None)
for _ in range(1025):
f(None)
f()

def test_too_many_defaults_2(self):
def f(x, y):
pass

f.__defaults__ = (None, None, None)
for _ in range(1025):
f(None, None)
f(None)
f()


if __name__ == "__main__":
import unittest
unittest.main()
74 changes: 74 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2759,6 +2759,80 @@ def x(self): ...
with self.assertRaisesRegex(TypeError, only_classes_allowed):
issubclass(1, BadPG)

def test_implicit_issubclass_between_two_protocols(self):
@runtime_checkable
class CallableMembersProto(Protocol):
def meth(self): ...

# All the below protocols should be considered "subclasses"
# of CallableMembersProto at runtime,
# even though none of them explicitly subclass CallableMembersProto

class IdenticalProto(Protocol):
def meth(self): ...

class SupersetProto(Protocol):
def meth(self): ...
def meth2(self): ...

class NonCallableMembersProto(Protocol):
meth: Callable[[], None]

class NonCallableMembersSupersetProto(Protocol):
meth: Callable[[], None]
meth2: Callable[[str, int], bool]

class MixedMembersProto1(Protocol):
meth: Callable[[], None]
def meth2(self): ...

class MixedMembersProto2(Protocol):
def meth(self): ...
meth2: Callable[[str, int], bool]

for proto in (
IdenticalProto, SupersetProto, NonCallableMembersProto,
NonCallableMembersSupersetProto, MixedMembersProto1, MixedMembersProto2
):
with self.subTest(proto=proto.__name__):
self.assertIsSubclass(proto, CallableMembersProto)

# These two shouldn't be considered subclasses of CallableMembersProto, however,
# since they don't have the `meth` protocol member

class EmptyProtocol(Protocol): ...
class UnrelatedProtocol(Protocol):
def wut(self): ...

self.assertNotIsSubclass(EmptyProtocol, CallableMembersProto)
self.assertNotIsSubclass(UnrelatedProtocol, CallableMembersProto)

# These aren't protocols at all (despite having annotations),
# so they should only be considered subclasses of CallableMembersProto
# if they *actually have an attribute* matching the `meth` member
# (just having an annotation is insufficient)

class AnnotatedButNotAProtocol:
meth: Callable[[], None]

class NotAProtocolButAnImplicitSubclass:
def meth(self): pass

class NotAProtocolButAnImplicitSubclass2:
meth: Callable[[], None]
def meth(self): pass

class NotAProtocolButAnImplicitSubclass3:
meth: Callable[[], None]
meth2: Callable[[int, str], bool]
def meth(self): pass
def meth(self, x, y): return True

self.assertNotIsSubclass(AnnotatedButNotAProtocol, CallableMembersProto)
self.assertIsSubclass(NotAProtocolButAnImplicitSubclass, CallableMembersProto)
self.assertIsSubclass(NotAProtocolButAnImplicitSubclass2, CallableMembersProto)
self.assertIsSubclass(NotAProtocolButAnImplicitSubclass3, CallableMembersProto)

def test_isinstance_checks_not_at_whim_of_gc(self):
self.addCleanup(gc.enable)
gc.disable()
Expand Down
Loading

0 comments on commit b1d5ba1

Please sign in to comment.