Add files using upload-large-folder tool
Browse files- LTA_openwebtext_dualt/docs/lta_samples/metrics_20260517/selected_long20k_len256_bs512_ode128_20260517_1830long20k/rollin_p50_s4_old/step_3000/decode_token_acc.tsv +2 -0
- LTA_openwebtext_dualt/logs/ctx1024_sampleds_sweep_4gpu/ctx1024_sampledpath_true_20260517_224139.log +331 -0
- LTA_openwebtext_dualt/logs/ctx1024_sampleds_sweep_4gpu/ctx1024_tradeoff_dual_20260517_225705.nohup +0 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/anyio/from_thread.py +578 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/anyio/py.typed +0 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/__init__.py +62 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_abnf.py +132 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_connection.py +659 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_events.py +369 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_headers.py +282 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_readers.py +250 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_receivebuffer.py +153 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_state.py +365 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_util.py +135 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_version.py +16 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_writers.py +145 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/py.typed +1 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/rwkv/__init__.py +27 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/rwkv/configuration_rwkv.py +76 -0
- LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/rwkv/modeling_rwkv.py +758 -0
LTA_openwebtext_dualt/docs/lta_samples/metrics_20260517/selected_long20k_len256_bs512_ode128_20260517_1830long20k/rollin_p50_s4_old/step_3000/decode_token_acc.tsv
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
run ckpt_step endpoint_softening token_acc_mean token_acc_min token_acc_max exact_acc exact_count exact_ref_coverage exact_ref_count
|
| 2 |
+
train8_rollin_len256_rollin_p50_s4_i32_20260517_171654 3000 none 0.9420166015625 0.12109375 1.0 0.140625 9 0.125 1
|
LTA_openwebtext_dualt/logs/ctx1024_sampleds_sweep_4gpu/ctx1024_sampledpath_true_20260517_224139.log
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[ctx1024-sampleds] start stamp=ctx1024_sampledpath_true_20260517_224139 len=1024 vocab=2664 out=docs/lta_samples/metrics_20260517/ctx1024_sampleds_sweep_bs512_ode128_ctx1024_sampledpath_true_20260517_224139
|
| 2 |
+
[ctx1024-sampleds] config=p50_path4_unif0_0p125_outwdm1 run=train8_ctx1024_p50_path4_unif0_0p125_outwdm1_ctx1024_sampledpath_true_20260517_224139 p=0.50 mode=sampled_path steps=4 s_dist=uniform s_frac=0.0->0.125 beta=2.0,6.0 outwd=-1 sync_t=1
|
| 3 |
+
[ctx1024-sampleds] train config=p50_path4_unif0_0p125_outwdm1 from=0 to=1000
|
| 4 |
+
[ctx1024-sampleds] eval config=p50_path4_unif0_0p125_outwdm1 step=1000
|
| 5 |
+
[eval-decode-acc] train8_ctx1024_p50_path4_unif0_0p125_outwdm1_ctx1024_sampledpath_true_20260517_224139 step=1000 soft=none
|
| 6 |
+
[decode] max_len=1024 generated=64/64
|
| 7 |
+
{
|
| 8 |
+
"num_rows": 1,
|
| 9 |
+
"best_by_run": {
|
| 10 |
+
"train8_ctx1024_p50_path4_unif0_0p125_outwdm1_ctx1024_sampledpath_true_20260517_224139::none": {
|
| 11 |
+
"run": "train8_ctx1024_p50_path4_unif0_0p125_outwdm1_ctx1024_sampledpath_true_20260517_224139",
|
| 12 |
+
"checkpoint": "runs/train8_ctx1024_p50_path4_unif0_0p125_outwdm1_ctx1024_sampledpath_true_20260517_224139/step_0001000.pt",
|
| 13 |
+
"ckpt_step": 1000,
|
| 14 |
+
"endpoint_softening": "none",
|
| 15 |
+
"decode_rule": "flowmap",
|
| 16 |
+
"steps": 128,
|
| 17 |
+
"time_schedule": "logit_normal",
|
| 18 |
+
"model_t_mode": "post",
|
| 19 |
+
"final_from": "state",
|
| 20 |
+
"n_gen": 64,
|
| 21 |
+
"n_refs": 8,
|
| 22 |
+
"token_acc_mean": 0.0288543701171875,
|
| 23 |
+
"token_acc_min": 0.01953125,
|
| 24 |
+
"token_acc_max": 0.0478515625,
|
| 25 |
+
"exact_acc": 0.0,
|
| 26 |
+
"exact_count": 0,
|
| 27 |
+
"exact_ref_coverage": 0.0,
|
| 28 |
+
"exact_ref_count": 0,
|
| 29 |
+
"exact_ref_hits": [],
|
| 30 |
+
"best_ref_idx": [
|
| 31 |
+
7,
|
| 32 |
+
2,
|
| 33 |
+
5,
|
| 34 |
+
7,
|
| 35 |
+
7,
|
| 36 |
+
7,
|
| 37 |
+
2,
|
| 38 |
+
0,
|
| 39 |
+
7,
|
| 40 |
+
3,
|
| 41 |
+
7,
|
| 42 |
+
4,
|
| 43 |
+
2,
|
| 44 |
+
7,
|
| 45 |
+
7,
|
| 46 |
+
5,
|
| 47 |
+
7,
|
| 48 |
+
2,
|
| 49 |
+
1,
|
| 50 |
+
0,
|
| 51 |
+
7,
|
| 52 |
+
2,
|
| 53 |
+
7,
|
| 54 |
+
5,
|
| 55 |
+
7,
|
| 56 |
+
0,
|
| 57 |
+
5,
|
| 58 |
+
5,
|
| 59 |
+
7,
|
| 60 |
+
7,
|
| 61 |
+
5,
|
| 62 |
+
7,
|
| 63 |
+
4,
|
| 64 |
+
7,
|
| 65 |
+
7,
|
| 66 |
+
5,
|
| 67 |
+
5,
|
| 68 |
+
4,
|
| 69 |
+
7,
|
| 70 |
+
5,
|
| 71 |
+
2,
|
| 72 |
+
7,
|
| 73 |
+
5,
|
| 74 |
+
7,
|
| 75 |
+
7,
|
| 76 |
+
7,
|
| 77 |
+
7,
|
| 78 |
+
4,
|
| 79 |
+
4,
|
| 80 |
+
5,
|
| 81 |
+
7,
|
| 82 |
+
2,
|
| 83 |
+
4,
|
| 84 |
+
7,
|
| 85 |
+
0,
|
| 86 |
+
7,
|
| 87 |
+
7,
|
| 88 |
+
5,
|
| 89 |
+
0,
|
| 90 |
+
4,
|
| 91 |
+
0,
|
| 92 |
+
7,
|
| 93 |
+
4,
|
| 94 |
+
5
|
| 95 |
+
],
|
| 96 |
+
"best_token_acc": [
|
| 97 |
+
0.0263671875,
|
| 98 |
+
0.021484375,
|
| 99 |
+
0.029296875,
|
| 100 |
+
0.01953125,
|
| 101 |
+
0.029296875,
|
| 102 |
+
0.0283203125,
|
| 103 |
+
0.0234375,
|
| 104 |
+
0.021484375,
|
| 105 |
+
0.0263671875,
|
| 106 |
+
0.0283203125,
|
| 107 |
+
0.0234375,
|
| 108 |
+
0.025390625,
|
| 109 |
+
0.0302734375,
|
| 110 |
+
0.0263671875,
|
| 111 |
+
0.0263671875,
|
| 112 |
+
0.0263671875,
|
| 113 |
+
0.0263671875,
|
| 114 |
+
0.0234375,
|
| 115 |
+
0.0283203125,
|
| 116 |
+
0.0302734375,
|
| 117 |
+
0.033203125,
|
| 118 |
+
0.0380859375,
|
| 119 |
+
0.0263671875,
|
| 120 |
+
0.0283203125,
|
| 121 |
+
0.02734375,
|
| 122 |
+
0.03515625,
|
| 123 |
+
0.044921875,
|
| 124 |
+
0.0419921875,
|
| 125 |
+
0.03125,
|
| 126 |
+
0.0283203125,
|
| 127 |
+
0.0478515625,
|
| 128 |
+
0.0224609375,
|
| 129 |
+
0.0263671875,
|
| 130 |
+
0.0283203125,
|
| 131 |
+
0.033203125,
|
| 132 |
+
0.041015625,
|
| 133 |
+
0.03125,
|
| 134 |
+
0.0224609375,
|
| 135 |
+
0.03125,
|
| 136 |
+
0.03515625,
|
| 137 |
+
0.0244140625,
|
| 138 |
+
0.0263671875,
|
| 139 |
+
0.0302734375,
|
| 140 |
+
0.0234375,
|
| 141 |
+
0.01953125,
|
| 142 |
+
0.021484375,
|
| 143 |
+
0.0224609375,
|
| 144 |
+
0.0322265625,
|
| 145 |
+
0.025390625,
|
| 146 |
+
0.0322265625,
|
| 147 |
+
0.029296875,
|
| 148 |
+
0.033203125,
|
| 149 |
+
0.0244140625,
|
| 150 |
+
0.033203125,
|
| 151 |
+
0.02734375,
|
| 152 |
+
0.041015625,
|
| 153 |
+
0.025390625,
|
| 154 |
+
0.0302734375,
|
| 155 |
+
0.0234375,
|
| 156 |
+
0.02734375,
|
| 157 |
+
0.0244140625,
|
| 158 |
+
0.025390625,
|
| 159 |
+
0.025390625,
|
| 160 |
+
0.0439453125
|
| 161 |
+
]
|
| 162 |
+
}
|
| 163 |
+
},
|
| 164 |
+
"first_exact_by_run": {}
|
| 165 |
+
}
|
| 166 |
+
RESULT config=p50_path4_unif0_0p125_outwdm1 run=train8_ctx1024_p50_path4_unif0_0p125_outwdm1_ctx1024_sampledpath_true_20260517_224139 ckpt_step=1000 views=512000 token_acc=0.0289 exact=0/64 exact_refs=0 hits=[]
|
| 167 |
+
[ctx1024-sampleds] train config=p50_path4_unif0_0p125_outwdm1 from=1000 to=2000
|
| 168 |
+
[ctx1024-sampleds] eval config=p50_path4_unif0_0p125_outwdm1 step=2000
|
| 169 |
+
[eval-decode-acc] train8_ctx1024_p50_path4_unif0_0p125_outwdm1_ctx1024_sampledpath_true_20260517_224139 step=2000 soft=none
|
| 170 |
+
[decode] max_len=1024 generated=64/64
|
| 171 |
+
{
|
| 172 |
+
"num_rows": 1,
|
| 173 |
+
"best_by_run": {
|
| 174 |
+
"train8_ctx1024_p50_path4_unif0_0p125_outwdm1_ctx1024_sampledpath_true_20260517_224139::none": {
|
| 175 |
+
"run": "train8_ctx1024_p50_path4_unif0_0p125_outwdm1_ctx1024_sampledpath_true_20260517_224139",
|
| 176 |
+
"checkpoint": "runs/train8_ctx1024_p50_path4_unif0_0p125_outwdm1_ctx1024_sampledpath_true_20260517_224139/step_0002000.pt",
|
| 177 |
+
"ckpt_step": 2000,
|
| 178 |
+
"endpoint_softening": "none",
|
| 179 |
+
"decode_rule": "flowmap",
|
| 180 |
+
"steps": 128,
|
| 181 |
+
"time_schedule": "logit_normal",
|
| 182 |
+
"model_t_mode": "post",
|
| 183 |
+
"final_from": "state",
|
| 184 |
+
"n_gen": 64,
|
| 185 |
+
"n_refs": 8,
|
| 186 |
+
"token_acc_mean": 0.86529541015625,
|
| 187 |
+
"token_acc_min": 0.2119140625,
|
| 188 |
+
"token_acc_max": 0.9921875,
|
| 189 |
+
"exact_acc": 0.0,
|
| 190 |
+
"exact_count": 0,
|
| 191 |
+
"exact_ref_coverage": 0.0,
|
| 192 |
+
"exact_ref_count": 0,
|
| 193 |
+
"exact_ref_hits": [],
|
| 194 |
+
"best_ref_idx": [
|
| 195 |
+
4,
|
| 196 |
+
4,
|
| 197 |
+
5,
|
| 198 |
+
4,
|
| 199 |
+
1,
|
| 200 |
+
7,
|
| 201 |
+
4,
|
| 202 |
+
4,
|
| 203 |
+
5,
|
| 204 |
+
1,
|
| 205 |
+
5,
|
| 206 |
+
1,
|
| 207 |
+
4,
|
| 208 |
+
1,
|
| 209 |
+
4,
|
| 210 |
+
4,
|
| 211 |
+
1,
|
| 212 |
+
5,
|
| 213 |
+
4,
|
| 214 |
+
1,
|
| 215 |
+
4,
|
| 216 |
+
4,
|
| 217 |
+
4,
|
| 218 |
+
4,
|
| 219 |
+
4,
|
| 220 |
+
4,
|
| 221 |
+
7,
|
| 222 |
+
4,
|
| 223 |
+
4,
|
| 224 |
+
1,
|
| 225 |
+
4,
|
| 226 |
+
4,
|
| 227 |
+
4,
|
| 228 |
+
1,
|
| 229 |
+
4,
|
| 230 |
+
4,
|
| 231 |
+
4,
|
| 232 |
+
4,
|
| 233 |
+
5,
|
| 234 |
+
4,
|
| 235 |
+
1,
|
| 236 |
+
4,
|
| 237 |
+
4,
|
| 238 |
+
3,
|
| 239 |
+
4,
|
| 240 |
+
5,
|
| 241 |
+
7,
|
| 242 |
+
7,
|
| 243 |
+
5,
|
| 244 |
+
4,
|
| 245 |
+
4,
|
| 246 |
+
4,
|
| 247 |
+
5,
|
| 248 |
+
4,
|
| 249 |
+
4,
|
| 250 |
+
4,
|
| 251 |
+
5,
|
| 252 |
+
4,
|
| 253 |
+
7,
|
| 254 |
+
4,
|
| 255 |
+
5,
|
| 256 |
+
4,
|
| 257 |
+
1,
|
| 258 |
+
4
|
| 259 |
+
],
|
| 260 |
+
"best_token_acc": [
|
| 261 |
+
0.962890625,
|
| 262 |
+
0.966796875,
|
| 263 |
+
0.9912109375,
|
| 264 |
+
0.3701171875,
|
| 265 |
+
0.78125,
|
| 266 |
+
0.7607421875,
|
| 267 |
+
0.9658203125,
|
| 268 |
+
0.9560546875,
|
| 269 |
+
0.9873046875,
|
| 270 |
+
0.376953125,
|
| 271 |
+
0.9921875,
|
| 272 |
+
0.970703125,
|
| 273 |
+
0.95703125,
|
| 274 |
+
0.72265625,
|
| 275 |
+
0.71484375,
|
| 276 |
+
0.7314453125,
|
| 277 |
+
0.9765625,
|
| 278 |
+
0.9892578125,
|
| 279 |
+
0.73828125,
|
| 280 |
+
0.861328125,
|
| 281 |
+
0.9521484375,
|
| 282 |
+
0.9716796875,
|
| 283 |
+
0.8662109375,
|
| 284 |
+
0.8330078125,
|
| 285 |
+
0.96875,
|
| 286 |
+
0.9482421875,
|
| 287 |
+
0.9853515625,
|
| 288 |
+
0.689453125,
|
| 289 |
+
0.962890625,
|
| 290 |
+
0.9775390625,
|
| 291 |
+
0.9580078125,
|
| 292 |
+
0.2119140625,
|
| 293 |
+
0.9345703125,
|
| 294 |
+
0.2138671875,
|
| 295 |
+
0.90625,
|
| 296 |
+
0.5400390625,
|
| 297 |
+
0.9736328125,
|
| 298 |
+
0.685546875,
|
| 299 |
+
0.9716796875,
|
| 300 |
+
0.8994140625,
|
| 301 |
+
0.974609375,
|
| 302 |
+
0.9736328125,
|
| 303 |
+
0.9248046875,
|
| 304 |
+
0.984375,
|
| 305 |
+
0.9677734375,
|
| 306 |
+
0.7060546875,
|
| 307 |
+
0.9345703125,
|
| 308 |
+
0.9228515625,
|
| 309 |
+
0.9912109375,
|
| 310 |
+
0.8466796875,
|
| 311 |
+
0.9599609375,
|
| 312 |
+
0.9638671875,
|
| 313 |
+
0.4013671875,
|
| 314 |
+
0.9033203125,
|
| 315 |
+
0.9736328125,
|
| 316 |
+
0.943359375,
|
| 317 |
+
0.9912109375,
|
| 318 |
+
0.9716796875,
|
| 319 |
+
0.98828125,
|
| 320 |
+
0.9716796875,
|
| 321 |
+
0.97265625,
|
| 322 |
+
0.94921875,
|
| 323 |
+
0.97265625,
|
| 324 |
+
0.9658203125
|
| 325 |
+
]
|
| 326 |
+
}
|
| 327 |
+
},
|
| 328 |
+
"first_exact_by_run": {}
|
| 329 |
+
}
|
| 330 |
+
RESULT config=p50_path4_unif0_0p125_outwdm1 run=train8_ctx1024_p50_path4_unif0_0p125_outwdm1_ctx1024_sampledpath_true_20260517_224139 ckpt_step=2000 views=1024000 token_acc=0.8653 exact=0/64 exact_refs=0 hits=[]
|
| 331 |
+
[ctx1024-sampleds] train config=p50_path4_unif0_0p125_outwdm1 from=2000 to=3000
|
LTA_openwebtext_dualt/logs/ctx1024_sampleds_sweep_4gpu/ctx1024_tradeoff_dual_20260517_225705.nohup
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/anyio/from_thread.py
ADDED
|
@@ -0,0 +1,578 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
__all__ = (
|
| 4 |
+
"BlockingPortal",
|
| 5 |
+
"BlockingPortalProvider",
|
| 6 |
+
"check_cancelled",
|
| 7 |
+
"run",
|
| 8 |
+
"run_sync",
|
| 9 |
+
"start_blocking_portal",
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
import sys
|
| 13 |
+
from collections.abc import Awaitable, Callable, Generator
|
| 14 |
+
from concurrent.futures import Future
|
| 15 |
+
from contextlib import (
|
| 16 |
+
AbstractAsyncContextManager,
|
| 17 |
+
AbstractContextManager,
|
| 18 |
+
contextmanager,
|
| 19 |
+
)
|
| 20 |
+
from dataclasses import dataclass, field
|
| 21 |
+
from functools import partial
|
| 22 |
+
from inspect import isawaitable
|
| 23 |
+
from threading import Lock, Thread, current_thread, get_ident
|
| 24 |
+
from types import TracebackType
|
| 25 |
+
from typing import (
|
| 26 |
+
Any,
|
| 27 |
+
Generic,
|
| 28 |
+
TypeVar,
|
| 29 |
+
cast,
|
| 30 |
+
overload,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
from ._core._eventloop import (
|
| 34 |
+
get_cancelled_exc_class,
|
| 35 |
+
threadlocals,
|
| 36 |
+
)
|
| 37 |
+
from ._core._eventloop import run as run_eventloop
|
| 38 |
+
from ._core._exceptions import NoEventLoopError
|
| 39 |
+
from ._core._synchronization import Event
|
| 40 |
+
from ._core._tasks import CancelScope, create_task_group
|
| 41 |
+
from .abc._tasks import TaskStatus
|
| 42 |
+
from .lowlevel import EventLoopToken, current_token
|
| 43 |
+
|
| 44 |
+
if sys.version_info >= (3, 11):
|
| 45 |
+
from typing import TypeVarTuple, Unpack
|
| 46 |
+
else:
|
| 47 |
+
from typing_extensions import TypeVarTuple, Unpack
|
| 48 |
+
|
| 49 |
+
T_Retval = TypeVar("T_Retval")
|
| 50 |
+
T_co = TypeVar("T_co", covariant=True)
|
| 51 |
+
PosArgsT = TypeVarTuple("PosArgsT")
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def _token_or_error(token: EventLoopToken | None) -> EventLoopToken:
|
| 55 |
+
if token is not None:
|
| 56 |
+
return token
|
| 57 |
+
|
| 58 |
+
try:
|
| 59 |
+
return threadlocals.current_token
|
| 60 |
+
except AttributeError:
|
| 61 |
+
raise NoEventLoopError(
|
| 62 |
+
"Not running inside an AnyIO worker thread, and no event loop token was "
|
| 63 |
+
"provided"
|
| 64 |
+
) from None
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def run(
|
| 68 |
+
func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
|
| 69 |
+
*args: Unpack[PosArgsT],
|
| 70 |
+
token: EventLoopToken | None = None,
|
| 71 |
+
) -> T_Retval:
|
| 72 |
+
"""
|
| 73 |
+
Call a coroutine function from a worker thread.
|
| 74 |
+
|
| 75 |
+
:param func: a coroutine function
|
| 76 |
+
:param args: positional arguments for the callable
|
| 77 |
+
:param token: an event loop token to use to get back to the event loop thread
|
| 78 |
+
(required if calling this function from outside an AnyIO worker thread)
|
| 79 |
+
:return: the return value of the coroutine function
|
| 80 |
+
:raises MissingTokenError: if no token was provided and called from outside an
|
| 81 |
+
AnyIO worker thread
|
| 82 |
+
:raises RunFinishedError: if the event loop tied to ``token`` is no longer running
|
| 83 |
+
|
| 84 |
+
.. versionchanged:: 4.11.0
|
| 85 |
+
Added the ``token`` parameter.
|
| 86 |
+
|
| 87 |
+
"""
|
| 88 |
+
explicit_token = token is not None
|
| 89 |
+
token = _token_or_error(token)
|
| 90 |
+
return token.backend_class.run_async_from_thread(
|
| 91 |
+
func, args, token=token.native_token if explicit_token else None
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def run_sync(
|
| 96 |
+
func: Callable[[Unpack[PosArgsT]], T_Retval],
|
| 97 |
+
*args: Unpack[PosArgsT],
|
| 98 |
+
token: EventLoopToken | None = None,
|
| 99 |
+
) -> T_Retval:
|
| 100 |
+
"""
|
| 101 |
+
Call a function in the event loop thread from a worker thread.
|
| 102 |
+
|
| 103 |
+
:param func: a callable
|
| 104 |
+
:param args: positional arguments for the callable
|
| 105 |
+
:param token: an event loop token to use to get back to the event loop thread
|
| 106 |
+
(required if calling this function from outside an AnyIO worker thread)
|
| 107 |
+
:return: the return value of the callable
|
| 108 |
+
:raises MissingTokenError: if no token was provided and called from outside an
|
| 109 |
+
AnyIO worker thread
|
| 110 |
+
:raises RunFinishedError: if the event loop tied to ``token`` is no longer running
|
| 111 |
+
|
| 112 |
+
.. versionchanged:: 4.11.0
|
| 113 |
+
Added the ``token`` parameter.
|
| 114 |
+
|
| 115 |
+
"""
|
| 116 |
+
explicit_token = token is not None
|
| 117 |
+
token = _token_or_error(token)
|
| 118 |
+
return token.backend_class.run_sync_from_thread(
|
| 119 |
+
func, args, token=token.native_token if explicit_token else None
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
class _BlockingAsyncContextManager(Generic[T_co], AbstractContextManager):
|
| 124 |
+
_enter_future: Future[T_co]
|
| 125 |
+
_exit_future: Future[bool | None]
|
| 126 |
+
_exit_event: Event
|
| 127 |
+
_exit_exc_info: tuple[
|
| 128 |
+
type[BaseException] | None, BaseException | None, TracebackType | None
|
| 129 |
+
] = (None, None, None)
|
| 130 |
+
|
| 131 |
+
def __init__(
|
| 132 |
+
self, async_cm: AbstractAsyncContextManager[T_co], portal: BlockingPortal
|
| 133 |
+
):
|
| 134 |
+
self._async_cm = async_cm
|
| 135 |
+
self._portal = portal
|
| 136 |
+
|
| 137 |
+
async def run_async_cm(self) -> bool | None:
|
| 138 |
+
try:
|
| 139 |
+
self._exit_event = Event()
|
| 140 |
+
value = await self._async_cm.__aenter__()
|
| 141 |
+
except BaseException as exc:
|
| 142 |
+
self._enter_future.set_exception(exc)
|
| 143 |
+
raise
|
| 144 |
+
else:
|
| 145 |
+
self._enter_future.set_result(value)
|
| 146 |
+
|
| 147 |
+
try:
|
| 148 |
+
# Wait for the sync context manager to exit.
|
| 149 |
+
# This next statement can raise `get_cancelled_exc_class()` if
|
| 150 |
+
# something went wrong in a task group in this async context
|
| 151 |
+
# manager.
|
| 152 |
+
await self._exit_event.wait()
|
| 153 |
+
finally:
|
| 154 |
+
# In case of cancellation, it could be that we end up here before
|
| 155 |
+
# `_BlockingAsyncContextManager.__exit__` is called, and an
|
| 156 |
+
# `_exit_exc_info` has been set.
|
| 157 |
+
result = await self._async_cm.__aexit__(*self._exit_exc_info)
|
| 158 |
+
|
| 159 |
+
return result
|
| 160 |
+
|
| 161 |
+
def __enter__(self) -> T_co:
|
| 162 |
+
self._enter_future = Future()
|
| 163 |
+
self._exit_future = self._portal.start_task_soon(self.run_async_cm)
|
| 164 |
+
return self._enter_future.result()
|
| 165 |
+
|
| 166 |
+
def __exit__(
|
| 167 |
+
self,
|
| 168 |
+
__exc_type: type[BaseException] | None,
|
| 169 |
+
__exc_value: BaseException | None,
|
| 170 |
+
__traceback: TracebackType | None,
|
| 171 |
+
) -> bool | None:
|
| 172 |
+
self._exit_exc_info = __exc_type, __exc_value, __traceback
|
| 173 |
+
self._portal.call(self._exit_event.set)
|
| 174 |
+
return self._exit_future.result()
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
class _BlockingPortalTaskStatus(TaskStatus):
|
| 178 |
+
def __init__(self, future: Future):
|
| 179 |
+
self._future = future
|
| 180 |
+
|
| 181 |
+
def started(self, value: object = None) -> None:
|
| 182 |
+
self._future.set_result(value)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
class BlockingPortal:
|
| 186 |
+
"""
|
| 187 |
+
An object that lets external threads run code in an asynchronous event loop.
|
| 188 |
+
|
| 189 |
+
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
| 190 |
+
current thread
|
| 191 |
+
"""
|
| 192 |
+
|
| 193 |
+
def __init__(self) -> None:
|
| 194 |
+
self._token = current_token()
|
| 195 |
+
self._event_loop_thread_id: int | None = get_ident()
|
| 196 |
+
self._stop_event = Event()
|
| 197 |
+
self._task_group = create_task_group()
|
| 198 |
+
|
| 199 |
+
async def __aenter__(self) -> BlockingPortal:
|
| 200 |
+
await self._task_group.__aenter__()
|
| 201 |
+
return self
|
| 202 |
+
|
| 203 |
+
async def __aexit__(
|
| 204 |
+
self,
|
| 205 |
+
exc_type: type[BaseException] | None,
|
| 206 |
+
exc_val: BaseException | None,
|
| 207 |
+
exc_tb: TracebackType | None,
|
| 208 |
+
) -> bool:
|
| 209 |
+
await self.stop()
|
| 210 |
+
return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
|
| 211 |
+
|
| 212 |
+
def _check_running(self) -> None:
|
| 213 |
+
if self._event_loop_thread_id is None:
|
| 214 |
+
raise RuntimeError("This portal is not running")
|
| 215 |
+
if self._event_loop_thread_id == get_ident():
|
| 216 |
+
raise RuntimeError(
|
| 217 |
+
"This method cannot be called from the event loop thread"
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
async def sleep_until_stopped(self) -> None:
|
| 221 |
+
"""Sleep until :meth:`stop` is called."""
|
| 222 |
+
await self._stop_event.wait()
|
| 223 |
+
|
| 224 |
+
async def stop(self, cancel_remaining: bool = False) -> None:
|
| 225 |
+
"""
|
| 226 |
+
Signal the portal to shut down.
|
| 227 |
+
|
| 228 |
+
This marks the portal as no longer accepting new calls and exits from
|
| 229 |
+
:meth:`sleep_until_stopped`.
|
| 230 |
+
|
| 231 |
+
:param cancel_remaining: ``True`` to cancel all the remaining tasks, ``False``
|
| 232 |
+
to let them finish before returning
|
| 233 |
+
|
| 234 |
+
"""
|
| 235 |
+
self._event_loop_thread_id = None
|
| 236 |
+
self._stop_event.set()
|
| 237 |
+
if cancel_remaining:
|
| 238 |
+
self._task_group.cancel_scope.cancel("the blocking portal is shutting down")
|
| 239 |
+
|
| 240 |
+
async def _call_func(
|
| 241 |
+
self,
|
| 242 |
+
func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
|
| 243 |
+
args: tuple[Unpack[PosArgsT]],
|
| 244 |
+
kwargs: dict[str, Any],
|
| 245 |
+
future: Future[T_Retval],
|
| 246 |
+
) -> None:
|
| 247 |
+
def callback(f: Future[T_Retval]) -> None:
|
| 248 |
+
if f.cancelled():
|
| 249 |
+
if self._event_loop_thread_id == get_ident():
|
| 250 |
+
scope.cancel("the future was cancelled")
|
| 251 |
+
elif self._event_loop_thread_id is not None:
|
| 252 |
+
self.call(scope.cancel, "the future was cancelled")
|
| 253 |
+
|
| 254 |
+
try:
|
| 255 |
+
retval_or_awaitable = func(*args, **kwargs)
|
| 256 |
+
if isawaitable(retval_or_awaitable):
|
| 257 |
+
with CancelScope() as scope:
|
| 258 |
+
future.add_done_callback(callback)
|
| 259 |
+
retval = await retval_or_awaitable
|
| 260 |
+
else:
|
| 261 |
+
retval = retval_or_awaitable
|
| 262 |
+
except get_cancelled_exc_class():
|
| 263 |
+
future.cancel()
|
| 264 |
+
future.set_running_or_notify_cancel()
|
| 265 |
+
except BaseException as exc:
|
| 266 |
+
if not future.cancelled():
|
| 267 |
+
future.set_exception(exc)
|
| 268 |
+
|
| 269 |
+
# Let base exceptions fall through
|
| 270 |
+
if not isinstance(exc, Exception):
|
| 271 |
+
raise
|
| 272 |
+
else:
|
| 273 |
+
if not future.cancelled():
|
| 274 |
+
future.set_result(retval)
|
| 275 |
+
finally:
|
| 276 |
+
scope = None # type: ignore[assignment]
|
| 277 |
+
|
| 278 |
+
def _spawn_task_from_thread(
|
| 279 |
+
self,
|
| 280 |
+
func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
|
| 281 |
+
args: tuple[Unpack[PosArgsT]],
|
| 282 |
+
kwargs: dict[str, Any],
|
| 283 |
+
name: object,
|
| 284 |
+
future: Future[T_Retval],
|
| 285 |
+
) -> None:
|
| 286 |
+
"""
|
| 287 |
+
Spawn a new task using the given callable.
|
| 288 |
+
|
| 289 |
+
:param func: a callable
|
| 290 |
+
:param args: positional arguments to be passed to the callable
|
| 291 |
+
:param kwargs: keyword arguments to be passed to the callable
|
| 292 |
+
:param name: name of the task (will be coerced to a string if not ``None``)
|
| 293 |
+
:param future: a future that will resolve to the return value of the callable,
|
| 294 |
+
or the exception raised during its execution
|
| 295 |
+
|
| 296 |
+
"""
|
| 297 |
+
run_sync(
|
| 298 |
+
partial(self._task_group.start_soon, name=name),
|
| 299 |
+
self._call_func,
|
| 300 |
+
func,
|
| 301 |
+
args,
|
| 302 |
+
kwargs,
|
| 303 |
+
future,
|
| 304 |
+
token=self._token,
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
@overload
|
| 308 |
+
def call(
|
| 309 |
+
self,
|
| 310 |
+
func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
|
| 311 |
+
*args: Unpack[PosArgsT],
|
| 312 |
+
) -> T_Retval: ...
|
| 313 |
+
|
| 314 |
+
@overload
|
| 315 |
+
def call(
|
| 316 |
+
self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT]
|
| 317 |
+
) -> T_Retval: ...
|
| 318 |
+
|
| 319 |
+
def call(
|
| 320 |
+
self,
|
| 321 |
+
func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
|
| 322 |
+
*args: Unpack[PosArgsT],
|
| 323 |
+
) -> T_Retval:
|
| 324 |
+
"""
|
| 325 |
+
Call the given function in the event loop thread.
|
| 326 |
+
|
| 327 |
+
If the callable returns a coroutine object, it is awaited on.
|
| 328 |
+
|
| 329 |
+
:param func: any callable
|
| 330 |
+
:raises RuntimeError: if the portal is not running or if this method is called
|
| 331 |
+
from within the event loop thread
|
| 332 |
+
|
| 333 |
+
"""
|
| 334 |
+
return cast(T_Retval, self.start_task_soon(func, *args).result())
|
| 335 |
+
|
| 336 |
+
@overload
|
| 337 |
+
def start_task_soon(
|
| 338 |
+
self,
|
| 339 |
+
func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
|
| 340 |
+
*args: Unpack[PosArgsT],
|
| 341 |
+
name: object = None,
|
| 342 |
+
) -> Future[T_Retval]: ...
|
| 343 |
+
|
| 344 |
+
@overload
|
| 345 |
+
def start_task_soon(
|
| 346 |
+
self,
|
| 347 |
+
func: Callable[[Unpack[PosArgsT]], T_Retval],
|
| 348 |
+
*args: Unpack[PosArgsT],
|
| 349 |
+
name: object = None,
|
| 350 |
+
) -> Future[T_Retval]: ...
|
| 351 |
+
|
| 352 |
+
def start_task_soon(
|
| 353 |
+
self,
|
| 354 |
+
func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval],
|
| 355 |
+
*args: Unpack[PosArgsT],
|
| 356 |
+
name: object = None,
|
| 357 |
+
) -> Future[T_Retval]:
|
| 358 |
+
"""
|
| 359 |
+
Start a task in the portal's task group.
|
| 360 |
+
|
| 361 |
+
The task will be run inside a cancel scope which can be cancelled by cancelling
|
| 362 |
+
the returned future.
|
| 363 |
+
|
| 364 |
+
:param func: the target function
|
| 365 |
+
:param args: positional arguments passed to ``func``
|
| 366 |
+
:param name: name of the task (will be coerced to a string if not ``None``)
|
| 367 |
+
:return: a future that resolves with the return value of the callable if the
|
| 368 |
+
task completes successfully, or with the exception raised in the task
|
| 369 |
+
:raises RuntimeError: if the portal is not running or if this method is called
|
| 370 |
+
from within the event loop thread
|
| 371 |
+
:rtype: concurrent.futures.Future[T_Retval]
|
| 372 |
+
|
| 373 |
+
.. versionadded:: 3.0
|
| 374 |
+
|
| 375 |
+
"""
|
| 376 |
+
self._check_running()
|
| 377 |
+
f: Future[T_Retval] = Future()
|
| 378 |
+
self._spawn_task_from_thread(func, args, {}, name, f)
|
| 379 |
+
return f
|
| 380 |
+
|
| 381 |
+
def start_task(
|
| 382 |
+
self,
|
| 383 |
+
func: Callable[..., Awaitable[T_Retval]],
|
| 384 |
+
*args: object,
|
| 385 |
+
name: object = None,
|
| 386 |
+
) -> tuple[Future[T_Retval], Any]:
|
| 387 |
+
"""
|
| 388 |
+
Start a task in the portal's task group and wait until it signals for readiness.
|
| 389 |
+
|
| 390 |
+
This method works the same way as :meth:`.abc.TaskGroup.start`.
|
| 391 |
+
|
| 392 |
+
:param func: the target function
|
| 393 |
+
:param args: positional arguments passed to ``func``
|
| 394 |
+
:param name: name of the task (will be coerced to a string if not ``None``)
|
| 395 |
+
:return: a tuple of (future, task_status_value) where the ``task_status_value``
|
| 396 |
+
is the value passed to ``task_status.started()`` from within the target
|
| 397 |
+
function
|
| 398 |
+
:rtype: tuple[concurrent.futures.Future[T_Retval], Any]
|
| 399 |
+
|
| 400 |
+
.. versionadded:: 3.0
|
| 401 |
+
|
| 402 |
+
"""
|
| 403 |
+
|
| 404 |
+
def task_done(future: Future[T_Retval]) -> None:
|
| 405 |
+
if not task_status_future.done():
|
| 406 |
+
if future.cancelled():
|
| 407 |
+
task_status_future.cancel()
|
| 408 |
+
elif future.exception():
|
| 409 |
+
task_status_future.set_exception(future.exception())
|
| 410 |
+
else:
|
| 411 |
+
exc = RuntimeError(
|
| 412 |
+
"Task exited without calling task_status.started()"
|
| 413 |
+
)
|
| 414 |
+
task_status_future.set_exception(exc)
|
| 415 |
+
|
| 416 |
+
self._check_running()
|
| 417 |
+
task_status_future: Future = Future()
|
| 418 |
+
task_status = _BlockingPortalTaskStatus(task_status_future)
|
| 419 |
+
f: Future = Future()
|
| 420 |
+
f.add_done_callback(task_done)
|
| 421 |
+
self._spawn_task_from_thread(func, args, {"task_status": task_status}, name, f)
|
| 422 |
+
return f, task_status_future.result()
|
| 423 |
+
|
| 424 |
+
def wrap_async_context_manager(
|
| 425 |
+
self, cm: AbstractAsyncContextManager[T_co]
|
| 426 |
+
) -> AbstractContextManager[T_co]:
|
| 427 |
+
"""
|
| 428 |
+
Wrap an async context manager as a synchronous context manager via this portal.
|
| 429 |
+
|
| 430 |
+
Spawns a task that will call both ``__aenter__()`` and ``__aexit__()``, stopping
|
| 431 |
+
in the middle until the synchronous context manager exits.
|
| 432 |
+
|
| 433 |
+
:param cm: an asynchronous context manager
|
| 434 |
+
:return: a synchronous context manager
|
| 435 |
+
|
| 436 |
+
.. versionadded:: 2.1
|
| 437 |
+
|
| 438 |
+
"""
|
| 439 |
+
return _BlockingAsyncContextManager(cm, self)
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
@dataclass
|
| 443 |
+
class BlockingPortalProvider:
|
| 444 |
+
"""
|
| 445 |
+
A manager for a blocking portal. Used as a context manager. The first thread to
|
| 446 |
+
enter this context manager causes a blocking portal to be started with the specific
|
| 447 |
+
parameters, and the last thread to exit causes the portal to be shut down. Thus,
|
| 448 |
+
there will be exactly one blocking portal running in this context as long as at
|
| 449 |
+
least one thread has entered this context manager.
|
| 450 |
+
|
| 451 |
+
The parameters are the same as for :func:`~anyio.run`.
|
| 452 |
+
|
| 453 |
+
:param backend: name of the backend
|
| 454 |
+
:param backend_options: backend options
|
| 455 |
+
|
| 456 |
+
.. versionadded:: 4.4
|
| 457 |
+
"""
|
| 458 |
+
|
| 459 |
+
backend: str = "asyncio"
|
| 460 |
+
backend_options: dict[str, Any] | None = None
|
| 461 |
+
_lock: Lock = field(init=False, default_factory=Lock)
|
| 462 |
+
_leases: int = field(init=False, default=0)
|
| 463 |
+
_portal: BlockingPortal = field(init=False)
|
| 464 |
+
_portal_cm: AbstractContextManager[BlockingPortal] | None = field(
|
| 465 |
+
init=False, default=None
|
| 466 |
+
)
|
| 467 |
+
|
| 468 |
+
def __enter__(self) -> BlockingPortal:
|
| 469 |
+
with self._lock:
|
| 470 |
+
if self._portal_cm is None:
|
| 471 |
+
self._portal_cm = start_blocking_portal(
|
| 472 |
+
self.backend, self.backend_options
|
| 473 |
+
)
|
| 474 |
+
self._portal = self._portal_cm.__enter__()
|
| 475 |
+
|
| 476 |
+
self._leases += 1
|
| 477 |
+
return self._portal
|
| 478 |
+
|
| 479 |
+
def __exit__(
|
| 480 |
+
self,
|
| 481 |
+
exc_type: type[BaseException] | None,
|
| 482 |
+
exc_val: BaseException | None,
|
| 483 |
+
exc_tb: TracebackType | None,
|
| 484 |
+
) -> None:
|
| 485 |
+
portal_cm: AbstractContextManager[BlockingPortal] | None = None
|
| 486 |
+
with self._lock:
|
| 487 |
+
assert self._portal_cm
|
| 488 |
+
assert self._leases > 0
|
| 489 |
+
self._leases -= 1
|
| 490 |
+
if not self._leases:
|
| 491 |
+
portal_cm = self._portal_cm
|
| 492 |
+
self._portal_cm = None
|
| 493 |
+
del self._portal
|
| 494 |
+
|
| 495 |
+
if portal_cm:
|
| 496 |
+
portal_cm.__exit__(None, None, None)
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
@contextmanager
|
| 500 |
+
def start_blocking_portal(
|
| 501 |
+
backend: str = "asyncio",
|
| 502 |
+
backend_options: dict[str, Any] | None = None,
|
| 503 |
+
*,
|
| 504 |
+
name: str | None = None,
|
| 505 |
+
) -> Generator[BlockingPortal, Any, None]:
|
| 506 |
+
"""
|
| 507 |
+
Start a new event loop in a new thread and run a blocking portal in its main task.
|
| 508 |
+
|
| 509 |
+
The parameters are the same as for :func:`~anyio.run`.
|
| 510 |
+
|
| 511 |
+
:param backend: name of the backend
|
| 512 |
+
:param backend_options: backend options
|
| 513 |
+
:param name: name of the thread
|
| 514 |
+
:return: a context manager that yields a blocking portal
|
| 515 |
+
|
| 516 |
+
.. versionchanged:: 3.0
|
| 517 |
+
Usage as a context manager is now required.
|
| 518 |
+
|
| 519 |
+
"""
|
| 520 |
+
|
| 521 |
+
async def run_portal() -> None:
|
| 522 |
+
async with BlockingPortal() as portal_:
|
| 523 |
+
if name is None:
|
| 524 |
+
current_thread().name = f"{backend}-portal-{id(portal_):x}"
|
| 525 |
+
|
| 526 |
+
future.set_result(portal_)
|
| 527 |
+
await portal_.sleep_until_stopped()
|
| 528 |
+
|
| 529 |
+
def run_blocking_portal() -> None:
|
| 530 |
+
if future.set_running_or_notify_cancel():
|
| 531 |
+
try:
|
| 532 |
+
run_eventloop(
|
| 533 |
+
run_portal, backend=backend, backend_options=backend_options
|
| 534 |
+
)
|
| 535 |
+
except BaseException as exc:
|
| 536 |
+
if not future.done():
|
| 537 |
+
future.set_exception(exc)
|
| 538 |
+
|
| 539 |
+
future: Future[BlockingPortal] = Future()
|
| 540 |
+
thread = Thread(target=run_blocking_portal, daemon=True, name=name)
|
| 541 |
+
thread.start()
|
| 542 |
+
try:
|
| 543 |
+
cancel_remaining_tasks = False
|
| 544 |
+
portal = future.result()
|
| 545 |
+
try:
|
| 546 |
+
yield portal
|
| 547 |
+
except BaseException:
|
| 548 |
+
cancel_remaining_tasks = True
|
| 549 |
+
raise
|
| 550 |
+
finally:
|
| 551 |
+
try:
|
| 552 |
+
portal.call(portal.stop, cancel_remaining_tasks)
|
| 553 |
+
except RuntimeError:
|
| 554 |
+
pass
|
| 555 |
+
finally:
|
| 556 |
+
thread.join()
|
| 557 |
+
|
| 558 |
+
|
| 559 |
+
def check_cancelled() -> None:
|
| 560 |
+
"""
|
| 561 |
+
Check if the cancel scope of the host task's running the current worker thread has
|
| 562 |
+
been cancelled.
|
| 563 |
+
|
| 564 |
+
If the host task's current cancel scope has indeed been cancelled, the
|
| 565 |
+
backend-specific cancellation exception will be raised.
|
| 566 |
+
|
| 567 |
+
:raises RuntimeError: if the current thread was not spawned by
|
| 568 |
+
:func:`.to_thread.run_sync`
|
| 569 |
+
|
| 570 |
+
"""
|
| 571 |
+
try:
|
| 572 |
+
token: EventLoopToken = threadlocals.current_token
|
| 573 |
+
except AttributeError:
|
| 574 |
+
raise NoEventLoopError(
|
| 575 |
+
"This function can only be called inside an AnyIO worker thread"
|
| 576 |
+
) from None
|
| 577 |
+
|
| 578 |
+
token.backend_class.check_cancelled()
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/anyio/py.typed
ADDED
|
File without changes
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/__init__.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# A highish-level implementation of the HTTP/1.1 wire protocol (RFC 7230),
|
| 2 |
+
# containing no networking code at all, loosely modelled on hyper-h2's generic
|
| 3 |
+
# implementation of HTTP/2 (and in particular the h2.connection.H2Connection
|
| 4 |
+
# class). There's still a bunch of subtle details you need to get right if you
|
| 5 |
+
# want to make this actually useful, because it doesn't implement all the
|
| 6 |
+
# semantics to check that what you're asking to write to the wire is sensible,
|
| 7 |
+
# but at least it gets you out of dealing with the wire itself.
|
| 8 |
+
|
| 9 |
+
from h11._connection import Connection, NEED_DATA, PAUSED
|
| 10 |
+
from h11._events import (
|
| 11 |
+
ConnectionClosed,
|
| 12 |
+
Data,
|
| 13 |
+
EndOfMessage,
|
| 14 |
+
Event,
|
| 15 |
+
InformationalResponse,
|
| 16 |
+
Request,
|
| 17 |
+
Response,
|
| 18 |
+
)
|
| 19 |
+
from h11._state import (
|
| 20 |
+
CLIENT,
|
| 21 |
+
CLOSED,
|
| 22 |
+
DONE,
|
| 23 |
+
ERROR,
|
| 24 |
+
IDLE,
|
| 25 |
+
MIGHT_SWITCH_PROTOCOL,
|
| 26 |
+
MUST_CLOSE,
|
| 27 |
+
SEND_BODY,
|
| 28 |
+
SEND_RESPONSE,
|
| 29 |
+
SERVER,
|
| 30 |
+
SWITCHED_PROTOCOL,
|
| 31 |
+
)
|
| 32 |
+
from h11._util import LocalProtocolError, ProtocolError, RemoteProtocolError
|
| 33 |
+
from h11._version import __version__
|
| 34 |
+
|
| 35 |
+
PRODUCT_ID = "python-h11/" + __version__
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
__all__ = (
|
| 39 |
+
"Connection",
|
| 40 |
+
"NEED_DATA",
|
| 41 |
+
"PAUSED",
|
| 42 |
+
"ConnectionClosed",
|
| 43 |
+
"Data",
|
| 44 |
+
"EndOfMessage",
|
| 45 |
+
"Event",
|
| 46 |
+
"InformationalResponse",
|
| 47 |
+
"Request",
|
| 48 |
+
"Response",
|
| 49 |
+
"CLIENT",
|
| 50 |
+
"CLOSED",
|
| 51 |
+
"DONE",
|
| 52 |
+
"ERROR",
|
| 53 |
+
"IDLE",
|
| 54 |
+
"MUST_CLOSE",
|
| 55 |
+
"SEND_BODY",
|
| 56 |
+
"SEND_RESPONSE",
|
| 57 |
+
"SERVER",
|
| 58 |
+
"SWITCHED_PROTOCOL",
|
| 59 |
+
"ProtocolError",
|
| 60 |
+
"LocalProtocolError",
|
| 61 |
+
"RemoteProtocolError",
|
| 62 |
+
)
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_abnf.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# We use native strings for all the re patterns, to take advantage of string
|
| 2 |
+
# formatting, and then convert to bytestrings when compiling the final re
|
| 3 |
+
# objects.
|
| 4 |
+
|
| 5 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#whitespace
|
| 6 |
+
# OWS = *( SP / HTAB )
|
| 7 |
+
# ; optional whitespace
|
| 8 |
+
OWS = r"[ \t]*"
|
| 9 |
+
|
| 10 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.token.separators
|
| 11 |
+
# token = 1*tchar
|
| 12 |
+
#
|
| 13 |
+
# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
|
| 14 |
+
# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
|
| 15 |
+
# / DIGIT / ALPHA
|
| 16 |
+
# ; any VCHAR, except delimiters
|
| 17 |
+
token = r"[-!#$%&'*+.^_`|~0-9a-zA-Z]+"
|
| 18 |
+
|
| 19 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#header.fields
|
| 20 |
+
# field-name = token
|
| 21 |
+
field_name = token
|
| 22 |
+
|
| 23 |
+
# The standard says:
|
| 24 |
+
#
|
| 25 |
+
# field-value = *( field-content / obs-fold )
|
| 26 |
+
# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
| 27 |
+
# field-vchar = VCHAR / obs-text
|
| 28 |
+
# obs-fold = CRLF 1*( SP / HTAB )
|
| 29 |
+
# ; obsolete line folding
|
| 30 |
+
# ; see Section 3.2.4
|
| 31 |
+
#
|
| 32 |
+
# https://tools.ietf.org/html/rfc5234#appendix-B.1
|
| 33 |
+
#
|
| 34 |
+
# VCHAR = %x21-7E
|
| 35 |
+
# ; visible (printing) characters
|
| 36 |
+
#
|
| 37 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.quoted-string
|
| 38 |
+
# obs-text = %x80-FF
|
| 39 |
+
#
|
| 40 |
+
# However, the standard definition of field-content is WRONG! It disallows
|
| 41 |
+
# fields containing a single visible character surrounded by whitespace,
|
| 42 |
+
# e.g. "foo a bar".
|
| 43 |
+
#
|
| 44 |
+
# See: https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189
|
| 45 |
+
#
|
| 46 |
+
# So our definition of field_content attempts to fix it up...
|
| 47 |
+
#
|
| 48 |
+
# Also, we allow lots of control characters, because apparently people assume
|
| 49 |
+
# that they're legal in practice (e.g., google analytics makes cookies with
|
| 50 |
+
# \x01 in them!):
|
| 51 |
+
# https://github.com/python-hyper/h11/issues/57
|
| 52 |
+
# We still don't allow NUL or whitespace, because those are often treated as
|
| 53 |
+
# meta-characters and letting them through can lead to nasty issues like SSRF.
|
| 54 |
+
vchar = r"[\x21-\x7e]"
|
| 55 |
+
vchar_or_obs_text = r"[^\x00\s]"
|
| 56 |
+
field_vchar = vchar_or_obs_text
|
| 57 |
+
field_content = r"{field_vchar}+(?:[ \t]+{field_vchar}+)*".format(**globals())
|
| 58 |
+
|
| 59 |
+
# We handle obs-fold at a different level, and our fixed-up field_content
|
| 60 |
+
# already grows to swallow the whole value, so ? instead of *
|
| 61 |
+
field_value = r"({field_content})?".format(**globals())
|
| 62 |
+
|
| 63 |
+
# header-field = field-name ":" OWS field-value OWS
|
| 64 |
+
header_field = (
|
| 65 |
+
r"(?P<field_name>{field_name})"
|
| 66 |
+
r":"
|
| 67 |
+
r"{OWS}"
|
| 68 |
+
r"(?P<field_value>{field_value})"
|
| 69 |
+
r"{OWS}".format(**globals())
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#request.line
|
| 73 |
+
#
|
| 74 |
+
# request-line = method SP request-target SP HTTP-version CRLF
|
| 75 |
+
# method = token
|
| 76 |
+
# HTTP-version = HTTP-name "/" DIGIT "." DIGIT
|
| 77 |
+
# HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive
|
| 78 |
+
#
|
| 79 |
+
# request-target is complicated (see RFC 7230 sec 5.3) -- could be path, full
|
| 80 |
+
# URL, host+port (for connect), or even "*", but in any case we are guaranteed
|
| 81 |
+
# that it contists of the visible printing characters.
|
| 82 |
+
method = token
|
| 83 |
+
request_target = r"{vchar}+".format(**globals())
|
| 84 |
+
http_version = r"HTTP/(?P<http_version>[0-9]\.[0-9])"
|
| 85 |
+
request_line = (
|
| 86 |
+
r"(?P<method>{method})"
|
| 87 |
+
r" "
|
| 88 |
+
r"(?P<target>{request_target})"
|
| 89 |
+
r" "
|
| 90 |
+
r"{http_version}".format(**globals())
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#status.line
|
| 94 |
+
#
|
| 95 |
+
# status-line = HTTP-version SP status-code SP reason-phrase CRLF
|
| 96 |
+
# status-code = 3DIGIT
|
| 97 |
+
# reason-phrase = *( HTAB / SP / VCHAR / obs-text )
|
| 98 |
+
status_code = r"[0-9]{3}"
|
| 99 |
+
reason_phrase = r"([ \t]|{vchar_or_obs_text})*".format(**globals())
|
| 100 |
+
status_line = (
|
| 101 |
+
r"{http_version}"
|
| 102 |
+
r" "
|
| 103 |
+
r"(?P<status_code>{status_code})"
|
| 104 |
+
# However, there are apparently a few too many servers out there that just
|
| 105 |
+
# leave out the reason phrase:
|
| 106 |
+
# https://github.com/scrapy/scrapy/issues/345#issuecomment-281756036
|
| 107 |
+
# https://github.com/seanmonstar/httparse/issues/29
|
| 108 |
+
# so make it optional. ?: is a non-capturing group.
|
| 109 |
+
r"(?: (?P<reason>{reason_phrase}))?".format(**globals())
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
HEXDIG = r"[0-9A-Fa-f]"
|
| 113 |
+
# Actually
|
| 114 |
+
#
|
| 115 |
+
# chunk-size = 1*HEXDIG
|
| 116 |
+
#
|
| 117 |
+
# but we impose an upper-limit to avoid ridiculosity. len(str(2**64)) == 20
|
| 118 |
+
chunk_size = r"({HEXDIG}){{1,20}}".format(**globals())
|
| 119 |
+
# Actually
|
| 120 |
+
#
|
| 121 |
+
# chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
| 122 |
+
#
|
| 123 |
+
# but we aren't parsing the things so we don't really care.
|
| 124 |
+
chunk_ext = r";.*"
|
| 125 |
+
chunk_header = (
|
| 126 |
+
r"(?P<chunk_size>{chunk_size})"
|
| 127 |
+
r"(?P<chunk_ext>{chunk_ext})?"
|
| 128 |
+
r"{OWS}\r\n".format(
|
| 129 |
+
**globals()
|
| 130 |
+
) # Even though the specification does not allow for extra whitespaces,
|
| 131 |
+
# we are lenient with trailing whitespaces because some servers on the wild use it.
|
| 132 |
+
)
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_connection.py
ADDED
|
@@ -0,0 +1,659 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This contains the main Connection class. Everything in h11 revolves around
|
| 2 |
+
# this.
|
| 3 |
+
from typing import (
|
| 4 |
+
Any,
|
| 5 |
+
Callable,
|
| 6 |
+
cast,
|
| 7 |
+
Dict,
|
| 8 |
+
List,
|
| 9 |
+
Optional,
|
| 10 |
+
overload,
|
| 11 |
+
Tuple,
|
| 12 |
+
Type,
|
| 13 |
+
Union,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
from ._events import (
|
| 17 |
+
ConnectionClosed,
|
| 18 |
+
Data,
|
| 19 |
+
EndOfMessage,
|
| 20 |
+
Event,
|
| 21 |
+
InformationalResponse,
|
| 22 |
+
Request,
|
| 23 |
+
Response,
|
| 24 |
+
)
|
| 25 |
+
from ._headers import get_comma_header, has_expect_100_continue, set_comma_header
|
| 26 |
+
from ._readers import READERS, ReadersType
|
| 27 |
+
from ._receivebuffer import ReceiveBuffer
|
| 28 |
+
from ._state import (
|
| 29 |
+
_SWITCH_CONNECT,
|
| 30 |
+
_SWITCH_UPGRADE,
|
| 31 |
+
CLIENT,
|
| 32 |
+
ConnectionState,
|
| 33 |
+
DONE,
|
| 34 |
+
ERROR,
|
| 35 |
+
MIGHT_SWITCH_PROTOCOL,
|
| 36 |
+
SEND_BODY,
|
| 37 |
+
SERVER,
|
| 38 |
+
SWITCHED_PROTOCOL,
|
| 39 |
+
)
|
| 40 |
+
from ._util import ( # Import the internal things we need
|
| 41 |
+
LocalProtocolError,
|
| 42 |
+
RemoteProtocolError,
|
| 43 |
+
Sentinel,
|
| 44 |
+
)
|
| 45 |
+
from ._writers import WRITERS, WritersType
|
| 46 |
+
|
| 47 |
+
# Everything in __all__ gets re-exported as part of the h11 public API.
|
| 48 |
+
__all__ = ["Connection", "NEED_DATA", "PAUSED"]
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class NEED_DATA(Sentinel, metaclass=Sentinel):
|
| 52 |
+
pass
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
class PAUSED(Sentinel, metaclass=Sentinel):
|
| 56 |
+
pass
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
# If we ever have this much buffered without it making a complete parseable
|
| 60 |
+
# event, we error out. The only time we really buffer is when reading the
|
| 61 |
+
# request/response line + headers together, so this is effectively the limit on
|
| 62 |
+
# the size of that.
|
| 63 |
+
#
|
| 64 |
+
# Some precedents for defaults:
|
| 65 |
+
# - node.js: 80 * 1024
|
| 66 |
+
# - tomcat: 8 * 1024
|
| 67 |
+
# - IIS: 16 * 1024
|
| 68 |
+
# - Apache: <8 KiB per line>
|
| 69 |
+
DEFAULT_MAX_INCOMPLETE_EVENT_SIZE = 16 * 1024
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
# RFC 7230's rules for connection lifecycles:
|
| 73 |
+
# - If either side says they want to close the connection, then the connection
|
| 74 |
+
# must close.
|
| 75 |
+
# - HTTP/1.1 defaults to keep-alive unless someone says Connection: close
|
| 76 |
+
# - HTTP/1.0 defaults to close unless both sides say Connection: keep-alive
|
| 77 |
+
# (and even this is a mess -- e.g. if you're implementing a proxy then
|
| 78 |
+
# sending Connection: keep-alive is forbidden).
|
| 79 |
+
#
|
| 80 |
+
# We simplify life by simply not supporting keep-alive with HTTP/1.0 peers. So
|
| 81 |
+
# our rule is:
|
| 82 |
+
# - If someone says Connection: close, we will close
|
| 83 |
+
# - If someone uses HTTP/1.0, we will close.
|
| 84 |
+
def _keep_alive(event: Union[Request, Response]) -> bool:
|
| 85 |
+
connection = get_comma_header(event.headers, b"connection")
|
| 86 |
+
if b"close" in connection:
|
| 87 |
+
return False
|
| 88 |
+
if getattr(event, "http_version", b"1.1") < b"1.1":
|
| 89 |
+
return False
|
| 90 |
+
return True
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def _body_framing(
|
| 94 |
+
request_method: bytes, event: Union[Request, Response]
|
| 95 |
+
) -> Tuple[str, Union[Tuple[()], Tuple[int]]]:
|
| 96 |
+
# Called when we enter SEND_BODY to figure out framing information for
|
| 97 |
+
# this body.
|
| 98 |
+
#
|
| 99 |
+
# These are the only two events that can trigger a SEND_BODY state:
|
| 100 |
+
assert type(event) in (Request, Response)
|
| 101 |
+
# Returns one of:
|
| 102 |
+
#
|
| 103 |
+
# ("content-length", count)
|
| 104 |
+
# ("chunked", ())
|
| 105 |
+
# ("http/1.0", ())
|
| 106 |
+
#
|
| 107 |
+
# which are (lookup key, *args) for constructing body reader/writer
|
| 108 |
+
# objects.
|
| 109 |
+
#
|
| 110 |
+
# Reference: https://tools.ietf.org/html/rfc7230#section-3.3.3
|
| 111 |
+
#
|
| 112 |
+
# Step 1: some responses always have an empty body, regardless of what the
|
| 113 |
+
# headers say.
|
| 114 |
+
if type(event) is Response:
|
| 115 |
+
if (
|
| 116 |
+
event.status_code in (204, 304)
|
| 117 |
+
or request_method == b"HEAD"
|
| 118 |
+
or (request_method == b"CONNECT" and 200 <= event.status_code < 300)
|
| 119 |
+
):
|
| 120 |
+
return ("content-length", (0,))
|
| 121 |
+
# Section 3.3.3 also lists another case -- responses with status_code
|
| 122 |
+
# < 200. For us these are InformationalResponses, not Responses, so
|
| 123 |
+
# they can't get into this function in the first place.
|
| 124 |
+
assert event.status_code >= 200
|
| 125 |
+
|
| 126 |
+
# Step 2: check for Transfer-Encoding (T-E beats C-L):
|
| 127 |
+
transfer_encodings = get_comma_header(event.headers, b"transfer-encoding")
|
| 128 |
+
if transfer_encodings:
|
| 129 |
+
assert transfer_encodings == [b"chunked"]
|
| 130 |
+
return ("chunked", ())
|
| 131 |
+
|
| 132 |
+
# Step 3: check for Content-Length
|
| 133 |
+
content_lengths = get_comma_header(event.headers, b"content-length")
|
| 134 |
+
if content_lengths:
|
| 135 |
+
return ("content-length", (int(content_lengths[0]),))
|
| 136 |
+
|
| 137 |
+
# Step 4: no applicable headers; fallback/default depends on type
|
| 138 |
+
if type(event) is Request:
|
| 139 |
+
return ("content-length", (0,))
|
| 140 |
+
else:
|
| 141 |
+
return ("http/1.0", ())
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
################################################################
|
| 145 |
+
#
|
| 146 |
+
# The main Connection class
|
| 147 |
+
#
|
| 148 |
+
################################################################
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
class Connection:
|
| 152 |
+
"""An object encapsulating the state of an HTTP connection.
|
| 153 |
+
|
| 154 |
+
Args:
|
| 155 |
+
our_role: If you're implementing a client, pass :data:`h11.CLIENT`. If
|
| 156 |
+
you're implementing a server, pass :data:`h11.SERVER`.
|
| 157 |
+
|
| 158 |
+
max_incomplete_event_size (int):
|
| 159 |
+
The maximum number of bytes we're willing to buffer of an
|
| 160 |
+
incomplete event. In practice this mostly sets a limit on the
|
| 161 |
+
maximum size of the request/response line + headers. If this is
|
| 162 |
+
exceeded, then :meth:`next_event` will raise
|
| 163 |
+
:exc:`RemoteProtocolError`.
|
| 164 |
+
|
| 165 |
+
"""
|
| 166 |
+
|
| 167 |
+
def __init__(
|
| 168 |
+
self,
|
| 169 |
+
our_role: Type[Sentinel],
|
| 170 |
+
max_incomplete_event_size: int = DEFAULT_MAX_INCOMPLETE_EVENT_SIZE,
|
| 171 |
+
) -> None:
|
| 172 |
+
self._max_incomplete_event_size = max_incomplete_event_size
|
| 173 |
+
# State and role tracking
|
| 174 |
+
if our_role not in (CLIENT, SERVER):
|
| 175 |
+
raise ValueError(f"expected CLIENT or SERVER, not {our_role!r}")
|
| 176 |
+
self.our_role = our_role
|
| 177 |
+
self.their_role: Type[Sentinel]
|
| 178 |
+
if our_role is CLIENT:
|
| 179 |
+
self.their_role = SERVER
|
| 180 |
+
else:
|
| 181 |
+
self.their_role = CLIENT
|
| 182 |
+
self._cstate = ConnectionState()
|
| 183 |
+
|
| 184 |
+
# Callables for converting data->events or vice-versa given the
|
| 185 |
+
# current state
|
| 186 |
+
self._writer = self._get_io_object(self.our_role, None, WRITERS)
|
| 187 |
+
self._reader = self._get_io_object(self.their_role, None, READERS)
|
| 188 |
+
|
| 189 |
+
# Holds any unprocessed received data
|
| 190 |
+
self._receive_buffer = ReceiveBuffer()
|
| 191 |
+
# If this is true, then it indicates that the incoming connection was
|
| 192 |
+
# closed *after* the end of whatever's in self._receive_buffer:
|
| 193 |
+
self._receive_buffer_closed = False
|
| 194 |
+
|
| 195 |
+
# Extra bits of state that don't fit into the state machine.
|
| 196 |
+
#
|
| 197 |
+
# These two are only used to interpret framing headers for figuring
|
| 198 |
+
# out how to read/write response bodies. their_http_version is also
|
| 199 |
+
# made available as a convenient public API.
|
| 200 |
+
self.their_http_version: Optional[bytes] = None
|
| 201 |
+
self._request_method: Optional[bytes] = None
|
| 202 |
+
# This is pure flow-control and doesn't at all affect the set of legal
|
| 203 |
+
# transitions, so no need to bother ConnectionState with it:
|
| 204 |
+
self.client_is_waiting_for_100_continue = False
|
| 205 |
+
|
| 206 |
+
@property
|
| 207 |
+
def states(self) -> Dict[Type[Sentinel], Type[Sentinel]]:
|
| 208 |
+
"""A dictionary like::
|
| 209 |
+
|
| 210 |
+
{CLIENT: <client state>, SERVER: <server state>}
|
| 211 |
+
|
| 212 |
+
See :ref:`state-machine` for details.
|
| 213 |
+
|
| 214 |
+
"""
|
| 215 |
+
return dict(self._cstate.states)
|
| 216 |
+
|
| 217 |
+
@property
|
| 218 |
+
def our_state(self) -> Type[Sentinel]:
|
| 219 |
+
"""The current state of whichever role we are playing. See
|
| 220 |
+
:ref:`state-machine` for details.
|
| 221 |
+
"""
|
| 222 |
+
return self._cstate.states[self.our_role]
|
| 223 |
+
|
| 224 |
+
@property
|
| 225 |
+
def their_state(self) -> Type[Sentinel]:
|
| 226 |
+
"""The current state of whichever role we are NOT playing. See
|
| 227 |
+
:ref:`state-machine` for details.
|
| 228 |
+
"""
|
| 229 |
+
return self._cstate.states[self.their_role]
|
| 230 |
+
|
| 231 |
+
@property
|
| 232 |
+
def they_are_waiting_for_100_continue(self) -> bool:
|
| 233 |
+
return self.their_role is CLIENT and self.client_is_waiting_for_100_continue
|
| 234 |
+
|
| 235 |
+
def start_next_cycle(self) -> None:
|
| 236 |
+
"""Attempt to reset our connection state for a new request/response
|
| 237 |
+
cycle.
|
| 238 |
+
|
| 239 |
+
If both client and server are in :data:`DONE` state, then resets them
|
| 240 |
+
both to :data:`IDLE` state in preparation for a new request/response
|
| 241 |
+
cycle on this same connection. Otherwise, raises a
|
| 242 |
+
:exc:`LocalProtocolError`.
|
| 243 |
+
|
| 244 |
+
See :ref:`keepalive-and-pipelining`.
|
| 245 |
+
|
| 246 |
+
"""
|
| 247 |
+
old_states = dict(self._cstate.states)
|
| 248 |
+
self._cstate.start_next_cycle()
|
| 249 |
+
self._request_method = None
|
| 250 |
+
# self.their_http_version gets left alone, since it presumably lasts
|
| 251 |
+
# beyond a single request/response cycle
|
| 252 |
+
assert not self.client_is_waiting_for_100_continue
|
| 253 |
+
self._respond_to_state_changes(old_states)
|
| 254 |
+
|
| 255 |
+
def _process_error(self, role: Type[Sentinel]) -> None:
|
| 256 |
+
old_states = dict(self._cstate.states)
|
| 257 |
+
self._cstate.process_error(role)
|
| 258 |
+
self._respond_to_state_changes(old_states)
|
| 259 |
+
|
| 260 |
+
def _server_switch_event(self, event: Event) -> Optional[Type[Sentinel]]:
|
| 261 |
+
if type(event) is InformationalResponse and event.status_code == 101:
|
| 262 |
+
return _SWITCH_UPGRADE
|
| 263 |
+
if type(event) is Response:
|
| 264 |
+
if (
|
| 265 |
+
_SWITCH_CONNECT in self._cstate.pending_switch_proposals
|
| 266 |
+
and 200 <= event.status_code < 300
|
| 267 |
+
):
|
| 268 |
+
return _SWITCH_CONNECT
|
| 269 |
+
return None
|
| 270 |
+
|
| 271 |
+
# All events go through here
|
| 272 |
+
def _process_event(self, role: Type[Sentinel], event: Event) -> None:
|
| 273 |
+
# First, pass the event through the state machine to make sure it
|
| 274 |
+
# succeeds.
|
| 275 |
+
old_states = dict(self._cstate.states)
|
| 276 |
+
if role is CLIENT and type(event) is Request:
|
| 277 |
+
if event.method == b"CONNECT":
|
| 278 |
+
self._cstate.process_client_switch_proposal(_SWITCH_CONNECT)
|
| 279 |
+
if get_comma_header(event.headers, b"upgrade"):
|
| 280 |
+
self._cstate.process_client_switch_proposal(_SWITCH_UPGRADE)
|
| 281 |
+
server_switch_event = None
|
| 282 |
+
if role is SERVER:
|
| 283 |
+
server_switch_event = self._server_switch_event(event)
|
| 284 |
+
self._cstate.process_event(role, type(event), server_switch_event)
|
| 285 |
+
|
| 286 |
+
# Then perform the updates triggered by it.
|
| 287 |
+
|
| 288 |
+
if type(event) is Request:
|
| 289 |
+
self._request_method = event.method
|
| 290 |
+
|
| 291 |
+
if role is self.their_role and type(event) in (
|
| 292 |
+
Request,
|
| 293 |
+
Response,
|
| 294 |
+
InformationalResponse,
|
| 295 |
+
):
|
| 296 |
+
event = cast(Union[Request, Response, InformationalResponse], event)
|
| 297 |
+
self.their_http_version = event.http_version
|
| 298 |
+
|
| 299 |
+
# Keep alive handling
|
| 300 |
+
#
|
| 301 |
+
# RFC 7230 doesn't really say what one should do if Connection: close
|
| 302 |
+
# shows up on a 1xx InformationalResponse. I think the idea is that
|
| 303 |
+
# this is not supposed to happen. In any case, if it does happen, we
|
| 304 |
+
# ignore it.
|
| 305 |
+
if type(event) in (Request, Response) and not _keep_alive(
|
| 306 |
+
cast(Union[Request, Response], event)
|
| 307 |
+
):
|
| 308 |
+
self._cstate.process_keep_alive_disabled()
|
| 309 |
+
|
| 310 |
+
# 100-continue
|
| 311 |
+
if type(event) is Request and has_expect_100_continue(event):
|
| 312 |
+
self.client_is_waiting_for_100_continue = True
|
| 313 |
+
if type(event) in (InformationalResponse, Response):
|
| 314 |
+
self.client_is_waiting_for_100_continue = False
|
| 315 |
+
if role is CLIENT and type(event) in (Data, EndOfMessage):
|
| 316 |
+
self.client_is_waiting_for_100_continue = False
|
| 317 |
+
|
| 318 |
+
self._respond_to_state_changes(old_states, event)
|
| 319 |
+
|
| 320 |
+
def _get_io_object(
|
| 321 |
+
self,
|
| 322 |
+
role: Type[Sentinel],
|
| 323 |
+
event: Optional[Event],
|
| 324 |
+
io_dict: Union[ReadersType, WritersType],
|
| 325 |
+
) -> Optional[Callable[..., Any]]:
|
| 326 |
+
# event may be None; it's only used when entering SEND_BODY
|
| 327 |
+
state = self._cstate.states[role]
|
| 328 |
+
if state is SEND_BODY:
|
| 329 |
+
# Special case: the io_dict has a dict of reader/writer factories
|
| 330 |
+
# that depend on the request/response framing.
|
| 331 |
+
framing_type, args = _body_framing(
|
| 332 |
+
cast(bytes, self._request_method), cast(Union[Request, Response], event)
|
| 333 |
+
)
|
| 334 |
+
return io_dict[SEND_BODY][framing_type](*args) # type: ignore[index]
|
| 335 |
+
else:
|
| 336 |
+
# General case: the io_dict just has the appropriate reader/writer
|
| 337 |
+
# for this state
|
| 338 |
+
return io_dict.get((role, state)) # type: ignore[return-value]
|
| 339 |
+
|
| 340 |
+
# This must be called after any action that might have caused
|
| 341 |
+
# self._cstate.states to change.
|
| 342 |
+
def _respond_to_state_changes(
|
| 343 |
+
self,
|
| 344 |
+
old_states: Dict[Type[Sentinel], Type[Sentinel]],
|
| 345 |
+
event: Optional[Event] = None,
|
| 346 |
+
) -> None:
|
| 347 |
+
# Update reader/writer
|
| 348 |
+
if self.our_state != old_states[self.our_role]:
|
| 349 |
+
self._writer = self._get_io_object(self.our_role, event, WRITERS)
|
| 350 |
+
if self.their_state != old_states[self.their_role]:
|
| 351 |
+
self._reader = self._get_io_object(self.their_role, event, READERS)
|
| 352 |
+
|
| 353 |
+
@property
|
| 354 |
+
def trailing_data(self) -> Tuple[bytes, bool]:
|
| 355 |
+
"""Data that has been received, but not yet processed, represented as
|
| 356 |
+
a tuple with two elements, where the first is a byte-string containing
|
| 357 |
+
the unprocessed data itself, and the second is a bool that is True if
|
| 358 |
+
the receive connection was closed.
|
| 359 |
+
|
| 360 |
+
See :ref:`switching-protocols` for discussion of why you'd want this.
|
| 361 |
+
"""
|
| 362 |
+
return (bytes(self._receive_buffer), self._receive_buffer_closed)
|
| 363 |
+
|
| 364 |
+
def receive_data(self, data: bytes) -> None:
|
| 365 |
+
"""Add data to our internal receive buffer.
|
| 366 |
+
|
| 367 |
+
This does not actually do any processing on the data, just stores
|
| 368 |
+
it. To trigger processing, you have to call :meth:`next_event`.
|
| 369 |
+
|
| 370 |
+
Args:
|
| 371 |
+
data (:term:`bytes-like object`):
|
| 372 |
+
The new data that was just received.
|
| 373 |
+
|
| 374 |
+
Special case: If *data* is an empty byte-string like ``b""``,
|
| 375 |
+
then this indicates that the remote side has closed the
|
| 376 |
+
connection (end of file). Normally this is convenient, because
|
| 377 |
+
standard Python APIs like :meth:`file.read` or
|
| 378 |
+
:meth:`socket.recv` use ``b""`` to indicate end-of-file, while
|
| 379 |
+
other failures to read are indicated using other mechanisms
|
| 380 |
+
like raising :exc:`TimeoutError`. When using such an API you
|
| 381 |
+
can just blindly pass through whatever you get from ``read``
|
| 382 |
+
to :meth:`receive_data`, and everything will work.
|
| 383 |
+
|
| 384 |
+
But, if you have an API where reading an empty string is a
|
| 385 |
+
valid non-EOF condition, then you need to be aware of this and
|
| 386 |
+
make sure to check for such strings and avoid passing them to
|
| 387 |
+
:meth:`receive_data`.
|
| 388 |
+
|
| 389 |
+
Returns:
|
| 390 |
+
Nothing, but after calling this you should call :meth:`next_event`
|
| 391 |
+
to parse the newly received data.
|
| 392 |
+
|
| 393 |
+
Raises:
|
| 394 |
+
RuntimeError:
|
| 395 |
+
Raised if you pass an empty *data*, indicating EOF, and then
|
| 396 |
+
pass a non-empty *data*, indicating more data that somehow
|
| 397 |
+
arrived after the EOF.
|
| 398 |
+
|
| 399 |
+
(Calling ``receive_data(b"")`` multiple times is fine,
|
| 400 |
+
and equivalent to calling it once.)
|
| 401 |
+
|
| 402 |
+
"""
|
| 403 |
+
if data:
|
| 404 |
+
if self._receive_buffer_closed:
|
| 405 |
+
raise RuntimeError("received close, then received more data?")
|
| 406 |
+
self._receive_buffer += data
|
| 407 |
+
else:
|
| 408 |
+
self._receive_buffer_closed = True
|
| 409 |
+
|
| 410 |
+
def _extract_next_receive_event(
|
| 411 |
+
self,
|
| 412 |
+
) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]:
|
| 413 |
+
state = self.their_state
|
| 414 |
+
# We don't pause immediately when they enter DONE, because even in
|
| 415 |
+
# DONE state we can still process a ConnectionClosed() event. But
|
| 416 |
+
# if we have data in our buffer, then we definitely aren't getting
|
| 417 |
+
# a ConnectionClosed() immediately and we need to pause.
|
| 418 |
+
if state is DONE and self._receive_buffer:
|
| 419 |
+
return PAUSED
|
| 420 |
+
if state is MIGHT_SWITCH_PROTOCOL or state is SWITCHED_PROTOCOL:
|
| 421 |
+
return PAUSED
|
| 422 |
+
assert self._reader is not None
|
| 423 |
+
event = self._reader(self._receive_buffer)
|
| 424 |
+
if event is None:
|
| 425 |
+
if not self._receive_buffer and self._receive_buffer_closed:
|
| 426 |
+
# In some unusual cases (basically just HTTP/1.0 bodies), EOF
|
| 427 |
+
# triggers an actual protocol event; in that case, we want to
|
| 428 |
+
# return that event, and then the state will change and we'll
|
| 429 |
+
# get called again to generate the actual ConnectionClosed().
|
| 430 |
+
if hasattr(self._reader, "read_eof"):
|
| 431 |
+
event = self._reader.read_eof()
|
| 432 |
+
else:
|
| 433 |
+
event = ConnectionClosed()
|
| 434 |
+
if event is None:
|
| 435 |
+
event = NEED_DATA
|
| 436 |
+
return event # type: ignore[no-any-return]
|
| 437 |
+
|
| 438 |
+
def next_event(self) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]:
|
| 439 |
+
"""Parse the next event out of our receive buffer, update our internal
|
| 440 |
+
state, and return it.
|
| 441 |
+
|
| 442 |
+
This is a mutating operation -- think of it like calling :func:`next`
|
| 443 |
+
on an iterator.
|
| 444 |
+
|
| 445 |
+
Returns:
|
| 446 |
+
: One of three things:
|
| 447 |
+
|
| 448 |
+
1) An event object -- see :ref:`events`.
|
| 449 |
+
|
| 450 |
+
2) The special constant :data:`NEED_DATA`, which indicates that
|
| 451 |
+
you need to read more data from your socket and pass it to
|
| 452 |
+
:meth:`receive_data` before this method will be able to return
|
| 453 |
+
any more events.
|
| 454 |
+
|
| 455 |
+
3) The special constant :data:`PAUSED`, which indicates that we
|
| 456 |
+
are not in a state where we can process incoming data (usually
|
| 457 |
+
because the peer has finished their part of the current
|
| 458 |
+
request/response cycle, and you have not yet called
|
| 459 |
+
:meth:`start_next_cycle`). See :ref:`flow-control` for details.
|
| 460 |
+
|
| 461 |
+
Raises:
|
| 462 |
+
RemoteProtocolError:
|
| 463 |
+
The peer has misbehaved. You should close the connection
|
| 464 |
+
(possibly after sending some kind of 4xx response).
|
| 465 |
+
|
| 466 |
+
Once this method returns :class:`ConnectionClosed` once, then all
|
| 467 |
+
subsequent calls will also return :class:`ConnectionClosed`.
|
| 468 |
+
|
| 469 |
+
If this method raises any exception besides :exc:`RemoteProtocolError`
|
| 470 |
+
then that's a bug -- if it happens please file a bug report!
|
| 471 |
+
|
| 472 |
+
If this method raises any exception then it also sets
|
| 473 |
+
:attr:`Connection.their_state` to :data:`ERROR` -- see
|
| 474 |
+
:ref:`error-handling` for discussion.
|
| 475 |
+
|
| 476 |
+
"""
|
| 477 |
+
|
| 478 |
+
if self.their_state is ERROR:
|
| 479 |
+
raise RemoteProtocolError("Can't receive data when peer state is ERROR")
|
| 480 |
+
try:
|
| 481 |
+
event = self._extract_next_receive_event()
|
| 482 |
+
if event not in [NEED_DATA, PAUSED]:
|
| 483 |
+
self._process_event(self.their_role, cast(Event, event))
|
| 484 |
+
if event is NEED_DATA:
|
| 485 |
+
if len(self._receive_buffer) > self._max_incomplete_event_size:
|
| 486 |
+
# 431 is "Request header fields too large" which is pretty
|
| 487 |
+
# much the only situation where we can get here
|
| 488 |
+
raise RemoteProtocolError(
|
| 489 |
+
"Receive buffer too long", error_status_hint=431
|
| 490 |
+
)
|
| 491 |
+
if self._receive_buffer_closed:
|
| 492 |
+
# We're still trying to complete some event, but that's
|
| 493 |
+
# never going to happen because no more data is coming
|
| 494 |
+
raise RemoteProtocolError("peer unexpectedly closed connection")
|
| 495 |
+
return event
|
| 496 |
+
except BaseException as exc:
|
| 497 |
+
self._process_error(self.their_role)
|
| 498 |
+
if isinstance(exc, LocalProtocolError):
|
| 499 |
+
exc._reraise_as_remote_protocol_error()
|
| 500 |
+
else:
|
| 501 |
+
raise
|
| 502 |
+
|
| 503 |
+
@overload
|
| 504 |
+
def send(self, event: ConnectionClosed) -> None:
|
| 505 |
+
...
|
| 506 |
+
|
| 507 |
+
@overload
|
| 508 |
+
def send(
|
| 509 |
+
self, event: Union[Request, InformationalResponse, Response, Data, EndOfMessage]
|
| 510 |
+
) -> bytes:
|
| 511 |
+
...
|
| 512 |
+
|
| 513 |
+
@overload
|
| 514 |
+
def send(self, event: Event) -> Optional[bytes]:
|
| 515 |
+
...
|
| 516 |
+
|
| 517 |
+
def send(self, event: Event) -> Optional[bytes]:
|
| 518 |
+
"""Convert a high-level event into bytes that can be sent to the peer,
|
| 519 |
+
while updating our internal state machine.
|
| 520 |
+
|
| 521 |
+
Args:
|
| 522 |
+
event: The :ref:`event <events>` to send.
|
| 523 |
+
|
| 524 |
+
Returns:
|
| 525 |
+
If ``type(event) is ConnectionClosed``, then returns
|
| 526 |
+
``None``. Otherwise, returns a :term:`bytes-like object`.
|
| 527 |
+
|
| 528 |
+
Raises:
|
| 529 |
+
LocalProtocolError:
|
| 530 |
+
Sending this event at this time would violate our
|
| 531 |
+
understanding of the HTTP/1.1 protocol.
|
| 532 |
+
|
| 533 |
+
If this method raises any exception then it also sets
|
| 534 |
+
:attr:`Connection.our_state` to :data:`ERROR` -- see
|
| 535 |
+
:ref:`error-handling` for discussion.
|
| 536 |
+
|
| 537 |
+
"""
|
| 538 |
+
data_list = self.send_with_data_passthrough(event)
|
| 539 |
+
if data_list is None:
|
| 540 |
+
return None
|
| 541 |
+
else:
|
| 542 |
+
return b"".join(data_list)
|
| 543 |
+
|
| 544 |
+
def send_with_data_passthrough(self, event: Event) -> Optional[List[bytes]]:
|
| 545 |
+
"""Identical to :meth:`send`, except that in situations where
|
| 546 |
+
:meth:`send` returns a single :term:`bytes-like object`, this instead
|
| 547 |
+
returns a list of them -- and when sending a :class:`Data` event, this
|
| 548 |
+
list is guaranteed to contain the exact object you passed in as
|
| 549 |
+
:attr:`Data.data`. See :ref:`sendfile` for discussion.
|
| 550 |
+
|
| 551 |
+
"""
|
| 552 |
+
if self.our_state is ERROR:
|
| 553 |
+
raise LocalProtocolError("Can't send data when our state is ERROR")
|
| 554 |
+
try:
|
| 555 |
+
if type(event) is Response:
|
| 556 |
+
event = self._clean_up_response_headers_for_sending(event)
|
| 557 |
+
# We want to call _process_event before calling the writer,
|
| 558 |
+
# because if someone tries to do something invalid then this will
|
| 559 |
+
# give a sensible error message, while our writers all just assume
|
| 560 |
+
# they will only receive valid events. But, _process_event might
|
| 561 |
+
# change self._writer. So we have to do a little dance:
|
| 562 |
+
writer = self._writer
|
| 563 |
+
self._process_event(self.our_role, event)
|
| 564 |
+
if type(event) is ConnectionClosed:
|
| 565 |
+
return None
|
| 566 |
+
else:
|
| 567 |
+
# In any situation where writer is None, process_event should
|
| 568 |
+
# have raised ProtocolError
|
| 569 |
+
assert writer is not None
|
| 570 |
+
data_list: List[bytes] = []
|
| 571 |
+
writer(event, data_list.append)
|
| 572 |
+
return data_list
|
| 573 |
+
except:
|
| 574 |
+
self._process_error(self.our_role)
|
| 575 |
+
raise
|
| 576 |
+
|
| 577 |
+
def send_failed(self) -> None:
|
| 578 |
+
"""Notify the state machine that we failed to send the data it gave
|
| 579 |
+
us.
|
| 580 |
+
|
| 581 |
+
This causes :attr:`Connection.our_state` to immediately become
|
| 582 |
+
:data:`ERROR` -- see :ref:`error-handling` for discussion.
|
| 583 |
+
|
| 584 |
+
"""
|
| 585 |
+
self._process_error(self.our_role)
|
| 586 |
+
|
| 587 |
+
# When sending a Response, we take responsibility for a few things:
|
| 588 |
+
#
|
| 589 |
+
# - Sometimes you MUST set Connection: close. We take care of those
|
| 590 |
+
# times. (You can also set it yourself if you want, and if you do then
|
| 591 |
+
# we'll respect that and close the connection at the right time. But you
|
| 592 |
+
# don't have to worry about that unless you want to.)
|
| 593 |
+
#
|
| 594 |
+
# - The user has to set Content-Length if they want it. Otherwise, for
|
| 595 |
+
# responses that have bodies (e.g. not HEAD), then we will automatically
|
| 596 |
+
# select the right mechanism for streaming a body of unknown length,
|
| 597 |
+
# which depends on depending on the peer's HTTP version.
|
| 598 |
+
#
|
| 599 |
+
# This function's *only* responsibility is making sure headers are set up
|
| 600 |
+
# right -- everything downstream just looks at the headers. There are no
|
| 601 |
+
# side channels.
|
| 602 |
+
def _clean_up_response_headers_for_sending(self, response: Response) -> Response:
|
| 603 |
+
assert type(response) is Response
|
| 604 |
+
|
| 605 |
+
headers = response.headers
|
| 606 |
+
need_close = False
|
| 607 |
+
|
| 608 |
+
# HEAD requests need some special handling: they always act like they
|
| 609 |
+
# have Content-Length: 0, and that's how _body_framing treats
|
| 610 |
+
# them. But their headers are supposed to match what we would send if
|
| 611 |
+
# the request was a GET. (Technically there is one deviation allowed:
|
| 612 |
+
# we're allowed to leave out the framing headers -- see
|
| 613 |
+
# https://tools.ietf.org/html/rfc7231#section-4.3.2 . But it's just as
|
| 614 |
+
# easy to get them right.)
|
| 615 |
+
method_for_choosing_headers = cast(bytes, self._request_method)
|
| 616 |
+
if method_for_choosing_headers == b"HEAD":
|
| 617 |
+
method_for_choosing_headers = b"GET"
|
| 618 |
+
framing_type, _ = _body_framing(method_for_choosing_headers, response)
|
| 619 |
+
if framing_type in ("chunked", "http/1.0"):
|
| 620 |
+
# This response has a body of unknown length.
|
| 621 |
+
# If our peer is HTTP/1.1, we use Transfer-Encoding: chunked
|
| 622 |
+
# If our peer is HTTP/1.0, we use no framing headers, and close the
|
| 623 |
+
# connection afterwards.
|
| 624 |
+
#
|
| 625 |
+
# Make sure to clear Content-Length (in principle user could have
|
| 626 |
+
# set both and then we ignored Content-Length b/c
|
| 627 |
+
# Transfer-Encoding overwrote it -- this would be naughty of them,
|
| 628 |
+
# but the HTTP spec says that if our peer does this then we have
|
| 629 |
+
# to fix it instead of erroring out, so we'll accord the user the
|
| 630 |
+
# same respect).
|
| 631 |
+
headers = set_comma_header(headers, b"content-length", [])
|
| 632 |
+
if self.their_http_version is None or self.their_http_version < b"1.1":
|
| 633 |
+
# Either we never got a valid request and are sending back an
|
| 634 |
+
# error (their_http_version is None), so we assume the worst;
|
| 635 |
+
# or else we did get a valid HTTP/1.0 request, so we know that
|
| 636 |
+
# they don't understand chunked encoding.
|
| 637 |
+
headers = set_comma_header(headers, b"transfer-encoding", [])
|
| 638 |
+
# This is actually redundant ATM, since currently we
|
| 639 |
+
# unconditionally disable keep-alive when talking to HTTP/1.0
|
| 640 |
+
# peers. But let's be defensive just in case we add
|
| 641 |
+
# Connection: keep-alive support later:
|
| 642 |
+
if self._request_method != b"HEAD":
|
| 643 |
+
need_close = True
|
| 644 |
+
else:
|
| 645 |
+
headers = set_comma_header(headers, b"transfer-encoding", [b"chunked"])
|
| 646 |
+
|
| 647 |
+
if not self._cstate.keep_alive or need_close:
|
| 648 |
+
# Make sure Connection: close is set
|
| 649 |
+
connection = set(get_comma_header(headers, b"connection"))
|
| 650 |
+
connection.discard(b"keep-alive")
|
| 651 |
+
connection.add(b"close")
|
| 652 |
+
headers = set_comma_header(headers, b"connection", sorted(connection))
|
| 653 |
+
|
| 654 |
+
return Response(
|
| 655 |
+
headers=headers,
|
| 656 |
+
status_code=response.status_code,
|
| 657 |
+
http_version=response.http_version,
|
| 658 |
+
reason=response.reason,
|
| 659 |
+
)
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_events.py
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# High level events that make up HTTP/1.1 conversations. Loosely inspired by
|
| 2 |
+
# the corresponding events in hyper-h2:
|
| 3 |
+
#
|
| 4 |
+
# http://python-hyper.org/h2/en/stable/api.html#events
|
| 5 |
+
#
|
| 6 |
+
# Don't subclass these. Stuff will break.
|
| 7 |
+
|
| 8 |
+
import re
|
| 9 |
+
from abc import ABC
|
| 10 |
+
from dataclasses import dataclass
|
| 11 |
+
from typing import List, Tuple, Union
|
| 12 |
+
|
| 13 |
+
from ._abnf import method, request_target
|
| 14 |
+
from ._headers import Headers, normalize_and_validate
|
| 15 |
+
from ._util import bytesify, LocalProtocolError, validate
|
| 16 |
+
|
| 17 |
+
# Everything in __all__ gets re-exported as part of the h11 public API.
|
| 18 |
+
__all__ = [
|
| 19 |
+
"Event",
|
| 20 |
+
"Request",
|
| 21 |
+
"InformationalResponse",
|
| 22 |
+
"Response",
|
| 23 |
+
"Data",
|
| 24 |
+
"EndOfMessage",
|
| 25 |
+
"ConnectionClosed",
|
| 26 |
+
]
|
| 27 |
+
|
| 28 |
+
method_re = re.compile(method.encode("ascii"))
|
| 29 |
+
request_target_re = re.compile(request_target.encode("ascii"))
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class Event(ABC):
|
| 33 |
+
"""
|
| 34 |
+
Base class for h11 events.
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
__slots__ = ()
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
@dataclass(init=False, frozen=True)
|
| 41 |
+
class Request(Event):
|
| 42 |
+
"""The beginning of an HTTP request.
|
| 43 |
+
|
| 44 |
+
Fields:
|
| 45 |
+
|
| 46 |
+
.. attribute:: method
|
| 47 |
+
|
| 48 |
+
An HTTP method, e.g. ``b"GET"`` or ``b"POST"``. Always a byte
|
| 49 |
+
string. :term:`Bytes-like objects <bytes-like object>` and native
|
| 50 |
+
strings containing only ascii characters will be automatically
|
| 51 |
+
converted to byte strings.
|
| 52 |
+
|
| 53 |
+
.. attribute:: target
|
| 54 |
+
|
| 55 |
+
The target of an HTTP request, e.g. ``b"/index.html"``, or one of the
|
| 56 |
+
more exotic formats described in `RFC 7320, section 5.3
|
| 57 |
+
<https://tools.ietf.org/html/rfc7230#section-5.3>`_. Always a byte
|
| 58 |
+
string. :term:`Bytes-like objects <bytes-like object>` and native
|
| 59 |
+
strings containing only ascii characters will be automatically
|
| 60 |
+
converted to byte strings.
|
| 61 |
+
|
| 62 |
+
.. attribute:: headers
|
| 63 |
+
|
| 64 |
+
Request headers, represented as a list of (name, value) pairs. See
|
| 65 |
+
:ref:`the header normalization rules <headers-format>` for details.
|
| 66 |
+
|
| 67 |
+
.. attribute:: http_version
|
| 68 |
+
|
| 69 |
+
The HTTP protocol version, represented as a byte string like
|
| 70 |
+
``b"1.1"``. See :ref:`the HTTP version normalization rules
|
| 71 |
+
<http_version-format>` for details.
|
| 72 |
+
|
| 73 |
+
"""
|
| 74 |
+
|
| 75 |
+
__slots__ = ("method", "headers", "target", "http_version")
|
| 76 |
+
|
| 77 |
+
method: bytes
|
| 78 |
+
headers: Headers
|
| 79 |
+
target: bytes
|
| 80 |
+
http_version: bytes
|
| 81 |
+
|
| 82 |
+
def __init__(
|
| 83 |
+
self,
|
| 84 |
+
*,
|
| 85 |
+
method: Union[bytes, str],
|
| 86 |
+
headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]],
|
| 87 |
+
target: Union[bytes, str],
|
| 88 |
+
http_version: Union[bytes, str] = b"1.1",
|
| 89 |
+
_parsed: bool = False,
|
| 90 |
+
) -> None:
|
| 91 |
+
super().__init__()
|
| 92 |
+
if isinstance(headers, Headers):
|
| 93 |
+
object.__setattr__(self, "headers", headers)
|
| 94 |
+
else:
|
| 95 |
+
object.__setattr__(
|
| 96 |
+
self, "headers", normalize_and_validate(headers, _parsed=_parsed)
|
| 97 |
+
)
|
| 98 |
+
if not _parsed:
|
| 99 |
+
object.__setattr__(self, "method", bytesify(method))
|
| 100 |
+
object.__setattr__(self, "target", bytesify(target))
|
| 101 |
+
object.__setattr__(self, "http_version", bytesify(http_version))
|
| 102 |
+
else:
|
| 103 |
+
object.__setattr__(self, "method", method)
|
| 104 |
+
object.__setattr__(self, "target", target)
|
| 105 |
+
object.__setattr__(self, "http_version", http_version)
|
| 106 |
+
|
| 107 |
+
# "A server MUST respond with a 400 (Bad Request) status code to any
|
| 108 |
+
# HTTP/1.1 request message that lacks a Host header field and to any
|
| 109 |
+
# request message that contains more than one Host header field or a
|
| 110 |
+
# Host header field with an invalid field-value."
|
| 111 |
+
# -- https://tools.ietf.org/html/rfc7230#section-5.4
|
| 112 |
+
host_count = 0
|
| 113 |
+
for name, value in self.headers:
|
| 114 |
+
if name == b"host":
|
| 115 |
+
host_count += 1
|
| 116 |
+
if self.http_version == b"1.1" and host_count == 0:
|
| 117 |
+
raise LocalProtocolError("Missing mandatory Host: header")
|
| 118 |
+
if host_count > 1:
|
| 119 |
+
raise LocalProtocolError("Found multiple Host: headers")
|
| 120 |
+
|
| 121 |
+
validate(method_re, self.method, "Illegal method characters")
|
| 122 |
+
validate(request_target_re, self.target, "Illegal target characters")
|
| 123 |
+
|
| 124 |
+
# This is an unhashable type.
|
| 125 |
+
__hash__ = None # type: ignore
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
@dataclass(init=False, frozen=True)
|
| 129 |
+
class _ResponseBase(Event):
|
| 130 |
+
__slots__ = ("headers", "http_version", "reason", "status_code")
|
| 131 |
+
|
| 132 |
+
headers: Headers
|
| 133 |
+
http_version: bytes
|
| 134 |
+
reason: bytes
|
| 135 |
+
status_code: int
|
| 136 |
+
|
| 137 |
+
def __init__(
|
| 138 |
+
self,
|
| 139 |
+
*,
|
| 140 |
+
headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]],
|
| 141 |
+
status_code: int,
|
| 142 |
+
http_version: Union[bytes, str] = b"1.1",
|
| 143 |
+
reason: Union[bytes, str] = b"",
|
| 144 |
+
_parsed: bool = False,
|
| 145 |
+
) -> None:
|
| 146 |
+
super().__init__()
|
| 147 |
+
if isinstance(headers, Headers):
|
| 148 |
+
object.__setattr__(self, "headers", headers)
|
| 149 |
+
else:
|
| 150 |
+
object.__setattr__(
|
| 151 |
+
self, "headers", normalize_and_validate(headers, _parsed=_parsed)
|
| 152 |
+
)
|
| 153 |
+
if not _parsed:
|
| 154 |
+
object.__setattr__(self, "reason", bytesify(reason))
|
| 155 |
+
object.__setattr__(self, "http_version", bytesify(http_version))
|
| 156 |
+
if not isinstance(status_code, int):
|
| 157 |
+
raise LocalProtocolError("status code must be integer")
|
| 158 |
+
# Because IntEnum objects are instances of int, but aren't
|
| 159 |
+
# duck-compatible (sigh), see gh-72.
|
| 160 |
+
object.__setattr__(self, "status_code", int(status_code))
|
| 161 |
+
else:
|
| 162 |
+
object.__setattr__(self, "reason", reason)
|
| 163 |
+
object.__setattr__(self, "http_version", http_version)
|
| 164 |
+
object.__setattr__(self, "status_code", status_code)
|
| 165 |
+
|
| 166 |
+
self.__post_init__()
|
| 167 |
+
|
| 168 |
+
def __post_init__(self) -> None:
|
| 169 |
+
pass
|
| 170 |
+
|
| 171 |
+
# This is an unhashable type.
|
| 172 |
+
__hash__ = None # type: ignore
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
@dataclass(init=False, frozen=True)
|
| 176 |
+
class InformationalResponse(_ResponseBase):
|
| 177 |
+
"""An HTTP informational response.
|
| 178 |
+
|
| 179 |
+
Fields:
|
| 180 |
+
|
| 181 |
+
.. attribute:: status_code
|
| 182 |
+
|
| 183 |
+
The status code of this response, as an integer. For an
|
| 184 |
+
:class:`InformationalResponse`, this is always in the range [100,
|
| 185 |
+
200).
|
| 186 |
+
|
| 187 |
+
.. attribute:: headers
|
| 188 |
+
|
| 189 |
+
Request headers, represented as a list of (name, value) pairs. See
|
| 190 |
+
:ref:`the header normalization rules <headers-format>` for
|
| 191 |
+
details.
|
| 192 |
+
|
| 193 |
+
.. attribute:: http_version
|
| 194 |
+
|
| 195 |
+
The HTTP protocol version, represented as a byte string like
|
| 196 |
+
``b"1.1"``. See :ref:`the HTTP version normalization rules
|
| 197 |
+
<http_version-format>` for details.
|
| 198 |
+
|
| 199 |
+
.. attribute:: reason
|
| 200 |
+
|
| 201 |
+
The reason phrase of this response, as a byte string. For example:
|
| 202 |
+
``b"OK"``, or ``b"Not Found"``.
|
| 203 |
+
|
| 204 |
+
"""
|
| 205 |
+
|
| 206 |
+
def __post_init__(self) -> None:
|
| 207 |
+
if not (100 <= self.status_code < 200):
|
| 208 |
+
raise LocalProtocolError(
|
| 209 |
+
"InformationalResponse status_code should be in range "
|
| 210 |
+
"[100, 200), not {}".format(self.status_code)
|
| 211 |
+
)
|
| 212 |
+
|
| 213 |
+
# This is an unhashable type.
|
| 214 |
+
__hash__ = None # type: ignore
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
@dataclass(init=False, frozen=True)
|
| 218 |
+
class Response(_ResponseBase):
|
| 219 |
+
"""The beginning of an HTTP response.
|
| 220 |
+
|
| 221 |
+
Fields:
|
| 222 |
+
|
| 223 |
+
.. attribute:: status_code
|
| 224 |
+
|
| 225 |
+
The status code of this response, as an integer. For an
|
| 226 |
+
:class:`Response`, this is always in the range [200,
|
| 227 |
+
1000).
|
| 228 |
+
|
| 229 |
+
.. attribute:: headers
|
| 230 |
+
|
| 231 |
+
Request headers, represented as a list of (name, value) pairs. See
|
| 232 |
+
:ref:`the header normalization rules <headers-format>` for details.
|
| 233 |
+
|
| 234 |
+
.. attribute:: http_version
|
| 235 |
+
|
| 236 |
+
The HTTP protocol version, represented as a byte string like
|
| 237 |
+
``b"1.1"``. See :ref:`the HTTP version normalization rules
|
| 238 |
+
<http_version-format>` for details.
|
| 239 |
+
|
| 240 |
+
.. attribute:: reason
|
| 241 |
+
|
| 242 |
+
The reason phrase of this response, as a byte string. For example:
|
| 243 |
+
``b"OK"``, or ``b"Not Found"``.
|
| 244 |
+
|
| 245 |
+
"""
|
| 246 |
+
|
| 247 |
+
def __post_init__(self) -> None:
|
| 248 |
+
if not (200 <= self.status_code < 1000):
|
| 249 |
+
raise LocalProtocolError(
|
| 250 |
+
"Response status_code should be in range [200, 1000), not {}".format(
|
| 251 |
+
self.status_code
|
| 252 |
+
)
|
| 253 |
+
)
|
| 254 |
+
|
| 255 |
+
# This is an unhashable type.
|
| 256 |
+
__hash__ = None # type: ignore
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
@dataclass(init=False, frozen=True)
|
| 260 |
+
class Data(Event):
|
| 261 |
+
"""Part of an HTTP message body.
|
| 262 |
+
|
| 263 |
+
Fields:
|
| 264 |
+
|
| 265 |
+
.. attribute:: data
|
| 266 |
+
|
| 267 |
+
A :term:`bytes-like object` containing part of a message body. Or, if
|
| 268 |
+
using the ``combine=False`` argument to :meth:`Connection.send`, then
|
| 269 |
+
any object that your socket writing code knows what to do with, and for
|
| 270 |
+
which calling :func:`len` returns the number of bytes that will be
|
| 271 |
+
written -- see :ref:`sendfile` for details.
|
| 272 |
+
|
| 273 |
+
.. attribute:: chunk_start
|
| 274 |
+
|
| 275 |
+
A marker that indicates whether this data object is from the start of a
|
| 276 |
+
chunked transfer encoding chunk. This field is ignored when when a Data
|
| 277 |
+
event is provided to :meth:`Connection.send`: it is only valid on
|
| 278 |
+
events emitted from :meth:`Connection.next_event`. You probably
|
| 279 |
+
shouldn't use this attribute at all; see
|
| 280 |
+
:ref:`chunk-delimiters-are-bad` for details.
|
| 281 |
+
|
| 282 |
+
.. attribute:: chunk_end
|
| 283 |
+
|
| 284 |
+
A marker that indicates whether this data object is the last for a
|
| 285 |
+
given chunked transfer encoding chunk. This field is ignored when when
|
| 286 |
+
a Data event is provided to :meth:`Connection.send`: it is only valid
|
| 287 |
+
on events emitted from :meth:`Connection.next_event`. You probably
|
| 288 |
+
shouldn't use this attribute at all; see
|
| 289 |
+
:ref:`chunk-delimiters-are-bad` for details.
|
| 290 |
+
|
| 291 |
+
"""
|
| 292 |
+
|
| 293 |
+
__slots__ = ("data", "chunk_start", "chunk_end")
|
| 294 |
+
|
| 295 |
+
data: bytes
|
| 296 |
+
chunk_start: bool
|
| 297 |
+
chunk_end: bool
|
| 298 |
+
|
| 299 |
+
def __init__(
|
| 300 |
+
self, data: bytes, chunk_start: bool = False, chunk_end: bool = False
|
| 301 |
+
) -> None:
|
| 302 |
+
object.__setattr__(self, "data", data)
|
| 303 |
+
object.__setattr__(self, "chunk_start", chunk_start)
|
| 304 |
+
object.__setattr__(self, "chunk_end", chunk_end)
|
| 305 |
+
|
| 306 |
+
# This is an unhashable type.
|
| 307 |
+
__hash__ = None # type: ignore
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
# XX FIXME: "A recipient MUST ignore (or consider as an error) any fields that
|
| 311 |
+
# are forbidden to be sent in a trailer, since processing them as if they were
|
| 312 |
+
# present in the header section might bypass external security filters."
|
| 313 |
+
# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#chunked.trailer.part
|
| 314 |
+
# Unfortunately, the list of forbidden fields is long and vague :-/
|
| 315 |
+
@dataclass(init=False, frozen=True)
|
| 316 |
+
class EndOfMessage(Event):
|
| 317 |
+
"""The end of an HTTP message.
|
| 318 |
+
|
| 319 |
+
Fields:
|
| 320 |
+
|
| 321 |
+
.. attribute:: headers
|
| 322 |
+
|
| 323 |
+
Default value: ``[]``
|
| 324 |
+
|
| 325 |
+
Any trailing headers attached to this message, represented as a list of
|
| 326 |
+
(name, value) pairs. See :ref:`the header normalization rules
|
| 327 |
+
<headers-format>` for details.
|
| 328 |
+
|
| 329 |
+
Must be empty unless ``Transfer-Encoding: chunked`` is in use.
|
| 330 |
+
|
| 331 |
+
"""
|
| 332 |
+
|
| 333 |
+
__slots__ = ("headers",)
|
| 334 |
+
|
| 335 |
+
headers: Headers
|
| 336 |
+
|
| 337 |
+
def __init__(
|
| 338 |
+
self,
|
| 339 |
+
*,
|
| 340 |
+
headers: Union[
|
| 341 |
+
Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]], None
|
| 342 |
+
] = None,
|
| 343 |
+
_parsed: bool = False,
|
| 344 |
+
) -> None:
|
| 345 |
+
super().__init__()
|
| 346 |
+
if headers is None:
|
| 347 |
+
headers = Headers([])
|
| 348 |
+
elif not isinstance(headers, Headers):
|
| 349 |
+
headers = normalize_and_validate(headers, _parsed=_parsed)
|
| 350 |
+
|
| 351 |
+
object.__setattr__(self, "headers", headers)
|
| 352 |
+
|
| 353 |
+
# This is an unhashable type.
|
| 354 |
+
__hash__ = None # type: ignore
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
@dataclass(frozen=True)
|
| 358 |
+
class ConnectionClosed(Event):
|
| 359 |
+
"""This event indicates that the sender has closed their outgoing
|
| 360 |
+
connection.
|
| 361 |
+
|
| 362 |
+
Note that this does not necessarily mean that they can't *receive* further
|
| 363 |
+
data, because TCP connections are composed to two one-way channels which
|
| 364 |
+
can be closed independently. See :ref:`closing` for details.
|
| 365 |
+
|
| 366 |
+
No fields.
|
| 367 |
+
"""
|
| 368 |
+
|
| 369 |
+
pass
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_headers.py
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
from typing import AnyStr, cast, List, overload, Sequence, Tuple, TYPE_CHECKING, Union
|
| 3 |
+
|
| 4 |
+
from ._abnf import field_name, field_value
|
| 5 |
+
from ._util import bytesify, LocalProtocolError, validate
|
| 6 |
+
|
| 7 |
+
if TYPE_CHECKING:
|
| 8 |
+
from ._events import Request
|
| 9 |
+
|
| 10 |
+
try:
|
| 11 |
+
from typing import Literal
|
| 12 |
+
except ImportError:
|
| 13 |
+
from typing_extensions import Literal # type: ignore
|
| 14 |
+
|
| 15 |
+
CONTENT_LENGTH_MAX_DIGITS = 20 # allow up to 1 billion TB - 1
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
# Facts
|
| 19 |
+
# -----
|
| 20 |
+
#
|
| 21 |
+
# Headers are:
|
| 22 |
+
# keys: case-insensitive ascii
|
| 23 |
+
# values: mixture of ascii and raw bytes
|
| 24 |
+
#
|
| 25 |
+
# "Historically, HTTP has allowed field content with text in the ISO-8859-1
|
| 26 |
+
# charset [ISO-8859-1], supporting other charsets only through use of
|
| 27 |
+
# [RFC2047] encoding. In practice, most HTTP header field values use only a
|
| 28 |
+
# subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD
|
| 29 |
+
# limit their field values to US-ASCII octets. A recipient SHOULD treat other
|
| 30 |
+
# octets in field content (obs-text) as opaque data."
|
| 31 |
+
# And it deprecates all non-ascii values
|
| 32 |
+
#
|
| 33 |
+
# Leading/trailing whitespace in header names is forbidden
|
| 34 |
+
#
|
| 35 |
+
# Values get leading/trailing whitespace stripped
|
| 36 |
+
#
|
| 37 |
+
# Content-Disposition actually needs to contain unicode semantically; to
|
| 38 |
+
# accomplish this it has a terrifically weird way of encoding the filename
|
| 39 |
+
# itself as ascii (and even this still has lots of cross-browser
|
| 40 |
+
# incompatibilities)
|
| 41 |
+
#
|
| 42 |
+
# Order is important:
|
| 43 |
+
# "a proxy MUST NOT change the order of these field values when forwarding a
|
| 44 |
+
# message"
|
| 45 |
+
# (and there are several headers where the order indicates a preference)
|
| 46 |
+
#
|
| 47 |
+
# Multiple occurences of the same header:
|
| 48 |
+
# "A sender MUST NOT generate multiple header fields with the same field name
|
| 49 |
+
# in a message unless either the entire field value for that header field is
|
| 50 |
+
# defined as a comma-separated list [or the header is Set-Cookie which gets a
|
| 51 |
+
# special exception]" - RFC 7230. (cookies are in RFC 6265)
|
| 52 |
+
#
|
| 53 |
+
# So every header aside from Set-Cookie can be merged by b", ".join if it
|
| 54 |
+
# occurs repeatedly. But, of course, they can't necessarily be split by
|
| 55 |
+
# .split(b","), because quoting.
|
| 56 |
+
#
|
| 57 |
+
# Given all this mess (case insensitive, duplicates allowed, order is
|
| 58 |
+
# important, ...), there doesn't appear to be any standard way to handle
|
| 59 |
+
# headers in Python -- they're almost like dicts, but... actually just
|
| 60 |
+
# aren't. For now we punt and just use a super simple representation: headers
|
| 61 |
+
# are a list of pairs
|
| 62 |
+
#
|
| 63 |
+
# [(name1, value1), (name2, value2), ...]
|
| 64 |
+
#
|
| 65 |
+
# where all entries are bytestrings, names are lowercase and have no
|
| 66 |
+
# leading/trailing whitespace, and values are bytestrings with no
|
| 67 |
+
# leading/trailing whitespace. Searching and updating are done via naive O(n)
|
| 68 |
+
# methods.
|
| 69 |
+
#
|
| 70 |
+
# Maybe a dict-of-lists would be better?
|
| 71 |
+
|
| 72 |
+
_content_length_re = re.compile(rb"[0-9]+")
|
| 73 |
+
_field_name_re = re.compile(field_name.encode("ascii"))
|
| 74 |
+
_field_value_re = re.compile(field_value.encode("ascii"))
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
class Headers(Sequence[Tuple[bytes, bytes]]):
|
| 78 |
+
"""
|
| 79 |
+
A list-like interface that allows iterating over headers as byte-pairs
|
| 80 |
+
of (lowercased-name, value).
|
| 81 |
+
|
| 82 |
+
Internally we actually store the representation as three-tuples,
|
| 83 |
+
including both the raw original casing, in order to preserve casing
|
| 84 |
+
over-the-wire, and the lowercased name, for case-insensitive comparisions.
|
| 85 |
+
|
| 86 |
+
r = Request(
|
| 87 |
+
method="GET",
|
| 88 |
+
target="/",
|
| 89 |
+
headers=[("Host", "example.org"), ("Connection", "keep-alive")],
|
| 90 |
+
http_version="1.1",
|
| 91 |
+
)
|
| 92 |
+
assert r.headers == [
|
| 93 |
+
(b"host", b"example.org"),
|
| 94 |
+
(b"connection", b"keep-alive")
|
| 95 |
+
]
|
| 96 |
+
assert r.headers.raw_items() == [
|
| 97 |
+
(b"Host", b"example.org"),
|
| 98 |
+
(b"Connection", b"keep-alive")
|
| 99 |
+
]
|
| 100 |
+
"""
|
| 101 |
+
|
| 102 |
+
__slots__ = "_full_items"
|
| 103 |
+
|
| 104 |
+
def __init__(self, full_items: List[Tuple[bytes, bytes, bytes]]) -> None:
|
| 105 |
+
self._full_items = full_items
|
| 106 |
+
|
| 107 |
+
def __bool__(self) -> bool:
|
| 108 |
+
return bool(self._full_items)
|
| 109 |
+
|
| 110 |
+
def __eq__(self, other: object) -> bool:
|
| 111 |
+
return list(self) == list(other) # type: ignore
|
| 112 |
+
|
| 113 |
+
def __len__(self) -> int:
|
| 114 |
+
return len(self._full_items)
|
| 115 |
+
|
| 116 |
+
def __repr__(self) -> str:
|
| 117 |
+
return "<Headers(%s)>" % repr(list(self))
|
| 118 |
+
|
| 119 |
+
def __getitem__(self, idx: int) -> Tuple[bytes, bytes]: # type: ignore[override]
|
| 120 |
+
_, name, value = self._full_items[idx]
|
| 121 |
+
return (name, value)
|
| 122 |
+
|
| 123 |
+
def raw_items(self) -> List[Tuple[bytes, bytes]]:
|
| 124 |
+
return [(raw_name, value) for raw_name, _, value in self._full_items]
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
HeaderTypes = Union[
|
| 128 |
+
List[Tuple[bytes, bytes]],
|
| 129 |
+
List[Tuple[bytes, str]],
|
| 130 |
+
List[Tuple[str, bytes]],
|
| 131 |
+
List[Tuple[str, str]],
|
| 132 |
+
]
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
@overload
|
| 136 |
+
def normalize_and_validate(headers: Headers, _parsed: Literal[True]) -> Headers:
|
| 137 |
+
...
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
@overload
|
| 141 |
+
def normalize_and_validate(headers: HeaderTypes, _parsed: Literal[False]) -> Headers:
|
| 142 |
+
...
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
@overload
|
| 146 |
+
def normalize_and_validate(
|
| 147 |
+
headers: Union[Headers, HeaderTypes], _parsed: bool = False
|
| 148 |
+
) -> Headers:
|
| 149 |
+
...
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def normalize_and_validate(
|
| 153 |
+
headers: Union[Headers, HeaderTypes], _parsed: bool = False
|
| 154 |
+
) -> Headers:
|
| 155 |
+
new_headers = []
|
| 156 |
+
seen_content_length = None
|
| 157 |
+
saw_transfer_encoding = False
|
| 158 |
+
for name, value in headers:
|
| 159 |
+
# For headers coming out of the parser, we can safely skip some steps,
|
| 160 |
+
# because it always returns bytes and has already run these regexes
|
| 161 |
+
# over the data:
|
| 162 |
+
if not _parsed:
|
| 163 |
+
name = bytesify(name)
|
| 164 |
+
value = bytesify(value)
|
| 165 |
+
validate(_field_name_re, name, "Illegal header name {!r}", name)
|
| 166 |
+
validate(_field_value_re, value, "Illegal header value {!r}", value)
|
| 167 |
+
assert isinstance(name, bytes)
|
| 168 |
+
assert isinstance(value, bytes)
|
| 169 |
+
|
| 170 |
+
raw_name = name
|
| 171 |
+
name = name.lower()
|
| 172 |
+
if name == b"content-length":
|
| 173 |
+
lengths = {length.strip() for length in value.split(b",")}
|
| 174 |
+
if len(lengths) != 1:
|
| 175 |
+
raise LocalProtocolError("conflicting Content-Length headers")
|
| 176 |
+
value = lengths.pop()
|
| 177 |
+
validate(_content_length_re, value, "bad Content-Length")
|
| 178 |
+
if len(value) > CONTENT_LENGTH_MAX_DIGITS:
|
| 179 |
+
raise LocalProtocolError("bad Content-Length")
|
| 180 |
+
if seen_content_length is None:
|
| 181 |
+
seen_content_length = value
|
| 182 |
+
new_headers.append((raw_name, name, value))
|
| 183 |
+
elif seen_content_length != value:
|
| 184 |
+
raise LocalProtocolError("conflicting Content-Length headers")
|
| 185 |
+
elif name == b"transfer-encoding":
|
| 186 |
+
# "A server that receives a request message with a transfer coding
|
| 187 |
+
# it does not understand SHOULD respond with 501 (Not
|
| 188 |
+
# Implemented)."
|
| 189 |
+
# https://tools.ietf.org/html/rfc7230#section-3.3.1
|
| 190 |
+
if saw_transfer_encoding:
|
| 191 |
+
raise LocalProtocolError(
|
| 192 |
+
"multiple Transfer-Encoding headers", error_status_hint=501
|
| 193 |
+
)
|
| 194 |
+
# "All transfer-coding names are case-insensitive"
|
| 195 |
+
# -- https://tools.ietf.org/html/rfc7230#section-4
|
| 196 |
+
value = value.lower()
|
| 197 |
+
if value != b"chunked":
|
| 198 |
+
raise LocalProtocolError(
|
| 199 |
+
"Only Transfer-Encoding: chunked is supported",
|
| 200 |
+
error_status_hint=501,
|
| 201 |
+
)
|
| 202 |
+
saw_transfer_encoding = True
|
| 203 |
+
new_headers.append((raw_name, name, value))
|
| 204 |
+
else:
|
| 205 |
+
new_headers.append((raw_name, name, value))
|
| 206 |
+
return Headers(new_headers)
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def get_comma_header(headers: Headers, name: bytes) -> List[bytes]:
|
| 210 |
+
# Should only be used for headers whose value is a list of
|
| 211 |
+
# comma-separated, case-insensitive values.
|
| 212 |
+
#
|
| 213 |
+
# The header name `name` is expected to be lower-case bytes.
|
| 214 |
+
#
|
| 215 |
+
# Connection: meets these criteria (including cast insensitivity).
|
| 216 |
+
#
|
| 217 |
+
# Content-Length: technically is just a single value (1*DIGIT), but the
|
| 218 |
+
# standard makes reference to implementations that do multiple values, and
|
| 219 |
+
# using this doesn't hurt. Ditto, case insensitivity doesn't things either
|
| 220 |
+
# way.
|
| 221 |
+
#
|
| 222 |
+
# Transfer-Encoding: is more complex (allows for quoted strings), so
|
| 223 |
+
# splitting on , is actually wrong. For example, this is legal:
|
| 224 |
+
#
|
| 225 |
+
# Transfer-Encoding: foo; options="1,2", chunked
|
| 226 |
+
#
|
| 227 |
+
# and should be parsed as
|
| 228 |
+
#
|
| 229 |
+
# foo; options="1,2"
|
| 230 |
+
# chunked
|
| 231 |
+
#
|
| 232 |
+
# but this naive function will parse it as
|
| 233 |
+
#
|
| 234 |
+
# foo; options="1
|
| 235 |
+
# 2"
|
| 236 |
+
# chunked
|
| 237 |
+
#
|
| 238 |
+
# However, this is okay because the only thing we are going to do with
|
| 239 |
+
# any Transfer-Encoding is reject ones that aren't just "chunked", so
|
| 240 |
+
# both of these will be treated the same anyway.
|
| 241 |
+
#
|
| 242 |
+
# Expect: the only legal value is the literal string
|
| 243 |
+
# "100-continue". Splitting on commas is harmless. Case insensitive.
|
| 244 |
+
#
|
| 245 |
+
out: List[bytes] = []
|
| 246 |
+
for _, found_name, found_raw_value in headers._full_items:
|
| 247 |
+
if found_name == name:
|
| 248 |
+
found_raw_value = found_raw_value.lower()
|
| 249 |
+
for found_split_value in found_raw_value.split(b","):
|
| 250 |
+
found_split_value = found_split_value.strip()
|
| 251 |
+
if found_split_value:
|
| 252 |
+
out.append(found_split_value)
|
| 253 |
+
return out
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def set_comma_header(headers: Headers, name: bytes, new_values: List[bytes]) -> Headers:
|
| 257 |
+
# The header name `name` is expected to be lower-case bytes.
|
| 258 |
+
#
|
| 259 |
+
# Note that when we store the header we use title casing for the header
|
| 260 |
+
# names, in order to match the conventional HTTP header style.
|
| 261 |
+
#
|
| 262 |
+
# Simply calling `.title()` is a blunt approach, but it's correct
|
| 263 |
+
# here given the cases where we're using `set_comma_header`...
|
| 264 |
+
#
|
| 265 |
+
# Connection, Content-Length, Transfer-Encoding.
|
| 266 |
+
new_headers: List[Tuple[bytes, bytes]] = []
|
| 267 |
+
for found_raw_name, found_name, found_raw_value in headers._full_items:
|
| 268 |
+
if found_name != name:
|
| 269 |
+
new_headers.append((found_raw_name, found_raw_value))
|
| 270 |
+
for new_value in new_values:
|
| 271 |
+
new_headers.append((name.title(), new_value))
|
| 272 |
+
return normalize_and_validate(new_headers)
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
def has_expect_100_continue(request: "Request") -> bool:
|
| 276 |
+
# https://tools.ietf.org/html/rfc7231#section-5.1.1
|
| 277 |
+
# "A server that receives a 100-continue expectation in an HTTP/1.0 request
|
| 278 |
+
# MUST ignore that expectation."
|
| 279 |
+
if request.http_version < b"1.1":
|
| 280 |
+
return False
|
| 281 |
+
expect = get_comma_header(request.headers, b"expect")
|
| 282 |
+
return b"100-continue" in expect
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_readers.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Code to read HTTP data
|
| 2 |
+
#
|
| 3 |
+
# Strategy: each reader is a callable which takes a ReceiveBuffer object, and
|
| 4 |
+
# either:
|
| 5 |
+
# 1) consumes some of it and returns an Event
|
| 6 |
+
# 2) raises a LocalProtocolError (for consistency -- e.g. we call validate()
|
| 7 |
+
# and it might raise a LocalProtocolError, so simpler just to always use
|
| 8 |
+
# this)
|
| 9 |
+
# 3) returns None, meaning "I need more data"
|
| 10 |
+
#
|
| 11 |
+
# If they have a .read_eof attribute, then this will be called if an EOF is
|
| 12 |
+
# received -- but this is optional. Either way, the actual ConnectionClosed
|
| 13 |
+
# event will be generated afterwards.
|
| 14 |
+
#
|
| 15 |
+
# READERS is a dict describing how to pick a reader. It maps states to either:
|
| 16 |
+
# - a reader
|
| 17 |
+
# - or, for body readers, a dict of per-framing reader factories
|
| 18 |
+
|
| 19 |
+
import re
|
| 20 |
+
from typing import Any, Callable, Dict, Iterable, NoReturn, Optional, Tuple, Type, Union
|
| 21 |
+
|
| 22 |
+
from ._abnf import chunk_header, header_field, request_line, status_line
|
| 23 |
+
from ._events import Data, EndOfMessage, InformationalResponse, Request, Response
|
| 24 |
+
from ._receivebuffer import ReceiveBuffer
|
| 25 |
+
from ._state import (
|
| 26 |
+
CLIENT,
|
| 27 |
+
CLOSED,
|
| 28 |
+
DONE,
|
| 29 |
+
IDLE,
|
| 30 |
+
MUST_CLOSE,
|
| 31 |
+
SEND_BODY,
|
| 32 |
+
SEND_RESPONSE,
|
| 33 |
+
SERVER,
|
| 34 |
+
)
|
| 35 |
+
from ._util import LocalProtocolError, RemoteProtocolError, Sentinel, validate
|
| 36 |
+
|
| 37 |
+
__all__ = ["READERS"]
|
| 38 |
+
|
| 39 |
+
header_field_re = re.compile(header_field.encode("ascii"))
|
| 40 |
+
obs_fold_re = re.compile(rb"[ \t]+")
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def _obsolete_line_fold(lines: Iterable[bytes]) -> Iterable[bytes]:
|
| 44 |
+
it = iter(lines)
|
| 45 |
+
last: Optional[bytes] = None
|
| 46 |
+
for line in it:
|
| 47 |
+
match = obs_fold_re.match(line)
|
| 48 |
+
if match:
|
| 49 |
+
if last is None:
|
| 50 |
+
raise LocalProtocolError("continuation line at start of headers")
|
| 51 |
+
if not isinstance(last, bytearray):
|
| 52 |
+
# Cast to a mutable type, avoiding copy on append to ensure O(n) time
|
| 53 |
+
last = bytearray(last)
|
| 54 |
+
last += b" "
|
| 55 |
+
last += line[match.end() :]
|
| 56 |
+
else:
|
| 57 |
+
if last is not None:
|
| 58 |
+
yield last
|
| 59 |
+
last = line
|
| 60 |
+
if last is not None:
|
| 61 |
+
yield last
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def _decode_header_lines(
|
| 65 |
+
lines: Iterable[bytes],
|
| 66 |
+
) -> Iterable[Tuple[bytes, bytes]]:
|
| 67 |
+
for line in _obsolete_line_fold(lines):
|
| 68 |
+
matches = validate(header_field_re, line, "illegal header line: {!r}", line)
|
| 69 |
+
yield (matches["field_name"], matches["field_value"])
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
request_line_re = re.compile(request_line.encode("ascii"))
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def maybe_read_from_IDLE_client(buf: ReceiveBuffer) -> Optional[Request]:
|
| 76 |
+
lines = buf.maybe_extract_lines()
|
| 77 |
+
if lines is None:
|
| 78 |
+
if buf.is_next_line_obviously_invalid_request_line():
|
| 79 |
+
raise LocalProtocolError("illegal request line")
|
| 80 |
+
return None
|
| 81 |
+
if not lines:
|
| 82 |
+
raise LocalProtocolError("no request line received")
|
| 83 |
+
matches = validate(
|
| 84 |
+
request_line_re, lines[0], "illegal request line: {!r}", lines[0]
|
| 85 |
+
)
|
| 86 |
+
return Request(
|
| 87 |
+
headers=list(_decode_header_lines(lines[1:])), _parsed=True, **matches
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
status_line_re = re.compile(status_line.encode("ascii"))
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def maybe_read_from_SEND_RESPONSE_server(
|
| 95 |
+
buf: ReceiveBuffer,
|
| 96 |
+
) -> Union[InformationalResponse, Response, None]:
|
| 97 |
+
lines = buf.maybe_extract_lines()
|
| 98 |
+
if lines is None:
|
| 99 |
+
if buf.is_next_line_obviously_invalid_request_line():
|
| 100 |
+
raise LocalProtocolError("illegal request line")
|
| 101 |
+
return None
|
| 102 |
+
if not lines:
|
| 103 |
+
raise LocalProtocolError("no response line received")
|
| 104 |
+
matches = validate(status_line_re, lines[0], "illegal status line: {!r}", lines[0])
|
| 105 |
+
http_version = (
|
| 106 |
+
b"1.1" if matches["http_version"] is None else matches["http_version"]
|
| 107 |
+
)
|
| 108 |
+
reason = b"" if matches["reason"] is None else matches["reason"]
|
| 109 |
+
status_code = int(matches["status_code"])
|
| 110 |
+
class_: Union[Type[InformationalResponse], Type[Response]] = (
|
| 111 |
+
InformationalResponse if status_code < 200 else Response
|
| 112 |
+
)
|
| 113 |
+
return class_(
|
| 114 |
+
headers=list(_decode_header_lines(lines[1:])),
|
| 115 |
+
_parsed=True,
|
| 116 |
+
status_code=status_code,
|
| 117 |
+
reason=reason,
|
| 118 |
+
http_version=http_version,
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
class ContentLengthReader:
|
| 123 |
+
def __init__(self, length: int) -> None:
|
| 124 |
+
self._length = length
|
| 125 |
+
self._remaining = length
|
| 126 |
+
|
| 127 |
+
def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]:
|
| 128 |
+
if self._remaining == 0:
|
| 129 |
+
return EndOfMessage()
|
| 130 |
+
data = buf.maybe_extract_at_most(self._remaining)
|
| 131 |
+
if data is None:
|
| 132 |
+
return None
|
| 133 |
+
self._remaining -= len(data)
|
| 134 |
+
return Data(data=data)
|
| 135 |
+
|
| 136 |
+
def read_eof(self) -> NoReturn:
|
| 137 |
+
raise RemoteProtocolError(
|
| 138 |
+
"peer closed connection without sending complete message body "
|
| 139 |
+
"(received {} bytes, expected {})".format(
|
| 140 |
+
self._length - self._remaining, self._length
|
| 141 |
+
)
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
chunk_header_re = re.compile(chunk_header.encode("ascii"))
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
class ChunkedReader:
|
| 149 |
+
def __init__(self) -> None:
|
| 150 |
+
self._bytes_in_chunk = 0
|
| 151 |
+
# After reading a chunk, we have to throw away the trailing \r\n.
|
| 152 |
+
# This tracks the bytes that we need to match and throw away.
|
| 153 |
+
self._bytes_to_discard = b""
|
| 154 |
+
self._reading_trailer = False
|
| 155 |
+
|
| 156 |
+
def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]:
|
| 157 |
+
if self._reading_trailer:
|
| 158 |
+
lines = buf.maybe_extract_lines()
|
| 159 |
+
if lines is None:
|
| 160 |
+
return None
|
| 161 |
+
return EndOfMessage(headers=list(_decode_header_lines(lines)))
|
| 162 |
+
if self._bytes_to_discard:
|
| 163 |
+
data = buf.maybe_extract_at_most(len(self._bytes_to_discard))
|
| 164 |
+
if data is None:
|
| 165 |
+
return None
|
| 166 |
+
if data != self._bytes_to_discard[: len(data)]:
|
| 167 |
+
raise LocalProtocolError(
|
| 168 |
+
f"malformed chunk footer: {data!r} (expected {self._bytes_to_discard!r})"
|
| 169 |
+
)
|
| 170 |
+
self._bytes_to_discard = self._bytes_to_discard[len(data) :]
|
| 171 |
+
if self._bytes_to_discard:
|
| 172 |
+
return None
|
| 173 |
+
# else, fall through and read some more
|
| 174 |
+
assert self._bytes_to_discard == b""
|
| 175 |
+
if self._bytes_in_chunk == 0:
|
| 176 |
+
# We need to refill our chunk count
|
| 177 |
+
chunk_header = buf.maybe_extract_next_line()
|
| 178 |
+
if chunk_header is None:
|
| 179 |
+
return None
|
| 180 |
+
matches = validate(
|
| 181 |
+
chunk_header_re,
|
| 182 |
+
chunk_header,
|
| 183 |
+
"illegal chunk header: {!r}",
|
| 184 |
+
chunk_header,
|
| 185 |
+
)
|
| 186 |
+
# XX FIXME: we discard chunk extensions. Does anyone care?
|
| 187 |
+
self._bytes_in_chunk = int(matches["chunk_size"], base=16)
|
| 188 |
+
if self._bytes_in_chunk == 0:
|
| 189 |
+
self._reading_trailer = True
|
| 190 |
+
return self(buf)
|
| 191 |
+
chunk_start = True
|
| 192 |
+
else:
|
| 193 |
+
chunk_start = False
|
| 194 |
+
assert self._bytes_in_chunk > 0
|
| 195 |
+
data = buf.maybe_extract_at_most(self._bytes_in_chunk)
|
| 196 |
+
if data is None:
|
| 197 |
+
return None
|
| 198 |
+
self._bytes_in_chunk -= len(data)
|
| 199 |
+
if self._bytes_in_chunk == 0:
|
| 200 |
+
self._bytes_to_discard = b"\r\n"
|
| 201 |
+
chunk_end = True
|
| 202 |
+
else:
|
| 203 |
+
chunk_end = False
|
| 204 |
+
return Data(data=data, chunk_start=chunk_start, chunk_end=chunk_end)
|
| 205 |
+
|
| 206 |
+
def read_eof(self) -> NoReturn:
|
| 207 |
+
raise RemoteProtocolError(
|
| 208 |
+
"peer closed connection without sending complete message body "
|
| 209 |
+
"(incomplete chunked read)"
|
| 210 |
+
)
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
class Http10Reader:
|
| 214 |
+
def __call__(self, buf: ReceiveBuffer) -> Optional[Data]:
|
| 215 |
+
data = buf.maybe_extract_at_most(999999999)
|
| 216 |
+
if data is None:
|
| 217 |
+
return None
|
| 218 |
+
return Data(data=data)
|
| 219 |
+
|
| 220 |
+
def read_eof(self) -> EndOfMessage:
|
| 221 |
+
return EndOfMessage()
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
def expect_nothing(buf: ReceiveBuffer) -> None:
|
| 225 |
+
if buf:
|
| 226 |
+
raise LocalProtocolError("Got data when expecting EOF")
|
| 227 |
+
return None
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
ReadersType = Dict[
|
| 231 |
+
Union[Type[Sentinel], Tuple[Type[Sentinel], Type[Sentinel]]],
|
| 232 |
+
Union[Callable[..., Any], Dict[str, Callable[..., Any]]],
|
| 233 |
+
]
|
| 234 |
+
|
| 235 |
+
READERS: ReadersType = {
|
| 236 |
+
(CLIENT, IDLE): maybe_read_from_IDLE_client,
|
| 237 |
+
(SERVER, IDLE): maybe_read_from_SEND_RESPONSE_server,
|
| 238 |
+
(SERVER, SEND_RESPONSE): maybe_read_from_SEND_RESPONSE_server,
|
| 239 |
+
(CLIENT, DONE): expect_nothing,
|
| 240 |
+
(CLIENT, MUST_CLOSE): expect_nothing,
|
| 241 |
+
(CLIENT, CLOSED): expect_nothing,
|
| 242 |
+
(SERVER, DONE): expect_nothing,
|
| 243 |
+
(SERVER, MUST_CLOSE): expect_nothing,
|
| 244 |
+
(SERVER, CLOSED): expect_nothing,
|
| 245 |
+
SEND_BODY: {
|
| 246 |
+
"chunked": ChunkedReader,
|
| 247 |
+
"content-length": ContentLengthReader,
|
| 248 |
+
"http/1.0": Http10Reader,
|
| 249 |
+
},
|
| 250 |
+
}
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_receivebuffer.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
import sys
|
| 3 |
+
from typing import List, Optional, Union
|
| 4 |
+
|
| 5 |
+
__all__ = ["ReceiveBuffer"]
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
# Operations we want to support:
|
| 9 |
+
# - find next \r\n or \r\n\r\n (\n or \n\n are also acceptable),
|
| 10 |
+
# or wait until there is one
|
| 11 |
+
# - read at-most-N bytes
|
| 12 |
+
# Goals:
|
| 13 |
+
# - on average, do this fast
|
| 14 |
+
# - worst case, do this in O(n) where n is the number of bytes processed
|
| 15 |
+
# Plan:
|
| 16 |
+
# - store bytearray, offset, how far we've searched for a separator token
|
| 17 |
+
# - use the how-far-we've-searched data to avoid rescanning
|
| 18 |
+
# - while doing a stream of uninterrupted processing, advance offset instead
|
| 19 |
+
# of constantly copying
|
| 20 |
+
# WARNING:
|
| 21 |
+
# - I haven't benchmarked or profiled any of this yet.
|
| 22 |
+
#
|
| 23 |
+
# Note that starting in Python 3.4, deleting the initial n bytes from a
|
| 24 |
+
# bytearray is amortized O(n), thanks to some excellent work by Antoine
|
| 25 |
+
# Martin:
|
| 26 |
+
#
|
| 27 |
+
# https://bugs.python.org/issue19087
|
| 28 |
+
#
|
| 29 |
+
# This means that if we only supported 3.4+, we could get rid of the code here
|
| 30 |
+
# involving self._start and self.compress, because it's doing exactly the same
|
| 31 |
+
# thing that bytearray now does internally.
|
| 32 |
+
#
|
| 33 |
+
# BUT unfortunately, we still support 2.7, and reading short segments out of a
|
| 34 |
+
# long buffer MUST be O(bytes read) to avoid DoS issues, so we can't actually
|
| 35 |
+
# delete this code. Yet:
|
| 36 |
+
#
|
| 37 |
+
# https://pythonclock.org/
|
| 38 |
+
#
|
| 39 |
+
# (Two things to double-check first though: make sure PyPy also has the
|
| 40 |
+
# optimization, and benchmark to make sure it's a win, since we do have a
|
| 41 |
+
# slightly clever thing where we delay calling compress() until we've
|
| 42 |
+
# processed a whole event, which could in theory be slightly more efficient
|
| 43 |
+
# than the internal bytearray support.)
|
| 44 |
+
blank_line_regex = re.compile(b"\n\r?\n", re.MULTILINE)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class ReceiveBuffer:
|
| 48 |
+
def __init__(self) -> None:
|
| 49 |
+
self._data = bytearray()
|
| 50 |
+
self._next_line_search = 0
|
| 51 |
+
self._multiple_lines_search = 0
|
| 52 |
+
|
| 53 |
+
def __iadd__(self, byteslike: Union[bytes, bytearray]) -> "ReceiveBuffer":
|
| 54 |
+
self._data += byteslike
|
| 55 |
+
return self
|
| 56 |
+
|
| 57 |
+
def __bool__(self) -> bool:
|
| 58 |
+
return bool(len(self))
|
| 59 |
+
|
| 60 |
+
def __len__(self) -> int:
|
| 61 |
+
return len(self._data)
|
| 62 |
+
|
| 63 |
+
# for @property unprocessed_data
|
| 64 |
+
def __bytes__(self) -> bytes:
|
| 65 |
+
return bytes(self._data)
|
| 66 |
+
|
| 67 |
+
def _extract(self, count: int) -> bytearray:
|
| 68 |
+
# extracting an initial slice of the data buffer and return it
|
| 69 |
+
out = self._data[:count]
|
| 70 |
+
del self._data[:count]
|
| 71 |
+
|
| 72 |
+
self._next_line_search = 0
|
| 73 |
+
self._multiple_lines_search = 0
|
| 74 |
+
|
| 75 |
+
return out
|
| 76 |
+
|
| 77 |
+
def maybe_extract_at_most(self, count: int) -> Optional[bytearray]:
|
| 78 |
+
"""
|
| 79 |
+
Extract a fixed number of bytes from the buffer.
|
| 80 |
+
"""
|
| 81 |
+
out = self._data[:count]
|
| 82 |
+
if not out:
|
| 83 |
+
return None
|
| 84 |
+
|
| 85 |
+
return self._extract(count)
|
| 86 |
+
|
| 87 |
+
def maybe_extract_next_line(self) -> Optional[bytearray]:
|
| 88 |
+
"""
|
| 89 |
+
Extract the first line, if it is completed in the buffer.
|
| 90 |
+
"""
|
| 91 |
+
# Only search in buffer space that we've not already looked at.
|
| 92 |
+
search_start_index = max(0, self._next_line_search - 1)
|
| 93 |
+
partial_idx = self._data.find(b"\r\n", search_start_index)
|
| 94 |
+
|
| 95 |
+
if partial_idx == -1:
|
| 96 |
+
self._next_line_search = len(self._data)
|
| 97 |
+
return None
|
| 98 |
+
|
| 99 |
+
# + 2 is to compensate len(b"\r\n")
|
| 100 |
+
idx = partial_idx + 2
|
| 101 |
+
|
| 102 |
+
return self._extract(idx)
|
| 103 |
+
|
| 104 |
+
def maybe_extract_lines(self) -> Optional[List[bytearray]]:
|
| 105 |
+
"""
|
| 106 |
+
Extract everything up to the first blank line, and return a list of lines.
|
| 107 |
+
"""
|
| 108 |
+
# Handle the case where we have an immediate empty line.
|
| 109 |
+
if self._data[:1] == b"\n":
|
| 110 |
+
self._extract(1)
|
| 111 |
+
return []
|
| 112 |
+
|
| 113 |
+
if self._data[:2] == b"\r\n":
|
| 114 |
+
self._extract(2)
|
| 115 |
+
return []
|
| 116 |
+
|
| 117 |
+
# Only search in buffer space that we've not already looked at.
|
| 118 |
+
match = blank_line_regex.search(self._data, self._multiple_lines_search)
|
| 119 |
+
if match is None:
|
| 120 |
+
self._multiple_lines_search = max(0, len(self._data) - 2)
|
| 121 |
+
return None
|
| 122 |
+
|
| 123 |
+
# Truncate the buffer and return it.
|
| 124 |
+
idx = match.span(0)[-1]
|
| 125 |
+
out = self._extract(idx)
|
| 126 |
+
lines = out.split(b"\n")
|
| 127 |
+
|
| 128 |
+
for line in lines:
|
| 129 |
+
if line.endswith(b"\r"):
|
| 130 |
+
del line[-1]
|
| 131 |
+
|
| 132 |
+
assert lines[-2] == lines[-1] == b""
|
| 133 |
+
|
| 134 |
+
del lines[-2:]
|
| 135 |
+
|
| 136 |
+
return lines
|
| 137 |
+
|
| 138 |
+
# In theory we should wait until `\r\n` before starting to validate
|
| 139 |
+
# incoming data. However it's interesting to detect (very) invalid data
|
| 140 |
+
# early given they might not even contain `\r\n` at all (hence only
|
| 141 |
+
# timeout will get rid of them).
|
| 142 |
+
# This is not a 100% effective detection but more of a cheap sanity check
|
| 143 |
+
# allowing for early abort in some useful cases.
|
| 144 |
+
# This is especially interesting when peer is messing up with HTTPS and
|
| 145 |
+
# sent us a TLS stream where we were expecting plain HTTP given all
|
| 146 |
+
# versions of TLS so far start handshake with a 0x16 message type code.
|
| 147 |
+
def is_next_line_obviously_invalid_request_line(self) -> bool:
|
| 148 |
+
try:
|
| 149 |
+
# HTTP header line must not contain non-printable characters
|
| 150 |
+
# and should not start with a space
|
| 151 |
+
return self._data[0] < 0x21
|
| 152 |
+
except IndexError:
|
| 153 |
+
return False
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_state.py
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
################################################################
|
| 2 |
+
# The core state machine
|
| 3 |
+
################################################################
|
| 4 |
+
#
|
| 5 |
+
# Rule 1: everything that affects the state machine and state transitions must
|
| 6 |
+
# live here in this file. As much as possible goes into the table-based
|
| 7 |
+
# representation, but for the bits that don't quite fit, the actual code and
|
| 8 |
+
# state must nonetheless live here.
|
| 9 |
+
#
|
| 10 |
+
# Rule 2: this file does not know about what role we're playing; it only knows
|
| 11 |
+
# about HTTP request/response cycles in the abstract. This ensures that we
|
| 12 |
+
# don't cheat and apply different rules to local and remote parties.
|
| 13 |
+
#
|
| 14 |
+
#
|
| 15 |
+
# Theory of operation
|
| 16 |
+
# ===================
|
| 17 |
+
#
|
| 18 |
+
# Possibly the simplest way to think about this is that we actually have 5
|
| 19 |
+
# different state machines here. Yes, 5. These are:
|
| 20 |
+
#
|
| 21 |
+
# 1) The client state, with its complicated automaton (see the docs)
|
| 22 |
+
# 2) The server state, with its complicated automaton (see the docs)
|
| 23 |
+
# 3) The keep-alive state, with possible states {True, False}
|
| 24 |
+
# 4) The SWITCH_CONNECT state, with possible states {False, True}
|
| 25 |
+
# 5) The SWITCH_UPGRADE state, with possible states {False, True}
|
| 26 |
+
#
|
| 27 |
+
# For (3)-(5), the first state listed is the initial state.
|
| 28 |
+
#
|
| 29 |
+
# (1)-(3) are stored explicitly in member variables. The last
|
| 30 |
+
# two are stored implicitly in the pending_switch_proposals set as:
|
| 31 |
+
# (state of 4) == (_SWITCH_CONNECT in pending_switch_proposals)
|
| 32 |
+
# (state of 5) == (_SWITCH_UPGRADE in pending_switch_proposals)
|
| 33 |
+
#
|
| 34 |
+
# And each of these machines has two different kinds of transitions:
|
| 35 |
+
#
|
| 36 |
+
# a) Event-triggered
|
| 37 |
+
# b) State-triggered
|
| 38 |
+
#
|
| 39 |
+
# Event triggered is the obvious thing that you'd think it is: some event
|
| 40 |
+
# happens, and if it's the right event at the right time then a transition
|
| 41 |
+
# happens. But there are somewhat complicated rules for which machines can
|
| 42 |
+
# "see" which events. (As a rule of thumb, if a machine "sees" an event, this
|
| 43 |
+
# means two things: the event can affect the machine, and if the machine is
|
| 44 |
+
# not in a state where it expects that event then it's an error.) These rules
|
| 45 |
+
# are:
|
| 46 |
+
#
|
| 47 |
+
# 1) The client machine sees all h11.events objects emitted by the client.
|
| 48 |
+
#
|
| 49 |
+
# 2) The server machine sees all h11.events objects emitted by the server.
|
| 50 |
+
#
|
| 51 |
+
# It also sees the client's Request event.
|
| 52 |
+
#
|
| 53 |
+
# And sometimes, server events are annotated with a _SWITCH_* event. For
|
| 54 |
+
# example, we can have a (Response, _SWITCH_CONNECT) event, which is
|
| 55 |
+
# different from a regular Response event.
|
| 56 |
+
#
|
| 57 |
+
# 3) The keep-alive machine sees the process_keep_alive_disabled() event
|
| 58 |
+
# (which is derived from Request/Response events), and this event
|
| 59 |
+
# transitions it from True -> False, or from False -> False. There's no way
|
| 60 |
+
# to transition back.
|
| 61 |
+
#
|
| 62 |
+
# 4&5) The _SWITCH_* machines transition from False->True when we get a
|
| 63 |
+
# Request that proposes the relevant type of switch (via
|
| 64 |
+
# process_client_switch_proposals), and they go from True->False when we
|
| 65 |
+
# get a Response that has no _SWITCH_* annotation.
|
| 66 |
+
#
|
| 67 |
+
# So that's event-triggered transitions.
|
| 68 |
+
#
|
| 69 |
+
# State-triggered transitions are less standard. What they do here is couple
|
| 70 |
+
# the machines together. The way this works is, when certain *joint*
|
| 71 |
+
# configurations of states are achieved, then we automatically transition to a
|
| 72 |
+
# new *joint* state. So, for example, if we're ever in a joint state with
|
| 73 |
+
#
|
| 74 |
+
# client: DONE
|
| 75 |
+
# keep-alive: False
|
| 76 |
+
#
|
| 77 |
+
# then the client state immediately transitions to:
|
| 78 |
+
#
|
| 79 |
+
# client: MUST_CLOSE
|
| 80 |
+
#
|
| 81 |
+
# This is fundamentally different from an event-based transition, because it
|
| 82 |
+
# doesn't matter how we arrived at the {client: DONE, keep-alive: False} state
|
| 83 |
+
# -- maybe the client transitioned SEND_BODY -> DONE, or keep-alive
|
| 84 |
+
# transitioned True -> False. Either way, once this precondition is satisfied,
|
| 85 |
+
# this transition is immediately triggered.
|
| 86 |
+
#
|
| 87 |
+
# What if two conflicting state-based transitions get enabled at the same
|
| 88 |
+
# time? In practice there's only one case where this arises (client DONE ->
|
| 89 |
+
# MIGHT_SWITCH_PROTOCOL versus DONE -> MUST_CLOSE), and we resolve it by
|
| 90 |
+
# explicitly prioritizing the DONE -> MIGHT_SWITCH_PROTOCOL transition.
|
| 91 |
+
#
|
| 92 |
+
# Implementation
|
| 93 |
+
# --------------
|
| 94 |
+
#
|
| 95 |
+
# The event-triggered transitions for the server and client machines are all
|
| 96 |
+
# stored explicitly in a table. Ditto for the state-triggered transitions that
|
| 97 |
+
# involve just the server and client state.
|
| 98 |
+
#
|
| 99 |
+
# The transitions for the other machines, and the state-triggered transitions
|
| 100 |
+
# that involve the other machines, are written out as explicit Python code.
|
| 101 |
+
#
|
| 102 |
+
# It'd be nice if there were some cleaner way to do all this. This isn't
|
| 103 |
+
# *too* terrible, but I feel like it could probably be better.
|
| 104 |
+
#
|
| 105 |
+
# WARNING
|
| 106 |
+
# -------
|
| 107 |
+
#
|
| 108 |
+
# The script that generates the state machine diagrams for the docs knows how
|
| 109 |
+
# to read out the EVENT_TRIGGERED_TRANSITIONS and STATE_TRIGGERED_TRANSITIONS
|
| 110 |
+
# tables. But it can't automatically read the transitions that are written
|
| 111 |
+
# directly in Python code. So if you touch those, you need to also update the
|
| 112 |
+
# script to keep it in sync!
|
| 113 |
+
from typing import cast, Dict, Optional, Set, Tuple, Type, Union
|
| 114 |
+
|
| 115 |
+
from ._events import *
|
| 116 |
+
from ._util import LocalProtocolError, Sentinel
|
| 117 |
+
|
| 118 |
+
# Everything in __all__ gets re-exported as part of the h11 public API.
|
| 119 |
+
__all__ = [
|
| 120 |
+
"CLIENT",
|
| 121 |
+
"SERVER",
|
| 122 |
+
"IDLE",
|
| 123 |
+
"SEND_RESPONSE",
|
| 124 |
+
"SEND_BODY",
|
| 125 |
+
"DONE",
|
| 126 |
+
"MUST_CLOSE",
|
| 127 |
+
"CLOSED",
|
| 128 |
+
"MIGHT_SWITCH_PROTOCOL",
|
| 129 |
+
"SWITCHED_PROTOCOL",
|
| 130 |
+
"ERROR",
|
| 131 |
+
]
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
class CLIENT(Sentinel, metaclass=Sentinel):
|
| 135 |
+
pass
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
class SERVER(Sentinel, metaclass=Sentinel):
|
| 139 |
+
pass
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
# States
|
| 143 |
+
class IDLE(Sentinel, metaclass=Sentinel):
|
| 144 |
+
pass
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
class SEND_RESPONSE(Sentinel, metaclass=Sentinel):
|
| 148 |
+
pass
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
class SEND_BODY(Sentinel, metaclass=Sentinel):
|
| 152 |
+
pass
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
class DONE(Sentinel, metaclass=Sentinel):
|
| 156 |
+
pass
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
class MUST_CLOSE(Sentinel, metaclass=Sentinel):
|
| 160 |
+
pass
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
class CLOSED(Sentinel, metaclass=Sentinel):
|
| 164 |
+
pass
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
class ERROR(Sentinel, metaclass=Sentinel):
|
| 168 |
+
pass
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
# Switch types
|
| 172 |
+
class MIGHT_SWITCH_PROTOCOL(Sentinel, metaclass=Sentinel):
|
| 173 |
+
pass
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
class SWITCHED_PROTOCOL(Sentinel, metaclass=Sentinel):
|
| 177 |
+
pass
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
class _SWITCH_UPGRADE(Sentinel, metaclass=Sentinel):
|
| 181 |
+
pass
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
class _SWITCH_CONNECT(Sentinel, metaclass=Sentinel):
|
| 185 |
+
pass
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
EventTransitionType = Dict[
|
| 189 |
+
Type[Sentinel],
|
| 190 |
+
Dict[
|
| 191 |
+
Type[Sentinel],
|
| 192 |
+
Dict[Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]], Type[Sentinel]],
|
| 193 |
+
],
|
| 194 |
+
]
|
| 195 |
+
|
| 196 |
+
EVENT_TRIGGERED_TRANSITIONS: EventTransitionType = {
|
| 197 |
+
CLIENT: {
|
| 198 |
+
IDLE: {Request: SEND_BODY, ConnectionClosed: CLOSED},
|
| 199 |
+
SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE},
|
| 200 |
+
DONE: {ConnectionClosed: CLOSED},
|
| 201 |
+
MUST_CLOSE: {ConnectionClosed: CLOSED},
|
| 202 |
+
CLOSED: {ConnectionClosed: CLOSED},
|
| 203 |
+
MIGHT_SWITCH_PROTOCOL: {},
|
| 204 |
+
SWITCHED_PROTOCOL: {},
|
| 205 |
+
ERROR: {},
|
| 206 |
+
},
|
| 207 |
+
SERVER: {
|
| 208 |
+
IDLE: {
|
| 209 |
+
ConnectionClosed: CLOSED,
|
| 210 |
+
Response: SEND_BODY,
|
| 211 |
+
# Special case: server sees client Request events, in this form
|
| 212 |
+
(Request, CLIENT): SEND_RESPONSE,
|
| 213 |
+
},
|
| 214 |
+
SEND_RESPONSE: {
|
| 215 |
+
InformationalResponse: SEND_RESPONSE,
|
| 216 |
+
Response: SEND_BODY,
|
| 217 |
+
(InformationalResponse, _SWITCH_UPGRADE): SWITCHED_PROTOCOL,
|
| 218 |
+
(Response, _SWITCH_CONNECT): SWITCHED_PROTOCOL,
|
| 219 |
+
},
|
| 220 |
+
SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE},
|
| 221 |
+
DONE: {ConnectionClosed: CLOSED},
|
| 222 |
+
MUST_CLOSE: {ConnectionClosed: CLOSED},
|
| 223 |
+
CLOSED: {ConnectionClosed: CLOSED},
|
| 224 |
+
SWITCHED_PROTOCOL: {},
|
| 225 |
+
ERROR: {},
|
| 226 |
+
},
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
StateTransitionType = Dict[
|
| 230 |
+
Tuple[Type[Sentinel], Type[Sentinel]], Dict[Type[Sentinel], Type[Sentinel]]
|
| 231 |
+
]
|
| 232 |
+
|
| 233 |
+
# NB: there are also some special-case state-triggered transitions hard-coded
|
| 234 |
+
# into _fire_state_triggered_transitions below.
|
| 235 |
+
STATE_TRIGGERED_TRANSITIONS: StateTransitionType = {
|
| 236 |
+
# (Client state, Server state) -> new states
|
| 237 |
+
# Protocol negotiation
|
| 238 |
+
(MIGHT_SWITCH_PROTOCOL, SWITCHED_PROTOCOL): {CLIENT: SWITCHED_PROTOCOL},
|
| 239 |
+
# Socket shutdown
|
| 240 |
+
(CLOSED, DONE): {SERVER: MUST_CLOSE},
|
| 241 |
+
(CLOSED, IDLE): {SERVER: MUST_CLOSE},
|
| 242 |
+
(ERROR, DONE): {SERVER: MUST_CLOSE},
|
| 243 |
+
(DONE, CLOSED): {CLIENT: MUST_CLOSE},
|
| 244 |
+
(IDLE, CLOSED): {CLIENT: MUST_CLOSE},
|
| 245 |
+
(DONE, ERROR): {CLIENT: MUST_CLOSE},
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
class ConnectionState:
|
| 250 |
+
def __init__(self) -> None:
|
| 251 |
+
# Extra bits of state that don't quite fit into the state model.
|
| 252 |
+
|
| 253 |
+
# If this is False then it enables the automatic DONE -> MUST_CLOSE
|
| 254 |
+
# transition. Don't set this directly; call .keep_alive_disabled()
|
| 255 |
+
self.keep_alive = True
|
| 256 |
+
|
| 257 |
+
# This is a subset of {UPGRADE, CONNECT}, containing the proposals
|
| 258 |
+
# made by the client for switching protocols.
|
| 259 |
+
self.pending_switch_proposals: Set[Type[Sentinel]] = set()
|
| 260 |
+
|
| 261 |
+
self.states: Dict[Type[Sentinel], Type[Sentinel]] = {CLIENT: IDLE, SERVER: IDLE}
|
| 262 |
+
|
| 263 |
+
def process_error(self, role: Type[Sentinel]) -> None:
|
| 264 |
+
self.states[role] = ERROR
|
| 265 |
+
self._fire_state_triggered_transitions()
|
| 266 |
+
|
| 267 |
+
def process_keep_alive_disabled(self) -> None:
|
| 268 |
+
self.keep_alive = False
|
| 269 |
+
self._fire_state_triggered_transitions()
|
| 270 |
+
|
| 271 |
+
def process_client_switch_proposal(self, switch_event: Type[Sentinel]) -> None:
|
| 272 |
+
self.pending_switch_proposals.add(switch_event)
|
| 273 |
+
self._fire_state_triggered_transitions()
|
| 274 |
+
|
| 275 |
+
def process_event(
|
| 276 |
+
self,
|
| 277 |
+
role: Type[Sentinel],
|
| 278 |
+
event_type: Type[Event],
|
| 279 |
+
server_switch_event: Optional[Type[Sentinel]] = None,
|
| 280 |
+
) -> None:
|
| 281 |
+
_event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]] = event_type
|
| 282 |
+
if server_switch_event is not None:
|
| 283 |
+
assert role is SERVER
|
| 284 |
+
if server_switch_event not in self.pending_switch_proposals:
|
| 285 |
+
raise LocalProtocolError(
|
| 286 |
+
"Received server _SWITCH_UPGRADE event without a pending proposal"
|
| 287 |
+
)
|
| 288 |
+
_event_type = (event_type, server_switch_event)
|
| 289 |
+
if server_switch_event is None and _event_type is Response:
|
| 290 |
+
self.pending_switch_proposals = set()
|
| 291 |
+
self._fire_event_triggered_transitions(role, _event_type)
|
| 292 |
+
# Special case: the server state does get to see Request
|
| 293 |
+
# events.
|
| 294 |
+
if _event_type is Request:
|
| 295 |
+
assert role is CLIENT
|
| 296 |
+
self._fire_event_triggered_transitions(SERVER, (Request, CLIENT))
|
| 297 |
+
self._fire_state_triggered_transitions()
|
| 298 |
+
|
| 299 |
+
def _fire_event_triggered_transitions(
|
| 300 |
+
self,
|
| 301 |
+
role: Type[Sentinel],
|
| 302 |
+
event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]],
|
| 303 |
+
) -> None:
|
| 304 |
+
state = self.states[role]
|
| 305 |
+
try:
|
| 306 |
+
new_state = EVENT_TRIGGERED_TRANSITIONS[role][state][event_type]
|
| 307 |
+
except KeyError:
|
| 308 |
+
event_type = cast(Type[Event], event_type)
|
| 309 |
+
raise LocalProtocolError(
|
| 310 |
+
"can't handle event type {} when role={} and state={}".format(
|
| 311 |
+
event_type.__name__, role, self.states[role]
|
| 312 |
+
)
|
| 313 |
+
) from None
|
| 314 |
+
self.states[role] = new_state
|
| 315 |
+
|
| 316 |
+
def _fire_state_triggered_transitions(self) -> None:
|
| 317 |
+
# We apply these rules repeatedly until converging on a fixed point
|
| 318 |
+
while True:
|
| 319 |
+
start_states = dict(self.states)
|
| 320 |
+
|
| 321 |
+
# It could happen that both these special-case transitions are
|
| 322 |
+
# enabled at the same time:
|
| 323 |
+
#
|
| 324 |
+
# DONE -> MIGHT_SWITCH_PROTOCOL
|
| 325 |
+
# DONE -> MUST_CLOSE
|
| 326 |
+
#
|
| 327 |
+
# For example, this will always be true of a HTTP/1.0 client
|
| 328 |
+
# requesting CONNECT. If this happens, the protocol switch takes
|
| 329 |
+
# priority. From there the client will either go to
|
| 330 |
+
# SWITCHED_PROTOCOL, in which case it's none of our business when
|
| 331 |
+
# they close the connection, or else the server will deny the
|
| 332 |
+
# request, in which case the client will go back to DONE and then
|
| 333 |
+
# from there to MUST_CLOSE.
|
| 334 |
+
if self.pending_switch_proposals:
|
| 335 |
+
if self.states[CLIENT] is DONE:
|
| 336 |
+
self.states[CLIENT] = MIGHT_SWITCH_PROTOCOL
|
| 337 |
+
|
| 338 |
+
if not self.pending_switch_proposals:
|
| 339 |
+
if self.states[CLIENT] is MIGHT_SWITCH_PROTOCOL:
|
| 340 |
+
self.states[CLIENT] = DONE
|
| 341 |
+
|
| 342 |
+
if not self.keep_alive:
|
| 343 |
+
for role in (CLIENT, SERVER):
|
| 344 |
+
if self.states[role] is DONE:
|
| 345 |
+
self.states[role] = MUST_CLOSE
|
| 346 |
+
|
| 347 |
+
# Tabular state-triggered transitions
|
| 348 |
+
joint_state = (self.states[CLIENT], self.states[SERVER])
|
| 349 |
+
changes = STATE_TRIGGERED_TRANSITIONS.get(joint_state, {})
|
| 350 |
+
self.states.update(changes)
|
| 351 |
+
|
| 352 |
+
if self.states == start_states:
|
| 353 |
+
# Fixed point reached
|
| 354 |
+
return
|
| 355 |
+
|
| 356 |
+
def start_next_cycle(self) -> None:
|
| 357 |
+
if self.states != {CLIENT: DONE, SERVER: DONE}:
|
| 358 |
+
raise LocalProtocolError(
|
| 359 |
+
f"not in a reusable state. self.states={self.states}"
|
| 360 |
+
)
|
| 361 |
+
# Can't reach DONE/DONE with any of these active, but still, let's be
|
| 362 |
+
# sure.
|
| 363 |
+
assert self.keep_alive
|
| 364 |
+
assert not self.pending_switch_proposals
|
| 365 |
+
self.states = {CLIENT: IDLE, SERVER: IDLE}
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_util.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Any, Dict, NoReturn, Pattern, Tuple, Type, TypeVar, Union
|
| 2 |
+
|
| 3 |
+
__all__ = [
|
| 4 |
+
"ProtocolError",
|
| 5 |
+
"LocalProtocolError",
|
| 6 |
+
"RemoteProtocolError",
|
| 7 |
+
"validate",
|
| 8 |
+
"bytesify",
|
| 9 |
+
]
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class ProtocolError(Exception):
|
| 13 |
+
"""Exception indicating a violation of the HTTP/1.1 protocol.
|
| 14 |
+
|
| 15 |
+
This as an abstract base class, with two concrete base classes:
|
| 16 |
+
:exc:`LocalProtocolError`, which indicates that you tried to do something
|
| 17 |
+
that HTTP/1.1 says is illegal, and :exc:`RemoteProtocolError`, which
|
| 18 |
+
indicates that the remote peer tried to do something that HTTP/1.1 says is
|
| 19 |
+
illegal. See :ref:`error-handling` for details.
|
| 20 |
+
|
| 21 |
+
In addition to the normal :exc:`Exception` features, it has one attribute:
|
| 22 |
+
|
| 23 |
+
.. attribute:: error_status_hint
|
| 24 |
+
|
| 25 |
+
This gives a suggestion as to what status code a server might use if
|
| 26 |
+
this error occurred as part of a request.
|
| 27 |
+
|
| 28 |
+
For a :exc:`RemoteProtocolError`, this is useful as a suggestion for
|
| 29 |
+
how you might want to respond to a misbehaving peer, if you're
|
| 30 |
+
implementing a server.
|
| 31 |
+
|
| 32 |
+
For a :exc:`LocalProtocolError`, this can be taken as a suggestion for
|
| 33 |
+
how your peer might have responded to *you* if h11 had allowed you to
|
| 34 |
+
continue.
|
| 35 |
+
|
| 36 |
+
The default is 400 Bad Request, a generic catch-all for protocol
|
| 37 |
+
violations.
|
| 38 |
+
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
def __init__(self, msg: str, error_status_hint: int = 400) -> None:
|
| 42 |
+
if type(self) is ProtocolError:
|
| 43 |
+
raise TypeError("tried to directly instantiate ProtocolError")
|
| 44 |
+
Exception.__init__(self, msg)
|
| 45 |
+
self.error_status_hint = error_status_hint
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
# Strategy: there are a number of public APIs where a LocalProtocolError can
|
| 49 |
+
# be raised (send(), all the different event constructors, ...), and only one
|
| 50 |
+
# public API where RemoteProtocolError can be raised
|
| 51 |
+
# (receive_data()). Therefore we always raise LocalProtocolError internally,
|
| 52 |
+
# and then receive_data will translate this into a RemoteProtocolError.
|
| 53 |
+
#
|
| 54 |
+
# Internally:
|
| 55 |
+
# LocalProtocolError is the generic "ProtocolError".
|
| 56 |
+
# Externally:
|
| 57 |
+
# LocalProtocolError is for local errors and RemoteProtocolError is for
|
| 58 |
+
# remote errors.
|
| 59 |
+
class LocalProtocolError(ProtocolError):
|
| 60 |
+
def _reraise_as_remote_protocol_error(self) -> NoReturn:
|
| 61 |
+
# After catching a LocalProtocolError, use this method to re-raise it
|
| 62 |
+
# as a RemoteProtocolError. This method must be called from inside an
|
| 63 |
+
# except: block.
|
| 64 |
+
#
|
| 65 |
+
# An easy way to get an equivalent RemoteProtocolError is just to
|
| 66 |
+
# modify 'self' in place.
|
| 67 |
+
self.__class__ = RemoteProtocolError # type: ignore
|
| 68 |
+
# But the re-raising is somewhat non-trivial -- you might think that
|
| 69 |
+
# now that we've modified the in-flight exception object, that just
|
| 70 |
+
# doing 'raise' to re-raise it would be enough. But it turns out that
|
| 71 |
+
# this doesn't work, because Python tracks the exception type
|
| 72 |
+
# (exc_info[0]) separately from the exception object (exc_info[1]),
|
| 73 |
+
# and we only modified the latter. So we really do need to re-raise
|
| 74 |
+
# the new type explicitly.
|
| 75 |
+
# On py3, the traceback is part of the exception object, so our
|
| 76 |
+
# in-place modification preserved it and we can just re-raise:
|
| 77 |
+
raise self
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
class RemoteProtocolError(ProtocolError):
|
| 81 |
+
pass
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def validate(
|
| 85 |
+
regex: Pattern[bytes], data: bytes, msg: str = "malformed data", *format_args: Any
|
| 86 |
+
) -> Dict[str, bytes]:
|
| 87 |
+
match = regex.fullmatch(data)
|
| 88 |
+
if not match:
|
| 89 |
+
if format_args:
|
| 90 |
+
msg = msg.format(*format_args)
|
| 91 |
+
raise LocalProtocolError(msg)
|
| 92 |
+
return match.groupdict()
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
# Sentinel values
|
| 96 |
+
#
|
| 97 |
+
# - Inherit identity-based comparison and hashing from object
|
| 98 |
+
# - Have a nice repr
|
| 99 |
+
# - Have a *bonus property*: type(sentinel) is sentinel
|
| 100 |
+
#
|
| 101 |
+
# The bonus property is useful if you want to take the return value from
|
| 102 |
+
# next_event() and do some sort of dispatch based on type(event).
|
| 103 |
+
|
| 104 |
+
_T_Sentinel = TypeVar("_T_Sentinel", bound="Sentinel")
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
class Sentinel(type):
|
| 108 |
+
def __new__(
|
| 109 |
+
cls: Type[_T_Sentinel],
|
| 110 |
+
name: str,
|
| 111 |
+
bases: Tuple[type, ...],
|
| 112 |
+
namespace: Dict[str, Any],
|
| 113 |
+
**kwds: Any
|
| 114 |
+
) -> _T_Sentinel:
|
| 115 |
+
assert bases == (Sentinel,)
|
| 116 |
+
v = super().__new__(cls, name, bases, namespace, **kwds)
|
| 117 |
+
v.__class__ = v # type: ignore
|
| 118 |
+
return v
|
| 119 |
+
|
| 120 |
+
def __repr__(self) -> str:
|
| 121 |
+
return self.__name__
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
# Used for methods, request targets, HTTP versions, header names, and header
|
| 125 |
+
# values. Accepts ascii-strings, or bytes/bytearray/memoryview/..., and always
|
| 126 |
+
# returns bytes.
|
| 127 |
+
def bytesify(s: Union[bytes, bytearray, memoryview, int, str]) -> bytes:
|
| 128 |
+
# Fast-path:
|
| 129 |
+
if type(s) is bytes:
|
| 130 |
+
return s
|
| 131 |
+
if isinstance(s, str):
|
| 132 |
+
s = s.encode("ascii")
|
| 133 |
+
if isinstance(s, int):
|
| 134 |
+
raise TypeError("expected bytes-like object, not int")
|
| 135 |
+
return bytes(s)
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_version.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file must be kept very simple, because it is consumed from several
|
| 2 |
+
# places -- it is imported by h11/__init__.py, execfile'd by setup.py, etc.
|
| 3 |
+
|
| 4 |
+
# We use a simple scheme:
|
| 5 |
+
# 1.0.0 -> 1.0.0+dev -> 1.1.0 -> 1.1.0+dev
|
| 6 |
+
# where the +dev versions are never released into the wild, they're just what
|
| 7 |
+
# we stick into the VCS in between releases.
|
| 8 |
+
#
|
| 9 |
+
# This is compatible with PEP 440:
|
| 10 |
+
# http://legacy.python.org/dev/peps/pep-0440/
|
| 11 |
+
# via the use of the "local suffix" "+dev", which is disallowed on index
|
| 12 |
+
# servers and causes 1.0.0+dev to sort after plain 1.0.0, which is what we
|
| 13 |
+
# want. (Contrast with the special suffix 1.0.0.dev, which sorts *before*
|
| 14 |
+
# 1.0.0.)
|
| 15 |
+
|
| 16 |
+
__version__ = "0.16.0"
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/_writers.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Code to read HTTP data
|
| 2 |
+
#
|
| 3 |
+
# Strategy: each writer takes an event + a write-some-bytes function, which is
|
| 4 |
+
# calls.
|
| 5 |
+
#
|
| 6 |
+
# WRITERS is a dict describing how to pick a reader. It maps states to either:
|
| 7 |
+
# - a writer
|
| 8 |
+
# - or, for body writers, a dict of framin-dependent writer factories
|
| 9 |
+
|
| 10 |
+
from typing import Any, Callable, Dict, List, Tuple, Type, Union
|
| 11 |
+
|
| 12 |
+
from ._events import Data, EndOfMessage, Event, InformationalResponse, Request, Response
|
| 13 |
+
from ._headers import Headers
|
| 14 |
+
from ._state import CLIENT, IDLE, SEND_BODY, SEND_RESPONSE, SERVER
|
| 15 |
+
from ._util import LocalProtocolError, Sentinel
|
| 16 |
+
|
| 17 |
+
__all__ = ["WRITERS"]
|
| 18 |
+
|
| 19 |
+
Writer = Callable[[bytes], Any]
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def write_headers(headers: Headers, write: Writer) -> None:
|
| 23 |
+
# "Since the Host field-value is critical information for handling a
|
| 24 |
+
# request, a user agent SHOULD generate Host as the first header field
|
| 25 |
+
# following the request-line." - RFC 7230
|
| 26 |
+
raw_items = headers._full_items
|
| 27 |
+
for raw_name, name, value in raw_items:
|
| 28 |
+
if name == b"host":
|
| 29 |
+
write(b"%s: %s\r\n" % (raw_name, value))
|
| 30 |
+
for raw_name, name, value in raw_items:
|
| 31 |
+
if name != b"host":
|
| 32 |
+
write(b"%s: %s\r\n" % (raw_name, value))
|
| 33 |
+
write(b"\r\n")
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def write_request(request: Request, write: Writer) -> None:
|
| 37 |
+
if request.http_version != b"1.1":
|
| 38 |
+
raise LocalProtocolError("I only send HTTP/1.1")
|
| 39 |
+
write(b"%s %s HTTP/1.1\r\n" % (request.method, request.target))
|
| 40 |
+
write_headers(request.headers, write)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
# Shared between InformationalResponse and Response
|
| 44 |
+
def write_any_response(
|
| 45 |
+
response: Union[InformationalResponse, Response], write: Writer
|
| 46 |
+
) -> None:
|
| 47 |
+
if response.http_version != b"1.1":
|
| 48 |
+
raise LocalProtocolError("I only send HTTP/1.1")
|
| 49 |
+
status_bytes = str(response.status_code).encode("ascii")
|
| 50 |
+
# We don't bother sending ascii status messages like "OK"; they're
|
| 51 |
+
# optional and ignored by the protocol. (But the space after the numeric
|
| 52 |
+
# status code is mandatory.)
|
| 53 |
+
#
|
| 54 |
+
# XX FIXME: could at least make an effort to pull out the status message
|
| 55 |
+
# from stdlib's http.HTTPStatus table. Or maybe just steal their enums
|
| 56 |
+
# (either by import or copy/paste). We already accept them as status codes
|
| 57 |
+
# since they're of type IntEnum < int.
|
| 58 |
+
write(b"HTTP/1.1 %s %s\r\n" % (status_bytes, response.reason))
|
| 59 |
+
write_headers(response.headers, write)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
class BodyWriter:
|
| 63 |
+
def __call__(self, event: Event, write: Writer) -> None:
|
| 64 |
+
if type(event) is Data:
|
| 65 |
+
self.send_data(event.data, write)
|
| 66 |
+
elif type(event) is EndOfMessage:
|
| 67 |
+
self.send_eom(event.headers, write)
|
| 68 |
+
else: # pragma: no cover
|
| 69 |
+
assert False
|
| 70 |
+
|
| 71 |
+
def send_data(self, data: bytes, write: Writer) -> None:
|
| 72 |
+
pass
|
| 73 |
+
|
| 74 |
+
def send_eom(self, headers: Headers, write: Writer) -> None:
|
| 75 |
+
pass
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
#
|
| 79 |
+
# These are all careful not to do anything to 'data' except call len(data) and
|
| 80 |
+
# write(data). This allows us to transparently pass-through funny objects,
|
| 81 |
+
# like placeholder objects referring to files on disk that will be sent via
|
| 82 |
+
# sendfile(2).
|
| 83 |
+
#
|
| 84 |
+
class ContentLengthWriter(BodyWriter):
|
| 85 |
+
def __init__(self, length: int) -> None:
|
| 86 |
+
self._length = length
|
| 87 |
+
|
| 88 |
+
def send_data(self, data: bytes, write: Writer) -> None:
|
| 89 |
+
self._length -= len(data)
|
| 90 |
+
if self._length < 0:
|
| 91 |
+
raise LocalProtocolError("Too much data for declared Content-Length")
|
| 92 |
+
write(data)
|
| 93 |
+
|
| 94 |
+
def send_eom(self, headers: Headers, write: Writer) -> None:
|
| 95 |
+
if self._length != 0:
|
| 96 |
+
raise LocalProtocolError("Too little data for declared Content-Length")
|
| 97 |
+
if headers:
|
| 98 |
+
raise LocalProtocolError("Content-Length and trailers don't mix")
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
class ChunkedWriter(BodyWriter):
|
| 102 |
+
def send_data(self, data: bytes, write: Writer) -> None:
|
| 103 |
+
# if we encoded 0-length data in the naive way, it would look like an
|
| 104 |
+
# end-of-message.
|
| 105 |
+
if not data:
|
| 106 |
+
return
|
| 107 |
+
write(b"%x\r\n" % len(data))
|
| 108 |
+
write(data)
|
| 109 |
+
write(b"\r\n")
|
| 110 |
+
|
| 111 |
+
def send_eom(self, headers: Headers, write: Writer) -> None:
|
| 112 |
+
write(b"0\r\n")
|
| 113 |
+
write_headers(headers, write)
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
class Http10Writer(BodyWriter):
|
| 117 |
+
def send_data(self, data: bytes, write: Writer) -> None:
|
| 118 |
+
write(data)
|
| 119 |
+
|
| 120 |
+
def send_eom(self, headers: Headers, write: Writer) -> None:
|
| 121 |
+
if headers:
|
| 122 |
+
raise LocalProtocolError("can't send trailers to HTTP/1.0 client")
|
| 123 |
+
# no need to close the socket ourselves, that will be taken care of by
|
| 124 |
+
# Connection: close machinery
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
WritersType = Dict[
|
| 128 |
+
Union[Tuple[Type[Sentinel], Type[Sentinel]], Type[Sentinel]],
|
| 129 |
+
Union[
|
| 130 |
+
Dict[str, Type[BodyWriter]],
|
| 131 |
+
Callable[[Union[InformationalResponse, Response], Writer], None],
|
| 132 |
+
Callable[[Request, Writer], None],
|
| 133 |
+
],
|
| 134 |
+
]
|
| 135 |
+
|
| 136 |
+
WRITERS: WritersType = {
|
| 137 |
+
(CLIENT, IDLE): write_request,
|
| 138 |
+
(SERVER, IDLE): write_any_response,
|
| 139 |
+
(SERVER, SEND_RESPONSE): write_any_response,
|
| 140 |
+
SEND_BODY: {
|
| 141 |
+
"chunked": ChunkedWriter,
|
| 142 |
+
"content-length": ContentLengthWriter,
|
| 143 |
+
"http/1.0": Http10Writer,
|
| 144 |
+
},
|
| 145 |
+
}
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/h11/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Marker
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/rwkv/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
from typing import TYPE_CHECKING
|
| 15 |
+
|
| 16 |
+
from ...utils import _LazyModule
|
| 17 |
+
from ...utils.import_utils import define_import_structure
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
if TYPE_CHECKING:
|
| 21 |
+
from .configuration_rwkv import *
|
| 22 |
+
from .modeling_rwkv import *
|
| 23 |
+
else:
|
| 24 |
+
import sys
|
| 25 |
+
|
| 26 |
+
_file = globals()["__file__"]
|
| 27 |
+
sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/rwkv/configuration_rwkv.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023 The OpenAI Team Authors and HuggingFace Inc. team.
|
| 2 |
+
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""RWKV configuration"""
|
| 16 |
+
|
| 17 |
+
from huggingface_hub.dataclasses import strict
|
| 18 |
+
|
| 19 |
+
from ...configuration_utils import PreTrainedConfig
|
| 20 |
+
from ...utils import auto_docstring
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@auto_docstring(checkpoint="RWKV/rwkv-4-169m-pile")
|
| 24 |
+
@strict
|
| 25 |
+
class RwkvConfig(PreTrainedConfig):
|
| 26 |
+
r"""
|
| 27 |
+
context_length (`int`, *optional*, defaults to 1024):
|
| 28 |
+
The maximum sequence length that this model can be used with in a single forward (using it in RNN mode
|
| 29 |
+
lets use any sequence length).
|
| 30 |
+
attention_hidden_size (`int`, *optional*):
|
| 31 |
+
Dimensionality of the attention hidden states. Will default to `hidden_size` if unset.
|
| 32 |
+
rescale_every (`int`, *optional*, defaults to 6):
|
| 33 |
+
At inference, the hidden states (and weights of the corresponding output layers) are divided by 2 every
|
| 34 |
+
`rescale_every` layer. If set to 0 or a negative number, no rescale is done.
|
| 35 |
+
|
| 36 |
+
Example:
|
| 37 |
+
|
| 38 |
+
```python
|
| 39 |
+
>>> from transformers import RwkvConfig, RwkvModel
|
| 40 |
+
|
| 41 |
+
>>> # Initializing a Rwkv configuration
|
| 42 |
+
>>> configuration = RwkvConfig()
|
| 43 |
+
|
| 44 |
+
>>> # Initializing a model (with random weights) from the configuration
|
| 45 |
+
>>> model = RwkvModel(configuration)
|
| 46 |
+
|
| 47 |
+
>>> # Accessing the model configuration
|
| 48 |
+
>>> configuration = model.config
|
| 49 |
+
```"""
|
| 50 |
+
|
| 51 |
+
model_type = "rwkv"
|
| 52 |
+
attribute_map = {"max_position_embeddings": "context_length"}
|
| 53 |
+
|
| 54 |
+
vocab_size: int = 50277
|
| 55 |
+
context_length: int = 1024
|
| 56 |
+
hidden_size: int = 4096
|
| 57 |
+
num_hidden_layers: int = 32
|
| 58 |
+
attention_hidden_size: int | None = None
|
| 59 |
+
intermediate_size: int | None = None
|
| 60 |
+
layer_norm_epsilon: float = 1e-5
|
| 61 |
+
bos_token_id: int | None = 0
|
| 62 |
+
eos_token_id: int | list[int] | None = 0
|
| 63 |
+
rescale_every: int = 6
|
| 64 |
+
tie_word_embeddings: bool = False
|
| 65 |
+
use_cache: bool = True
|
| 66 |
+
|
| 67 |
+
def __post_init__(self, **kwargs):
|
| 68 |
+
self.attention_hidden_size = (
|
| 69 |
+
self.attention_hidden_size if self.attention_hidden_size is not None else self.hidden_size
|
| 70 |
+
)
|
| 71 |
+
self.intermediate_size = self.intermediate_size if self.intermediate_size is not None else 4 * self.hidden_size
|
| 72 |
+
|
| 73 |
+
super().__post_init__(**kwargs)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
__all__ = ["RwkvConfig"]
|
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/rwkv/modeling_rwkv.py
ADDED
|
@@ -0,0 +1,758 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023 Bo Peng and HuggingFace Inc. team.
|
| 2 |
+
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""PyTorch RWKV model."""
|
| 16 |
+
|
| 17 |
+
import math
|
| 18 |
+
from dataclasses import dataclass
|
| 19 |
+
|
| 20 |
+
import torch
|
| 21 |
+
from torch import nn
|
| 22 |
+
|
| 23 |
+
from ... import initialization as init
|
| 24 |
+
from ...generation import GenerationMixin
|
| 25 |
+
from ...modeling_layers import GradientCheckpointingLayer
|
| 26 |
+
from ...modeling_utils import PreTrainedModel
|
| 27 |
+
from ...utils import (
|
| 28 |
+
ModelOutput,
|
| 29 |
+
auto_docstring,
|
| 30 |
+
is_bitsandbytes_available,
|
| 31 |
+
is_kernels_available,
|
| 32 |
+
is_ninja_available,
|
| 33 |
+
is_torch_cuda_available,
|
| 34 |
+
logging,
|
| 35 |
+
)
|
| 36 |
+
from .configuration_rwkv import RwkvConfig
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
logger = logging.get_logger(__name__)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
rwkv_cuda_kernel = None
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def load_wkv_cuda_kernel(context_length):
|
| 46 |
+
global rwkv_cuda_kernel
|
| 47 |
+
if not is_kernels_available():
|
| 48 |
+
raise ImportError("kernels is not installed, please install it with `pip install kernels`")
|
| 49 |
+
|
| 50 |
+
from ...integrations.hub_kernels import get_kernel
|
| 51 |
+
|
| 52 |
+
rwkv_cuda_kernel = get_kernel("kernels-community/rwkv")
|
| 53 |
+
rwkv_cuda_kernel.max_seq_length = context_length
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class RwkvLinearAttention(torch.autograd.Function):
|
| 57 |
+
@staticmethod
|
| 58 |
+
def forward(ctx, time_decay, time_first, key, value, state=None, return_state=False):
|
| 59 |
+
batch_size, seq_len, hidden_size = key.size()
|
| 60 |
+
if seq_len > rwkv_cuda_kernel.max_seq_length:
|
| 61 |
+
raise ValueError(
|
| 62 |
+
f"Cannot process a batch with {seq_len} tokens at the same time, use a maximum of "
|
| 63 |
+
f"{rwkv_cuda_kernel.max_seq_length} with this model."
|
| 64 |
+
)
|
| 65 |
+
if batch_size * hidden_size % min(hidden_size, 32) != 0:
|
| 66 |
+
raise ValueError(
|
| 67 |
+
f"The product of batch size ({batch_size}) and hidden size ({hidden_size}) needs to be a round "
|
| 68 |
+
f"multiple of {min(hidden_size, 32)}."
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
ctx.input_dtype = key.dtype
|
| 72 |
+
|
| 73 |
+
if (
|
| 74 |
+
time_decay.device.type != "cuda"
|
| 75 |
+
or time_first.device.type != "cuda"
|
| 76 |
+
or key.device.type != "cuda"
|
| 77 |
+
or value.device.type != "cuda"
|
| 78 |
+
):
|
| 79 |
+
raise ValueError("Calling the CUDA kernel for wkv attention requires all tensors to be on CUDA devices.")
|
| 80 |
+
|
| 81 |
+
time_decay = -torch.exp(time_decay.float().contiguous())
|
| 82 |
+
if key.dtype == torch.float16:
|
| 83 |
+
time_first = time_first.float()
|
| 84 |
+
key = key.float()
|
| 85 |
+
value = value.float()
|
| 86 |
+
time_first = time_first.contiguous()
|
| 87 |
+
key = key.contiguous()
|
| 88 |
+
value = value.contiguous()
|
| 89 |
+
# The CUDA kernel will fill this tensor.
|
| 90 |
+
output = torch.empty_like(key, memory_format=torch.contiguous_format)
|
| 91 |
+
if return_state or state is not None:
|
| 92 |
+
if state is None:
|
| 93 |
+
state = torch.zeros(
|
| 94 |
+
batch_size,
|
| 95 |
+
hidden_size,
|
| 96 |
+
3,
|
| 97 |
+
dtype=torch.float32,
|
| 98 |
+
device=key.device,
|
| 99 |
+
memory_format=torch.contiguous_format,
|
| 100 |
+
)
|
| 101 |
+
state[:, :, 2] -= 1e38
|
| 102 |
+
else:
|
| 103 |
+
state = torch.cat([s.unsqueeze(2) for s in state], dim=2).contiguous()
|
| 104 |
+
if key.dtype == torch.bfloat16:
|
| 105 |
+
forward_func = rwkv_cuda_kernel.forward_with_state_bf16
|
| 106 |
+
else:
|
| 107 |
+
forward_func = rwkv_cuda_kernel.forward_with_state
|
| 108 |
+
forward_func(time_decay, time_first, key, value, output, state)
|
| 109 |
+
else:
|
| 110 |
+
forward_func = rwkv_cuda_kernel.forward_bf16 if key.dtype == torch.bfloat16 else rwkv_cuda_kernel.forward
|
| 111 |
+
forward_func(time_decay, time_first, key, value, output)
|
| 112 |
+
|
| 113 |
+
ctx.save_for_backward(time_decay, time_first, key, value, output)
|
| 114 |
+
|
| 115 |
+
if state is not None:
|
| 116 |
+
state = [s.squeeze(2) for s in torch.chunk(state, 3, dim=2)]
|
| 117 |
+
|
| 118 |
+
return output.to(ctx.input_dtype), state
|
| 119 |
+
|
| 120 |
+
@staticmethod
|
| 121 |
+
# g stands for grad
|
| 122 |
+
def backward(ctx, g_output, g_state=None):
|
| 123 |
+
input_dtype = ctx.input_dtype
|
| 124 |
+
|
| 125 |
+
time_decay, time_first, key, value, output = ctx.saved_tensors
|
| 126 |
+
# The CUDA kernel will fill those tensors.
|
| 127 |
+
g_time_decay = torch.empty_like(
|
| 128 |
+
time_decay,
|
| 129 |
+
memory_format=torch.contiguous_format,
|
| 130 |
+
dtype=torch.bfloat16 if input_dtype == torch.bfloat16 else torch.float32,
|
| 131 |
+
)
|
| 132 |
+
g_time_first = torch.empty_like(time_first, memory_format=torch.contiguous_format)
|
| 133 |
+
g_key = torch.empty_like(key, memory_format=torch.contiguous_format)
|
| 134 |
+
g_value = torch.empty_like(value, memory_format=torch.contiguous_format)
|
| 135 |
+
|
| 136 |
+
if input_dtype == torch.float16:
|
| 137 |
+
g_output = g_output.float()
|
| 138 |
+
backward_func = rwkv_cuda_kernel.backward_bf16 if input_dtype == torch.bfloat16 else rwkv_cuda_kernel.backward
|
| 139 |
+
backward_func(
|
| 140 |
+
time_decay,
|
| 141 |
+
time_first,
|
| 142 |
+
key,
|
| 143 |
+
value,
|
| 144 |
+
output,
|
| 145 |
+
g_output.contiguous(),
|
| 146 |
+
g_time_decay,
|
| 147 |
+
g_time_first,
|
| 148 |
+
g_key,
|
| 149 |
+
g_value,
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
return (
|
| 153 |
+
g_time_decay.to(input_dtype),
|
| 154 |
+
g_time_first.to(input_dtype),
|
| 155 |
+
g_key.to(input_dtype),
|
| 156 |
+
g_value.to(input_dtype),
|
| 157 |
+
None,
|
| 158 |
+
None,
|
| 159 |
+
)
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def rwkv_linear_attention_cpu(time_decay, time_first, key, value, state=None, return_state=False):
|
| 163 |
+
# For CPU fallback. Will be slower and probably take more memory than the custom CUDA kernel if not executed
|
| 164 |
+
# within a torch.no_grad.
|
| 165 |
+
_, seq_length, _ = key.size()
|
| 166 |
+
output = torch.zeros_like(key)
|
| 167 |
+
|
| 168 |
+
if state is None:
|
| 169 |
+
num_state = torch.zeros_like(key[:, 0], dtype=torch.float32)
|
| 170 |
+
den_state = torch.zeros_like(key[:, 0], dtype=torch.float32)
|
| 171 |
+
max_state = torch.zeros_like(key[:, 0], dtype=torch.float32) - 1e38
|
| 172 |
+
else:
|
| 173 |
+
num_state, den_state, max_state = state
|
| 174 |
+
# For numerical stability
|
| 175 |
+
# real_numerator_state = num_state * torch.exp(max_state)
|
| 176 |
+
# real_denominator_state = den_state * torch.exp(max_state)
|
| 177 |
+
|
| 178 |
+
time_decay = -torch.exp(time_decay)
|
| 179 |
+
|
| 180 |
+
for current_index in range(seq_length):
|
| 181 |
+
current_key = key[:, current_index].float()
|
| 182 |
+
current_value = value[:, current_index]
|
| 183 |
+
|
| 184 |
+
# wkv computation at time t
|
| 185 |
+
max_for_output = torch.maximum(max_state, current_key + time_first)
|
| 186 |
+
e1 = torch.exp(max_state - max_for_output)
|
| 187 |
+
e2 = torch.exp(current_key + time_first - max_for_output)
|
| 188 |
+
numerator = e1 * num_state + e2 * current_value
|
| 189 |
+
denominator = e1 * den_state + e2
|
| 190 |
+
output[:, current_index] = (numerator / denominator).to(output.dtype)
|
| 191 |
+
|
| 192 |
+
# Update state for next iteration
|
| 193 |
+
max_for_state = torch.maximum(max_state + time_decay, current_key)
|
| 194 |
+
e1 = torch.exp(max_state + time_decay - max_for_state)
|
| 195 |
+
e2 = torch.exp(current_key - max_for_state)
|
| 196 |
+
num_state = e1 * num_state + e2 * current_value
|
| 197 |
+
den_state = e1 * den_state + e2
|
| 198 |
+
max_state = max_for_state
|
| 199 |
+
|
| 200 |
+
if return_state or state is not None:
|
| 201 |
+
state = [num_state, den_state, max_state]
|
| 202 |
+
|
| 203 |
+
return output, state
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def rwkv_linear_attention(time_decay, time_first, key, value, state=None, return_state=False):
|
| 207 |
+
no_cuda = any(t.device.type != "cuda" for t in [time_decay, time_first, key, value])
|
| 208 |
+
# Launching the CUDA kernel for just one token will actually be slower (there is no for loop in the CPU version
|
| 209 |
+
# in this case).
|
| 210 |
+
one_token = key.size(1) == 1
|
| 211 |
+
if rwkv_cuda_kernel is None or no_cuda or one_token:
|
| 212 |
+
return rwkv_linear_attention_cpu(time_decay, time_first, key, value, state=state, return_state=return_state)
|
| 213 |
+
else:
|
| 214 |
+
return RwkvLinearAttention.apply(time_decay, time_first, key, value, state, return_state)
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
class RwkvSelfAttention(nn.Module):
|
| 218 |
+
def __init__(self, config, layer_id=0):
|
| 219 |
+
super().__init__()
|
| 220 |
+
self.config = config
|
| 221 |
+
kernel_loaded = rwkv_cuda_kernel is not None and rwkv_cuda_kernel.max_seq_length == config.context_length
|
| 222 |
+
if is_ninja_available() and is_torch_cuda_available() and not kernel_loaded:
|
| 223 |
+
try:
|
| 224 |
+
load_wkv_cuda_kernel(config.context_length)
|
| 225 |
+
except Exception:
|
| 226 |
+
logger.info("Could not load the custom CUDA kernel for RWKV attention.")
|
| 227 |
+
self.layer_id = layer_id
|
| 228 |
+
hidden_size = config.hidden_size
|
| 229 |
+
attention_hidden_size = (
|
| 230 |
+
config.attention_hidden_size if config.attention_hidden_size is not None else hidden_size
|
| 231 |
+
)
|
| 232 |
+
self.attention_hidden_size = attention_hidden_size
|
| 233 |
+
|
| 234 |
+
self.time_decay = nn.Parameter(torch.empty(attention_hidden_size))
|
| 235 |
+
self.time_first = nn.Parameter(torch.empty(attention_hidden_size))
|
| 236 |
+
|
| 237 |
+
self.time_mix_key = nn.Parameter(torch.empty(1, 1, hidden_size))
|
| 238 |
+
self.time_mix_value = nn.Parameter(torch.empty(1, 1, hidden_size))
|
| 239 |
+
self.time_mix_receptance = nn.Parameter(torch.empty(1, 1, hidden_size))
|
| 240 |
+
|
| 241 |
+
self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
|
| 242 |
+
self.key = nn.Linear(hidden_size, attention_hidden_size, bias=False)
|
| 243 |
+
self.value = nn.Linear(hidden_size, attention_hidden_size, bias=False)
|
| 244 |
+
self.receptance = nn.Linear(hidden_size, attention_hidden_size, bias=False)
|
| 245 |
+
self.output = nn.Linear(attention_hidden_size, hidden_size, bias=False)
|
| 246 |
+
|
| 247 |
+
# TODO: maybe jit, otherwise move inside forward
|
| 248 |
+
def extract_key_value(self, hidden, state=None):
|
| 249 |
+
# Mix hidden with the previous timestep to produce key, value, receptance
|
| 250 |
+
if hidden.size(1) == 1 and state is not None:
|
| 251 |
+
shifted = state[1][:, :, self.layer_id]
|
| 252 |
+
else:
|
| 253 |
+
shifted = self.time_shift(hidden)
|
| 254 |
+
if state is not None:
|
| 255 |
+
shifted[:, 0] = state[1][:, :, self.layer_id]
|
| 256 |
+
key = hidden * self.time_mix_key + shifted * (1 - self.time_mix_key)
|
| 257 |
+
value = hidden * self.time_mix_value + shifted * (1 - self.time_mix_value)
|
| 258 |
+
receptance = hidden * self.time_mix_receptance + shifted * (1 - self.time_mix_receptance)
|
| 259 |
+
|
| 260 |
+
key = self.key(key)
|
| 261 |
+
value = self.value(value)
|
| 262 |
+
receptance = torch.sigmoid(self.receptance(receptance))
|
| 263 |
+
if state is not None:
|
| 264 |
+
state[1][:, :, self.layer_id] = hidden[:, -1]
|
| 265 |
+
return receptance, key, value, state
|
| 266 |
+
|
| 267 |
+
def forward(self, hidden, state=None, use_cache=False):
|
| 268 |
+
receptance, key, value, state = self.extract_key_value(hidden, state=state)
|
| 269 |
+
layer_state = tuple(s[:, :, self.layer_id] for s in state[2:]) if state is not None else None
|
| 270 |
+
rwkv, layer_state = rwkv_linear_attention(
|
| 271 |
+
self.time_decay,
|
| 272 |
+
self.time_first,
|
| 273 |
+
key,
|
| 274 |
+
value,
|
| 275 |
+
state=layer_state,
|
| 276 |
+
return_state=use_cache,
|
| 277 |
+
)
|
| 278 |
+
|
| 279 |
+
if layer_state is not None:
|
| 280 |
+
state[2][:, :, self.layer_id] = layer_state[0]
|
| 281 |
+
state[3][:, :, self.layer_id] = layer_state[1]
|
| 282 |
+
state[4][:, :, self.layer_id] = layer_state[2]
|
| 283 |
+
|
| 284 |
+
return self.output(receptance * rwkv), state
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
class RwkvFeedForward(nn.Module):
|
| 288 |
+
def __init__(self, config, layer_id=0):
|
| 289 |
+
super().__init__()
|
| 290 |
+
self.config = config
|
| 291 |
+
self.layer_id = layer_id
|
| 292 |
+
hidden_size = config.hidden_size
|
| 293 |
+
intermediate_size = (
|
| 294 |
+
config.intermediate_size if config.intermediate_size is not None else 4 * config.hidden_size
|
| 295 |
+
)
|
| 296 |
+
|
| 297 |
+
self.time_shift = nn.ZeroPad2d((0, 0, 1, -1))
|
| 298 |
+
self.time_mix_key = nn.Parameter(torch.empty(1, 1, hidden_size))
|
| 299 |
+
self.time_mix_receptance = nn.Parameter(torch.empty(1, 1, hidden_size))
|
| 300 |
+
|
| 301 |
+
self.key = nn.Linear(hidden_size, intermediate_size, bias=False)
|
| 302 |
+
self.receptance = nn.Linear(hidden_size, hidden_size, bias=False)
|
| 303 |
+
self.value = nn.Linear(intermediate_size, hidden_size, bias=False)
|
| 304 |
+
|
| 305 |
+
def forward(self, hidden, state=None):
|
| 306 |
+
if hidden.size(1) == 1 and state is not None:
|
| 307 |
+
shifted = state[0][:, :, self.layer_id]
|
| 308 |
+
else:
|
| 309 |
+
shifted = self.time_shift(hidden)
|
| 310 |
+
if state is not None:
|
| 311 |
+
shifted[:, 0] = state[0][:, :, self.layer_id]
|
| 312 |
+
key = hidden * self.time_mix_key + shifted * (1 - self.time_mix_key)
|
| 313 |
+
receptance = hidden * self.time_mix_receptance + shifted * (1 - self.time_mix_receptance)
|
| 314 |
+
|
| 315 |
+
key = torch.square(torch.relu(self.key(key)))
|
| 316 |
+
value = self.value(key)
|
| 317 |
+
receptance = torch.sigmoid(self.receptance(receptance))
|
| 318 |
+
|
| 319 |
+
if state is not None:
|
| 320 |
+
state[0][:, :, self.layer_id] = hidden[:, -1]
|
| 321 |
+
|
| 322 |
+
return receptance * value, state
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
class RwkvBlock(GradientCheckpointingLayer):
|
| 326 |
+
def __init__(self, config, layer_id):
|
| 327 |
+
super().__init__()
|
| 328 |
+
self.config = config
|
| 329 |
+
self.layer_id = layer_id
|
| 330 |
+
|
| 331 |
+
if layer_id == 0:
|
| 332 |
+
self.pre_ln = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_epsilon)
|
| 333 |
+
|
| 334 |
+
self.ln1 = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_epsilon)
|
| 335 |
+
self.ln2 = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_epsilon)
|
| 336 |
+
|
| 337 |
+
self.attention = RwkvSelfAttention(config, layer_id)
|
| 338 |
+
self.feed_forward = RwkvFeedForward(config, layer_id)
|
| 339 |
+
|
| 340 |
+
def forward(self, hidden, state=None, use_cache=False, output_attentions=False):
|
| 341 |
+
if self.layer_id == 0:
|
| 342 |
+
hidden = self.pre_ln(hidden)
|
| 343 |
+
|
| 344 |
+
attention, state = self.attention(self.ln1(hidden), state=state, use_cache=use_cache)
|
| 345 |
+
hidden = hidden + attention
|
| 346 |
+
|
| 347 |
+
feed_forward, state = self.feed_forward(self.ln2(hidden), state=state)
|
| 348 |
+
hidden = hidden + feed_forward
|
| 349 |
+
|
| 350 |
+
outputs = (hidden, state)
|
| 351 |
+
if output_attentions:
|
| 352 |
+
outputs += (attention,)
|
| 353 |
+
else:
|
| 354 |
+
outputs += (None,)
|
| 355 |
+
|
| 356 |
+
return outputs
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
@auto_docstring
|
| 360 |
+
class RwkvPreTrainedModel(PreTrainedModel):
|
| 361 |
+
config: RwkvConfig
|
| 362 |
+
base_model_prefix = "rwkv"
|
| 363 |
+
_no_split_modules = ["RwkvBlock"]
|
| 364 |
+
_keep_in_fp32_modules = ["time_decay", "time_first"]
|
| 365 |
+
supports_gradient_checkpointing = True
|
| 366 |
+
_is_stateful = True
|
| 367 |
+
|
| 368 |
+
@torch.no_grad()
|
| 369 |
+
def _init_weights(self, module: nn.Module):
|
| 370 |
+
"""Initialize the weights."""
|
| 371 |
+
if isinstance(module, RwkvSelfAttention):
|
| 372 |
+
layer_id = module.layer_id
|
| 373 |
+
num_hidden_layers = module.config.num_hidden_layers
|
| 374 |
+
hidden_size = module.config.hidden_size
|
| 375 |
+
attention_hidden_size = module.attention_hidden_size
|
| 376 |
+
|
| 377 |
+
ratio_0_to_1 = layer_id / (num_hidden_layers - 1) # 0 to 1
|
| 378 |
+
ratio_1_to_almost0 = 1.0 - (layer_id / num_hidden_layers) # 1 to ~0
|
| 379 |
+
|
| 380 |
+
time_weight = torch.tensor(
|
| 381 |
+
[i / hidden_size for i in range(hidden_size)],
|
| 382 |
+
dtype=module.time_mix_key.dtype,
|
| 383 |
+
device=module.time_mix_key.device,
|
| 384 |
+
)
|
| 385 |
+
time_weight = time_weight[None, None, :]
|
| 386 |
+
|
| 387 |
+
decay_speed = [
|
| 388 |
+
-5 + 8 * (h / (attention_hidden_size - 1)) ** (0.7 + 1.3 * ratio_0_to_1)
|
| 389 |
+
for h in range(attention_hidden_size)
|
| 390 |
+
]
|
| 391 |
+
decay_speed = torch.tensor(decay_speed, dtype=module.time_decay.dtype, device=module.time_decay.device)
|
| 392 |
+
zigzag = (
|
| 393 |
+
torch.tensor(
|
| 394 |
+
[(i + 1) % 3 - 1 for i in range(attention_hidden_size)],
|
| 395 |
+
dtype=module.time_first.dtype,
|
| 396 |
+
device=module.time_first.device,
|
| 397 |
+
)
|
| 398 |
+
* 0.5
|
| 399 |
+
)
|
| 400 |
+
|
| 401 |
+
init.copy_(module.time_decay, decay_speed)
|
| 402 |
+
init.copy_(module.time_first, torch.ones_like(module.time_first * math.log(0.3) + zigzag))
|
| 403 |
+
|
| 404 |
+
init.copy_(module.time_mix_key, torch.pow(time_weight, ratio_1_to_almost0))
|
| 405 |
+
init.copy_(module.time_mix_value, torch.pow(time_weight, ratio_1_to_almost0) + 0.3 * ratio_0_to_1)
|
| 406 |
+
init.copy_(module.time_mix_receptance, torch.pow(time_weight, 0.5 * ratio_1_to_almost0))
|
| 407 |
+
elif isinstance(module, RwkvFeedForward):
|
| 408 |
+
layer_id = module.layer_id
|
| 409 |
+
num_hidden_layers = module.config.num_hidden_layers
|
| 410 |
+
hidden_size = module.config.hidden_size
|
| 411 |
+
|
| 412 |
+
ratio_1_to_almost0 = 1.0 - (layer_id / num_hidden_layers) # 1 to ~0
|
| 413 |
+
|
| 414 |
+
time_weight = torch.tensor(
|
| 415 |
+
[i / hidden_size for i in range(hidden_size)],
|
| 416 |
+
dtype=module.time_mix_key.dtype,
|
| 417 |
+
device=module.time_mix_key.device,
|
| 418 |
+
)
|
| 419 |
+
time_weight = time_weight[None, None, :]
|
| 420 |
+
|
| 421 |
+
init.copy_(module.time_mix_key, torch.pow(time_weight, ratio_1_to_almost0))
|
| 422 |
+
init.copy_(module.time_mix_receptance, torch.pow(time_weight, ratio_1_to_almost0))
|
| 423 |
+
elif isinstance(module, nn.Linear):
|
| 424 |
+
shape = module.weight.shape
|
| 425 |
+
gain = 1.0
|
| 426 |
+
scale = 1.0 # extra scale for gain
|
| 427 |
+
if module.bias is not None:
|
| 428 |
+
init.zeros_(module.bias)
|
| 429 |
+
if shape[0] > shape[1]:
|
| 430 |
+
gain = math.sqrt(shape[0] / shape[1])
|
| 431 |
+
if shape[0] == self.config.vocab_size and shape[1] == self.config.hidden_size: # final projection?
|
| 432 |
+
scale = 0.5
|
| 433 |
+
|
| 434 |
+
gain *= scale
|
| 435 |
+
init.orthogonal_(module.weight, gain=gain)
|
| 436 |
+
elif isinstance(module, nn.Embedding):
|
| 437 |
+
shape = module.weight.shape
|
| 438 |
+
gain = 1e-4 * math.sqrt(max(shape[0], shape[1]))
|
| 439 |
+
init.orthogonal_(module.weight, gain=gain)
|
| 440 |
+
elif isinstance(module, nn.LayerNorm):
|
| 441 |
+
init.ones_(module.weight)
|
| 442 |
+
init.zeros_(module.bias)
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
@auto_docstring(
|
| 446 |
+
custom_intro="""
|
| 447 |
+
Class for the RWKV model outputs.
|
| 448 |
+
"""
|
| 449 |
+
)
|
| 450 |
+
@dataclass
|
| 451 |
+
class RwkvOutput(ModelOutput):
|
| 452 |
+
r"""
|
| 453 |
+
state (list of five `torch.FloatTensor` of shape `(batch_size, hidden_size, num_hidden_layers)`):
|
| 454 |
+
The state of the model at the last time step. Can be used in a forward method with the next `input_ids` to
|
| 455 |
+
avoid providing the old `input_ids`.
|
| 456 |
+
"""
|
| 457 |
+
|
| 458 |
+
last_hidden_state: torch.FloatTensor | None = None
|
| 459 |
+
state: list[torch.FloatTensor] | None = None
|
| 460 |
+
hidden_states: tuple[torch.FloatTensor, ...] | None = None
|
| 461 |
+
attentions: tuple[torch.FloatTensor, ...] | None = None
|
| 462 |
+
|
| 463 |
+
|
| 464 |
+
@auto_docstring(
|
| 465 |
+
custom_intro="""
|
| 466 |
+
Base class for causal language model (or autoregressive) outputs.
|
| 467 |
+
"""
|
| 468 |
+
)
|
| 469 |
+
@dataclass
|
| 470 |
+
class RwkvCausalLMOutput(ModelOutput):
|
| 471 |
+
r"""
|
| 472 |
+
loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
|
| 473 |
+
Language modeling loss (for next-token prediction).
|
| 474 |
+
logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
|
| 475 |
+
Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
|
| 476 |
+
state (list of five `torch.FloatTensor` of shape `(batch_size, hidden_size, num_hidden_layers)`):
|
| 477 |
+
The state of the model at the last time step. Can be used in a forward method with the next `input_ids` to
|
| 478 |
+
avoid providing the old `input_ids`.
|
| 479 |
+
"""
|
| 480 |
+
|
| 481 |
+
loss: torch.FloatTensor | None = None
|
| 482 |
+
logits: torch.FloatTensor | None = None
|
| 483 |
+
state: list[torch.FloatTensor] | None = None
|
| 484 |
+
hidden_states: tuple[torch.FloatTensor, ...] | None = None
|
| 485 |
+
attentions: tuple[torch.FloatTensor, ...] | None = None
|
| 486 |
+
|
| 487 |
+
|
| 488 |
+
@auto_docstring
|
| 489 |
+
class RwkvModel(RwkvPreTrainedModel):
|
| 490 |
+
def __init__(self, config):
|
| 491 |
+
super().__init__(config)
|
| 492 |
+
|
| 493 |
+
self.embeddings = nn.Embedding(config.vocab_size, config.hidden_size)
|
| 494 |
+
self.blocks = nn.ModuleList([RwkvBlock(config, layer_id=idx) for idx in range(config.num_hidden_layers)])
|
| 495 |
+
self.ln_out = nn.LayerNorm(config.hidden_size)
|
| 496 |
+
|
| 497 |
+
self.layers_are_rescaled = False
|
| 498 |
+
|
| 499 |
+
self.gradient_checkpointing = False
|
| 500 |
+
|
| 501 |
+
# Initialize weights and apply final processing
|
| 502 |
+
self.post_init()
|
| 503 |
+
|
| 504 |
+
def get_input_embeddings(self):
|
| 505 |
+
return self.embeddings
|
| 506 |
+
|
| 507 |
+
def set_input_embeddings(self, new_embeddings):
|
| 508 |
+
self.embeddings = new_embeddings
|
| 509 |
+
|
| 510 |
+
@auto_docstring
|
| 511 |
+
def forward(
|
| 512 |
+
self,
|
| 513 |
+
input_ids: torch.LongTensor | None = None,
|
| 514 |
+
attention_mask: torch.LongTensor | None = None,
|
| 515 |
+
inputs_embeds: torch.FloatTensor | None = None,
|
| 516 |
+
state: list[torch.FloatTensor] | None = None,
|
| 517 |
+
use_cache: bool | None = None,
|
| 518 |
+
output_attentions: bool | None = None,
|
| 519 |
+
output_hidden_states: bool | None = None,
|
| 520 |
+
return_dict: bool | None = None,
|
| 521 |
+
**kwargs,
|
| 522 |
+
) -> tuple | RwkvOutput:
|
| 523 |
+
r"""
|
| 524 |
+
input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
|
| 525 |
+
`input_ids_length` = `sequence_length` if `past_key_values` is `None` else
|
| 526 |
+
`past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
|
| 527 |
+
sequence tokens in the vocabulary.
|
| 528 |
+
|
| 529 |
+
If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
|
| 530 |
+
`input_ids`.
|
| 531 |
+
|
| 532 |
+
Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
|
| 533 |
+
[`PreTrainedTokenizer.__call__`] for details.
|
| 534 |
+
|
| 535 |
+
[What are input IDs?](../glossary#input-ids)
|
| 536 |
+
state (tuple of five `torch.FloatTensor` of shape `(batch_size, hidden_size, num_hidden_layers)`, *optional*):
|
| 537 |
+
If passed along, the model uses the previous state in all the blocks (which will give the output for the
|
| 538 |
+
`input_ids` provided as if the model add `state_input_ids + input_ids` as context).
|
| 539 |
+
use_cache (`bool`, *optional*):
|
| 540 |
+
If set to `True`, the last state is returned and can be used to quickly generate the next logits.
|
| 541 |
+
"""
|
| 542 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 543 |
+
output_hidden_states = (
|
| 544 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 545 |
+
)
|
| 546 |
+
use_cache = use_cache if use_cache is not None else (self.config.use_cache if not self.training else False)
|
| 547 |
+
return_dict = return_dict if return_dict is not None else self.config.return_dict
|
| 548 |
+
|
| 549 |
+
if attention_mask is not None:
|
| 550 |
+
logger.warning_once("`attention_mask` was passed, but it is unused in this model.")
|
| 551 |
+
|
| 552 |
+
if self.training == self.layers_are_rescaled:
|
| 553 |
+
self._rescale_layers()
|
| 554 |
+
|
| 555 |
+
if input_ids is not None and inputs_embeds is not None:
|
| 556 |
+
raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
|
| 557 |
+
elif input_ids is None and inputs_embeds is None:
|
| 558 |
+
raise ValueError("You have to specify either input_ids or inputs_embeds")
|
| 559 |
+
|
| 560 |
+
if inputs_embeds is None:
|
| 561 |
+
inputs_embeds = self.embeddings(input_ids)
|
| 562 |
+
|
| 563 |
+
if use_cache and state is None:
|
| 564 |
+
shape = (inputs_embeds.size(0), self.config.hidden_size, self.config.num_hidden_layers)
|
| 565 |
+
state = [
|
| 566 |
+
torch.zeros(
|
| 567 |
+
*shape, dtype=inputs_embeds.dtype if i <= 1 else torch.float32, device=inputs_embeds.device
|
| 568 |
+
)
|
| 569 |
+
for i in range(5)
|
| 570 |
+
]
|
| 571 |
+
state[4] -= 1e30
|
| 572 |
+
|
| 573 |
+
if self.gradient_checkpointing and self.training:
|
| 574 |
+
if use_cache:
|
| 575 |
+
logger.warning_once(
|
| 576 |
+
"`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..."
|
| 577 |
+
)
|
| 578 |
+
use_cache = False
|
| 579 |
+
|
| 580 |
+
hidden_states = inputs_embeds
|
| 581 |
+
|
| 582 |
+
all_self_attentions = () if output_attentions else None
|
| 583 |
+
all_hidden_states = () if output_hidden_states else None
|
| 584 |
+
for idx, block in enumerate(self.blocks):
|
| 585 |
+
hidden_states, state, attentions = block(
|
| 586 |
+
hidden_states, state=state, use_cache=use_cache, output_attentions=output_attentions
|
| 587 |
+
)
|
| 588 |
+
|
| 589 |
+
if (
|
| 590 |
+
self.layers_are_rescaled
|
| 591 |
+
and self.config.rescale_every > 0
|
| 592 |
+
and (idx + 1) % self.config.rescale_every == 0
|
| 593 |
+
):
|
| 594 |
+
hidden_states = hidden_states / 2
|
| 595 |
+
|
| 596 |
+
if output_hidden_states:
|
| 597 |
+
all_hidden_states = all_hidden_states + (hidden_states,)
|
| 598 |
+
|
| 599 |
+
if output_attentions:
|
| 600 |
+
all_self_attentions = all_self_attentions + (attentions,)
|
| 601 |
+
|
| 602 |
+
hidden_states = self.ln_out(hidden_states)
|
| 603 |
+
|
| 604 |
+
if output_hidden_states:
|
| 605 |
+
all_hidden_states = all_hidden_states + (hidden_states,)
|
| 606 |
+
|
| 607 |
+
if not return_dict:
|
| 608 |
+
return tuple(x for x in [hidden_states, state, all_hidden_states, all_self_attentions] if x is not None)
|
| 609 |
+
|
| 610 |
+
return RwkvOutput(
|
| 611 |
+
last_hidden_state=hidden_states,
|
| 612 |
+
state=state,
|
| 613 |
+
hidden_states=all_hidden_states,
|
| 614 |
+
attentions=all_self_attentions,
|
| 615 |
+
)
|
| 616 |
+
|
| 617 |
+
def _rescale_layers(self):
|
| 618 |
+
# Layers should be rescaled for inference only.
|
| 619 |
+
if self.layers_are_rescaled == (not self.training):
|
| 620 |
+
return
|
| 621 |
+
if self.config.rescale_every > 0:
|
| 622 |
+
with torch.no_grad():
|
| 623 |
+
for block_id, block in enumerate(self.blocks):
|
| 624 |
+
if self.training:
|
| 625 |
+
block.attention.output.weight.mul_(2 ** int(block_id // self.config.rescale_every))
|
| 626 |
+
block.feed_forward.value.weight.mul_(2 ** int(block_id // self.config.rescale_every))
|
| 627 |
+
else:
|
| 628 |
+
# Deal with quantization statistics
|
| 629 |
+
if hasattr(block.attention.output.weight, "SCB"):
|
| 630 |
+
block.attention.output.weight.SCB.div_(2 ** int(block_id // self.config.rescale_every))
|
| 631 |
+
block.feed_forward.value.weight.SCB.div_(2 ** int(block_id // self.config.rescale_every))
|
| 632 |
+
elif hasattr(block.attention.output.weight, "quant_state"):
|
| 633 |
+
self._bnb_4bit_dequantize_and_rescale(block.attention.output, block_id)
|
| 634 |
+
self._bnb_4bit_dequantize_and_rescale(block.feed_forward.value, block_id)
|
| 635 |
+
else:
|
| 636 |
+
block.attention.output.weight.div_(2 ** int(block_id // self.config.rescale_every))
|
| 637 |
+
block.feed_forward.value.weight.div_(2 ** int(block_id // self.config.rescale_every))
|
| 638 |
+
|
| 639 |
+
self.layers_are_rescaled = not self.training
|
| 640 |
+
|
| 641 |
+
def _bnb_4bit_dequantize_and_rescale(self, target_layer, block_id):
|
| 642 |
+
r"""
|
| 643 |
+
Perform the dequantization and rescaling of the weights of a given layer. After that operation the layer will
|
| 644 |
+
be quantized again.
|
| 645 |
+
"""
|
| 646 |
+
if not is_bitsandbytes_available():
|
| 647 |
+
raise ImportError("Please install bitsandbytes to use this method.")
|
| 648 |
+
import bitsandbytes as bnb
|
| 649 |
+
|
| 650 |
+
dequant_weights = bnb.functional.dequantize_4bit(target_layer.weight.data, target_layer.weight.quant_state)
|
| 651 |
+
|
| 652 |
+
dequant_weights.div_(2 ** int(block_id // self.config.rescale_every))
|
| 653 |
+
|
| 654 |
+
# re-quantize the model:
|
| 655 |
+
# we need to put it first on CPU then back to the device
|
| 656 |
+
# this will create an overhead :/
|
| 657 |
+
# We set requires_grad=False as we cannot compute gradients on top of 4bit parameters anyway and to avoid
|
| 658 |
+
# bugs with bnb
|
| 659 |
+
quant_weight = bnb.nn.Params4bit(dequant_weights.to("cpu"), requires_grad=False).to(dequant_weights.device)
|
| 660 |
+
setattr(target_layer, "weight", quant_weight)
|
| 661 |
+
|
| 662 |
+
|
| 663 |
+
@auto_docstring(
|
| 664 |
+
custom_intro="""
|
| 665 |
+
The RWKV Model transformer with a language modeling head on top (linear layer with weights tied to the input
|
| 666 |
+
embeddings).
|
| 667 |
+
"""
|
| 668 |
+
)
|
| 669 |
+
class RwkvForCausalLM(RwkvPreTrainedModel, GenerationMixin):
|
| 670 |
+
_tied_weights_keys = {"head.weight": "rwkv.embeddings.weight"}
|
| 671 |
+
|
| 672 |
+
def __init__(self, config):
|
| 673 |
+
super().__init__(config)
|
| 674 |
+
self.rwkv = RwkvModel(config)
|
| 675 |
+
self.head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
|
| 676 |
+
|
| 677 |
+
# Initialize weights and apply final processing
|
| 678 |
+
self.post_init()
|
| 679 |
+
|
| 680 |
+
def get_output_embeddings(self):
|
| 681 |
+
return self.head
|
| 682 |
+
|
| 683 |
+
def set_output_embeddings(self, new_embeddings):
|
| 684 |
+
self.head = new_embeddings
|
| 685 |
+
|
| 686 |
+
@auto_docstring
|
| 687 |
+
def forward(
|
| 688 |
+
self,
|
| 689 |
+
input_ids: torch.LongTensor | None = None,
|
| 690 |
+
attention_mask: torch.LongTensor | None = None,
|
| 691 |
+
inputs_embeds: torch.FloatTensor | None = None,
|
| 692 |
+
state: list[torch.FloatTensor] | None = None,
|
| 693 |
+
labels: torch.LongTensor | None = None,
|
| 694 |
+
use_cache: bool | None = None,
|
| 695 |
+
output_attentions: bool | None = None,
|
| 696 |
+
output_hidden_states: bool | None = None,
|
| 697 |
+
return_dict: bool | None = None,
|
| 698 |
+
logits_to_keep: int | torch.Tensor = 0,
|
| 699 |
+
**kwargs,
|
| 700 |
+
) -> tuple | RwkvCausalLMOutput:
|
| 701 |
+
r"""
|
| 702 |
+
input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
|
| 703 |
+
`input_ids_length` = `sequence_length` if `past_key_values` is `None` else
|
| 704 |
+
`past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
|
| 705 |
+
sequence tokens in the vocabulary.
|
| 706 |
+
|
| 707 |
+
If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
|
| 708 |
+
`input_ids`.
|
| 709 |
+
|
| 710 |
+
Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
|
| 711 |
+
[`PreTrainedTokenizer.__call__`] for details.
|
| 712 |
+
|
| 713 |
+
[What are input IDs?](../glossary#input-ids)
|
| 714 |
+
state (tuple of five `torch.FloatTensor` of shape `(batch_size, hidden_size, num_hidden_layers)`, *optional*):
|
| 715 |
+
If passed along, the model uses the previous state in all the blocks (which will give the output for the
|
| 716 |
+
`input_ids` provided as if the model add `state_input_ids + input_ids` as context).
|
| 717 |
+
labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
|
| 718 |
+
Labels for language modeling. Note that the labels **are shifted** inside the model, i.e. you can set
|
| 719 |
+
`labels = input_ids` Indices are selected in `[-100, 0, ..., config.vocab_size]` All labels set to `-100`
|
| 720 |
+
are ignored (masked), the loss is only computed for labels in `[0, ..., config.vocab_size]`
|
| 721 |
+
use_cache (`bool`, *optional*):
|
| 722 |
+
If set to `True`, the last state is returned and can be used to quickly generate the next logits.
|
| 723 |
+
"""
|
| 724 |
+
return_dict = return_dict if return_dict is not None else self.config.return_dict
|
| 725 |
+
|
| 726 |
+
rwkv_outputs = self.rwkv(
|
| 727 |
+
input_ids,
|
| 728 |
+
inputs_embeds=inputs_embeds,
|
| 729 |
+
state=state,
|
| 730 |
+
use_cache=use_cache,
|
| 731 |
+
output_attentions=output_attentions,
|
| 732 |
+
output_hidden_states=output_hidden_states,
|
| 733 |
+
return_dict=return_dict,
|
| 734 |
+
)
|
| 735 |
+
|
| 736 |
+
hidden_states = rwkv_outputs[0]
|
| 737 |
+
# Only compute necessary logits, and do not upcast them to float if we are not computing the loss
|
| 738 |
+
slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
|
| 739 |
+
logits = self.head(hidden_states[:, slice_indices, :])
|
| 740 |
+
|
| 741 |
+
loss = None
|
| 742 |
+
if labels is not None:
|
| 743 |
+
loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
|
| 744 |
+
|
| 745 |
+
if not return_dict:
|
| 746 |
+
output = (logits,) + rwkv_outputs[1:]
|
| 747 |
+
return ((loss,) + output) if loss is not None else output
|
| 748 |
+
|
| 749 |
+
return RwkvCausalLMOutput(
|
| 750 |
+
loss=loss,
|
| 751 |
+
logits=logits,
|
| 752 |
+
state=rwkv_outputs.state,
|
| 753 |
+
hidden_states=rwkv_outputs.hidden_states,
|
| 754 |
+
attentions=rwkv_outputs.attentions,
|
| 755 |
+
)
|
| 756 |
+
|
| 757 |
+
|
| 758 |
+
__all__ = ["RwkvForCausalLM", "RwkvModel", "RwkvPreTrainedModel"]
|