virtualenv 21.2.3__tar.gz → 21.3.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 (186) hide show
  1. {virtualenv-21.2.3 → virtualenv-21.3.0}/PKG-INFO +1 -1
  2. {virtualenv-21.2.3 → virtualenv-21.3.0}/pyproject.toml +1 -0
  3. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/__init__.py +2 -0
  4. virtualenv-21.3.0/src/virtualenv/activation/xonsh/__init__.py +34 -0
  5. virtualenv-21.3.0/src/virtualenv/activation/xonsh/activate.xsh +101 -0
  6. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/config/cli/parser.py +3 -2
  7. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/run/plugin/base.py +8 -2
  8. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/embed/via_app_data/pip_install/base.py +29 -1
  9. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/wheels/acquire.py +63 -0
  10. virtualenv-21.3.0/src/virtualenv/seed/wheels/embed/__init__.py +120 -0
  11. virtualenv-21.3.0/src/virtualenv/seed/wheels/embed/pip-26.1-py3-none-any.whl +0 -0
  12. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/wheels/periodic_update.py +12 -2
  13. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/util/zipapp.py +14 -16
  14. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/version.py +2 -2
  15. virtualenv-21.3.0/tasks/upgrade_wheels.py +265 -0
  16. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/activation/conftest.py +22 -9
  17. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/activation/test_activation_support.py +3 -2
  18. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/activation/test_powershell.py +2 -1
  19. virtualenv-21.3.0/tests/unit/activation/test_xonsh.py +91 -0
  20. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/seed/embed/test_bootstrap_link_via_app_data.py +57 -1
  21. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/seed/wheels/test_acquire.py +33 -0
  22. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/seed/wheels/test_bundle.py +64 -1
  23. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/seed/wheels/test_periodic_update.py +17 -4
  24. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/test_util.py +28 -0
  25. virtualenv-21.2.3/src/virtualenv/seed/wheels/embed/__init__.py +0 -60
  26. virtualenv-21.2.3/tasks/upgrade_wheels.py +0 -157
  27. {virtualenv-21.2.3 → virtualenv-21.3.0}/.gitignore +0 -0
  28. {virtualenv-21.2.3 → virtualenv-21.3.0}/LICENSE +0 -0
  29. {virtualenv-21.2.3 → virtualenv-21.3.0}/README.md +0 -0
  30. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/__init__.py +0 -0
  31. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/__main__.py +0 -0
  32. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/activator.py +0 -0
  33. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/bash/__init__.py +0 -0
  34. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/bash/activate.sh +0 -0
  35. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/batch/__init__.py +0 -0
  36. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/batch/activate.bat +0 -0
  37. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/batch/deactivate.bat +0 -0
  38. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/batch/pydoc.bat +0 -0
  39. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/cshell/__init__.py +0 -0
  40. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/cshell/activate.csh +0 -0
  41. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/fish/__init__.py +0 -0
  42. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/fish/activate.fish +0 -0
  43. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/nushell/__init__.py +0 -0
  44. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/nushell/activate.nu +0 -0
  45. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/powershell/__init__.py +0 -0
  46. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/powershell/activate.ps1 +0 -0
  47. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/python/__init__.py +0 -0
  48. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/python/activate_this.py +0 -0
  49. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/activation/via_template.py +0 -0
  50. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/app_data/__init__.py +0 -0
  51. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/app_data/base.py +0 -0
  52. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/app_data/na.py +0 -0
  53. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/app_data/read_only.py +0 -0
  54. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/app_data/via_disk_folder.py +0 -0
  55. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/app_data/via_tempdir.py +0 -0
  56. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/config/__init__.py +0 -0
  57. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/config/cli/__init__.py +0 -0
  58. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/config/convert.py +0 -0
  59. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/config/env_var.py +0 -0
  60. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/config/ini.py +0 -0
  61. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/__init__.py +0 -0
  62. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/creator.py +0 -0
  63. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/debug.py +0 -0
  64. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/describe.py +0 -0
  65. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/pyenv_cfg.py +0 -0
  66. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/__init__.py +0 -0
  67. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/_virtualenv.py +0 -0
  68. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/api.py +0 -0
  69. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/__init__.py +0 -0
  70. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/builtin_way.py +0 -0
  71. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/cpython/__init__.py +0 -0
  72. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/cpython/common.py +0 -0
  73. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py +0 -0
  74. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py +0 -0
  75. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/graalpy/__init__.py +0 -0
  76. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/pypy/__init__.py +0 -0
  77. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/pypy/common.py +0 -0
  78. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py +0 -0
  79. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/ref.py +0 -0
  80. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/rustpython/__init__.py +0 -0
  81. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py +0 -0
  82. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/store.py +0 -0
  83. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/create/via_global_ref/venv.py +0 -0
  84. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/discovery/__init__.py +0 -0
  85. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/discovery/builtin.py +0 -0
  86. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/discovery/cached_py_info.py +0 -0
  87. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/discovery/discover.py +0 -0
  88. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/discovery/py_info.py +0 -0
  89. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/discovery/py_spec.py +0 -0
  90. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/info.py +0 -0
  91. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/py.typed +0 -0
  92. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/report.py +0 -0
  93. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/run/__init__.py +0 -0
  94. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/run/plugin/__init__.py +0 -0
  95. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/run/plugin/activators.py +0 -0
  96. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/run/plugin/creators.py +0 -0
  97. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/run/plugin/discovery.py +0 -0
  98. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/run/plugin/seeders.py +0 -0
  99. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/run/session.py +0 -0
  100. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/__init__.py +0 -0
  101. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/embed/__init__.py +0 -0
  102. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/embed/base_embed.py +0 -0
  103. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/embed/pip_invoke.py +0 -0
  104. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/embed/via_app_data/__init__.py +0 -0
  105. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/embed/via_app_data/pip_install/__init__.py +0 -0
  106. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/embed/via_app_data/pip_install/copy.py +0 -0
  107. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/embed/via_app_data/pip_install/symlink.py +0 -0
  108. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/embed/via_app_data/via_app_data.py +0 -0
  109. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/seeder.py +0 -0
  110. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/wheels/__init__.py +0 -0
  111. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/wheels/bundle.py +0 -0
  112. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/wheels/embed/pip-25.0.1-py3-none-any.whl +0 -0
  113. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/wheels/embed/pip-26.0.1-py3-none-any.whl +0 -0
  114. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/wheels/embed/setuptools-75.3.4-py3-none-any.whl +0 -0
  115. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/wheels/embed/setuptools-82.0.1-py3-none-any.whl +0 -0
  116. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/wheels/embed/wheel-0.45.1-py3-none-any.whl +0 -0
  117. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/seed/wheels/util.py +0 -0
  118. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/util/__init__.py +0 -0
  119. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/util/error.py +0 -0
  120. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/util/lock.py +0 -0
  121. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/util/path/__init__.py +0 -0
  122. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/util/path/_permission.py +0 -0
  123. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/util/path/_sync.py +0 -0
  124. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/util/path/_win.py +0 -0
  125. {virtualenv-21.2.3 → virtualenv-21.3.0}/src/virtualenv/util/subprocess/__init__.py +0 -0
  126. {virtualenv-21.2.3 → virtualenv-21.3.0}/tasks/__main__zipapp.py +0 -0
  127. {virtualenv-21.2.3 → virtualenv-21.3.0}/tasks/make_zipapp.py +0 -0
  128. {virtualenv-21.2.3 → virtualenv-21.3.0}/tasks/release.py +0 -0
  129. {virtualenv-21.2.3 → virtualenv-21.3.0}/tasks/update_embedded.py +0 -0
  130. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/conftest.py +0 -0
  131. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/integration/test_cachedir_tag.py +0 -0
  132. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/integration/test_race_condition_simulation.py +0 -0
  133. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/integration/test_run_int.py +0 -0
  134. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/integration/test_zipapp.py +0 -0
  135. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/types.py +0 -0
  136. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/activation/test_activator.py +0 -0
  137. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/activation/test_bash.py +0 -0
  138. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/activation/test_batch.py +0 -0
  139. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/activation/test_csh.py +0 -0
  140. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/activation/test_fish.py +0 -0
  141. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/activation/test_nushell.py +0 -0
  142. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/activation/test_python_activator.py +0 -0
  143. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/config/cli/test_help_formatter.py +0 -0
  144. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/config/cli/test_parser.py +0 -0
  145. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/config/test___main__.py +0 -0
  146. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/config/test_env_var.py +0 -0
  147. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/config/test_ini.py +0 -0
  148. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/conftest.py +0 -0
  149. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/console_app/demo/__init__.py +0 -0
  150. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/console_app/demo/__main__.py +0 -0
  151. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/console_app/setup.cfg +0 -0
  152. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/console_app/setup.py +0 -0
  153. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/test_creator.py +0 -0
  154. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/test_interpreters.py +0 -0
  155. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/_test_race_condition_helper.py +0 -0
  156. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/conftest.py +0 -0
  157. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/cpython/conftest.py +0 -0
  158. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/cpython/cpython3_win_embed.json +0 -0
  159. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/cpython/cpython3_win_free_threaded.json +0 -0
  160. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/cpython/test_cpython3_posix.py +0 -0
  161. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/cpython/test_cpython3_win.py +0 -0
  162. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/pypy/deb_pypy37.json +0 -0
  163. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/pypy/deb_pypy38.json +0 -0
  164. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/pypy/portable_pypy38.json +0 -0
  165. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/pypy/test_pypy3.py +0 -0
  166. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/rustpython/rustpython_posix.json +0 -0
  167. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/rustpython/rustpython_windows.json +0 -0
  168. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/rustpython/test_rustpython.py +0 -0
  169. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/testing/__init__.py +0 -0
  170. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/testing/helpers.py +0 -0
  171. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/testing/path.py +0 -0
  172. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/builtin/testing/py_info.py +0 -0
  173. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/greet/greet2.c +0 -0
  174. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/greet/greet3.c +0 -0
  175. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/greet/setup.py +0 -0
  176. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/test_api.py +0 -0
  177. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/test_build_c_ext.py +0 -0
  178. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/create/via_global_ref/test_race_condition.py +0 -0
  179. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/discovery/test_discovery.py +0 -0
  180. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/seed/embed/test_base_embed.py +0 -0
  181. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/seed/embed/test_pip_invoke.py +0 -0
  182. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/seed/wheels/test_acquire_find_wheel.py +0 -0
  183. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/seed/wheels/test_wheels_util.py +0 -0
  184. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/test_file_limit.py +0 -0
  185. {virtualenv-21.2.3 → virtualenv-21.3.0}/tests/unit/test_run.py +0 -0
  186. {virtualenv-21.2.3 → virtualenv-21.3.0}/tox.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: virtualenv
