Skip to content

Commit

Permalink
Drop MAC_OS_X_VERSION* values
Browse files Browse the repository at this point in the history
This also changes the API for ``objc.options.deprecation_warnings``
to use string values instead of integers.

Closes #547
  • Loading branch information
ronaldoussoren committed Jun 7, 2023
1 parent fb806d0 commit 9a6b781
Show file tree
Hide file tree
Showing 105 changed files with 174 additions and 23,276 deletions.
51 changes: 12 additions & 39 deletions docs/api/module-objc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,23 @@ Bridge options

.. data:: objc.options.deprecation_warnings

When 0 (the default) the bridge will not emit deprecation warnings,
otherwise the value should be one of the :data:`MAC_OS_X_VERSION_10_N`
constants and the bridge will emit a deprecation warning for APIs
When ``"0.0"`` (the default) the bridge will not emit deprecation warnings,
otherwise the value should be a platform version in the form
of a string (e.g. ``"10.15"``)
and the bridge will emit a deprecation warning for APIs
that were deprecated in the SDK (or earlier).

Set to :data:`None` or ``"0.0"`` to disable warnings.

Deprecation warnings are emitted using the :mod:`warnings` module,
using the warning :class:`objc.ApiDeprecationWarning`.

.. versionadded:: 3.3

.. versionchanged: 10.0
The value for this option is now a string instead of an integer.
.. data:: objc.options.structs_indexable

When True (the default) PyObjC's wrappers for C structs can be indexed
Expand Down Expand Up @@ -1222,44 +1229,10 @@ Constants
parameters and pass a pointer to that buffer, with :data:`NULL` the
bridge will always pass a :c:data:`NULL` pointer.

.. data:: MAC_OS_X_VERSION_MAX_ALLOWED

The value of :c:data:`MAC_OS_X_VERSION_MAX_ALLOWED` when PyObjC was
compiled.

.. data:: MAC_OS_X_VERSION_MIN_REQUIRED

The value of :c:data:`MAC_OS_X_VERSION_MIN_REQUIRED` when PyObjC was
compiled.

.. data:: MAC_OS_X_VERSION_CURRENT

The currently running macOS version in the same format as
the various ``MAC_OS_X_VERSION_10_N`` constants.

The intended use is with API availability checks, more or less like
the ``@available`` construct in Objective-C, that is:

.. sourcecode:: python

if objc.MAC_OS_X_VERSION_CURRENT >= objc.MAC_OS_X_VERSION_10_14:
# Use API introduced in macOS 10.14
...

else:
# Use fallback implementation
...

.. data:: PyObjC_BUILD_RELEASE

The version number of the SDK used to build PyObjC, in the same format
as :data:`MAC_OS_X_VERSION_10_N`

.. data:: MAC_OS_X_VERSION_10_N

There are currently 6 constants of this form, for ``N`` from 1 to 10,
and these have the same value as the Objective-C constant of the same
name.
The version number of the SDK used to build PyObjC, the value
is ``major * 100 + minor`` (e.g. ``1305`` for macOS 13.5).

.. data:: platform

Expand Down
10 changes: 9 additions & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ An overview of the relevant changes in new, and older, releases.
Version 10.0
------------

* PyObjC 10 requires Python 3.8 and no longer supports Python 3.7
* :issue:`542`: PyObjC 10 requires Python 3.8 and no longer supports Python 3.7

* :issue:`547`: Removed all ``MAC_OS_X_VERSION*`` constants from ``objc``.

These constants are needed in practice (switch to :func:`objc.available` to
check for platform availability), and caused unnecessary code churn.

* The value for ``objc.options.deprecation_warnings`` is now a string
instead of an integer.

Version 9.2
-----------
Expand Down
11 changes: 9 additions & 2 deletions pyobjc-core/Lib/objc/_lazyimport.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,17 @@
_name_re = re.compile("^[A-Za-z_][A-Za-z_0-9]*$")


def _deprecation_level(value):
if isinstance(value, int):
return value
major, _, minor = value.partition(".")
return int(major) * 100 + int(minor)


