FlaskTester 5.1__tar.gz → 6.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.4
2
2
  Name: FlaskTester
3
- Version: 5.1
3
+ Version: 6.0
4
4
  Summary: Pytest fixtures for Flask internal and external authenticated tests
5
5
  Author-email: Fabien Coelho <flask.tester@coelho.net>
6
6
  License-Expression: CC0-1.0
@@ -25,6 +25,7 @@ Requires-Dist: pyright; extra == "dev"
25
25
  Requires-Dist: ruff; extra == "dev"
26
26
  Requires-Dist: coverage; extra == "dev"
27
27
  Requires-Dist: pymarkdownlnt!=0.9.5; extra == "dev"
28
+ Requires-Dist: flake8; extra == "dev"
28
29
  Requires-Dist: types-flask; extra == "dev"
29
30
  Requires-Dist: types-requests; extra == "dev"
30
31
  Requires-Dist: FlaskSimpleAuth>=35.0; extra == "dev"
@@ -7,6 +7,7 @@ pyright
7
7
  ruff
8
8
  coverage
9
9
  pymarkdownlnt!=0.9.5
10
+ flake8
10
11
  types-flask
11
12
  types-requests
12
13
  FlaskSimpleAuth>=35.0
@@ -7,7 +7,8 @@ import os
7
7
  import io
8
8
  import re
9
9
  # Python >= 3.11: Self
10
- from typing import Any, Callable
10
+ from typing import Any
11
+ from collections.abc import Callable
11
12
  import importlib
12
13
  import logging
13
14
  import pytest # for explicit fail calls, see _pytestFail
@@ -90,23 +91,26 @@ class Authenticator:
90
91
  _AUTH_SCHEMES.update(_TOKEN_SCHEMES)
91
92
  _AUTH_SCHEMES.update(_PASS_SCHEMES)
92
93
 
94
+ # should not be mutable
95
+ _AUTH_DEFAULT_SCHEMES = ["bearer", "basic", "param", "none"]
96
+
93
97
  # authenticator login/pass hook
94
98
  _AuthHook = Callable[[str, str|None], None]
95
99
 
96
100
  def __init__(self,
97
- allow: list[str] = ["bearer", "basic", "param", "none"],
98
- # parameter names for "basic" and "param"
99
- user: str = "USER",
100
- pwd: str = "PASS",
101
- # parameter name for "fake"
102
- login: str = "LOGIN",
103
- # 4 token options
104
- bearer: str = "Bearer",
105
- header: str = "Auth",
106
- cookie: str = "auth",
107
- tparam: str = "AUTH",
108
- ptype: str = "data",
109
- ):
101
+ allow: list[str] = _AUTH_DEFAULT_SCHEMES,
102
+ # parameter names for "basic" and "param"
103
+ user: str = "USER",
104
+ pwd: str = "PASS",
105
+ # parameter name for "fake"
106
+ login: str = "LOGIN",
107
+ # 4 token options
108
+ bearer: str = "Bearer",
109
+ header: str = "Auth",
110
+ cookie: str = "auth",
111
+ tparam: str = "AUTH",
112
+ ptype: str = "data",
113
+ ):
110
114
 
111
115
  self._has_pass, self._has_token = False, False
112
116
  for auth in allow:
@@ -139,8 +143,7 @@ class Authenticator:
139
143
  def _set(self, key: str, val: str|None, store: dict[str, str]):
140
144
  """Set a key/value in a directory, with None for delete."""
141
145
  if val is None:
142
- if key in store:
143
- del store[key]
146
+ store.pop(key, None)
144
147
  else:
145
148
  assert isinstance(val, str)
146
149
  store[key] = val
@@ -315,6 +318,7 @@ class Client:
315
318
  AuthHook = Callable[[Any, str, str|None], None] # type: ignore
316
319
 
317
320
  def __init__(self, auth: Authenticator, default_login: str|None = None):
321
+ self._calls = 0
318
322
  self._auth = auth
319
323
  self._cookies: dict[str, dict[str, str]] = {} # login -> name -> value
320
324
  self._default_login = default_login
@@ -357,6 +361,8 @@ class Client:
357
361
  :param **kwargs: More request parameters (headers, data, json…).
