Skip to content

Commit

Permalink
Merge branch 'master' of github.com:ronaldoussoren/pyobjc
Browse files Browse the repository at this point in the history
  • Loading branch information
ronaldoussoren committed Nov 16, 2022
2 parents cf74978 + 6175909 commit cf9cc1b
Show file tree
Hide file tree
Showing 187 changed files with 845 additions and 285 deletions.
4 changes: 2 additions & 2 deletions docs/api/module-objc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ Types
The *cobject* and *c_void_p* arguments should always be passed as keyword arguments,
and at most one of them should be provided. This will construct a proxy object of the
right subclass of :class:`objc_object` for the Cocoa object that the passed in value
refers to. *Cobject* should be a Pytho capsule created using the :meth:`__cobject__`
refers to. *Cobject* should be a Python capsule created using the :meth:`__cobject__`
method, *c_void_p* should be a :class:`ctypes.c_void_p`.

.. note::
Expand All @@ -631,7 +631,7 @@ Types
Read-only property that provides explicit access to just the instance methods
of an object.

.. data:: \__block_signature__
.. data:: __block_signature__

Property with the type signature for calling a block, or :data:`None`.

Expand Down
12 changes: 6 additions & 6 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ What's new in PyObjC

An overview of the relevant changes in new, and older, releases.

Version 9.0a1
-------------
Version 9.0
-----------

* Support for macOS 13 (Xcode 14 beta 4)

Expand Down Expand Up @@ -142,17 +142,14 @@ Version 9.0a1
* #502: Fix incompatibility with Nuitka.

Earlier version of PyObjC failed when compiled using Nuitka, this
version does work.
version does work when using Nuitka 1.1.6 or later.

Limitations:
- The automatic calculation of the method signature in ``selector()``
assumes that methods return ``id`` for Nuitka compiled code.

That should not be a problem in practice.

- Invocations of 0-argument super will result in errors. That's
due to a limitation in Nuitka that the developer is lookin into.

As a side effect of this builtin functions are accepted as
the callable for a selector, even when not specifying a
signature (e.g. ``objc.selector(dir)`` now works).
Expand All @@ -167,6 +164,9 @@ Version 9.0a1
This can avoid an ``objc.error`` exception when introspecting existing
Cocoa classes.

* #479: Revert change that made it impossible to replace a method
with a property.

Version 8.6
-----------

Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
# built documents.
#
# The short X.Y version.
version = "9.0a1"
version = "9.0"
# The full version, including alpha/beta/rc tags.
release = version

Expand Down
2 changes: 1 addition & 1 deletion docs/core/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Underscores, and lots of them

Objective-C objects communicate with each other by sending messages.
The syntax for messages is somewhere in-between Python's positional and
keyword arguments. Specificlaly, Objective-C message dispatch uses
keyword arguments. Specifically, Objective-C message dispatch uses
positional arguments, but parts of the message name (called "selector"
in Objective-C terminology) are interleaved with the arguments.

Expand Down
7 changes: 3 additions & 4 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ in pure Python. See our tutorial for an example of this.
Release information
-------------------

PyObjC 9 will support Python 3.7 and later.
PyObjC 9.0 was released on 2022-11-05. See the :doc:`changelog <changelog>` for more information. PyObjC 9 supports Python 3.7 and later.

PyObjC 8.5 was released on 2022-04-19. See the :doc:`changelog <changelog>` for more information. PyObjC 8 supports Python 3.6 and later.

PyObjC 5.3 is the last version supporting Python 2 and was released on 2019-10-16.
PyObjC 8.5 is the last version supporting Python 3.6. PyObjC 5.3 is the last version supporting Python 2. These versions are
no longer supported.

