wexample-wex-addon-dev-python 15.8.1__tar.gz → 15.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/PKG-INFO +3 -3
  2. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/README.md +1 -1
  3. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/pyproject.toml +2 -2
  4. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/workdir/python_package_workdir.py +70 -3
  5. wexample_wex_addon_dev_python-15.10.0/tests/unit/test_pypi_background_upload.py +66 -0
  6. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/__init__.py +0 -0
  7. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/__init__.py +0 -0
  8. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/code/__init__.py +0 -0
  9. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/code/check/__init__.py +0 -0
  10. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/code/check/mypy.py +0 -0
  11. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/code/check/pylint.py +0 -0
  12. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/code/check/pyright.py +0 -0
  13. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/code/check.py +0 -0
  14. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/code/format/__init__.py +0 -0
  15. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/code/format/black.py +0 -0
  16. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/code/format/isort.py +0 -0
  17. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/code/format.py +0 -0
  18. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/code/rename.py +0 -0
  19. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/examples/__init__.py +0 -0
  20. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/examples/utils/__init__.py +0 -0
  21. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/examples/utils/some_example_type.py +0 -0
  22. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/examples/validate.py +0 -0
  23. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/commands/release/__init__.py +0 -0
  24. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/common/__init__.py +0 -0
  25. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/common/pypi_registry_gateway.py +0 -0
  26. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/config_value/__init__.py +0 -0
  27. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/config_value/python_package_readme_config_value.py +0 -0
  28. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/const/__init__.py +0 -0
  29. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/const/package.py +0 -0
  30. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/const/python.py +0 -0
  31. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/const/tags.py +0 -0
  32. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/file/__init__.py +0 -0
  33. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/file/python_app_iml_file.py +0 -0
  34. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/file/python_pyproject_toml_file.py +0 -0
  35. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/formatter/__init__.py +0 -0
  36. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/formatter/python_code_formatter.py +0 -0
  37. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/helper/__init__.py +0 -0
  38. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/helper/pdm.py +0 -0
  39. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/middleware/__init__.py +0 -0
  40. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/middleware/each_python_file_middleware.py +0 -0
  41. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/py.typed +0 -0
  42. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/python_addon_manager.py +0 -0
  43. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/__init__.py +0 -0
  44. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/abstract_python_path_rename_handler.py +0 -0
  45. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/abstract_python_rename_handler.py +0 -0
  46. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/abstract_python_symbol_rename_handler.py +0 -0
  47. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/applier.py +0 -0
  48. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/python_class_rename_handler.py +0 -0
  49. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/python_common.py +0 -0
  50. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/python_constant_rename_handler.py +0 -0
  51. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/python_function_rename_handler.py +0 -0
  52. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/python_module_rename_handler.py +0 -0
  53. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/python_package_rename_handler.py +0 -0
  54. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/rename_plan.py +0 -0
  55. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/refactor/symbol_kind.py +0 -0
  56. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/resources/.wex.yml +0 -0
  57. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/resources/ai/agents/describer/agent.yml +0 -0
  58. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/resources/ai/agents/describer/context.j2 +0 -0
  59. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/resources/design_rules/python_code.md +0 -0
  60. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/resources/docker/Dockerfile.python-profiling +0 -0
  61. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/resources/package_gitlab_ci.yml +0 -0
  62. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/resources/package_publish_gitlab.yml +0 -0
  63. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/resources/readme_templates/install.md.j2 +0 -0
  64. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/resources/readme_templates/tests.md.j2 +0 -0
  65. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/resources/writing_rules/python_code.md +0 -0
  66. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/selection/__init__.py +0 -0
  67. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/selection/abstract_python_code_selection.py +0 -0
  68. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/selection/package_description_missing_selection.py +0 -0
  69. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/selection/python_code_performance_selection.py +0 -0
  70. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/__init__.py +0 -0
  71. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/__init__.py +0 -0
  72. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/app_service.py +0 -0
  73. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/commands/__init__.py +0 -0
  74. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/commands/service/__init__.py +0 -0
  75. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/commands/service/install.py +0 -0
  76. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/commands/service/setup.py +0 -0
  77. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/docker/.wex.yml +0 -0
  78. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/docker/docker-compose.yml +0 -0
  79. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/samples/__init__.py +0 -0
  80. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/samples/docker/__init__.py +0 -0
  81. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/samples/docker/docker-compose.yml +0 -0
  82. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/samples/docker/entrypoint.sh +0 -0
  83. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/samples/docker/images/Dockerfile.base +0 -0
  84. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/samples/docker/images/Dockerfile.develop +0 -0
  85. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/samples/docker/images/__init__.py +0 -0
  86. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/services/python/service.yml +0 -0
  87. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/workdir/__init__.py +0 -0
  88. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/workdir/mixin/__init__.py +0 -0
  89. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/workdir/mixin/with_profiling_python_workdir_mixin.py +0 -0
  90. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/workdir/python_packages_suite_workdir.py +0 -0
  91. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/src/wexample_wex_addon_dev_python/workdir/python_workdir.py +0 -0
  92. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/.gitkeep +0 -0
  93. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/helper/__init__.py +0 -0
  94. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/helper/test_pdm.py +0 -0
  95. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/integration/__init__.py +0 -0
  96. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/integration/refactor/__init__.py +0 -0
  97. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/integration/refactor/test_python_class_rename_handler.py +0 -0
  98. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/integration/refactor/test_python_constant_rename_handler.py +0 -0
  99. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/integration/refactor/test_python_function_rename_handler.py +0 -0
  100. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/integration/refactor/test_python_module_rename_handler.py +0 -0
  101. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/integration/refactor/test_python_package_rename_handler.py +0 -0
  102. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/__init__.py +0 -0
  103. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/helper/__init__.py +0 -0
  104. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/helper/test_pdm.py +0 -0
  105. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/__init__.py +0 -0
  106. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_abstract_python_path_rename_handler.py +0 -0
  107. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_abstract_python_rename_handler.py +0 -0
  108. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_abstract_python_symbol_rename_handler.py +0 -0
  109. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_applier.py +0 -0
  110. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_python_class_rename_handler.py +0 -0
  111. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_python_common.py +0 -0
  112. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_python_constant_rename_handler.py +0 -0
  113. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_python_function_rename_handler.py +0 -0
  114. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_python_module_rename_handler.py +0 -0
  115. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_python_package_rename_handler.py +0 -0
  116. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_rename_plan.py +0 -0
  117. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/refactor/test_symbol_kind.py +0 -0
  118. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/resources/__init__.py +0 -0
  119. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/resources/test_shipped_agents.py +0 -0
  120. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/workdir/__init__.py +0 -0
  121. {wexample_wex_addon_dev_python-15.8.1 → wexample_wex_addon_dev_python-15.10.0}/tests/unit/workdir/test_python_package_workdir.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wexample-wex-addon-dev-python