3
- Version: 21.2.3
3
+ Version: 21.3.0
4
4
  Summary: Virtual Python Environment builder
5
5
  Project-URL: Documentation, https://virtualenv.pypa.io
6
6
  Project-URL: Homepage, https://github.com/pypa/virtualenv
@@ -65,6 +65,7 @@ entry-points."virtualenv.activate".fish = "virtualenv.activation.fish:FishActiva
65
65
  entry-points."virtualenv.activate".nushell = "virtualenv.activation.nushell:NushellActivator"
66
66
  entry-points."virtualenv.activate".powershell = "virtualenv.activation.powershell:PowerShellActivator"
67
67
  entry-points."virtualenv.activate".python = "virtualenv.activation.python:PythonActivator"
68
+ entry-points."virtualenv.activate".xonsh = "virtualenv.activation.xonsh:XonshActivator"
68
69
  entry-points."virtualenv.create".cpython3-mac-brew = "virtualenv.create.via_global_ref.builtin.cpython.mac_os:CPython3macOsBrew"
69
70
  entry-points."virtualenv.create".cpython3-mac-framework = "virtualenv.create.via_global_ref.builtin.cpython.mac_os:CPython3macOsFramework"
70
71
  entry-points."virtualenv.create".cpython3-posix = "virtualenv.create.via_global_ref.builtin.cpython.cpython3:CPython3Posix"
