bas-http 1.0.0__tar.gz → 1.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,532 @@
1
+ Metadata-Version: 2.4
2
+ Name: bas-http
3
+ Version: 1.0.2
4
+ Summary: Copy-paste your browser's request, bas handles the rest. No impersonation needed. Zero dependencies.
5
+ Author: bas
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://pypi.org/project/bas-http/
8
+ Project-URL: Repository, https://pypi.org/project/bas-http/
9
+ Project-URL: Issues, https://pypi.org/project/bas-http/
10
+ Keywords: http,curl,cookie,scraping,browser,devtools,requests
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Internet :: WWW/HTTP
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.8
24
+ Description-Content-Type: text/markdown
25
+
26
+ # bas
27
+
28
+ Copy-paste your browser's request, bas handles the rest.
29
+
30
+ No impersonation needed. No fingerprint chasing. No broken cookies. Just grab your headers + cookies from DevTools and go.
31
+
32
+ ## Why bas?
33
+
34
+ | Problem | curlcffi | bas |
35
+ |---------|----------|-----|
36
+ | Cookies lost during redirects | **BUG** | **Fixed** |
37
+ | Cookies not accumulating across requests | **BUG** | **Fixed** |
38
+ | Manual Cookie header construction | Yes | **Auto-injected** |
39
+ | Requires curl-impersonate | Yes | **No — pure Python** |
40
+ | Browser fingerprint updates | Constant chase | **None — bring your own headers** |
41
+ | External dependencies | Yes | **Zero** |
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ pip install bas-http
47
+ ```
48
+
49
+ ## Quick Start
50
+
51
+ ### Method 1: Paste a curl command from DevTools (Recommended)
52
+
53
+ This is the fastest way. Copy a curl command from your browser and bas does the rest.
54
+
55
+ ```
56
+ 1. Open browser DevTools (F12) → Network tab
57
+ 2. Make a request on the website
58
+ 3. Right-click the request → "Copy as cURL"
59
+ 4. Paste into Python
60
+ ```
61
+
62
+ ```python
63
+ import bas
64
+
65
+ # Paste your curl command (use r'' raw string to preserve backslashes)
66
+ s = bas.from_curl(r'''curl "https://example.com/page" ^
67
+ -H "accept: text/html,application/xhtml+xml" ^
68
+ -H "accept-language: en-US,en;q=0.9" ^
69
+ -H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" ^
70
+ -b "cf_clearance=abc123; session_id=xyz; token=def456"''')
71
+
72
+ # Now make requests — all headers and cookies are auto-injected
73
+ r = s.get("https://example.com/page")
74
+ print(r.status_code)
75
+ print(r.text)
76
+
77
+ # Follow-up requests keep the same headers and cookies
78
+ r2 = s.get("https://example.com/dashboard")
79
+ print(r2.status_code)
80
+
81
+ # Add more cookies manually if needed
82
+ s.set_cookie("new_cookie", "value", domain="example.com")
83
+ r3 = s.get("https://example.com/api/data")
84
+ ```
85
+
86
+ ### Method 2: Build headers manually
87
+
88
+ If you have headers copied from DevTools (not as a curl command):
89
+
90
+ ```python
91
+ import bas
92
+
93
+ s = bas.Session()
94
+
95
+ # Paste your headers from DevTools → Network → Headers tab
96
+ s.headers = {
97
+ "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
98
+ "accept-language": "en-US,en;q=0.9",
99
+ "cache-control": "max-age=0",
100
+ "sec-ch-ua": '"Chromium";v="137", "Not/A)Brand";v="24"',
101
+ "sec-ch-ua-mobile": "?0",
102
+ "sec-ch-ua-platform": '"Windows"',
103
+ "sec-fetch-dest": "document",
104
+ "sec-fetch-mode": "navigate",
105
+ "sec-fetch-site": "none",
106
+ "sec-fetch-user": "?1",
107
+ "upgrade-insecure-requests": "1",
108
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
109
+ }
110
+
111
+ # Add your cookies from DevTools → Application → Cookies
112
+ s.set_cookie("cf_clearance", "abc123", domain="example.com")
113
+ s.set_cookie("session_id", "xyz", domain="example.com")
114
+ s.set_cookie("csrf_token", "def456", domain="example.com")
115
+
116
+ # Go!
117
+ r = s.get("https://example.com/page")
118
+ print(r.status_code, len(r.text))
119
+ ```
120
+
121
+ ### Method 3: Headers + cookies in one call
122
+
123
+ ```python
124
+ import bas
125
+
126
+ s = bas.from_headers(
127
+ url="https://example.com",
128
+ headers={
129
+ "user-agent": "Mozilla/5.0 ...",
130
+ "accept": "text/html,...",
131
+ },
132
+ cookies={
133
+ "cf_clearance": "abc123",
134
+ "session": "xyz",
135
+ },
136
+ )
137
+
138
+ r = s.get("https://example.com/page")
139
+ ```
140
+
141
+ ## HTTP Methods
142
+
143
+ ```python
144
+ import bas
145
+
146
+ s = bas.from_curl(r'''curl "https://example.com" -b "session=abc"''')
147
+
148
+ # GET
149
+ r = s.get("https://example.com/page")
150
+
151
+ # POST with form data
152
+ r = s.post("https://example.com/login", data={"username": "user", "password": "pass"})
153
+
154
+ # POST with JSON
155
+ r = s.post("https://example.com/api", json={"key": "value"})
156
+
157
+ # PUT
158
+ r = s.put("https://example.com/api/123", json={"name": "updated"})
159
+
160
+ # DELETE
161
+ r = s.delete("https://example.com/api/123")
162
+
163
+ # PATCH
164
+ r = s.patch("https://example.com/api/123", json={"name": "patched"})
165
+
166
+ # HEAD
167
+ r = s.head("https://example.com/page")
168
+ ```
169
+
170
+ ## Response Object
171
+
172
+ ```python
173
+ r = s.get("https://example.com/page")
174
+
175
+ # Status code
176
+ print(r.status_code) # 200
177
+ print(r.ok) # True (status < 400)
178
+ print(r.reason_phrase) # "OK"
179
+
180
+ # Content
181
+ print(r.text) # Decoded text
182
+ print(r.body) # Raw bytes
183
+ print(r.json) # Parsed JSON
184
+ print(r.content_type) # "text/html; charset=utf-8"
185
+
186
+ # Headers
187
+ print(r.headers) # Case-insensitive headers dict
188
+ print(r.headers["content-type"])
189
+
190
+ # URL info
191
+ print(r.url) # Final URL (after redirects)
192
+ print(r.ip) # Server IP
193
+ print(r.http_version) # "1.1" or "2.0"
194
+ print(r.elapsed) # Response time in seconds
195
+
196
+ # Redirect history
197
+ for resp in r.history:
198
+ print(resp.status_code, resp.url)
199
+
200
+ # Raise exception on error
201
+ r.raise_for_status() # Raises HTTPError if status >= 400
202
+
203
+ # Iterate over content
204
+ for chunk in r.iter_content(chunk_size=8192):
205
+ process(chunk)
206
+
207
+ for line in r.iter_lines():
208
+ print(line)
209
+ ```
210
+
211
+ ## Cookie Management
212
+
213
+ ### Auto-injection
214
+
215
+ bas automatically injects cookies into every request. No manual header construction needed.
216
+
217
+ ```python
218
+ import bas
219
+
220
+ s = bas.from_curl(r'''curl "https://example.com" -b "session=abc123; token=xyz"''')
221
+
222
+ # This request automatically has Cookie: session=abc123; token=xyz
223
+ r = s.get("https://example.com/page")
224
+
225
+ # See what cookies were sent
226
+ print(r.request.headers.get("Cookie"))
227
+ # "session=abc123; token=xyz"
228
+ ```
229
+
230
+ ### Set cookies manually
231
+
232
+ ```python
233
+ s = bas.Session()
234
+ s.set_cookie("name", "value", domain="example.com", path="/")
235
+ ```
236
+
237
+ ### Get cookies for a URL
238
+
239
+ ```python
240
+ cookies = s.get_cookies("https://example.com/page")
241
+ print(cookies) # {"session": "abc123", "token": "xyz"}
242
+ ```
243
+
244
+ ### Clear all cookies
245
+
246
+ ```python
247
+ s.clear_cookies()
248
+ ```
249
+
250
+ ### Save cookies to file
251
+
252
+ ```python
253
+ # JSON format
254
+ s.save_cookies("cookies.json")
255
+
256
+ # Netscape format (compatible with curl/wget)
257
+ s.save_cookies("cookies.txt", format="netscape")
258
+ ```
259
+
260
+ ### Load cookies from file
261
+
262
+ ```python
263
+ s.load_cookies("cookies.json")
264
+ s.load_cookies("cookies.txt", format="netscape")
265
+ ```
266
+
267
+ ### Cookie accumulation across requests
268
+
269
+ When the server sends Set-Cookie headers, bas automatically stores them:
270
+
271
+ ```python
272
+ s = bas.Session()
273
+
274
+ # Server sets cookies in this response
275
+ r1 = s.get("https://example.com/login")
276
+ # Set-Cookie: session_id=abc123; Path=/
277
+ # Set-Cookie: user=john; Path=/
278
+
279
+ # These cookies are automatically sent with the next request
280
+ r2 = s.get("https://example.com/dashboard")
281
+ # Cookie: session_id=abc123; user=john
282
+ ```
283
+
284
+ ### Cookies survive redirects
285
+
286
+ ```python
287
+ # Server redirects and sets more cookies
288
+ r = s.get("https://example.com/page")
289
+ # 302 → https://example.com/dashboard
290
+ # Set-Cookie: tracking=xyz; Path=/
291
+
292
+ # ALL cookies are preserved through redirects
293
+ # No cookies lost (unlike curlcffi!)
294
+ ```
295
+
296
+ ## Session Persistence
297
+
298
+ Keep a session alive across multiple script runs:
299
+
300
+ ```python
301
+ import bas
302
+
303
+ s = bas.Session()
304
+
305
+ # Try to load previous session
306
+ try:
307
+ s.load_cookies("my_session.json")
308
+ except FileNotFoundError:
309
+ pass
310
+
311
+ # Make requests
312
+ r = s.get("https://example.com/page")
313
+
314
+ # Save session for next run
315
+ s.save_cookies("my_session.json")
316
+ ```
317
+
318
+ ## Parse curl commands
319
+
320
+ ```python
321
+ from bas.curl_parser import parse_curl, print_curl_summary
322
+
323
+ # See what's in a curl command
324
+ print_curl_summary(r'''curl "https://example.com" -H "User-Agent: ..." -b "cookie=value"''')
325
+
326
+ # Or get it as a dict
327
+ parsed = parse_curl(r'''curl "https://example.com" -H "User-Agent: ..." -b "cookie=value"''')
328
+ print(parsed["method"]) # "GET"
329
+ print(parsed["url"]) # "https://example.com"
330
+ print(parsed["headers"]) # {"User-Agent": "..."}
331
+ print(parsed["cookies"]) # {"cookie": "value"}
332
+ print(parsed["user_agent"]) # "Mozilla/5.0 ..."
333
+ print(parsed["referer"]) # "..."
334
+ ```
335
+
336
+ ## SSL Verification
337
+
338
+ ```python
339
+ # Disable SSL verification (default is enabled)
340
+ s = bas.Session(verify=False)
341
+
342
+ # Or per-request
343
+ r = s.get("https://self-signed.example.com", verify=False)
344
+ ```
345
+
346
+ ## Timeouts
347
+
348
+ ```python
349
+ # Set default timeout (seconds)
350
+ s = bas.Session(timeout=60)
351
+
352
+ # Or per-request
353
+ r = s.get("https://slow.example.com", timeout=120)
354
+ ```
355
+
356
+ ## Redirects
357
+
358
+ ```python
359
+ # Follow redirects (default: True)
360
+ r = s.get("https://example.com/page")
361
+
362
+ # Don't follow redirects
363
+ r = s.get("https://example.com/page", allow_redirects=False)
364
+ print(r.status_code) # 302
365
+ print(r.location) # "https://example.com/dashboard"
366
+
367
+ # Set max redirects
368
+ s = bas.Session(max_redirects=5)
369
+ ```
370
+
371
+ ## Proxy Support
372
+
373
+ ```python
374
+ # TODO: Proxy support coming in v1.1
375
+ ```
376
+
377
+ ## Real-World Example: Web Scraping
378
+
379
+ ```python
380
+ import bas
381
+
382
+ # Step 1: Copy curl from DevTools
383
+ s = bas.from_curl(r'''curl "https://spaceshooter.net/faucet/ltc" ^
384
+ -H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" ^
385
+ -H "accept-language: en-US,en;q=0.9" ^
386
+ -H "sec-ch-ua: \"Chromium\";v=\"137\", \"Not/A)Brand\";v=\"24\"" ^
387
+ -H "sec-ch-ua-mobile: ?0" ^
388
+ -H "sec-ch-ua-platform: \"Windows\"" ^
389
+ -H "sec-fetch-dest: document" ^
390
+ -H "sec-fetch-mode: navigate" ^
391
+ -H "sec-fetch-site: none" ^
392
+ -H "upgrade-insecure-requests: 1" ^
393
+ -H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" ^
394
+ -b "captcha=rscaptcha; cf_clearance=abc123; ci_session=xyz123; uf=def456"''')
395
+
396
+ # Step 2: Make the request
397
+ r = s.get("https://spaceshooter.net/faucet/ltc")
398
+
399
+ # Step 3: Parse the response
400
+ if r.ok:
401
+ print("Success!")
402
+ print(f"Status: {r.status_code}")
403
+ print(f"Content length: {len(r.text)}")
404
+ print(f"Cookies after request: {s.get_cookies('https://spaceshooter.net')}")
405
+ else:
406
+ print(f"Failed: {r.status_code}")
407
+ ```
408
+
409
+ ## Real-World Example: Form Submission
410
+
411
+ ```python
412
+ import bas
413
+
414
+ # Copy the POST request curl from DevTools
415
+ s = bas.from_curl(r'''curl "https://example.com/login" ^
416
+ -H "content-type: application/x-www-form-urlencoded" ^
417
+ -H "user-agent: Mozilla/5.0 ..." ^
418
+ -H "referer: https://example.com/login" ^
419
+ -b "csrf_token=abc123" ^
420
+ --data-raw "username=myuser&password=mypass"''')
421
+
422
+ r = s.post("https://example.com/login", data={
423
+ "username": "myuser",
424
+ "password": "mypass",
425
+ })
426
+
427
+ print(r.status_code)
428
+ ```
429
+
430
+ ## Comparison with curlcffi
431
+
432
+ ### curlcffi (BROKEN)
433
+
434
+ ```python
435
+ from curl_cffi.requests import Session
436
+
437
+ s = Session(impersonate="chrome131")
438
+
439
+ # Cookies lost during redirects!
440
+ r1 = s.get("https://example.com/page") # 302 redirect
441
+ # r1 is missing cookies from Set-Cookie headers!
442
+
443
+ # Cookies not accumulating!
444
+ r2 = s.get("https://example.com/page")
445
+ # r2 has LOST cookies from r1!
446
+ ```
447
+
448
+ ### bas (FIXED)
449
+
450
+ ```python
451
+ import bas
452
+
453
+ s = bas.from_curl(r'''curl "https://example.com" -b "session=abc"''')
454
+
455
+ # Cookies survive redirects!
456
+ r1 = s.get("https://example.com/page") # 302 redirect
457
+ # ALL cookies preserved through redirect
458
+
459
+ # Cookies accumulate!
460
+ r2 = s.get("https://example.com/dashboard")
461
+ # r2 has ALL cookies from r1 + new ones
462
+
463
+ # No manual Cookie header needed!
464
+ # bas auto-injects cookies from the jar
465
+ ```
466
+
467
+ ## API Reference
468
+
469
+ ### `bas.from_curl(curl_cmd, **kwargs)` → Session
470
+
471
+ Create a Session from a curl command. Main entry point.
472
+
473
+ ### `bas.from_headers(url, headers, cookies, **kwargs)` → Session
474
+
475
+ Create a Session from raw headers and cookies.
476
+
477
+ ### `bas.Session(headers, cookies, verify, timeout, allow_redirects, max_redirects)`
478
+
479
+ Pure Python HTTP session. Zero external dependencies.
480
+
481
+ **Methods:**
482
+ - `get(url, **kwargs)` → Response
483
+ - `post(url, **kwargs)` → Response
484
+ - `put(url, **kwargs)` → Response
485
+ - `delete(url, **kwargs)` → Response
486
+ - `patch(url, **kwargs)` → Response
487
+ - `head(url, **kwargs)` → Response
488
+ - `set_cookie(name, value, domain, path)` → None
489
+ - `get_cookies(url)` → dict
490
+ - `clear_cookies()` → None
491
+ - `save_cookies(filepath, format)` → None
492
+ - `load_cookies(filepath, format)` → None
493
+
494
+ ### `bas.Cookie(name, value, domain, path, expires, max_age, secure, http_only, same_site)`
495
+
496
+ Individual cookie object with RFC 6265 compliance.
497
+
498
+ ### `bas.CookieJar`
499
+
500
+ Thread-safe cookie container. Full RFC 6265 domain/path matching.
501
+
502
+ **Methods:**
503
+ - `add(cookie)` → None
504
+ - `remove(cookie)` → None
505
+ - `get(name, domain, path)` → Cookie | None
506
+ - `match(url)` → list[Cookie]
507
+ - `to_header(url)` → str
508
+ - `parse_set_cookie(header, url)` → Cookie
509
+ - `save_json(filepath)` → None
510
+ - `load_json(filepath)` → None
511
+ - `save_netscape(filepath)` → None
512
+ - `load_netscape(filepath)` → None
513
+
514
+ ### `bas.Response`
515
+
516
+ HTTP response object.
517
+
518
+ **Properties:**
519
+ - `status_code` (int): HTTP status code
520
+ - `ok` (bool): True if status < 400
521
+ - `text` (str): Decoded text content
522
+ - `body` (bytes): Raw bytes
523
+ - `json` (Any): Parsed JSON
524
+ - `headers` (Headers): Response headers
525
+ - `url` (str): Final URL
526
+ - `elapsed` (float): Response time
527
+ - `history` (list): Redirect history
528
+ - `cookies` (CookieJar): Cookie jar
529
+
530
+ ## License
531
+
532
+ MIT