3
- Version: 15.8.1
3
+ Version: 15.10.0
4
4
  Summary: Extends wex with commands to format, lint, and rename Python symbols, plus workdir types for Python packages and suites
5
5
  Author-Email: weeger <contact@wexample.com>
6
6
  License: MIT
@@ -20,7 +20,7 @@ Requires-Dist: pyright
20
20
  Requires-Dist: wexample-api>=6.8.0
21
21
  Requires-Dist: wexample-filestate-python>=9.4.0
22
22
  Requires-Dist: wexample-wex-addon-ai>=19.1.0
23
- Requires-Dist: wexample-wex-addon-app>=35.2.0
23
+ Requires-Dist: wexample-wex-addon-app>=35.4.0
24
24
  Provides-Extra: dev
25
25
  Requires-Dist: pytest; extra == "dev"
26
26
  Requires-Dist: pytest-cov; extra == "dev"
@@ -28,7 +28,7 @@ Description-Content-Type: text/markdown
28
28
 
29
29
  # wex_addon_dev_python
30
30
 
31
- Version: 15.8.1
31
+ Version: 15.10.0
32
32
 
33
33
  Extends wex with commands to format, lint, and rename Python symbols, plus workdir types for Python packages and suites
34
34
 
@@ -1,6 +1,6 @@
1
1
  # wex_addon_dev_python
2
2
 
3
- Version: 15.8.1
3
+ Version: 15.10.0
4
4
 
5
5
  Extends wex with commands to format, lint, and rename Python symbols, plus workdir types for Python packages and suites
6
6
 
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
6
6
 
7
7
  [project]
8
8
  name = "wexample-wex-addon-dev-python"
9
- version = "15.8.1"
9
+ version = "15.10.0"
10
10
  description = "Extends wex with commands to format, lint, and rename Python symbols, plus workdir types for Python packages and suites"
