Bug 43521

Summary: Изменения в релизе 4.5.0-alt2 ломают сборку (тесты) zope.server
Product: Sisyphus Reporter: Николай Костригин <nickel>
Component: python3-module-zope.contenttypeAssignee: Grigory Ustinov <grenka>
Status: CLOSED FIXED QA Contact: qa-sisyphus
Severity: normal    
Priority: P5 CC: antohami, cas, cow, evg, george, grenka, imz, kotopesutility, lav, nbr, qa_viy, rider, sem, shaba, sin, slev, toni, vitty, viy
Version: unstable   
Hardware: x86_64   
OS: Linux   

Description Николай Костригин 2022-08-13 15:29:16 MSK
Test-module import failures:

Module: zope.server.http.tests.test_wsgiserver

Traceback (most recent call last):
  File "/usr/src/RPM/BUILD/python3-module-zope.server-4.0.2/.tox/py310/lib/python3/site-packages/zope/server/http/tests/test_wsgiserver.py", line 33, in <module>
    from zope.publisher.http import IHTTPRequest
  File "/usr/lib64/python3/site-packages/zope/publisher/http.py", line 39, in <module>
    import zope.contenttype.parse
ModuleNotFoundError: No module named 'zope.contenttype'


Откат к релизу alt1 делает возможность импорта соответствующего модуля возможной.
Насколько я помню, виной всему перевод пакетов из архитектурозависимых в noarch.
Comment 1 Николай Костригин 2022-08-13 15:43:21 MSK
Простите за тавтологию в предыдущем комментарии...

По существу: заодно упомянутые изменения ломают тесты с такими же симптомами в


1. python3-module-zope.i18n

[...]
----------------------------------------------------------------------
File "/usr/src/RPM/BUILD/python3-module-zope.i18n-4.8.0/src/zope/i18n/testing.py", line 48, in zope.i18n.testing.PlacelessSetup.setUp
Failed example:
    from zope.publisher.browser import TestRequest
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib64/python3.10/doctest.py", line 1350, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest zope.i18n.testing.PlacelessSetup.setUp[1]>", line 1, in <module>
        from zope.publisher.browser import TestRequest
      File "/usr/lib64/python3/site-packages/zope/publisher/browser.py", line 42, in <module>
        from zope.publisher.http import HTTPRequest
      File "/usr/lib64/python3/site-packages/zope/publisher/http.py", line 39, in <module>
        import zope.contenttype.parse
    ModuleNotFoundError: No module named 'zope.contenttype'
[...]

2. python3-module-zope.publisher