358
362
  """
359
363
 
364
+ self._calls += 1
365
+
360
366
  if "login" in kwargs:
361
367
  login = kwargs["login"]
362
368
  del kwargs["login"]
@@ -387,7 +393,7 @@ class Client:
387
393
  elif "model_dump" in val.__dir__() and callable(val.model_dump):
388
394
  # probably pydantic
389
395
  json_param[name] = val.model_dump()
390
- else: # pydantic or standard dataclasses?
396
+ else: # pydantic or standard dataclasses?
391
397
  json_param[name] = dataclasses.asdict(val)
392
398
 
393
399
  # convert data parameters to simple strings
@@ -415,16 +421,14 @@ class Client:
415
421
  res = self._request(method, path, cookies, **kwargs) # type: ignore
416
422
 
417
423
  # check status
418
- if status is not None:
419
- if res.status_code != status: # show error before aborting
420
- # FIXME what if the useful part is at the end?
421
- _pytestFail(f"bad {status} result: {res.status_code} {res.text[:512]}...")
424
+ if status is not None and res.status_code != status: # show error before aborting
425
+ # FIXME what if the useful part is at the end?
426
+ _pytestFail(f"bad {status} result: {res.status_code} {res.text[:512]}...")
422
427
 
423
428
  # check content
424
- if content is not None:
425
- if not re.search(content, res.text, re.DOTALL):
426
- # FIXME what if the useful part is at the end?
427
- _pytestFail(f"cannot find {content} in {res.text[:512]}...")
429
+ if content is not None and not re.search(content, res.text, re.DOTALL):
430
+ # FIXME what if the useful part is at the end?
431
+ _pytestFail(f"cannot find {content} in {res.text[:512]}...")
428
432
 
429
433
  return res
430
434
 
@@ -448,6 +452,12 @@ class Client:
448
452
  """HTTP DELETE request, see `Client.request`."""
449
453
  return self.request("DELETE", path, status=status, content=content, **kwargs)
450
454
 
455
+ def __str__(self):
456
+ return f"client calls: {self._calls}"
457
+
458
+ def __del__(self):
459
+ log.info(f"ending, {self}")
460
+
451
461
 
452
462
  class RequestClient(Client):
453
463
  """Request-based test provider.
@@ -526,6 +536,7 @@ class FlaskClient(Client):
526
536
 
527
537
  return self._client.open(method=method, path=path, **kwargs)
528
538
 
539
+
529
540
  def _ft_authenticator():
530
541
  """Fixture implementation, separated for testing purposes."""
531
542
 
@@ -559,6 +570,7 @@ def _ft_authenticator():
559
570
 
560
571
  return auth
561
572
 
573
+
562
574
  @pytest.fixture
563
575
  def ft_authenticator():
564
576
  """Pytest Fixture: ft_authenticator.
@@ -593,6 +605,7 @@ def ft_authenticator():
593
605
 
594
606
  yield _ft_authenticator()
595
607
 
608
+
596
609
  def _ft_client(authenticator):
597
610
  """Fixture implementation separated for testing."""
598
611
 
@@ -601,7 +614,7 @@ def _ft_client(authenticator):
601
614
 
602
615
  test_app = os.environ.get("FLASK_TESTER_APP", "app")
603
616
 
604
- if test_app.startswith("http://") or test_app.startswith("https://"):
617
+ if test_app.startswith(("http://", "https://")):
605
618
  client = RequestClient(authenticator, test_app, default_login)
606
619
  else:
607
620
  # load app package
@@ -627,6 +640,7 @@ def _ft_client(authenticator):
627
640
 
628
641
  return client
629
642
 
643
+
630
644
  @pytest.fixture
631
645
  def ft_client(ft_authenticator):
632
646
  """Pytest Fixture: ft_client.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FlaskTester
3
- Version: 5.1
3
+ Version: 6.0
4
4
  Summary: Pytest fixtures for Flask internal and external authenticated tests
5
5
  Author-email: Fabien Coelho <flask.tester@coelho.net>
6
6
  License-Expression: CC0-1.0
@@ -25,6 +25,7 @@ Requires-Dist: pyright; extra == "dev"
25
25
  Requires-Dist: ruff; extra == "dev"
26
26
  Requires-Dist: coverage; extra == "dev"
27
27
  Requires-Dist: pymarkdownlnt!=0.9.5; extra == "dev"
28
+ Requires-Dist: flake8; extra == "dev"
28
29
  Requires-Dist: types-flask; extra == "dev"
29
30
  Requires-Dist: types-requests; extra == "dev"
30
31
  Requires-Dist: FlaskSimpleAuth>=35.0; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "FlaskTester"
7
- version = "5.1"
7
+ version = "6.0"
8
8
  authors = [ { name = "Fabien Coelho", email = "flask.tester@coelho.net" } ]
9
9
  description = "Pytest fixtures for Flask internal and external authenticated tests"
10
10
  readme = "README.md"
@@ -33,7 +33,7 @@ package = "https://pypi.org/project/FlaskTester/"
33
33
  # various dev tools
34
34
  dev = [
35
35
  # static checks…
36
- "mypy", "pyright", "ruff", "coverage", "pymarkdownlnt!=0.9.5",
36
+ "mypy", "pyright", "ruff", "coverage", "pymarkdownlnt!=0.9.5", "flake8",
37
37
  # tests
38
38
  "types-flask", "types-requests", "FlaskSimpleAuth>=35.0", "bcrypt", "pydantic"
39
39
  ]
@@ -47,3 +47,7 @@ py-modules = [ "FlaskTester" ]
47
47
  [tool.black]
48
48
  line-length = 100
49
49
  target-version = ["py310"]
50
+
51
+ [tool.ruff.lint]
52
+ # ruff rules are what they are, but they are not always right
53
+ ignore = ["BLE001", "B006", "I001", "SIM114", "RUF012", "PIE790"]
File without changes
File without changes
File without changes
File without changes