Skip to content

Commit c36c2b7

Browse files
authored
Merge branch 'main' into gh-139269
2 parents c2cf365 + 408154d commit c36c2b7

File tree

8 files changed

+84
-19
lines changed

8 files changed

+84
-19
lines changed

Apple/__main__.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ def unpack_deps(
316316
for name_ver in [
317317
"BZip2-1.0.8-2",
318318
"libFFI-3.4.7-2",
319-
"OpenSSL-3.0.16-2",
319+
"OpenSSL-3.0.17-1",
320320
"XZ-5.6.4-2",
321321
"mpdecimal-4.0.0-2",
322322
"zstd-1.5.7-1",
@@ -577,6 +577,7 @@ def create_xcframework(platform: str) -> str:
577577

578578
# Extract the package version from the merged framework
579579
version = package_version(package_path / "Python.xcframework")
580+
version_tag = ".".join(version.split(".")[:2])
580581

581582
# On non-macOS platforms, each framework in XCframework only contains the
582583
# headers, libPython, plus an Info.plist. Other resources like the standard
@@ -647,6 +648,23 @@ def create_xcframework(platform: str) -> str:
647648
slice_framework / f"Headers/pyconfig-{arch}.h",
648649
)
649650

651+
# Apple identifies certain libraries as "security risks"; if you
652+
# statically link those libraries into a Framework, you become
653+
# responsible for providing a privacy manifest for that framework.
654+
xcprivacy_file = {
655+
"OpenSSL": subdir(host_triple) / "prefix/share/OpenSSL.xcprivacy"
656+
}
657+
print(f" - {multiarch} xcprivacy files")
658+
for module, lib in [
659+
("_hashlib", "OpenSSL"),
660+
("_ssl", "OpenSSL"),
661+
]:
662+
shutil.copy(
663+
xcprivacy_file[lib],
664+
slice_path
665+
/ f"lib-{arch}/python{version_tag}/lib-dynload/{module}.xcprivacy",
666+
)
667+
650668
print(" - build tools")
651669
shutil.copytree(
652670
PYTHON_DIR / "Apple/testbed/Python.xcframework/build",

Apple/testbed/Python.xcframework/build/utils.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ install_dylib () {
6767

6868
# The name of the extension file
6969
EXT=$(basename "$FULL_EXT")
70+
# The name and location of the module
71+
MODULE_PATH=$(dirname "$FULL_EXT")
72+
MODULE_NAME=$(echo $EXT | cut -d "." -f 1)
7073
# The location of the extension file, relative to the bundle
7174
RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/}
7275
# The path to the extension file, relative to the install base
@@ -94,6 +97,16 @@ install_dylib () {
9497
# Create a back reference to the .so file location in the framework
9598
echo "${RELATIVE_EXT%.so}.fwork" > "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin"
9699

100+
# If the framework provides an xcprivacy file, install it.
101+
if [ -e "$MODULE_PATH/$MODULE_NAME.xcprivacy" ]; then
102+
echo "Installing XCPrivacy file for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME"
103+
XCPRIVACY_FILE="$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/PrivacyInfo.xcprivacy"
104+
if [ -e "$XCPRIVACY_FILE" ]; then
105+
rm -rf "$XCPRIVACY_FILE"
106+
fi
107+
mv "$MODULE_PATH/$MODULE_NAME.xcprivacy" "$XCPRIVACY_FILE"
108+
fi
109+
97110
echo "Signing framework as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)..."
98111
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER"
99112
}

Doc/using/ios.rst

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,12 @@ App Store Compliance
328328
The only mechanism for distributing apps to third-party iOS devices is to
329329
submit the app to the iOS App Store; apps submitted for distribution must pass
330330
Apple's app review process. This process includes a set of automated validation
331-
rules that inspect the submitted application bundle for problematic code.
331+
rules that inspect the submitted application bundle for problematic code. There
332+
are some steps that must be taken to ensure that your app will be able to pass
333+
these validation steps.
334+
335+
Incompatible code in the standard library
336+
-----------------------------------------
332337

333338
The Python standard library contains some code that is known to violate these
334339
automated rules. While these violations appear to be false positives, Apple's
@@ -339,3 +344,18 @@ The Python source tree contains
339344
:source:`a patch file <Mac/Resources/app-store-compliance.patch>` that will remove
340345
all code that is known to cause issues with the App Store review process. This
341346
patch is applied automatically when building for iOS.
347+
348+
Privacy manifests
349+
-----------------
350+
351+
In April 2025, Apple introduced a requirement for `certain third-party
352+
libraries to provide a Privacy Manifest
353+
<https://developer.apple.com/support/third-party-SDK-requirements>`__.
354+
As a result, if you have a binary module that uses one of the affected
355+
libraries, you must provide an ``.xcprivacy`` file for that library.
356+
OpenSSL is one library affected by this requirement, but there are others.
357+
358+
If you produce a binary module named ``mymodule.so``, and use you the Xcode
359+
build script described in step 7 above, you can place a ``mymodule.xcprivacy``
360+
file next to ``mymodule.so``, and the privacy manifest will be installed into
361+
the required location when the binary module is converted into a framework.

Lib/imaplib.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -497,8 +497,6 @@ def append(self, mailbox, flags, date_time, message):
497497
else:
498498
date_time = None
499499
literal = MapCRLF.sub(CRLF, message)
500-
if self.utf8_enabled:
501-
literal = b'UTF8 (' + literal + b')'
502500
self.literal = literal
503501
return self._simple_command(name, mailbox, flags, date_time)
504502

@@ -1119,7 +1117,11 @@ def _command(self, name, *args):
11191117
literator = literal
11201118
else:
11211119
literator = None
1122-
data = data + bytes(' {%s}' % len(literal), self._encoding)
1120+
if self.utf8_enabled:
1121+
data = data + bytes(' UTF8 (~{%s}' % len(literal), self._encoding)
1122+
literal = literal + b')'
1123+
else:
1124+
data = data + bytes(' {%s}' % len(literal), self._encoding)
11231125

11241126
if __debug__:
11251127
if self.debug >= 4:

Lib/test/test_imaplib.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,11 @@ def cmd_AUTHENTICATE(self, tag, args):
372372
self._send_tagged(tag, 'OK', 'FAKEAUTH successful')
373373
def cmd_APPEND(self, tag, args):
374374
self._send_textline('+')
375-
self.server.response = yield
375+
self.server.response = args
376+
literal = yield
377+
self.server.response.append(literal)
378+
literal = yield
379+
self.server.response.append(literal)
376380
self._send_tagged(tag, 'OK', 'okay')
377381
client, server = self._setup(UTF8AppendServer)
378382
self.assertEqual(client._encoding, 'ascii')
@@ -383,10 +387,13 @@ def cmd_APPEND(self, tag, args):
383387
self.assertEqual(code, 'OK')
384388
self.assertEqual(client._encoding, 'utf-8')
385389
msg_string = 'Subject: üñí©öðé'
386-
typ, data = client.append(None, None, None, msg_string.encode('utf-8'))
390+
typ, data = client.append(
391+
None, None, None, (msg_string + '\n').encode('utf-8'))
387392
self.assertEqual(typ, 'OK')
388393
self.assertEqual(server.response,
389-
('UTF8 (%s)\r\n' % msg_string).encode('utf-8'))
394+
['INBOX', 'UTF8',
395+
'(~{25}', ('%s\r\n' % msg_string).encode('utf-8'),
396+
b')\r\n' ])
390397

391398
def test_search_disallows_charset_in_utf8_mode(self):
392399
class UTF8Server(SimpleIMAPHandler):
@@ -881,7 +888,11 @@ def test_enable_UTF8_True_append(self):
881888
class UTF8AppendServer(self.UTF8Server):
882889
def cmd_APPEND(self, tag, args):
883890
self._send_textline('+')
884-
self.server.response = yield
891+
self.server.response = args
892+
literal = yield
893+
self.server.response.append(literal)
894+
literal = yield
895+
self.server.response.append(literal)
885896
self._send_tagged(tag, 'OK', 'okay')
886897

887898
with self.reaped_pair(UTF8AppendServer) as (server, client):
@@ -895,12 +906,12 @@ def cmd_APPEND(self, tag, args):
895906
self.assertEqual(client._encoding, 'utf-8')
896907
msg_string = 'Subject: üñí©öðé'
897908
typ, data = client.append(
898-
None, None, None, msg_string.encode('utf-8'))
909+
None, None, None, (msg_string + '\n').encode('utf-8'))
899910
self.assertEqual(typ, 'OK')
900-
self.assertEqual(
901-
server.response,
902-
('UTF8 (%s)\r\n' % msg_string).encode('utf-8')
903-
)
911+
self.assertEqual(server.response,
912+
['INBOX', 'UTF8',
913+
'(~{25}', ('%s\r\n' % msg_string).encode('utf-8'),
914+
b')\r\n' ])
904915

905916
# XXX also need a test that makes sure that the Literal and Untagged_status
906917
# regexes uses unicode in UTF8 mode instead of the default ASCII.

Lib/urllib/parse.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,9 @@ def clear_cache():
9797
_byte_quoter_factory.cache_clear()
9898

9999
# Helpers for bytes handling
100-
# For 3.2, we deliberately require applications that
101-
# handle improperly quoted URLs to do their own
102-
# decoding and encoding. If valid use cases are
103-
# presented, we may relax this by using latin-1
104-
# decoding internally for 3.3
100+
# We deliberately require applications that
101+
# handle improperly quoted URLs to do their
102+
# own decoding and encoding.
105103
_implicit_encoding = 'ascii'
106104
_implicit_errors = 'strict'
107105

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
UTF8 support for the IMAP APPEND command has been made RFC compliant.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
XCframeworks now include privacy manifests to satisfy Apple App Store
2+
submission requirements.

0 commit comments

Comments
 (0)