@@ -7,6 +7,7 @@ from .fish import FishActivator
7
7
  from .nushell import NushellActivator
8
8
  from .powershell import PowerShellActivator
9
9
  from .python import PythonActivator
10
+ from .xonsh import XonshActivator
10
11
 
11
12
  __all__ = [
12
13
  "BashActivator",
@@ -16,4 +17,5 @@ __all__ = [
16
17
  "NushellActivator",
17
18
  "PowerShellActivator",
18
19
  "PythonActivator",
20
+ "XonshActivator",
19
21
  ]
@@ -0,0 +1,34 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from virtualenv.activation.via_template import ViaTemplateActivator
6
+
7
+ if TYPE_CHECKING:
8
+ from collections.abc import Iterator
9
+ from pathlib import Path
10
+
11
+ from virtualenv.create.creator import Creator
12
+
13
+
14
+ class XonshActivator(ViaTemplateActivator):
15
+ def templates(self) -> Iterator[str]:
16
+ yield "activate.xsh"
17
+
18
+ @staticmethod
19
+ def quote(string: str) -> str:
20
+ """Quote as a Python literal — xonsh parses the activation script as Python."""
21
+ return repr(string)
22
+
23
+ def replacements(self, creator: Creator, dest_folder: Path) -> dict[str, str]:
24
+ data = super().replacements(creator, dest_folder)
25
+ data.update({
26
+ "__TCL_LIBRARY__": getattr(creator.interpreter, "tcl_lib", None) or "",
27
+ "__TK_LIBRARY__": getattr(creator.interpreter, "tk_lib", None) or "",
28
+ })
29
+ return data
30
+
31
+
32
+ __all__ = [
33
+ "XonshActivator",
34
+ ]
@@ -0,0 +1,101 @@
1
+ # This file must be used with "source bin/activate.xsh" *from xonsh*.
2
+ # You cannot run it directly.
3
+
4
+
5
+ class _VirtualEnvActivator:
6
+ """xonsh activation for virtualenv.
7
+
8
+ Rendered into ``<venv>/bin/activate.xsh`` by the template in
9
+ ``src/virtualenv/activation/xonsh/`` at generate-time.
10
+
11
+ - Project: https://github.com/pypa/virtualenv
12
+ - Template source: https://github.com/pypa/virtualenv/tree/main/src/virtualenv/activation/xonsh
13
+ """
14
+
15
+ # Stashed in _OLD_VIRTUAL_{name} when the variable was not set before
16
+ # activation. deactivate treats this as "unset" rather than "restore to
17
+ # this string". Chosen to be vanishingly unlikely as a real value.
18
+ _UNSET_SENTINEL = "__virtualenv_was_not_set__"
19
+
20
+ def __init__(self):
21
+ self.env = __xonsh__.env
22
+ try:
23
+ # Values substituted by virtualenv's XonshActivator at generate-time.
24
+ # If this file was sourced before template rendering, the bare
25
+ # identifiers below resolve to NameError.
26
+ self.embedded_virtual_env = __VIRTUAL_ENV__
27
+ self.embedded_virtual_prompt = __VIRTUAL_PROMPT__
28
+ self.embedded_bin_name = __BIN_NAME__
29
+ self.embedded_tcl_library = __TCL_LIBRARY__
30
+ self.embedded_tk_library = __TK_LIBRARY__
31
+ except NameError:
32
+ print("virtualenv activate.xsh: template was not rendered")
33
+ raise
34
+ # Variables this activator may save+override; deactivate walks this list.
35
+ self.managed_vars = ("PATH", "PYTHONHOME", "TCL_LIBRARY", "TK_LIBRARY")
36
+
37
+ def _backup_name(self, name):
38
+ return f"_OLD_VIRTUAL_{name}"
39
+
40
+ def _save(self, name):
41
+ """Stash the current value (or sentinel if unset) so deactivate can undo."""
42
+ backup = self._backup_name(name)
43
+ self.env[backup] = self.env[name] if name in self.env else self._UNSET_SENTINEL
44
+
45
+ def _override(self, name, value):
46
+ self._save(name)
47
+ self.env[name] = value
48
+
49
+ def _drop(self, name):
50
+ self._save(name)
51
+ self.env.pop(name, None)
52
+
53
+ def register_pydoc(self):
54
+ aliases["pydoc"] = ["python", "-m", "pydoc"]
55
+
56
+ def unregister_pydoc(self):
57
+ aliases.pop("pydoc", None)
58
+
59
+ def activate(self):
60
+ from os.path import join, basename
61
+
62
+ aliases["deactivate"] = self.deactivate
63
+ self.deactivate(["nondestructive"]) # wipe any stale state from a prior activation
64
+
65
+ $VIRTUAL_ENV = self.embedded_virtual_env
66
+ $VIRTUAL_ENV_PROMPT = self.embedded_virtual_prompt or basename($VIRTUAL_ENV)
67
+
68
+ self._override("PATH", [join($VIRTUAL_ENV, self.embedded_bin_name), *$PATH])
69
+ self._drop("PYTHONHOME")
70
+ for name, value in (("TCL_LIBRARY", self.embedded_tcl_library), ("TK_LIBRARY", self.embedded_tk_library)):
71
+ if value:
72
+ self._override(name, value)
73
+
74
+ self.register_pydoc()
75
+
76
+ def deactivate(self, args=None):
77
+ for name in self.managed_vars:
78
+ backup = self._backup_name(name)
79
+ if backup not in self.env:
80
+ continue
81
+ previous = self.env[backup]
82
+ del self.env[backup]
83
+ if previous == self._UNSET_SENTINEL:
84
+ self.env.pop(name, None)
85
+ else:
86
+ self.env[name] = previous
87
+ for name in ("VIRTUAL_ENV", "VIRTUAL_ENV_PROMPT"):
88
+ self.env.pop(name, None)
89
+ self.unregister_pydoc()
90
+ if args is None or "nondestructive" not in args:
91
+ del aliases["deactivate"]
92
+ try:
93
+ del __xonsh__.xontrib.virtualenv
94
+ except AttributeError:
95
+ pass
96
+
97
+
98
+ if not hasattr(__xonsh__, "xontrib"):
99
+ __xonsh__.xontrib = __xonsh__.imp.types.SimpleNamespace()
100
+ __xonsh__.xontrib.virtualenv = _VirtualEnvActivator()
101
+ __xonsh__.xontrib.virtualenv.activate()
@@ -113,7 +113,8 @@ class VirtualEnvConfigParser(ArgumentParser):
113
113
  if outcome is not None:
114
114
  break
115
115
  if outcome is not None:
116
- action.default, action.default_source = outcome
116
+ action.default, default_source = outcome
117
+ vars(action)["default_source"] = default_source
117
118
  else:
118
119
  outcome = action.default, "default"
119
120
  self.options.set_src(action.dest, *outcome)
@@ -122,7 +123,7 @@ class VirtualEnvConfigParser(ArgumentParser):
122
123
  self._fix_defaults()
123
124
  self.add_argument("-h", "--help", action="help", default=SUPPRESS, help="show this help message and exit")
124
125
 
125
- def parse_known_args(
126
+ def parse_known_args( # ty: ignore[invalid-method-override]
126
127
  self, args: Sequence[str] | None = None, namespace: VirtualEnvOptions | None = None
127
128
  ) -> tuple[VirtualEnvOptions, list[str]]:
128
129
  if namespace is None:
@@ -22,8 +22,14 @@ class PluginLoader:
22
22
  @classmethod
23
23
  def entry_points_for(cls, key: str) -> OrderedDict[str, type]:
24
24
  if sys.version_info >= (3, 10) or importlib_metadata_version >= (3, 6):
25
- return OrderedDict((e.name, e.load()) for e in cls.entry_points().select(group=key)) # ty: ignore[unresolved-attribute]
26
- return OrderedDict((e.name, e.load()) for e in cls.entry_points().get(key, {})) # ty: ignore[unresolved-attribute]
25
+ selected = list(cls.entry_points().select(group=key)) # ty: ignore[unresolved-attribute]
26
+ else:
27
+ selected = list(cls.entry_points().get(key, [])) # ty: ignore[unresolved-attribute]
28
+ # Third-party packages may register entry points with the same name as virtualenv's
29
+ # built-ins (e.g. xonsh's own `virtualenv.activate.xonsh`). Sort so built-ins are
30
+ # inserted last into the OrderedDict, making them win on name collision.
31
+ selected.sort(key=lambda e: e.value.startswith("virtualenv."))
32
+ return OrderedDict((e.name, e.load()) for e in selected)
27
33
 
28
34
  @staticmethod
29
35
  def entry_points() -> object:
@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
+ import ntpath
4
5
  import os
6
+ import posixpath
5
7
  import re
6
8
  import zipfile
7
9
  from abc import ABC, abstractmethod
@@ -21,6 +23,24 @@ if TYPE_CHECKING:
21
23
  LOGGER = logging.getLogger(__name__)
22
24
 
23
25
 
26
+ def _safe_extract_zip(zip_ref: zipfile.ZipFile, target_dir: Path) -> None:
27
+ # Guard against zip slip: a wheel is a zip and a tampered entry name (absolute path or one containing ``..``)
28
+ # could escape ``target_dir``.
29
+ base = target_dir.resolve()
30
+ for info in zip_ref.infolist():
31
+ name = info.filename
32
+ if name.startswith(("/", "\\")) or ntpath.isabs(name) or posixpath.isabs(name):
33
+ msg = f"refusing to extract absolute path entry from wheel: {name!r}"
34
+ raise RuntimeError(msg)
35
+ candidate = (base / name).resolve()
36
+ try:
37
+ candidate.relative_to(base)
38
+ except ValueError as exc:
39
+ msg = f"refusing to extract entry escaping target directory: {name!r}"
40
+ raise RuntimeError(msg) from exc
41
+ zip_ref.extractall(str(target_dir))
42
+
43
+
24
44
  class PipInstall(ABC):
25
45
  def __init__(self, wheel: Path, creator: Creator, image_folder: Path) -> None:
26
46
  self._wheel = wheel
@@ -49,11 +69,19 @@ class PipInstall(ABC):
49
69
  LOGGER.debug("generated console scripts %s", " ".join(i.name for i in consoles))
50
70
 
51
71
  def build_image(self) -> None:
72
+ """Extract the seed wheel into the image directory and fix up its RECORD file.
73
+
74
+ Each archive entry is validated before extraction so a tampered wheel cannot escape the image directory via an
75
+ absolute path or ``..`` traversal.
76
+
77
+ :raises RuntimeError: if the wheel contains an entry that would land outside the image directory.
78
+
79
+ """
52
80
  # 1. first extract the wheel
53
81
  LOGGER.debug("build install image for %s to %s", self._wheel.name, self._image_dir)
54
82
  with zipfile.ZipFile(str(self._wheel)) as zip_ref:
55
83
  self._shorten_path_if_needed(zip_ref)
56
- zip_ref.extractall(str(self._image_dir))
84
+ _safe_extract_zip(zip_ref, self._image_dir)
57
85
  self._extracted = True
58
86
  # 2. now add additional files not present in the distribution
59
87
  new_files = self._generate_new_files()
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import logging
6
+ import re
6
7
  import sys
7
8
  from operator import eq, lt
8
9
  from pathlib import Path
@@ -18,6 +19,34 @@ if TYPE_CHECKING:
18
19
 
19
20
  LOGGER = logging.getLogger(__name__)
20
21
 
22
+ # PEP 503 normalized distribution name. Anything outside this character set on the way to ``pip download`` means
23
+ # somebody is smuggling pip options or extras, so reject it before we build the command line.
24
+ _DISTRIBUTION_RE = re.compile(
25
+ r"""
26
+ ^
27
+ (?P<name>
28
+ [A-Za-z0-9] # must start with an alnum
29
+ (?:[A-Za-z0-9._-]* # inner chars: alnum plus . _ -
30
+ [A-Za-z0-9])? # must also end with an alnum (unless length is 1)
31
+ )
32
+ $
33
+ """,
34
+ re.VERBOSE,
35
+ )
36
+
37
+ # Version specifier that matches what ``Version.as_version_spec`` emits: either empty, ``==<ver>`` or ``<<ver>`` where
38
+ # ``<ver>`` is a subset of PEP 440 public versions. Kept deliberately strict so a crafted version cannot inject pip
39
+ # flags.
40
+ _VERSION_SPEC_RE = re.compile(
41
+ r"""
42
+ ^
43
+ (?P<operator>==|<) # only the operators Version.as_version_spec can emit
44
+ (?P<version>[A-Za-z0-9._+!-]+) # PEP 440 public-version character set, no whitespace
45
+ $
46
+ """,
47
+ re.VERBOSE,
48
+ )
49
+
21
50
 
22
51
  def get_wheel( # noqa: PLR0913
23
52
  distribution: str,
@@ -63,6 +92,26 @@ def download_wheel( # noqa: PLR0913
63
92
  to_folder: Path,
64
93
  env: dict[str, str],
65
94
  ) -> Wheel:
95
+ """Invoke ``pip download`` in a subprocess to fetch a seed wheel.
96
+
97
+ :param distribution: PEP 503 normalized project name; rejected if it contains anything other than
98
+ ``[A-Za-z0-9._-]``.
99
+ :param version_spec: optional version specifier of the form ``==<ver>`` or ``<<ver>`` as emitted by
100
+ :func:`Version.as_version_spec`, or ``None``/empty for the latest compatible release.
101
+ :param for_py_version: major.minor Python version to pass through to ``pip --python-version``.
102
+ :param search_dirs: additional directories to treat as a local wheel index when bootstrapping pip.
103
+ :param app_data: application data store used to locate the embedded pip wheel.
104
+ :param to_folder: directory the downloaded wheel is written into.
105
+ :param env: environment mapping passed through to the subprocess.
106
+
107
+ :returns: the downloaded :class:`Wheel`.
108
+
109
+ :raises ValueError: if ``distribution`` or ``version_spec`` fail the strict allow-list check.
110
+ :raises CalledProcessError: if ``pip download`` exits with a non-zero status.
111
+
112
+ """
113
+ _check_distribution(distribution)
114
+ _check_version_spec(version_spec)
66
115
  to_download = f"{distribution}{version_spec or ''}"
67
116
  LOGGER.debug("download wheel %s %s to %s", to_download, for_py_version, to_folder)
68
117
  cmd = [
@@ -143,6 +192,20 @@ def pip_wheel_env_run(search_dirs: list[Path], app_data: AppData, env: dict[str,
143
192
  return env
144
193
 
145
194
 
195
+ def _check_distribution(distribution: str) -> None:
196
+ if not _DISTRIBUTION_RE.fullmatch(distribution):
197
+ msg = f"refusing to download wheel for suspicious distribution name: {distribution!r}"
198
+ raise ValueError(msg)
199
+
200
+
201
+ def _check_version_spec(version_spec: str | None) -> None:
202
+ if not version_spec:
203
+ return
204
+ if not _VERSION_SPEC_RE.fullmatch(version_spec):
205
+ msg = f"refusing to download wheel with suspicious version spec: {version_spec!r}"
206
+ raise ValueError(msg)
207
+
208
+
146
209
  __all__ = [
147
210
  "download_wheel",
148
211
  "get_wheel",
@@ -0,0 +1,120 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import zipfile
5
+ from pathlib import Path
6
+
7
+ from virtualenv.info import IS_ZIPAPP, ROOT
8
+ from virtualenv.seed.wheels.util import Wheel
9
+
10
+ BUNDLE_FOLDER = Path(__file__).absolute().parent
11
+ BUNDLE_SUPPORT = {
12
+ "3.8": {
13
+ "pip": "pip-25.0.1-py3-none-any.whl",
14
+ "setuptools": "setuptools-75.3.4-py3-none-any.whl",
15
+ "wheel": "wheel-0.45.1-py3-none-any.whl",
16
+ },
17
+ "3.9": {
18
+ "pip": "pip-26.0.1-py3-none-any.whl",
19
+ "setuptools": "setuptools-82.0.1-py3-none-any.whl",
20
+ },
21
+ "3.10": {
22
+ "pip": "pip-26.1-py3-none-any.whl",
23
+ "setuptools": "setuptools-82.0.1-py3-none-any.whl",
24
+ },
25
+ "3.11": {
26
+ "pip": "pip-26.1-py3-none-any.whl",
27
+ "setuptools": "setuptools-82.0.1-py3-none-any.whl",
28
+ },
29
+ "3.12": {
30
+ "pip": "pip-26.1-py3-none-any.whl",
31
+ "setuptools": "setuptools-82.0.1-py3-none-any.whl",
32
+ },
33
+ "3.13": {
34
+ "pip": "pip-26.1-py3-none-any.whl",
35
+ "setuptools": "setuptools-82.0.1-py3-none-any.whl",
36
+ },
37
+ "3.14": {
38
+ "pip": "pip-26.1-py3-none-any.whl",
39
+ "setuptools": "setuptools-82.0.1-py3-none-any.whl",
40
+ },
41
+ "3.15": {
42
+ "pip": "pip-26.1-py3-none-any.whl",
43
+ "setuptools": "setuptools-82.0.1-py3-none-any.whl",
44
+ },
45
+ }
46
+ MAX = "3.8"
47
+
48
+ # SHA-256 of every bundled wheel. Verified on load so a corrupted or tampered wheel on disk fails loud instead of
49
+ # being handed to pip. Generated together with ``BUNDLE_SUPPORT`` by ``tasks/upgrade_wheels.py``.
50
+ BUNDLE_SHA256 = {
51
+ "pip-25.0.1-py3-none-any.whl": "c46efd13b6aa8279f33f2864459c8ce587ea6a1a59ee20de055868d8f7688f7f",
52
+ "pip-26.0.1-py3-none-any.whl": "bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b",
53
+ "pip-26.1-py3-none-any.whl": "4e8486d821d814b77319acb7b9e8bf5a4ee7590a643e7cb21029f209be8573c1",
54
+ "setuptools-75.3.4-py3-none-any.whl": "2dd50a7f42dddfa1d02a36f275dbe716f38ed250224f609d35fb60a09593d93e",
55
+ "setuptools-82.0.1-py3-none-any.whl": "a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb",
56
+ "wheel-0.45.1-py3-none-any.whl": "708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248",
57
+ }
58
+
59
+ _VERIFIED_WHEELS: set[str] = set()
60
+
61
+
62
+ def get_embed_wheel(distribution: str, for_py_version: str) -> Wheel | None:
63
+ """Return the bundled wheel that ships with virtualenv for a given distribution and Python version.
64
+
65
+ :param distribution: project name of the seed package, for example ``pip`` or ``setuptools``.
66
+ :param for_py_version: major.minor Python version string the environment will be created for.
67
+
68
+ :returns: a :class:`Wheel` pointing at the verified bundled file, or ``None`` when no wheel is bundled for the
69
+ requested combination.
70
+
71
+ :raises RuntimeError: if the bundled wheel on disk fails SHA-256 verification.
72
+
73
+ """
74
+ mapping = BUNDLE_SUPPORT.get(for_py_version, {}) or BUNDLE_SUPPORT[MAX]
75
+ wheel_file = mapping.get(distribution)
76
+ if wheel_file is None:
77
+ return None
78
+ path = BUNDLE_FOLDER / wheel_file
79
+ _verify_bundled_wheel(path)
80
+ return Wheel.from_path(path)
81
+
82
+
83
+ def _verify_bundled_wheel(path: Path) -> None:
84
+ name = path.name
85
+ if name in _VERIFIED_WHEELS:
86
+ return
87
+ expected = BUNDLE_SHA256.get(name)
88
+ if expected is None:
89
+ msg = f"bundled wheel {name} has no recorded sha256 in BUNDLE_SHA256"
90
+ raise RuntimeError(msg)
91
+ actual = _hash_bundled_wheel(path)
92
+ if actual != expected:
93
+ msg = f"bundled wheel {name} sha256 mismatch: expected {expected}, got {actual}"
94
+ raise RuntimeError(msg)
95
+ _VERIFIED_WHEELS.add(name)
96
+
97
+
98
+ def _hash_bundled_wheel(path: Path) -> str:
99
+ # ``path`` is under the package directory; when virtualenv runs from a zipapp the wheel lives inside the
100
+ # archive and cannot be opened as a regular file, so read the bytes straight from the zipapp entry.
101
+ digest = hashlib.sha256()
102
+ if IS_ZIPAPP:
103
+ entry = path.resolve().relative_to(Path(ROOT).resolve()).as_posix()
104
+ with zipfile.ZipFile(ROOT, "r") as archive, archive.open(entry) as stream:
105
+ for chunk in iter(lambda: stream.read(1 << 20), b""):
106
+ digest.update(chunk)
107
+ else:
108
+ with path.open("rb") as stream:
109
+ for chunk in iter(lambda: stream.read(1 << 20), b""):
110
+ digest.update(chunk)
111
+ return digest.hexdigest()
112
+
113
+
114
+ __all__ = [
115
+ "BUNDLE_FOLDER",
116
+ "BUNDLE_SHA256",
117
+ "BUNDLE_SUPPORT",
118
+ "MAX",
119
+ "get_embed_wheel",
120
+ ]
@@ -360,10 +360,20 @@ def release_date_for_wheel_path(dest: Path) -> datetime | None:
360
360
  return None
361
361
 
362
362
 
363
+ #: Opt-in escape hatch to restore the pre-2026 behavior of falling back to an unverified HTTPS context when the
364
+ #: verified request fails. Off by default: a failed TLS handshake on the PyPI metadata lookup now aborts the update
365
+ #: instead of silently downgrading, because the response drives which wheel version virtualenv thinks is up to date.
366
+ _INSECURE_FALLBACK_ENV = "VIRTUALENV_PERIODIC_UPDATE_INSECURE"
367
+
368
+
363
369
  def _request_context() -> Generator[ssl.SSLContext | None, None, None]:
364
370
  yield None
365
- # fallback to non verified HTTPS (the information we request is not sensitive, so fallback)
366
- yield ssl._create_unverified_context() # noqa: S323, SLF001
371
+ if os.environ.get(_INSECURE_FALLBACK_ENV):
372
+ LOGGER.warning(
373
+ "falling back to unverified HTTPS for PyPI metadata because %s is set",
374
+ _INSECURE_FALLBACK_ENV,
375
+ )
376
+ yield ssl._create_unverified_context() # noqa: S323, SLF001
367
377
 
368
378
 
369
379
  _PYPI_CACHE = {}
@@ -1,14 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- import os
5
4
  import zipfile
6
- from typing import TYPE_CHECKING
5
+ from pathlib import Path
7
6
 
8
- from virtualenv.info import IS_WIN, ROOT
9
-
10
- if TYPE_CHECKING:
11
- from pathlib import Path
7
+ from virtualenv.info import ROOT
12
8
 
13
9
  LOGGER = logging.getLogger(__name__)
14
10
 
@@ -29,16 +25,18 @@ def extract(full_path: str | Path, dest: Path) -> None:
29
25
 
30
26
 
31
27
  def _get_path_within_zip(full_path: str | Path) -> str:
32
- full_path = os.path.realpath(os.path.abspath(str(full_path)))
33
- prefix = f"{ROOT}{os.sep}"
34
- if not full_path.startswith(prefix):
35
- msg = f"full_path={full_path} should start with prefix={prefix}."
36
- raise RuntimeError(msg)
37
- sub_file = full_path[len(prefix) :]
38
- if IS_WIN:
39
- # paths are always UNIX separators, even on Windows, though __file__ still follows platform default
40
- sub_file = sub_file.replace(os.sep, "/")
41
- return sub_file
28
+ # Use Path.relative_to so symlinks and ``..`` segments cannot slip through a string ``startswith`` check. The zipapp
29
+ # root is a real file we own so ``resolve`` is safe; anything that does not resolve under ROOT is a bug or an
30
+ # attempt to escape the archive and we refuse it.
31
+ resolved = Path(full_path).resolve()
32
+ root = Path(ROOT).resolve()
33
+ try:
34
+ relative = resolved.relative_to(root)
35
+ except ValueError as exc:
36
+ msg = f"full_path={resolved} should be within ROOT={root}"
37
+ raise RuntimeError(msg) from exc
38
+ # Zip entries always use forward slashes regardless of platform.
39
+ return relative.as_posix()
42
40
 
43
41
 
44
42
  __all__ = [
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '21.2.3'
22
- __version_tuple__ = version_tuple = (21, 2, 3)
21
+ __version__ = version = '21.3.0'
22
+ __version_tuple__ = version_tuple = (21, 3, 0)
23
23
 
24
24
  __commit_id__ = commit_id = None