def _check_deprecated(name, deprecation_version):
if (
objc.options.deprecation_warnings
and objc.options.deprecation_warnings >= deprecation_version
objc.options.deprecation_warnings != "0.0"
and _deprecation_level(objc.options.deprecation_warnings) >= deprecation_version
):
warnings.warn(
"%r is deprecated in macOS %d.%d"
Expand Down
86 changes: 5 additions & 81 deletions pyobjc-core/Modules/objc/module.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
* 2. Split the legacy code path to a separate function for testing.
* 3. Only enable the legacy code when the deployment target is 10.9
*/
static long
static void
calc_current_version(void)
{
#if PyObjC_BUILD_RELEASE >= 1010
Expand All @@ -80,14 +80,14 @@
@"/System/Library/CoreServices/SystemVersion.plist"];
if (!plist) {
NSLog(@"Cannot determine system version");
return 0;
return;
}

parts = [[plist valueForKey:@"ProductVersion"] componentsSeparatedByString:@"."];

if (!parts || [parts count] < 2) {
NSLog(@"Cannot determine system version");
return 0;
return;
}

gSystemVersion.majorVersion = [[parts objectAtIndex:0] intValue];
Expand All @@ -99,13 +99,6 @@

[pool release];
}

if (gSystemVersion.majorVersion >= 10 || gSystemVersion.minorVersion >= 10) {
return gSystemVersion.majorVersion * 10000 + gSystemVersion.minorVersion * 100
+ gSystemVersion.patchVersion;
} else {
return gSystemVersion.majorVersion * 100 + gSystemVersion.minorVersion;
}
}

