fastapi2postman 0.1.2__tar.gz → 0.3.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,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 ashhadahsan
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
1
+ MIT License
2
+
3
+ Copyright (c) 2023 ashhadahsan
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
21
  SOFTWARE.
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi2postman
3
+ Version: 0.3.0
4
+ Summary: Turn a FastAPI app into a Postman collection you can run straight away.
5
+ Keywords: fastapi,postman,newman,openapi,api,cli
6
+ Author: Ashhad Ahsan Rehman
7
+ Author-email: Ashhad Ahsan Rehman <ashhadahsan@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Framework :: FastAPI
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Programming Language :: Python :: 3.15
20
+ Classifier: Topic :: Software Development :: Testing
21
+ Classifier: Typing :: Typed
22
+ Requires-Dist: fastapi>=0.105
23
+ Requires-Python: >=3.11
24
+ Project-URL: Source, https://github.com/ashhadahsan/fastapi2postman
25
+ Project-URL: Changelog, https://github.com/ashhadahsan/fastapi2postman/blob/main/CHANGELOG.md
26
+ Description-Content-Type: text/markdown
27
+
28
+ # fastapi2postman
29
+
30
+ [![PyPI](https://img.shields.io/pypi/v/fastapi2postman?cacheSeconds=3600)](https://pypi.org/project/fastapi2postman/)
31
+ [![Python](https://img.shields.io/pypi/pyversions/fastapi2postman?cacheSeconds=3600)](https://pypi.org/project/fastapi2postman/)
32
+ [![CI](https://github.com/ashhadahsan/fastapi2postman/actions/workflows/ci.yml/badge.svg)](https://github.com/ashhadahsan/fastapi2postman/actions/workflows/ci.yml)
33
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ashhadahsan/fastapi2postman/blob/main/LICENSE)
34
+
35
+ **Turn a FastAPI app into a Postman collection you can run straight away.**
36
+
37
+ ```console
38
+ $ pip install fastapi2postman
39
+ $ fastapi2postman --app main.py
40
+ output.json: 25 requests
41
+ ```
42
+
43
+ Import `output.json` into Postman, fill in the collection's variables, and send the login request. The token is saved for every other request.
44
+
45
+ Install it in the same environment as your app, because it imports the app to read its schema. Everything comes from `app.openapi()`, so what shows in your `/docs` shows in the collection.
46
+
47
+ ## What's in the collection
48
+
49
+ | In your app | In Postman |
50
+ | --- | --- |
51
+ | `include_router()` and mounted FastAPI apps | Every route with its full path. Routes with `include_in_schema=False` and mounted apps with `openapi_url=None` stay out |
52
+ | Tags | A folder per tag |
53
+ | A route with several methods | One request per method |
54
+ | Path parameters | Path variables such as `:item_id` with a sample value |
55
+ | Query and header parameters, including those inside `Depends()` | Filled in, with optional ones switched off |
56
+ | Pydantic body models | A sample JSON body that follows your `Field` limits, enums, formats, defaults and declared examples |
57
+ | `Form()` and `File()` | Form fields and file pickers |
58
+ | Docstrings | Request descriptions |
59
+ | Title, version and description | Collection info |
60
+ | `servers`, or `--base-url` | A `{{baseUrl}}` variable |
61
+
62
+ ## Auth
63
+
64
+ Auth is found wherever you declared it: on the route, in a nested `Depends()`, in `Security()`, or in the `dependencies=` of a route, `APIRouter`, `include_router()` or `FastAPI`.
65
+
66
+ | In your app | In Postman |
67
+ | --- | --- |
68
+ | `HTTPBearer` and subclasses, any `OAuth2…` scheme, OpenID Connect | Bearer auth with `{{bearerToken}}` |
69
+ | A dependency with an `authorization` header parameter | Bearer auth with `{{bearerToken}}` |
70
+ | `HTTPBasic` | Basic auth with `{{username}}` and `{{password}}` |
71
+ | `APIKeyHeader`, `APIKeyQuery` | The header or query parameter, set to a variable with the same name |
72
+ | A query parameter, header or form field whose name contains `token`, `api_key`, `secret` or `password` | The same, so `x_token` is sent as `{{x-token}}` |
73
+
74
+ Secret variables start blank, so the file holds no credentials. You fill them in inside Postman.
75
+
76
+ **The token saves itself.** A `POST` route that is your `tokenUrl`, or that returns `access_token` or `accessToken`, gets a script that stores the token in `{{bearerToken}}`. Those requests come first in the collection, so running the whole collection works.
77
+
78
+ **Auth the schema can't show.** Middleware, or a dependency that reads `request.headers`, doesn't appear in OpenAPI. Use `--bearer` to send `{{bearerToken}}` on every request that has no auth of its own.
79
+
80
+ ## Check your routes
81
+
82
+ Two more commands read the routes themselves, so they also see what the schema leaves out: routes hidden from the docs, websockets, and plain Starlette routes.
83
+
84
+ `routes` lists everything the app serves and what protects it, like `flask routes`:
85
+
86
+ ```console
87
+ $ fastapi2postman routes --app main.py
88
+ METHOD PATH AUTH TAGS NOTE
89
+ GET /health - -
90
+ GET /internal - - hidden from the docs
91
+ GET /me HTTPBearer -
92
+ POST /orders - orders
93
+ * /static ? - can't read: StaticFiles
94
+ ```
95
+
96
+ `audit` fails when a route has no visible auth, so a route merged without its `Depends()` turns CI red:
97
+
98
+ ```console
99
+ $ fastapi2postman audit --app main.py --allow "/health" --allow "/static*"
100
+ 2 of 5 routes have no visible auth:
101
+
102
+ GET /internal no auth, hidden from the docs
103
+ POST /orders no auth
104
+
105
+ Protect them, or allow the ones meant to be open, for example --allow "/health" or --allow "GET /public/*".
106
+ ```
107
+
108
+ It exits with 1 when it finds any, and 0 otherwise. `--allow` takes a path or a method and a path, and `*` matches anything, including `/`. An `--allow` that matches no route prints a warning, because it could let a route added later through unnoticed.
109
+
110
+ Auth counts when a route's dependencies declare it, at any depth: a security scheme such as `HTTPBearer`, or a required header, query parameter or cookie named like a credential, such as `x_token` or `session_id`.
111
+
112
+ **`audit` reads what routes declare and doesn't run them.** So:
113
+
114
+ - Auth in middleware, or in a dependency reading `request.headers`, looks like no auth. Allow those routes.
115
+ - A dependency that declares a token but never checks it still counts as auth. So does `auto_error=False`, which `routes` shows as `HTTPBearer (optional)`.
116
+ - Plain Starlette routes and mounted apps such as `StaticFiles` have nothing to read, so they're always flagged until you allow them.
117
+
118
+ ## Pointing it at your app
119
+
120
+ Run it from your project root.
121
+
122
+ | `--app` | Loads |
123
+ | --- | --- |
124
+ | `main.py` | `app` from that file |
125
+ | `main.py:api` | `api` from that file |
126
+ | `src/shop/main.py` | `app` from that file. Relative imports work if the folder has an `__init__.py` |
127
+ | `shop.main` | `app` from that module |
128
+ | `shop.main:api` | `api` from that module |
129
+ | `shop.main:create_app` | The app that this factory function returns |
130
+
131
+ ## Options
132
+
133
+ ```text
134
+ --app APP the app: main.py, main.py:api or package.main:api. The
135
+ name defaults to app, and may be a factory
136
+ --output OUTPUT output file (default: output.json)
137
+ --name NAME collection name (default: the app's title)
138
+ --base-url BASE_URL where the requests go (default: the app's first server,
139
+ else http://localhost:8000)
140
+ --host HOST shorthand for --base-url http://HOST:PORT
141
+ --port PORT shorthand for --base-url http://HOST:PORT
142
+ --bearer send {{bearerToken}} on every request without auth of
143
+ its own: for auth in middleware, or in a dependency
144
+ reading request.headers, which the app's schema can't
145
+ show
146
+ --version show program's version number and exit
147
+ ```
148
+
149
+ ## From Python
150
+
151
+ ```python
152
+ from fastapi2postman import to_postman
153
+
154
+ collection = to_postman(app, name="Shop", base_url="https://api.example.com")
155
+ ```
156
+
157
+ `to_postman` returns the collection as a dict.
158
+
159
+ ## Limits
160
+
161
+ - **Samples are placeholders.** Any `pattern`, and validators that compare fields, can't be read from the schema. Declare `examples=` on the field where a sample gets rejected.
162
+ - **A request holds one auth.** If a route declares several, the first one Postman supports is used. API keys don't count, because they're sent as plain headers or query parameters.
163
+ - **Cookies are left to Postman's cookie jar**, which fills up when you log in. That covers `Cookie()` parameters and `APIKeyCookie`.
164
+ - **The token script reads the top level of a JSON response.** A login returning a bare `token` field or a nested token isn't detected, because a bare `token` could just as well be a reset or invite token.
165
+ - **Only FastAPI apps are followed into mounts.** Other mounted apps have no schema to read.
166
+
167
+ ## Contributing
168
+
169
+ See [CONTRIBUTING.md](https://github.com/ashhadahsan/fastapi2postman/blob/main/CONTRIBUTING.md). Each change is checked by running a generated collection in Newman, Postman's command-line runner, against a live app.
170
+
171
+ ## License
172
+
173
+ MIT. Inspired by [flask2postman](https://github.com/numberly/flask2postman).
@@ -0,0 +1,146 @@
1
+ # fastapi2postman
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/fastapi2postman?cacheSeconds=3600)](https://pypi.org/project/fastapi2postman/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/fastapi2postman?cacheSeconds=3600)](https://pypi.org/project/fastapi2postman/)
5
+ [![CI](https://github.com/ashhadahsan/fastapi2postman/actions/workflows/ci.yml/badge.svg)](https://github.com/ashhadahsan/fastapi2postman/actions/workflows/ci.yml)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ashhadahsan/fastapi2postman/blob/main/LICENSE)
7
+
8
+ **Turn a FastAPI app into a Postman collection you can run straight away.**
9
+
10
+ ```console
11
+ $ pip install fastapi2postman
12
+ $ fastapi2postman --app main.py
13
+ output.json: 25 requests
14
+ ```
15
+
16
+ Import `output.json` into Postman, fill in the collection's variables, and send the login request. The token is saved for every other request.
17
+
18
+ Install it in the same environment as your app, because it imports the app to read its schema. Everything comes from `app.openapi()`, so what shows in your `/docs` shows in the collection.
19
+
20
+ ## What's in the collection
21
+
22
+ | In your app | In Postman |
23
+ | --- | --- |
24
+ | `include_router()` and mounted FastAPI apps | Every route with its full path. Routes with `include_in_schema=False` and mounted apps with `openapi_url=None` stay out |
25
+ | Tags | A folder per tag |
26
+ | A route with several methods | One request per method |
27
+ | Path parameters | Path variables such as `:item_id` with a sample value |
28
+ | Query and header parameters, including those inside `Depends()` | Filled in, with optional ones switched off |
29
+ | Pydantic body models | A sample JSON body that follows your `Field` limits, enums, formats, defaults and declared examples |
30
+ | `Form()` and `File()` | Form fields and file pickers |
31
+ | Docstrings | Request descriptions |
32
+ | Title, version and description | Collection info |
33
+ | `servers`, or `--base-url` | A `{{baseUrl}}` variable |
34
+
35
+ ## Auth
36
+
37
+ Auth is found wherever you declared it: on the route, in a nested `Depends()`, in `Security()`, or in the `dependencies=` of a route, `APIRouter`, `include_router()` or `FastAPI`.
38
+
39
+ | In your app | In Postman |
40
+ | --- | --- |
41
+ | `HTTPBearer` and subclasses, any `OAuth2…` scheme, OpenID Connect | Bearer auth with `{{bearerToken}}` |
42
+ | A dependency with an `authorization` header parameter | Bearer auth with `{{bearerToken}}` |
43
+ | `HTTPBasic` | Basic auth with `{{username}}` and `{{password}}` |
44
+ | `APIKeyHeader`, `APIKeyQuery` | The header or query parameter, set to a variable with the same name |
45
+ | A query parameter, header or form field whose name contains `token`, `api_key`, `secret` or `password` | The same, so `x_token` is sent as `{{x-token}}` |
46
+
47
+ Secret variables start blank, so the file holds no credentials. You fill them in inside Postman.
48
+
49
+ **The token saves itself.** A `POST` route that is your `tokenUrl`, or that returns `access_token` or `accessToken`, gets a script that stores the token in `{{bearerToken}}`. Those requests come first in the collection, so running the whole collection works.
50
+
51
+ **Auth the schema can't show.** Middleware, or a dependency that reads `request.headers`, doesn't appear in OpenAPI. Use `--bearer` to send `{{bearerToken}}` on every request that has no auth of its own.
52
+
53
+ ## Check your routes
54
+
55
+ Two more commands read the routes themselves, so they also see what the schema leaves out: routes hidden from the docs, websockets, and plain Starlette routes.
56
+
57
+ `routes` lists everything the app serves and what protects it, like `flask routes`:
58
+
59
+ ```console
60
+ $ fastapi2postman routes --app main.py
61
+ METHOD PATH AUTH TAGS NOTE
62
+ GET /health - -
63
+ GET /internal - - hidden from the docs
64
+ GET /me HTTPBearer -
65
+ POST /orders - orders
66
+ * /static ? - can't read: StaticFiles
67
+ ```
68
+
69
+ `audit` fails when a route has no visible auth, so a route merged without its `Depends()` turns CI red:
70
+
71
+ ```console
72
+ $ fastapi2postman audit --app main.py --allow "/health" --allow "/static*"
73
+ 2 of 5 routes have no visible auth:
74
+
75
+ GET /internal no auth, hidden from the docs
76
+ POST /orders no auth
77
+
78
+ Protect them, or allow the ones meant to be open, for example --allow "/health" or --allow "GET /public/*".
79
+ ```
80
+
81
+ It exits with 1 when it finds any, and 0 otherwise. `--allow` takes a path or a method and a path, and `*` matches anything, including `/`. An `--allow` that matches no route prints a warning, because it could let a route added later through unnoticed.
82
+
83
+ Auth counts when a route's dependencies declare it, at any depth: a security scheme such as `HTTPBearer`, or a required header, query parameter or cookie named like a credential, such as `x_token` or `session_id`.
84
+
85
+ **`audit` reads what routes declare and doesn't run them.** So:
86
+
87
+ - Auth in middleware, or in a dependency reading `request.headers`, looks like no auth. Allow those routes.
88
+ - A dependency that declares a token but never checks it still counts as auth. So does `auto_error=False`, which `routes` shows as `HTTPBearer (optional)`.
89
+ - Plain Starlette routes and mounted apps such as `StaticFiles` have nothing to read, so they're always flagged until you allow them.
90
+
91
+ ## Pointing it at your app
92
+
93
+ Run it from your project root.
94
+
95
+ | `--app` | Loads |
96
+ | --- | --- |
97
+ | `main.py` | `app` from that file |
98
+ | `main.py:api` | `api` from that file |
99
+ | `src/shop/main.py` | `app` from that file. Relative imports work if the folder has an `__init__.py` |
100
+ | `shop.main` | `app` from that module |
101
+ | `shop.main:api` | `api` from that module |
102
+ | `shop.main:create_app` | The app that this factory function returns |
103
+
104
+ ## Options
105
+
106
+ ```text
107
+ --app APP the app: main.py, main.py:api or package.main:api. The
108
+ name defaults to app, and may be a factory
109
+ --output OUTPUT output file (default: output.json)
110
+ --name NAME collection name (default: the app's title)
111
+ --base-url BASE_URL where the requests go (default: the app's first server,
112
+ else http://localhost:8000)
113
+ --host HOST shorthand for --base-url http://HOST:PORT
114
+ --port PORT shorthand for --base-url http://HOST:PORT
115
+ --bearer send {{bearerToken}} on every request without auth of
116
+ its own: for auth in middleware, or in a dependency
117
+ reading request.headers, which the app's schema can't
118
+ show
119
+ --version show program's version number and exit
120
+ ```
121
+
122
+ ## From Python
123
+
124
+ ```python
125
+ from fastapi2postman import to_postman
126
+
127
+ collection = to_postman(app, name="Shop", base_url="https://api.example.com")
128
+ ```
129
+
130
+ `to_postman` returns the collection as a dict.
131
+
132
+ ## Limits
133
+
134
+ - **Samples are placeholders.** Any `pattern`, and validators that compare fields, can't be read from the schema. Declare `examples=` on the field where a sample gets rejected.
135
+ - **A request holds one auth.** If a route declares several, the first one Postman supports is used. API keys don't count, because they're sent as plain headers or query parameters.
136
+ - **Cookies are left to Postman's cookie jar**, which fills up when you log in. That covers `Cookie()` parameters and `APIKeyCookie`.
137
+ - **The token script reads the top level of a JSON response.** A login returning a bare `token` field or a nested token isn't detected, because a bare `token` could just as well be a reset or invite token.
138
+ - **Only FastAPI apps are followed into mounts.** Other mounted apps have no schema to read.
139
+
140
+ ## Contributing
141
+
142
+ See [CONTRIBUTING.md](https://github.com/ashhadahsan/fastapi2postman/blob/main/CONTRIBUTING.md). Each change is checked by running a generated collection in Newman, Postman's command-line runner, against a live app.
143
+
144
+ ## License
145
+
146
+ MIT. Inspired by [flask2postman](https://github.com/numberly/flask2postman).
@@ -0,0 +1,80 @@
1
+ [project]
2
+ name = "fastapi2postman"
3
+ version = "0.3.0"
4
+ description = "Turn a FastAPI app into a Postman collection you can run straight away."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.11"
9
+ dependencies = ["fastapi>=0.105"]
10
+ keywords = [
11
+ "fastapi",
12
+ "postman",
13
+ "newman",
14
+ "openapi",
15
+ "api",
16
+ "cli",
17
+ ]
18
+ classifiers = [
19
+ "Development Status :: 4 - Beta",
20
+ "Environment :: Console",
21
+ "Framework :: FastAPI",
22
+ "Intended Audience :: Developers",
23
+ "Programming Language :: Python :: 3 :: Only",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Programming Language :: Python :: 3.14",
28
+ "Programming Language :: Python :: 3.15",
29
+ "Topic :: Software Development :: Testing",
30
+ "Typing :: Typed",
31
+ ]
32
+
33
+ [[project.authors]]
34
+ name = "Ashhad Ahsan Rehman"
35
+ email = "ashhadahsan@gmail.com"
36
+
37
+ [project.urls]
38
+ Source = "https://github.com/ashhadahsan/fastapi2postman"
39
+ Changelog = "https://github.com/ashhadahsan/fastapi2postman/blob/main/CHANGELOG.md"
40
+
41
+ [project.scripts]
42
+ fastapi2postman = "fastapi2postman.cli:main"
43
+
44
+ [build-system]
45
+ requires = ["uv_build>=0.13.6,<0.14"]
46
+ build-backend = "uv_build"
47
+
48
+ [tool.ruff]
49
+ line-length = 120
50
+
51
+ [tool.ruff.lint]
52
+ select = [
53
+ "E",
54
+ "F",
55
+ "I",
56
+ "UP",
57
+ "B",
58
+ ]
59
+
60
+ [tool.mypy]
61
+ strict = true
62
+ files = ["src"]
63
+
64
+ [tool.pytest.ini_options]
65
+ testpaths = ["tests"]
66
+ filterwarnings = ["ignore:Duplicate Operation ID:UserWarning"]
67
+
68
+ [tool.coverage.run]
69
+ source = ["fastapi2postman"]
70
+
71
+ [dependency-groups]
72
+ dev = [
73
+ "coverage>=7.17.2",
74
+ "jsonschema>=4.26.0",
75
+ "mypy>=2.4.1",
76
+ "pytest>=9.2.0",
77
+ "python-multipart>=0.0.33",
78
+ "ruff>=0.17.3",
79
+ "uvicorn>=0.54.0",
80
+ ]
@@ -0,0 +1,65 @@
1
+ [project]
2
+ name = "fastapi2postman"
3
+ version = "0.3.0"
4
+ description = "Turn a FastAPI app into a Postman collection you can run straight away."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ authors = [{ name = "Ashhad Ahsan Rehman", email = "ashhadahsan@gmail.com" }]
9
+ requires-python = ">=3.11"
10
+ dependencies = ["fastapi>=0.105"]
11
+ keywords = ["fastapi", "postman", "newman", "openapi", "api", "cli"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Environment :: Console",
15
+ "Framework :: FastAPI",
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Programming Language :: Python :: 3.15",
23
+ "Topic :: Software Development :: Testing",
24
+ "Typing :: Typed",
25
+ ]
26
+
27
+ [project.urls]
28
+ Source = "https://github.com/ashhadahsan/fastapi2postman"
29
+ Changelog = "https://github.com/ashhadahsan/fastapi2postman/blob/main/CHANGELOG.md"
30
+
31
+ [project.scripts]
32
+ fastapi2postman = "fastapi2postman.cli:main"
33
+
34
+ [build-system]
35
+ requires = ["uv_build>=0.13.6,<0.14"]
36
+ build-backend = "uv_build"
37
+
38
+ [tool.ruff]
39
+ line-length = 120
40
+
41
+ [tool.ruff.lint]
42
+ select = ["E", "F", "I", "UP", "B"]
43
+
44
+ [tool.mypy]
45
+ strict = true
46
+ files = ["src"]
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = ["tests"]
50
+ # FastAPI warns about every route with several methods; the collection doesn't use operation ids.
51
+ filterwarnings = ["ignore:Duplicate Operation ID:UserWarning"]
52
+
53
+ [tool.coverage.run]
54
+ source = ["fastapi2postman"]
55
+
56
+ [dependency-groups]
57
+ dev = [
58
+ "coverage>=7.17.2",
59
+ "jsonschema>=4.26.0",
60
+ "mypy>=2.4.1",
61
+ "pytest>=9.2.0",
62
+ "python-multipart>=0.0.33",
63
+ "ruff>=0.17.3",
64
+ "uvicorn>=0.54.0",
65
+ ]