Supported platforms
-------------------
Expand Down
101 changes: 51 additions & 50 deletions pyobjc-core/Lib/PyObjCTools/TestSupport.py
Original file line number Diff line number Diff line change
Expand Up @@ -1087,60 +1087,59 @@ def assertPickleRoundTrips(self, value):
def _validateCallableMetadata(
self, value, class_name=None, skip_simple_charptr_check=False
):
with self.subTest(repr(value)):
callable_meta = value.__metadata__()
argcount = len(callable_meta["arguments"])
# print(value)
callable_meta = value.__metadata__()
argcount = len(callable_meta["arguments"])

for idx, meta in [("retval", callable_meta["retval"])] + list(
enumerate(callable_meta["arguments"])
for idx, meta in [("retval", callable_meta["retval"])] + list(
enumerate(callable_meta["arguments"])
):
if meta["type"].endswith(objc._C_PTR + objc._C_CHR):
if meta.get("c_array_delimited_by_null", False):
self.fail(
f"{value}: {idx}: null-delimited 'char*', use _C_CHAR_AS_TEXT instead {class_name or ''}"
)
if not skip_simple_charptr_check:
self.fail(f"{value}: {idx}: 'char*' {class_name or ''}")

v = meta.get("c_array_size_in_arg", None)
if isinstance(v, int):
if not (0 <= v < argcount):
self.fail(
f"{value}: {idx}: c_array_size_in_arg out of range {v} {class_name or ''}"
)
elif isinstance(v, tuple):
b, e = v
if not (0 <= b < argcount):
self.fail(
f"{value}: {idx}: c_array_size_in_arg out of range {b} {class_name or ''}"
)
if not (0 <= e < argcount):
self.fail(
f"{value}: {idx}: c_array_size_in_arg out of range {e} {class_name or ''}"
)

tp = meta["type"]
if any(
tp.startswith(pfx) for pfx in (objc._C_IN, objc._C_OUT, objc._C_INOUT)
):
if meta["type"].endswith(objc._C_PTR + objc._C_CHR):
if meta.get("c_array_delimited_by_null", False):
self.fail(
f"{value}: {idx}: null-delimited 'char*', use _C_CHAR_AS_TEXT instead {class_name or ''}"
)
if not skip_simple_charptr_check:
self.fail(f"{value}: {idx}: 'char*' {class_name or ''}")
rest = tp[1:]
if not rest.startswith(objc._C_PTR) and not rest.startswith(
objc._C_CHARPTR
):
self.fail(
f"{value}: {idx}: byref specifier on non-pointer: {tp} {class_name or ''}"
)

v = meta.get("c_array_size_in_arg", None)
if isinstance(v, int):
if not (0 <= v < argcount):
self.fail(
f"{value}: {idx}: c_array_size_in_arg out of range {v} {class_name or ''}"
)
elif isinstance(v, tuple):
b, e = v
if not (0 <= b < argcount):
self.fail(
f"{value}: {idx}: c_array_size_in_arg out of range {b} {class_name or ''}"
)
if not (0 <= e < argcount):
self.fail(
f"{value}: {idx}: c_array_size_in_arg out of range {e} {class_name or ''}"
)
rest = rest[1:]

tp = meta["type"]
if any(
tp.startswith(pfx)
for pfx in (objc._C_IN, objc._C_OUT, objc._C_INOUT)
):
rest = tp[1:]
if not rest.startswith(objc._C_PTR) and not rest.startswith(
objc._C_CHARPTR
):
if rest.startswith(objc._C_STRUCT_B):
name, fields = objc.splitStructSignature(rest)
if not fields:
self.fail(
f"{value}: {idx}: byref specifier on non-pointer: {tp} {class_name or ''}"
f"{value}: {idx}: byref to empty struct (handle/CFType?): {tp} {class_name or ''}"
)

rest = rest[1:]

if rest.startswith(objc._C_STRUCT_B):
name, fields = objc.splitStructSignature(rest)
if not fields:
self.fail(
f"{value}: {idx}: byref to empty struct (handle/CFType?): {tp} {class_name or ''}"
)

def assertCallableMetadataIsSane(
self, module, *, exclude_cocoa=True, exclude_attrs=()
):
Expand Down Expand Up @@ -1179,7 +1178,9 @@ def assertCallableMetadataIsSane(
)
)

