Skip to content

Commit 728432c

Browse files
authoredJul 21, 2024
gh-120522: Apply App Store compliance patch during installation (#121947)
Adds a --with-app-store-compliance configuration option that patches out code known to be an issue with App Store review processes. This option is applied automatically on iOS, and optionally on macOS.
1 parent 5901d92 commit 728432c

File tree

9 files changed

+207
-2
lines changed

9 files changed

+207
-2
lines changed
 

‎Doc/library/urllib.parse.rst

+9-1
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,19 @@ to an absolute URL given a "base URL."
2222

2323
The module has been designed to match the internet RFC on Relative Uniform
2424
Resource Locators. It supports the following URL schemes: ``file``, ``ftp``,
25-
``gopher``, ``hdl``, ``http``, ``https``, ``imap``, ``mailto``, ``mms``,
25+
``gopher``, ``hdl``, ``http``, ``https``, ``imap``, ``itms-services``, ``mailto``, ``mms``,
2626
``news``, ``nntp``, ``prospero``, ``rsync``, ``rtsp``, ``rtsps``, ``rtspu``,
2727
``sftp``, ``shttp``, ``sip``, ``sips``, ``snews``, ``svn``, ``svn+ssh``,
2828
``telnet``, ``wais``, ``ws``, ``wss``.
2929

30+
.. impl-detail::
31+
32+
The inclusion of the ``itms-services`` URL scheme can prevent an app from
33+
passing Apple's App Store review process for the macOS and iOS App Stores.
34+
Handling for the ``itms-services`` scheme is always removed on iOS; on
35+
macOS, it *may* be removed if CPython has been built with the
36+
:option:`--with-app-store-compliance` option.
37+
3038
The :mod:`urllib.parse` module defines functions that fall into two broad
3139
categories: URL parsing and URL quoting. These are covered in detail in
3240
the following sections.

‎Doc/using/configure.rst

+11
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,17 @@ See :source:`Mac/README.rst`.
945945
Specify the name for the python framework on macOS only valid when
946946
:option:`--enable-framework` is set (default: ``Python``).
947947

948+
.. option:: --with-app-store-compliance
949+
.. option:: --with-app-store-compliance=PATCH-FILE
950+
951+
The Python standard library contains strings that are known to trigger
952+
automated inspection tool errors when submitted for distribution by
953+
the macOS and iOS App Stores. If enabled, this option will apply the list of
954+
patches that are known to correct app store compliance. A custom patch
955+
file can also be specified. This option is disabled by default.
956+
957+
.. versionadded:: 3.13
958+
948959
iOS Options
949960
-----------
950961

‎Doc/using/ios.rst

+18
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,21 @@ modules in your app, some additional steps will be required:
323323

324324
* If you're using a separate folder for third-party packages, ensure that folder
325325
is included as part of the ``PYTHONPATH`` configuration in step 10.
326+
327+
App Store Compliance
328+
====================
329+
330+
The only mechanism for distributing apps to third-party iOS devices is to
331+
submit the app to the iOS App Store; apps submitted for distribution must pass
332+
Apple's app review process. This process includes a set of automated validation
333+
rules that inspect the submitted application bundle for problematic code.
334+
335+
The Python standard library contains some code that is known to violate these
336+
automated rules. While these violations appear to be false positives, Apple's
337+
review rules cannot be challenged; so, it is necessary to modify the Python
338+
standard library for an app to pass App Store review.
339+
340+
The Python source tree contains
341+
:source:`a patch file <Mac/Resources/app-store-compliance.patch>` that will remove
342+
all code that is known to cause issues with the App Store review process. This
343+
patch is applied automatically when building for iOS.

‎Doc/using/mac.rst

+22
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,28 @@ distributable application:
188188
* `PyInstaller <https://pyinstaller.org/>`__: A cross-platform packaging tool that creates
189189
a single file or folder as a distributable artifact.
190190

191+
App Store Compliance
192+
--------------------
193+
194+
Apps submitted for distribution through the macOS App Store must pass Apple's
195+
app review process. This process includes a set of automated validation rules
196+
that inspect the submitted application bundle for problematic code.
197+
198+
The Python standard library contains some code that is known to violate these
199+
automated rules. While these violations appear to be false positives, Apple's
200+
review rules cannot be challenged. Therefore, it is necessary to modify the
201+
Python standard library for an app to pass App Store review.
202+
203+
The Python source tree contains
204+
:source:`a patch file <Mac/Resources/app-store-compliance.patch>` that will remove
205+
all code that is known to cause issues with the App Store review process. This
206+
patch is applied automatically when CPython is configured with the
207+
:option:`--with-app-store-compliance` option.
208+
209+
This patch is not normally required to use CPython on a Mac; nor is it required
210+
if you are distributing an app *outside* the macOS App Store. It is *only*
211+
required if you are using the macOS App Store as a distribution channel.
212+
191213
Other Resources
192214
===============
193215

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py
2+
index d6c83a75c1c..19ed4e01091 100644
3+
--- a/Lib/test/test_urlparse.py
4+
+++ b/Lib/test/test_urlparse.py
5+
@@ -237,11 +237,6 @@ def test_roundtrips(self):
6+
'','',''),
7+
('git+ssh', 'git@github.com','/user/project.git',
8+
'', '')),
9+
- ('itms-services://?action=download-manifest&url=https://example.com/app',
10+
- ('itms-services', '', '', '',
11+
- 'action=download-manifest&url=https://example.com/app', ''),
12+
- ('itms-services', '', '',
13+
- 'action=download-manifest&url=https://example.com/app', '')),
14+
('+scheme:path/to/file',
15+
('', '', '+scheme:path/to/file', '', '', ''),
16+
('', '', '+scheme:path/to/file', '', '')),
17+
diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py
18+
index 8f724f907d4..148caf742c9 100644
19+
--- a/Lib/urllib/parse.py
20+
+++ b/Lib/urllib/parse.py
21+
@@ -59,7 +59,7 @@
22+
'imap', 'wais', 'file', 'mms', 'https', 'shttp',
23+
'snews', 'prospero', 'rtsp', 'rtsps', 'rtspu', 'rsync',
24+
'svn', 'svn+ssh', 'sftp', 'nfs', 'git', 'git+ssh',
25+
- 'ws', 'wss', 'itms-services']
26+
+ 'ws', 'wss']
27+
28+
uses_params = ['', 'ftp', 'hdl', 'prospero', 'http', 'imap',
29+
'https', 'shttp', 'rtsp', 'rtsps', 'rtspu', 'sip',

‎Makefile.pre.in

+22-1
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ EXPORTSFROM= @EXPORTSFROM@
179179
EXE= @EXEEXT@
180180
BUILDEXE= @BUILDEXEEXT@
181181

182+
# Name of the patch file to apply for app store compliance
183+
APP_STORE_COMPLIANCE_PATCH=@APP_STORE_COMPLIANCE_PATCH@
184+
182185
# Short name and location for Mac OS X Python framework
183186
UNIVERSALSDK=@UNIVERSALSDK@
184187
PYTHONFRAMEWORK= @PYTHONFRAMEWORK@
@@ -692,7 +695,7 @@ list-targets:
692695
@grep -E '^[A-Za-z][-A-Za-z0-9]+:' Makefile | awk -F : '{print $$1}'
693696

694697
.PHONY: build_all
695-
build_all: check-clean-src $(BUILDPYTHON) platform sharedmods \
698+
build_all: check-clean-src check-app-store-compliance $(BUILDPYTHON) platform sharedmods \
696699
gdbhooks Programs/_testembed scripts checksharedmods rundsymutil
697700

698701
.PHONY: build_wasm
@@ -715,6 +718,16 @@ check-clean-src:
715718
exit 1; \
716719
fi
717720

721+
# Check that the app store compliance patch can be applied (if configured).
722+
# This is checked as a dry-run against the original library sources;
723+
# the patch will be actually applied during the install phase.
724+
.PHONY: check-app-store-compliance
725+
check-app-store-compliance:
726+
@if [ "$(APP_STORE_COMPLIANCE_PATCH)" != "" ]; then \
727+
patch --dry-run --quiet --force --strip 1 --directory "$(abs_srcdir)" --input "$(abs_srcdir)/$(APP_STORE_COMPLIANCE_PATCH)"; \
728+
echo "App store compliance patch can be applied."; \
729+
fi
730+
718731
# Profile generation build must start from a clean tree.
719732
profile-clean-stamp:
720733
$(MAKE) clean
@@ -2570,6 +2583,14 @@ libinstall: all $(srcdir)/Modules/xxmodule.c
25702583
$(INSTALL_DATA) `cat pybuilddir.txt`/_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).py \
25712584
$(DESTDIR)$(LIBDEST); \
25722585
$(INSTALL_DATA) $(srcdir)/LICENSE $(DESTDIR)$(LIBDEST)/LICENSE.txt
2586+
@ # If app store compliance has been configured, apply the patch to the
2587+
@ # installed library code. The patch has been previously validated against
2588+
@ # the original source tree, so we can ignore any errors that are raised
2589+
@ # due to files that are missing because of --disable-test-modules etc.
2590+
@if [ "$(APP_STORE_COMPLIANCE_PATCH)" != "" ]; then \
2591+
echo "Applying app store compliance patch"; \
2592+
patch --force --reject-file "$(abs_builddir)/app-store-compliance.rej" --strip 2 --directory "$(DESTDIR)$(LIBDEST)" --input "$(abs_srcdir)/$(APP_STORE_COMPLIANCE_PATCH)" || true ; \
2593+
fi
25732594
@ # Build PYC files for the 3 optimization levels (0, 1, 2)
25742595
-PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \
25752596
$(PYTHON_FOR_BUILD) -Wi $(DESTDIR)$(LIBDEST)/compileall.py \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Added a :option:`--with-app-store-compliance` option to patch out known
2+
issues with macOS/iOS App Store review processes.