11
11
  authors = [
12
12
  { name = "weeger", email = "contact@wexample.com" },
@@ -29,7 +29,7 @@ dependencies = [
29
29
  "wexample-api>=6.8.0",
30
30
  "wexample-filestate-python>=9.4.0",
31
31
  "wexample-wex-addon-ai>=19.1.0",
32
- "wexample-wex-addon-app>=35.2.0",
32
+ "wexample-wex-addon-app>=35.4.0",
33
33
  ]
34
34
 
35
35
  [project.readme]
@@ -58,6 +58,44 @@ class PythonPackageWorkdir(PythonWorkdir):
58
58
  "Install: pipx install pdm — then run: wex core::env/configure"
59
59
  )
60
60
 
61
+ def collect_pypi_upload(self) -> str | None:
62
+ """Wait for this package's background PyPI upload, if one is running,
63
+ and say what went wrong when it failed (None when it did not)."""
64
+ import os
65
+ import shutil
66
+ import time
67
+
68
+ upload_dir = self._get_pypi_upload_dir()
69
+ pid_file = upload_dir / "pid"
70
+ if not pid_file.is_file():
71
+ return None
72
+ pid = int(pid_file.read_text().strip() or 0)
73
+ rc_file = upload_dir / "rc"
74
+ # Done once the shell wrote the exit code, its last act. Its /proc entry
75
+ # outlives it as a zombie until this process reaps it, so it is no sign
76
+ # of life — waiting on it alone hung the release forever.
77
+ while pid and not rc_file.is_file() and os.path.exists(f"/proc/{pid}"):
78
+ try:
79
+ if os.waitpid(pid, os.WNOHANG)[0]:
80
+ break
81
+ except ChildProcessError:
82
+ # Not this process's child (started by an earlier one): only
83
+ # its /proc entry tells.
84
+ pass
85
+ time.sleep(1)
86
+ try:
87
+ os.waitpid(pid, os.WNOHANG)
88
+ except ChildProcessError:
89
+ pass
90
+ rc = rc_file.read_text().strip() if rc_file.is_file() else "unknown"
91
+ log_file = upload_dir / "log"
92
+ report = None
93
+ if rc != "0":
94
+ tail = log_file.read_text(errors="replace").splitlines()[-5:] if log_file.is_file() else []
95
+ report = f"{self.get_package_name()}: PyPI upload failed (exit {rc}): " + " / ".join(tail)
96
+ shutil.rmtree(upload_dir, ignore_errors=True)
97
+ return report
98
+
61
99
  def get_gitlab_registry_url(self) -> str | None:
62
100
  """The GitLab PyPI registry of the group, when the suite publishes there.
63
101
 
@@ -339,6 +377,9 @@ class PythonPackageWorkdir(PythonWorkdir):
339
377
  def _get_critical_directories(self) -> list[str]:
340
378
  return ["src"]
341
379
 
380
+ def _get_pypi_upload_dir(self):
381
+ return self.get_path() / ".wex" / "tmp" / "pypi-upload"
382
+
342
383
  def _get_readme_content(self) -> ReadmeContentConfigValue | None:
343
384
  from wexample_wex_addon_dev_python.config_value.python_package_readme_config_value import (
344
385
  PythonPackageReadmeContentConfigValue,
@@ -494,7 +535,10 @@ class PythonPackageWorkdir(PythonWorkdir):
494
535
  if self.get_gitlab_registry_url():
495
536
  self._publish_to_gitlab_registry(force=force)
496
537
  try:
497
- self._publish_to_pypi(force=force)
538
+ # In the background: PyPI only mirrors the GitLab registry,
539
+ # nothing waits for it. The suite's publication collects
540
+ # the uploads at its end (`collect_pypi_upload`).
541
+ self._publish_to_pypi(force=force, background=True)
498
542
  except ShellCommandFailedException as exception:
499
543
  self.warning(
500
544
  f"PyPI refused {package_name} {version}, published on the GitLab "
@@ -560,7 +604,7 @@ class PythonPackageWorkdir(PythonWorkdir):
560
604
  cwd=self.get_path(),
561
605
  )
562
606
 
563
- def _publish_to_pypi(self, force: bool = False) -> None:
607
+ def _publish_to_pypi(self, force: bool = False, background: bool = False) -> None:
564
608
  from wexample_filestate_python.common.pipy_gateway import PipyGateway
565
609
  from wexample_helpers.helper.shell import shell_run
566
610
 
@@ -596,7 +640,30 @@ class PythonPackageWorkdir(PythonWorkdir):
596
640
  if password:
597
641
  publish_cmd += ["--password", password]
598
642
 
599
- shell_run(publish_cmd, inherit_stdio=True, cwd=self.get_path())
643
+ if not background:
644
+ shell_run(publish_cmd, inherit_stdio=True, cwd=self.get_path())
645
+ return
646
+
647
+ import shlex
648
+ import shutil
649
+ import subprocess
650
+
651
+ # From a copy of the distributions: dist/ is cleaned as soon as this
652
+ # returns. The exit code is written where collect_pypi_upload reads it.
653
+ upload_dir = self._get_pypi_upload_dir()
654
+ shutil.rmtree(upload_dir, ignore_errors=True)
655
+ shutil.copytree(self.get_path() / "dist", upload_dir / "dist")
656
+ command = " ".join(shlex.quote(part) for part in [*publish_cmd, "--dest", str(upload_dir / "dist")])
657
+ with open(upload_dir / "log", "w") as log:
658
+ process = subprocess.Popen(
659
+ ["sh", "-c", f'( {command} ); echo $? > "{upload_dir / "rc"}"'],
660
+ cwd=self.get_path(),
661
+ stdout=log,
662
+ stderr=subprocess.STDOUT,
663
+ start_new_session=True,
664
+ )
665
+ (upload_dir / "pid").write_text(str(process.pid))
666
+ self.log(f"PyPI upload of {package_name} {version} running in the background.")
600
667
 
601
668
  def _wait_for_registry(self) -> None:
602
669
  from wexample_helpers.helper.polling_callback_manager import (
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+ import threading
5
+ import time
6
+ from types import SimpleNamespace
7
+
8
+ from wexample_wex_addon_dev_python.workdir.python_package_workdir import (
9
+ PythonPackageWorkdir,
10
+ )
11
+
12
+
13
+ def _package(tmp_path):
14
+ """Just what collect_pypi_upload reads off a package."""
15
+ package = SimpleNamespace(get_path=lambda: tmp_path, get_package_name=lambda: "demo")
16
+ package._get_pypi_upload_dir = lambda: PythonPackageWorkdir._get_pypi_upload_dir(package)
17
+ return package
18
+
19
+
20
+ def _upload(tmp_path, script: str, reap: bool = True) -> None:
21
+ """A background upload as _publish_to_pypi leaves it: its pid, its log,
22
+ its exit code written when it ends. reap=False drops it still running, as
23
+ _publish_to_pypi does: Python then reaps it only at a later Popen, and
24
+ after the last package's there is none — it stays a zombie."""
25
+ upload_dir = tmp_path / ".wex" / "tmp" / "pypi-upload"
26
+ upload_dir.mkdir(parents=True)
27
+ with open(upload_dir / "log", "w") as log:
28
+ process = subprocess.Popen(
29
+ ["sh", "-c", f'( {script} ); echo $? > "{upload_dir / "rc"}"'],
30
+ stdout=log,
31
+ stderr=subprocess.STDOUT,
32
+ )
33
+ (upload_dir / "pid").write_text(str(process.pid))
34
+ if reap:
35
+ process.wait()
36
+
37
+
38
+ def test_nothing_to_collect_without_an_upload(tmp_path) -> None:
39
+ assert PythonPackageWorkdir.collect_pypi_upload(_package(tmp_path)) is None
40
+
41
+
42
+ def test_a_successful_upload_is_collected_silently(tmp_path) -> None:
43
+ _upload(tmp_path, "echo uploaded")
44
+ assert PythonPackageWorkdir.collect_pypi_upload(_package(tmp_path)) is None
45
+ assert not (tmp_path / ".wex" / "tmp" / "pypi-upload").exists()
46
+
47
+
48
+ def test_a_refused_upload_is_reported_with_its_log(tmp_path) -> None:
49
+ _upload(tmp_path, "echo 'HTTP 400 File already exists'; exit 1")
50
+ report = PythonPackageWorkdir.collect_pypi_upload(_package(tmp_path))
51
+ assert report.startswith("demo: PyPI upload failed (exit 1)")
52
+ assert "File already exists" in report
53
+
54
+
55
+ def test_an_ended_upload_left_a_zombie_is_collected(tmp_path) -> None:
56
+ # Its /proc entry stays until reaped: waiting on it alone hung forever.
57
+ _upload(tmp_path, "sleep 0.5; echo uploaded", reap=False)
58
+ result = {}
59
+ thread = threading.Thread(
60
+ target=lambda: result.setdefault("report", PythonPackageWorkdir.collect_pypi_upload(_package(tmp_path))),
61
+ daemon=True,
62
+ )
63
+ thread.start()
64
+ thread.join(timeout=5)
65
+ assert not thread.is_alive()
66
+ assert result["report"] is None