for nm in dir(module):
module_names = sorted(dir(module))
for _idx, nm in enumerate(module_names):
# print(f"{_idx}/{len(module_names)} {nm}")
if nm in exclude_names:
continue
if nm in exclude_attrs:
Expand All @@ -1191,7 +1192,7 @@ def assertCallableMetadataIsSane(
# Root class, does not conform to the NSObject
# protocol and useless to test.
continue
for attr_name in dir(value.pyobjc_instanceMethods):
for attr_name in sorted(dir(value.pyobjc_instanceMethods)):
if (nm, attr_name) in exclude_attrs:
continue
if attr_name.startswith("_"):
Expand All @@ -1205,7 +1206,7 @@ def assertCallableMetadataIsSane(
attr, nm, skip_simple_charptr_check=not exclude_cocoa
)

for attr_name in dir(value.pyobjc_classMethods):
for attr_name in sorted(dir(value.pyobjc_classMethods)):
if (nm, attr_name) in exclude_attrs:
continue
if attr_name.startswith("_"):
Expand Down
2 changes: 2 additions & 0 deletions pyobjc-core/Modules/objc/objc-class.m
Original file line number Diff line number Diff line change
Expand Up @@ -2036,6 +2036,7 @@ static Class _Nullable objc_metaclass_locate(PyObject* meta_class)
return 0;
}

#if 0 /* XXX: Disabled check due to #479 */
/* Check if there is a current attribute with the same name that
* is an unbound selector.
*/
Expand All @@ -2052,6 +2053,7 @@ static Class _Nullable objc_metaclass_locate(PyObject* meta_class)

return -1;
}
#endif

res = PyType_Type.tp_setattro(self, name, value);
return res;
Expand Down
2 changes: 1 addition & 1 deletion pyobjc-core/Modules/objc/pyobjc.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*
*/

#define OBJC_VERSION "9.0a1"
#define OBJC_VERSION "9.0"

#define PY_SSIZE_T_CLEAN
#include <Python.h>
Expand Down
6 changes: 4 additions & 2 deletions pyobjc-core/PyObjCTest/test_bundleFunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ def test_parsing_arguments(self):
objc.loadBundleFunctions(self.bundle, {}, 42)

with self.assertRaisesRegex(
TypeError, "'str' object cannot be interpreted as an integer"
TypeError,
r"('str' object cannot be interpreted as an integer)|(an integer is required \(got type str\))",
):
objc.loadBundleFunctions(self.bundle, {}, [], "hello")

Expand Down Expand Up @@ -143,7 +144,8 @@ def test_parsing_arguments(self):
objc.loadFunctionList(function_list, {}, 42)

with self.assertRaisesRegex(
TypeError, "'str' object cannot be interpreted as an integer"
TypeError,
r"('str' object cannot be interpreted as an integer)|(an integer is required \(got type str\))",
):
objc.loadFunctionList(function_list, {}, [], "hello")

Expand Down
9 changes: 6 additions & 3 deletions pyobjc-core/PyObjCTest/test_bundleVariables.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ def test_parsing_arguments(self):
objc.loadBundleVariables(self.bundle, {}, 42)

with self.assertRaisesRegex(
TypeError, "'str' object cannot be interpreted as an integer"
TypeError,
r"('str' object cannot be interpreted as an integer)|(an integer is required \(got type str\))",
):
objc.loadBundleVariables(self.bundle, {}, [], "hello")

Expand Down Expand Up @@ -141,15 +142,17 @@ def test_parsing_arguments(self):
objc.loadSpecialVar(self.bundle, 42, 42, "hello")

with self.assertRaisesRegex(
TypeError, "'str' object cannot be interpreted as an integer"
TypeError,
r"('str' object cannot be interpreted as an integer)|(an integer is required \(got type str\))",
):
objc.loadSpecialVar(self.bundle, {}, "42", "hello")