‎configure

+53
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎configure.ac

+41
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,47 @@ AC_SUBST([INSTALLTARGETS])
695695
AC_DEFINE_UNQUOTED([_PYTHONFRAMEWORK], ["${PYTHONFRAMEWORK}"],
696696
[framework name])
697697

698+
dnl quadrigraphs "@<:@" and "@:>@" produce "[" and "]" in the output
699+
AC_MSG_CHECKING([for --with-app-store-compliance])
700+
AC_ARG_WITH(
701+
[app_store_compliance],
702+
[AS_HELP_STRING(
703+
[--with-app-store-compliance=@<:@PATCH-FILE@:>@],
704+
[Enable any patches required for compiliance with app stores.
705+
Optional PATCH-FILE specifies the custom patch to apply.]
706+
)],[
707+
case "$withval" in
708+
yes)
709+
case $ac_sys_system in
710+
Darwin|iOS)
711+
# iOS is able to share the macOS patch
712+
APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch"
713+
;;
714+
*) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;;
715+
esac
716+
AC_MSG_RESULT([applying default app store compliance patch])
717+
;;
718+
*)
719+
APP_STORE_COMPLIANCE_PATCH="${withval}"
720+
AC_MSG_RESULT([applying custom app store compliance patch])
721+
;;
722+
esac
723+
],[
724+
case $ac_sys_system in
725+
iOS)
726+
# Always apply the compliance patch on iOS; we can use the macOS patch
727+
APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch"
728+
AC_MSG_RESULT([applying default app store compliance patch])
729+
;;
730+
*)
731+
# No default app compliance patching on any other platform
732+
APP_STORE_COMPLIANCE_PATCH=
733+
AC_MSG_RESULT([not patching for app store compliance])
734+
;;
735+
esac
736+
])
737+
AC_SUBST([APP_STORE_COMPLIANCE_PATCH])
738+
698739
AC_SUBST([_PYTHON_HOST_PLATFORM])
699740
if test "$cross_compiling" = yes; then
700741
case "$host" in

0 commit comments

Comments
 (0)