PyObjC_FINAL_CLASS @interface OC_NSAutoreleasePoolCollector : NSObject
Expand Down Expand Up @@ -2100,71 +2093,6 @@ + (void)targetForBecomingMultiThreaded:(id)sender
long value;
} objc_int_values[] = {
// { "NAME", value },
{"MAC_OS_X_VERSION_MAX_ALLOWED", MAC_OS_X_VERSION_MAX_ALLOWED},
{"MAC_OS_X_VERSION_MIN_REQUIRED", MAC_OS_X_VERSION_MIN_REQUIRED},
{"MAC_OS_X_VERSION_10_0", MAC_OS_X_VERSION_10_0},
{"MAC_OS_X_VERSION_10_1", MAC_OS_X_VERSION_10_1},
{"MAC_OS_X_VERSION_10_2", MAC_OS_X_VERSION_10_2},
{"MAC_OS_X_VERSION_10_3", MAC_OS_X_VERSION_10_3},
{"MAC_OS_X_VERSION_10_4", MAC_OS_X_VERSION_10_4},
{"MAC_OS_X_VERSION_10_5", MAC_OS_X_VERSION_10_5},
{"MAC_OS_X_VERSION_10_6", MAC_OS_X_VERSION_10_6},
{"MAC_OS_X_VERSION_10_7", MAC_OS_X_VERSION_10_7},
{"MAC_OS_X_VERSION_10_8", MAC_OS_X_VERSION_10_8},
{"MAC_OS_X_VERSION_10_9", MAC_OS_X_VERSION_10_9},
{"MAC_OS_X_VERSION_10_10", MAC_OS_X_VERSION_10_10},
{"MAC_OS_X_VERSION_10_10_2", MAC_OS_X_VERSION_10_10_2},
{"MAC_OS_X_VERSION_10_10_3", MAC_OS_X_VERSION_10_10_3},
{"MAC_OS_X_VERSION_10_11", MAC_OS_X_VERSION_10_11},
{"MAC_OS_X_VERSION_10_11_2", MAC_OS_X_VERSION_10_11_2},
{"MAC_OS_X_VERSION_10_11_3", MAC_OS_X_VERSION_10_11_3},
{"MAC_OS_X_VERSION_10_11_4", MAC_OS_X_VERSION_10_11_4},
{"MAC_OS_X_VERSION_10_12", MAC_OS_X_VERSION_10_12},
{"MAC_OS_X_VERSION_10_12_1", MAC_OS_X_VERSION_10_12_1},
{"MAC_OS_X_VERSION_10_12_2", MAC_OS_X_VERSION_10_12_2},
{"MAC_OS_X_VERSION_10_12_4", MAC_OS_X_VERSION_10_12_4},
{"MAC_OS_X_VERSION_10_13", MAC_OS_X_VERSION_10_13},
{"MAC_OS_X_VERSION_10_13_1", MAC_OS_X_VERSION_10_13_1},
{"MAC_OS_X_VERSION_10_13_2", MAC_OS_X_VERSION_10_13_2},
{"MAC_OS_X_VERSION_10_13_3", MAC_OS_X_VERSION_10_13_3},
{"MAC_OS_X_VERSION_10_13_4", MAC_OS_X_VERSION_10_13_4},
{"MAC_OS_X_VERSION_10_13_5", MAC_OS_X_VERSION_10_13_5},
{"MAC_OS_X_VERSION_10_13_6", MAC_OS_X_VERSION_10_13_6},
{"MAC_OS_X_VERSION_10_14", MAC_OS_X_VERSION_10_14},
{"MAC_OS_X_VERSION_10_14_1", MAC_OS_X_VERSION_10_14_1},
{"MAC_OS_X_VERSION_10_14_2", MAC_OS_X_VERSION_10_14_2},
{"MAC_OS_X_VERSION_10_14_3", MAC_OS_X_VERSION_10_14_3},
{"MAC_OS_X_VERSION_10_14_4", MAC_OS_X_VERSION_10_14_4},
{"MAC_OS_X_VERSION_10_14_5", MAC_OS_X_VERSION_10_14_5},
{"MAC_OS_X_VERSION_10_14_6", MAC_OS_X_VERSION_10_14_6},
{"MAC_OS_X_VERSION_10_15", MAC_OS_X_VERSION_10_15},
{"MAC_OS_X_VERSION_10_15_1", MAC_OS_X_VERSION_10_15_1},
{"MAC_OS_X_VERSION_10_15_2", MAC_OS_X_VERSION_10_15_2},
{"MAC_OS_X_VERSION_10_15_3", MAC_OS_X_VERSION_10_15_3},
{"MAC_OS_X_VERSION_10_15_4", MAC_OS_X_VERSION_10_15_4},
{"MAC_OS_X_VERSION_10_15_5", MAC_OS_X_VERSION_10_15_5},
{"MAC_OS_X_VERSION_10_15_6", MAC_OS_X_VERSION_10_15_6},
{"MAC_OS_X_VERSION_10_16", MAC_OS_X_VERSION_10_16},
{"MAC_OS_X_VERSION_11_0", MAC_OS_X_VERSION_11_0},
{"MAC_OS_X_VERSION_11_1", MAC_OS_X_VERSION_11_1},
{"MAC_OS_X_VERSION_11_2", MAC_OS_X_VERSION_11_2},
{"MAC_OS_X_VERSION_11_3", MAC_OS_X_VERSION_11_3},
{"MAC_OS_X_VERSION_11_4", MAC_OS_X_VERSION_11_4},
{"MAC_OS_X_VERSION_11_5", MAC_OS_X_VERSION_11_5},
{"MAC_OS_X_VERSION_11_6", MAC_OS_X_VERSION_11_6},
{"MAC_OS_X_VERSION_12_0", MAC_OS_X_VERSION_12_0},
{"MAC_OS_X_VERSION_12_1", MAC_OS_X_VERSION_12_1},
{"MAC_OS_X_VERSION_12_2", MAC_OS_X_VERSION_12_2},
{"MAC_OS_X_VERSION_12_3", MAC_OS_X_VERSION_12_3},
{"MAC_OS_X_VERSION_12_4", MAC_OS_X_VERSION_12_4},
{"MAC_OS_X_VERSION_12_5", MAC_OS_X_VERSION_12_5},
{"MAC_OS_X_VERSION_12_6", MAC_OS_X_VERSION_12_6},
{"MAC_OS_X_VERSION_13_0", MAC_OS_X_VERSION_13_0},
{"MAC_OS_X_VERSION_13_1", MAC_OS_X_VERSION_13_1},
{"MAC_OS_X_VERSION_13_2", MAC_OS_X_VERSION_13_2},
{"MAC_OS_X_VERSION_13_3", MAC_OS_X_VERSION_13_3},
{"MAC_OS_X_VERSION_13_4", MAC_OS_X_VERSION_13_4},
{"MAC_OS_X_VERSION_13_5", MAC_OS_X_VERSION_13_5},
{"PyObjC_BUILD_RELEASE", PyObjC_BUILD_RELEASE},
{"_NSNotFound", NSNotFound},
{"OBJC_ASSOCIATION_ASSIGN", OBJC_ASSOCIATION_ASSIGN},
Expand Down Expand Up @@ -2281,6 +2209,8 @@ + (void)targetForBecomingMultiThreaded:(id)sender
// LCOV_EXCL_STOP
}

calc_current_version();