with self.assertRaisesRegex(TypeError, "variable name not a string"):
objc.loadSpecialVar(self.bundle, {}, 42, 0)

with self.assertRaisesRegex(
TypeError, "'str' object cannot be interpreted as an integer"
TypeError,
r"('str' object cannot be interpreted as an integer)|(an integer is required \(got type str\))",
):
objc.loadSpecialVar(self.bundle, {}, 42, "hello", "world")

Expand Down
3 changes: 2 additions & 1 deletion pyobjc-core/PyObjCTest/test_methodaccess.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ def test_replace_through_instane(self):
):
o.description = 42

def test_replace_through_class(self):
def no_test_replace_through_class(self):
# XXX: #479
with self.assertRaisesRegex(
AttributeError,
"Cannot replace selector 'alloc' in 'NSObject' by non-selector",
Expand Down
6 changes: 4 additions & 2 deletions pyobjc-core/PyObjCTest/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ def test_nscodiing_version(self):
self.assertEqual(objc.options._nscoding_version, 2)

with self.assertRaisesRegex(
TypeError, "'str' object cannot be interpreted as an integer"
TypeError,
r"('str' object cannot be interpreted as an integer)|(an integer is required \(got type str\))",
):
objc.options._nscoding_version = ""

Expand Down Expand Up @@ -139,7 +140,8 @@ def test_deprecation_warnings(self):
self.assertEqual(objc.options.deprecation_warnings, 2)

with self.assertRaisesRegex(
TypeError, "'str' object cannot be interpreted as an integer"
TypeError,
r"('str' object cannot be interpreted as an integer)|(an integer is required \(got type str\))",
):
objc.options.deprecation_warnings = ""

Expand Down
22 changes: 22 additions & 0 deletions pyobjc-core/PyObjCTest/test_regr.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,28 @@ class Foo:
f.alloc().init()


class TestReplaceMethod(TestCase):
def test_replace_method(self):
# GH #479
NSAppleScript = objc.lookUpClass("NSAppleScript")
o = NSAppleScript.alloc().initWithSource_("tell application Safari to quit")
self.assertIsNot(o, None)

v = o.source()
self.assertIsInstance(v, str)

# In some applications it is useful to replace an Objective-C method
# by a property definition, like so:
NSAppleScript.source = property(
lambda self: self.pyobjc_instanceMethods.source()
)

v2 = o.source
self.assertIsInstance(v, str)

self.assertEqual(v, v2)


# This probably needs to be a seperate file:


Expand Down
15 changes: 12 additions & 3 deletions pyobjc-core/PyObjCTest/test_subclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -908,13 +908,22 @@ class TestSelectorEdgeCases(TestCase):
# and one for the regular caller.

def test_no_keywords(self):
with self.assertRaisesRegex(TypeError, "does not accept keyword arguments"):
with self.assertRaisesRegex(
TypeError,
"(does not accept keyword arguments)|(keyword arguments not supported)",
):
NSArray.alloc().init().copyWithZone_(zone=None)

with self.assertRaisesRegex(TypeError, "does not accept keyword arguments"):
with self.assertRaisesRegex(
TypeError,
"(does not accept keyword arguments)|(keyword arguments not supported)",
):
NSData.dataWithBytes_length_(data=b"hello", length=3)

with self.assertRaisesRegex(TypeError, "does not accept keyword arguments"):
with self.assertRaisesRegex(
TypeError,
"(does not accept keyword arguments)|(keyword arguments not supported)",
):
NSArray.alloc(cls=NSArray)

def test_call_on_wrong_self(self):
Expand Down
172 changes: 166 additions & 6 deletions pyobjc-framework-AVFoundation/Lib/AVFoundation/_metadata.py

Large diffs are not rendered by default.

0 comments on commit cf9cc1b

Please sign in to comment.