[...]
----------------------------------------------------------------------
File "/usr/src/RPM/BUILD/python3-module-zope.publisher-6.0.1/src/zope/publisher/tests/../paste.txt", line 49, in paste.txt
Failed example:
    app_factory = pkg_resources.load_entry_point(
        'zope.publisher', 'paste.app_factory', 'main')
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib64/python3.10/doctest.py", line 1350, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest paste.txt[2]>", line 1, in <module>
        app_factory = pkg_resources.load_entry_point(
      File "/usr/lib64/python3/site-packages/pkg_resources/__init__.py", line 486, in load_entry_point
        return get_distribution(dist).load_entry_point(group, name)
      File "/usr/lib64/python3/site-packages/pkg_resources/__init__.py", line 2867, in load_entry_point
        return ep.load()
      File "/usr/lib64/python3/site-packages/pkg_resources/__init__.py", line 2471, in load
        return self.resolve()
      File "/usr/lib64/python3/site-packages/pkg_resources/__init__.py", line 2477, in resolve
        module = __import__(self.module_name, fromlist=['__name__'], level=0)
      File "/usr/src/RPM/BUILD/python3-module-zope.publisher-6.0.1/src/zope/publisher/paste.py", line 16, in <module>
        import zope.publisher.browser
      File "/usr/src/RPM/BUILD/python3-module-zope.publisher-6.0.1/src/zope/publisher/browser.py", line 42, in <module>
        from zope.publisher.http import HTTPRequest
      File "/usr/src/RPM/BUILD/python3-module-zope.publisher-6.0.1/src/zope/publisher/http.py", line 39, in <module>
        import zope.contenttype.parse
    ModuleNotFoundError: No module named 'zope.contenttype'
[...]

Если нет веских причин сохранять внесенные изменения, я предлагаю автору их откатить.
Comment 2 Grigory Ustinov 2022-08-13 15:46:01 MSK
Да, я прекрасно помню, что я это сломал. Надо разобраться в том, что же там такого архитектурозависимого. Ну то есть у нас много пакетов, которые собираются таким образом ошибочно, некоторые исправились хорошо, а вот этот сломал 3 пакета, если я не ошибаюсь. Исправлю в ближайшее время, а потом подумаю.
Comment 3 Ivan Zakharyaschev 2022-08-14 13:53:15 MSK
(In reply to Николай Костригин from comment #0)

>   File "/usr/lib64/python3/site-packages/zope/publisher/http.py", line 39,
> in <module>
>     import zope.contenttype.parse
> ModuleNotFoundError: No module named 'zope.contenttype'
> 
> 
> Откат к релизу alt1 делает возможность импорта соответствующего модуля
> возможной.
> Насколько я помню, виной всему перевод пакетов из архитектурозависимых в
> noarch.

Насколько я помню, это объясняется как-то так, что zope оформлен как модуль со своим __init__.py :

/ALT/Sisyphus/x86_64/base/contents_index:/usr/lib64/python3/site-packages/zope/__init__.py      python3-module-zope

поэтому при import zope.contenttype.parse сначала ищется и находится этот модуль и загружается, и всё остальное может быть найдено только внутри него.

В современном питоне, начиная с 3.x что-то, есть возможность, если я правильно помню, оформлять более настоящие namespaces, без __init__.py -- тогда поиск вложенных в namespace модулей должен быть по всем подходящим путям.

Можно пофантазировать о каком-то автоматическом способе избежать таких ошибок с помощью зависимостей, но это как-то не так просто придумать, потому что в современном питоне есть два варианта, когда это будет работать, насолько я понимаю: требуется файл __init__.py на уровне выше /usr/lib/python3/site-packages/zope/__init__.py , или его отсутствие по всем путям, т.е. нет ни одного из: /usr/lib64/python3/site-packages/zope/__init__.py , /usr/lib/python3/site-packages/zope/__init__.py  и прочих.
Comment 4 Ivan Zakharyaschev 2022-08-14 14:06:36 MSK
(In reply to Ivan Zakharyaschev from comment #3)

> В современном питоне, начиная с 3.x что-то, есть возможность, если я
> правильно помню, оформлять более настоящие namespaces, без __init__.py --
> тогда поиск вложенных в namespace модулей должен быть по всем подходящим
> путям.
> 
> Можно пофантазировать о каком-то автоматическом способе избежать таких
> ошибок с помощью зависимостей, но это как-то не так просто придумать, потому
> что в современном питоне есть два варианта, когда это будет работать,
> насолько я понимаю: требуется файл __init__.py на уровне выше
> /usr/lib/python3/site-packages/zope/__init__.py , или его отсутствие по всем
> путям, т.е. нет ни одного из:
> /usr/lib64/python3/site-packages/zope/__init__.py ,
> /usr/lib/python3/site-packages/zope/__init__.py  и прочих.

Но у нас, мне кажется, поиск Provides как раз считает, что есть только первый вариант, поэтому при отсутствии __init__.py на уровень выше, будет отсутствовать Provides у пакета. И это могло бы быть способом обнаружения ошибки.

В случае с zope.contenttype почему-то, правда, я такого эффекта не наблюдаю. Они на месте:

$ rpm -qp /ALT/Sisyphus/noarch/RPMS.classic/python3-module-zope.contenttype-4.5.0-alt2.noarch.rpm --provides
python3(zope.contenttype)
python3(zope.contenttype.__main__)
python3(zope.contenttype.parse)
python3-module-zope.contenttype = 4.5.0-alt2:sisyphus+301404.100.1.1

Во время сборки была попытка создать такой файл, но вроде пропущена:

Skipping installation of /usr/src/tmp/python3-module-zope.contenttype-buildroot/usr/lib/python3/site-packages/zope/__init__.py (namespace package)
Comment 5 Ivan Zakharyaschev 2022-08-14 14:11:25 MSK
(In reply to Ivan Zakharyaschev from comment #3)

> Можно пофантазировать о каком-то автоматическом способе избежать таких
> ошибок с помощью зависимостей, но это как-то не так просто придумать, потому
> что в современном питоне есть два варианта, когда это будет работать,
> насолько я понимаю: требуется файл __init__.py на уровне выше
> /usr/lib/python3/site-packages/zope/__init__.py

и его отсутствие по всем остальным таким путям.

> , или его отсутствие по всем
> путям, т.е. нет ни одного из:
> /usr/lib64/python3/site-packages/zope/__init__.py ,
> /usr/lib/python3/site-packages/zope/__init__.py  и прочих.
Comment 6 Repository Robot 2022-08-17 15:12:35 MSK
python3-module-zope.contenttype-4.5.0-alt3 -> sisyphus:

 Wed Aug 17 2022 Grigory Ustinov <grenka@altlinux> 4.5.0-alt3
 - Make package arch dependent back (Closes: #43521).
Comment 7 Stanislav Levin 2022-08-17 16:10:32 MSK
TL;DR
Все файловые руты namespaсe package должны быть в python3-module-zope
(и архитектурно-зависимые, и архитектурно-независимые).

Сама идея namespace пакета подразумевает возможность разнесения частей одного пакета по различным файловым иерархиям (хотя это может быть и не только файловая система) с возможными *вложенными* namespace пакетами:
https://docs.python.org/3/reference/import.html#namespace-packages

zope - это namespace package, у которого есть как архитектурно-зависимые подпакеты, так и архитектурно-независимые. Upstream zope выбрал pkg_resources-style для namespace packages:
https://packaging.python.org/en/latest/guides/packaging-namespace-packages/#creating-a-namespace-package

Это буквально означает, что *каждый* возможный устанавливаемый рут для подпакета *должен* иметь __init__.py cо следующим содержимым:
> __import__('pkg_resources').declare_namespace(__name__)

В текущей схеме пакетирования zope в ALT distro количество возможных рутов ограничено одним. Реализовано это как принудительное заархитектуривание *всех* подпакетов zope, то есть все такие подпакеты *должны* устанавливаться в /usr/lib64/python3/site-packages/zope/ (для x86_64). В противном случае, импорт таких подпакетов может не работать (зависит от многих факторов).

Например, разберем проблему сборки данного пакета (zope.server).

На файловой системе лежат:
[user@host dir]# ls -1 /usr/lib{,64}/python3/site-packages/zope/
/usr/lib/python3/site-packages/zope/:
__pycache__
contenttype

/usr/lib64/python3/site-packages/zope/:
__init__.py
__pycache__
browser
component
configuration
deferredimport
deprecation
event
exceptions
hookable
i18n
i18nmessageid
interface
location
proxy
publisher
schema
security
testing
testrunner

Что происходит при импорте zope:
[root@localhost .in]# echo -e "import sys\nfor f in sys.meta_path:\n    spec = f.find_spec('zope', path=None)\n    if spec is not None:\n        print(f, spec.submodule_search_locations)\n        break" | python3 -
<class '_frozen_importlib_external.PathFinder'> ['/usr/lib64/python3/site-packages/zope']

то есть path-based finder (https://docs.python.org/3/reference/import.html#the-path-based-finder) находит нам спек для zope.

При этом и path entry finder для архитектурно-зависимых sitepackages, и path entry finder для архитектурно-независимых sitepackages могут найти спек для zope:
[root@localhost .in]# python3 -c 'import sys, zope; print(sys.path_importer_cache["/usr/lib/python3/site-packages"].find_spec("zope")); print(sys.path_importer_cache["/usr/lib64/python3/site-packages"].find_spec("zope"))'
ModuleSpec(name='zope', loader=None, submodule_search_locations=['/usr/lib/python3/site-packages/zope'])
ModuleSpec(name='zope', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7f70b478ab60>, origin='/usr/lib64/python3/site-packages/zope/__init__.py', submodule_search_locations=['/usr/lib64/python3/site-packages/zope'])

Но так как согласно https://peps.python.org/pep-0420/#specification
> During import processing, the import machinery will continue to iterate over each directory in the parent path as it does in Python 3.2. While looking for a module or package named “foo”, for each directory in the parent path:
> If <directory>/foo/__init__.py is found, a regular package is imported and returned.
> If not, but <directory>/foo.{py,pyc,so,pyd} is found, a module is imported and returned. The exact list of extension varies by platform and whether the -O flag is specified. The list here is representative.
> If not, but <directory>/foo is found and is a directory, it is recorded and the scan continues with the next directory in the parent path.
> Otherwise the scan continues with the next directory in the parent path.
> If the scan completes without returning a module or package, and at least one directory was recorded, then a namespace package is created.

(детали тут: importlib/_bootstrap_external.py::FileFinder::find_spec)
native namespace package ("/usr/lib/python3/site-packages/zope") и все его содержимое игнорируется.

С точки зрения Python zope - это регулярный пакет (наличие zope/__init__.py), но инициализация этого пакета создает имитацию namespace пакета с помощью pkg_resources. pkg_resources.declare_namespace из /usr/lib64/python3/site-packages/zope/__init__.py, о котором говорилось ранее, патчит __path__ пакета zope:
https://github.com/pypa/setuptools/blob/d03da04e024ad4289342077eef6de40013630a44/pkg_resources/__init__.py#L2289

[user@host dir]# python3 -c "import zope, sys; print(sys.modules['zope'].__path__)"
['/usr/lib64/python3/site-packages/zope']

Поэтому, если эта *обязательная* инициализация отсутствует во всех возможных рутах namespace пакета zope, то импорты могу не работать. Какие варианты пакетирования:
- упаковать два рута, перевести все на native namespacе, удалив zope.__init__.py, и следить, чтобы никто и никогда не упаковал zope/__init__.py.
- упаковать два рута, использовать схему upstream c pkg_resources, сделав зависимость обязательной