m = PyModule_Create(&mod_module);
if (m == 0) { // LCOV_BR_EXCL_LINE
return NULL; // LCOV_EXCL_LINE // LCOV_EXCL_LINE
Expand Down Expand Up @@ -2366,12 +2296,6 @@ + (void)targetForBecomingMultiThreaded:(id)sender
ADD_CONSTANT_TABLE(m, objc_typestr_values, bytes_from_char);
ADD_CONSTANT_TABLE(m, objc_typestr_long_values, PyBytes_FromString);

if (PyModule_AddIntConstant( // LCOV_BR_EXCL_LINE
m, "MAC_OS_X_VERSION_CURRENT", calc_current_version())
< 0) {
return NULL; // LCOV_EXCL_LINE
}

/* XXX: Why is this needed? */
if (![NSThread isMultiThreaded]) {
[NSThread detachNewThreadSelector:@selector(targetForBecomingMultiThreaded:)
Expand Down
99 changes: 96 additions & 3 deletions pyobjc-core/Modules/objc/options.m
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@
BOOL_PROP(structs_writable, PyObjC_StructsWritable, YES)

INT_PROP(_nscoding_version, PyObjC_NSCoding_Version, 0)
INT_PROP(deprecation_warnings, PyObjC_DeprecationVersion, 0)
SSIZE_T_PROP(_mapping_count, PyObjC_MappingCount, 0)

/* Private properties */
Expand Down Expand Up @@ -171,6 +170,96 @@
return PyBool_FromLong([OC_NSBundleHack bundleHackUsed]);
}

int PyObjC_DeprecationVersion = 0;

static PyObject*
deprecation_warnings_get(PyObject* s __attribute__((__unused__)),
void* c __attribute__((__unused__)))
{
return PyUnicode_FromFormat("%d.%d", PyObjC_DeprecationVersion / 100,
PyObjC_DeprecationVersion % 100);
}

static int
deprecation_warnings_set(PyObject* s __attribute__((__unused__)), PyObject* newVal,
void* c __attribute__((__unused__)))
{
if (newVal == NULL) {
PyErr_SetString(PyExc_AttributeError,
"Cannot delete option 'deprecation_warnings'");
return -1;
} else if (PyLong_Check(newVal)) {
if (PyErr_WarnEx(
PyExc_DeprecationWarning,
"Setting 'objc.options.deprecation_warnings' to an integer is deprecated",
1)
< 0) {
return -1;
}
PyObjC_DeprecationVersion = (int)PyLong_AsLong(newVal);
if (PyObjC_DeprecationVersion == -1 && PyErr_Occurred()) {
return -1;
}
return 0;
} else if (newVal == Py_None) {
PyObjC_DeprecationVersion = 0;
return 0;
} else if (PyUnicode_Check(newVal)) {
/* Cast to 'char*' is necessary to get rid of 'const', and that's
* needed due to the harebrained interface of strtoul
*/
char* text = (char*)PyUnicode_AsUTF8(newVal);
if (text == NULL) {
return -1;
}

unsigned long major = 0;
unsigned long minor = 0;

major = strtoul(text, &text, 10);
if (major >= 100 || ((major == 0 || major == ULONG_MAX) && errno != 0)) {
PyErr_Format(PyExc_ValueError,
"Invalid version for 'objc.options.deprecation_warning': %R",
newVal);
return -1;
}
if (*text != '\0') {
if (*text != '.') {
PyErr_Format(PyExc_ValueError,
"Invalid version for 'objc.options.deprecation_warning': %R",
newVal);
return -1;
}

text++;

minor = strtoul(text, &text, 10);
if (minor >= 100 || ((minor == 0 || minor == ULONG_MAX) && errno != 0)) {
PyErr_Format(PyExc_ValueError,
"Invalid version for 'objc.options.deprecation_warning': %R",
newVal);
return -1;
}
if (*text != '\0') {
PyErr_Format(PyExc_ValueError,
"Invalid version for 'objc.options.deprecation_warning': %R",
newVal);
return -1;
}
}

PyObjC_DeprecationVersion = (int)(major * 100 + minor);
return 0;

} else {
PyErr_Format(PyExc_TypeError,
"Expecting 'str' value for 'objc.options.deprecation_warnings', got "
"instance of '%s'",
Py_TYPE(newVal)->tp_name);
return -1;
}
}

static PyGetSetDef options_getset[] = {
/* Public properties */
GETSET(verbose, "If True the bridge is more verbose"),
Expand Down Expand Up @@ -200,8 +289,6 @@
GETSET(_datetime_datetime_type, "Prive config for datetime.datetime"),
GETSET(_callable_signature,
"Private helper function for generating __signature__ for selectors"),
GETSET(deprecation_warnings,
"If not 0 give deprecation warnings for the given SDK version"),
GETSET(_getKey, "Private helper used for KeyValueCoding support"),
GETSET(_setKey, "Private helper used for KeyValueCoding support"),
GETSET(_getKeyPath, "Private helper used for KeyValueCoding support"),
Expand All @@ -210,6 +297,12 @@
"Private helper used for transforming attributes for Objective-C classes"),
GETSET(_processClassDict,
"Private helper used for splitting a class dict into parts"),
{
.name = "deprecation_warnings",
.get = deprecation_warnings_get,
.set = deprecation_warnings_set,
.doc = "If not '0.0.0' give deprecation warnings for the given SDK version",
},
{
.name = "_bundle_hack_used",
.get = bundle_hack_get,
Expand Down
Loading

0 comments on commit 9a6b781

Please sign in to comment.