pypricecharting 0.0.1__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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,130 @@
1
+ Metadata-Version: 2.4
2
+ Name: pypricecharting
3
+ Version: 0.0.1
4
+ Summary: Python PriceCharting API Client
5
+ Keywords: pricecharting,api,client
6
+ Author: ahcodedev
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.15
13
+ Classifier: Typing :: Typed
14
+ Requires-Dist: httpx2>=2.16.0
15
+ Requires-Dist: pydantic>=2.14.3
16
+ Requires-Python: >=3.15
17
+ Project-URL: Repository, https://github.com/ahcodedev/pypricecharting
18
+ Project-URL: Issues, https://github.com/ahcodedev/pypricecharting/issues
19
+ Project-URL: Documentation, https://ahcodedev.github.io/pypricecharting/
20
+ Description-Content-Type: text/markdown
21
+
22
+ # PyPriceCharting API Client
23
+
24
+ [![CI](https://github.com/ahcodedev/pypricecharting/actions/workflows/python.yml/badge.svg?branch=main)](https://github.com/ahcodedev/pypricecharting/actions/workflows/python.yml)
25
+ [![Docs](https://github.com/ahcodedev/pypricecharting/actions/workflows/docs.yml/badge.svg?branch=main)](https://github.com/ahcodedev/pypricecharting/actions/workflows/docs.yml)
26
+ [![Security](https://github.com/ahcodedev/pypricecharting/actions/workflows/security.yml/badge.svg?branch=main)](https://github.com/ahcodedev/pypricecharting/actions/workflows/security.yml)
27
+ [![gitleaks](https://img.shields.io/badge/protected%20by-gitleaks-blue)](https://github.com/gitleaks/gitleaks)
28
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
29
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
30
+ ![Python](https://img.shields.io/badge/python-3.15-blue)
31
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/ahcodedev/pypricecharting/blob/main/LICENSE)
32
+
33
+ PriceCharting API Client written in Python
34
+
35
+ ## Setup
36
+
37
+ Project supports local development and VSCode Dev container development
38
+
39
+ ### Local Development Installation
40
+
41
+ Note: Project uses `uv` and `just` to manage project configuration and commands
42
+
43
+ * Install `just` - `brew install just`
44
+ * Set up project - `just setup`
45
+ * Installs Homebrew dependencies (`gitleaks`, `just`, `uv`, `zizmor`)
46
+ * Installs `prek` git hooks
47
+ * Syncs project dependencies
48
+
49
+ ### VS Code Dev Container
50
+
51
+ * Install Docker
52
+ * Install VSCode
53
+ * Open project folder in VS Code
54
+ * Reopen in container using Remote Development Container menu
55
+
56
+ ## Project Commands
57
+
58
+ * `just setup` - install tools and git hooks, then sync dependencies
59
+ * `just sync` - sync project dependencies
60
+ * `just build` - build the sdist and wheel into `dist/`
61
+ * `just lint` - type check (`ty`), lint and format (`ruff`), and spellcheck (`codespell`). Fixes files in place
62
+ * `just ci-lint` - same checks as `just lint`, without modifying files
63
+ * `just test` - run unit tests
64
+ * `just prek` - run all pre-commit hooks
65
+ * `just git-leaks` - scan staged changes for secrets
66
+ * `just zizmor` - audit GitHub Actions workflows for security issues
67
+ * `just docs` - serve the docs site locally with live reload
68
+ * `just docs-build` - build the docs site into `site/` in strict mode
69
+ * `uv add {package}` - install a dependency
70
+
71
+ ### Pre-commit Hooks
72
+
73
+ `prek` runs these hooks on commit (configured in `prek.toml`):
74
+
75
+ * Built-in checks: trailing whitespace, end of file, large files, YAML, and JSON
76
+ * `just ci-lint`
77
+ * `just test`
78
+ * `gitleaks` secret scan of staged changes
79
+
80
+ ## CI
81
+
82
+ See [CI.md](https://github.com/ahcodedev/pypricecharting/blob/main/CI.md) for the GitHub Actions workflows and Dependabot setup.
83
+
84
+ ## Documentation
85
+
86
+ The docs site is built with [Zensical](https://zensical.org) from `mkdocs.yml` and the pages in
87
+ `docs/`. API reference pages are generated from docstrings with `mkdocstrings`. The `docs.yml`
88
+ workflow deploys the site to GitHub Pages on every push to `main`.
89
+
90
+ ## Releases
91
+
92
+ Releases are published to GitHub Releases and to [PyPI](https://pypi.org/project/pypricecharting/)
93
+ by the `Release` workflow. See
94
+ [RELEASE.md](https://github.com/ahcodedev/pypricecharting/blob/main/RELEASE.md) for how releases
95
+ and release labels work.
96
+
97
+ ## Usage
98
+
99
+ Both clients have a method for every Prices API and Marketplace API operation.
100
+
101
+ ```python
102
+ from pypricecharting import AsyncPriceCharting, PriceCharting
103
+
104
+ with PriceCharting("<40-character API token>") as client:
105
+ game = client.product(id="6910")
106
+ print(game.product_name, game.cib_price) # prices are in pennies
107
+
108
+ async with AsyncPriceCharting("<40-character API token>") as client:
109
+ game = await client.product(q="earthbound")
110
+ results = await client.products("chrono trigger") # up to 20 matches
111
+ ```
112
+
113
+ Without a token argument, the token is read from `PRICECHARTING_TOKEN`. See the docs site for
114
+ errors and client options.
115
+
116
+ ## Supporting Files
117
+
118
+ Project contains an OpenAPI spec and a Bruno project for easy testing and mocking
119
+
120
+ * `./bruno` - Bruno project
121
+ * `./open_api` - OpenAPI spec
122
+
123
+ ## TODO / Investigate
124
+
125
+ * **Offer details price limits** - `OfferDetailsResponse` reads `price-max` and `price-min`, the
126
+ names in the documentation's key table. If the API sends `max-price` and `min-price` instead,
127
+ `price_max` and `price_min` come back as `None` with no error. Validate `/api/offer-details`
128
+ * **Extra Marketplace keys** - `pending-reason`, `seller-id`, `image-url`, `best-competing-price`,
129
+ `is-best-priced-offer` and `value` are only on `OfferDetailsResponse`. The documentation doesn't
130
+ say which endpoint returns each one. If `/api/offers` returns any of them, add them to `Offer`
@@ -0,0 +1,109 @@
1
+ # PyPriceCharting API Client
2
+
3
+ [![CI](https://github.com/ahcodedev/pypricecharting/actions/workflows/python.yml/badge.svg?branch=main)](https://github.com/ahcodedev/pypricecharting/actions/workflows/python.yml)
4
+ [![Docs](https://github.com/ahcodedev/pypricecharting/actions/workflows/docs.yml/badge.svg?branch=main)](https://github.com/ahcodedev/pypricecharting/actions/workflows/docs.yml)
5
+ [![Security](https://github.com/ahcodedev/pypricecharting/actions/workflows/security.yml/badge.svg?branch=main)](https://github.com/ahcodedev/pypricecharting/actions/workflows/security.yml)
6
+ [![gitleaks](https://img.shields.io/badge/protected%20by-gitleaks-blue)](https://github.com/gitleaks/gitleaks)
7
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
8
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
9
+ ![Python](https://img.shields.io/badge/python-3.15-blue)
10
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/ahcodedev/pypricecharting/blob/main/LICENSE)
11
+
12
+ PriceCharting API Client written in Python
13
+
14
+ ## Setup
15
+
16
+ Project supports local development and VSCode Dev container development
17
+
18
+ ### Local Development Installation
19
+
20
+ Note: Project uses `uv` and `just` to manage project configuration and commands
21
+
22
+ * Install `just` - `brew install just`
23
+ * Set up project - `just setup`
24
+ * Installs Homebrew dependencies (`gitleaks`, `just`, `uv`, `zizmor`)
25
+ * Installs `prek` git hooks
26
+ * Syncs project dependencies
27
+
28
+ ### VS Code Dev Container
29
+
30
+ * Install Docker
31
+ * Install VSCode
32
+ * Open project folder in VS Code
33
+ * Reopen in container using Remote Development Container menu
34
+
35
+ ## Project Commands
36
+
37
+ * `just setup` - install tools and git hooks, then sync dependencies
38
+ * `just sync` - sync project dependencies
39
+ * `just build` - build the sdist and wheel into `dist/`
40
+ * `just lint` - type check (`ty`), lint and format (`ruff`), and spellcheck (`codespell`). Fixes files in place
41
+ * `just ci-lint` - same checks as `just lint`, without modifying files
42
+ * `just test` - run unit tests
43
+ * `just prek` - run all pre-commit hooks
44
+ * `just git-leaks` - scan staged changes for secrets
45
+ * `just zizmor` - audit GitHub Actions workflows for security issues
46
+ * `just docs` - serve the docs site locally with live reload
47
+ * `just docs-build` - build the docs site into `site/` in strict mode
48
+ * `uv add {package}` - install a dependency
49
+
50
+ ### Pre-commit Hooks
51
+
52
+ `prek` runs these hooks on commit (configured in `prek.toml`):
53
+
54
+ * Built-in checks: trailing whitespace, end of file, large files, YAML, and JSON
55
+ * `just ci-lint`
56
+ * `just test`
57
+ * `gitleaks` secret scan of staged changes
58
+
59
+ ## CI
60
+
61
+ See [CI.md](https://github.com/ahcodedev/pypricecharting/blob/main/CI.md) for the GitHub Actions workflows and Dependabot setup.
62
+
63
+ ## Documentation
64
+
65
+ The docs site is built with [Zensical](https://zensical.org) from `mkdocs.yml` and the pages in
66
+ `docs/`. API reference pages are generated from docstrings with `mkdocstrings`. The `docs.yml`
67
+ workflow deploys the site to GitHub Pages on every push to `main`.
68
+
69
+ ## Releases
70
+
71
+ Releases are published to GitHub Releases and to [PyPI](https://pypi.org/project/pypricecharting/)
72
+ by the `Release` workflow. See
73
+ [RELEASE.md](https://github.com/ahcodedev/pypricecharting/blob/main/RELEASE.md) for how releases
74
+ and release labels work.
75
+
76
+ ## Usage
77
+
78
+ Both clients have a method for every Prices API and Marketplace API operation.
79
+
80
+ ```python
81
+ from pypricecharting import AsyncPriceCharting, PriceCharting
82
+
83
+ with PriceCharting("<40-character API token>") as client:
84
+ game = client.product(id="6910")
85
+ print(game.product_name, game.cib_price) # prices are in pennies
86
+
87
+ async with AsyncPriceCharting("<40-character API token>") as client:
88
+ game = await client.product(q="earthbound")
89
+ results = await client.products("chrono trigger") # up to 20 matches
90
+ ```
91
+
92
+ Without a token argument, the token is read from `PRICECHARTING_TOKEN`. See the docs site for
93
+ errors and client options.
94
+
95
+ ## Supporting Files
96
+
97
+ Project contains an OpenAPI spec and a Bruno project for easy testing and mocking
98
+
99
+ * `./bruno` - Bruno project
100
+ * `./open_api` - OpenAPI spec
101
+
102
+ ## TODO / Investigate
103
+
104
+ * **Offer details price limits** - `OfferDetailsResponse` reads `price-max` and `price-min`, the
105
+ names in the documentation's key table. If the API sends `max-price` and `min-price` instead,
106
+ `price_max` and `price_min` come back as `None` with no error. Validate `/api/offer-details`
107
+ * **Extra Marketplace keys** - `pending-reason`, `seller-id`, `image-url`, `best-competing-price`,
108
+ `is-best-priced-offer` and `value` are only on `OfferDetailsResponse`. The documentation doesn't
109
+ say which endpoint returns each one. If `/api/offers` returns any of them, add them to `Offer`
@@ -0,0 +1,99 @@
1
+ [project]
2
+ name = "pypricecharting"
3
+ description = "Python PriceCharting API Client"
4
+ version = "0.0.1"
5
+ requires-python = ">=3.15"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ readme = "README.md"
9
+ keywords = [
10
+ "pricecharting",
11
+ "api",
12
+ "client",
13
+ ]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.15",
19
+ "Typing :: Typed",
20
+ ]
21
+ dependencies = [
22
+ "httpx2>=2.16.0",
23
+ "pydantic>=2.14.3",
24
+ ]
25
+
26
+ [[project.authors]]
27
+ name = "ahcodedev"
28
+
29
+ [project.urls]
30
+ Repository = "https://github.com/ahcodedev/pypricecharting"
31
+ Issues = "https://github.com/ahcodedev/pypricecharting/issues"
32
+ Documentation = "https://ahcodedev.github.io/pypricecharting/"
33
+
34
+ [build-system]
35
+ requires = ["uv_build>=0.13.7,<0.14.0"]
36
+ build-backend = "uv_build"
37
+
38
+ [dependency-groups]
39
+ dev = [
40
+ "codespell>=2.4.3",
41
+ "prek>=0.5.10",
42
+ "pytest",
43
+ "pytest-asyncio>=1.4.1.post1",
44
+ "pytest-cov",
45
+ "ruff>=0.16.0",
46
+ "ty>=0.0.96",
47
+ ]
48
+ docs = [
49
+ "mkdocstrings[python]>=1.0.8",
50
+ "zensical>=0.1.0",
51
+ ]
52
+
53
+ [tool.codespell]
54
+ ignore-words-list = "gameboy"
55
+ skip = "./site"
56
+
57
+ [tool.pytest.ini_options]
58
+ addopts = "--cov=src --cov-report=term-missing --cov-fail-under 98"
59
+ asyncio_mode = "auto"
60
+ asyncio_default_fixture_loop_scope = "function"
61
+
62
+ [tool.ruff]
63
+ line-length = 100
64
+ target-version = "py315"
65
+
66
+ [tool.ruff.lint]
67
+ select = [
68
+ "B",
69
+ "E",
70
+ "F",
71
+ "W",
72
+ "I",
73
+ "N",
74
+ "PT",
75
+ "D",
76
+ "UP",
77
+ "RUF",
78
+ "SIM",
79
+ ]
80
+ ignore = [
81
+ "D100",
82
+ "D104",
83
+ "D211",
84
+ "D203",
85
+ "D213",
86
+ "D400",
87
+ "D401",
88
+ "D415",
89
+ ]
90
+ extend-ignore = ["E501"]
91
+
92
+ [tool.ruff.lint.per-file-ignores]
93
+ "tests/*" = ["D"]
94
+
95
+ [tool.ruff.format]
96
+ quote-style = "double"
97
+
98
+ [tool.uv]
99
+ required-version = ">=0.13,<0.14"
@@ -0,0 +1,77 @@
1
+ [project]
2
+ name = "pypricecharting"
3
+ description = "Python PriceCharting API Client"
4
+ version = "0.0.1"
5
+ requires-python = ">=3.15"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ readme = "README.md"
9
+ authors = [
10
+ { name = "ahcodedev" },
11
+ ]
12
+ keywords = ["pricecharting", "api", "client"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Intended Audience :: Developers",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.15",
18
+ "Typing :: Typed",
19
+ ]
20
+
21
+ dependencies = [
22
+ "httpx2>=2.16.0",
23
+ "pydantic>=2.14.3",
24
+ ]
25
+
26
+ [project.urls]
27
+ Repository = "https://github.com/ahcodedev/pypricecharting"
28
+ Issues = "https://github.com/ahcodedev/pypricecharting/issues"
29
+ Documentation = "https://ahcodedev.github.io/pypricecharting/"
30
+
31
+ [build-system]
32
+ requires = ["uv_build>=0.13.7,<0.14.0"]
33
+ build-backend = "uv_build"
34
+
35
+ [dependency-groups]
36
+ dev = [
37
+ "codespell>=2.4.3",
38
+ "prek>=0.5.10",
39
+ "pytest",
40
+ "pytest-asyncio>=1.4.1.post1",
41
+ "pytest-cov",
42
+ "ruff>=0.16.0",
43
+ "ty>=0.0.96",
44
+ ]
45
+ docs = [
46
+ "mkdocstrings[python]>=1.0.8",
47
+ "zensical>=0.1.0",
48
+ ]
49
+
50
+ [tool.codespell]
51
+ # Product names used in PriceCharting's example data.
52
+ ignore-words-list = "gameboy"
53
+ # MkDocs build output.
54
+ skip = "./site"
55
+
56
+ [tool.pytest.ini_options]
57
+ addopts = "--cov=src --cov-report=term-missing --cov-fail-under 98"
58
+ asyncio_mode = "auto"
59
+ asyncio_default_fixture_loop_scope = "function"
60
+
61
+ [tool.ruff]
62
+ line-length = 100
63
+ target-version = "py315"
64
+
65
+ [tool.ruff.lint]
66
+ select = ["B", "E", "F", "W", "I", "N", "PT", "D", "UP", "RUF", "SIM"] # Choose your rule categories
67
+ ignore = ["D100", "D104", "D211", "D203", "D213", "D400", "D401", "D415"]
68
+ extend-ignore = ["E501"] # Line too long (handled by formatter)
69
+
70
+ [tool.ruff.lint.per-file-ignores]
71
+ "tests/*" = ["D"] # Ignore docstring (D) errors in tests
72
+
73
+ [tool.ruff.format]
74
+ quote-style = "double"
75
+
76
+ [tool.uv]
77
+ required-version = ">=0.13,<0.14"
@@ -0,0 +1,66 @@
1
+ from pypricecharting.asyncpricecharting import AsyncPriceCharting
2
+ from pypricecharting.error import APIError, ParameterError, PriceChartingError
3
+ from pypricecharting.models import Buyer, Offer, PriceChartingModel, ProductSummary
4
+ from pypricecharting.pricecharting import PriceCharting
5
+ from pypricecharting.requests import (
6
+ OfferPublishRequestParams,
7
+ OffersRequestParams,
8
+ OffersSort,
9
+ OffersStatus,
10
+ )
11
+ from pypricecharting.responses import (
12
+ ErrorResponse,
13
+ OfferDetailsResponse,
14
+ OfferEndResponse,
15
+ OfferFeedbackResponse,
16
+ OfferPublishResponse,
17
+ OfferRefundResponse,
18
+ OfferShipResponse,
19
+ OffersResponse,
20
+ ProductResponse,
21
+ ProductsResponse,
22
+ )
23
+ from pypricecharting.types import (
24
+ Condition,
25
+ Console,
26
+ Genre,
27
+ OfferStatus,
28
+ RateLimit,
29
+ Rating,
30
+ State,
31
+ Status,
32
+ )
33
+
34
+ __all__ = [
35
+ "APIError",
36
+ "AsyncPriceCharting",
37
+ "Buyer",
38
+ "Condition",
39
+ "Console",
40
+ "ErrorResponse",
41
+ "Genre",
42
+ "Offer",
43
+ "OfferDetailsResponse",
44
+ "OfferEndResponse",
45
+ "OfferFeedbackResponse",
46
+ "OfferPublishRequestParams",
47
+ "OfferPublishResponse",
48
+ "OfferRefundResponse",
49
+ "OfferShipResponse",
50
+ "OfferStatus",
51
+ "OffersRequestParams",
52
+ "OffersResponse",
53
+ "OffersSort",
54
+ "OffersStatus",
55
+ "ParameterError",
56
+ "PriceCharting",
57
+ "PriceChartingError",
58
+ "PriceChartingModel",
59
+ "ProductResponse",
60
+ "ProductSummary",
61
+ "ProductsResponse",
62
+ "RateLimit",
63
+ "Rating",
64
+ "State",
65
+ "Status",
66
+ ]
@@ -0,0 +1,57 @@
1
+ import logging
2
+ import re
3
+ from collections.abc import Generator
4
+ from urllib.parse import urlencode
5
+
6
+ from httpx2 import Auth, Request, Response
7
+ from pydantic import SecretStr
8
+
9
+ _TOKEN_PARAM = re.compile(r"([?&]t=)[^&\s\"']+")
10
+
11
+
12
+ class _TokenAuth(Auth):
13
+ """Add the PriceCharting token as `t` in the query string (GET) or form body (POST)."""
14
+
15
+ requires_request_body = True
16
+
17
+ def __init__(self, token: SecretStr):
18
+ self._token = token
19
+
20
+ def auth_flow(self, request: Request) -> Generator[Request, Response]:
21
+ """Attach the token to the outgoing request."""
22
+ token = self._token.get_secret_value()
23
+
24
+ if request.method == "GET":
25
+ request.url = request.url.copy_set_param("t", token)
26
+ yield request
27
+ return
28
+
29
+ param = urlencode({"t": token}).encode()
30
+ body = request.content + b"&" + param if request.content else param
31
+ headers = request.headers.copy()
32
+ headers.pop("Content-Length", None) # forces httpx2 to recalculate length
33
+ headers["Content-Type"] = "application/x-www-form-urlencoded"
34
+
35
+ yield Request(
36
+ request.method,
37
+ request.url,
38
+ headers=headers,
39
+ content=body,
40
+ extensions=request.extensions,
41
+ )
42
+
43
+
44
+ class _RedactTokenFilter(logging.Filter):
45
+ """Replace the `t` query parameter in httpx2 log records with `REDACTED`."""
46
+
47
+ def filter(self, record: logging.LogRecord) -> bool:
48
+ """Redact the token in place and always let the record through."""
49
+ message = record.getMessage()
50
+ redacted = _TOKEN_PARAM.sub(r"\1REDACTED", message)
51
+ if redacted != message:
52
+ record.msg = redacted
53
+ record.args = None
54
+ return True
55
+
56
+
57
+ _REDACT_FILTER = _RedactTokenFilter()