diff --git a/00001-rpath.patch b/00001-rpath.patch index 170908e..778c077 100644 --- a/00001-rpath.patch +++ b/00001-rpath.patch @@ -9,7 +9,7 @@ Subject: [PATCH] 00001: Fixup distutils/unixccompiler.py to remove standard 1 file changed, 9 insertions(+) diff --git a/Lib/distutils/unixccompiler.py b/Lib/distutils/unixccompiler.py -index f0792de74a..4d837936c6 100644 +index d00c48981e..0283a28c19 100644 --- a/Lib/distutils/unixccompiler.py +++ b/Lib/distutils/unixccompiler.py @@ -82,6 +82,15 @@ class UnixCCompiler(CCompiler): diff --git a/00111-no-static-lib.patch b/00111-no-static-lib.patch index b44dbc9..51e0e36 100644 --- a/00111-no-static-lib.patch +++ b/00111-no-static-lib.patch @@ -21,10 +21,10 @@ Co-authored-by: Miro Hrončok 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in -index 0c3981c132..5587422ec7 100644 +index 42b1ec622a..c1cf1585df 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in -@@ -589,7 +589,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c +@@ -588,7 +588,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c $(PYTHON_FOR_REGEN) $(srcdir)/Tools/clinic/clinic.py --make --srcdir $(srcdir) # Build the interpreter @@ -33,7 +33,7 @@ index 0c3981c132..5587422ec7 100644 $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) platform: $(BUILDPYTHON) pybuilddir.txt -@@ -637,12 +637,6 @@ sharedmods: $(BUILDPYTHON) pybuilddir.txt Modules/_math.o +@@ -636,12 +636,6 @@ sharedmods: $(BUILDPYTHON) pybuilddir.txt Modules/_math.o _TCLTK_INCLUDES='$(TCLTK_INCLUDES)' _TCLTK_LIBS='$(TCLTK_LIBS)' \ $(PYTHON_FOR_BUILD) $(srcdir)/setup.py $$quiet build @@ -46,7 +46,7 @@ index 0c3981c132..5587422ec7 100644 libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS) if test $(INSTSONAME) != $(LDLIBRARY); then \ $(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ -@@ -724,7 +718,7 @@ Makefile Modules/config.c: Makefile.pre \ +@@ -723,7 +717,7 @@ Makefile Modules/config.c: Makefile.pre \ @echo "The Makefile was updated, you may need to re-run make." @@ -55,7 +55,7 @@ index 0c3981c132..5587422ec7 100644 $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) ############################################################################ -@@ -1652,17 +1646,6 @@ libainstall: @DEF_MAKE_RULE@ python-config +@@ -1651,17 +1645,6 @@ libainstall: @DEF_MAKE_RULE@ python-config else true; \ fi; \ done diff --git a/00189-use-rpm-wheels.patch b/00189-use-rpm-wheels.patch index 608d265..0fed4aa 100644 --- a/00189-use-rpm-wheels.patch +++ b/00189-use-rpm-wheels.patch @@ -8,11 +8,11 @@ We keep them in /usr/share/python-wheels Downstream only: upstream bundles We might eventually pursuit upstream support, but it's low prio --- - Lib/ensurepip/__init__.py | 33 ++++++++++++++++++++++----------- - 1 file changed, 22 insertions(+), 11 deletions(-) + Lib/ensurepip/__init__.py | 37 ++++++++++++++++++++++++++----------- + 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py -index 57e576d1a3..5bd16a6c59 100644 +index e510cc7fb2..5bd16a6c59 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -1,3 +1,5 @@ @@ -21,7 +21,7 @@ index 57e576d1a3..5bd16a6c59 100644 import os import os.path import sys -@@ -6,16 +8,28 @@ import tempfile +@@ -6,13 +8,29 @@ import tempfile import subprocess from importlib import resources @@ -30,13 +30,13 @@ index 57e576d1a3..5bd16a6c59 100644 __all__ = ["version", "bootstrap"] - +-_SETUPTOOLS_VERSION = "58.1.0" +-_PIP_VERSION = "22.0.4" ++ +_WHEEL_DIR = "/usr/share/python-wheels/" - --_SETUPTOOLS_VERSION = "56.0.0" ++ +_wheels = {} - --_PIP_VERSION = "21.1.1" ++ +def _get_most_recent_wheel_version(pkg): + prefix = os.path.join(_WHEEL_DIR, "{}-".format(pkg)) + _wheels[pkg] = {} @@ -51,10 +51,11 @@ index 57e576d1a3..5bd16a6c59 100644 +_SETUPTOOLS_VERSION = _get_most_recent_wheel_version("setuptools") + +_PIP_VERSION = _get_most_recent_wheel_version("pip") - ++ _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), -@@ -105,13 +119,10 @@ def _bootstrap(*, root=None, upgrade=False, user=False, + ("pip", _PIP_VERSION, "py3"), +@@ -101,13 +119,10 @@ def _bootstrap(*, root=None, upgrade=False, user=False, # additional paths that need added to sys.path additional_paths = [] for project, version, py_tag in _PROJECTS: diff --git a/00251-change-user-install-location.patch b/00251-change-user-install-location.patch index 57b71bf..074e59d 100644 --- a/00251-change-user-install-location.patch +++ b/00251-change-user-install-location.patch @@ -2,41 +2,61 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Michal Cyprian Date: Mon, 26 Jun 2017 16:32:56 +0200 Subject: [PATCH] 00251: Change user install location +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit Set values of prefix and exec_prefix in distutils install command to /usr/local if executable is /usr/bin/python* and RPM build is not detected to make pip and distutils install into separate location. Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe -Downstream only: Awaiting resources to work on upstream PEP +Downstream only: Reworked in Fedora 36+/Python 3.10+ to follow https://bugs.python.org/issue43976 + +pypa/distutils integration: https://github.com/pypa/distutils/pull/70 + +Also set sysconfig._PIP_USE_SYSCONFIG = False, to force pip-upgraded-pip +to respect this patched distutils install command. +See https://bugzilla.redhat.com/show_bug.cgi?id=2014513 + +Co-authored-by: Miro Hrončok --- - Lib/distutils/command/install.py | 15 +++++++++++++-- + Lib/distutils/command/install.py | 9 +++++++-- Lib/site.py | 9 ++++++++- - 2 files changed, 21 insertions(+), 3 deletions(-) + Lib/sysconfig.py | 17 +++++++++++++++++ + 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Lib/distutils/command/install.py b/Lib/distutils/command/install.py -index aaa300efa9..f8d453912a 100644 +index aaa300efa9..18f01f10d4 100644 --- a/Lib/distutils/command/install.py +++ b/Lib/distutils/command/install.py -@@ -419,8 +419,19 @@ class install(Command): +@@ -3,6 +3,7 @@ + Implements the Distutils 'install' command.""" + + import sys ++import sysconfig + import os + + from distutils import log +@@ -142,6 +143,8 @@ class install(Command): + + negative_opt = {'no-compile' : 'compile'} + ++ # Allow Fedora to add components to the prefix ++ _prefix_addition = getattr(sysconfig, '_prefix_addition', '') + + def initialize_options(self): + """Initializes options.""" +@@ -419,8 +422,10 @@ class install(Command): raise DistutilsOptionError( "must not supply exec-prefix without prefix") - self.prefix = os.path.normpath(sys.prefix) - self.exec_prefix = os.path.normpath(sys.exec_prefix) -+ # self.prefix is set to sys.prefix + /local/ -+ # if neither RPM build nor virtual environment is -+ # detected to make pip and distutils install packages -+ # into the separate location. -+ if (not (hasattr(sys, 'real_prefix') or -+ sys.prefix != sys.base_prefix) and -+ 'RPM_BUILD_ROOT' not in os.environ): -+ addition = "/local" -+ else: -+ addition = "" -+ -+ self.prefix = os.path.normpath(sys.prefix) + addition -+ self.exec_prefix = os.path.normpath(sys.exec_prefix) + addition ++ self.prefix = ( ++ os.path.normpath(sys.prefix) + self._prefix_addition) ++ self.exec_prefix = ( ++ os.path.normpath(sys.exec_prefix) + self._prefix_addition) else: if self.exec_prefix is None: @@ -60,3 +80,31 @@ index 9e617afb00..db14f715f9 100644 for sitedir in getsitepackages(prefixes): if os.path.isdir(sitedir): addsitedir(sitedir, known_paths) +diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py +index e3f79bfde5..e124104876 100644 +--- a/Lib/sysconfig.py ++++ b/Lib/sysconfig.py +@@ -86,6 +86,23 @@ _INSTALL_SCHEMES = { + }, + } + ++# Force pip to use distutils paths instead of sysconfig ++# https://github.com/pypa/pip/issues/10647 ++_PIP_USE_SYSCONFIG = False ++ ++# This is used by distutils.command.install in the stdlib ++# as well as pypa/distutils (e.g. bundled in setuptools). ++# The self.prefix value is set to sys.prefix + /local/ ++# if neither RPM build nor virtual environment is ++# detected to make distutils install packages ++# into the separate location. ++# https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++if (not (hasattr(sys, 'real_prefix') or ++ sys.prefix != sys.base_prefix) and ++ 'RPM_BUILD_ROOT' not in os.environ): ++ _prefix_addition = "/local" ++ ++ + _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include', + 'scripts', 'data') + diff --git a/00353-architecture-names-upstream-downstream.patch b/00353-architecture-names-upstream-downstream.patch index 5e98140..b0b955c 100644 --- a/00353-architecture-names-upstream-downstream.patch +++ b/00353-architecture-names-upstream-downstream.patch @@ -34,10 +34,10 @@ Co-authored-by: Miro Hrončok 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py -index 7c4157369d..c3b95638a6 100644 +index f3828b10e1..f0c9f8e383 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py -@@ -1636,7 +1636,7 @@ def _get_supported_file_loaders(): +@@ -1645,7 +1645,7 @@ def _get_supported_file_loaders(): Each item is a tuple (loader, suffixes). """ @@ -46,7 +46,7 @@ index 7c4157369d..c3b95638a6 100644 source = SourceFileLoader, SOURCE_SUFFIXES bytecode = SourcelessFileLoader, BYTECODE_SUFFIXES return [extensions, source, bytecode] -@@ -1692,7 +1692,7 @@ def _setup(_bootstrap_module): +@@ -1701,7 +1701,7 @@ def _setup(_bootstrap_module): # Constants setattr(self_module, '_relax_case', _make_relax_case()) @@ -55,7 +55,7 @@ index 7c4157369d..c3b95638a6 100644 if builtin_os == 'nt': SOURCE_SUFFIXES.append('.pyw') if '_d.pyd' in EXTENSION_SUFFIXES: -@@ -1705,3 +1705,39 @@ def _install(_bootstrap_module): +@@ -1714,3 +1714,39 @@ def _install(_bootstrap_module): supported_loaders = _get_supported_file_loaders() sys.path_hooks.extend([FileFinder.path_hook(*supported_loaders)]) sys.meta_path.append(PathFinder) diff --git a/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch b/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch new file mode 100644 index 0000000..c089771 --- /dev/null +++ b/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch @@ -0,0 +1,103 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= +Date: Fri, 19 Nov 2021 13:37:16 +0100 +Subject: [PATCH] 00371: Revert "bpo-1596321: Fix threading._shutdown() for the + main thread (GH-28549) (GH-28589)" + +This reverts commit 94d19f606fa18a1c4d2faca1caf2f470a8ce6d46. It +introduced regression causing FreeIPA's tests to fail. + +For more info see: +https://bodhi.fedoraproject.org/updates/FEDORA-2021-e152ce5f31 +https://github.com/GrahamDumpleton/mod_wsgi/issues/730 +--- + Lib/test/test_threading.py | 33 --------------------------------- + Lib/threading.py | 25 ++++++++----------------- + 2 files changed, 8 insertions(+), 50 deletions(-) + +diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py +index af480b9014..a57085b75d 100644 +--- a/Lib/test/test_threading.py ++++ b/Lib/test/test_threading.py +@@ -814,39 +814,6 @@ class ThreadTests(BaseTestCase): + threading.Thread(target=noop).start() + # Thread.join() is not called + +- def test_import_from_another_thread(self): +- # bpo-1596321: If the threading module is first import from a thread +- # different than the main thread, threading._shutdown() must handle +- # this case without logging an error at Python exit. +- code = textwrap.dedent(''' +- import _thread +- import sys +- +- event = _thread.allocate_lock() +- event.acquire() +- +- def import_threading(): +- import threading +- event.release() +- +- if 'threading' in sys.modules: +- raise Exception('threading is already imported') +- +- _thread.start_new_thread(import_threading, ()) +- +- # wait until the threading module is imported +- event.acquire() +- event.release() +- +- if 'threading' not in sys.modules: +- raise Exception('threading is not imported') +- +- # don't wait until the thread completes +- ''') +- rc, out, err = assert_python_ok("-c", code) +- self.assertEqual(out, b'') +- self.assertEqual(err, b'') +- + + class ThreadJoinOnShutdown(BaseTestCase): + +diff --git a/Lib/threading.py b/Lib/threading.py +index 565f868e92..f0ef109a6e 100644 +--- a/Lib/threading.py ++++ b/Lib/threading.py +@@ -1440,29 +1440,20 @@ def _shutdown(): + + global _SHUTTING_DOWN + _SHUTTING_DOWN = True ++ # Main thread ++ tlock = _main_thread._tstate_lock ++ # The main thread isn't finished yet, so its thread state lock can't have ++ # been released. ++ assert tlock is not None ++ assert tlock.locked() ++ tlock.release() ++ _main_thread._stop() + + # Call registered threading atexit functions before threads are joined. + # Order is reversed, similar to atexit. + for atexit_call in reversed(_threading_atexits): + atexit_call() + +- # Main thread +- if _main_thread.ident == get_ident(): +- tlock = _main_thread._tstate_lock +- # The main thread isn't finished yet, so its thread state lock can't +- # have been released. +- assert tlock is not None +- assert tlock.locked() +- tlock.release() +- _main_thread._stop() +- else: +- # bpo-1596321: _shutdown() must be called in the main thread. +- # If the threading module was not imported by the main thread, +- # _main_thread is the thread which imported the threading module. +- # In this case, ignore _main_thread, similar behavior than for threads +- # spawned by C libraries or using _thread.start_new_thread(). +- pass +- + # Join all non-deamon threads + while True: + with _shutdown_locks_lock: diff --git a/python3.9.spec b/python3.9.spec index 9abe196..4e2f83e 100644 --- a/python3.9.spec +++ b/python3.9.spec @@ -13,11 +13,11 @@ URL: https://www.python.org/ # WARNING When rebasing to a new Python version, # remember to update the python3-docs package as well -%global general_version %{pybasever}.5 +%global general_version %{pybasever}.13 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 3%{?dist} +Release: 1%{?dist} License: Python @@ -305,7 +305,7 @@ Patch1: 00001-rpath.patch # See https://bugzilla.redhat.com/show_bug.cgi?id=556092 Patch111: 00111-no-static-lib.patch -# 00189 # 4cb983ce1c5ef32068ca9349203a6f1ef9667451 +# 00189 # a79a85be3f0ad45792d998aed1104c2c2a0ef729 # Instead of bundled wheels, use our RPM packaged wheels # # We keep them in /usr/share/python-wheels @@ -317,10 +317,10 @@ Patch189: 00189-use-rpm-wheels.patch # The versions are written in Lib/ensurepip/__init__.py, this patch removes them. # When the bundled setuptools/pip wheel is updated, the patch no longer applies cleanly. # In such cases, the patch needs to be amended and the versions updated here: -%global pip_version 21.1.1 -%global setuptools_version 56.0.0 +%global pip_version 22.0.4 +%global setuptools_version 58.1.0 -# 00251 # 2eabd04356402d488060bc8fe316ad13fc8a3356 +# 00251 # 1b1047c14ff98eae6d355b4aac4df3e388813f62 # Change user install location # # Set values of prefix and exec_prefix in distutils install command @@ -328,7 +328,13 @@ Patch189: 00189-use-rpm-wheels.patch # is not detected to make pip and distutils install into separate location. # # Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe -# Downstream only: Awaiting resources to work on upstream PEP +# Downstream only: Reworked in Fedora 36+/Python 3.10+ to follow https://bugs.python.org/issue43976 +# +# pypa/distutils integration: https://github.com/pypa/distutils/pull/70 +# +# Also set sysconfig._PIP_USE_SYSCONFIG = False, to force pip-upgraded-pip +# to respect this patched distutils install command. +# See https://bugzilla.redhat.com/show_bug.cgi?id=2014513 Patch251: 00251-change-user-install-location.patch # 00328 # 367fdcb5a075f083aea83ac174999272a8faf75c @@ -370,6 +376,17 @@ Patch328: 00328-pyc-timestamp-invalidation-mode.patch # a nightmare because it's basically a binary file. Patch353: 00353-architecture-names-upstream-downstream.patch +# 00371 # 1fc313929648e9b543542de09f59c55e175ac45a +# Revert "bpo-1596321: Fix threading._shutdown() for the main thread (GH-28549) (GH-28589)" +# +# This reverts commit 94d19f606fa18a1c4d2faca1caf2f470a8ce6d46. It +# introduced regression causing FreeIPA's tests to fail. +# +# For more info see: +# https://bodhi.fedoraproject.org/updates/FEDORA-2021-e152ce5f31 +# https://github.com/GrahamDumpleton/mod_wsgi/issues/730 +Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -563,6 +580,7 @@ Requires: %{pkgname}-libs%{?_isa} = %{version}-%{release} # But we want them when packages BuildRequire python3-devel Requires: (python-rpm-macros if rpm-build) Requires: (python3-rpm-macros if rpm-build) +Requires: (pyproject-rpm-macros if rpm-build) %if %{without bootstrap} # This is not "API" (packages that need setuptools should still BuildRequire it) @@ -633,6 +651,10 @@ configuration, browsers, and other dialogs. Summary: A GUI toolkit for Python Requires: %{pkgname} = %{version}-%{release} +# The importable module "turtle" is here, so provide python3-turtle. +# (We don't provide python3-turtledemo, that's not too useful when imported.) +%py_provides %{pkgname}-turtle + %description -n %{pkgname}-tkinter The Tkinter (Tk interface) library is a graphical user interface toolkit for the Python programming language. @@ -726,10 +748,10 @@ Requires: tzdata %description Python %{pybasever} package for developers. -This package exists to allow developers to test their code against a newer +This package exists to allow developers to test their code against an older version of Python. This is not a full Python stack and if you wish to run -your applications with Python %{pybasever}, update your Fedora to a newer -version once Python %{pybasever} is stable. +your applications with Python %{pybasever}, see other distributions +that support it, such as CentOS or RHEL or older Fedora releases. %endif # with flatpackage @@ -1778,6 +1800,44 @@ CheckPython optimized # ====================================================== %changelog +* Wed May 18 2022 Tomáš Hrnčiar - 3.9.13-1 +- Update to 3.9.13 + +* Fri Mar 25 2022 Tomáš Hrnčiar - 3.9.12-1 +- Update to 3.9.12 + +* Thu Mar 17 2022 Tomáš Hrnčiar - 3.9.11-1 +- Update to 3.9.11 + +* Mon Jan 17 2022 Tomáš Hrnčiar - 3.9.10-1 +- Update to 3.9.10 + +* Sat Jan 08 2022 Miro Hrončok - 3.9.9-4 +- Instruct pip to use distutils +- Instruct pypa/distutils to add /local/ addition to prefix + +* Fri Nov 19 2021 Tomáš Hrnčiar - 3.9.9-2 +- Add patch to revert "bpo-1596321: Fix threading._shutdown() for the main thread" + +* Tue Nov 16 2021 Tomáš Hrnčiar - 3.9.9-1 +- Update to 3.9.9 + +* Mon Nov 08 2021 Tomáš Hrnčiar - 3.9.8-1 +- Update to 3.9.8 + +* Mon Aug 30 2021 Miro Hrončok - 3.9.7-1 +- Update to 3.9.7 + +* Fri Jul 23 2021 Fedora Release Engineering - 3.9.6-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Fri Jul 16 2021 Petr Viktorin - 3.9.6-2 +- Provide python3-turtle from python3-tkinter +- Require pyproject-rpm-macros from python3-devel + +* Tue Jun 29 2021 Tomas Hrnciar - 3.9.6-1 +- Update to 3.9.6 + * Tue Jun 01 2021 Python Maint - 3.9.5-3 - Rebuilt for Python 3.10 diff --git a/sources b/sources index 373c745..faf7226 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.9.5.tar.xz) = 7b581af1b3e73695f124ff6a3a960e22104153a5d920ca930c046f42ea313b1e7ec77e39876db2bf5d5da6d7412cb072ff8ae7814dda12c14f9da6c570fb0995 -SHA512 (Python-3.9.5.tar.xz.asc) = cfee8c161447544d6f18c3490b96ce12c8df5e9b143fca09734066f3606abe767cd7b4d05315c691f41c0e565d9585456774c10770ffe2ef7983a911bbcfdb83 +SHA512 (Python-3.9.13.tar.xz) = e9664e7f908092df11236b22465d217531d6f0378e88d889108d19fe77f28f46ffb629b8733f84b41409e255367321893a2b1bd64518930d9d8cae5d1b774d23 +SHA512 (Python-3.9.13.tar.xz.asc) = 9367afe8ec32195adbb64fa32eb9e3881cfdbbb38efcb5e0804a15486232aecbc81ed20a8ffed652f06a881744f3efd2c2df3cc652d70671c94d6668a17a391e