Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

dependencies.md 9.8 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. # Managing dependencies with Poetry
  2. This is a quick cheat sheet for developers on how to use [`poetry`](https://python-poetry.org/).
  3. # Installing
  4. See the [contributing guide](contributing_guide.md#4-install-the-dependencies).
  5. Developers should use Poetry 1.3.2 or higher. If you encounter problems related
  6. to poetry, please [double-check your poetry version](#check-the-version-of-poetry-with-poetry---version).
  7. # Background
  8. Synapse uses a variety of third-party Python packages to function as a homeserver.
  9. Some of these are direct dependencies, listed in `pyproject.toml` under the
  10. `[tool.poetry.dependencies]` section. The rest are transitive dependencies (the
  11. things that our direct dependencies themselves depend on, and so on recursively.)
  12. We maintain a locked list of all our dependencies (transitive included) so that
  13. we can track exactly which version of each dependency appears in a given release.
  14. See [here](https://github.com/matrix-org/synapse/issues/11537#issue-1074469665)
  15. for discussion of why we wanted this for Synapse. We chose to use
  16. [`poetry`](https://python-poetry.org/) to manage this locked list; see
  17. [this comment](https://github.com/matrix-org/synapse/issues/11537#issuecomment-1015975819)
  18. for the reasoning.
  19. The locked dependencies get included in our "self-contained" releases: namely,
  20. our docker images and our debian packages. We also use the locked dependencies
  21. in development and our continuous integration.
  22. Separately, our "broad" dependencies—the version ranges specified in
  23. `pyproject.toml`—are included as metadata in our "sdists" and "wheels" [uploaded
  24. to PyPI](https://pypi.org/project/matrix-synapse). Installing from PyPI or from
  25. the Synapse source tree directly will _not_ use the locked dependencies; instead,
  26. they'll pull in the latest version of each package available at install time.
  27. ## Example dependency
  28. An example may help. We have a broad dependency on
  29. [`phonenumbers`](https://pypi.org/project/phonenumbers/), as declared in
  30. this snippet from pyproject.toml [as of Synapse 1.57](
  31. https://github.com/matrix-org/synapse/blob/release-v1.57/pyproject.toml#L133
  32. ):
  33. ```toml
  34. [tool.poetry.dependencies]
  35. # ...
  36. phonenumbers = ">=8.2.0"
  37. ```
  38. In our lockfile this is
  39. [pinned]( https://github.com/matrix-org/synapse/blob/dfc7646504cef3e4ff396c36089e1c6f1b1634de/poetry.lock#L679-L685)
  40. to version 8.12.44, even though
  41. [newer versions are available](https://pypi.org/project/phonenumbers/#history).
  42. ```toml
  43. [[package]]
  44. name = "phonenumbers"
  45. version = "8.12.44"
  46. description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
  47. category = "main"
  48. optional = false
  49. python-versions = "*"
  50. ```
  51. The lockfile also includes a
  52. [cryptographic checksum](https://github.com/matrix-org/synapse/blob/release-v1.57/poetry.lock#L2178-L2181)
  53. of the sdists and wheels provided for this version of `phonenumbers`.
  54. ```toml
  55. [metadata.files]
  56. # ...
  57. phonenumbers = [
  58. {file = "phonenumbers-8.12.44-py2.py3-none-any.whl", hash = "sha256:cc1299cf37b309ecab6214297663ab86cb3d64ae37fd5b88e904fe7983a874a6"},
  59. {file = "phonenumbers-8.12.44.tar.gz", hash = "sha256:26cfd0257d1704fe2f88caff2caabb70d16a877b1e65b6aae51f9fbbe10aa8ce"},
  60. ]
  61. ```
  62. We can see this pinned version inside the docker image for that release:
  63. ```
  64. $ docker pull matrixdotorg/synapse:v1.57.0
  65. ...
  66. $ docker run --entrypoint pip matrixdotorg/synapse:v1.57.0 show phonenumbers
  67. Name: phonenumbers
  68. Version: 8.12.44
  69. Summary: Python version of Google's common library for parsing, formatting, storing and validating international phone numbers.
  70. Home-page: https://github.com/daviddrysdale/python-phonenumbers
  71. Author: David Drysdale
  72. Author-email: dmd@lurklurk.org
  73. License: Apache License 2.0
  74. Location: /usr/local/lib/python3.9/site-packages
  75. Requires:
  76. Required-by: matrix-synapse
  77. ```
  78. Whereas the wheel metadata just contains the broad dependencies:
  79. ```
  80. $ cd /tmp
  81. $ wget https://files.pythonhosted.org/packages/ca/5e/d722d572cc5b3092402b783d6b7185901b444427633bd8a6b00ea0dd41b7/matrix_synapse-1.57.0rc1-py3-none-any.whl
  82. ...
  83. $ unzip -c matrix_synapse-1.57.0rc1-py3-none-any.whl matrix_synapse-1.57.0rc1.dist-info/METADATA | grep phonenumbers
  84. Requires-Dist: phonenumbers (>=8.2.0)
  85. ```
  86. # Tooling recommendation: direnv
  87. [`direnv`](https://direnv.net/) is a tool for activating environments in your
  88. shell inside a given directory. Its support for poetry is unofficial (a
  89. community wiki recipe only), but works solidly in our experience. We thoroughly
  90. recommend it for daily use. To use it:
  91. 1. [Install `direnv`](https://direnv.net/docs/installation.html) - it's likely
  92. packaged for your system already.
  93. 2. Teach direnv about poetry. The [shell config here](https://github.com/direnv/direnv/wiki/Python#poetry)
  94. needs to be added to `~/.config/direnv/direnvrc` (or more generally `$XDG_CONFIG_HOME/direnv/direnvrc`).
  95. 3. Mark the synapse checkout as a poetry project: `echo layout poetry > .envrc`.
  96. 4. Convince yourself that you trust this `.envrc` configuration and project.
  97. Then formally confirm this to `direnv` by running `direnv allow`.
  98. Then whenever you navigate to the synapse checkout, you should be able to run
  99. e.g. `mypy` instead of `poetry run mypy`; `python` instead of
  100. `poetry run python`; and your shell commands will automatically run in the
  101. context of poetry's venv, without having to run `poetry shell` beforehand.
  102. # How do I...
  103. ## ...reset my venv to the locked environment?
  104. ```shell
  105. poetry install --all-extras --sync
  106. ```
  107. ## ...delete everything and start over from scratch?
  108. ```shell
  109. # Stop the current virtualenv if active
  110. $ deactivate
  111. # Remove all of the files from the current environment.
  112. # Don't worry, even though it says "all", this will only
  113. # remove the Poetry virtualenvs for the current project.
  114. $ poetry env remove --all
  115. # Reactivate Poetry shell to create the virtualenv again
  116. $ poetry shell
  117. # Install everything again
  118. $ poetry install --extras all
  119. ```
  120. ## ...run a command in the `poetry` virtualenv?
  121. Use `poetry run cmd args` when you need the python virtualenv context.
  122. To avoid typing `poetry run` all the time, you can run `poetry shell`
  123. to start a new shell in the poetry virtualenv context. Within `poetry shell`,
  124. `python`, `pip`, `mypy`, `trial`, etc. are all run inside the project virtualenv
  125. and isolated from the rest o the system.
  126. Roughly speaking, the translation from a traditional virtualenv is:
  127. - `env/bin/activate` -> `poetry shell`, and
  128. - `deactivate` -> close the terminal (Ctrl-D, `exit`, etc.)
  129. See also the direnv recommendation above, which makes `poetry run` and
  130. `poetry shell` unnecessary.
  131. ## ...inspect the `poetry` virtualenv?
  132. Some suggestions:
  133. ```shell
  134. # Current env only
  135. poetry env info
  136. # All envs: this allows you to have e.g. a poetry managed venv for Python 3.7,
  137. # and another for Python 3.10.
  138. poetry env list --full-path
  139. poetry run pip list
  140. ```
  141. Note that `poetry show` describes the abstract *lock file* rather than your
  142. on-disk environment. With that said, `poetry show --tree` can sometimes be
  143. useful.
  144. ## ...add a new dependency?
  145. Either:
  146. - manually update `pyproject.toml`; then `poetry lock --no-update`; or else
  147. - `poetry add packagename`. See `poetry add --help`; note the `--dev`,
  148. `--extras` and `--optional` flags in particular.
  149. Include the updated `pyproject.toml` and `poetry.lock` files in your commit.
  150. ## ...remove a dependency?
  151. This is not done often and is untested, but
  152. ```shell
  153. poetry remove packagename
  154. ```
  155. ought to do the trick. Alternatively, manually update `pyproject.toml` and
  156. `poetry lock --no-update`. Include the updated `pyproject.toml` and `poetry.lock`
  157. files in your commit.
  158. ## ...update the version range for an existing dependency?
  159. Best done by manually editing `pyproject.toml`, then `poetry lock --no-update`.
  160. Include the updated `pyproject.toml` and `poetry.lock` in your commit.
  161. ## ...update a dependency in the locked environment?
  162. Use
  163. ```shell
  164. poetry update packagename
  165. ```
  166. to use the latest version of `packagename` in the locked environment, without
  167. affecting the broad dependencies listed in the wheel.
  168. There doesn't seem to be a way to do this whilst locking a _specific_ version of
  169. `packagename`. We can workaround this (crudely) as follows:
  170. ```shell
  171. poetry add packagename==1.2.3
  172. # This should update pyproject.lock.
  173. # Now undo the changes to pyproject.toml. For example
  174. # git restore pyproject.toml
  175. # Get poetry to recompute the content-hash of pyproject.toml without changing
  176. # the locked package versions.
  177. poetry lock --no-update
  178. ```
  179. Either way, include the updated `poetry.lock` file in your commit.
  180. ## ...export a `requirements.txt` file?
  181. ```shell
  182. poetry export --extras all
  183. ```
  184. Be wary of bugs in `poetry export` and `pip install -r requirements.txt`.
  185. ## ...build a test wheel?
  186. I usually use
  187. ```shell
  188. poetry run pip install build && poetry run python -m build
  189. ```
  190. because [`build`](https://github.com/pypa/build) is a standardish tool which
  191. doesn't require poetry. (It's what we use in CI too). However, you could try
  192. `poetry build` too.
  193. # Troubleshooting
  194. ## Check the version of poetry with `poetry --version`.
  195. The minimum version of poetry supported by Synapse is 1.3.2.
  196. It can also be useful to check the version of `poetry-core` in use. If you've
  197. installed `poetry` with `pipx`, try `pipx runpip poetry list | grep
  198. poetry-core`.
  199. ## Clear caches: `poetry cache clear --all pypi`.
  200. Poetry caches a bunch of information about packages that isn't readily available
  201. from PyPI. (This is what makes poetry seem slow when doing the first
  202. `poetry install`.) Try `poetry cache list` and `poetry cache clear --all
  203. <name of cache>` to see if that fixes things.
  204. ## Remove outdated egg-info
  205. Delete the `matrix_synapse.egg-info/` directory from the root of your Synapse
  206. install.
  207. This stores some cached information about dependencies and often conflicts with
  208. letting Poetry do the right thing.
  209. ## Try `--verbose` or `--dry-run` arguments.
  210. Sometimes useful to see what poetry's internal logic is.