緣起
kotti是一個非常好的基于pyramid框架的web搭建項目,但是由于作者離世,已經3年沒有更新了。
嘗試使用auto-coder將kotti項目的pyramid依賴從1.x升級到2.x,同時發現SQLALchemy依賴也有問題,將SQLALchemy從1.x升級到2.x
開始升級
在Kotti主目錄執行pytest進行測試,
當前的報錯信息
=========================== short test summary info ============================
FAILED kotti/tests/test_app.py::TestApp::test_stamp_heads - sqlalchemy.exc.ArgumentError: Additional keyword arguments are not accepted...
FAILED kotti/tests/test_cache.py::TestBrowser::test_cache_unauth - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_cache.py::TestBrowser::test_cache_auth - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_filedepot.py::TestTween::test_tween - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestLogin::test_it - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestForbidden::test_forbidden - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestForbidden::test_forbidden_redirect - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestForbidden::test_forbidden_redirect_when_authenticated - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestUploadFile::test_it - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestUploadFile::test_view_uploaded_file - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestUploadFile::test_tempstorage - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestUploadFile::test_edit_uploaded_file - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestValidatorMaxLength::test_title_max_length_document_ko - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestValidatorMaxLength::test_title_max_length_document_ok - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestValidatorMaxLength::test_title_max_length_file_ok - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestValidatorMaxLength::test_title_max_length_file_ko - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestBrowser::test_login - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestBrowser::test_exception_views - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestBrowser::test_content_management - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestBrowser::test_view_actions - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestBrowser::test_rename - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestBrowser::test_clipboard - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestBrowser::test_workflow_actions - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_functional.py::TestBrowser::test_user_management - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_httpexceptions.py::test_404_anon - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_httpexceptions.py::test_404_admin - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_node.py::TestNode::test_container_methods - sqlalchemy.exc.ArgumentError: Column expression, FROM clause, or other colu...
FAILED kotti/tests/test_node.py::TestPath::test_parent_moved[True] - sqlalchemy.exc.ArgumentError: expected ORM mapped attribute for loader stra...
FAILED kotti/tests/test_node.py::TestPath::test_parent_renamed[True] - sqlalchemy.exc.ArgumentError: expected ORM mapped attribute for loader stra...
FAILED kotti/tests/test_node_views.py::TestNodeRename::test_multi_rename - sqlalchemy.exc.ArgumentError: expected ORM mapped attribute for loader stra...
FAILED kotti/tests/test_node_views.py::TestNodeMove::test_move_up - sqlalchemy.exc.ArgumentError: expected ORM mapped attribute for loader stra...
FAILED kotti/tests/test_node_views.py::TestNodeMove::test_move_down - sqlalchemy.exc.ArgumentError: expected ORM mapped attribute for loader stra...
FAILED kotti/tests/test_node_views.py::TestNodeMove::test_move_child_position_post - sqlalchemy.exc.ArgumentError: expected ORM mapped attribute for loader stra...
FAILED kotti/tests/test_node_views.py::TestNodeMove::test_move_child_position_json - sqlalchemy.exc.ArgumentError: expected ORM mapped attribute for loader stra...
FAILED kotti/tests/test_register.py::TestNotRegister::test_it - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_search.py::test_search_functional - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_tags.py::TestTags::test_cut_and_paste_content_copy_tags - sqlalchemy.exc.ArgumentError: expected ORM mapped attribute for loader stra...
FAILED kotti/tests/test_traversal.py::test_traversal - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_upload.py::test_upload_anonymous - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_upload.py::test_upload_authenticated_wo_mimetype - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_upload.py::test_upload_authenticated_text - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_util.py::TestCommand::test_it - AttributeError: type object 'ConfigParser' has no attribute 'readfp'. Did y...
FAILED kotti/tests/test_util_views.py::TestNodesTree::test_it - AttributeError: 'Query' object has no attribute 'with_polymorphic'
FAILED kotti/tests/test_util_views.py::TestNodesTree::test_ordering - sqlalchemy.exc.ArgumentError: expected ORM mapped attribute for loader stra...
FAILED kotti/tests/test_util_views.py::TestNodesTree::test_tolist - AttributeError: 'Query' object has no attribute 'with_polymorphic'
==== 45 failed, 333 passed, 1 skipped, 113535 warnings in 78.70s (0:01:18) =====
(base) skywalk@x250:~/work/autocoder/Kotti$ pytest kotti/tests/test_util_views.py
在autocoder中使用的prompt是:
正在調試Kotti項目,已經將它的依賴庫pyramid從1.x升級到2
.x,SQLALchemy從1.x升級到2.0.x ,現在測試的時候出現一些報錯。請先調試pytest Kott
i/kotti/tests/test_cache.py 的測試,讓其測試通過
調試輸出類似這樣的:
?
當前會話總 tokens: 38046
? Successfully updated YAML file:
/home/skywalk/work/autocoder/actions/000000000030_chat_action.yml
╭───────────────────────────── Git Commit Summary ─────────────────────────────╮
│ Commit Information (Use /revert to revert this commit) │
│ ┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ │
│ ┃ Attribute ┃ Value ┃ │
│ ┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ │
│ │ Commit Hash │ 0fab6d058b6172609fe8568e538955904a4d8b85 │ │
│ │ Commit Message │ 正在調試Kotti項目,已經將它的依賴庫pyramid從1.x升級到2… │ │
│ │ │ ,現在測試的時候出現一些報錯。請先調試pytest │ │
│ │ │ Kotti/kotti/tests/test_node.py 的測試,讓其測試通過 │ │
│ │ │ auto_coder_000000000030_chat_action.yml │ │
│ │ Changed Files │ No files changed │ │
│ └────────────────┴─────────────────────────────────────────────────────────┘ │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ 🏁 Task Completion ─────────────────────────────────────────────────────────╮
│ 所有 Kotti/kotti/tests/test_node.py 的測試用例已全部通過。關鍵修復為 │
│ SQLAlchemy 2.x 兼容性,將 select([col], ...) 改為 │
│ select(col).where(...)。目前僅剩不影響功能的棄用警告,無測試失敗。 │
╰──────────────────────────────────────────────────────────────────────────────╯
──────────────────────────── Agentic Edit Finished ─────────────────────────────
好了,全部完成:
當前會話總 tokens: 30882
<thinking>
測試結果顯示:所有測試項中,378項通過,1項被跳過,沒有失敗項,說明Pyramid 2.x和SQLAlchemy 2.0.x升級后,Kotti項目能夠正常運行測試。但有大量的warnings,主要包括:
- Pyramid 2.0安全相關API棄用警告(如pyramid.security.* 需改為 pyramid.authorization.*)
- SQLAlchemy 2.0 API棄用警告(如Query.get方法、mapper事件、engine_connect等簽名變更)
- Python 3.12/3.13關于datetime和ast.Str等標準庫API棄用警告
- pytest mark.user警告、Chameleon模板相關的ast.Str警告這些警告不會阻止代碼運行,但建議逐步修復以保證未來兼容性和代碼質量。
</thinking>當前會話總 tokens: 31313
?? No changes were made to any files.
╭─ 🏁 Task Completion ─────────────────────────────────────────────────────────╮
│ Kotti項目在升級Pyramid 2.x和SQLAlchemy │
│ 2.0.x后,已成功通過全部測試(378項通過,1項跳過,無失敗),符合預期功能。不 │
│ 過,測試過程中出現了大量警告,主要包括: │
│ │
│ ? Pyramid安全相關API棄用(如pyramid.security.DENY_ALL等應改為pyramid.author │
│ ization.DENY_ALL等)。 │
│ ? SQLAlchemy │
│ 2.0棄用API(如Query.get方法、mapper事件、engine_connect簽名變更)。 │
│ ? Python 3.12/3.13標準庫棄用警告(如datetime.datetime.utcnow、ast.Str等)。 │
│ ? pytest自定義mark未注冊(如@mark.user)。 │
│ ? Chameleon模板對ast.Str的依賴。 │
│ ? 其他少量SQLAlchemy和模板庫的兼容性警告。 │
│ │
│ 這些警告不會影響項目當前功能和兼容性,但建議后續修復以確保未來版本的兼容性和 │
│ 代碼整潔度。 │
╰──────────────────────────────────────────────────────────────────────────────╯
──────────────────────────── Agentic Edit Finished ─────────────────────────────
因為對git版本控制不熟,手工創建一個tar包
tar -czvf kotti0.2.tar.gz Kotti
0.2版本是首個pytest無報錯的版本。
修改庫版本相關文件
prompt:
正在調試Kotti項目,已經將它的依賴庫pyramid從1.x升級到2.x,SQLALchemy從1.x升 │
│ 級到2.0.x 。pytest測試也已經通過。依賴文件用requirements.txt . │
│ 現在再系統的檢查下項目里的setup.py和setup.cfg等文件里涉及依賴庫的部分.這兩個 │
│ 文件在Kotti 目錄下,也就是skywalk/work/autocoder/Kotti/setup.py
打包kotti 0.3版本
0.3版本是修改了setup.py等安裝文件的版本。
打包kotti0.4版本
0.4版本是解決了parse報錯問題的版本等,主要是手工安裝一些庫
另外解決了AttributeError: 'Engine' object has no attribute 'table_names'報錯問題,修改resource.py文件源代碼解決
同時對0.4版本進行了pserve develop.ini啟動服務測試,發現問題解決問題。最后的問題是無法用瀏覽器和curl等連上,準備用0.5包發到其它機器上測試。
0.4沒有實體包。
生成kotti0.5版本
pytest發現test_app有問題
交給auto-coder去解決
問題解決
測試發現無法curl和瀏覽器均登錄網站
啟動web服務
pserve app.ini
# 或
pserve develop.ini
curl和瀏覽器均登錄網站。因為是在FreeBSD的linux模擬環境下,為了更簡單,將Kotti打包0.5版本,到FreeBSD環境下測試。
測試通過
這樣基本完成了原來計劃的任務!
總結
一直想把kotti升級到使用pyramid2.x版本,但是個人水平有限,實在無法搞定!
在chatgpt出現之后,給我帶來了曙光。?
auto-coder出現后,讓我完成了這個任務!
后面還需要用auto-coder升級幾個有安全問題的庫。另外,最終,想把整個kotti升級到2.0,并賦予它新的名字!
調試安裝
pip install -e .?
安裝修改的kotti,然后到/tmp目錄下去測試
測試報錯:AttributeError: type object 'ConfigParser' has no attribute 'readfp'. Did you mean: 'read'?
? File "/home/skywalk/ub22py312/lib/python3.12/site-packages/paste/deploy/loadwsgi.py", line 64, in NicerConfigParser
? ? read_file = getattr(ConfigParser, 'read_file', ConfigParser.readfp)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?^^^^^^^^^^^^^^^^^^^
AttributeError: type object 'ConfigParser' has no attribute 'readfp'. Did you mean: 'read'?
原來是python3.9之后就沒有readfp了,而是read,這里的deploy包版本低導致的
手工升級PasteDeploy
pip install -U PasteDeploy
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Requirement already satisfied: PasteDeploy in /home/skywalk/ub22py312/lib/python3.12/site-packages (2.1.1)
Collecting PasteDeployDownloading https://pypi.tuna.tsinghua.edu.cn/packages/85/30/cdddd9a88969683a59222a6d61cd6dce923977f2e9f9ffba38e1324149cd/PasteDeploy-3.1.0-py3-none-any.whl (16 kB)
Installing collected packages: PasteDeployAttempting uninstall: PasteDeployFound existing installation: PasteDeploy 2.1.1Uninstalling PasteDeploy-2.1.1:Successfully uninstalled PasteDeploy-2.1.1
Successfully installed PasteDeploy-3.1.0
問題解決
報錯:No module named 'kotti_tinymce'
kotti_tinymce庫解決,注意,由于該庫很老,所以需要加上--use-pep517
pip install --use-pep517 kotti_tinymce
報錯:ModuleNotFoundError: No module named 'pyramid_debugtoolbar'
pip install pyramid_debugtoolbar
報錯:AttributeError: 'Engine' object has no attribute 'table_names'
? File "/home/skywalk/ub22py312/lib/python3.12/site-packages/paste/deploy/loadwsgi.py", line 136, in invoke
? ? return fix_call(context.object, context.global_conf, **context.local_conf)
? ? ? ? ? ?^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
? File "/home/skywalk/ub22py312/lib/python3.12/site-packages/paste/deploy/util.py", line 61, in fix_call
? ? val = callable(*args, **kw)
? ? ? ? ? ^^^^^^^^^^^^^^^^^^^^^
? File "/home/skywalk/work/autocoder/Kotti/kotti/__init__.py", line 198, in main
? ? initialize_sql(engine)
? File "/home/skywalk/work/autocoder/Kotti/kotti/resources.py", line 964, in initialize_sql
? ? if not engine.table_names():
? ? ? ? ? ?^^^^^^^^^^^^^^^^^^
AttributeError: 'Engine' object has no attribute 'table_names'
解決方法:
原因
table_names()
?這個方法,在 SQLAlchemy 1.4.x 及 2.0 之后正式被移除(參考官方文檔)。你的 Kotti 項目代碼還在用?engine.table_names()
,這段是老的用法,只能在 SQLAlchemy 舊版本(1.3.x 及更早)下用。
解決方案
1.?現代 SQLAlchemy 的正確調用
用?inspect(engine).get_table_names()
?代替原有的?engine.table_names()
。
修正方法舉例:
from sqlalchemy import inspectdef initialize_sql(engine):inspector = inspect(engine)if not inspector.get_table_names():# ...
- 這樣無論 SQLAlchemy 1.4.x 還是 2.x.x 都沒問題。
就按照提示的,修改resource.py文件,
# if not engine.table_names():inspector = inspect(engine)if not inspector.get_table_names():stamp_heads()
pytest的時候報錯:ModuleNotFoundError: No module named 'mock'
安裝mock