Compare commits
640 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 24409c7c15 | |||
| 7e0dd52782 | |||
| 7f77f1386c | |||
| 220af1df54 | |||
| 03ba8c83be | |||
| 18a045ba69 | |||
| 42dc734f85 | |||
| 4398548556 | |||
| 1235316645 | |||
| aca3a117a9 | |||
| 382c9e9acd | |||
| 3d5242da14 | |||
| dedd4cd061 | |||
| a2b6ee7b00 | |||
| 3f30bdca19 | |||
| f4419438c1 | |||
| 3a5edde170 | |||
| 9d2d3bdee6 | |||
| 42374f46f5 | |||
| ce8f7d118c | |||
| 7ba0336010 | |||
| 7c59528d54 | |||
| 04b00974e5 | |||
| 80bf6da876 | |||
| aedc5b04b9 | |||
| d20c92d2c2 | |||
| e91a929049 | |||
| 7cd167f607 | |||
| d39ca69d0b | |||
| ba5d52f50c | |||
| 08d6aa3340 | |||
| 0a9bcc0313 | |||
| 9d2dab8e7c | |||
| fa0fc4acce | |||
| 54f2ebbea5 | |||
| e808df9e2e | |||
| 8881bd77d4 | |||
| 860ed0e636 | |||
| 614f6ccb48 | |||
| f825db3d6c | |||
| 1bfb991453 | |||
| b1ed0e044c | |||
| e3b00708bd | |||
| d45a0af44d | |||
| 302491c81e | |||
| 7335ab609e | |||
| 4ff9477996 | |||
| 4cee2a6330 | |||
| dc6e607edc | |||
| 771d2041cd | |||
| 7b83ac8675 | |||
| 42f8b22ce2 | |||
| 3ba5d67cb8 | |||
| 474a964fb7 | |||
| a794ad17c2 | |||
| 8674fa3bc9 | |||
| 27c2386b3d | |||
| c37cd85d8a | |||
| 5f814b2838 | |||
| ee5377c9aa | |||
| ea2d01366c | |||
| f986db2adb | |||
| 36383d48f3 | |||
| fd1b6334f0 | |||
| 895c12e421 | |||
| d4c9fa5dbd | |||
| b9bbbebc06 | |||
| 2207fa50b4 | |||
| fdc1063782 | |||
| 0203dcfe46 | |||
| 20e10495f6 | |||
| f1b625ad56 | |||
| 3d92936c14 | |||
| c5ba1d67b1 | |||
| ed2f158a38 | |||
| 3bf466698f | |||
| 6705391eec | |||
| 2b9f741f3a | |||
| 7977b7f6a8 | |||
| d1e6333f12 | |||
| 20d4418485 | |||
| 8e8a00829f | |||
| 5cc41f1b05 | |||
| 99e7fb1d52 | |||
| 1a776f6710 | |||
| 717a5c085a | |||
| 59544678a1 | |||
| a9c146ac53 | |||
| dbceb00688 | |||
| cb79a2b785 | |||
| 098e964bbc | |||
| e147f067c5 | |||
| ce06cb59c3 | |||
| 2a93005a7b | |||
| 9d3b04e749 | |||
| 459d2ae157 | |||
| 2bb023ba2b | |||
| d8c1a21492 | |||
| c59b9711f3 | |||
| 27e6e45349 | |||
| 2aacb49446 | |||
| 6ce0dc0620 | |||
| ebbd37b66d | |||
| 4cb6ffdf46 | |||
| b9a0f7983d | |||
| ba3a888a05 | |||
| a8340692ab | |||
| 2cc4c33d0a | |||
| bb0a4bd6d5 | |||
| 6227858482 | |||
| 361ffb8f0a | |||
| c9fb203ce0 | |||
| 003c2ab629 | |||
| 97ef48b07e | |||
| dffdda5f89 | |||
| 4d03ad5255 | |||
| 92bbe0ebe1 | |||
| ca2e5b2060 | |||
| 3cd1277a7b | |||
| 619cc1d230 | |||
| ac646fed06 | |||
| fe3466a8ad | |||
| 2f4fe530a1 | |||
| 18572fddcc | |||
| 40e92b224c | |||
| 53d7d92511 | |||
| 0dcc40e81a | |||
| 11dda97e86 | |||
| 1d3a9fe6e5 | |||
| 12c92eec18 | |||
| c0121d69e7 | |||
| cc07548d71 | |||
| 8a2be4f799 | |||
| fc8a586ab9 | |||
| 7eea8de9de | |||
| cfddd43bfb | |||
| d70eefc313 | |||
| b4bdab8e52 | |||
| b18d18d03b | |||
| 077d441d42 | |||
| ffe17e47ce | |||
| f271b32bea | |||
| 5575b5cb43 | |||
| 0bbdca814a | |||
| c80e8b98de | |||
| a0e48edd2f | |||
| 6f4c860c56 | |||
| 89013b99bd | |||
| a4b573ee48 | |||
| f6e1e48dcf | |||
| fc8bab8291 | |||
| efc5febc33 | |||
| 68d7704d19 | |||
| f36457dce2 | |||
| 09361655d2 | |||
| a027b65c8e | |||
| d2fcfef679 | |||
| 2d83d19061 | |||
| 6942377f90 | |||
| 019ed44b84 | |||
| b0b8110acd | |||
| ab717bceb5 | |||
| 6157f02067 | |||
| 7865bc1dfb | |||
| e11cd368b7 | |||
| a8f48246b6 | |||
| 696e7df2e8 | |||
| 9979fef4fe | |||
| 9773abf3d8 | |||
| 7b5022ee2e | |||
| f21a46216f | |||
| a468d19fd6 | |||
| edac710bc0 | |||
| bf4ad8070e | |||
| 118e789e0c | |||
| 77866a5f5f | |||
| 053c8c019f | |||
| fbcc8703dc | |||
| f46b864748 | |||
| 2b8650547f | |||
| f36a060d2c | |||
| ea74189a90 | |||
| 05ec9e8d37 | |||
| 6557eba7dc | |||
| d8953b34f2 | |||
| 56c0c711c1 | |||
| 61a970e25f | |||
| 50c242fa29 | |||
| 8965291b87 | |||
| 81c7b0515d | |||
| fdbccc1e74 | |||
| 0e56f02d5d | |||
| c7934aee2c | |||
| 5d405f7e7a | |||
| 5054eb291e | |||
| 47d2b49e2b | |||
| 1f507c2515 | |||
| 5ee8a1c50a | |||
| 7b7831bb63 | |||
| a4aa56a0eb | |||
| fa0f99e4f2 | |||
| 844b3e3f65 | |||
| 3f6530ed55 | |||
| 25757a3d47 | |||
| 6e774373c2 | |||
| 512e313f18 | |||
| a574751a87 | |||
| bde75f5f66 | |||
| e33124a642 | |||
| bed4fa29fd | |||
| f5ab7d8306 | |||
| 029c9ef967 | |||
| e6b27b480c | |||
| 43dc637136 | |||
| 00c62b9d07 | |||
| 82a91208d6 | |||
| 91fd180be1 | |||
| fb4a4f9f15 | |||
| 5a4f09228d | |||
| 97d12dcf56 | |||
| f4f65f4e99 | |||
| 863359a04f | |||
| 33a349df91 | |||
| a41b1dc49f | |||
| 16744644f6 | |||
| dbf46f3891 | |||
| 52984e9e69 | |||
| ce2107eee1 | |||
| 8373e6254f | |||
| 1ff3bc332a | |||
| 172ddb3b45 | |||
| d60af9305a | |||
| bcb6b243e9 | |||
| 32457baa40 | |||
| ab4cd7f802 | |||
| e9b5e642c3 | |||
| 9250ee8650 | |||
| bdbe2b12c2 | |||
| 43bcb1e54e | |||
| cd2f831b9d | |||
| 4b43332131 | |||
| 77daff166d | |||
| 5ccc0785c1 | |||
| b0a463f758 | |||
| 8a8d01d732 | |||
| 1c22954668 | |||
| e675bbcc49 | |||
| 607367aeb1 | |||
| ac6c5f198e | |||
| db13a8607e | |||
| cfb3096e33 | |||
| 7b6f857aa9 | |||
| 9eea5c43af | |||
| 104c490274 | |||
| bbb7ca1f15 | |||
| 27ef82d972 | |||
| 9df3a8a19f | |||
| 5c4b13cd8f | |||
| d74e8f2875 | |||
| cc23d81a74 | |||
| 505ea932f5 | |||
| 5f224a4794 | |||
| 3f488bfded | |||
| b4c1cfacc2 | |||
| afe4c4e02e | |||
| 527f73d902 | |||
| 82fec809a5 | |||
| b2ccc72a00 | |||
| be777e325d | |||
| 25880bd441 | |||
| cc86fbc9ad | |||
| bd30967bd7 | |||
| 8fed03aa3e | |||
| ba66e3dfef | |||
| 199ab854d6 | |||
| c16bb06d25 | |||
| d06f4ab693 | |||
| 8ba1086801 | |||
| fea4b1d6ad | |||
| ae91d50100 | |||
| 0d29112624 | |||
| d6c7550cf5 | |||
| 4cf4c7dc99 | |||
| 6fdf5ef66b | |||
| d4220574a2 | |||
| 1a9c8d5ee9 | |||
| 407dbf8574 | |||
| 8beca8e21f | |||
| cf92310da2 | |||
| 89f795fe8a | |||
| 1c347c84bf | |||
| 0d8fb99cdf | |||
| b3a9ad124c | |||
| a902468354 | |||
| 84639b32ae | |||
| dac5d5ae42 | |||
| 6bd2a39a7d | |||
| 309e30bae3 | |||
| 7ff7a415d1 | |||
| 6610343332 | |||
| 5adad58d95 | |||
| d7c7f64f17 | |||
| c4c84b67d5 | |||
| 617541c4c6 | |||
| 4d3ebc3620 | |||
| ae4f6140f1 | |||
| 323cfe3efb | |||
| b0d2add89d | |||
| ff20747703 | |||
| 9192f1b9dd | |||
| 89d178e8e7 | |||
| 1c24e35e85 | |||
| 5debf3071c | |||
| e9bd4bb388 | |||
| 649e48a799 | |||
| 9b0157686b | |||
| 8288218b29 | |||
| da5e6eea45 | |||
| 2fdfa96ee6 | |||
| fb3f1fb5c0 | |||
| 9b8212d256 | |||
| aead826d2d | |||
| 4cd2a4ae3a | |||
| 66cd243e6f | |||
| 7b66a1f0d9 | |||
| 059e91bdce | |||
| f86962cb6b | |||
| 03c694bb08 | |||
| 08d68c5296 | |||
| 568461b5ec | |||
| 6b73b8b70c | |||
| 936686ed2d | |||
| 74050d0c1c | |||
| 69111a8b2a | |||
| d840ee5bde | |||
| e3d811e85d | |||
| 578ad9fc48 | |||
| 9dbe34f0d0 | |||
| 93a0751302 | |||
| bc936b5657 | |||
| d6eae548a7 | |||
| e439438b9b | |||
| f8a1e0d1d2 | |||
| 8a29def84a | |||
| 77a166577a | |||
| 7d5268d37c | |||
| c854d255e5 | |||
| c660962d4d | |||
| 767bef0033 | |||
| 4d02bfd6e1 | |||
| a099ab7d38 | |||
| ce72a9ccdb | |||
| bace86ed15 | |||
| 45bf455948 | |||
| 859663565c | |||
| 0876a5b641 | |||
| 5b5314ee41 | |||
| aff9189149 | |||
| 2eda49a8db | |||
| 96b17d4e4f | |||
| aadc131dc1 | |||
| 0a522121a0 | |||
| 0b5e2c8093 | |||
| c665d154a2 | |||
| 31295b5a60 | |||
| aebe20c452 | |||
| 508e0f9310 | |||
| e04e7e830e | |||
| 5407e69732 | |||
| 2c59eb368c | |||
| 6d1a3e2bdd | |||
| 7fa4586e36 | |||
| 33b4aa8d99 | |||
| 627cf5def8 | |||
| b409d51dee | |||
| 4a4e620f30 | |||
| 28889d8da5 | |||
| 15b2a50817 | |||
| b852a8247d | |||
| 7b55cca011 | |||
| a9577ab1f4 | |||
| cb217d5d60 | |||
| f4f5355bcf | |||
| 23bb2713d2 | |||
| b2471e1109 | |||
| 610219d53d | |||
| b464afe283 | |||
| 7657ad3ced | |||
| 721086a291 | |||
| 6e6b53ed3a | |||
| 601b50672d | |||
| a7af389da0 | |||
| 99db0d76fd | |||
| 561b0f79bc | |||
| 8569f3cdef | |||
| 7b61e6f5d6 | |||
| 05241b3031 | |||
| e01026d84d | |||
| ee91c69ef7 | |||
| e0eef47315 | |||
| 44d2ca2990 | |||
| 9240622c1a | |||
| 0dbba85e95 | |||
| 1ceeccb769 | |||
| 39883e85bd | |||
| 68f53b7a0e | |||
| e679b008ff | |||
| e80a5b7492 | |||
| b272e7345f | |||
| a81e0233e9 | |||
| 80898481ab | |||
| 9d4c716d85 | |||
| d90b0946ed | |||
| 8d5762b0dc | |||
| a7efbc5416 | |||
| be362cb8f8 | |||
| 873ff9522b | |||
| c1ee2999a0 | |||
| 9b2b386f76 | |||
| 65fe31786d | |||
| 70b6d1dfd6 | |||
| ee62aed72e | |||
| c02f26319d | |||
| fdd182870c | |||
| 4102cb220a | |||
| 5299707329 | |||
| 43e01be158 | |||
| 589e080c6b | |||
| 24e48bc9ff | |||
| 576b62a6a3 | |||
| ad2ba70959 | |||
| a330505025 | |||
| 67b73fd147 | |||
| c08e4dbadc | |||
| 6dbd498772 | |||
| 03b09b32d6 | |||
| 8f1711da0e | |||
| 6fb6c98f71 | |||
| aad993f24d | |||
| 544e101c24 | |||
| 8699f380f0 | |||
| e91a68ef3a | |||
| 9f5048c198 | |||
| b3c40ba58a | |||
| 8d69193a42 | |||
| bbcd19f2d0 | |||
| 3cd598135f | |||
| 1c8f2c34ff | |||
| ca03f90ee7 | |||
| 9feee29d76 | |||
| e7dcee13da | |||
| 7467738834 | |||
| d75fb8ae22 | |||
| ae25a8efef | |||
| fc5be50d56 | |||
| aadba440da | |||
| ec94d6a590 | |||
| 42ce90c3f7 | |||
| 8467756dc1 | |||
| 613b443ff0 | |||
| 233b61ac61 | |||
| f41c9d37d6 | |||
| 1048e2ca6a | |||
| ce0ce1add3 | |||
| b0bf1ea7bd | |||
| 2561b628af | |||
| 73c6630718 | |||
| a189bb03ab | |||
| 404a2d70be | |||
| ed8ccc3737 | |||
| 18b1a92162 | |||
| 199aa72d35 | |||
| 8f7dbbc14a | |||
| 27dbc9ac42 | |||
| e9aa401994 | |||
| 9e9572c79e | |||
| c7285607a3 | |||
| a6e2546980 | |||
| dc510e0e43 | |||
| ed12338f35 | |||
| bf3f8b8855 | |||
| 67acd1aa1b | |||
| 75c924430e | |||
| 6087c53830 | |||
| b50fe65a22 | |||
| 17009e689b | |||
| 5d2f755d3f | |||
| 8d7c0264bc | |||
| 000d230901 | |||
| eb0334b07c | |||
| 4d07dc0d18 | |||
| 0ea52872ab | |||
| 6868d53fe9 | |||
| 68af15637b | |||
| 4da63d9f6f | |||
| 085d69b0bd | |||
| 776fe6c184 | |||
| 0e07d2c7d5 | |||
| 90ec885805 | |||
| 5a28154c4d | |||
| 2fcb51e703 | |||
| 26f524872f | |||
| 88af0317a2 | |||
| c10c71e70d | |||
| 93555af5c9 | |||
| 06622e4110 | |||
| 155efa9e36 | |||
| 3175edc5d8 | |||
| d95252c01f | |||
| 5bd2e2c31d | |||
| 84528e4fb2 | |||
| e4381ed514 | |||
| d9235b9e29 | |||
| ce5f3b1ba5 | |||
| 7b5c04312e | |||
| f5bafd70f4 | |||
| d97c3a6ce6 | |||
| 341c35614a | |||
| fecf28319c | |||
| 345d8cfb69 | |||
| b60d005156 | |||
| 6c232a69df | |||
| e97c1df30c | |||
| decb5698b3 | |||
| 62962e30e4 | |||
| 05413d4e20 | |||
| ca46dcf683 | |||
| d351be1567 | |||
| c7f2eaf4f4 | |||
| 53d25116df | |||
| 08e25ffa0c | |||
| 1c148e442b | |||
| acaca1b4e9 | |||
| 4777836b83 | |||
| 7da659dd6d | |||
| 77dfe51aba | |||
| ef7865e2f2 | |||
| 5cb15c0443 | |||
| b43172ffbc | |||
| b4796d1814 | |||
| 482d06774a | |||
| 046d731fbd | |||
| 892f6c98ec | |||
| 7fafa2d954 | |||
| 1d63046542 | |||
| 4c238a9a91 | |||
| 002db39a36 | |||
| c4074e4ab6 | |||
| 7960e814e5 | |||
| 080025e533 | |||
| 9accd63a38 | |||
| 3dd704ee9a | |||
| 28e28a1974 | |||
| b699178aa1 | |||
| c08c649fa1 | |||
| 5c0c4b4079 | |||
| b55cdfaa31 | |||
| 34406cf22c | |||
| f91aefd245 | |||
| f8281f42c8 | |||
| 7171bdf279 | |||
| 9f2d14ee26 | |||
| ead471e72d | |||
| 9a4011de46 | |||
| 33551be61b | |||
| eeb29d99fd | |||
| 1a0c407e6b | |||
| c4b37cbf18 | |||
| 7fa156af80 | |||
| 78825f4f1c | |||
| 6e15b5debe | |||
| 2e0d2879d0 | |||
| 128043072b | |||
| b2fda9d20e | |||
| 3c8c5eabc2 | |||
| 2da2041e2e | |||
| b5eef203f4 | |||
| df73da691f | |||
| 30d054e0bb | |||
| ebb3cc4ab6 | |||
| 17201abd53 | |||
| 2f141f4c41 | |||
| 638c0bf49b | |||
| d1065e6f51 | |||
| 567863127a | |||
| f5abc10724 | |||
| bb795b56da | |||
| 4dd0604f61 | |||
| c05d278ba0 | |||
| 49a3163958 | |||
| 1a568041fa | |||
| c9db8b0c32 | |||
| aa1bf10b91 | |||
| 5222907bea | |||
| e1eb147f2a | |||
| e43eb47c5f | |||
| 27eb4c45cd | |||
| b136d7ff8f | |||
| 9e56e1ab30 | |||
| 742f757337 | |||
| 2f5dfe299c | |||
| e4eec87c6a | |||
| f793ff4571 | |||
| 195aae2f16 | |||
| 7c79f2cb72 | |||
| f04e35c170 | |||
| 36bbac05bd | |||
| e2a4b7681e | |||
| 957944eee4 | |||
| bf425e533e | |||
| ca21957b8a | |||
| 6a95270671 | |||
| 82781f5838 | |||
| aae6d3ff69 | |||
| 9175225adf | |||
| 7a32fa0101 | |||
| d46450195b | |||
| c0128c1021 | |||
| 3320b7c9a4 | |||
| 4c22c9b0b6 | |||
| 6d6ea1bb40 | |||
| 9e38981ae4 | |||
| 463e7c2709 | |||
| ce9d0b1d0c | |||
| 80786d5caf | |||
| e18378c3e2 | |||
| 0ca2857baa | |||
| e21c312e16 | |||
| 1031bd25f8 | |||
| fae708c0e8 | |||
| 8f8ea91eef | |||
| 7a1406d144 | |||
| 6373874833 | |||
| a79823e64b | |||
| 1766a5fdc0 | |||
| e6b1ea3eb2 | |||
| e5537cf983 | |||
| 43bb12e640 | |||
| 66dcbf47a3 | |||
| a285fe05fd |
@@ -46,7 +46,7 @@ if not IS_PR:
|
||||
"database": "sqlite",
|
||||
"extras": "all",
|
||||
}
|
||||
for version in ("3.8", "3.9", "3.10", "3.11")
|
||||
for version in ("3.8", "3.9", "3.10")
|
||||
)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ trial_postgres_tests = [
|
||||
{
|
||||
"python-version": "3.7",
|
||||
"database": "postgres",
|
||||
"postgres-version": "11",
|
||||
"postgres-version": "10",
|
||||
"extras": "all",
|
||||
}
|
||||
]
|
||||
@@ -62,7 +62,7 @@ trial_postgres_tests = [
|
||||
if not IS_PR:
|
||||
trial_postgres_tests.append(
|
||||
{
|
||||
"python-version": "3.11",
|
||||
"python-version": "3.10",
|
||||
"database": "postgres",
|
||||
"postgres-version": "14",
|
||||
"extras": "all",
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
name: Deploy documentation PR preview
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [ "Prepare documentation PR preview" ]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
netlify:
|
||||
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# There's a 'download artifact' action, but it hasn't been updated for the workflow_run action
|
||||
# (https://github.com/actions/download-artifact/issues/60) so instead we get this mess:
|
||||
- name: 📥 Download artifact
|
||||
uses: dawidd6/action-download-artifact@46b4ae883bf0726f5949d025d31cb62c7a5ac70c # v2.24.0
|
||||
with:
|
||||
workflow: docs-pr.yaml
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
name: book
|
||||
path: book
|
||||
|
||||
- name: 📤 Deploy to Netlify
|
||||
uses: matrix-org/netlify-pr-preview@v1
|
||||
with:
|
||||
path: book
|
||||
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
|
||||
branch: ${{ github.event.workflow_run.head_branch }}
|
||||
revision: ${{ github.event.workflow_run.head_sha }}
|
||||
token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
site_id: ${{ secrets.NETLIFY_SITE_ID }}
|
||||
desc: Documentation preview
|
||||
deployment_env: PR Documentation Preview
|
||||
@@ -1,34 +0,0 @@
|
||||
name: Prepare documentation PR preview
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- docs/**
|
||||
|
||||
jobs:
|
||||
pages:
|
||||
name: GitHub Pages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup mdbook
|
||||
uses: peaceiris/actions-mdbook@adeb05db28a0c0004681db83893d56c0388ea9ea # v1.2.0
|
||||
with:
|
||||
mdbook-version: '0.4.17'
|
||||
|
||||
- name: Build the documentation
|
||||
# mdbook will only create an index.html if we're including docs/README.md in SUMMARY.md.
|
||||
# However, we're using docs/README.md for other purposes and need to pick a new page
|
||||
# as the default. Let's opt for the welcome page instead.
|
||||
run: |
|
||||
mdbook build
|
||||
cp book/welcome_and_overview.html book/index.html
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: book
|
||||
path: book
|
||||
# We'll only use this in a workflow_run, then we're done with it
|
||||
retention-days: 1
|
||||
@@ -27,9 +27,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# The dev dependencies aren't exposed in the wheel metadata (at least with current
|
||||
@@ -61,9 +62,10 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- run: sudo apt-get -qq install xmlsec1
|
||||
@@ -134,9 +136,10 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Ensure sytest runs `pip install`
|
||||
|
||||
+18
-40
@@ -33,8 +33,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- uses: matrix-org/setup-python-poetry@v1
|
||||
with:
|
||||
extras: "all"
|
||||
@@ -46,8 +44,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- run: "pip install 'click==8.1.1' 'GitPython>=3.1.20'"
|
||||
- run: scripts-dev/check_schema_delta.py --force-colors
|
||||
|
||||
@@ -72,8 +68,6 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- run: "pip install 'towncrier>=18.6.0rc1'"
|
||||
- run: scripts-dev/check-newsfragment.sh
|
||||
env:
|
||||
@@ -99,12 +93,10 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
# There don't seem to be versioned releases of this action per se: for each rust
|
||||
# version there is a branch which gets constantly rebased on top of master.
|
||||
# We pin to a specific commit for paranoia's sake.
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.58.1
|
||||
override: true
|
||||
components: clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
@@ -119,13 +111,11 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
# There don't seem to be versioned releases of this action per se: for each rust
|
||||
# version there is a branch which gets constantly rebased on top of master.
|
||||
# We pin to a specific commit for paranoia's sake.
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.58.1
|
||||
components: rustfmt
|
||||
toolchain: 1.58.1
|
||||
override: true
|
||||
components: rustfmt
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- run: cargo fmt --check
|
||||
@@ -153,8 +143,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- id: get-matrix
|
||||
run: .ci/scripts/calculate_jobs.py
|
||||
outputs:
|
||||
@@ -181,12 +169,10 @@ jobs:
|
||||
postgres:${{ matrix.job.postgres-version }}
|
||||
|
||||
- name: Install Rust
|
||||
# There don't seem to be versioned releases of this action per se: for each rust
|
||||
# version there is a branch which gets constantly rebased on top of master.
|
||||
# We pin to a specific commit for paranoia's sake.
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.58.1
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- uses: matrix-org/setup-python-poetry@v1
|
||||
@@ -225,12 +211,10 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
# There don't seem to be versioned releases of this action per se: for each rust
|
||||
# version there is a branch which gets constantly rebased on top of master.
|
||||
# We pin to a specific commit for paranoia's sake.
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.58.1
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# There aren't wheels for some of the older deps, so we need to install
|
||||
@@ -343,12 +327,10 @@ jobs:
|
||||
run: cat sytest-blacklist .ci/worker-blacklist > synapse-blacklist-with-workers
|
||||
|
||||
- name: Install Rust
|
||||
# There don't seem to be versioned releases of this action per se: for each rust
|
||||
# version there is a branch which gets constantly rebased on top of master.
|
||||
# We pin to a specific commit for paranoia's sake.
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.58.1
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Run SyTest
|
||||
@@ -409,9 +391,9 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- python-version: "3.7"
|
||||
postgres-version: "11"
|
||||
postgres-version: "10"
|
||||
|
||||
- python-version: "3.11"
|
||||
- python-version: "3.10"
|
||||
postgres-version: "14"
|
||||
|
||||
services:
|
||||
@@ -477,12 +459,10 @@ jobs:
|
||||
path: synapse
|
||||
|
||||
- name: Install Rust
|
||||
# There don't seem to be versioned releases of this action per se: for each rust
|
||||
# version there is a branch which gets constantly rebased on top of master.
|
||||
# We pin to a specific commit for paranoia's sake.
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.58.1
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Prepare Complement's Prerequisites
|
||||
@@ -505,12 +485,10 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
# There don't seem to be versioned releases of this action per se: for each rust
|
||||
# version there is a branch which gets constantly rebased on top of master.
|
||||
# We pin to a specific commit for paranoia's sake.
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.58.1
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- run: cargo test
|
||||
|
||||
@@ -11,34 +11,34 @@ jobs:
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'X-Needs-Info')
|
||||
steps:
|
||||
- uses: actions/add-to-project@main
|
||||
id: add_project
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
with:
|
||||
project-url: "https://github.com/orgs/matrix-org/projects/67"
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
- name: Set status
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
run: |
|
||||
gh api graphql -f query='
|
||||
mutation(
|
||||
$project: ID!
|
||||
$item: ID!
|
||||
$fieldid: ID!
|
||||
$columnid: String!
|
||||
) {
|
||||
updateProjectV2ItemFieldValue(
|
||||
input: {
|
||||
projectId: $project
|
||||
itemId: $item
|
||||
fieldId: $fieldid
|
||||
value: {
|
||||
singleSelectOptionId: $columnid
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation {
|
||||
updateProjectV2ItemFieldValue(
|
||||
input: {
|
||||
projectId: $projectid
|
||||
itemId: $contentid
|
||||
fieldId: $fieldid
|
||||
value: {
|
||||
singleSelectOptionId: "Todo"
|
||||
}
|
||||
}
|
||||
) {
|
||||
projectV2Item {
|
||||
id
|
||||
}
|
||||
) {
|
||||
projectV2Item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}' -f project="PVT_kwDOAIB0Bs4AFDdZ" -f item=${{ steps.add_project.outputs.itemId }} -f fieldid="PVTSSF_lADOAIB0Bs4AFDdZzgC6ZA4" -f columnid=ba22e43c --silent
|
||||
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
fieldid: ${{ env.FIELD_ID }}
|
||||
optionid: ${{ env.OPTION_ID }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAIB0Bs4AFDdZ"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
FIELD_ID: "PVTSSF_lADOAIB0Bs4AFDdZzgC6ZA4"
|
||||
OPTION_ID: "ba22e43c"
|
||||
|
||||
@@ -18,9 +18,10 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- uses: matrix-org/setup-python-poetry@v1
|
||||
@@ -43,9 +44,10 @@ jobs:
|
||||
- run: sudo apt-get -qq install xmlsec1
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- uses: matrix-org/setup-python-poetry@v1
|
||||
@@ -82,9 +84,10 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Patch dependencies
|
||||
|
||||
+1
-35
@@ -1,37 +1,3 @@
|
||||
Synapse 1.71.0 (2022-11-08)
|
||||
===========================
|
||||
|
||||
Please note that, as announced in the release notes for Synapse 1.69.0, legacy Prometheus metric names are now disabled by default.
|
||||
They will be removed altogether in Synapse 1.73.0.
|
||||
If not already done, server administrators should update their dashboards and alerting rules to avoid using the deprecated metric names.
|
||||
See the [upgrade notes](https://matrix-org.github.io/synapse/v1.71/upgrade.html#upgrading-to-v1710) for more details.
|
||||
|
||||
**Note:** in line with our [deprecation policy](https://matrix-org.github.io/synapse/latest/deprecation_policy.html) for platform dependencies, this will be the last release to support PostgreSQL 10, which reaches upstream end-of-life on November 10th, 2022. Future releases of Synapse will require PostgreSQL 11+.
|
||||
|
||||
No significant changes since 1.71.0rc2.
|
||||
|
||||
|
||||
Synapse 1.71.0rc2 (2022-11-04)
|
||||
==============================
|
||||
|
||||
Improved Documentation
|
||||
----------------------
|
||||
|
||||
- Document the changes to monthly active user metrics due to deprecation of legacy Prometheus metric names. ([\#14358](https://github.com/matrix-org/synapse/issues/14358), [\#14360](https://github.com/matrix-org/synapse/issues/14360))
|
||||
|
||||
|
||||
Deprecations and Removals
|
||||
-------------------------
|
||||
|
||||
- Disable legacy Prometheus metric names by default. They can still be re-enabled for now, but they will be removed altogether in Synapse 1.73.0. ([\#14353](https://github.com/matrix-org/synapse/issues/14353))
|
||||
|
||||
|
||||
Internal Changes
|
||||
----------------
|
||||
|
||||
- Run unit tests against Python 3.11. ([\#13812](https://github.com/matrix-org/synapse/issues/13812))
|
||||
|
||||
|
||||
Synapse 1.71.0rc1 (2022-11-01)
|
||||
==============================
|
||||
|
||||
@@ -1438,7 +1404,7 @@ Internal Changes
|
||||
Synapse 1.59.1 (2022-05-18)
|
||||
===========================
|
||||
|
||||
This release fixes a long-standing issue which could prevent Synapse's user directory for updating properly.
|
||||
This release fixes a long-standing issue which could prevent Synapse's user directory from updating properly.
|
||||
|
||||
Bugfixes
|
||||
----------------
|
||||
|
||||
Generated
+12
-12
@@ -194,9 +194,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3"
|
||||
version = "0.17.3"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "268be0c73583c183f2b14052337465768c07726936a260f480f0857cb95ba543"
|
||||
checksum = "201b6887e5576bf2f945fe65172c1fcbf3fcf285b23e4d71eb171d9736e38d32"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
@@ -212,9 +212,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-build-config"
|
||||
version = "0.17.3"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28fcd1e73f06ec85bf3280c48c67e731d8290ad3d730f8be9dc07946923005c8"
|
||||
checksum = "bf0708c9ed01692635cbf056e286008e5a2927ab1a5e48cdd3aeb1ba5a6fef47"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"target-lexicon",
|
||||
@@ -222,9 +222,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-ffi"
|
||||
version = "0.17.3"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f6cb136e222e49115b3c51c32792886defbfb0adead26a688142b346a0b9ffc"
|
||||
checksum = "90352dea4f486932b72ddf776264d293f85b79a1d214de1d023927b41461132d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pyo3-build-config",
|
||||
@@ -243,9 +243,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros"
|
||||
version = "0.17.3"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94144a1266e236b1c932682136dc35a9dee8d3589728f68130c7c3861ef96b28"
|
||||
checksum = "7eb24b804a2d9e88bfcc480a5a6dd76f006c1e3edaf064e8250423336e2cd79d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
@@ -255,9 +255,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros-backend"
|
||||
version = "0.17.3"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8df9be978a2d2f0cdebabb03206ed73b11314701a5bfe71b0d753b81997777f"
|
||||
checksum = "f22bb49f6a7348c253d7ac67a6875f2dc65f36c2ae64a82c381d528972bea6d6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -294,9 +294,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.0"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Fix a performance regression in `/sync` handling, introduced in 1.49.0.
|
||||
@@ -1 +0,0 @@
|
||||
Upload documentation PRs to Netlify.
|
||||
@@ -1 +0,0 @@
|
||||
Run unit tests against Python 3.11.
|
||||
@@ -1 +0,0 @@
|
||||
Add all Stream Writer worker types to configure_workers_and_start.py.
|
||||
@@ -1 +0,0 @@
|
||||
Add experimental support for [MSC3912](https://github.com/matrix-org/matrix-spec-proposals/pull/3912): Relation-based redactions.
|
||||
@@ -1 +0,0 @@
|
||||
Faster joins: do not block creation of or queries for room aliases during the resync.
|
||||
@@ -1 +0,0 @@
|
||||
Add example on how to load balance /sync requests. Contributed by [aceArt](https://aceart.de).
|
||||
@@ -1 +0,0 @@
|
||||
Switch to a maintained action for installing Rust in CI.
|
||||
@@ -1 +0,0 @@
|
||||
Add override ability to `complement.sh` command line script to request certain types of workers.
|
||||
@@ -1 +0,0 @@
|
||||
Bump flake8-bugbear from 22.9.23 to 22.10.27.
|
||||
@@ -1 +0,0 @@
|
||||
Enabling testing of [MSC3874](https://github.com/matrix-org/matrix-spec-proposals/pull/3874) (filtering of `/messages` by relation type) in complement.
|
||||
@@ -1 +0,0 @@
|
||||
Bump twisted from 22.8.0 to 22.10.0.
|
||||
@@ -1 +0,0 @@
|
||||
Fix a bug introduced in Synapse 1.64.0rc1 which could cause log spam when fetching events from other homeservers.
|
||||
@@ -1 +0,0 @@
|
||||
Use a maintained Github action to install Rust.
|
||||
@@ -1 +0,0 @@
|
||||
Fix a bug introduced in v1.71.0rc1 where the power level event was incorrectly created during initial room creation.
|
||||
@@ -1 +0,0 @@
|
||||
Fix refresh token endpoint to be under /r0 and /v3 instead of /v1. Contributed by Tulir @ Beeper.
|
||||
@@ -1 +0,0 @@
|
||||
Fix a long-standing bug where Synapse would raise an error when encountering an unrecognised field in a `/sync` filter, instead of ignoring it for forward compatibility.
|
||||
@@ -1 +0,0 @@
|
||||
Upload documentation PRs to Netlify.
|
||||
@@ -1 +0,0 @@
|
||||
Batch up state groups to store when creating initial room events.
|
||||
@@ -1 +0,0 @@
|
||||
Fix a background database update, introduced in Synapse 1.64.0, which could cause poor database performance.
|
||||
@@ -1 +0,0 @@
|
||||
Bump dawidd6/action-download-artifact from 2.15.0 to 2.24.0.
|
||||
@@ -1 +0,0 @@
|
||||
Bump peaceiris/actions-mdbook from 1.1.14 to 1.2.0.
|
||||
@@ -1 +0,0 @@
|
||||
Bump regex from 1.6.0 to 1.7.0.
|
||||
@@ -1 +0,0 @@
|
||||
Bump pyo3 from 0.17.2 to 0.17.3.
|
||||
@@ -1 +0,0 @@
|
||||
Bump types-setuptools from 65.5.0.1 to 65.5.0.2.
|
||||
@@ -1 +0,0 @@
|
||||
Bump pillow from 9.2.0 to 9.3.0.
|
||||
@@ -1 +0,0 @@
|
||||
Bump cryptography from 36.0.1 to 38.0.3.
|
||||
@@ -1 +0,0 @@
|
||||
Bump types-pyyaml from 6.0.12 to 6.0.12.1.
|
||||
@@ -1 +0,0 @@
|
||||
Bump types-jsonschema from 4.4.6 to 4.17.0.0.
|
||||
@@ -1 +0,0 @@
|
||||
Remove support for PostgreSQL 10.
|
||||
Vendored
-12
@@ -1,15 +1,3 @@
|
||||
matrix-synapse-py3 (1.71.0) stable; urgency=medium
|
||||
|
||||
* New Synapse release 1.71.0.
|
||||
|
||||
-- Synapse Packaging team <packages@matrix.org> Tue, 08 Nov 2022 10:38:10 +0000
|
||||
|
||||
matrix-synapse-py3 (1.71.0~rc2) stable; urgency=medium
|
||||
|
||||
* New Synapse release 1.71.0rc2.
|
||||
|
||||
-- Synapse Packaging team <packages@matrix.org> Fri, 04 Nov 2022 12:00:33 +0000
|
||||
|
||||
matrix-synapse-py3 (1.71.0~rc1) stable; urgency=medium
|
||||
|
||||
* New Synapse release 1.71.0rc1.
|
||||
|
||||
@@ -45,12 +45,7 @@ esac
|
||||
|
||||
if [[ -n "$SYNAPSE_COMPLEMENT_USE_WORKERS" ]]; then
|
||||
# Specify the workers to test with
|
||||
# Allow overriding by explicitly setting SYNAPSE_WORKER_TYPES outside, while still
|
||||
# utilizing WORKERS=1 for backwards compatibility.
|
||||
# -n True if the length of string is non-zero.
|
||||
# -z True if the length of string is zero.
|
||||
if [[ -z "$SYNAPSE_WORKER_TYPES" ]]; then
|
||||
export SYNAPSE_WORKER_TYPES="\
|
||||
export SYNAPSE_WORKER_TYPES="\
|
||||
event_persister, \
|
||||
event_persister, \
|
||||
background_worker, \
|
||||
@@ -66,8 +61,6 @@ if [[ -n "$SYNAPSE_COMPLEMENT_USE_WORKERS" ]]; then
|
||||
appservice, \
|
||||
pusher"
|
||||
|
||||
fi
|
||||
log "Workers requested: $SYNAPSE_WORKER_TYPES"
|
||||
# Improve startup times by using a launcher based on fork()
|
||||
export SYNAPSE_USE_EXPERIMENTAL_FORKING_LAUNCHER=1
|
||||
else
|
||||
|
||||
@@ -92,6 +92,8 @@ allow_device_name_lookup_over_federation: true
|
||||
## Experimental Features ##
|
||||
|
||||
experimental_features:
|
||||
# Enable spaces support
|
||||
spaces_enabled: true
|
||||
# Enable history backfilling support
|
||||
msc2716_enabled: true
|
||||
# server-side support for partial state in /send_join responses
|
||||
@@ -102,8 +104,6 @@ experimental_features:
|
||||
{% endif %}
|
||||
# Enable jump to date endpoint
|
||||
msc3030_enabled: true
|
||||
# Filtering /messages by relation type.
|
||||
msc3874_enabled: true
|
||||
|
||||
server_notices:
|
||||
system_mxid_localpart: _server
|
||||
|
||||
@@ -50,12 +50,7 @@ from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
MAIN_PROCESS_HTTP_LISTENER_PORT = 8080
|
||||
|
||||
# Workers with exposed endpoints needs either "client", "federation", or "media" listener_resources
|
||||
# Watching /_matrix/client needs a "client" listener
|
||||
# Watching /_matrix/federation needs a "federation" listener
|
||||
# Watching /_matrix/media and related needs a "media" listener
|
||||
# Stream Writers require "client" and "replication" listeners because they
|
||||
# have to attach by instance_map to the master process and have client endpoints.
|
||||
|
||||
WORKERS_CONFIG: Dict[str, Dict[str, Any]] = {
|
||||
"pusher": {
|
||||
"app": "synapse.app.pusher",
|
||||
@@ -214,49 +209,6 @@ WORKERS_CONFIG: Dict[str, Dict[str, Any]] = {
|
||||
% (MAIN_PROCESS_HTTP_LISTENER_PORT,)
|
||||
),
|
||||
},
|
||||
"account_data": {
|
||||
"app": "synapse.app.generic_worker",
|
||||
"listener_resources": ["client", "replication"],
|
||||
"endpoint_patterns": [
|
||||
"^/_matrix/client/(r0|v3|unstable)/.*/tags",
|
||||
"^/_matrix/client/(r0|v3|unstable)/.*/account_data",
|
||||
],
|
||||
"shared_extra_conf": {},
|
||||
"worker_extra_conf": "",
|
||||
},
|
||||
"presence": {
|
||||
"app": "synapse.app.generic_worker",
|
||||
"listener_resources": ["client", "replication"],
|
||||
"endpoint_patterns": ["^/_matrix/client/(api/v1|r0|v3|unstable)/presence/"],
|
||||
"shared_extra_conf": {},
|
||||
"worker_extra_conf": "",
|
||||
},
|
||||
"receipts": {
|
||||
"app": "synapse.app.generic_worker",
|
||||
"listener_resources": ["client", "replication"],
|
||||
"endpoint_patterns": [
|
||||
"^/_matrix/client/(r0|v3|unstable)/rooms/.*/receipt",
|
||||
"^/_matrix/client/(r0|v3|unstable)/rooms/.*/read_markers",
|
||||
],
|
||||
"shared_extra_conf": {},
|
||||
"worker_extra_conf": "",
|
||||
},
|
||||
"to_device": {
|
||||
"app": "synapse.app.generic_worker",
|
||||
"listener_resources": ["client", "replication"],
|
||||
"endpoint_patterns": ["^/_matrix/client/(r0|v3|unstable)/sendToDevice/"],
|
||||
"shared_extra_conf": {},
|
||||
"worker_extra_conf": "",
|
||||
},
|
||||
"typing": {
|
||||
"app": "synapse.app.generic_worker",
|
||||
"listener_resources": ["client", "replication"],
|
||||
"endpoint_patterns": [
|
||||
"^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/typing"
|
||||
],
|
||||
"shared_extra_conf": {},
|
||||
"worker_extra_conf": "",
|
||||
},
|
||||
}
|
||||
|
||||
# Templates for sections that may be inserted multiple times in config files
|
||||
@@ -319,7 +271,7 @@ def convert(src: str, dst: str, **template_vars: object) -> None:
|
||||
outfile.write(rendered)
|
||||
|
||||
|
||||
def add_worker_roles_to_shared_config(
|
||||
def add_sharding_to_shared_config(
|
||||
shared_config: dict,
|
||||
worker_type: str,
|
||||
worker_name: str,
|
||||
@@ -357,20 +309,6 @@ def add_worker_roles_to_shared_config(
|
||||
"port": worker_port,
|
||||
}
|
||||
|
||||
elif worker_type in ["account_data", "presence", "receipts", "to_device", "typing"]:
|
||||
# Update the list of stream writers
|
||||
# It's convienent that the name of the worker type is the same as the event stream
|
||||
shared_config.setdefault("stream_writers", {}).setdefault(
|
||||
worker_type, []
|
||||
).append(worker_name)
|
||||
|
||||
# Map of stream writer instance names to host/ports combos
|
||||
# For now, all stream writers need http replication ports
|
||||
instance_map[worker_name] = {
|
||||
"host": "localhost",
|
||||
"port": worker_port,
|
||||
}
|
||||
|
||||
elif worker_type == "media_repository":
|
||||
# The first configured media worker will run the media background jobs
|
||||
shared_config.setdefault("media_instance_running_background_jobs", worker_name)
|
||||
@@ -503,11 +441,11 @@ def generate_worker_files(
|
||||
|
||||
# Check if more than one instance of this worker type has been specified
|
||||
worker_type_total_count = worker_types.count(worker_type)
|
||||
|
||||
# Update the shared config with sharding-related options if necessary
|
||||
add_worker_roles_to_shared_config(
|
||||
shared_config, worker_type, worker_name, worker_port
|
||||
)
|
||||
if worker_type_total_count > 1:
|
||||
# Update the shared config with sharding-related options if necessary
|
||||
add_sharding_to_shared_config(
|
||||
shared_config, worker_type, worker_name, worker_port
|
||||
)
|
||||
|
||||
# Enable the worker in supervisord
|
||||
worker_descriptors.append(worker_config)
|
||||
|
||||
@@ -324,12 +324,6 @@ The above will run a monolithic (single-process) Synapse with SQLite as the data
|
||||
|
||||
- Passing `POSTGRES=1` as an environment variable to use the Postgres database instead.
|
||||
- Passing `WORKERS=1` as an environment variable to use a workerised setup instead. This option implies the use of Postgres.
|
||||
- If setting `WORKERS=1`, optionally set `WORKER_TYPES=` to declare which worker
|
||||
types you wish to test. A simple comma-delimited string containing the worker types
|
||||
defined from the `WORKERS_CONFIG` template in
|
||||
[here](https://github.com/matrix-org/synapse/blob/develop/docker/configure_workers_and_start.py#L54).
|
||||
A safe example would be `WORKER_TYPES="federation_inbound, federation_sender, synchrotron"`.
|
||||
See the [worker documentation](../workers.md) for additional information on workers.
|
||||
|
||||
To increase the log level for the tests, set `SYNAPSE_TEST_LOG_LEVEL`, e.g:
|
||||
```sh
|
||||
|
||||
@@ -209,9 +209,6 @@ altogether in Synapse v1.73.0.**
|
||||
| synapse_http_httppusher_http_pushes_failed_total | synapse_http_httppusher_http_pushes_failed |
|
||||
| synapse_http_httppusher_badge_updates_processed_total | synapse_http_httppusher_badge_updates_processed |
|
||||
| synapse_http_httppusher_badge_updates_failed_total | synapse_http_httppusher_badge_updates_failed |
|
||||
| synapse_admin_mau_current | synapse_admin_mau:current |
|
||||
| synapse_admin_mau_max | synapse_admin_mau:max |
|
||||
| synapse_admin_mau_registered_reserved_users | synapse_admin_mau:registered_reserved_users |
|
||||
|
||||
Removal of deprecated metrics & time based counters becoming histograms in 0.31.0
|
||||
---------------------------------------------------------------------------------
|
||||
|
||||
@@ -88,16 +88,6 @@ process, for example:
|
||||
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
|
||||
```
|
||||
|
||||
# Upgrading to v1.72.0
|
||||
|
||||
## Dropping support for PostgreSQL 10
|
||||
|
||||
In line with our [deprecation policy](deprecation_policy.md), we've dropped
|
||||
support for PostgreSQL 10, as it is no longer supported upstream.
|
||||
|
||||
This release of Synapse requires PostgreSQL 11+.
|
||||
|
||||
|
||||
# Upgrading to v1.71.0
|
||||
|
||||
## Removal of the `generate_short_term_login_token` module API method
|
||||
@@ -126,22 +116,6 @@ local users and some remote users is why the spec was changed/clarified and this
|
||||
caveat is no longer supported.
|
||||
|
||||
|
||||
## Legacy Prometheus metric names are now disabled by default
|
||||
|
||||
Synapse v1.71.0 disables legacy Prometheus metric names by default.
|
||||
For administrators that still rely on them and have not yet had chance to update their
|
||||
uses of the metrics, it's still possible to specify `enable_legacy_metrics: true` in
|
||||
the configuration to re-enable them temporarily.
|
||||
|
||||
Synapse v1.73.0 will **remove legacy metric names altogether** and at that point,
|
||||
it will no longer be possible to re-enable them.
|
||||
|
||||
If you do not use metrics or you have already updated your Grafana dashboard(s),
|
||||
Prometheus console(s) and alerting rule(s), there is no action needed.
|
||||
|
||||
See [v1.69.0: Deprecation of legacy Prometheus metric names](#deprecation-of-legacy-prometheus-metric-names).
|
||||
|
||||
|
||||
# Upgrading to v1.69.0
|
||||
|
||||
## Changes to the receipts replication streams
|
||||
|
||||
@@ -73,12 +73,12 @@ When a request is blocked, the response will have the `errcode` `M_RESOURCE_LIMI
|
||||
|
||||
Synapse records several different prometheus metrics for MAU.
|
||||
|
||||
`synapse_admin_mau_current` records the current MAU figure for native (non-application-service) users.
|
||||
`synapse_admin_mau:current` records the current MAU figure for native (non-application-service) users.
|
||||
|
||||
`synapse_admin_mau_max` records the maximum MAU as dictated by the `max_mau_value` config value.
|
||||
`synapse_admin_mau:max` records the maximum MAU as dictated by the `max_mau_value` config value.
|
||||
|
||||
`synapse_admin_mau_current_mau_by_service` records the current MAU including application service users. The label `app_service` can be used
|
||||
to filter by a specific service ID. This *also* includes non-application-service users under `app_service=native` .
|
||||
|
||||
`synapse_admin_mau_registered_reserved_users` records the number of users specified in `mau_limits_reserved_threepids` which have
|
||||
`synapse_admin_mau:registered_reserved_users` records the number of users specified in `mau_limits_reserved_threepids` which have
|
||||
registered accounts on the homeserver.
|
||||
|
||||
@@ -2441,8 +2441,8 @@ enable_metrics: true
|
||||
|
||||
Set to `true` to publish both legacy and non-legacy Prometheus metric names,
|
||||
or to `false` to only publish non-legacy Prometheus metric names.
|
||||
Defaults to `false`. Has no effect if `enable_metrics` is `false`.
|
||||
**In Synapse v1.67.0 up to and including Synapse v1.70.1, this defaulted to `true`.**
|
||||
Defaults to `true`. Has no effect if `enable_metrics` is `false`.
|
||||
**In Synapse v1.71.0, this will default to `false` before being removed in Synapse v1.73.0.**
|
||||
|
||||
Legacy metric names include:
|
||||
- metrics containing colons in the name, such as `synapse_util_caches_response_cache:hits`, because colons are supposed to be reserved for user-defined recording rules;
|
||||
|
||||
+3
-5
@@ -305,11 +305,9 @@ may wish to run multiple groups of workers handling different endpoints so that
|
||||
load balancing can be done in different ways.
|
||||
|
||||
For `/sync` and `/initialSync` requests it will be more efficient if all
|
||||
requests from a particular user are routed to a single instance. This can
|
||||
be done e.g. in nginx via IP `hash $http_x_forwarded_for;` or via
|
||||
`hash $http_authorization consistent;` which contains the users access token.
|
||||
|
||||
Admins may additionally wish to separate out `/sync`
|
||||
requests from a particular user are routed to a single instance. Extracting a
|
||||
user ID from the access token or `Authorization` header is currently left as an
|
||||
exercise for the reader. Admins may additionally wish to separate out `/sync`
|
||||
requests that have a `since` query parameter from those that don't (and
|
||||
`/initialSync`), as requests that don't are known as "initial sync" that happens
|
||||
when a user logs in on a new device and can be *very* resource intensive, so
|
||||
|
||||
Generated
+143
-192
@@ -25,7 +25,7 @@ cryptography = ">=3.2"
|
||||
|
||||
[[package]]
|
||||
name = "automat"
|
||||
version = "22.10.0"
|
||||
version = "20.2.0"
|
||||
description = "Self-service finite-state machines for the programmer on the go."
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -186,7 +186,7 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "38.0.3"
|
||||
version = "36.0.1"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -196,12 +196,12 @@ python-versions = ">=3.6"
|
||||
cffi = ">=1.12"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"]
|
||||
docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx_rtd_theme"]
|
||||
docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
|
||||
pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
|
||||
sdist = ["setuptools-rust (>=0.11.4)"]
|
||||
sdist = ["setuptools_rust (>=0.11.4)"]
|
||||
ssh = ["bcrypt (>=3.1.5)"]
|
||||
test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
|
||||
test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
|
||||
|
||||
[[package]]
|
||||
name = "defusedxml"
|
||||
@@ -260,18 +260,18 @@ pyflakes = ">=2.4.0,<2.5.0"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-bugbear"
|
||||
version = "22.10.27"
|
||||
version = "22.9.23"
|
||||
description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
attrs = ">=19.2.0"
|
||||
flake8 = ">=3.0.0"
|
||||
|
||||
[package.extras]
|
||||
dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "tox"]
|
||||
dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit"]
|
||||
|
||||
[[package]]
|
||||
name = "flake8-comprehensions"
|
||||
@@ -671,16 +671,12 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "9.3.0"
|
||||
version = "9.0.1"
|
||||
description = "Python Imaging Library (Fork)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"]
|
||||
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
|
||||
|
||||
[[package]]
|
||||
name = "pkginfo"
|
||||
version = "1.8.2"
|
||||
@@ -1313,7 +1309,7 @@ urllib3 = ">=1.26.0"
|
||||
|
||||
[[package]]
|
||||
name = "twisted"
|
||||
version = "22.10.0"
|
||||
version = "22.8.0"
|
||||
description = "An asynchronous networking framework written in Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -1333,21 +1329,21 @@ typing-extensions = ">=3.6.5"
|
||||
"zope.interface" = ">=4.4.2"
|
||||
|
||||
[package.extras]
|
||||
all-non-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"]
|
||||
all-non-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"]
|
||||
conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "cryptography (>=2.6)", "pyasn1"]
|
||||
conch-nacl = ["PyNaCl", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "cryptography (>=2.6)", "pyasn1"]
|
||||
contextvars = ["contextvars (>=2.4,<3)"]
|
||||
dev = ["coverage (>=6b1,<7)", "pydoctor (>=22.9.0,<22.10.0)", "pyflakes (>=2.2,<3.0)", "python-subunit (>=1.4,<2.0)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "sphinx (>=5.0,<6)", "sphinx-rtd-theme (>=1.0,<2.0)", "towncrier (>=22.8,<23.0)", "twistedchecker (>=0.7,<1.0)"]
|
||||
dev-release = ["pydoctor (>=22.9.0,<22.10.0)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "sphinx (>=5.0,<6)", "sphinx-rtd-theme (>=1.0,<2.0)", "towncrier (>=22.8,<23.0)"]
|
||||
gtk-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pygobject", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"]
|
||||
dev = ["coverage (>=6b1,<7)", "pydoctor (>=22.7.0,<22.8.0)", "pyflakes (>=2.2,<3.0)", "python-subunit (>=1.4,<2.0)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "sphinx (>=4.1.2,<6)", "sphinx-rtd-theme (>=0.5,<1.0)", "towncrier (>=19.2,<20.0)", "twistedchecker (>=0.7,<1.0)"]
|
||||
dev-release = ["pydoctor (>=22.7.0,<22.8.0)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "sphinx (>=4.1.2,<6)", "sphinx-rtd-theme (>=0.5,<1.0)", "towncrier (>=19.2,<20.0)"]
|
||||
gtk-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pygobject", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"]
|
||||
http2 = ["h2 (>=3.0,<5.0)", "priority (>=1.1.0,<2.0)"]
|
||||
macos-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"]
|
||||
mypy = ["PyHamcrest (>=1.9.0)", "PyNaCl", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "coverage (>=6b1,<7)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "mypy (==0.930)", "mypy-zope (==0.3.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pydoctor (>=22.9.0,<22.10.0)", "pyflakes (>=2.2,<3.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "service-identity (>=18.1.0)", "sphinx (>=5.0,<6)", "sphinx-rtd-theme (>=1.0,<2.0)", "towncrier (>=22.8,<23.0)", "twistedchecker (>=0.7,<1.0)", "types-pyOpenSSL", "types-setuptools"]
|
||||
osx-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"]
|
||||
macos-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"]
|
||||
mypy = ["PyHamcrest (>=1.9.0)", "PyNaCl", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "coverage (>=6b1,<7)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "mypy (==0.930)", "mypy-zope (==0.3.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pydoctor (>=22.7.0,<22.8.0)", "pyflakes (>=2.2,<3.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "service-identity (>=18.1.0)", "sphinx (>=4.1.2,<6)", "sphinx-rtd-theme (>=0.5,<1.0)", "towncrier (>=19.2,<20.0)", "twistedchecker (>=0.7,<1.0)", "types-pyOpenSSL", "types-setuptools"]
|
||||
osx-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"]
|
||||
serial = ["pyserial (>=3.0)", "pywin32 (!=226)"]
|
||||
test = ["PyHamcrest (>=1.9.0)", "cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.0,<7.0)"]
|
||||
test = ["PyHamcrest (>=1.9.0)", "cython-test-exception-raiser (>=1.0.2,<2)"]
|
||||
tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"]
|
||||
windows-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)"]
|
||||
windows-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "twisted-iocpsupport"
|
||||
@@ -1423,7 +1419,7 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "types-jsonschema"
|
||||
version = "4.17.0.0"
|
||||
version = "4.4.6"
|
||||
description = "Typing stubs for jsonschema"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -1465,8 +1461,8 @@ python-versions = "*"
|
||||
types-cryptography = "*"
|
||||
|
||||
[[package]]
|
||||
name = "types-pyyaml"
|
||||
version = "6.0.12.1"
|
||||
name = "types-PyYAML"
|
||||
version = "6.0.12"
|
||||
description = "Typing stubs for PyYAML"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -1485,7 +1481,7 @@ types-urllib3 = "<1.27"
|
||||
|
||||
[[package]]
|
||||
name = "types-setuptools"
|
||||
version = "65.5.0.2"
|
||||
version = "65.5.0.1"
|
||||
description = "Typing stubs for setuptools"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -1538,7 +1534,7 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "wrapt"
|
||||
version = "1.14.1"
|
||||
version = "1.13.3"
|
||||
description = "Module for decorators, wrappers and monkey patching."
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -1650,8 +1646,8 @@ Authlib = [
|
||||
{file = "Authlib-1.1.0.tar.gz", hash = "sha256:0a270c91409fc2b7b0fbee6996e09f2ee3187358762111a9a4225c874b94e891"},
|
||||
]
|
||||
automat = [
|
||||
{file = "Automat-22.10.0-py2.py3-none-any.whl", hash = "sha256:c3164f8742b9dc440f3682482d32aaff7bb53f71740dd018533f9de286b64180"},
|
||||
{file = "Automat-22.10.0.tar.gz", hash = "sha256:e56beb84edad19dcc11d30e8d9b895f75deeb5ef5e96b84a467066b3b84bb04e"},
|
||||
{file = "Automat-20.2.0-py2.py3-none-any.whl", hash = "sha256:b6feb6455337df834f6c9962d6ccf771515b7d939bca142b29c20c2376bc6111"},
|
||||
{file = "Automat-20.2.0.tar.gz", hash = "sha256:7979803c74610e11ef0c0d68a2942b152df52da55336e0c9d58daf1831cbdf33"},
|
||||
]
|
||||
bcrypt = [
|
||||
{file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"},
|
||||
@@ -1787,32 +1783,26 @@ constantly = [
|
||||
{file = "constantly-15.1.0.tar.gz", hash = "sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35"},
|
||||
]
|
||||
cryptography = [
|
||||
{file = "cryptography-38.0.3-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:984fe150f350a3c91e84de405fe49e688aa6092b3525f407a18b9646f6612320"},
|
||||
{file = "cryptography-38.0.3-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:ed7b00096790213e09eb11c97cc6e2b757f15f3d2f85833cd2d3ec3fe37c1722"},
|
||||
{file = "cryptography-38.0.3-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:bbf203f1a814007ce24bd4d51362991d5cb90ba0c177a9c08825f2cc304d871f"},
|
||||
{file = "cryptography-38.0.3-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554bec92ee7d1e9d10ded2f7e92a5d70c1f74ba9524947c0ba0c850c7b011828"},
|
||||
{file = "cryptography-38.0.3-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b52c9e5f8aa2b802d48bd693190341fae201ea51c7a167d69fc48b60e8a959"},
|
||||
{file = "cryptography-38.0.3-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:728f2694fa743a996d7784a6194da430f197d5c58e2f4e278612b359f455e4a2"},
|
||||
{file = "cryptography-38.0.3-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dfb4f4dd568de1b6af9f4cda334adf7d72cf5bc052516e1b2608b683375dd95c"},
|
||||
{file = "cryptography-38.0.3-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5419a127426084933076132d317911e3c6eb77568a1ce23c3ac1e12d111e61e0"},
|
||||
{file = "cryptography-38.0.3-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9b24bcff7853ed18a63cfb0c2b008936a9554af24af2fb146e16d8e1aed75748"},
|
||||
{file = "cryptography-38.0.3-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:25c1d1f19729fb09d42e06b4bf9895212292cb27bb50229f5aa64d039ab29146"},
|
||||
{file = "cryptography-38.0.3-cp36-abi3-win32.whl", hash = "sha256:7f836217000342d448e1c9a342e9163149e45d5b5eca76a30e84503a5a96cab0"},
|
||||
{file = "cryptography-38.0.3-cp36-abi3-win_amd64.whl", hash = "sha256:c46837ea467ed1efea562bbeb543994c2d1f6e800785bd5a2c98bc096f5cb220"},
|
||||
{file = "cryptography-38.0.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06fc3cc7b6f6cca87bd56ec80a580c88f1da5306f505876a71c8cfa7050257dd"},
|
||||
{file = "cryptography-38.0.3-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:65535bc550b70bd6271984d9863a37741352b4aad6fb1b3344a54e6950249b55"},
|
||||
{file = "cryptography-38.0.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:5e89468fbd2fcd733b5899333bc54d0d06c80e04cd23d8c6f3e0542358c6060b"},
|
||||
{file = "cryptography-38.0.3-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6ab9516b85bebe7aa83f309bacc5f44a61eeb90d0b4ec125d2d003ce41932d36"},
|
||||
{file = "cryptography-38.0.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068147f32fa662c81aebab95c74679b401b12b57494872886eb5c1139250ec5d"},
|
||||
{file = "cryptography-38.0.3-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:402852a0aea73833d982cabb6d0c3bb582c15483d29fb7085ef2c42bfa7e38d7"},
|
||||
{file = "cryptography-38.0.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b1b35d9d3a65542ed2e9d90115dfd16bbc027b3f07ee3304fc83580f26e43249"},
|
||||
{file = "cryptography-38.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6addc3b6d593cd980989261dc1cce38263c76954d758c3c94de51f1e010c9a50"},
|
||||
{file = "cryptography-38.0.3-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:be243c7e2bfcf6cc4cb350c0d5cdf15ca6383bbcb2a8ef51d3c9411a9d4386f0"},
|
||||
{file = "cryptography-38.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78cf5eefac2b52c10398a42765bfa981ce2372cbc0457e6bf9658f41ec3c41d8"},
|
||||
{file = "cryptography-38.0.3-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:4e269dcd9b102c5a3d72be3c45d8ce20377b8076a43cbed6f660a1afe365e436"},
|
||||
{file = "cryptography-38.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8d41a46251bf0634e21fac50ffd643216ccecfaf3701a063257fe0b2be1b6548"},
|
||||
{file = "cryptography-38.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:785e4056b5a8b28f05a533fab69febf5004458e20dad7e2e13a3120d8ecec75a"},
|
||||
{file = "cryptography-38.0.3.tar.gz", hash = "sha256:bfbe6ee19615b07a98b1d2287d6a6073f734735b49ee45b11324d85efc4d5cbd"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:73bc2d3f2444bcfeac67dd130ff2ea598ea5f20b40e36d19821b4df8c9c5037b"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:2d87cdcb378d3cfed944dac30596da1968f88fb96d7fc34fdae30a99054b2e31"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74d6c7e80609c0f4c2434b97b80c7f8fdfaa072ca4baab7e239a15d6d70ed73a"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:6c0c021f35b421ebf5976abf2daacc47e235f8b6082d3396a2fe3ccd537ab173"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59a9d55027a8b88fd9fd2826c4392bd487d74bf628bb9d39beecc62a644c12"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a817b961b46894c5ca8a66b599c745b9a3d9f822725221f0e0fe49dc043a3a3"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:94ae132f0e40fe48f310bba63f477f14a43116f05ddb69d6fa31e93f05848ae2"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7be0eec337359c155df191d6ae00a5e8bbb63933883f4f5dffc439dac5348c3f"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e0344c14c9cb89e76eb6a060e67980c9e35b3f36691e15e1b7a9e58a0a6c6dc3"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-win32.whl", hash = "sha256:4caa4b893d8fad33cf1964d3e51842cd78ba87401ab1d2e44556826df849a8ca"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:391432971a66cfaf94b21c24ab465a4cc3e8bf4a939c1ca5c3e3a6e0abebdbcf"},
|
||||
{file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bb5829d027ff82aa872d76158919045a7c1e91fbf241aec32cb07956e9ebd3c9"},
|
||||
{file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc15b1c22e55c4d5566e3ca4db8689470a0ca2babef8e3a9ee057a8b82ce4b1"},
|
||||
{file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:596f3cd67e1b950bc372c33f1a28a0692080625592ea6392987dba7f09f17a94"},
|
||||
{file = "cryptography-36.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:30ee1eb3ebe1644d1c3f183d115a8c04e4e603ed6ce8e394ed39eea4a98469ac"},
|
||||
{file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec63da4e7e4a5f924b90af42eddf20b698a70e58d86a72d943857c4c6045b3ee"},
|
||||
{file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca238ceb7ba0bdf6ce88c1b74a87bffcee5afbfa1e41e173b1ceb095b39add46"},
|
||||
{file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:ca28641954f767f9822c24e927ad894d45d5a1e501767599647259cbf030b903"},
|
||||
{file = "cryptography-36.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:39bdf8e70eee6b1c7b289ec6e5d84d49a6bfa11f8b8646b5b3dfe41219153316"},
|
||||
{file = "cryptography-36.0.1.tar.gz", hash = "sha256:53e5c1dc3d7a953de055d77bef2ff607ceef7a2aac0353b5d630ab67f7423638"},
|
||||
]
|
||||
defusedxml = [
|
||||
{file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
|
||||
@@ -1835,8 +1825,8 @@ flake8 = [
|
||||
{file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
|
||||
]
|
||||
flake8-bugbear = [
|
||||
{file = "flake8-bugbear-22.10.27.tar.gz", hash = "sha256:a6708608965c9e0de5fff13904fed82e0ba21ac929fe4896459226a797e11cd5"},
|
||||
{file = "flake8_bugbear-22.10.27-py3-none-any.whl", hash = "sha256:6ad0ab754507319060695e2f2be80e6d8977cfcea082293089a9226276bd825d"},
|
||||
{file = "flake8-bugbear-22.9.23.tar.gz", hash = "sha256:17b9623325e6e0dcdcc80ed9e4aa811287fcc81d7e03313b8736ea5733759937"},
|
||||
{file = "flake8_bugbear-22.9.23-py3-none-any.whl", hash = "sha256:cd2779b2b7ada212d7a322814a1e5651f1868ab0d3f24cc9da66169ab8fda474"},
|
||||
]
|
||||
flake8-comprehensions = [
|
||||
{file = "flake8-comprehensions-3.8.0.tar.gz", hash = "sha256:8e108707637b1d13734f38e03435984f6b7854fa6b5a4e34f93e69534be8e521"},
|
||||
@@ -2261,67 +2251,41 @@ phonenumbers = [
|
||||
{file = "phonenumbers-8.12.56.tar.gz", hash = "sha256:82a4f226c930d02dcdf6d4b29e4cfd8678991fe65c2efd5fdd143557186f0868"},
|
||||
]
|
||||
pillow = [
|
||||
{file = "Pillow-9.3.0-1-cp37-cp37m-win32.whl", hash = "sha256:e6ea6b856a74d560d9326c0f5895ef8050126acfdc7ca08ad703eb0081e82b74"},
|
||||
{file = "Pillow-9.3.0-1-cp37-cp37m-win_amd64.whl", hash = "sha256:32a44128c4bdca7f31de5be641187367fe2a450ad83b833ef78910397db491aa"},
|
||||
{file = "Pillow-9.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:0b7257127d646ff8676ec8a15520013a698d1fdc48bc2a79ba4e53df792526f2"},
|
||||
{file = "Pillow-9.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b90f7616ea170e92820775ed47e136208e04c967271c9ef615b6fbd08d9af0e3"},
|
||||
{file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68943d632f1f9e3dce98908e873b3a090f6cba1cbb1b892a9e8d97c938871fbe"},
|
||||
{file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be55f8457cd1eac957af0c3f5ece7bc3f033f89b114ef30f710882717670b2a8"},
|
||||
{file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d77adcd56a42d00cc1be30843d3426aa4e660cab4a61021dc84467123f7a00c"},
|
||||
{file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:829f97c8e258593b9daa80638aee3789b7df9da5cf1336035016d76f03b8860c"},
|
||||
{file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:801ec82e4188e935c7f5e22e006d01611d6b41661bba9fe45b60e7ac1a8f84de"},
|
||||
{file = "Pillow-9.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:871b72c3643e516db4ecf20efe735deb27fe30ca17800e661d769faab45a18d7"},
|
||||
{file = "Pillow-9.3.0-cp310-cp310-win32.whl", hash = "sha256:655a83b0058ba47c7c52e4e2df5ecf484c1b0b0349805896dd350cbc416bdd91"},
|
||||
{file = "Pillow-9.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:9f47eabcd2ded7698106b05c2c338672d16a6f2a485e74481f524e2a23c2794b"},
|
||||
{file = "Pillow-9.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:57751894f6618fd4308ed8e0c36c333e2f5469744c34729a27532b3db106ee20"},
|
||||
{file = "Pillow-9.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7db8b751ad307d7cf238f02101e8e36a128a6cb199326e867d1398067381bff4"},
|
||||
{file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3033fbe1feb1b59394615a1cafaee85e49d01b51d54de0cbf6aa8e64182518a1"},
|
||||
{file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22b012ea2d065fd163ca096f4e37e47cd8b59cf4b0fd47bfca6abb93df70b34c"},
|
||||
{file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a65733d103311331875c1dca05cb4606997fd33d6acfed695b1232ba1df193"},
|
||||
{file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:502526a2cbfa431d9fc2a079bdd9061a2397b842bb6bc4239bb176da00993812"},
|
||||
{file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:90fb88843d3902fe7c9586d439d1e8c05258f41da473952aa8b328d8b907498c"},
|
||||
{file = "Pillow-9.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:89dca0ce00a2b49024df6325925555d406b14aa3efc2f752dbb5940c52c56b11"},
|
||||
{file = "Pillow-9.3.0-cp311-cp311-win32.whl", hash = "sha256:3168434d303babf495d4ba58fc22d6604f6e2afb97adc6a423e917dab828939c"},
|
||||
{file = "Pillow-9.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:18498994b29e1cf86d505edcb7edbe814d133d2232d256db8c7a8ceb34d18cef"},
|
||||
{file = "Pillow-9.3.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:772a91fc0e03eaf922c63badeca75e91baa80fe2f5f87bdaed4280662aad25c9"},
|
||||
{file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa4107d1b306cdf8953edde0534562607fe8811b6c4d9a486298ad31de733b2"},
|
||||
{file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4012d06c846dc2b80651b120e2cdd787b013deb39c09f407727ba90015c684f"},
|
||||
{file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77ec3e7be99629898c9a6d24a09de089fa5356ee408cdffffe62d67bb75fdd72"},
|
||||
{file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:6c738585d7a9961d8c2821a1eb3dcb978d14e238be3d70f0a706f7fa9316946b"},
|
||||
{file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:828989c45c245518065a110434246c44a56a8b2b2f6347d1409c787e6e4651ee"},
|
||||
{file = "Pillow-9.3.0-cp37-cp37m-win32.whl", hash = "sha256:82409ffe29d70fd733ff3c1025a602abb3e67405d41b9403b00b01debc4c9a29"},
|
||||
{file = "Pillow-9.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:41e0051336807468be450d52b8edd12ac60bebaa97fe10c8b660f116e50b30e4"},
|
||||
{file = "Pillow-9.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:b03ae6f1a1878233ac620c98f3459f79fd77c7e3c2b20d460284e1fb370557d4"},
|
||||
{file = "Pillow-9.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4390e9ce199fc1951fcfa65795f239a8a4944117b5935a9317fb320e7767b40f"},
|
||||
{file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40e1ce476a7804b0fb74bcfa80b0a2206ea6a882938eaba917f7a0f004b42502"},
|
||||
{file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a06a052c5f37b4ed81c613a455a81f9a3a69429b4fd7bb913c3fa98abefc20"},
|
||||
{file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03150abd92771742d4a8cd6f2fa6246d847dcd2e332a18d0c15cc75bf6703040"},
|
||||
{file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:15c42fb9dea42465dfd902fb0ecf584b8848ceb28b41ee2b58f866411be33f07"},
|
||||
{file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:51e0e543a33ed92db9f5ef69a0356e0b1a7a6b6a71b80df99f1d181ae5875636"},
|
||||
{file = "Pillow-9.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3dd6caf940756101205dffc5367babf288a30043d35f80936f9bfb37f8355b32"},
|
||||
{file = "Pillow-9.3.0-cp38-cp38-win32.whl", hash = "sha256:f1ff2ee69f10f13a9596480335f406dd1f70c3650349e2be67ca3139280cade0"},
|
||||
{file = "Pillow-9.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:276a5ca930c913f714e372b2591a22c4bd3b81a418c0f6635ba832daec1cbcfc"},
|
||||
{file = "Pillow-9.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:73bd195e43f3fadecfc50c682f5055ec32ee2c933243cafbfdec69ab1aa87cad"},
|
||||
{file = "Pillow-9.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c7c8ae3864846fc95f4611c78129301e203aaa2af813b703c55d10cc1628535"},
|
||||
{file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0918e03aa0c72ea56edbb00d4d664294815aa11291a11504a377ea018330d3"},
|
||||
{file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0915e734b33a474d76c28e07292f196cdf2a590a0d25bcc06e64e545f2d146c"},
|
||||
{file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0372acb5d3598f36ec0914deed2a63f6bcdb7b606da04dc19a88d31bf0c05b"},
|
||||
{file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ad58d27a5b0262c0c19b47d54c5802db9b34d38bbf886665b626aff83c74bacd"},
|
||||
{file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:97aabc5c50312afa5e0a2b07c17d4ac5e865b250986f8afe2b02d772567a380c"},
|
||||
{file = "Pillow-9.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9aaa107275d8527e9d6e7670b64aabaaa36e5b6bd71a1015ddd21da0d4e06448"},
|
||||
{file = "Pillow-9.3.0-cp39-cp39-win32.whl", hash = "sha256:bac18ab8d2d1e6b4ce25e3424f709aceef668347db8637c2296bcf41acb7cf48"},
|
||||
{file = "Pillow-9.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:b472b5ea442148d1c3e2209f20f1e0bb0eb556538690fa70b5e1f79fa0ba8dc2"},
|
||||
{file = "Pillow-9.3.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ab388aaa3f6ce52ac1cb8e122c4bd46657c15905904b3120a6248b5b8b0bc228"},
|
||||
{file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb8e7f2abee51cef77673be97760abff1674ed32847ce04b4af90f610144c7b"},
|
||||
{file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca31dd6014cb8b0b2db1e46081b0ca7d936f856da3b39744aef499db5d84d02"},
|
||||
{file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c7025dce65566eb6e89f56c9509d4f628fddcedb131d9465cacd3d8bac337e7e"},
|
||||
{file = "Pillow-9.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ebf2029c1f464c59b8bdbe5143c79fa2045a581ac53679733d3a91d400ff9efb"},
|
||||
{file = "Pillow-9.3.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b59430236b8e58840a0dfb4099a0e8717ffb779c952426a69ae435ca1f57210c"},
|
||||
{file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12ce4932caf2ddf3e41d17fc9c02d67126935a44b86df6a206cf0d7161548627"},
|
||||
{file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae5331c23ce118c53b172fa64a4c037eb83c9165aba3a7ba9ddd3ec9fa64a699"},
|
||||
{file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0b07fffc13f474264c336298d1b4ce01d9c5a011415b79d4ee5527bb69ae6f65"},
|
||||
{file = "Pillow-9.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:073adb2ae23431d3b9bcbcff3fe698b62ed47211d0716b067385538a1b0f28b8"},
|
||||
{file = "Pillow-9.3.0.tar.gz", hash = "sha256:c935a22a557a560108d780f9a0fc426dd7459940dc54faa49d83249c8d3e760f"},
|
||||
{file = "Pillow-9.0.1-1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5d24e1d674dd9d72c66ad3ea9131322819ff86250b30dc5821cbafcfa0b96b4"},
|
||||
{file = "Pillow-9.0.1-1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2632d0f846b7c7600edf53c48f8f9f1e13e62f66a6dbc15191029d950bfed976"},
|
||||
{file = "Pillow-9.0.1-1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9618823bd237c0d2575283f2939655f54d51b4527ec3972907a927acbcc5bfc"},
|
||||
{file = "Pillow-9.0.1-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:9bfdb82cdfeccec50aad441afc332faf8606dfa5e8efd18a6692b5d6e79f00fd"},
|
||||
{file = "Pillow-9.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5100b45a4638e3c00e4d2320d3193bdabb2d75e79793af7c3eb139e4f569f16f"},
|
||||
{file = "Pillow-9.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:528a2a692c65dd5cafc130de286030af251d2ee0483a5bf50c9348aefe834e8a"},
|
||||
{file = "Pillow-9.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f29d831e2151e0b7b39981756d201f7108d3d215896212ffe2e992d06bfe049"},
|
||||
{file = "Pillow-9.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:855c583f268edde09474b081e3ddcd5cf3b20c12f26e0d434e1386cc5d318e7a"},
|
||||
{file = "Pillow-9.0.1-cp310-cp310-win32.whl", hash = "sha256:d9d7942b624b04b895cb95af03a23407f17646815495ce4547f0e60e0b06f58e"},
|
||||
{file = "Pillow-9.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:81c4b81611e3a3cb30e59b0cf05b888c675f97e3adb2c8672c3154047980726b"},
|
||||
{file = "Pillow-9.0.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:413ce0bbf9fc6278b2d63309dfeefe452835e1c78398efb431bab0672fe9274e"},
|
||||
{file = "Pillow-9.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80fe64a6deb6fcfdf7b8386f2cf216d329be6f2781f7d90304351811fb591360"},
|
||||
{file = "Pillow-9.0.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cef9c85ccbe9bee00909758936ea841ef12035296c748aaceee535969e27d31b"},
|
||||
{file = "Pillow-9.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d19397351f73a88904ad1aee421e800fe4bbcd1aeee6435fb62d0a05ccd1030"},
|
||||
{file = "Pillow-9.0.1-cp37-cp37m-win32.whl", hash = "sha256:d21237d0cd37acded35154e29aec853e945950321dd2ffd1a7d86fe686814669"},
|
||||
{file = "Pillow-9.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ede5af4a2702444a832a800b8eb7f0a7a1c0eed55b644642e049c98d589e5092"},
|
||||
{file = "Pillow-9.0.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:b5b3f092fe345c03bca1e0b687dfbb39364b21ebb8ba90e3fa707374b7915204"},
|
||||
{file = "Pillow-9.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:335ace1a22325395c4ea88e00ba3dc89ca029bd66bd5a3c382d53e44f0ccd77e"},
|
||||
{file = "Pillow-9.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db6d9fac65bd08cea7f3540b899977c6dee9edad959fa4eaf305940d9cbd861c"},
|
||||
{file = "Pillow-9.0.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f154d173286a5d1863637a7dcd8c3437bb557520b01bddb0be0258dcb72696b5"},
|
||||
{file = "Pillow-9.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d4b1341ac07ae07eb2cc682f459bec932a380c3b122f5540432d8977e64eae"},
|
||||
{file = "Pillow-9.0.1-cp38-cp38-win32.whl", hash = "sha256:effb7749713d5317478bb3acb3f81d9d7c7f86726d41c1facca068a04cf5bb4c"},
|
||||
{file = "Pillow-9.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:7f7609a718b177bf171ac93cea9fd2ddc0e03e84d8fa4e887bdfc39671d46b00"},
|
||||
{file = "Pillow-9.0.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:80ca33961ced9c63358056bd08403ff866512038883e74f3a4bf88ad3eb66838"},
|
||||
{file = "Pillow-9.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c3c33ac69cf059bbb9d1a71eeaba76781b450bc307e2291f8a4764d779a6b28"},
|
||||
{file = "Pillow-9.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12875d118f21cf35604176872447cdb57b07126750a33748bac15e77f90f1f9c"},
|
||||
{file = "Pillow-9.0.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:514ceac913076feefbeaf89771fd6febde78b0c4c1b23aaeab082c41c694e81b"},
|
||||
{file = "Pillow-9.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3c5c79ab7dfce6d88f1ba639b77e77a17ea33a01b07b99840d6ed08031cb2a7"},
|
||||
{file = "Pillow-9.0.1-cp39-cp39-win32.whl", hash = "sha256:718856856ba31f14f13ba885ff13874be7fefc53984d2832458f12c38205f7f7"},
|
||||
{file = "Pillow-9.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:f25ed6e28ddf50de7e7ea99d7a976d6a9c415f03adcaac9c41ff6ff41b6d86ac"},
|
||||
{file = "Pillow-9.0.1-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:011233e0c42a4a7836498e98c1acf5e744c96a67dd5032a6f666cc1fb97eab97"},
|
||||
{file = "Pillow-9.0.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:253e8a302a96df6927310a9d44e6103055e8fb96a6822f8b7f514bb7ef77de56"},
|
||||
{file = "Pillow-9.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6295f6763749b89c994fcb6d8a7f7ce03c3992e695f89f00b741b4580b199b7e"},
|
||||
{file = "Pillow-9.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a9f44cd7e162ac6191491d7249cceb02b8116b0f7e847ee33f739d7cb1ea1f70"},
|
||||
{file = "Pillow-9.0.1.tar.gz", hash = "sha256:6c8bc8238a7dfdaf7a75f5ec5a663f4173f8c367e5a39f87e720495e1eed75fa"},
|
||||
]
|
||||
pkginfo = [
|
||||
{file = "pkginfo-1.8.2-py2.py3-none-any.whl", hash = "sha256:c24c487c6a7f72c66e816ab1796b96ac6c3d14d49338293d2141664330b55ffc"},
|
||||
@@ -2730,8 +2694,8 @@ twine = [
|
||||
{file = "twine-4.0.1.tar.gz", hash = "sha256:96b1cf12f7ae611a4a40b6ae8e9570215daff0611828f5fe1f37a16255ab24a0"},
|
||||
]
|
||||
twisted = [
|
||||
{file = "Twisted-22.10.0-py3-none-any.whl", hash = "sha256:86c55f712cc5ab6f6d64e02503352464f0400f66d4f079096d744080afcccbd0"},
|
||||
{file = "Twisted-22.10.0.tar.gz", hash = "sha256:32acbd40a94f5f46e7b42c109bfae2b302250945561783a8b7a059048f2d4d31"},
|
||||
{file = "Twisted-22.8.0-py3-none-any.whl", hash = "sha256:8d4718d1e48dcc28933f8beb48dc71cfe77a125e37ad1eb7a3d0acc49baf6c99"},
|
||||
{file = "Twisted-22.8.0.tar.gz", hash = "sha256:e5b60de39f2d1da153fbe1874d885fe3fcbdb21fcc446fa759a53e8fc3513bed"},
|
||||
]
|
||||
twisted-iocpsupport = [
|
||||
{file = "twisted-iocpsupport-1.0.2.tar.gz", hash = "sha256:72068b206ee809c9c596b57b5287259ea41ddb4774d86725b19f35bf56aa32a9"},
|
||||
@@ -2798,8 +2762,8 @@ types-ipaddress = [
|
||||
{file = "types_ipaddress-1.0.8-py3-none-any.whl", hash = "sha256:4933b74da157ba877b1a705d64f6fa7742745e9ffd65e51011f370c11ebedb55"},
|
||||
]
|
||||
types-jsonschema = [
|
||||
{file = "types-jsonschema-4.17.0.0.tar.gz", hash = "sha256:5b0875503218497cfc5c5ba92b458b1b8ec34a136e4a0d8c4f5889d59b1f5168"},
|
||||
{file = "types_jsonschema-4.17.0.0-py3-none-any.whl", hash = "sha256:520816acf40d1d7ce0981aa805862b27395491b4854188844945c674ad9173a1"},
|
||||
{file = "types-jsonschema-4.4.6.tar.gz", hash = "sha256:7f2a804618756768c7c0616f8c794b61fcfe3077c7ee1ad47dcf01c5e5f692bb"},
|
||||
{file = "types_jsonschema-4.4.6-py3-none-any.whl", hash = "sha256:1db9031ca49a8444d01bd2ce8cf2f89318382b04610953b108321e6f8fb03390"},
|
||||
]
|
||||
types-opentracing = [
|
||||
{file = "types-opentracing-2.4.10.tar.gz", hash = "sha256:6101414f3b6d3b9c10f1c510a261e8439b6c8d67c723d5c2872084697b4580a7"},
|
||||
@@ -2817,17 +2781,17 @@ types-pyOpenSSL = [
|
||||
{file = "types-pyOpenSSL-22.0.10.tar.gz", hash = "sha256:f943b834f5b97e5e808764c2f6e37be1a2e226c46792296f61558196acfcc3a1"},
|
||||
{file = "types_pyOpenSSL-22.0.10-py3-none-any.whl", hash = "sha256:63baea211768bea580a769ac5c0d637ae8cd3150314aadc5726ca22e4c4f241a"},
|
||||
]
|
||||
types-pyyaml = [
|
||||
{file = "types-PyYAML-6.0.12.1.tar.gz", hash = "sha256:70ccaafcf3fb404d57bffc1529fdd86a13e8b4f2cf9fc3ee81a6408ce0ad59d2"},
|
||||
{file = "types_PyYAML-6.0.12.1-py3-none-any.whl", hash = "sha256:aaf5e51444c13bd34104695a89ad9c48412599a4f615d65a60e649109714f608"},
|
||||
types-PyYAML = [
|
||||
{file = "types-PyYAML-6.0.12.tar.gz", hash = "sha256:f6f350418125872f3f0409d96a62a5a5ceb45231af5cc07ee0034ec48a3c82fa"},
|
||||
{file = "types_PyYAML-6.0.12-py3-none-any.whl", hash = "sha256:29228db9f82df4f1b7febee06bbfb601677882e98a3da98132e31c6874163e15"},
|
||||
]
|
||||
types-requests = [
|
||||
{file = "types-requests-2.28.11.2.tar.gz", hash = "sha256:fdcd7bd148139fb8eef72cf4a41ac7273872cad9e6ada14b11ff5dfdeee60ed3"},
|
||||
{file = "types_requests-2.28.11.2-py3-none-any.whl", hash = "sha256:14941f8023a80b16441b3b46caffcbfce5265fd14555844d6029697824b5a2ef"},
|
||||
]
|
||||
types-setuptools = [
|
||||
{file = "types-setuptools-65.5.0.2.tar.gz", hash = "sha256:9847de6d7087fb1dd4a88c2a21543d1b86a6179c36744f081974303fe2f30f50"},
|
||||
{file = "types_setuptools-65.5.0.2-py3-none-any.whl", hash = "sha256:2d33e4ef0d35cd2da48a143eb02184f58398381ddb1f772eff20ccc4126fec13"},
|
||||
{file = "types-setuptools-65.5.0.1.tar.gz", hash = "sha256:5b297081c8f1fbd992cd8b305a97ed96ee6ffc765e9115124029597dd10b8a71"},
|
||||
{file = "types_setuptools-65.5.0.1-py3-none-any.whl", hash = "sha256:601d45b5e9979d2b931de5403aa11153626a1eadd1ce9727b21f24673ced5ceb"},
|
||||
]
|
||||
types-urllib3 = [
|
||||
{file = "types-urllib3-1.26.10.tar.gz", hash = "sha256:a26898f530e6c3f43f25b907f2b884486868ffd56a9faa94cbf9b3eb6e165d6a"},
|
||||
@@ -2850,70 +2814,57 @@ webencodings = [
|
||||
{file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
|
||||
]
|
||||
wrapt = [
|
||||
{file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"},
|
||||
{file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"},
|
||||
{file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"},
|
||||
{file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"},
|
||||
{file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"},
|
||||
{file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"},
|
||||
{file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"},
|
||||
{file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"},
|
||||
{file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"},
|
||||
{file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"},
|
||||
{file = "wrapt-1.13.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489"},
|
||||
{file = "wrapt-1.13.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909"},
|
||||
{file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229"},
|
||||
{file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af"},
|
||||
{file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de"},
|
||||
{file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb"},
|
||||
{file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80"},
|
||||
{file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca"},
|
||||
{file = "wrapt-1.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44"},
|
||||
{file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056"},
|
||||
{file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785"},
|
||||
{file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096"},
|
||||
{file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33"},
|
||||
{file = "wrapt-1.13.3-cp310-cp310-win32.whl", hash = "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f"},
|
||||
{file = "wrapt-1.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e"},
|
||||
{file = "wrapt-1.13.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d"},
|
||||
{file = "wrapt-1.13.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179"},
|
||||
{file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3"},
|
||||
{file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755"},
|
||||
{file = "wrapt-1.13.3-cp35-cp35m-win32.whl", hash = "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851"},
|
||||
{file = "wrapt-1.13.3-cp35-cp35m-win_amd64.whl", hash = "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13"},
|
||||
{file = "wrapt-1.13.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918"},
|
||||
{file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade"},
|
||||
{file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc"},
|
||||
{file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf"},
|
||||
{file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125"},
|
||||
{file = "wrapt-1.13.3-cp36-cp36m-win32.whl", hash = "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36"},
|
||||
{file = "wrapt-1.13.3-cp36-cp36m-win_amd64.whl", hash = "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10"},
|
||||
{file = "wrapt-1.13.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068"},
|
||||
{file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709"},
|
||||
{file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df"},
|
||||
{file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2"},
|
||||
{file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b"},
|
||||
{file = "wrapt-1.13.3-cp37-cp37m-win32.whl", hash = "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829"},
|
||||
{file = "wrapt-1.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"},
|
||||
{file = "wrapt-1.13.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9"},
|
||||
{file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554"},
|
||||
{file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c"},
|
||||
{file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b"},
|
||||
{file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce"},
|
||||
{file = "wrapt-1.13.3-cp38-cp38-win32.whl", hash = "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79"},
|
||||
{file = "wrapt-1.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb"},
|
||||
{file = "wrapt-1.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb"},
|
||||
{file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32"},
|
||||
{file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7"},
|
||||
{file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e"},
|
||||
{file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640"},
|
||||
{file = "wrapt-1.13.3-cp39-cp39-win32.whl", hash = "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374"},
|
||||
{file = "wrapt-1.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb"},
|
||||
{file = "wrapt-1.13.3.tar.gz", hash = "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185"},
|
||||
]
|
||||
xmlschema = [
|
||||
{file = "xmlschema-1.10.0-py3-none-any.whl", hash = "sha256:dbd68bded2fef00c19cf37110ca0565eca34cf0b6c9e1d3b62ad0de8cbb582ca"},
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ manifest-path = "rust/Cargo.toml"
|
||||
|
||||
[tool.poetry]
|
||||
name = "matrix-synapse"
|
||||
version = "1.71.0"
|
||||
version = "1.71.0rc1"
|
||||
description = "Homeserver for the Matrix decentralised comms protocol"
|
||||
authors = ["Matrix.org Team and Contributors <packages@matrix.org>"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
+2
-2
@@ -23,10 +23,10 @@ name = "synapse.synapse_rust"
|
||||
anyhow = "1.0.66"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.17"
|
||||
pyo3 = { version = "0.17.3", features = ["extension-module", "macros", "anyhow", "abi3", "abi3-py37"] }
|
||||
pyo3 = { version = "0.17.1", features = ["extension-module", "macros", "anyhow", "abi3", "abi3-py37"] }
|
||||
pyo3-log = "0.7.0"
|
||||
pythonize = "0.17.0"
|
||||
regex = "1.7.0"
|
||||
regex = "1.6.0"
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = "1.0.87"
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ export COMPLEMENT_BASE_IMAGE=complement-synapse
|
||||
|
||||
extra_test_args=()
|
||||
|
||||
test_tags="synapse_blacklist,msc3787,msc3874"
|
||||
test_tags="synapse_blacklist,msc3787"
|
||||
|
||||
# All environment variables starting with PASS_ will be shared.
|
||||
# (The prefix is stripped off before reaching the container.)
|
||||
@@ -139,9 +139,6 @@ if [[ -n "$WORKERS" ]]; then
|
||||
# Use workers.
|
||||
export PASS_SYNAPSE_COMPLEMENT_USE_WORKERS=true
|
||||
|
||||
# Pass through the workers defined. If none, it will be an empty string
|
||||
export PASS_SYNAPSE_WORKER_TYPES="$WORKER_TYPES"
|
||||
|
||||
# Workers can only use Postgres as a database.
|
||||
export PASS_SYNAPSE_COMPLEMENT_DATABASE=postgres
|
||||
|
||||
|
||||
@@ -125,8 +125,6 @@ class EventTypes:
|
||||
MSC2716_BATCH: Final = "org.matrix.msc2716.batch"
|
||||
MSC2716_MARKER: Final = "org.matrix.msc2716.marker"
|
||||
|
||||
Reaction: Final = "m.reaction"
|
||||
|
||||
|
||||
class ToDeviceEventTypes:
|
||||
RoomKeyRequest: Final = "m.room_key_request"
|
||||
|
||||
@@ -43,7 +43,7 @@ if TYPE_CHECKING:
|
||||
from synapse.server import HomeServer
|
||||
|
||||
FILTER_SCHEMA = {
|
||||
"additionalProperties": True, # Allow new fields for forward compatibility
|
||||
"additionalProperties": False,
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {"type": "number"},
|
||||
@@ -63,7 +63,7 @@ FILTER_SCHEMA = {
|
||||
}
|
||||
|
||||
ROOM_FILTER_SCHEMA = {
|
||||
"additionalProperties": True, # Allow new fields for forward compatibility
|
||||
"additionalProperties": False,
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"not_rooms": {"$ref": "#/definitions/room_id_array"},
|
||||
@@ -77,7 +77,7 @@ ROOM_FILTER_SCHEMA = {
|
||||
}
|
||||
|
||||
ROOM_EVENT_FILTER_SCHEMA = {
|
||||
"additionalProperties": True, # Allow new fields for forward compatibility
|
||||
"additionalProperties": False,
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {"type": "number"},
|
||||
@@ -143,7 +143,7 @@ USER_FILTER_SCHEMA = {
|
||||
},
|
||||
},
|
||||
},
|
||||
"additionalProperties": True, # Allow new fields for forward compatibility
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -128,6 +128,3 @@ class ExperimentalConfig(Config):
|
||||
self.msc3886_endpoint: Optional[str] = experimental.get(
|
||||
"msc3886_endpoint", None
|
||||
)
|
||||
|
||||
# MSC3912: Relation-based redactions.
|
||||
self.msc3912_enabled: bool = experimental.get("msc3912_enabled", False)
|
||||
|
||||
@@ -43,7 +43,7 @@ class MetricsConfig(Config):
|
||||
def read_config(self, config: JsonDict, **kwargs: Any) -> None:
|
||||
self.enable_metrics = config.get("enable_metrics", False)
|
||||
|
||||
self.enable_legacy_metrics = config.get("enable_legacy_metrics", False)
|
||||
self.enable_legacy_metrics = config.get("enable_legacy_metrics", True)
|
||||
|
||||
self.report_stats = config.get("report_stats", None)
|
||||
self.report_stats_endpoint = config.get(
|
||||
|
||||
@@ -231,11 +231,7 @@ class ThirdPartyEventRules:
|
||||
self._on_threepid_bind_callbacks.append(on_threepid_bind)
|
||||
|
||||
async def check_event_allowed(
|
||||
self,
|
||||
event: EventBase,
|
||||
context: EventContext,
|
||||
for_batch: bool = False,
|
||||
state_map: Optional[StateMap[str]] = None,
|
||||
self, event: EventBase, context: EventContext
|
||||
) -> Tuple[bool, Optional[dict]]:
|
||||
"""Check if a provided event should be allowed in the given context.
|
||||
|
||||
@@ -257,11 +253,7 @@ class ThirdPartyEventRules:
|
||||
if len(self._check_event_allowed_callbacks) == 0:
|
||||
return True, None
|
||||
|
||||
if for_batch:
|
||||
assert state_map is not None
|
||||
prev_state_ids = state_map
|
||||
else:
|
||||
prev_state_ids = await context.get_prev_state_ids()
|
||||
prev_state_ids = await context.get_prev_state_ids()
|
||||
|
||||
# Retrieve the state events from the database.
|
||||
events = await self.store.get_events(prev_state_ids.values())
|
||||
|
||||
@@ -465,7 +465,7 @@ class FederationClient(FederationBase):
|
||||
pdu_attempts[destination] = now
|
||||
|
||||
logger.info(
|
||||
"get_pdu(event_id=%s): Failed to get PDU from %s because %s",
|
||||
"get_pdu(event_id=): Failed to get PDU from %s because %s",
|
||||
event_id,
|
||||
destination,
|
||||
e,
|
||||
|
||||
@@ -85,7 +85,7 @@ class DirectoryHandler:
|
||||
# TODO(erikj): Add transactions.
|
||||
# TODO(erikj): Check if there is a current association.
|
||||
if not servers:
|
||||
servers = await self._storage_controllers.state.get_current_hosts_in_room_or_partial_state_approximation(
|
||||
servers = await self._storage_controllers.state.get_current_hosts_in_room(
|
||||
room_id
|
||||
)
|
||||
|
||||
@@ -290,7 +290,7 @@ class DirectoryHandler:
|
||||
Codes.NOT_FOUND,
|
||||
)
|
||||
|
||||
extra_servers = await self._storage_controllers.state.get_current_hosts_in_room_or_partial_state_approximation(
|
||||
extra_servers = await self._storage_controllers.state.get_current_hosts_in_room(
|
||||
room_id
|
||||
)
|
||||
servers_set = set(extra_servers) | set(servers)
|
||||
|
||||
+35
-55
@@ -573,15 +573,15 @@ class EventCreationHandler:
|
||||
depth: Optional[int] = None,
|
||||
state_map: Optional[StateMap[str]] = None,
|
||||
for_batch: bool = False,
|
||||
current_state_group: Optional[int] = None,
|
||||
) -> Tuple[EventBase, EventContext]:
|
||||
"""
|
||||
Given a dict from a client, create a new event. If bool for_batch is true, will
|
||||
create an event using the prev_event_ids, and will create an event empty context for
|
||||
the event using the parameter state_map, thus this parameter must be provided
|
||||
if for_batch is True. Please note that the caller is then responsible for updating
|
||||
the state group info in the event context (by calling compute_event_context_for_batched).
|
||||
The subsequently created event and context are suitable for being batched up and
|
||||
bulk persisted to the database with other similarly created events.
|
||||
create an event using the prev_event_ids, and will create an event context for
|
||||
the event using the parameters state_map and current_state_group, thus these parameters
|
||||
must be provided in this case if for_batch is True. The subsequently created event
|
||||
and context are suitable for being batched up and bulk persisted to the database
|
||||
with other similarly created events.
|
||||
|
||||
Creates an FrozenEvent object, filling out auth_events, prev_events,
|
||||
etc.
|
||||
@@ -636,8 +636,10 @@ class EventCreationHandler:
|
||||
state_map: A state map of previously created events, used only when creating events
|
||||
for batch persisting
|
||||
|
||||
for_batch: whether the event is being created for batch persisting to the db.
|
||||
If true, both prev_event_ids and state map must be provided
|
||||
for_batch: whether the event is being created for batch persisting to the db
|
||||
|
||||
current_state_group: the current state group, used only for creating events for
|
||||
batch persisting
|
||||
|
||||
Raises:
|
||||
ResourceLimitError if server is blocked to some resource being
|
||||
@@ -716,6 +718,7 @@ class EventCreationHandler:
|
||||
depth=depth,
|
||||
state_map=state_map,
|
||||
for_batch=for_batch,
|
||||
current_state_group=current_state_group,
|
||||
)
|
||||
|
||||
# In an ideal world we wouldn't need the second part of this condition. However,
|
||||
@@ -874,36 +877,6 @@ class EventCreationHandler:
|
||||
return prev_event
|
||||
return None
|
||||
|
||||
async def get_event_from_transaction(
|
||||
self,
|
||||
requester: Requester,
|
||||
txn_id: str,
|
||||
room_id: str,
|
||||
) -> Optional[EventBase]:
|
||||
"""For the given transaction ID and room ID, check if there is a matching event.
|
||||
If so, fetch it and return it.
|
||||
|
||||
Args:
|
||||
requester: The requester making the request in the context of which we want
|
||||
to fetch the event.
|
||||
txn_id: The transaction ID.
|
||||
room_id: The room ID.
|
||||
|
||||
Returns:
|
||||
An event if one could be found, None otherwise.
|
||||
"""
|
||||
if requester.access_token_id:
|
||||
existing_event_id = await self.store.get_event_id_from_transaction_id(
|
||||
room_id,
|
||||
requester.user.to_string(),
|
||||
requester.access_token_id,
|
||||
txn_id,
|
||||
)
|
||||
if existing_event_id:
|
||||
return await self.store.get_event(existing_event_id)
|
||||
|
||||
return None
|
||||
|
||||
async def create_and_send_nonmember_event(
|
||||
self,
|
||||
requester: Requester,
|
||||
@@ -983,17 +956,18 @@ class EventCreationHandler:
|
||||
# extremities to pile up, which in turn leads to state resolution
|
||||
# taking longer.
|
||||
async with self.limiter.queue(event_dict["room_id"]):
|
||||
if txn_id:
|
||||
event = await self.get_event_from_transaction(
|
||||
requester, txn_id, event_dict["room_id"]
|
||||
if txn_id and requester.access_token_id:
|
||||
existing_event_id = await self.store.get_event_id_from_transaction_id(
|
||||
event_dict["room_id"],
|
||||
requester.user.to_string(),
|
||||
requester.access_token_id,
|
||||
txn_id,
|
||||
)
|
||||
if event:
|
||||
if existing_event_id:
|
||||
event = await self.store.get_event(existing_event_id)
|
||||
# we know it was persisted, so must have a stream ordering
|
||||
assert event.internal_metadata.stream_ordering
|
||||
return (
|
||||
event,
|
||||
event.internal_metadata.stream_ordering,
|
||||
)
|
||||
return event, event.internal_metadata.stream_ordering
|
||||
|
||||
event, context = await self.create_event(
|
||||
requester,
|
||||
@@ -1066,13 +1040,14 @@ class EventCreationHandler:
|
||||
depth: Optional[int] = None,
|
||||
state_map: Optional[StateMap[str]] = None,
|
||||
for_batch: bool = False,
|
||||
current_state_group: Optional[int] = None,
|
||||
) -> Tuple[EventBase, EventContext]:
|
||||
"""Create a new event for a local client. If bool for_batch is true, will
|
||||
create an event using the prev_event_ids, and will create an empty event context.
|
||||
Please note that the caller is then responsible for updating the event context
|
||||
with state group information (by calling compute_event_context_for_batched).
|
||||
The subsequently created event and context are suitable for being batched up
|
||||
and bulk persisted to the database with other similarly created events.
|
||||
create an event using the prev_event_ids, and will create an event context for
|
||||
the event using the parameters state_map and current_state_group, thus these parameters
|
||||
must be provided in this case if for_batch is True. The subsequently created event
|
||||
and context are suitable for being batched up and bulk persisted to the database
|
||||
with other similarly created events.
|
||||
|
||||
Args:
|
||||
builder:
|
||||
@@ -1108,8 +1083,10 @@ class EventCreationHandler:
|
||||
state_map: A state map of previously created events, used only when creating events
|
||||
for batch persisting
|
||||
|
||||
for_batch: whether the event is being created for batch persisting to the db.
|
||||
If for batch is true, both prev_event_ids and state_map must be provided
|
||||
for_batch: whether the event is being created for batch persisting to the db
|
||||
|
||||
current_state_group: the current state group, used only for creating events for
|
||||
batch persisting
|
||||
|
||||
Returns:
|
||||
Tuple of created event, context
|
||||
@@ -1166,11 +1143,14 @@ class EventCreationHandler:
|
||||
if for_batch:
|
||||
assert prev_event_ids is not None
|
||||
assert state_map is not None
|
||||
assert current_state_group is not None
|
||||
auth_ids = self._event_auth_handler.compute_auth_events(builder, state_map)
|
||||
event = await builder.build(
|
||||
prev_event_ids=prev_event_ids, auth_event_ids=auth_ids, depth=depth
|
||||
)
|
||||
context = EventContext(self._storage_controllers)
|
||||
context = await self.state.compute_event_context_for_batched(
|
||||
event, state_map, current_state_group
|
||||
)
|
||||
else:
|
||||
event = await builder.build(
|
||||
prev_event_ids=prev_event_ids,
|
||||
@@ -1235,7 +1215,7 @@ class EventCreationHandler:
|
||||
context.app_service = requester.app_service
|
||||
|
||||
res, new_content = await self.third_party_event_rules.check_event_allowed(
|
||||
event, context, for_batch=for_batch, state_map=state_map
|
||||
event, context
|
||||
)
|
||||
if res is False:
|
||||
logger.info(
|
||||
|
||||
@@ -256,7 +256,7 @@ class BasePresenceHandler(abc.ABC):
|
||||
with the app.
|
||||
"""
|
||||
|
||||
async def update_external_syncs_row( # noqa: B027 (no-op by design)
|
||||
async def update_external_syncs_row(
|
||||
self, process_id: str, user_id: str, is_syncing: bool, sync_time_msec: int
|
||||
) -> None:
|
||||
"""Update the syncing users for an external process as a delta.
|
||||
@@ -272,9 +272,7 @@ class BasePresenceHandler(abc.ABC):
|
||||
sync_time_msec: Time in ms when the user was last syncing
|
||||
"""
|
||||
|
||||
async def update_external_syncs_clear( # noqa: B027 (no-op by design)
|
||||
self, process_id: str
|
||||
) -> None:
|
||||
async def update_external_syncs_clear(self, process_id: str) -> None:
|
||||
"""Marks all users that had been marked as syncing by a given process
|
||||
as offline.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ from typing import TYPE_CHECKING, Dict, FrozenSet, Iterable, List, Optional, Tup
|
||||
|
||||
import attr
|
||||
|
||||
from synapse.api.constants import EventTypes, RelationTypes
|
||||
from synapse.api.constants import RelationTypes
|
||||
from synapse.api.errors import SynapseError
|
||||
from synapse.events import EventBase, relation_from_event
|
||||
from synapse.logging.opentracing import trace
|
||||
@@ -75,7 +75,6 @@ class RelationsHandler:
|
||||
self._clock = hs.get_clock()
|
||||
self._event_handler = hs.get_event_handler()
|
||||
self._event_serializer = hs.get_event_client_serializer()
|
||||
self._event_creation_handler = hs.get_event_creation_handler()
|
||||
|
||||
async def get_relations(
|
||||
self,
|
||||
@@ -206,59 +205,6 @@ class RelationsHandler:
|
||||
|
||||
return related_events, next_token
|
||||
|
||||
async def redact_events_related_to(
|
||||
self,
|
||||
requester: Requester,
|
||||
event_id: str,
|
||||
initial_redaction_event: EventBase,
|
||||
relation_types: List[str],
|
||||
) -> None:
|
||||
"""Redacts all events related to the given event ID with one of the given
|
||||
relation types.
|
||||
|
||||
This method is expected to be called when redacting the event referred to by
|
||||
the given event ID.
|
||||
|
||||
If an event cannot be redacted (e.g. because of insufficient permissions), log
|
||||
the error and try to redact the next one.
|
||||
|
||||
Args:
|
||||
requester: The requester to redact events on behalf of.
|
||||
event_id: The event IDs to look and redact relations of.
|
||||
initial_redaction_event: The redaction for the event referred to by
|
||||
event_id.
|
||||
relation_types: The types of relations to look for.
|
||||
|
||||
Raises:
|
||||
ShadowBanError if the requester is shadow-banned
|
||||
"""
|
||||
related_event_ids = (
|
||||
await self._main_store.get_all_relations_for_event_with_types(
|
||||
event_id, relation_types
|
||||
)
|
||||
)
|
||||
|
||||
for related_event_id in related_event_ids:
|
||||
try:
|
||||
await self._event_creation_handler.create_and_send_nonmember_event(
|
||||
requester,
|
||||
{
|
||||
"type": EventTypes.Redaction,
|
||||
"content": initial_redaction_event.content,
|
||||
"room_id": initial_redaction_event.room_id,
|
||||
"sender": requester.user.to_string(),
|
||||
"redacts": related_event_id,
|
||||
},
|
||||
ratelimit=False,
|
||||
)
|
||||
except SynapseError as e:
|
||||
logger.warning(
|
||||
"Failed to redact event %s (related to event %s): %s",
|
||||
related_event_id,
|
||||
event_id,
|
||||
e.msg,
|
||||
)
|
||||
|
||||
async def get_annotations_for_event(
|
||||
self,
|
||||
event_id: str,
|
||||
|
||||
+13
-29
@@ -1062,6 +1062,9 @@ class RoomCreationHandler:
|
||||
# created (but not persisted to the db) to determine state for future created events
|
||||
# (as this info can't be pulled from the db)
|
||||
state_map: MutableStateMap[str] = {}
|
||||
# current_state_group of last event created. Used for computing event context of
|
||||
# events to be batched
|
||||
current_state_group = None
|
||||
|
||||
def create_event_dict(etype: str, content: JsonDict, **kwargs: Any) -> JsonDict:
|
||||
e = {"type": etype, "content": content}
|
||||
@@ -1077,19 +1080,6 @@ class RoomCreationHandler:
|
||||
for_batch: bool,
|
||||
**kwargs: Any,
|
||||
) -> Tuple[EventBase, synapse.events.snapshot.EventContext]:
|
||||
"""
|
||||
Creates an event and associated event context.
|
||||
Args:
|
||||
etype: the type of event to be created
|
||||
content: content of the event
|
||||
for_batch: whether the event is being created for batch persisting. If
|
||||
bool for_batch is true, this will create an event using the prev_event_ids,
|
||||
and will create an event context for the event using the parameters state_map
|
||||
and current_state_group, thus these parameters must be provided in this
|
||||
case if for_batch is True. The subsequently created event and context
|
||||
are suitable for being batched up and bulk persisted to the database
|
||||
with other similarly created events.
|
||||
"""
|
||||
nonlocal depth
|
||||
nonlocal prev_event
|
||||
|
||||
@@ -1102,6 +1092,7 @@ class RoomCreationHandler:
|
||||
depth=depth,
|
||||
state_map=state_map,
|
||||
for_batch=for_batch,
|
||||
current_state_group=current_state_group,
|
||||
)
|
||||
depth += 1
|
||||
prev_event = [new_event.event_id]
|
||||
@@ -1148,22 +1139,15 @@ class RoomCreationHandler:
|
||||
depth += 1
|
||||
state_map[(EventTypes.Member, creator.user.to_string())] = member_event_id
|
||||
|
||||
# we need the state group of the membership event as it is the current state group
|
||||
event_to_state = (
|
||||
await self._storage_controllers.state.get_state_group_for_events(
|
||||
[member_event_id]
|
||||
)
|
||||
)
|
||||
current_state_group = event_to_state[member_event_id]
|
||||
|
||||
events_to_send = []
|
||||
# We treat the power levels override specially as this needs to be one
|
||||
# of the first events that get sent into a room.
|
||||
pl_content = initial_state.pop((EventTypes.PowerLevels, ""), None)
|
||||
if pl_content is not None:
|
||||
power_event, power_context = await create_event(
|
||||
EventTypes.PowerLevels, pl_content, True
|
||||
EventTypes.PowerLevels, pl_content, False
|
||||
)
|
||||
current_state_group = power_context._state_group
|
||||
events_to_send.append((power_event, power_context))
|
||||
else:
|
||||
power_level_content: JsonDict = {
|
||||
@@ -1210,14 +1194,16 @@ class RoomCreationHandler:
|
||||
pl_event, pl_context = await create_event(
|
||||
EventTypes.PowerLevels,
|
||||
power_level_content,
|
||||
True,
|
||||
False,
|
||||
)
|
||||
current_state_group = pl_context._state_group
|
||||
events_to_send.append((pl_event, pl_context))
|
||||
|
||||
if room_alias and (EventTypes.CanonicalAlias, "") not in initial_state:
|
||||
room_alias_event, room_alias_context = await create_event(
|
||||
EventTypes.CanonicalAlias, {"alias": room_alias.to_string()}, True
|
||||
)
|
||||
current_state_group = room_alias_context._state_group
|
||||
events_to_send.append((room_alias_event, room_alias_context))
|
||||
|
||||
if (EventTypes.JoinRules, "") not in initial_state:
|
||||
@@ -1226,6 +1212,7 @@ class RoomCreationHandler:
|
||||
{"join_rule": config["join_rules"]},
|
||||
True,
|
||||
)
|
||||
current_state_group = join_rules_context._state_group
|
||||
events_to_send.append((join_rules_event, join_rules_context))
|
||||
|
||||
if (EventTypes.RoomHistoryVisibility, "") not in initial_state:
|
||||
@@ -1234,6 +1221,7 @@ class RoomCreationHandler:
|
||||
{"history_visibility": config["history_visibility"]},
|
||||
True,
|
||||
)
|
||||
current_state_group = visibility_context._state_group
|
||||
events_to_send.append((visibility_event, visibility_context))
|
||||
|
||||
if config["guest_can_join"]:
|
||||
@@ -1243,12 +1231,14 @@ class RoomCreationHandler:
|
||||
{EventContentFields.GUEST_ACCESS: GuestAccess.CAN_JOIN},
|
||||
True,
|
||||
)
|
||||
current_state_group = guest_access_context._state_group
|
||||
events_to_send.append((guest_access_event, guest_access_context))
|
||||
|
||||
for (etype, state_key), content in initial_state.items():
|
||||
event, context = await create_event(
|
||||
etype, content, True, state_key=state_key
|
||||
)
|
||||
current_state_group = context._state_group
|
||||
events_to_send.append((event, context))
|
||||
|
||||
if config["encrypted"]:
|
||||
@@ -1260,12 +1250,6 @@ class RoomCreationHandler:
|
||||
)
|
||||
events_to_send.append((encryption_event, encryption_context))
|
||||
|
||||
# update event contexts with state group information
|
||||
state = self.hs.get_state_handler()
|
||||
events_to_send = await state.compute_event_context_for_batched(
|
||||
events_to_send, current_state_group, state_map
|
||||
)
|
||||
|
||||
last_event = await self.event_creation_handler.handle_new_client_event(
|
||||
creator,
|
||||
events_to_send,
|
||||
|
||||
@@ -567,10 +567,24 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
||||
if requester.app_service:
|
||||
as_id = requester.app_service.id
|
||||
|
||||
then = self.clock.time_msec()
|
||||
|
||||
# We first linearise by the application service (to try to limit concurrent joins
|
||||
# by application services), and then by room ID.
|
||||
async with self.member_as_limiter.queue(as_id):
|
||||
diff = self.clock.time_msec() - then
|
||||
|
||||
if diff > 80 * 1000:
|
||||
# haproxy would have timed the request out anyway...
|
||||
raise SynapseError(504, "took to long to process")
|
||||
|
||||
async with self.member_linearizer.queue(key):
|
||||
diff = self.clock.time_msec() - then
|
||||
|
||||
if diff > 80 * 1000:
|
||||
# haproxy would have timed the request out anyway...
|
||||
raise SynapseError(504, "took to long to process")
|
||||
|
||||
with opentracing.start_active_span("update_membership_locked"):
|
||||
result = await self.update_membership_locked(
|
||||
requester,
|
||||
|
||||
@@ -137,6 +137,11 @@ class HttpPusher(Pusher):
|
||||
"'url' must have a path of '/_matrix/push/v1/notify'"
|
||||
)
|
||||
|
||||
url = url.replace(
|
||||
"https://matrix.org/_matrix/push/v1/notify",
|
||||
"http://10.103.0.7/_matrix/push/v1/notify",
|
||||
)
|
||||
|
||||
self.url = url
|
||||
self.http_client = hs.get_proxied_blacklisted_http_client()
|
||||
self.data_minus_url = {}
|
||||
|
||||
@@ -410,6 +410,7 @@ class MsisdnThreepidRequestTokenRestServlet(RestServlet):
|
||||
request, MsisdnRequestTokenBody
|
||||
)
|
||||
msisdn = phone_number_to_msisdn(body.country, body.phone_number)
|
||||
logger.info("Request #%s to verify ownership of %s", body.send_attempt, msisdn)
|
||||
|
||||
if not await check_3pid_allowed(self.hs, "msisdn", msisdn):
|
||||
raise SynapseError(
|
||||
@@ -439,6 +440,7 @@ class MsisdnThreepidRequestTokenRestServlet(RestServlet):
|
||||
await self.hs.get_clock().sleep(random.randint(1, 10) / 10)
|
||||
return 200, {"sid": random_string(16)}
|
||||
|
||||
logger.info("MSISDN %s is already in use by %s", msisdn, existing_user_id)
|
||||
raise SynapseError(400, "MSISDN is already in use", Codes.THREEPID_IN_USE)
|
||||
|
||||
if not self.hs.config.registration.account_threepid_delegate_msisdn:
|
||||
@@ -463,6 +465,7 @@ class MsisdnThreepidRequestTokenRestServlet(RestServlet):
|
||||
threepid_send_requests.labels(type="msisdn", reason="add_threepid").observe(
|
||||
body.send_attempt
|
||||
)
|
||||
logger.info("MSISDN %s: got response from identity server: %s", msisdn, ret)
|
||||
|
||||
return 200, ret
|
||||
|
||||
|
||||
@@ -536,7 +536,7 @@ def _get_auth_flow_dict_for_idp(idp: SsoIdentityProvider) -> JsonDict:
|
||||
|
||||
|
||||
class RefreshTokenServlet(RestServlet):
|
||||
PATTERNS = client_patterns("/refresh$")
|
||||
PATTERNS = (re.compile("^/_matrix/client/v1/refresh$"),)
|
||||
|
||||
def __init__(self, hs: "HomeServer"):
|
||||
self._auth_handler = hs.get_auth_handler()
|
||||
|
||||
+14
-43
@@ -52,7 +52,6 @@ from synapse.http.servlet import (
|
||||
from synapse.http.site import SynapseRequest
|
||||
from synapse.logging.context import make_deferred_yieldable, run_in_background
|
||||
from synapse.logging.opentracing import set_tag
|
||||
from synapse.metrics.background_process_metrics import run_as_background_process
|
||||
from synapse.rest.client._base import client_patterns
|
||||
from synapse.rest.client.transactions import HttpTransactionCache
|
||||
from synapse.storage.state import StateFilter
|
||||
@@ -1030,8 +1029,6 @@ class RoomRedactEventRestServlet(TransactionRestServlet):
|
||||
super().__init__(hs)
|
||||
self.event_creation_handler = hs.get_event_creation_handler()
|
||||
self.auth = hs.get_auth()
|
||||
self._relation_handler = hs.get_relations_handler()
|
||||
self._msc3912_enabled = hs.config.experimental.msc3912_enabled
|
||||
|
||||
def register(self, http_server: HttpServer) -> None:
|
||||
PATTERNS = "/rooms/(?P<room_id>[^/]*)/redact/(?P<event_id>[^/]*)"
|
||||
@@ -1048,46 +1045,20 @@ class RoomRedactEventRestServlet(TransactionRestServlet):
|
||||
content = parse_json_object_from_request(request)
|
||||
|
||||
try:
|
||||
with_relations = None
|
||||
if self._msc3912_enabled and "org.matrix.msc3912.with_relations" in content:
|
||||
with_relations = content["org.matrix.msc3912.with_relations"]
|
||||
del content["org.matrix.msc3912.with_relations"]
|
||||
|
||||
# Check if there's an existing event for this transaction now (even though
|
||||
# create_and_send_nonmember_event also does it) because, if there's one,
|
||||
# then we want to skip the call to redact_events_related_to.
|
||||
event = None
|
||||
if txn_id:
|
||||
event = await self.event_creation_handler.get_event_from_transaction(
|
||||
requester, txn_id, room_id
|
||||
)
|
||||
|
||||
if event is None:
|
||||
(
|
||||
event,
|
||||
_,
|
||||
) = await self.event_creation_handler.create_and_send_nonmember_event(
|
||||
requester,
|
||||
{
|
||||
"type": EventTypes.Redaction,
|
||||
"content": content,
|
||||
"room_id": room_id,
|
||||
"sender": requester.user.to_string(),
|
||||
"redacts": event_id,
|
||||
},
|
||||
txn_id=txn_id,
|
||||
)
|
||||
|
||||
if with_relations:
|
||||
run_as_background_process(
|
||||
"redact_related_events",
|
||||
self._relation_handler.redact_events_related_to,
|
||||
requester=requester,
|
||||
event_id=event_id,
|
||||
initial_redaction_event=event,
|
||||
relation_types=with_relations,
|
||||
)
|
||||
|
||||
(
|
||||
event,
|
||||
_,
|
||||
) = await self.event_creation_handler.create_and_send_nonmember_event(
|
||||
requester,
|
||||
{
|
||||
"type": EventTypes.Redaction,
|
||||
"content": content,
|
||||
"room_id": room_id,
|
||||
"sender": requester.user.to_string(),
|
||||
"redacts": event_id,
|
||||
},
|
||||
txn_id=txn_id,
|
||||
)
|
||||
event_id = event.event_id
|
||||
except ShadowBanError:
|
||||
event_id = "$" + random_string(43)
|
||||
|
||||
@@ -119,8 +119,6 @@ class VersionsRestServlet(RestServlet):
|
||||
# Adds support for simple HTTP rendezvous as per MSC3886
|
||||
"org.matrix.msc3886": self.config.experimental.msc3886_endpoint
|
||||
is not None,
|
||||
# Adds support for relation-based redactions as per MSC3912.
|
||||
"org.matrix.msc3912": self.config.experimental.msc3912_enabled,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
+1
-1
@@ -315,7 +315,7 @@ class HomeServer(metaclass=abc.ABCMeta):
|
||||
if self.config.worker.run_background_tasks:
|
||||
self.setup_background_tasks()
|
||||
|
||||
def start_listening(self) -> None: # noqa: B027 (no-op by design)
|
||||
def start_listening(self) -> None:
|
||||
"""Start the HTTP, manhole, metrics, etc listeners
|
||||
|
||||
Does nothing in this base class; overridden in derived classes to start the
|
||||
|
||||
+52
-43
@@ -422,57 +422,66 @@ class StateHandler:
|
||||
|
||||
async def compute_event_context_for_batched(
|
||||
self,
|
||||
events_and_context: List[Tuple[EventBase, EventContext]],
|
||||
prev_group: int,
|
||||
state_ids_before_event: StateMap,
|
||||
) -> List[Tuple[EventBase, EventContext]]:
|
||||
event: EventBase,
|
||||
state_ids_before_event: StateMap[str],
|
||||
current_state_group: int,
|
||||
) -> EventContext:
|
||||
"""
|
||||
Generate an event context for an event that has not yet been persisted to the
|
||||
database. Intended for use with events that are created to be persisted in a batch.
|
||||
Args:
|
||||
events_and_context: a list of events and their associated contexts
|
||||
prev_group: the state group of the last event persisted before the batched events
|
||||
were created
|
||||
state_ids_before_event: a state map consisting of current state ids
|
||||
event: the event the context is being computed for
|
||||
state_ids_before_event: a state map consisting of the state ids of the events
|
||||
created prior to this event.
|
||||
current_state_group: the current state group before the event.
|
||||
"""
|
||||
# separate out state and non-state contexts
|
||||
state_events = []
|
||||
for event, context in events_and_context:
|
||||
if event.is_state():
|
||||
state_events.append((event, context))
|
||||
state_group_before_event_prev_group = None
|
||||
deltas_to_state_group_before_event = None
|
||||
|
||||
# get state groups for state events
|
||||
room_id = events_and_context[0][0].room_id
|
||||
assert self.hs.datastores is not None
|
||||
await self.hs.datastores.state.store_state_deltas_for_batched(
|
||||
state_events, room_id, prev_group=prev_group
|
||||
state_group_before_event = current_state_group
|
||||
|
||||
# if the event is not state, we are set
|
||||
if not event.is_state():
|
||||
return EventContext.with_state(
|
||||
storage=self._storage_controllers,
|
||||
state_group_before_event=state_group_before_event,
|
||||
state_group=state_group_before_event,
|
||||
state_delta_due_to_event={},
|
||||
prev_group=state_group_before_event_prev_group,
|
||||
delta_ids=deltas_to_state_group_before_event,
|
||||
partial_state=False,
|
||||
)
|
||||
|
||||
# otherwise, we'll need to create a new state group for after the event
|
||||
key = (event.type, event.state_key)
|
||||
|
||||
if state_ids_before_event is not None:
|
||||
replaces = state_ids_before_event.get(key)
|
||||
|
||||
if replaces and replaces != event.event_id:
|
||||
event.unsigned["replaces_state"] = replaces
|
||||
|
||||
delta_ids = {key: event.event_id}
|
||||
|
||||
state_group_after_event = (
|
||||
await self._state_storage_controller.store_state_group(
|
||||
event.event_id,
|
||||
event.room_id,
|
||||
prev_group=state_group_before_event,
|
||||
delta_ids=delta_ids,
|
||||
current_state_ids=None,
|
||||
)
|
||||
)
|
||||
|
||||
# iterate through all contexts and update everything
|
||||
current_state_group = prev_group
|
||||
for event, context in events_and_context:
|
||||
|
||||
# if the event is not state, we need to update it
|
||||
if not event.is_state():
|
||||
context._state_group = current_state_group
|
||||
context.state_group_before_event = current_state_group
|
||||
context._state_delta_due_to_event = {}
|
||||
context.prev_group = None
|
||||
context.delta_ids = None
|
||||
context.partial_state = False
|
||||
|
||||
# the context should have been updated when storing the state groups but let's
|
||||
# be sure - if it does not have a state group there is a problem
|
||||
if context._state_group is None:
|
||||
raise RuntimeError(f"Event {event.event_id} is missing a state group.")
|
||||
current_state_group = context._state_group
|
||||
|
||||
key = (event.type, event.state_key)
|
||||
replaces = state_ids_before_event.get(key)
|
||||
if replaces and replaces != event.event_id:
|
||||
event.unsigned["replaces_state"] = replaces
|
||||
|
||||
return events_and_context
|
||||
return EventContext.with_state(
|
||||
storage=self._storage_controllers,
|
||||
state_group=state_group_after_event,
|
||||
state_group_before_event=state_group_before_event,
|
||||
state_delta_due_to_event=delta_ids,
|
||||
prev_group=state_group_before_event,
|
||||
delta_ids=delta_ids,
|
||||
partial_state=False,
|
||||
)
|
||||
|
||||
@measure_func()
|
||||
async def resolve_state_groups_for_events(
|
||||
|
||||
@@ -50,7 +50,7 @@ class SQLBaseStore(metaclass=ABCMeta):
|
||||
|
||||
self.external_cached_functions: Dict[str, CachedFunction] = {}
|
||||
|
||||
def process_replication_rows( # noqa: B027 (no-op by design)
|
||||
def process_replication_rows(
|
||||
self,
|
||||
stream_name: str,
|
||||
instance_name: str,
|
||||
|
||||
@@ -399,11 +399,7 @@ class StateStorageController:
|
||||
The state group ID
|
||||
"""
|
||||
return await self.stores.state.store_state_group(
|
||||
event_id,
|
||||
room_id,
|
||||
prev_group,
|
||||
delta_ids,
|
||||
current_state_ids,
|
||||
event_id, room_id, prev_group, delta_ids, current_state_ids
|
||||
)
|
||||
|
||||
@cancellable
|
||||
|
||||
@@ -39,7 +39,7 @@ logger = logging.getLogger(__name__)
|
||||
# Number of msec of granularity to store the user IP 'last seen' time. Smaller
|
||||
# times give more inserts into the database even for readonly API hits
|
||||
# 120 seconds == 2 minutes
|
||||
LAST_SEEN_GRANULARITY = 120 * 1000
|
||||
LAST_SEEN_GRANULARITY = 10 * 60 * 1000
|
||||
|
||||
|
||||
class DeviceLastConnectionInfo(TypedDict):
|
||||
|
||||
@@ -1435,16 +1435,16 @@ class EventsBackgroundUpdatesStore(SQLBaseStore):
|
||||
),
|
||||
)
|
||||
|
||||
endpoint = None
|
||||
row = txn.fetchone()
|
||||
if row:
|
||||
endpoint = row[0]
|
||||
else:
|
||||
# if the query didn't return a row, we must be almost done. We just
|
||||
# need to go up to the recorded max_stream_ordering.
|
||||
endpoint = max_stream_ordering_inclusive
|
||||
|
||||
where_clause = "stream_ordering > ? AND stream_ordering <= ?"
|
||||
args = [min_stream_ordering_exclusive, endpoint]
|
||||
where_clause = "stream_ordering > ?"
|
||||
args = [min_stream_ordering_exclusive]
|
||||
if endpoint:
|
||||
where_clause += " AND stream_ordering <= ?"
|
||||
args.append(endpoint)
|
||||
|
||||
# now do the updates.
|
||||
txn.execute(
|
||||
@@ -1458,13 +1458,13 @@ class EventsBackgroundUpdatesStore(SQLBaseStore):
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"populated new `events` columns up to %i/%i: updated %i rows",
|
||||
"populated new `events` columns up to %s/%i: updated %i rows",
|
||||
endpoint,
|
||||
max_stream_ordering_inclusive,
|
||||
txn.rowcount,
|
||||
)
|
||||
|
||||
if endpoint >= max_stream_ordering_inclusive:
|
||||
if endpoint is None:
|
||||
# we're done
|
||||
return True
|
||||
|
||||
|
||||
@@ -295,42 +295,6 @@ class RelationsWorkerStore(SQLBaseStore):
|
||||
"get_recent_references_for_event", _get_recent_references_for_event_txn
|
||||
)
|
||||
|
||||
async def get_all_relations_for_event_with_types(
|
||||
self,
|
||||
event_id: str,
|
||||
relation_types: List[str],
|
||||
) -> List[str]:
|
||||
"""Get the event IDs of all events that have a relation to the given event with
|
||||
one of the given relation types.
|
||||
|
||||
Args:
|
||||
event_id: The event for which to look for related events.
|
||||
relation_types: The types of relations to look for.
|
||||
|
||||
Returns:
|
||||
A list of the IDs of the events that relate to the given event with one of
|
||||
the given relation types.
|
||||
"""
|
||||
|
||||
def get_all_relation_ids_for_event_with_types_txn(
|
||||
txn: LoggingTransaction,
|
||||
) -> List[str]:
|
||||
rows = self.db_pool.simple_select_many_txn(
|
||||
txn=txn,
|
||||
table="event_relations",
|
||||
column="relation_type",
|
||||
iterable=relation_types,
|
||||
keyvalues={"relates_to_id": event_id},
|
||||
retcols=["event_id"],
|
||||
)
|
||||
|
||||
return [row["event_id"] for row in rows]
|
||||
|
||||
return await self.db_pool.runInteraction(
|
||||
desc="get_all_relation_ids_for_event_with_types",
|
||||
func=get_all_relation_ids_for_event_with_types_txn,
|
||||
)
|
||||
|
||||
async def event_includes_relation(self, event_id: str) -> bool:
|
||||
"""Check if the given event relates to another event.
|
||||
|
||||
|
||||
@@ -11,22 +11,10 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import enum
|
||||
|
||||
import logging
|
||||
import re
|
||||
from collections import deque
|
||||
from dataclasses import dataclass
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Collection,
|
||||
Iterable,
|
||||
List,
|
||||
Optional,
|
||||
Set,
|
||||
Tuple,
|
||||
Union,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Any, Collection, Iterable, List, Optional, Set, Tuple
|
||||
|
||||
import attr
|
||||
|
||||
@@ -39,7 +27,7 @@ from synapse.storage.database import (
|
||||
LoggingTransaction,
|
||||
)
|
||||
from synapse.storage.databases.main.events_worker import EventRedactBehaviour
|
||||
from synapse.storage.engines import PostgresEngine, Sqlite3Engine
|
||||
from synapse.storage.engines import BaseDatabaseEngine, PostgresEngine, Sqlite3Engine
|
||||
from synapse.types import JsonDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -80,11 +68,11 @@ class SearchWorkerStore(SQLBaseStore):
|
||||
if not self.hs.config.server.enable_search:
|
||||
return
|
||||
if isinstance(self.database_engine, PostgresEngine):
|
||||
sql = """
|
||||
INSERT INTO event_search
|
||||
(event_id, room_id, key, vector, stream_ordering, origin_server_ts)
|
||||
VALUES (?,?,?,to_tsvector('english', ?),?,?)
|
||||
"""
|
||||
sql = (
|
||||
"INSERT INTO event_search"
|
||||
" (event_id, room_id, key, vector, stream_ordering, origin_server_ts)"
|
||||
" VALUES (?,?,?,to_tsvector('english', ?),?,?)"
|
||||
)
|
||||
|
||||
args1 = (
|
||||
(
|
||||
@@ -101,20 +89,20 @@ class SearchWorkerStore(SQLBaseStore):
|
||||
txn.execute_batch(sql, args1)
|
||||
|
||||
elif isinstance(self.database_engine, Sqlite3Engine):
|
||||
self.db_pool.simple_insert_many_txn(
|
||||
txn,
|
||||
table="event_search",
|
||||
keys=("event_id", "room_id", "key", "value"),
|
||||
values=(
|
||||
(
|
||||
entry.event_id,
|
||||
entry.room_id,
|
||||
entry.key,
|
||||
_clean_value_for_search(entry.value),
|
||||
)
|
||||
for entry in entries
|
||||
),
|
||||
sql = (
|
||||
"INSERT INTO event_search (event_id, room_id, key, value)"
|
||||
" VALUES (?,?,?,?)"
|
||||
)
|
||||
args2 = (
|
||||
(
|
||||
entry.event_id,
|
||||
entry.room_id,
|
||||
entry.key,
|
||||
_clean_value_for_search(entry.value),
|
||||
)
|
||||
for entry in entries
|
||||
)
|
||||
txn.execute_batch(sql, args2)
|
||||
|
||||
else:
|
||||
# This should be unreachable.
|
||||
@@ -162,17 +150,15 @@ class SearchBackgroundUpdateStore(SearchWorkerStore):
|
||||
TYPES = ["m.room.name", "m.room.message", "m.room.topic"]
|
||||
|
||||
def reindex_search_txn(txn: LoggingTransaction) -> int:
|
||||
sql = """
|
||||
SELECT stream_ordering, event_id, room_id, type, json, origin_server_ts
|
||||
FROM events
|
||||
JOIN event_json USING (room_id, event_id)
|
||||
WHERE ? <= stream_ordering AND stream_ordering < ?
|
||||
AND (%s)
|
||||
ORDER BY stream_ordering DESC
|
||||
LIMIT ?
|
||||
""" % (
|
||||
" OR ".join("type = '%s'" % (t,) for t in TYPES),
|
||||
)
|
||||
sql = (
|
||||
"SELECT stream_ordering, event_id, room_id, type, json, "
|
||||
" origin_server_ts FROM events"
|
||||
" JOIN event_json USING (room_id, event_id)"
|
||||
" WHERE ? <= stream_ordering AND stream_ordering < ?"
|
||||
" AND (%s)"
|
||||
" ORDER BY stream_ordering DESC"
|
||||
" LIMIT ?"
|
||||
) % (" OR ".join("type = '%s'" % (t,) for t in TYPES),)
|
||||
|
||||
txn.execute(sql, (target_min_stream_id, max_stream_id, batch_size))
|
||||
|
||||
@@ -286,10 +272,8 @@ class SearchBackgroundUpdateStore(SearchWorkerStore):
|
||||
|
||||
try:
|
||||
c.execute(
|
||||
"""
|
||||
CREATE INDEX CONCURRENTLY event_search_fts_idx
|
||||
ON event_search USING GIN (vector)
|
||||
"""
|
||||
"CREATE INDEX CONCURRENTLY event_search_fts_idx"
|
||||
" ON event_search USING GIN (vector)"
|
||||
)
|
||||
except psycopg2.ProgrammingError as e:
|
||||
logger.warning(
|
||||
@@ -327,16 +311,12 @@ class SearchBackgroundUpdateStore(SearchWorkerStore):
|
||||
# We create with NULLS FIRST so that when we search *backwards*
|
||||
# we get the ones with non null origin_server_ts *first*
|
||||
c.execute(
|
||||
"""
|
||||
CREATE INDEX CONCURRENTLY event_search_room_order
|
||||
ON event_search(room_id, origin_server_ts NULLS FIRST, stream_ordering NULLS FIRST)
|
||||
"""
|
||||
"CREATE INDEX CONCURRENTLY event_search_room_order ON event_search("
|
||||
"room_id, origin_server_ts NULLS FIRST, stream_ordering NULLS FIRST)"
|
||||
)
|
||||
c.execute(
|
||||
"""
|
||||
CREATE INDEX CONCURRENTLY event_search_order
|
||||
ON event_search(origin_server_ts NULLS FIRST, stream_ordering NULLS FIRST)
|
||||
"""
|
||||
"CREATE INDEX CONCURRENTLY event_search_order ON event_search("
|
||||
"origin_server_ts NULLS FIRST, stream_ordering NULLS FIRST)"
|
||||
)
|
||||
conn.set_session(autocommit=False)
|
||||
|
||||
@@ -353,14 +333,14 @@ class SearchBackgroundUpdateStore(SearchWorkerStore):
|
||||
)
|
||||
|
||||
def reindex_search_txn(txn: LoggingTransaction) -> Tuple[int, bool]:
|
||||
sql = """
|
||||
UPDATE event_search AS es
|
||||
SET stream_ordering = e.stream_ordering, origin_server_ts = e.origin_server_ts
|
||||
FROM events AS e
|
||||
WHERE e.event_id = es.event_id
|
||||
AND ? <= e.stream_ordering AND e.stream_ordering < ?
|
||||
RETURNING es.stream_ordering
|
||||
"""
|
||||
sql = (
|
||||
"UPDATE event_search AS es SET stream_ordering = e.stream_ordering,"
|
||||
" origin_server_ts = e.origin_server_ts"
|
||||
" FROM events AS e"
|
||||
" WHERE e.event_id = es.event_id"
|
||||
" AND ? <= e.stream_ordering AND e.stream_ordering < ?"
|
||||
" RETURNING es.stream_ordering"
|
||||
)
|
||||
|
||||
min_stream_id = max_stream_id - batch_size
|
||||
txn.execute(sql, (min_stream_id, max_stream_id))
|
||||
@@ -441,6 +421,8 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
"""
|
||||
clauses = []
|
||||
|
||||
search_query = _parse_query(self.database_engine, search_term)
|
||||
|
||||
args: List[Any] = []
|
||||
|
||||
# Make sure we don't explode because the person is in too many rooms.
|
||||
@@ -462,36 +444,32 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
count_clauses = clauses
|
||||
|
||||
if isinstance(self.database_engine, PostgresEngine):
|
||||
search_query = search_term
|
||||
tsquery_func = self.database_engine.tsquery_func
|
||||
sql = f"""
|
||||
SELECT ts_rank_cd(vector, {tsquery_func}('english', ?)) AS rank,
|
||||
room_id, event_id
|
||||
FROM event_search
|
||||
WHERE vector @@ {tsquery_func}('english', ?)
|
||||
"""
|
||||
sql = (
|
||||
"SELECT ts_rank_cd(vector, to_tsquery('english', ?)) AS rank,"
|
||||
" room_id, event_id"
|
||||
" FROM event_search"
|
||||
" WHERE vector @@ to_tsquery('english', ?)"
|
||||
)
|
||||
args = [search_query, search_query] + args
|
||||
|
||||
count_sql = f"""
|
||||
SELECT room_id, count(*) as count FROM event_search
|
||||
WHERE vector @@ {tsquery_func}('english', ?)
|
||||
"""
|
||||
count_sql = (
|
||||
"SELECT room_id, count(*) as count FROM event_search"
|
||||
" WHERE vector @@ to_tsquery('english', ?)"
|
||||
)
|
||||
count_args = [search_query] + count_args
|
||||
elif isinstance(self.database_engine, Sqlite3Engine):
|
||||
search_query = _parse_query_for_sqlite(search_term)
|
||||
|
||||
sql = """
|
||||
SELECT rank(matchinfo(event_search)) as rank, room_id, event_id
|
||||
FROM event_search
|
||||
WHERE value MATCH ?
|
||||
"""
|
||||
sql = (
|
||||
"SELECT rank(matchinfo(event_search)) as rank, room_id, event_id"
|
||||
" FROM event_search"
|
||||
" WHERE value MATCH ?"
|
||||
)
|
||||
args = [search_query] + args
|
||||
|
||||
count_sql = """
|
||||
SELECT room_id, count(*) as count FROM event_search
|
||||
WHERE value MATCH ?
|
||||
"""
|
||||
count_args = [search_query] + count_args
|
||||
count_sql = (
|
||||
"SELECT room_id, count(*) as count FROM event_search"
|
||||
" WHERE value MATCH ?"
|
||||
)
|
||||
count_args = [search_term] + count_args
|
||||
else:
|
||||
# This should be unreachable.
|
||||
raise Exception("Unrecognized database engine")
|
||||
@@ -523,9 +501,7 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
|
||||
highlights = None
|
||||
if isinstance(self.database_engine, PostgresEngine):
|
||||
highlights = await self._find_highlights_in_postgres(
|
||||
search_query, events, tsquery_func
|
||||
)
|
||||
highlights = await self._find_highlights_in_postgres(search_query, events)
|
||||
|
||||
count_sql += " GROUP BY room_id"
|
||||
|
||||
@@ -534,6 +510,7 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
)
|
||||
|
||||
count = sum(row["count"] for row in count_results if row["room_id"] in room_ids)
|
||||
|
||||
return {
|
||||
"results": [
|
||||
{"event": event_map[r["event_id"]], "rank": r["rank"]}
|
||||
@@ -565,6 +542,9 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
Each match as a dictionary.
|
||||
"""
|
||||
clauses = []
|
||||
|
||||
search_query = _parse_query(self.database_engine, search_term)
|
||||
|
||||
args: List[Any] = []
|
||||
|
||||
# Make sure we don't explode because the person is in too many rooms.
|
||||
@@ -596,30 +576,26 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
raise SynapseError(400, "Invalid pagination token")
|
||||
|
||||
clauses.append(
|
||||
"""
|
||||
(origin_server_ts < ? OR (origin_server_ts = ? AND stream_ordering < ?))
|
||||
"""
|
||||
"(origin_server_ts < ?"
|
||||
" OR (origin_server_ts = ? AND stream_ordering < ?))"
|
||||
)
|
||||
args.extend([origin_server_ts, origin_server_ts, stream])
|
||||
|
||||
if isinstance(self.database_engine, PostgresEngine):
|
||||
search_query = search_term
|
||||
tsquery_func = self.database_engine.tsquery_func
|
||||
sql = f"""
|
||||
SELECT ts_rank_cd(vector, {tsquery_func}('english', ?)) as rank,
|
||||
origin_server_ts, stream_ordering, room_id, event_id
|
||||
FROM event_search
|
||||
WHERE vector @@ {tsquery_func}('english', ?) AND
|
||||
"""
|
||||
sql = (
|
||||
"SELECT ts_rank_cd(vector, to_tsquery('english', ?)) as rank,"
|
||||
" origin_server_ts, stream_ordering, room_id, event_id"
|
||||
" FROM event_search"
|
||||
" WHERE vector @@ to_tsquery('english', ?) AND "
|
||||
)
|
||||
args = [search_query, search_query] + args
|
||||
|
||||
count_sql = f"""
|
||||
SELECT room_id, count(*) as count FROM event_search
|
||||
WHERE vector @@ {tsquery_func}('english', ?) AND
|
||||
"""
|
||||
count_sql = (
|
||||
"SELECT room_id, count(*) as count FROM event_search"
|
||||
" WHERE vector @@ to_tsquery('english', ?) AND "
|
||||
)
|
||||
count_args = [search_query] + count_args
|
||||
elif isinstance(self.database_engine, Sqlite3Engine):
|
||||
|
||||
# We use CROSS JOIN here to ensure we use the right indexes.
|
||||
# https://sqlite.org/optoverview.html#crossjoin
|
||||
#
|
||||
@@ -628,25 +604,23 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
# in the events table to get the topological ordering. We need
|
||||
# to use the indexes in this order because sqlite refuses to
|
||||
# MATCH unless it uses the full text search index
|
||||
sql = """
|
||||
SELECT
|
||||
rank(matchinfo) as rank, room_id, event_id, origin_server_ts, stream_ordering
|
||||
FROM (
|
||||
SELECT key, event_id, matchinfo(event_search) as matchinfo
|
||||
FROM event_search
|
||||
WHERE value MATCH ?
|
||||
sql = (
|
||||
"SELECT rank(matchinfo) as rank, room_id, event_id,"
|
||||
" origin_server_ts, stream_ordering"
|
||||
" FROM (SELECT key, event_id, matchinfo(event_search) as matchinfo"
|
||||
" FROM event_search"
|
||||
" WHERE value MATCH ?"
|
||||
" )"
|
||||
" CROSS JOIN events USING (event_id)"
|
||||
" WHERE "
|
||||
)
|
||||
CROSS JOIN events USING (event_id)
|
||||
WHERE
|
||||
"""
|
||||
search_query = _parse_query_for_sqlite(search_term)
|
||||
args = [search_query] + args
|
||||
|
||||
count_sql = """
|
||||
SELECT room_id, count(*) as count FROM event_search
|
||||
WHERE value MATCH ? AND
|
||||
"""
|
||||
count_args = [search_query] + count_args
|
||||
count_sql = (
|
||||
"SELECT room_id, count(*) as count FROM event_search"
|
||||
" WHERE value MATCH ? AND "
|
||||
)
|
||||
count_args = [search_term] + count_args
|
||||
else:
|
||||
# This should be unreachable.
|
||||
raise Exception("Unrecognized database engine")
|
||||
@@ -657,10 +631,10 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
# We add an arbitrary limit here to ensure we don't try to pull the
|
||||
# entire table from the database.
|
||||
if isinstance(self.database_engine, PostgresEngine):
|
||||
sql += """
|
||||
ORDER BY origin_server_ts DESC NULLS LAST, stream_ordering DESC NULLS LAST
|
||||
LIMIT ?
|
||||
"""
|
||||
sql += (
|
||||
" ORDER BY origin_server_ts DESC NULLS LAST,"
|
||||
" stream_ordering DESC NULLS LAST LIMIT ?"
|
||||
)
|
||||
elif isinstance(self.database_engine, Sqlite3Engine):
|
||||
sql += " ORDER BY origin_server_ts DESC, stream_ordering DESC LIMIT ?"
|
||||
else:
|
||||
@@ -686,9 +660,7 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
|
||||
highlights = None
|
||||
if isinstance(self.database_engine, PostgresEngine):
|
||||
highlights = await self._find_highlights_in_postgres(
|
||||
search_query, events, tsquery_func
|
||||
)
|
||||
highlights = await self._find_highlights_in_postgres(search_query, events)
|
||||
|
||||
count_sql += " GROUP BY room_id"
|
||||
|
||||
@@ -714,7 +686,7 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
}
|
||||
|
||||
async def _find_highlights_in_postgres(
|
||||
self, search_query: str, events: List[EventBase], tsquery_func: str
|
||||
self, search_query: str, events: List[EventBase]
|
||||
) -> Set[str]:
|
||||
"""Given a list of events and a search term, return a list of words
|
||||
that match from the content of the event.
|
||||
@@ -725,7 +697,6 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
Args:
|
||||
search_query
|
||||
events: A list of events
|
||||
tsquery_func: The tsquery_* function to use when making queries
|
||||
|
||||
Returns:
|
||||
A set of strings.
|
||||
@@ -758,7 +729,7 @@ class SearchStore(SearchBackgroundUpdateStore):
|
||||
while stop_sel in value:
|
||||
stop_sel += ">"
|
||||
|
||||
query = f"SELECT ts_headline(?, {tsquery_func}('english', ?), %s)" % (
|
||||
query = "SELECT ts_headline(?, to_tsquery('english', ?), %s)" % (
|
||||
_to_postgres_options(
|
||||
{
|
||||
"StartSel": start_sel,
|
||||
@@ -789,127 +760,20 @@ def _to_postgres_options(options_dict: JsonDict) -> str:
|
||||
return "'%s'" % (",".join("%s=%s" % (k, v) for k, v in options_dict.items()),)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Phrase:
|
||||
phrase: List[str]
|
||||
|
||||
|
||||
class SearchToken(enum.Enum):
|
||||
Not = enum.auto()
|
||||
Or = enum.auto()
|
||||
And = enum.auto()
|
||||
|
||||
|
||||
Token = Union[str, Phrase, SearchToken]
|
||||
TokenList = List[Token]
|
||||
|
||||
|
||||
def _is_stop_word(word: str) -> bool:
|
||||
# TODO Pull these out of the dictionary:
|
||||
# https://github.com/postgres/postgres/blob/master/src/backend/snowball/stopwords/english.stop
|
||||
return word in {"the", "a", "you", "me", "and", "but"}
|
||||
|
||||
|
||||
def _tokenize_query(query: str) -> TokenList:
|
||||
"""
|
||||
Convert the user-supplied `query` into a TokenList, which can be translated into
|
||||
some DB-specific syntax.
|
||||
|
||||
The following constructs are supported:
|
||||
|
||||
- phrase queries using "double quotes"
|
||||
- case-insensitive `or` and `and` operators
|
||||
- negation of a keyword via unary `-`
|
||||
- unary hyphen to denote NOT e.g. 'include -exclude'
|
||||
|
||||
The following differs from websearch_to_tsquery:
|
||||
|
||||
- Stop words are not removed.
|
||||
- Unclosed phrases are treated differently.
|
||||
|
||||
"""
|
||||
tokens: TokenList = []
|
||||
|
||||
# Find phrases.
|
||||
in_phrase = False
|
||||
parts = deque(query.split('"'))
|
||||
for i, part in enumerate(parts):
|
||||
# The contents inside double quotes is treated as a phrase.
|
||||
in_phrase = bool(i % 2)
|
||||
|
||||
# Pull out the individual words, discarding any non-word characters.
|
||||
words = deque(re.findall(r"([\w\-]+)", part, re.UNICODE))
|
||||
|
||||
# Phrases have simplified handling of words.
|
||||
if in_phrase:
|
||||
# Skip stop words.
|
||||
phrase = [word for word in words if not _is_stop_word(word)]
|
||||
|
||||
# Consecutive words are implicitly ANDed together.
|
||||
if tokens and tokens[-1] not in (SearchToken.Not, SearchToken.Or):
|
||||
tokens.append(SearchToken.And)
|
||||
|
||||
# Add the phrase.
|
||||
tokens.append(Phrase(phrase))
|
||||
continue
|
||||
|
||||
# Otherwise, not in a phrase.
|
||||
while words:
|
||||
word = words.popleft()
|
||||
|
||||
if word.startswith("-"):
|
||||
tokens.append(SearchToken.Not)
|
||||
|
||||
# If there's more word, put it back to be processed again.
|
||||
word = word[1:]
|
||||
if word:
|
||||
words.appendleft(word)
|
||||
elif word.lower() == "or":
|
||||
tokens.append(SearchToken.Or)
|
||||
else:
|
||||
# Skip stop words.
|
||||
if _is_stop_word(word):
|
||||
continue
|
||||
|
||||
# Consecutive words are implicitly ANDed together.
|
||||
if tokens and tokens[-1] not in (SearchToken.Not, SearchToken.Or):
|
||||
tokens.append(SearchToken.And)
|
||||
|
||||
# Add the search term.
|
||||
tokens.append(word)
|
||||
|
||||
return tokens
|
||||
|
||||
|
||||
def _tokens_to_sqlite_match_query(tokens: TokenList) -> str:
|
||||
"""
|
||||
Convert the list of tokens to a string suitable for passing to sqlite's MATCH.
|
||||
Assume sqlite was compiled with enhanced query syntax.
|
||||
|
||||
Ref: https://www.sqlite.org/fts3.html#full_text_index_queries
|
||||
"""
|
||||
match_query = []
|
||||
for token in tokens:
|
||||
if isinstance(token, str):
|
||||
match_query.append(token)
|
||||
elif isinstance(token, Phrase):
|
||||
match_query.append('"' + " ".join(token.phrase) + '"')
|
||||
elif token == SearchToken.Not:
|
||||
# TODO: SQLite treats NOT as a *binary* operator. Hopefully a search
|
||||
# term has already been added before this.
|
||||
match_query.append(" NOT ")
|
||||
elif token == SearchToken.Or:
|
||||
match_query.append(" OR ")
|
||||
elif token == SearchToken.And:
|
||||
match_query.append(" AND ")
|
||||
else:
|
||||
raise ValueError(f"unknown token {token}")
|
||||
|
||||
return "".join(match_query)
|
||||
|
||||
|
||||
def _parse_query_for_sqlite(search_term: str) -> str:
|
||||
def _parse_query(database_engine: BaseDatabaseEngine, search_term: str) -> str:
|
||||
"""Takes a plain unicode string from the user and converts it into a form
|
||||
that can be passed to sqllite's matchinfo().
|
||||
that can be passed to database.
|
||||
We use this so that we can add prefix matching, which isn't something
|
||||
that is supported by default.
|
||||
"""
|
||||
return _tokens_to_sqlite_match_query(_tokenize_query(search_term))
|
||||
|
||||
# Pull out the individual words, discarding any non-word characters.
|
||||
results = re.findall(r"([\w\-]+)", search_term, re.UNICODE)
|
||||
|
||||
if isinstance(database_engine, PostgresEngine):
|
||||
return " & ".join(result for result in results)
|
||||
elif isinstance(database_engine, Sqlite3Engine):
|
||||
return " & ".join(result + "*" for result in results)
|
||||
else:
|
||||
# This should be unreachable.
|
||||
raise Exception("Unrecognized database engine")
|
||||
|
||||
@@ -18,8 +18,6 @@ from typing import TYPE_CHECKING, Collection, Dict, Iterable, List, Optional, Se
|
||||
import attr
|
||||
|
||||
from synapse.api.constants import EventTypes
|
||||
from synapse.events import EventBase
|
||||
from synapse.events.snapshot import EventContext
|
||||
from synapse.storage._base import SQLBaseStore
|
||||
from synapse.storage.database import (
|
||||
DatabasePool,
|
||||
@@ -406,112 +404,6 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
||||
fetched_keys=non_member_types,
|
||||
)
|
||||
|
||||
async def store_state_deltas_for_batched(
|
||||
self,
|
||||
events_and_context: List[Tuple[EventBase, EventContext]],
|
||||
room_id: str,
|
||||
prev_group: int,
|
||||
) -> List[int]:
|
||||
"""Generate and store state deltas for a group of events and contexts created to be
|
||||
batch persisted.
|
||||
|
||||
Args:
|
||||
events_and_context: the events to generate and store a state groups for
|
||||
and their associated contexts
|
||||
room_id: the id of the room the events were created for
|
||||
prev_group: the state group of the last event persisted before the batched events
|
||||
were created
|
||||
Returns: list of state groups that correspond to the events in `events_and_context`
|
||||
"""
|
||||
|
||||
def insert_deltas_group_txn(
|
||||
txn: LoggingTransaction,
|
||||
events_and_context: List[Tuple[EventBase, EventContext]],
|
||||
prev_group: int,
|
||||
) -> List[int]:
|
||||
"""Generate and store state groups for the provided events and contexts.
|
||||
|
||||
Requires that we have the state as a delta from the last persisted state group.
|
||||
|
||||
Returns:
|
||||
A list of state groups
|
||||
"""
|
||||
is_in_db = self.db_pool.simple_select_one_onecol_txn(
|
||||
txn,
|
||||
table="state_groups",
|
||||
keyvalues={"id": prev_group},
|
||||
retcol="id",
|
||||
allow_none=True,
|
||||
)
|
||||
if not is_in_db:
|
||||
raise Exception(
|
||||
"Trying to persist state with unpersisted prev_group: %r"
|
||||
% (prev_group,)
|
||||
)
|
||||
|
||||
num_state_groups = len(events_and_context)
|
||||
|
||||
state_groups = self._state_group_seq_gen.get_next_mult_txn(
|
||||
txn, num_state_groups
|
||||
)
|
||||
|
||||
index = 0
|
||||
for event, context in events_and_context:
|
||||
context._state_group = state_groups[index]
|
||||
# The first prev_group will be the last persisted state group, which is passed in
|
||||
# else it will be the group most recently assigned
|
||||
if index > 0:
|
||||
context.prev_group = state_groups[index - 1]
|
||||
context.state_group_before_event = state_groups[index - 1]
|
||||
else:
|
||||
context.prev_group = prev_group
|
||||
context.state_group_before_event = prev_group
|
||||
context.delta_ids = {(event.type, event.state_key): event.event_id}
|
||||
context._state_delta_due_to_event = {
|
||||
(event.type, event.state_key): event.event_id
|
||||
}
|
||||
index += 1
|
||||
|
||||
self.db_pool.simple_insert_many_txn(
|
||||
txn,
|
||||
table="state_groups",
|
||||
keys=("id", "room_id", "event_id"),
|
||||
values=[
|
||||
(context._state_group, room_id, event.event_id)
|
||||
for event, context in events_and_context
|
||||
],
|
||||
)
|
||||
|
||||
self.db_pool.simple_insert_many_txn(
|
||||
txn,
|
||||
table="state_group_edges",
|
||||
keys=("state_group", "prev_state_group"),
|
||||
values=[
|
||||
(context._state_group, context.prev_group)
|
||||
for _, context in events_and_context
|
||||
],
|
||||
)
|
||||
|
||||
for _, context in events_and_context:
|
||||
assert context.delta_ids is not None
|
||||
self.db_pool.simple_insert_many_txn(
|
||||
txn,
|
||||
table="state_groups_state",
|
||||
keys=("state_group", "room_id", "type", "state_key", "event_id"),
|
||||
values=[
|
||||
(context._state_group, room_id, key[0], key[1], state_id)
|
||||
for key, state_id in context.delta_ids.items()
|
||||
],
|
||||
)
|
||||
return state_groups
|
||||
|
||||
return await self.db_pool.runInteraction(
|
||||
"store_state_deltas_for_batched.insert_deltas_group",
|
||||
insert_deltas_group_txn,
|
||||
events_and_context,
|
||||
prev_group,
|
||||
)
|
||||
|
||||
async def store_state_group(
|
||||
self,
|
||||
event_id: str,
|
||||
|
||||
@@ -81,8 +81,8 @@ class PostgresEngine(
|
||||
allow_unsafe_locale = self.config.get("allow_unsafe_locale", False)
|
||||
|
||||
# Are we on a supported PostgreSQL version?
|
||||
if not allow_outdated_version and self._version < 110000:
|
||||
raise RuntimeError("Synapse requires PostgreSQL 11 or above.")
|
||||
if not allow_outdated_version and self._version < 100000:
|
||||
raise RuntimeError("Synapse requires PostgreSQL 10 or above.")
|
||||
|
||||
with db_conn.cursor() as txn:
|
||||
txn.execute("SHOW SERVER_ENCODING")
|
||||
@@ -170,22 +170,6 @@ class PostgresEngine(
|
||||
"""Do we support the `RETURNING` clause in insert/update/delete?"""
|
||||
return True
|
||||
|
||||
@property
|
||||
def tsquery_func(self) -> str:
|
||||
"""
|
||||
Selects a tsquery_* func to use.
|
||||
|
||||
Ref: https://www.postgresql.org/docs/current/textsearch-controls.html
|
||||
|
||||
Returns:
|
||||
The function name.
|
||||
"""
|
||||
# Postgres 11 added support for websearch_to_tsquery.
|
||||
assert self._version is not None
|
||||
if self._version >= 110000:
|
||||
return "websearch_to_tsquery"
|
||||
return "plainto_tsquery"
|
||||
|
||||
def is_deadlock(self, error: Exception) -> bool:
|
||||
if isinstance(error, psycopg2.DatabaseError):
|
||||
# https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
# Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import json
|
||||
|
||||
from synapse.storage.engines import BaseDatabaseEngine, Sqlite3Engine
|
||||
from synapse.storage.types import Cursor
|
||||
|
||||
|
||||
def run_create(cur: Cursor, database_engine: BaseDatabaseEngine) -> None:
|
||||
"""
|
||||
Upgrade the event_search table to use the porter tokenizer if it isn't already
|
||||
|
||||
Applies only for sqlite.
|
||||
"""
|
||||
if not isinstance(database_engine, Sqlite3Engine):
|
||||
return
|
||||
|
||||
# Rebuild the table event_search table with tokenize=porter configured.
|
||||
cur.execute("DROP TABLE event_search")
|
||||
cur.execute(
|
||||
"""
|
||||
CREATE VIRTUAL TABLE event_search
|
||||
USING fts4 (tokenize=porter, event_id, room_id, sender, key, value )
|
||||
"""
|
||||
)
|
||||
|
||||
# Re-run the background job to re-populate the event_search table.
|
||||
cur.execute("SELECT MIN(stream_ordering) FROM events")
|
||||
row = cur.fetchone()
|
||||
min_stream_id = row[0]
|
||||
|
||||
# If there are not any events, nothing to do.
|
||||
if min_stream_id is None:
|
||||
return
|
||||
|
||||
cur.execute("SELECT MAX(stream_ordering) FROM events")
|
||||
row = cur.fetchone()
|
||||
max_stream_id = row[0]
|
||||
|
||||
progress = {
|
||||
"target_min_stream_id_inclusive": min_stream_id,
|
||||
"max_stream_id_exclusive": max_stream_id + 1,
|
||||
}
|
||||
progress_json = json.dumps(progress)
|
||||
|
||||
sql = """
|
||||
INSERT into background_updates (ordering, update_name, progress_json)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
|
||||
cur.execute(sql, (7310, "event_search", progress_json))
|
||||
@@ -46,36 +46,19 @@ class FilteringTestCase(unittest.HomeserverTestCase):
|
||||
self.datastore = hs.get_datastores().main
|
||||
|
||||
def test_errors_on_invalid_filters(self):
|
||||
# See USER_FILTER_SCHEMA for the filter schema.
|
||||
invalid_filters = [
|
||||
# `account_data` must be a dictionary
|
||||
{"boom": {}},
|
||||
{"account_data": "Hello World"},
|
||||
# `event_fields` entries must not contain backslashes
|
||||
{"event_fields": [r"\\foo"]},
|
||||
# `event_format` must be "client" or "federation"
|
||||
{"room": {"timeline": {"limit": 0}, "state": {"not_bars": ["*"]}}},
|
||||
{"event_format": "other"},
|
||||
# `not_rooms` must contain valid room IDs
|
||||
{"room": {"not_rooms": ["#foo:pik-test"]}},
|
||||
# `senders` must contain valid user IDs
|
||||
{"presence": {"senders": ["@bar;pik.test.com"]}},
|
||||
]
|
||||
for filter in invalid_filters:
|
||||
with self.assertRaises(SynapseError):
|
||||
self.filtering.check_valid_filter(filter)
|
||||
|
||||
def test_ignores_unknown_filter_fields(self):
|
||||
# For forward compatibility, we must ignore unknown filter fields.
|
||||
# See USER_FILTER_SCHEMA for the filter schema.
|
||||
filters = [
|
||||
{"org.matrix.msc9999.future_option": True},
|
||||
{"presence": {"org.matrix.msc9999.future_option": True}},
|
||||
{"room": {"org.matrix.msc9999.future_option": True}},
|
||||
{"room": {"timeline": {"org.matrix.msc9999.future_option": True}}},
|
||||
]
|
||||
for filter in filters:
|
||||
self.filtering.check_valid_filter(filter)
|
||||
# Must not raise.
|
||||
|
||||
def test_valid_filters(self):
|
||||
valid_filters = [
|
||||
{
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
import json
|
||||
from unittest.mock import Mock
|
||||
|
||||
import ijson.common
|
||||
|
||||
from synapse.api.room_versions import RoomVersions
|
||||
from synapse.federation.transport.client import SendJoinParser
|
||||
from synapse.util import ExceptionBundle
|
||||
@@ -119,17 +117,8 @@ class SendJoinParserTestCase(TestCase):
|
||||
coro_3 = Mock()
|
||||
coro_3.close = Mock(side_effect=RuntimeError("Couldn't close coro 3"))
|
||||
|
||||
original_coros = parser._coros
|
||||
parser._coros = [coro_1, coro_2, coro_3]
|
||||
|
||||
# Close the original coroutines. If we don't, when we garbage collect them
|
||||
# they will throw, failing the test. (Oddly, this only started in CPython 3.11).
|
||||
for coro in original_coros:
|
||||
try:
|
||||
coro.close()
|
||||
except ijson.common.IncompleteJSONError:
|
||||
pass
|
||||
|
||||
# Send half of the data to the parser
|
||||
parser.write(serialisation[: len(serialisation) // 2])
|
||||
|
||||
|
||||
@@ -635,7 +635,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase):
|
||||
"""
|
||||
return self.make_request(
|
||||
"POST",
|
||||
"/_matrix/client/v3/refresh",
|
||||
"/_matrix/client/v1/refresh",
|
||||
{"refresh_token": refresh_token},
|
||||
)
|
||||
|
||||
@@ -724,7 +724,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase):
|
||||
|
||||
refresh_response = self.make_request(
|
||||
"POST",
|
||||
"/_matrix/client/v3/refresh",
|
||||
"/_matrix/client/v1/refresh",
|
||||
{"refresh_token": login_response.json_body["refresh_token"]},
|
||||
)
|
||||
self.assertEqual(refresh_response.code, HTTPStatus.OK, refresh_response.result)
|
||||
@@ -765,7 +765,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase):
|
||||
|
||||
refresh_response = self.make_request(
|
||||
"POST",
|
||||
"/_matrix/client/v3/refresh",
|
||||
"/_matrix/client/v1/refresh",
|
||||
{"refresh_token": login_response.json_body["refresh_token"]},
|
||||
)
|
||||
self.assertEqual(refresh_response.code, HTTPStatus.OK, refresh_response.result)
|
||||
@@ -1002,7 +1002,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase):
|
||||
# This first refresh should work properly
|
||||
first_refresh_response = self.make_request(
|
||||
"POST",
|
||||
"/_matrix/client/v3/refresh",
|
||||
"/_matrix/client/v1/refresh",
|
||||
{"refresh_token": login_response.json_body["refresh_token"]},
|
||||
)
|
||||
self.assertEqual(
|
||||
@@ -1012,7 +1012,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase):
|
||||
# This one as well, since the token in the first one was never used
|
||||
second_refresh_response = self.make_request(
|
||||
"POST",
|
||||
"/_matrix/client/v3/refresh",
|
||||
"/_matrix/client/v1/refresh",
|
||||
{"refresh_token": login_response.json_body["refresh_token"]},
|
||||
)
|
||||
self.assertEqual(
|
||||
@@ -1022,7 +1022,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase):
|
||||
# This one should not, since the token from the first refresh is not valid anymore
|
||||
third_refresh_response = self.make_request(
|
||||
"POST",
|
||||
"/_matrix/client/v3/refresh",
|
||||
"/_matrix/client/v1/refresh",
|
||||
{"refresh_token": first_refresh_response.json_body["refresh_token"]},
|
||||
)
|
||||
self.assertEqual(
|
||||
@@ -1056,7 +1056,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase):
|
||||
# Now that the access token from the last valid refresh was used once, refreshing with the N-1 token should fail
|
||||
fourth_refresh_response = self.make_request(
|
||||
"POST",
|
||||
"/_matrix/client/v3/refresh",
|
||||
"/_matrix/client/v1/refresh",
|
||||
{"refresh_token": login_response.json_body["refresh_token"]},
|
||||
)
|
||||
self.assertEqual(
|
||||
@@ -1068,7 +1068,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase):
|
||||
# But refreshing from the last valid refresh token still works
|
||||
fifth_refresh_response = self.make_request(
|
||||
"POST",
|
||||
"/_matrix/client/v3/refresh",
|
||||
"/_matrix/client/v1/refresh",
|
||||
{"refresh_token": second_refresh_response.json_body["refresh_token"]},
|
||||
)
|
||||
self.assertEqual(
|
||||
|
||||
@@ -11,18 +11,17 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from typing import List, Optional
|
||||
from typing import List
|
||||
|
||||
from twisted.test.proto_helpers import MemoryReactor
|
||||
|
||||
from synapse.api.constants import EventTypes, RelationTypes
|
||||
from synapse.rest import admin
|
||||
from synapse.rest.client import login, room, sync
|
||||
from synapse.server import HomeServer
|
||||
from synapse.types import JsonDict
|
||||
from synapse.util import Clock
|
||||
|
||||
from tests.unittest import HomeserverTestCase, override_config
|
||||
from tests.unittest import HomeserverTestCase
|
||||
|
||||
|
||||
class RedactionsTestCase(HomeserverTestCase):
|
||||
@@ -68,12 +67,7 @@ class RedactionsTestCase(HomeserverTestCase):
|
||||
)
|
||||
|
||||
def _redact_event(
|
||||
self,
|
||||
access_token: str,
|
||||
room_id: str,
|
||||
event_id: str,
|
||||
expect_code: int = 200,
|
||||
with_relations: Optional[List[str]] = None,
|
||||
self, access_token: str, room_id: str, event_id: str, expect_code: int = 200
|
||||
) -> JsonDict:
|
||||
"""Helper function to send a redaction event.
|
||||
|
||||
@@ -81,13 +75,7 @@ class RedactionsTestCase(HomeserverTestCase):
|
||||
"""
|
||||
path = "/_matrix/client/r0/rooms/%s/redact/%s" % (room_id, event_id)
|
||||
|
||||
request_content = {}
|
||||
if with_relations:
|
||||
request_content["org.matrix.msc3912.with_relations"] = with_relations
|
||||
|
||||
channel = self.make_request(
|
||||
"POST", path, request_content, access_token=access_token
|
||||
)
|
||||
channel = self.make_request("POST", path, content={}, access_token=access_token)
|
||||
self.assertEqual(channel.code, expect_code)
|
||||
return channel.json_body
|
||||
|
||||
@@ -213,256 +201,3 @@ class RedactionsTestCase(HomeserverTestCase):
|
||||
# These should all succeed, even though this would be denied by
|
||||
# the standard message ratelimiter
|
||||
self._redact_event(self.mod_access_token, self.room_id, msg_id)
|
||||
|
||||
@override_config({"experimental_features": {"msc3912_enabled": True}})
|
||||
def test_redact_relations(self) -> None:
|
||||
"""Tests that we can redact the relations of an event at the same time as the
|
||||
event itself.
|
||||
"""
|
||||
# Send a root event.
|
||||
res = self.helper.send_event(
|
||||
room_id=self.room_id,
|
||||
type=EventTypes.Message,
|
||||
content={"msgtype": "m.text", "body": "hello"},
|
||||
tok=self.mod_access_token,
|
||||
)
|
||||
root_event_id = res["event_id"]
|
||||
|
||||
# Send an edit to this root event.
|
||||
res = self.helper.send_event(
|
||||
room_id=self.room_id,
|
||||
type=EventTypes.Message,
|
||||
content={
|
||||
"body": " * hello world",
|
||||
"m.new_content": {
|
||||
"body": "hello world",
|
||||
"msgtype": "m.text",
|
||||
},
|
||||
"m.relates_to": {
|
||||
"event_id": root_event_id,
|
||||
"rel_type": RelationTypes.REPLACE,
|
||||
},
|
||||
"msgtype": "m.text",
|
||||
},
|
||||
tok=self.mod_access_token,
|
||||
)
|
||||
edit_event_id = res["event_id"]
|
||||
|
||||
# Also send a threaded message whose root is the same as the edit's.
|
||||
res = self.helper.send_event(
|
||||
room_id=self.room_id,
|
||||
type=EventTypes.Message,
|
||||
content={
|
||||
"msgtype": "m.text",
|
||||
"body": "message 1",
|
||||
"m.relates_to": {
|
||||
"event_id": root_event_id,
|
||||
"rel_type": RelationTypes.THREAD,
|
||||
},
|
||||
},
|
||||
tok=self.mod_access_token,
|
||||
)
|
||||
threaded_event_id = res["event_id"]
|
||||
|
||||
# Also send a reaction, again with the same root.
|
||||
res = self.helper.send_event(
|
||||
room_id=self.room_id,
|
||||
type=EventTypes.Reaction,
|
||||
content={
|
||||
"m.relates_to": {
|
||||
"rel_type": RelationTypes.ANNOTATION,
|
||||
"event_id": root_event_id,
|
||||
"key": "👍",
|
||||
}
|
||||
},
|
||||
tok=self.mod_access_token,
|
||||
)
|
||||
reaction_event_id = res["event_id"]
|
||||
|
||||
# Redact the root event, specifying that we also want to delete events that
|
||||
# relate to it with m.replace.
|
||||
self._redact_event(
|
||||
self.mod_access_token,
|
||||
self.room_id,
|
||||
root_event_id,
|
||||
with_relations=[
|
||||
RelationTypes.REPLACE,
|
||||
RelationTypes.THREAD,
|
||||
],
|
||||
)
|
||||
|
||||
# Check that the root event got redacted.
|
||||
event_dict = self.helper.get_event(
|
||||
self.room_id, root_event_id, self.mod_access_token
|
||||
)
|
||||
self.assertIn("redacted_because", event_dict, event_dict)
|
||||
|
||||
# Check that the edit got redacted.
|
||||
event_dict = self.helper.get_event(
|
||||
self.room_id, edit_event_id, self.mod_access_token
|
||||
)
|
||||
self.assertIn("redacted_because", event_dict, event_dict)
|
||||
|
||||
# Check that the threaded message got redacted.
|
||||
event_dict = self.helper.get_event(
|
||||
self.room_id, threaded_event_id, self.mod_access_token
|
||||
)
|
||||
self.assertIn("redacted_because", event_dict, event_dict)
|
||||
|
||||
# Check that the reaction did not get redacted.
|
||||
event_dict = self.helper.get_event(
|
||||
self.room_id, reaction_event_id, self.mod_access_token
|
||||
)
|
||||
self.assertNotIn("redacted_because", event_dict, event_dict)
|
||||
|
||||
@override_config({"experimental_features": {"msc3912_enabled": True}})
|
||||
def test_redact_relations_no_perms(self) -> None:
|
||||
"""Tests that, when redacting a message along with its relations, if not all
|
||||
the related messages can be redacted because of insufficient permissions, the
|
||||
server still redacts all the ones that can be.
|
||||
"""
|
||||
# Send a root event.
|
||||
res = self.helper.send_event(
|
||||
room_id=self.room_id,
|
||||
type=EventTypes.Message,
|
||||
content={
|
||||
"msgtype": "m.text",
|
||||
"body": "root",
|
||||
},
|
||||
tok=self.other_access_token,
|
||||
)
|
||||
root_event_id = res["event_id"]
|
||||
|
||||
# Send a first threaded message, this one from the moderator. We do this for the
|
||||
# first message with the m.thread relation (and not the last one) to ensure
|
||||
# that, when the server fails to redact it, it doesn't stop there, and it
|
||||
# instead goes on to redact the other one.
|
||||
res = self.helper.send_event(
|
||||
room_id=self.room_id,
|
||||
type=EventTypes.Message,
|
||||
content={
|
||||
"msgtype": "m.text",
|
||||
"body": "message 1",
|
||||
"m.relates_to": {
|
||||
"event_id": root_event_id,
|
||||
"rel_type": RelationTypes.THREAD,
|
||||
},
|
||||
},
|
||||
tok=self.mod_access_token,
|
||||
)
|
||||
first_threaded_event_id = res["event_id"]
|
||||
|
||||
# Send a second threaded message, this time from the user who'll perform the
|
||||
# redaction.
|
||||
res = self.helper.send_event(
|
||||
room_id=self.room_id,
|
||||
type=EventTypes.Message,
|
||||
content={
|
||||
"msgtype": "m.text",
|
||||
"body": "message 2",
|
||||
"m.relates_to": {
|
||||
"event_id": root_event_id,
|
||||
"rel_type": RelationTypes.THREAD,
|
||||
},
|
||||
},
|
||||
tok=self.other_access_token,
|
||||
)
|
||||
second_threaded_event_id = res["event_id"]
|
||||
|
||||
# Redact the thread's root, and request that all threaded messages are also
|
||||
# redacted. Send that request from the non-mod user, so that the first threaded
|
||||
# event cannot be redacted.
|
||||
self._redact_event(
|
||||
self.other_access_token,
|
||||
self.room_id,
|
||||
root_event_id,
|
||||
with_relations=[RelationTypes.THREAD],
|
||||
)
|
||||
|
||||
# Check that the thread root got redacted.
|
||||
event_dict = self.helper.get_event(
|
||||
self.room_id, root_event_id, self.other_access_token
|
||||
)
|
||||
self.assertIn("redacted_because", event_dict, event_dict)
|
||||
|
||||
# Check that the last message in the thread got redacted, despite failing to
|
||||
# redact the one before it.
|
||||
event_dict = self.helper.get_event(
|
||||
self.room_id, second_threaded_event_id, self.other_access_token
|
||||
)
|
||||
self.assertIn("redacted_because", event_dict, event_dict)
|
||||
|
||||
# Check that the message that was sent into the tread by the mod user is not
|
||||
# redacted.
|
||||
event_dict = self.helper.get_event(
|
||||
self.room_id, first_threaded_event_id, self.other_access_token
|
||||
)
|
||||
self.assertIn("body", event_dict["content"], event_dict)
|
||||
self.assertEqual("message 1", event_dict["content"]["body"])
|
||||
|
||||
@override_config({"experimental_features": {"msc3912_enabled": True}})
|
||||
def test_redact_relations_txn_id_reuse(self) -> None:
|
||||
"""Tests that redacting a message using a transaction ID, then reusing the same
|
||||
transaction ID but providing an additional list of relations to redact, is
|
||||
effectively a no-op.
|
||||
"""
|
||||
# Send a root event.
|
||||
res = self.helper.send_event(
|
||||
room_id=self.room_id,
|
||||
type=EventTypes.Message,
|
||||
content={
|
||||
"msgtype": "m.text",
|
||||
"body": "root",
|
||||
},
|
||||
tok=self.mod_access_token,
|
||||
)
|
||||
root_event_id = res["event_id"]
|
||||
|
||||
# Send a first threaded message.
|
||||
res = self.helper.send_event(
|
||||
room_id=self.room_id,
|
||||
type=EventTypes.Message,
|
||||
content={
|
||||
"msgtype": "m.text",
|
||||
"body": "I'm in a thread!",
|
||||
"m.relates_to": {
|
||||
"event_id": root_event_id,
|
||||
"rel_type": RelationTypes.THREAD,
|
||||
},
|
||||
},
|
||||
tok=self.mod_access_token,
|
||||
)
|
||||
threaded_event_id = res["event_id"]
|
||||
|
||||
# Send a first redaction request which redacts only the root event.
|
||||
channel = self.make_request(
|
||||
method="PUT",
|
||||
path=f"/rooms/{self.room_id}/redact/{root_event_id}/foo",
|
||||
content={},
|
||||
access_token=self.mod_access_token,
|
||||
)
|
||||
self.assertEqual(channel.code, 200)
|
||||
|
||||
# Send a second redaction request which redacts the root event as well as
|
||||
# threaded messages.
|
||||
channel = self.make_request(
|
||||
method="PUT",
|
||||
path=f"/rooms/{self.room_id}/redact/{root_event_id}/foo",
|
||||
content={"org.matrix.msc3912.with_relations": [RelationTypes.THREAD]},
|
||||
access_token=self.mod_access_token,
|
||||
)
|
||||
self.assertEqual(channel.code, 200)
|
||||
|
||||
# Check that the root event got redacted.
|
||||
event_dict = self.helper.get_event(
|
||||
self.room_id, root_event_id, self.mod_access_token
|
||||
)
|
||||
self.assertIn("redacted_because", event_dict)
|
||||
|
||||
# Check that the threaded message didn't get redacted (since that wasn't part of
|
||||
# the original redaction).
|
||||
event_dict = self.helper.get_event(
|
||||
self.room_id, threaded_event_id, self.mod_access_token
|
||||
)
|
||||
self.assertIn("body", event_dict["content"], event_dict)
|
||||
self.assertEqual("I'm in a thread!", event_dict["content"]["body"])
|
||||
|
||||
@@ -715,7 +715,7 @@ class RoomsCreateTestCase(RoomBase):
|
||||
self.assertEqual(HTTPStatus.OK, channel.code, channel.result)
|
||||
self.assertTrue("room_id" in channel.json_body)
|
||||
assert channel.resource_usage is not None
|
||||
self.assertEqual(30, channel.resource_usage.db_txn_count)
|
||||
self.assertEqual(34, channel.resource_usage.db_txn_count)
|
||||
|
||||
def test_post_room_initial_state(self) -> None:
|
||||
# POST with initial_state config key, expect new room id
|
||||
@@ -728,7 +728,7 @@ class RoomsCreateTestCase(RoomBase):
|
||||
self.assertEqual(HTTPStatus.OK, channel.code, channel.result)
|
||||
self.assertTrue("room_id" in channel.json_body)
|
||||
assert channel.resource_usage is not None
|
||||
self.assertEqual(32, channel.resource_usage.db_txn_count)
|
||||
self.assertEqual(37, channel.resource_usage.db_txn_count)
|
||||
|
||||
def test_post_room_visibility_key(self) -> None:
|
||||
# POST with visibility config key, expect new room id
|
||||
|
||||
@@ -410,43 +410,6 @@ class RestHelper:
|
||||
|
||||
return channel.json_body
|
||||
|
||||
def get_event(
|
||||
self,
|
||||
room_id: str,
|
||||
event_id: str,
|
||||
tok: Optional[str] = None,
|
||||
expect_code: int = HTTPStatus.OK,
|
||||
) -> JsonDict:
|
||||
"""Request a specific event from the server.
|
||||
|
||||
Args:
|
||||
room_id: the room in which the event was sent.
|
||||
event_id: the event's ID.
|
||||
tok: the token to request the event with.
|
||||
expect_code: the expected HTTP status for the response.
|
||||
|
||||
Returns:
|
||||
The event as a dict.
|
||||
"""
|
||||
path = f"/_matrix/client/v3/rooms/{room_id}/event/{event_id}"
|
||||
if tok:
|
||||
path = path + f"?access_token={tok}"
|
||||
|
||||
channel = make_request(
|
||||
self.hs.get_reactor(),
|
||||
self.site,
|
||||
"GET",
|
||||
path,
|
||||
)
|
||||
|
||||
assert channel.code == expect_code, "Expected: %d, got: %d, resp: %r" % (
|
||||
expect_code,
|
||||
channel.code,
|
||||
channel.result["body"],
|
||||
)
|
||||
|
||||
return channel.json_body
|
||||
|
||||
def _read_write_state(
|
||||
self,
|
||||
room_id: str,
|
||||
|
||||
@@ -12,22 +12,11 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from typing import List, Tuple, Union
|
||||
from unittest.case import SkipTest
|
||||
from unittest.mock import PropertyMock, patch
|
||||
|
||||
from twisted.test.proto_helpers import MemoryReactor
|
||||
|
||||
import synapse.rest.admin
|
||||
from synapse.api.constants import EventTypes
|
||||
from synapse.api.errors import StoreError
|
||||
from synapse.rest.client import login, room
|
||||
from synapse.server import HomeServer
|
||||
from synapse.storage.databases.main import DataStore
|
||||
from synapse.storage.databases.main.search import Phrase, SearchToken, _tokenize_query
|
||||
from synapse.storage.engines import PostgresEngine
|
||||
from synapse.storage.engines.sqlite import Sqlite3Engine
|
||||
from synapse.util import Clock
|
||||
|
||||
from tests.unittest import HomeserverTestCase, skip_unless
|
||||
from tests.utils import USE_POSTGRES_FOR_TESTS
|
||||
@@ -198,213 +187,3 @@ class EventSearchInsertionTest(HomeserverTestCase):
|
||||
),
|
||||
)
|
||||
self.assertCountEqual(values, ["hi", "2"])
|
||||
|
||||
|
||||
class MessageSearchTest(HomeserverTestCase):
|
||||
"""
|
||||
Check message search.
|
||||
|
||||
A powerful way to check the behaviour is to run the following in Postgres >= 11:
|
||||
|
||||
# SELECT websearch_to_tsquery('english', <your string>);
|
||||
|
||||
The result can be compared to the tokenized version for SQLite and Postgres < 11.
|
||||
|
||||
"""
|
||||
|
||||
servlets = [
|
||||
synapse.rest.admin.register_servlets_for_client_rest_resource,
|
||||
login.register_servlets,
|
||||
room.register_servlets,
|
||||
]
|
||||
|
||||
PHRASE = "the quick brown fox jumps over the lazy dog"
|
||||
|
||||
# Each entry is a search query, followed by either a boolean of whether it is
|
||||
# in the phrase OR a tuple of booleans: whether it matches using websearch
|
||||
# and using plain search.
|
||||
COMMON_CASES: List[Tuple[str, Union[bool, Tuple[bool, bool]]]] = [
|
||||
("nope", False),
|
||||
("brown", True),
|
||||
("quick brown", True),
|
||||
("brown quick", True),
|
||||
("quick \t brown", True),
|
||||
("jump", True),
|
||||
("brown nope", False),
|
||||
('"brown quick"', (False, True)),
|
||||
('"jumps over"', True),
|
||||
('"quick fox"', (False, True)),
|
||||
("nope OR doublenope", False),
|
||||
("furphy OR fox", (True, False)),
|
||||
("fox -nope", (True, False)),
|
||||
("fox -brown", (False, True)),
|
||||
('"fox" quick', True),
|
||||
('"quick brown', True),
|
||||
('" quick "', True),
|
||||
('" nope"', False),
|
||||
]
|
||||
# TODO Test non-ASCII cases.
|
||||
|
||||
# Case that fail on SQLite.
|
||||
POSTGRES_CASES: List[Tuple[str, Union[bool, Tuple[bool, bool]]]] = [
|
||||
# SQLite treats NOT as a binary operator.
|
||||
("- fox", (False, True)),
|
||||
("- nope", (True, False)),
|
||||
('"-fox quick', (False, True)),
|
||||
# PostgreSQL skips stop words.
|
||||
('"the quick brown"', True),
|
||||
('"over lazy"', True),
|
||||
]
|
||||
|
||||
def prepare(
|
||||
self, reactor: MemoryReactor, clock: Clock, homeserver: HomeServer
|
||||
) -> None:
|
||||
# Register a user and create a room, create some messages
|
||||
self.register_user("alice", "password")
|
||||
self.access_token = self.login("alice", "password")
|
||||
self.room_id = self.helper.create_room_as("alice", tok=self.access_token)
|
||||
|
||||
# Send the phrase as a message and check it was created
|
||||
response = self.helper.send(self.room_id, self.PHRASE, tok=self.access_token)
|
||||
self.assertIn("event_id", response)
|
||||
|
||||
# The behaviour of a missing trailing double quote changed in PostgreSQL 14
|
||||
# from ignoring the initial double quote to treating it as a phrase.
|
||||
main_store = homeserver.get_datastores().main
|
||||
found = False
|
||||
if isinstance(main_store.database_engine, PostgresEngine):
|
||||
assert main_store.database_engine._version is not None
|
||||
found = main_store.database_engine._version < 140000
|
||||
self.COMMON_CASES.append(('"fox quick', (found, True)))
|
||||
|
||||
def test_tokenize_query(self) -> None:
|
||||
"""Test the custom logic to tokenize a user's query."""
|
||||
cases = (
|
||||
("brown", ["brown"]),
|
||||
("quick brown", ["quick", SearchToken.And, "brown"]),
|
||||
("quick \t brown", ["quick", SearchToken.And, "brown"]),
|
||||
('"brown quick"', [Phrase(["brown", "quick"])]),
|
||||
("furphy OR fox", ["furphy", SearchToken.Or, "fox"]),
|
||||
("fox -brown", ["fox", SearchToken.Not, "brown"]),
|
||||
("- fox", [SearchToken.Not, "fox"]),
|
||||
('"fox" quick', [Phrase(["fox"]), SearchToken.And, "quick"]),
|
||||
# No trailing double quote.
|
||||
('"fox quick', [Phrase(["fox", "quick"])]),
|
||||
('"-fox quick', [Phrase(["-fox", "quick"])]),
|
||||
('" quick "', [Phrase(["quick"])]),
|
||||
(
|
||||
'q"uick brow"n',
|
||||
[
|
||||
"q",
|
||||
SearchToken.And,
|
||||
Phrase(["uick", "brow"]),
|
||||
SearchToken.And,
|
||||
"n",
|
||||
],
|
||||
),
|
||||
(
|
||||
'-"quick brown"',
|
||||
[SearchToken.Not, Phrase(["quick", "brown"])],
|
||||
),
|
||||
)
|
||||
|
||||
for query, expected in cases:
|
||||
tokenized = _tokenize_query(query)
|
||||
self.assertEqual(
|
||||
tokenized, expected, f"{tokenized} != {expected} for {query}"
|
||||
)
|
||||
|
||||
def _check_test_cases(
|
||||
self,
|
||||
store: DataStore,
|
||||
cases: List[Tuple[str, Union[bool, Tuple[bool, bool]]]],
|
||||
index=0,
|
||||
) -> None:
|
||||
# Run all the test cases versus search_msgs
|
||||
for query, expect_to_contain in cases:
|
||||
if isinstance(expect_to_contain, tuple):
|
||||
expect_to_contain = expect_to_contain[index]
|
||||
|
||||
result = self.get_success(
|
||||
store.search_msgs([self.room_id], query, ["content.body"])
|
||||
)
|
||||
self.assertEquals(
|
||||
result["count"],
|
||||
1 if expect_to_contain else 0,
|
||||
f"expected '{query}' to match '{self.PHRASE}'"
|
||||
if expect_to_contain
|
||||
else f"'{query}' unexpectedly matched '{self.PHRASE}'",
|
||||
)
|
||||
self.assertEquals(
|
||||
len(result["results"]),
|
||||
1 if expect_to_contain else 0,
|
||||
"results array length should match count",
|
||||
)
|
||||
|
||||
# Run them again versus search_rooms
|
||||
for query, expect_to_contain in cases:
|
||||
if isinstance(expect_to_contain, tuple):
|
||||
expect_to_contain = expect_to_contain[index]
|
||||
|
||||
result = self.get_success(
|
||||
store.search_rooms([self.room_id], query, ["content.body"], 10)
|
||||
)
|
||||
self.assertEquals(
|
||||
result["count"],
|
||||
1 if expect_to_contain else 0,
|
||||
f"expected '{query}' to match '{self.PHRASE}'"
|
||||
if expect_to_contain
|
||||
else f"'{query}' unexpectedly matched '{self.PHRASE}'",
|
||||
)
|
||||
self.assertEquals(
|
||||
len(result["results"]),
|
||||
1 if expect_to_contain else 0,
|
||||
"results array length should match count",
|
||||
)
|
||||
|
||||
def test_postgres_web_search_for_phrase(self):
|
||||
"""
|
||||
Test searching for phrases using typical web search syntax, as per postgres' websearch_to_tsquery.
|
||||
This test is skipped unless the postgres instance supports websearch_to_tsquery.
|
||||
"""
|
||||
|
||||
store = self.hs.get_datastores().main
|
||||
if not isinstance(store.database_engine, PostgresEngine):
|
||||
raise SkipTest("Test only applies when postgres is used as the database")
|
||||
|
||||
if store.database_engine.tsquery_func != "websearch_to_tsquery":
|
||||
raise SkipTest(
|
||||
"Test only applies when postgres supporting websearch_to_tsquery is used as the database"
|
||||
)
|
||||
|
||||
self._check_test_cases(store, self.COMMON_CASES + self.POSTGRES_CASES, index=0)
|
||||
|
||||
def test_postgres_non_web_search_for_phrase(self):
|
||||
"""
|
||||
Test postgres searching for phrases without using web search, which is used when websearch_to_tsquery isn't
|
||||
supported by the current postgres version.
|
||||
"""
|
||||
|
||||
store = self.hs.get_datastores().main
|
||||
if not isinstance(store.database_engine, PostgresEngine):
|
||||
raise SkipTest("Test only applies when postgres is used as the database")
|
||||
|
||||
# Patch supports_websearch_to_tsquery to always return False to ensure we're testing the plainto_tsquery path.
|
||||
with patch(
|
||||
"synapse.storage.engines.postgres.PostgresEngine.tsquery_func",
|
||||
new_callable=PropertyMock,
|
||||
) as supports_websearch_to_tsquery:
|
||||
supports_websearch_to_tsquery.return_value = "plainto_tsquery"
|
||||
self._check_test_cases(
|
||||
store, self.COMMON_CASES + self.POSTGRES_CASES, index=1
|
||||
)
|
||||
|
||||
def test_sqlite_search(self):
|
||||
"""
|
||||
Test sqlite searching for phrases.
|
||||
"""
|
||||
store = self.hs.get_datastores().main
|
||||
if not isinstance(store.database_engine, Sqlite3Engine):
|
||||
raise SkipTest("Test only applies when sqlite is used as the database")
|
||||
|
||||
self._check_test_cases(store, self.COMMON_CASES, index=0)
|
||||
|
||||
Reference in New Issue
Block a user