browserfetch 0.14.0__tar.gz → 0.15.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: browserfetch
3
- Version: 0.14.0
3
+ Version: 0.15.0
4
4
  Summary: fetch in Python using your browser!
5
5
  Keywords: browser,fetch,python,cookies
6
6
  License: GNU General Public License v3 (GPLv3)
@@ -8,7 +8,7 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
8
8
  Requires-Dist: aiohttp
9
9
  Requires-Dist: pyperclip
10
10
  Requires-Dist: cyclopts
11
- Requires-Python: >=3.11
11
+ Requires-Python: >=3.13
12
12
  Project-URL: Homepage, https://github.com/5j9/browserfetch
13
13
  Description-Content-Type: text/x-rst
14
14
 
@@ -1,11 +1,9 @@
1
- __version__ = '0.14.0'
1
+ __version__ = '0.15.0'
2
2
  __all__ = [
3
3
  'BrowserError',
4
4
  'Response',
5
5
  'evaluate',
6
6
  'fetch',
7
- 'get',
8
- 'post',
9
7
  'start_server',
10
8
  ]
11
9
  import atexit
@@ -23,7 +21,6 @@ from html import escape
23
21
  from json import dumps as _dumps, loads as _loads
24
22
  from logging import getLogger
25
23
  from typing import Any as _Any
26
- from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse
27
24
 
28
25
  from aiohttp import ClientSession, ClientWebSocketResponse
