Title: Messed plugin config list order · Issue #677 · python-lsp/python-lsp-server · GitHub
Open Graph Title: Messed plugin config list order · Issue #677 · python-lsp/python-lsp-server
X Title: Messed plugin config list order · Issue #677 · python-lsp/python-lsp-server
Description: Hi! TL;DR I think that merge_dicts implementation in _utils.py is wrong: def merge_dicts(dict_a, dict_b): """Recursively merge dictionary b into dictionary a. If override_nones is True, then """ def _merge_dicts_(a, b): for key in set(a....
Open Graph Description: Hi! TL;DR I think that merge_dicts implementation in _utils.py is wrong: def merge_dicts(dict_a, dict_b): """Recursively merge dictionary b into dictionary a. If override_nones is True, then """ de...
X Description: Hi! TL;DR I think that merge_dicts implementation in _utils.py is wrong: def merge_dicts(dict_a, dict_b): """Recursively merge dictionary b into dictionary a. If override_nones is Tr...
Opengraph URL: https://github.com/python-lsp/python-lsp-server/issues/677
X: @github
Domain: patch-diff.githubusercontent.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Messed plugin config list order","articleBody":"Hi!\n\n**TL;DR**\n\nI think that merge_dicts implementation in `_utils.py` is wrong:\n\n```py\ndef merge_dicts(dict_a, dict_b):\n \"\"\"Recursively merge dictionary b into dictionary a.\n\n If override_nones is True, then\n \"\"\"\n\n def _merge_dicts_(a, b):\n for key in set(a.keys()).union(b.keys()):\n if key in a and key in b:\n if isinstance(a[key], dict) and isinstance(b[key], dict):\n yield (key, dict(_merge_dicts_(a[key], b[key])))\n elif isinstance(a[key], list) and isinstance(b[key], list):\n yield (key, list(set(a[key] + b[key]))) # \u003c--- this line here is wrong\n elif b[key] is not None:\n yield (key, b[key])\n else:\n yield (key, a[key])\n elif key in a:\n yield (key, a[key])\n elif b[key] is not None:\n yield (key, b[key])\n\n return dict(_merge_dicts_(dict_a, dict_b))\n```\n\nBy converting the list to a set, it not only gets rid of the duplicates, but also messes up the list order unpredictably. Replacing `set()` with `OrderedDict.fromkeys()` seems to fix the issue described in detail below.\n\n----\n\nI'm using pylsp v1.13.1 with pylsp_mypy v0.7.0. (The whole LSP is run by Emacs v30.1 via eglot, but this probably doesn't matter.) And I noticed the following strange behavior, which I can't really put my finger on yet.\n\nWhen pylsp invokes pylsp_mypy, it uses the following configuration:\n\n```\n[stderr] 2025-10-08 12:21:46,942 CEST - INFO - pylsp.config.config - Updated settings to {'plugins': {'jedi': {'environment': '/home/david/pyproj/.venv'}, 'ruff': {'executable': '/home/david/pyproj/.venv/bin/ruff'}, 'pylsp_mypy': {'overrides': ['--python-executable', '/home/david/pyproj/.venv/bin/python', True]}, 'flake8': {'executable': None}, 'pylint': {'executable': None}}}\n```\n\nNote the `overrides` list. The next log line after this, however, is this:\n\n```\n[stderr] 2025-10-08 12:21:47,475 CEST - INFO - pylsp_mypy.plugin - lint settings = {'overrides': [True, '/home/david/pyproj/.venv/bin/python', '--python-executable']} document.path = /home/david/pyproj/src/pyproj/__init__.py is_saved = False\n```\n\nNote how the `overrides` list is in reverse order. And right enough, mypy refuses to run:\n\n```\nerror: argument --python-executable: expected one **argument**\n```\n\nI also noticed that it's not always reverse, the order seems to be pretty random. Sometimes it's ok and works, other times, it's messed up.","author":{"url":"https://github.com/dhanak","@type":"Person","name":"dhanak"},"datePublished":"2025-10-08T10:51:16.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/677/python-lsp-server/issues/677"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:52cba5d9-7c0e-7b51-f176-85173c133cd0 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 8292:A2119:20604E1:2BEE0D8:6970861F |
| html-safe-nonce | 7f7a2e11cfa65b6c6ec897f6440f833cb686ea21f7ffb99b918cc26e01a0f6f6 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4MjkyOkEyMTE5OjIwNjA0RTE6MkJFRTBEODo2OTcwODYxRiIsInZpc2l0b3JfaWQiOiI2MjY3Njc5NzExMzU4Mzg3NzQzIiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 41e21f6cf28981547b90f4973e22e783a8e5aa61e676433034f4ec32bad1257e |
| hovercard-subject-tag | issue:3494920635 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/python-lsp/python-lsp-server/677/issue_layout |
| twitter:image | https://opengraph.githubassets.com/382f1c55a6424df154e4ae73d4127723cdd5de76c96dbeac1cc2d7ab345411a9/python-lsp/python-lsp-server/issues/677 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/382f1c55a6424df154e4ae73d4127723cdd5de76c96dbeac1cc2d7ab345411a9/python-lsp/python-lsp-server/issues/677 |
| og:image:alt | Hi! TL;DR I think that merge_dicts implementation in _utils.py is wrong: def merge_dicts(dict_a, dict_b): """Recursively merge dictionary b into dictionary a. If override_nones is True, then """ de... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | dhanak |
| hostname | github.com |
| expected-hostname | github.com |
| None | 9920a62ba22d06470388e2904804fb7e5ec51c9e35f81784e9191394c74b2bd2 |
| turbo-cache-control | no-preview |
| go-import | github.com/python-lsp/python-lsp-server git https://github.com/python-lsp/python-lsp-server.git |
| octolytics-dimension-user_id | 51609341 |
| octolytics-dimension-user_login | python-lsp |
| octolytics-dimension-repository_id | 341006790 |
| octolytics-dimension-repository_nwo | python-lsp/python-lsp-server |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 341006790 |
| octolytics-dimension-repository_network_root_nwo | python-lsp/python-lsp-server |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 7d6181066430cc06553c8396ca201e194ae33cb9 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width