29
26
  from aiohttp.web import (
@@ -260,23 +257,29 @@ async def evaluate(
260
257
  async def fetch(
261
258
  url: str,
262
259
  *,
260
+ method: str | None = None,
263
261
  params: dict | None = None,
264
262
  data: _Any = None,
265
263
  form: dict | None = None,
266
264
  timeout: int | float = 95,
267
- options: dict | None = None,
265
+ headers: dict | None = None,
268
266
  host: str | None = None,
267
+ options: dict | None = None,
269
268
  ) -> Response:
270
269
  """Fetch using browser fetch API available on host.
271
270
 
272
- This refactored version handles the 'data' and 'form' parameters
273
- directly, making it a more unified and powerful fetch function,
274
- similar to Playwright's API.
271
+ This function tries to be similar to Playwright's API.
272
+ (but it's not identical)
275
273
 
276
274
  :param url: the URL of the resource you want to fetch.
277
275
  :param params: parameters to be url-encoded and added to url.
278
276
  :param data: the JSON-serializable body of the request.
277
+ If data is str or bytes, `application/octet-stream` content-type
278
+ header will be set. Otherwise, the object will be encoded and sent as
279
+ `application/json`.
279
280
  :param form: a dict of form data to be url-encoded.
281
+ Passing `form` will automatically set
282
+ `application/x-www-form-urlencoded` header.
280
283
  :param timeout: timeout in seconds (do not add to options).
281
284
  :param options: See https://developer.mozilla.org/en-US/docs/Web/API/fetch
282
285
  :param host: `location.host` of the tab that is supposed to handle this
@@ -288,40 +291,28 @@ async def fetch(
288
291
  assert form is None
289
292
  if isinstance(data, str):
290
293
  body = data.encode()
294
+ content_type = 'application/octet-stream'
291
295
  elif isinstance(data, bytes):
292
296
  body = data
297
+ content_type = 'application/octet-stream'
293
298
  else:
294
299
  body = _jdumps(data).encode()
295
- if options is None:
296
- options = {}
297
- headers = options.setdefault('headers', {})
298
- headers['Content-Type'] = 'application/json'
299
- elif form is not None:
300
- body = urlencode(form).encode()
301
- if options is None:
302
- options = {}
303
- headers = options.setdefault('headers', {})
304
- headers['Content-Type'] = 'application/x-www-form-urlencoded'
300
+ content_type = 'application/json'
305
301
  else:
306
- body = None
307
-
308
- if params is not None:
309
- parsed_url = urlparse(url)
310
- url = urlunparse(
311
- parsed_url._replace(
312
- query=urlencode(
313
- [*parse_qsl(parsed_url.query), *params.items()]
314
- )
315
- )
316
- )
302
+ content_type = body = None
317
303
 
318
304
  d = await _request(
319
305
  host,
320
306
  {
321
307
  'action': 'fetch',
322
308
  'url': url,
309
+ 'method': method,
310
+ 'headers': headers,
323
311
  'options': options,
324
312
  'timeout': timeout,
313
+ 'params': params,
314
+ 'form': form,
315
+ 'content_type': content_type,
325
316
  },
326
317
  body,
327
318
  )
@@ -332,51 +323,6 @@ async def fetch(
332
323
  return Response(**d)
333
324
 
334
325
 
335
- async def get(
336
- url: str,
337
- *,
338
- params: dict | None = None,
339
- options: dict | None = None,
340
- host: str | None = None,
341
- timeout: int | float = 95,
342
- ) -> Response:
343
- """Convenience function for a GET request."""
344
- if options is None:
345
- options = {'method': 'GET'}
346
- else:
347
- options['method'] = 'GET'
348
- return await fetch(
349
- url, options=options, host=host, timeout=timeout, params=params
350
- )
351
-
352
-
353
- async def post(
354
- url: str,
355
- *,
356
- params: dict | None = None,
357
- data: _Any = None,
358
- form: dict | None = None,
359
- timeout: int | float = 95,
360
- options: dict | None = None,
361
- host: str | None = None,
362
- ) -> Response:
363
- """Convenience function for a POST request."""
364
- if options is None:
365
- options = {'method': 'POST'}
366
- else:
367
- options['method'] = 'POST'
368
-
369
- return await fetch(
370
- url,
371
- options=options,
372
- host=host,
373
- timeout=timeout,
374
- params=params,
375
- data=data,
376
- form=form,
377
- )
378
-
379
-
380
326
  app = Application()
381
327
  app.add_routes(routes)
382
328
  app_runner = AppRunner(app)
@@ -7,16 +7,42 @@
7
7
  // @ts-check
8
8
  (async () => {
9
9
  /**
10
- * @param {Uint8Array | null} body
10
+ * @param {Uint8Array | URLSearchParams | null} body
11
11
  * @param {any} req
12
12
  * @returns {Promise<Blob>}
13
13
  */
14
14
  async function doFetch(req, body) {
15
- var returnData, response;
16
- var options = req['options'] || {};
15
+ var returnData, response, headers;
16
+ var url = req.url;
17
+ var options = req.options || {};
17
18
 
18
- if (req['timeout']) {
19
- options.signal = AbortSignal.timeout(req['timeout'] * 1000);
19
+ if (req.method) {
20
+ options.method = req.method;
21
+ }
22
+
23
+ if (req.headers) {
24
+ headers = { ...options.headers, ...req.headers };
25
+ } else {
26
+ headers = options.headers || {};
27
+ }
28
+ options.headers = headers;
29
+
30
+ if (req.params) {
31
+ url = new URL(url);
32
+ for (const [key, value] of Object.entries(req.params)) {
33
+ url.searchParams.set(key, value);
34
+ }
35
+ }
36
+
37
+ if (req.form) {
38
+ body = new URLSearchParams(req.form);
39
+ headers['Content-Type'] = 'application/x-www-form-urlencoded';
40
+ } else if (req.content_type) {
41
+ headers['Content-Type'] = req.content_type;
42
+ }
43
+
44
+ if (req.timeout) {
45
+ options.signal = AbortSignal.timeout(req.timeout * 1000);
20
46
  }
21
47
 
22
48
  if (body !== null) {
@@ -24,9 +50,9 @@
24
50
  }
25
51
 
26
52
  try {
27
- var r = await fetch(req['url'], options);
53
+ var r = await fetch(url, options);
28
54
  returnData = {
29
- 'event_id': req['event_id'],
55
+ 'event_id': req.event_id,
30
56
  'headers': Object.fromEntries([...r.headers]),
31
57
  'ok': r.ok,
32
58
  'redirected': r.redirected,
@@ -38,7 +64,7 @@
38
64
  response = await r.blob();
39
65
  } catch (/**@type {any} */err) {
40
66
  returnData = {
41
- 'event_id': req['event_id'],
67
+ 'event_id': req.event_id,
42
68
  'error': err.toString()
43
69
  };
44
70
  response = "";
@@ -9,13 +9,13 @@ keywords = ["browser", "fetch", "python", "cookies"]
9
9
  classifiers = [
10
10
  "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
11
11
  ]
12
- requires-python = ">=3.11"
12
+ requires-python = ">=3.13"
13
13
  dependencies = [
14
14
  "aiohttp",
15
15
  "pyperclip",
16
16
  "cyclopts",
17
17
  ]
18
- version = "0.14.0"
18
+ version = "0.15.0"
19
19
 
20
20
  [[authors]]
21
21
  name = "5j9"
@@ -76,5 +76,6 @@ module-name = "browserfetch"
76
76
  dev = [
77
77
  "playwright>=1.52.0",
78
78
  "pytest>=8.4.0",
79
+ "pytest-aiohttp>=1.1.0",
79
80
  "pytest-asyncio>=1.0.0",
80
81
  ]
File without changes