+
{% for user in (contributors.values() | list)[:50] %}
{% if user.login not in skip_users %}
diff --git a/scripts/people.py b/scripts/people.py
new file mode 100644
index 000000000..f61fd31c9
--- /dev/null
+++ b/scripts/people.py
@@ -0,0 +1,401 @@
+import logging
+import secrets
+import subprocess
+import time
+from collections import Counter
+from datetime import datetime, timedelta, timezone
+from pathlib import Path
+from typing import Any, Container, Union
+
+import httpx
+import yaml
+from github import Github
+from pydantic import BaseModel, SecretStr
+from pydantic_settings import BaseSettings
+
+github_graphql_url = "https://api.github.com/graphql"
+questions_category_id = "MDE4OkRpc2N1c3Npb25DYXRlZ29yeTMyMDAxNDM0"
+
+discussions_query = """
+query Q($after: String, $category_id: ID) {
+ repository(name: "fastapi", owner: "fastapi") {
+ discussions(first: 100, after: $after, categoryId: $category_id) {
+ edges {
+ cursor
+ node {
+ number
+ author {
+ login
+ avatarUrl
+ url
+ }
+ createdAt
+ comments(first: 50) {
+ totalCount
+ nodes {
+ createdAt
+ author {
+ login
+ avatarUrl
+ url
+ }
+ isAnswer
+ replies(first: 10) {
+ totalCount
+ nodes {
+ createdAt
+ author {
+ login
+ avatarUrl
+ url
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+"""
+
+
+class Author(BaseModel):
+ login: str
+ avatarUrl: str | None = None
+ url: str | None = None
+
+
+class CommentsNode(BaseModel):
+ createdAt: datetime
+ author: Union[Author, None] = None
+
+
+class Replies(BaseModel):
+ totalCount: int
+ nodes: list[CommentsNode]
+
+
+class DiscussionsCommentsNode(CommentsNode):
+ replies: Replies
+
+
+class DiscussionsComments(BaseModel):
+ totalCount: int
+ nodes: list[DiscussionsCommentsNode]
+
+
+class DiscussionsNode(BaseModel):
+ number: int
+ author: Union[Author, None] = None
+ title: str | None = None
+ createdAt: datetime
+ comments: DiscussionsComments
+
+
+class DiscussionsEdge(BaseModel):
+ cursor: str
+ node: DiscussionsNode
+
+
+class Discussions(BaseModel):
+ edges: list[DiscussionsEdge]
+
+
+class DiscussionsRepository(BaseModel):
+ discussions: Discussions
+
+
+class DiscussionsResponseData(BaseModel):
+ repository: DiscussionsRepository
+
+
+class DiscussionsResponse(BaseModel):
+ data: DiscussionsResponseData
+
+
+class Settings(BaseSettings):
+ github_token: SecretStr
+ github_repository: str
+ httpx_timeout: int = 30
+
+
+def get_graphql_response(
+ *,
+ settings: Settings,
+ query: str,
+ after: Union[str, None] = None,
+ category_id: Union[str, None] = None,
+) -> dict[str, Any]:
+ headers = {"Authorization": f"token {settings.github_token.get_secret_value()}"}
+ variables = {"after": after, "category_id": category_id}
+ response = httpx.post(
+ github_graphql_url,
+ headers=headers,
+ timeout=settings.httpx_timeout,
+ json={"query": query, "variables": variables, "operationName": "Q"},
+ )
+ if response.status_code != 200:
+ logging.error(
+ f"Response was not 200, after: {after}, category_id: {category_id}"
+ )
+ logging.error(response.text)
+ raise RuntimeError(response.text)
+ data = response.json()
+ if "errors" in data:
+ logging.error(f"Errors in response, after: {after}, category_id: {category_id}")
+ logging.error(data["errors"])
+ logging.error(response.text)
+ raise RuntimeError(response.text)
+ return data
+
+
+def get_graphql_question_discussion_edges(
+ *,
+ settings: Settings,
+ after: Union[str, None] = None,
+) -> list[DiscussionsEdge]:
+ data = get_graphql_response(
+ settings=settings,
+ query=discussions_query,
+ after=after,
+ category_id=questions_category_id,
+ )
+ graphql_response = DiscussionsResponse.model_validate(data)
+ return graphql_response.data.repository.discussions.edges
+
+
+class DiscussionExpertsResults(BaseModel):
+ commenters: Counter[str]
+ last_month_commenters: Counter[str]
+ three_months_commenters: Counter[str]
+ six_months_commenters: Counter[str]
+ one_year_commenters: Counter[str]
+ authors: dict[str, Author]
+
+
+def get_discussion_nodes(settings: Settings) -> list[DiscussionsNode]:
+ discussion_nodes: list[DiscussionsNode] = []
+ discussion_edges = get_graphql_question_discussion_edges(settings=settings)
+
+ while discussion_edges:
+ for discussion_edge in discussion_edges:
+ discussion_nodes.append(discussion_edge.node)
+ last_edge = discussion_edges[-1]
+ # Handle GitHub secondary rate limits, requests per minute
+ time.sleep(5)
+ discussion_edges = get_graphql_question_discussion_edges(
+ settings=settings, after=last_edge.cursor
+ )
+ return discussion_nodes
+
+
+def get_discussions_experts(
+ discussion_nodes: list[DiscussionsNode],
+) -> DiscussionExpertsResults:
+ commenters = Counter[str]()
+ last_month_commenters = Counter[str]()
+ three_months_commenters = Counter[str]()
+ six_months_commenters = Counter[str]()
+ one_year_commenters = Counter[str]()
+ authors: dict[str, Author] = {}
+
+ now = datetime.now(tz=timezone.utc)
+ one_month_ago = now - timedelta(days=30)
+ three_months_ago = now - timedelta(days=90)
+ six_months_ago = now - timedelta(days=180)
+ one_year_ago = now - timedelta(days=365)
+
+ for discussion in discussion_nodes:
+ discussion_author_name = None
+ if discussion.author:
+ authors[discussion.author.login] = discussion.author
+ discussion_author_name = discussion.author.login
+ discussion_commentors: dict[str, datetime] = {}
+ for comment in discussion.comments.nodes:
+ if comment.author:
+ authors[comment.author.login] = comment.author
+ if comment.author.login != discussion_author_name:
+ author_time = discussion_commentors.get(
+ comment.author.login, comment.createdAt
+ )
+ discussion_commentors[comment.author.login] = max(
+ author_time, comment.createdAt
+ )
+ for reply in comment.replies.nodes:
+ if reply.author:
+ authors[reply.author.login] = reply.author
+ if reply.author.login != discussion_author_name:
+ author_time = discussion_commentors.get(
+ reply.author.login, reply.createdAt
+ )
+ discussion_commentors[reply.author.login] = max(
+ author_time, reply.createdAt
+ )
+ for author_name, author_time in discussion_commentors.items():
+ commenters[author_name] += 1
+ if author_time > one_month_ago:
+ last_month_commenters[author_name] += 1
+ if author_time > three_months_ago:
+ three_months_commenters[author_name] += 1
+ if author_time > six_months_ago:
+ six_months_commenters[author_name] += 1
+ if author_time > one_year_ago:
+ one_year_commenters[author_name] += 1
+ discussion_experts_results = DiscussionExpertsResults(
+ authors=authors,
+ commenters=commenters,
+ last_month_commenters=last_month_commenters,
+ three_months_commenters=three_months_commenters,
+ six_months_commenters=six_months_commenters,
+ one_year_commenters=one_year_commenters,
+ )
+ return discussion_experts_results
+
+
+def get_top_users(
+ *,
+ counter: Counter[str],
+ authors: dict[str, Author],
+ skip_users: Container[str],
+ min_count: int = 2,
+) -> list[dict[str, Any]]:
+ users: list[dict[str, Any]] = []
+ for commenter, count in counter.most_common(50):
+ if commenter in skip_users:
+ continue
+ if count >= min_count:
+ author = authors[commenter]
+ users.append(
+ {
+ "login": commenter,
+ "count": count,
+ "avatarUrl": author.avatarUrl,
+ "url": author.url,
+ }
+ )
+ return users
+
+
+def get_users_to_write(
+ *,
+ counter: Counter[str],
+ authors: dict[str, Author],
+ min_count: int = 2,
+) -> list[dict[str, Any]]:
+ users: dict[str, Any] = {}
+ users_list: list[dict[str, Any]] = []
+ for user, count in counter.most_common(60):
+ if count >= min_count:
+ author = authors[user]
+ user_data = {
+ "login": user,
+ "count": count,
+ "avatarUrl": author.avatarUrl,
+ "url": author.url,
+ }
+ users[user] = user_data
+ users_list.append(user_data)
+ return users_list
+
+
+def update_content(*, content_path: Path, new_content: Any) -> bool:
+ old_content = content_path.read_text(encoding="utf-8")
+
+ new_content = yaml.dump(new_content, sort_keys=False, width=200, allow_unicode=True)
+ if old_content == new_content:
+ logging.info(f"The content hasn't changed for {content_path}")
+ return False
+ content_path.write_text(new_content, encoding="utf-8")
+ logging.info(f"Updated {content_path}")
+ return True
+
+
+def main() -> None:
+ logging.basicConfig(level=logging.INFO)
+ settings = Settings()
+ logging.info(f"Using config: {settings.model_dump_json()}")
+ g = Github(settings.github_token.get_secret_value())
+ repo = g.get_repo(settings.github_repository)
+
+ discussion_nodes = get_discussion_nodes(settings=settings)
+ experts_results = get_discussions_experts(discussion_nodes=discussion_nodes)
+
+ authors = experts_results.authors
+ maintainers_logins = {"tiangolo"}
+ maintainers = []
+ for login in maintainers_logins:
+ user = authors[login]
+ maintainers.append(
+ {
+ "login": login,
+ "answers": experts_results.commenters[login],
+ "avatarUrl": user.avatarUrl,
+ "url": user.url,
+ }
+ )
+
+ experts = get_users_to_write(
+ counter=experts_results.commenters,
+ authors=authors,
+ )
+ last_month_experts = get_users_to_write(
+ counter=experts_results.last_month_commenters,
+ authors=authors,
+ )
+ three_months_experts = get_users_to_write(
+ counter=experts_results.three_months_commenters,
+ authors=authors,
+ )
+ six_months_experts = get_users_to_write(
+ counter=experts_results.six_months_commenters,
+ authors=authors,
+ )
+ one_year_experts = get_users_to_write(
+ counter=experts_results.one_year_commenters,
+ authors=authors,
+ )
+
+ people = {
+ "maintainers": maintainers,
+ "experts": experts,
+ "last_month_experts": last_month_experts,
+ "three_months_experts": three_months_experts,
+ "six_months_experts": six_months_experts,
+ "one_year_experts": one_year_experts,
+ }
+
+ # For local development
+ # people_path = Path("../docs/en/data/people.yml")
+ people_path = Path("./docs/en/data/people.yml")
+
+ updated = update_content(content_path=people_path, new_content=people)
+
+ if not updated:
+ logging.info("The data hasn't changed, finishing.")
+ return
+
+ logging.info("Setting up GitHub Actions git user")
+ subprocess.run(["git", "config", "user.name", "github-actions"], check=True)
+ subprocess.run(
+ ["git", "config", "user.email", "github-actions@github.com"], check=True
+ )
+ branch_name = f"fastapi-people-experts-{secrets.token_hex(4)}"
+ logging.info(f"Creating a new branch {branch_name}")
+ subprocess.run(["git", "checkout", "-b", branch_name], check=True)
+ logging.info("Adding updated file")
+ subprocess.run(["git", "add", str(people_path)], check=True)
+ logging.info("Committing updated file")
+ message = "👥 Update FastAPI People - Experts"
+ subprocess.run(["git", "commit", "-m", message], check=True)
+ logging.info("Pushing branch")
+ subprocess.run(["git", "push", "origin", branch_name], check=True)
+ logging.info("Creating PR")
+ pr = repo.create_pull(title=message, body=message, base="master", head=branch_name)
+ logging.info(f"Created PR: {pr.number}")
+ logging.info("Finished")
+
+
+if __name__ == "__main__":
+ main()
From e925c0ec8e4dd5ad0d2d7082de280678cead8d43 Mon Sep 17 00:00:00 2001
From: github-actions
Date: Tue, 28 Jan 2025 20:35:19 +0000
Subject: [PATCH 006/423] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[skip ci]
---
docs/en/docs/release-notes.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md
index 76333d313..3db7042fb 100644
--- a/docs/en/docs/release-notes.md
+++ b/docs/en/docs/release-notes.md
@@ -16,6 +16,7 @@ hide:
### Internal
+* 🔨 Update FastAPI People Experts script, refactor and optimize data fetching to handle rate limits. PR [#13267](https://github.com/fastapi/fastapi/pull/13267) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4. PR [#13251](https://github.com/fastapi/fastapi/pull/13251) by [@dependabot[bot]](https://github.com/apps/dependabot).
## 0.115.7
From 3da797aeb894d82be24d4a137046b65c2c1029c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?=
Date: Tue, 28 Jan 2025 20:41:08 +0000
Subject: [PATCH 007/423] =?UTF-8?q?=F0=9F=91=A5=20Update=20FastAPI=20Peopl?=
=?UTF-8?q?e=20-=20Experts=20(#13269)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: github-actions
---
docs/en/data/people.yml | 1676 ++++++++++++++-------------------------
1 file changed, 592 insertions(+), 1084 deletions(-)
diff --git a/docs/en/data/people.yml b/docs/en/data/people.yml
index 02d1779e0..112567778 100644
--- a/docs/en/data/people.yml
+++ b/docs/en/data/people.yml
@@ -1,24 +1,35 @@
maintainers:
- login: tiangolo
- answers: 1885
- prs: 577
- avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=740f11212a731f56798f558ceddb0bd07642afa7&v=4
+ answers: 1894
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
url: https://github.com/tiangolo
experts:
+- login: tiangolo
+ count: 1894
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
+ url: https://github.com/tiangolo
+- login: github-actions
+ count: 770
+ avatarUrl: https://avatars.githubusercontent.com/in/15368?v=4
+ url: https://github.com/apps/github-actions
- login: Kludex
- count: 608
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
+ count: 644
+ avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
url: https://github.com/Kludex
-- login: dmontagu
- count: 241
- avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
- url: https://github.com/dmontagu
- login: jgould22
- count: 241
+ count: 250
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
+- login: dmontagu
+ count: 240
+ avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
+ url: https://github.com/dmontagu
+- login: YuriiMotov
+ count: 223
+ avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
+ url: https://github.com/YuriiMotov
- login: Mause
- count: 220
+ count: 219
avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4
url: https://github.com/Mause
- login: ycd
@@ -26,7 +37,7 @@ experts:
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=29682e4b6ac7d5293742ccf818188394b9a82972&v=4
url: https://github.com/ycd
- login: JarroVGIT
- count: 193
+ count: 192
avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
url: https://github.com/JarroVGIT
- login: euri10
@@ -41,78 +52,82 @@ experts:
count: 126
avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4
url: https://github.com/phy25
-- login: YuriiMotov
- count: 104
- avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
- url: https://github.com/YuriiMotov
+- login: JavierSanchezCastro
+ count: 85
+ avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
+ url: https://github.com/JavierSanchezCastro
- login: raphaelauv
count: 83
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
url: https://github.com/raphaelauv
-- login: ArcLightSlavik
- count: 71
- avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4
- url: https://github.com/ArcLightSlavik
- login: ghandic
count: 71
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
url: https://github.com/ghandic
-- login: JavierSanchezCastro
- count: 64
- avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
- url: https://github.com/JavierSanchezCastro
+- login: ArcLightSlavik
+ count: 71
+ avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4
+ url: https://github.com/ArcLightSlavik
+- login: n8sty
+ count: 66
+ avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
+ url: https://github.com/n8sty
- login: falkben
count: 59
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=ad9838e089058c9e5a0bab94c0eec7cc181e0cd0&v=4
url: https://github.com/falkben
-- login: n8sty
- count: 56
- avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
- url: https://github.com/n8sty
- login: acidjunk
count: 50
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
url: https://github.com/acidjunk
-- login: yinziyan1206
- count: 49
- avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
- url: https://github.com/yinziyan1206
- login: sm-Fifteen
count: 49
avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
url: https://github.com/sm-Fifteen
-- login: insomnes
- count: 45
- avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
- url: https://github.com/insomnes
+- login: yinziyan1206
+ count: 49
+ avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
+ url: https://github.com/yinziyan1206
+- login: adriangb
+ count: 46
+ avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
+ url: https://github.com/adriangb
- login: Dustyposa
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
url: https://github.com/Dustyposa
-- login: adriangb
+- login: insomnes
count: 45
- avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
- url: https://github.com/adriangb
-- login: frankie567
- count: 43
- avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=c159fe047727aedecbbeeaa96a1b03ceb9d39add&v=4
- url: https://github.com/frankie567
+ avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
+ url: https://github.com/insomnes
- login: odiseo0
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=241a71f6b7068738b81af3e57f45ffd723538401&v=4
url: https://github.com/odiseo0
+- login: frankie567
+ count: 43
+ avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=c159fe047727aedecbbeeaa96a1b03ceb9d39add&v=4
+ url: https://github.com/frankie567
- login: includeamin
count: 40
avatarUrl: https://avatars.githubusercontent.com/u/11836741?u=8bd5ef7e62fe6a82055e33c4c0e0a7879ff8cfb6&v=4
url: https://github.com/includeamin
+- login: sinisaos
+ count: 39
+ avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4
+ url: https://github.com/sinisaos
- login: chbndrhnns
- count: 38
+ count: 37
avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
url: https://github.com/chbndrhnns
- login: STeveShary
count: 37
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
url: https://github.com/STeveShary
+- login: luzzodev
+ count: 36
+ avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
+ url: https://github.com/luzzodev
- login: krishnardt
count: 35
avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4
@@ -123,7 +138,7 @@ experts:
url: https://github.com/panla
- login: prostomarkeloff
count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=72309cc1f2e04e40fa38b29969cb4e9d3f722e7b&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=6918e39a1224194ba636e897461a02a20126d7ad&v=4
url: https://github.com/prostomarkeloff
- login: hasansezertasan
count: 27
@@ -137,10 +152,6 @@ experts:
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
url: https://github.com/wshayes
-- login: acnebs
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/9054108?v=4
- url: https://github.com/acnebs
- login: SirTelemak
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4
@@ -149,6 +160,10 @@ experts:
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
url: https://github.com/nymous
+- login: acnebs
+ count: 22
+ avatarUrl: https://avatars.githubusercontent.com/u/9054108?v=4
+ url: https://github.com/acnebs
- login: chrisK824
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
@@ -157,30 +172,38 @@ experts:
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=5fe59a56e1f2f9ccd8005d71752a8276f133ae1a&v=4
url: https://github.com/rafsaf
+- login: ebottos94
+ count: 20
+ avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=8b91053b3abe4a9209375e3651e1c1ef192d884b&v=4
+ url: https://github.com/ebottos94
- login: nsidnev
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
url: https://github.com/nsidnev
-- login: ebottos94
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
- url: https://github.com/ebottos94
- login: chris-allnutt
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
url: https://github.com/chris-allnutt
-- login: retnikt
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
- url: https://github.com/retnikt
+- login: estebanx64
+ count: 19
+ avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
+ url: https://github.com/estebanx64
- login: zoliknemet
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/22326718?u=31ba446ac290e23e56eea8e4f0c558aaf0b40779&v=4
url: https://github.com/zoliknemet
-- login: nkhitrov
+- login: retnikt
+ count: 18
+ avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
+ url: https://github.com/retnikt
+- login: sehraramiz
count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/28262306?u=66ee21316275ef356081c2efc4ed7a4572e690dc&v=4
- url: https://github.com/nkhitrov
+ avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
+ url: https://github.com/sehraramiz
+- login: caeser1996
+ count: 17
+ avatarUrl: https://avatars.githubusercontent.com/u/16540232?u=05d2beb8e034d584d0a374b99d8826327bd7f614&v=4
+ url: https://github.com/caeser1996
- login: Hultner
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/2669034?u=115e53df959309898ad8dc9443fbb35fee71df07&v=4
@@ -189,1170 +212,655 @@ experts:
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/1765494?u=5b1ab7c582db4b4016fa31affe977d10af108ad4&v=4
url: https://github.com/harunyasar
-- login: caeser1996
+- login: nkhitrov
count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/16540232?u=05d2beb8e034d584d0a374b99d8826327bd7f614&v=4
- url: https://github.com/caeser1996
+ avatarUrl: https://avatars.githubusercontent.com/u/28262306?u=e19427d8dc296d6950e9c424adacc92d37496fe9&v=4
+ url: https://github.com/nkhitrov
+- login: jonatasoli
+ count: 16
+ avatarUrl: https://avatars.githubusercontent.com/u/26334101?u=071c062d2861d3dd127f6b4a5258cd8ef55d4c50&v=4
+ url: https://github.com/jonatasoli
- login: dstlny
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
url: https://github.com/dstlny
+- login: jorgerpo
+ count: 15
+ avatarUrl: https://avatars.githubusercontent.com/u/12537771?u=7444d20019198e34911082780cc7ad73f2b97cb3&v=4
+ url: https://github.com/jorgerpo
+- login: simondale00
+ count: 15
+ avatarUrl: https://avatars.githubusercontent.com/u/33907262?u=2721fb37014d50daf473267c808aa678ecaefe09&v=4
+ url: https://github.com/simondale00
+- login: ghost
+ count: 15
+ avatarUrl: https://avatars.githubusercontent.com/u/10137?u=b1951d34a583cf12ec0d3b0781ba19be97726318&v=4
+ url: https://github.com/ghost
+- login: abhint
+ count: 15
+ avatarUrl: https://avatars.githubusercontent.com/u/25699289?u=b5d219277b4d001ac26fb8be357fddd88c29d51b&v=4
+ url: https://github.com/abhint
+- login: pythonweb2
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
+ url: https://github.com/pythonweb2
last_month_experts:
+- login: Kludex
+ count: 15
+ avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
+ url: https://github.com/Kludex
- login: YuriiMotov
- count: 29
+ count: 10
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
url: https://github.com/YuriiMotov
-- login: killjoy1221
+- login: sehraramiz
count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4
- url: https://github.com/killjoy1221
-- login: Kludex
- count: 7
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: JavierSanchezCastro
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
- url: https://github.com/JavierSanchezCastro
-- login: hasansezertasan
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: PhysicallyActive
+ avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
+ url: https://github.com/sehraramiz
+- login: luzzodev
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
+ url: https://github.com/luzzodev
+- login: yokwejuste
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/71908316?u=592c1e42aa0ee5cb94890e0b863e2acc78cc3bbc&v=4
+ url: https://github.com/yokwejuste
+- login: alv2017
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
- url: https://github.com/PhysicallyActive
-- login: n8sty
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
- url: https://github.com/n8sty
-- login: pedroconceicao
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/32837064?u=5a0e6559bc391442629a28b6923790b54deb4464&v=4
- url: https://github.com/pedroconceicao
+ avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
+ url: https://github.com/alv2017
- login: PREPONDERANCE
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
url: https://github.com/PREPONDERANCE
-- login: aanchlia
+- login: nbx3
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/2835374?u=3c3ed29aa8b09ccaf8d66def0ce82bc2f7e5aab6&v=4
- url: https://github.com/aanchlia
-- login: 0sahil
+ avatarUrl: https://avatars.githubusercontent.com/u/34649527?u=943812f69e0d40adbd3fa1c9b8ef50dd971a2a45&v=4
+ url: https://github.com/nbx3
+- login: XiaoXinYo
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/58521386?u=ac00b731c07c712d0baa57b8b70ac8422acf183c&v=4
- url: https://github.com/0sahil
-- login: jgould22
+ avatarUrl: https://avatars.githubusercontent.com/u/56395004?u=b3b7cb758997f283c271a581833e407229dab82c&v=4
+ url: https://github.com/XiaoXinYo
+- login: iloveitaly
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
- url: https://github.com/jgould22
+ avatarUrl: https://avatars.githubusercontent.com/u/150855?v=4
+ url: https://github.com/iloveitaly
three_months_experts:
+- login: luzzodev
+ count: 34
+ avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
+ url: https://github.com/luzzodev
- login: YuriiMotov
- count: 101
+ count: 33
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
url: https://github.com/YuriiMotov
-- login: JavierSanchezCastro
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
- url: https://github.com/JavierSanchezCastro
- login: Kludex
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
+ count: 23
+ avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
url: https://github.com/Kludex
-- login: jgould22
- count: 14
- avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
- url: https://github.com/jgould22
-- login: killjoy1221
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4
- url: https://github.com/killjoy1221
-- login: hasansezertasan
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: PhysicallyActive
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
- url: https://github.com/PhysicallyActive
-- login: n8sty
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
- url: https://github.com/n8sty
- login: sehraramiz
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/14166324?v=4
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
url: https://github.com/sehraramiz
-- login: acidjunk
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
- url: https://github.com/acidjunk
- login: estebanx64
- count: 3
+ count: 7
avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
url: https://github.com/estebanx64
-- login: PREPONDERANCE
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
- url: https://github.com/PREPONDERANCE
-- login: chrisK824
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
- url: https://github.com/chrisK824
-- login: ryanisn
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/53449841?v=4
- url: https://github.com/ryanisn
-- login: pythonweb2
+- login: yvallois
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/36999744?v=4
+ url: https://github.com/yvallois
+- login: yokwejuste
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/71908316?u=592c1e42aa0ee5cb94890e0b863e2acc78cc3bbc&v=4
+ url: https://github.com/yokwejuste
+- login: jgould22
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
+ url: https://github.com/jgould22
+- login: alv2017
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
- url: https://github.com/pythonweb2
-- login: omarcruzpantoja
+ avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
+ url: https://github.com/alv2017
+- login: viniciusCalcantara
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/15116058?u=4b64c643fad49225d854e1aaecd1ffc6f9071a1b&v=4
- url: https://github.com/omarcruzpantoja
-- login: mskrip
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/17459600?u=10019d5c38ae3374dd4a6743b0223e56a78d4855&v=4
- url: https://github.com/mskrip
-- login: pedroconceicao
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/32837064?u=5a0e6559bc391442629a28b6923790b54deb4464&v=4
- url: https://github.com/pedroconceicao
-- login: Jackiexiao
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/18050469?u=a2003e21a7780477ba00bf87a9abef8af58e91d1&v=4
- url: https://github.com/Jackiexiao
-- login: aanchlia
- count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/2835374?u=3c3ed29aa8b09ccaf8d66def0ce82bc2f7e5aab6&v=4
- url: https://github.com/aanchlia
-- login: moreno-p
+ avatarUrl: https://avatars.githubusercontent.com/u/108818737?u=3d7ffe5808843ee4372f9cc5a559ff1674cf1792&v=4
+ url: https://github.com/viniciusCalcantara
+- login: PREPONDERANCE
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/164261630?v=4
- url: https://github.com/moreno-p
-- login: 0sahil
+ avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
+ url: https://github.com/PREPONDERANCE
+- login: nbx3
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/58521386?u=ac00b731c07c712d0baa57b8b70ac8422acf183c&v=4
- url: https://github.com/0sahil
-- login: patrick91
+ avatarUrl: https://avatars.githubusercontent.com/u/34649527?u=943812f69e0d40adbd3fa1c9b8ef50dd971a2a45&v=4
+ url: https://github.com/nbx3
+- login: XiaoXinYo
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/667029?u=e35958a75ac1f99c81b4bc99e22db8cd665ae7f0&v=4
- url: https://github.com/patrick91
-- login: pprunty
+ avatarUrl: https://avatars.githubusercontent.com/u/56395004?u=b3b7cb758997f283c271a581833e407229dab82c&v=4
+ url: https://github.com/XiaoXinYo
+- login: JavierSanchezCastro
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/58374462?u=5736576e586429abc97a803b8bcd4a6d828b8a2f&v=4
- url: https://github.com/pprunty
-- login: angely-dev
+ avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
+ url: https://github.com/JavierSanchezCastro
+- login: iloveitaly
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/4362224?v=4
- url: https://github.com/angely-dev
-- login: mastizada
+ avatarUrl: https://avatars.githubusercontent.com/u/150855?v=4
+ url: https://github.com/iloveitaly
+- login: LincolnPuzey
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/1975818?u=0751a06d7271c8bf17cb73b1b845644ab4d2c6dc&v=4
- url: https://github.com/mastizada
-- login: sm-Fifteen
+ avatarUrl: https://avatars.githubusercontent.com/u/18750802?v=4
+ url: https://github.com/LincolnPuzey
+- login: Knighthawk-Leo
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
- url: https://github.com/sm-Fifteen
-- login: methane
+ avatarUrl: https://avatars.githubusercontent.com/u/72437494?u=27c68db94a3107b605e603cc136f4ba83f0106d5&v=4
+ url: https://github.com/Knighthawk-Leo
+- login: gelezo43
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/199592?v=4
- url: https://github.com/methane
-- login: konstantinos1981
+ avatarUrl: https://avatars.githubusercontent.com/u/40732698?u=611f39d3c1d2f4207a590937a78c1f10eed6232c&v=4
+ url: https://github.com/gelezo43
+- login: dbfreem
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/39465388?v=4
- url: https://github.com/konstantinos1981
-- login: druidance
+ avatarUrl: https://avatars.githubusercontent.com/u/9778569?u=f2f1e9135b5e4f1b0c6821a548b17f97572720fc&v=4
+ url: https://github.com/dbfreem
+- login: AliYmn
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/160344534?v=4
- url: https://github.com/druidance
-- login: fabianfalon
+ avatarUrl: https://avatars.githubusercontent.com/u/18416653?u=98c1fca46c7e4dabe8c39d17b5e55d1511d41cf9&v=4
+ url: https://github.com/AliYmn
+- login: RichieB2B
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/3700760?u=95f69e31280b17ac22299cdcd345323b142fe0af&v=4
- url: https://github.com/fabianfalon
-- login: VatsalJagani
+ avatarUrl: https://avatars.githubusercontent.com/u/1461970?u=edaa57d1077705244ea5c9244f4783d94ff11f12&v=4
+ url: https://github.com/RichieB2B
+- login: Synrom
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/20964366?u=43552644be05c9107c029e26d5ab3be5a1920f45&v=4
- url: https://github.com/VatsalJagani
-- login: khaledadrani
+ avatarUrl: https://avatars.githubusercontent.com/u/30272537?v=4
+ url: https://github.com/Synrom
+- login: iiotsrc
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/45245894?u=49ed5056426a149a5af29d385d8bd3847101d3a4&v=4
- url: https://github.com/khaledadrani
-- login: ThirVondukr
+ avatarUrl: https://avatars.githubusercontent.com/u/131771119?u=bcaf2559ef6266af70b151b7fda31a1ee3dbecb3&v=4
+ url: https://github.com/iiotsrc
+- login: Kfir-G
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/50728601?u=167c0bd655e52817082e50979a86d2f98f95b1a3&v=4
- url: https://github.com/ThirVondukr
+ avatarUrl: https://avatars.githubusercontent.com/u/57500876?u=0cd29db046a17f12f382d398141319fca7ff230a&v=4
+ url: https://github.com/Kfir-G
six_months_experts:
- login: YuriiMotov
- count: 104
+ count: 72
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
url: https://github.com/YuriiMotov
- login: Kludex
- count: 104
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
+ count: 39
+ avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
url: https://github.com/Kludex
+- login: sinisaos
+ count: 37
+ avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4
+ url: https://github.com/sinisaos
+- login: luzzodev
+ count: 36
+ avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
+ url: https://github.com/luzzodev
- login: JavierSanchezCastro
- count: 40
+ count: 16
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
url: https://github.com/JavierSanchezCastro
+- login: tiangolo
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
+ url: https://github.com/tiangolo
+- login: Kfir-G
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/57500876?u=0cd29db046a17f12f382d398141319fca7ff230a&v=4
+ url: https://github.com/Kfir-G
+- login: sehraramiz
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
+ url: https://github.com/sehraramiz
+- login: estebanx64
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
+ url: https://github.com/estebanx64
+- login: ceb10n
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
+ url: https://github.com/ceb10n
+- login: yvallois
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/36999744?v=4
+ url: https://github.com/yvallois
+- login: n8sty
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
+ url: https://github.com/n8sty
+- login: TomFaulkner
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/14956620?v=4
+ url: https://github.com/TomFaulkner
+- login: yokwejuste
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/71908316?u=592c1e42aa0ee5cb94890e0b863e2acc78cc3bbc&v=4
+ url: https://github.com/yokwejuste
- login: jgould22
- count: 40
+ count: 4
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
-- login: hasansezertasan
- count: 21
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: n8sty
- count: 19
- avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
- url: https://github.com/n8sty
-- login: killjoy1221
- count: 9
- avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4
- url: https://github.com/killjoy1221
-- login: aanchlia
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/2835374?u=3c3ed29aa8b09ccaf8d66def0ce82bc2f7e5aab6&v=4
- url: https://github.com/aanchlia
-- login: estebanx64
- count: 7
- avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
- url: https://github.com/estebanx64
-- login: PhysicallyActive
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
- url: https://github.com/PhysicallyActive
-- login: dolfinus
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/4661021?u=a51b39001a2e5e7529b45826980becf786de2327&v=4
- url: https://github.com/dolfinus
-- login: Ventura94
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/43103937?u=ccb837005aaf212a449c374618c4339089e2f733&v=4
- url: https://github.com/Ventura94
-- login: sehraramiz
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/14166324?v=4
- url: https://github.com/sehraramiz
-- login: acidjunk
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
- url: https://github.com/acidjunk
-- login: shashstormer
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/90090313?v=4
- url: https://github.com/shashstormer
-- login: GodMoonGoodman
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/29688727?u=7b251da620d999644c37c1feeb292d033eed7ad6&v=4
- url: https://github.com/GodMoonGoodman
-- login: flo-at
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/564288?v=4
- url: https://github.com/flo-at
-- login: PREPONDERANCE
+- login: alv2017
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
- url: https://github.com/PREPONDERANCE
-- login: chrisK824
+ avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
+ url: https://github.com/alv2017
+- login: viniciusCalcantara
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
- url: https://github.com/chrisK824
-- login: angely-dev
+ avatarUrl: https://avatars.githubusercontent.com/u/108818737?u=3d7ffe5808843ee4372f9cc5a559ff1674cf1792&v=4
+ url: https://github.com/viniciusCalcantara
+- login: pawelad
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/4362224?v=4
- url: https://github.com/angely-dev
-- login: fmelihh
+ avatarUrl: https://avatars.githubusercontent.com/u/7062874?u=d27dc220545a8401ad21840590a97d474d7101e6&v=4
+ url: https://github.com/pawelad
+- login: dbfreem
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/99879453?u=671117dba9022db2237e3da7a39cbc2efc838db0&v=4
- url: https://github.com/fmelihh
-- login: ryanisn
+ avatarUrl: https://avatars.githubusercontent.com/u/9778569?u=f2f1e9135b5e4f1b0c6821a548b17f97572720fc&v=4
+ url: https://github.com/dbfreem
+- login: Isuxiz
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/53449841?v=4
- url: https://github.com/ryanisn
-- login: JoshYuJump
+ avatarUrl: https://avatars.githubusercontent.com/u/48672727?u=34d7b4ade252687d22a27cf53037b735b244bfc1&v=4
+ url: https://github.com/Isuxiz
+- login: bertomaniac
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/5901894?u=cdbca6296ac4cdcdf6945c112a1ce8d5342839ea&v=4
- url: https://github.com/JoshYuJump
-- login: pythonweb2
+ avatarUrl: https://avatars.githubusercontent.com/u/10235051?u=14484a96833228a7b29fee4a7916d411c242c4f6&v=4
+ url: https://github.com/bertomaniac
+- login: PhysicallyActive
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
- url: https://github.com/pythonweb2
-- login: omarcruzpantoja
+ avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
+ url: https://github.com/PhysicallyActive
+- login: Minibrams
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/15116058?u=4b64c643fad49225d854e1aaecd1ffc6f9071a1b&v=4
- url: https://github.com/omarcruzpantoja
-- login: bogdan-coman-uv
+ avatarUrl: https://avatars.githubusercontent.com/u/8108085?u=b028dbc308fa8485e0e2e9402b3d03d8deb22bf9&v=4
+ url: https://github.com/Minibrams
+- login: AIdjis
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/92912507?v=4
- url: https://github.com/bogdan-coman-uv
-- login: ahmedabdou14
+ avatarUrl: https://avatars.githubusercontent.com/u/88404339?u=2a80d80b054e9228391e32fb9bb39571509dab6a&v=4
+ url: https://github.com/AIdjis
+- login: svlandeg
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4
- url: https://github.com/ahmedabdou14
-- login: mskrip
+ avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4
+ url: https://github.com/svlandeg
+- login: PREPONDERANCE
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/17459600?u=10019d5c38ae3374dd4a6743b0223e56a78d4855&v=4
- url: https://github.com/mskrip
-- login: leonidktoto
+ avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
+ url: https://github.com/PREPONDERANCE
+- login: nbx3
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/159561986?v=4
- url: https://github.com/leonidktoto
-- login: pedroconceicao
+ avatarUrl: https://avatars.githubusercontent.com/u/34649527?u=943812f69e0d40adbd3fa1c9b8ef50dd971a2a45&v=4
+ url: https://github.com/nbx3
+- login: yanggeorge
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/32837064?u=5a0e6559bc391442629a28b6923790b54deb4464&v=4
- url: https://github.com/pedroconceicao
-- login: hwong557
+ avatarUrl: https://avatars.githubusercontent.com/u/2434407?v=4
+ url: https://github.com/yanggeorge
+- login: XiaoXinYo
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/460259?u=7d2f1b33ea5bda4d8e177ab3cb924a673d53087e&v=4
- url: https://github.com/hwong557
-- login: Jackiexiao
+ avatarUrl: https://avatars.githubusercontent.com/u/56395004?u=b3b7cb758997f283c271a581833e407229dab82c&v=4
+ url: https://github.com/XiaoXinYo
+- login: pythonweb2
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/18050469?u=a2003e21a7780477ba00bf87a9abef8af58e91d1&v=4
- url: https://github.com/Jackiexiao
-- login: admo1
+ avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
+ url: https://github.com/pythonweb2
+- login: slafs
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/14835916?v=4
- url: https://github.com/admo1
-- login: binbjz
+ avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4
+ url: https://github.com/slafs
+- login: AmirHmZz
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/8213913?u=22b68b7a0d5bf5e09c02084c0f5f53d7503114cd&v=4
- url: https://github.com/binbjz
-- login: nameer
+ avatarUrl: https://avatars.githubusercontent.com/u/38752106?u=07f80e451bda00a9492bbc764e49d24ad3ada8cc&v=4
+ url: https://github.com/AmirHmZz
+- login: iloveitaly
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/3931725?u=6199fb065df098fc13ac0a5e649f89672b586732&v=4
- url: https://github.com/nameer
-- login: moreno-p
+ avatarUrl: https://avatars.githubusercontent.com/u/150855?v=4
+ url: https://github.com/iloveitaly
+- login: LincolnPuzey
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/164261630?v=4
- url: https://github.com/moreno-p
-- login: 0sahil
+ avatarUrl: https://avatars.githubusercontent.com/u/18750802?v=4
+ url: https://github.com/LincolnPuzey
+- login: alejsdev
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/58521386?u=ac00b731c07c712d0baa57b8b70ac8422acf183c&v=4
- url: https://github.com/0sahil
-- login: nymous
+ avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=356f39ff3f0211c720b06d3dbb060e98884085e3&v=4
+ url: https://github.com/alejsdev
+- login: Knighthawk-Leo
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
- url: https://github.com/nymous
-- login: patrick91
+ avatarUrl: https://avatars.githubusercontent.com/u/72437494?u=27c68db94a3107b605e603cc136f4ba83f0106d5&v=4
+ url: https://github.com/Knighthawk-Leo
+- login: gelezo43
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/667029?u=e35958a75ac1f99c81b4bc99e22db8cd665ae7f0&v=4
- url: https://github.com/patrick91
-- login: pprunty
+ avatarUrl: https://avatars.githubusercontent.com/u/40732698?u=611f39d3c1d2f4207a590937a78c1f10eed6232c&v=4
+ url: https://github.com/gelezo43
+- login: christiansicari
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/58374462?u=5736576e586429abc97a803b8bcd4a6d828b8a2f&v=4
- url: https://github.com/pprunty
-- login: JonnyBootsNpants
+ avatarUrl: https://avatars.githubusercontent.com/u/29756552?v=4
+ url: https://github.com/christiansicari
+- login: 1001pepi
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/155071540?u=2d3a72b74a2c4c8eaacdb625c7ac850369579352&v=4
- url: https://github.com/JonnyBootsNpants
-- login: richin13
+ avatarUrl: https://avatars.githubusercontent.com/u/82064861?u=8c6ffdf2275d6970a07294752c545cd2702c57d3&v=4
+ url: https://github.com/1001pepi
+- login: AliYmn
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/8370058?u=8e37a4cdbc78983a5f4b4847f6d1879fb39c851c&v=4
- url: https://github.com/richin13
-- login: mastizada
+ avatarUrl: https://avatars.githubusercontent.com/u/18416653?u=98c1fca46c7e4dabe8c39d17b5e55d1511d41cf9&v=4
+ url: https://github.com/AliYmn
+- login: RichieB2B
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/1975818?u=0751a06d7271c8bf17cb73b1b845644ab4d2c6dc&v=4
- url: https://github.com/mastizada
-- login: sm-Fifteen
+ avatarUrl: https://avatars.githubusercontent.com/u/1461970?u=edaa57d1077705244ea5c9244f4783d94ff11f12&v=4
+ url: https://github.com/RichieB2B
+- login: Synrom
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
- url: https://github.com/sm-Fifteen
-- login: amacfie
+ avatarUrl: https://avatars.githubusercontent.com/u/30272537?v=4
+ url: https://github.com/Synrom
+- login: ecly
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/8410422?v=4
+ url: https://github.com/ecly
+- login: iiotsrc
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/131771119?u=bcaf2559ef6266af70b151b7fda31a1ee3dbecb3&v=4
+ url: https://github.com/iiotsrc
+- login: simondale00
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/33907262?u=2721fb37014d50daf473267c808aa678ecaefe09&v=4
+ url: https://github.com/simondale00
+- login: jd-solanki
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/47495003?u=6e225cb42c688d0cd70e65c6baedb9f5922b1178&v=4
+ url: https://github.com/jd-solanki
+- login: AumGupta
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/86357151?u=7d05aa606c0611a18f4db16cf26361ce10a6e195&v=4
+ url: https://github.com/AumGupta
+- login: DeoLeung
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/3764720?u=4c222ef513814de4c7fb3736d0a7adf11d953d43&v=4
+ url: https://github.com/DeoLeung
+- login: Reemyos
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/44867003?v=4
+ url: https://github.com/Reemyos
+- login: deight93
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/37678115?u=a608798b5bd0034183a9c430ebb42fb266db86ce&v=4
+ url: https://github.com/deight93
+- login: Jkrox
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/83181939?u=d6a922d97129f7f3916d6a1c166bc011b3a72b7f&v=4
+ url: https://github.com/Jkrox
+- login: mmzeynalli
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/33568903?u=19efd0c0722730b83a70b7c86c36e5b7d83e07d2&v=4
+ url: https://github.com/mmzeynalli
+- login: ddahan
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1933516?u=1d200a620e8d6841df017e9f2bb7efb58b580f40&v=4
+ url: https://github.com/ddahan
+- login: jfeaver
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1091338?u=0bcba366447d8fadad63f6705a52d128da4c7ec2&v=4
+ url: https://github.com/jfeaver
+- login: Wurstnase
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/889657?u=d70187989940b085bcbfa3bedad8dbc5f3ab1fe7&v=4
- url: https://github.com/amacfie
-- login: garg10may
+ avatarUrl: https://avatars.githubusercontent.com/u/8709415?u=f479af475a97aee9a1dab302cfc35d07e9ea245f&v=4
+ url: https://github.com/Wurstnase
+- login: tristan
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/8787120?u=7028d2b3a2a26534c1806eb76c7425a3fac9732f&v=4
- url: https://github.com/garg10may
-- login: methane
+ avatarUrl: https://avatars.githubusercontent.com/u/1412?u=aab8aaa4cc0f1210ac45fc93873a5909d314c965&v=4
+ url: https://github.com/tristan
+- login: chandanch
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/199592?v=4
- url: https://github.com/methane
-- login: konstantinos1981
+ avatarUrl: https://avatars.githubusercontent.com/u/8663552?u=afc484bc0a952c83f1fb6a1583cda443f807cd66&v=4
+ url: https://github.com/chandanch
+- login: rvishruth
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/39465388?v=4
- url: https://github.com/konstantinos1981
-- login: druidance
+ avatarUrl: https://avatars.githubusercontent.com/u/79176273?v=4
+ url: https://github.com/rvishruth
+- login: mattmess1221
count: 2
- avatarUrl: https://avatars.githubusercontent.com/u/160344534?v=4
- url: https://github.com/druidance
+ avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4
+ url: https://github.com/mattmess1221
+- login: meower1
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/109747197?u=0a5cc2a6ae74e558f0afc2874da85132e5953d8b&v=4
+ url: https://github.com/meower1
one_year_experts:
-- login: Kludex
- count: 207
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: jgould22
- count: 118
- avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
- url: https://github.com/jgould22
- login: YuriiMotov
- count: 104
+ count: 223
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
url: https://github.com/YuriiMotov
+- login: Kludex
+ count: 83
+ avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
+ url: https://github.com/Kludex
- login: JavierSanchezCastro
- count: 59
+ count: 47
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
url: https://github.com/JavierSanchezCastro
+- login: jgould22
+ count: 42
+ avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
+ url: https://github.com/jgould22
+- login: sinisaos
+ count: 39
+ avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4
+ url: https://github.com/sinisaos
+- login: luzzodev
+ count: 36
+ avatarUrl: https://avatars.githubusercontent.com/u/27291415?v=4
+ url: https://github.com/luzzodev
+- login: tiangolo
+ count: 24
+ avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
+ url: https://github.com/tiangolo
- login: n8sty
- count: 40
+ count: 23
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
url: https://github.com/n8sty
-- login: hasansezertasan
- count: 27
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: chrisK824
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
- url: https://github.com/chrisK824
-- login: ahmedabdou14
- count: 13
- avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4
- url: https://github.com/ahmedabdou14
-- login: arjwilliams
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/22227620?v=4
- url: https://github.com/arjwilliams
-- login: killjoy1221
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4
- url: https://github.com/killjoy1221
-- login: WilliamStam
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/182800?v=4
- url: https://github.com/WilliamStam
-- login: iudeen
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
- url: https://github.com/iudeen
-- login: nymous
- count: 9
- avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
- url: https://github.com/nymous
-- login: aanchlia
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/2835374?u=3c3ed29aa8b09ccaf8d66def0ce82bc2f7e5aab6&v=4
- url: https://github.com/aanchlia
- login: estebanx64
- count: 7
+ count: 19
avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
url: https://github.com/estebanx64
-- login: pythonweb2
- count: 7
- avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
- url: https://github.com/pythonweb2
-- login: romabozhanovgithub
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/67696229?u=e4b921eef096415300425aca249348f8abb78ad7&v=4
- url: https://github.com/romabozhanovgithub
+- login: sehraramiz
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
+ url: https://github.com/sehraramiz
- login: PhysicallyActive
- count: 6
+ count: 14
avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
url: https://github.com/PhysicallyActive
-- login: mikeedjones
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/4087139?u=cc4a242896ac2fcf88a53acfaf190d0fe0a1f0c9&v=4
- url: https://github.com/mikeedjones
-- login: dolfinus
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/4661021?u=a51b39001a2e5e7529b45826980becf786de2327&v=4
- url: https://github.com/dolfinus
-- login: ebottos94
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
- url: https://github.com/ebottos94
-- login: Ventura94
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/43103937?u=ccb837005aaf212a449c374618c4339089e2f733&v=4
- url: https://github.com/Ventura94
-- login: White-Mask
+- login: ceb10n
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
+ url: https://github.com/ceb10n
+- login: Kfir-G
+ count: 13
+ avatarUrl: https://avatars.githubusercontent.com/u/57500876?u=0cd29db046a17f12f382d398141319fca7ff230a&v=4
+ url: https://github.com/Kfir-G
+- login: mattmess1221
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=723662989f2027755e67d200137c13c53ae154ac&v=4
+ url: https://github.com/mattmess1221
+- login: hasansezertasan
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
+ url: https://github.com/hasansezertasan
+- login: AIdjis
+ count: 8
+ avatarUrl: https://avatars.githubusercontent.com/u/88404339?u=2a80d80b054e9228391e32fb9bb39571509dab6a&v=4
+ url: https://github.com/AIdjis
+- login: yvallois
count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/31826970?u=8625355dc25ddf9c85a8b2b0b9932826c4c8f44c&v=4
- url: https://github.com/White-Mask
-- login: sehraramiz
+ avatarUrl: https://avatars.githubusercontent.com/u/36999744?v=4
+ url: https://github.com/yvallois
+- login: PREPONDERANCE
count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/14166324?v=4
- url: https://github.com/sehraramiz
+ avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
+ url: https://github.com/PREPONDERANCE
+- login: pythonweb2
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
+ url: https://github.com/pythonweb2
- login: acidjunk
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
url: https://github.com/acidjunk
-- login: JoshYuJump
+- login: gustavosett
count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/5901894?u=cdbca6296ac4cdcdf6945c112a1ce8d5342839ea&v=4
- url: https://github.com/JoshYuJump
-- login: alex-pobeditel-2004
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/14791483?v=4
- url: https://github.com/alex-pobeditel-2004
-- login: shashstormer
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/90090313?v=4
- url: https://github.com/shashstormer
-- login: wu-clan
+ avatarUrl: https://avatars.githubusercontent.com/u/99373133?u=1739ca547c3d200f1b72450520bce46a97aab184&v=4
+ url: https://github.com/gustavosett
+- login: binbjz
count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/52145145?u=f8c9e5c8c259d248e1683fedf5027b4ee08a0967&v=4
- url: https://github.com/wu-clan
-- login: abhint
+ avatarUrl: https://avatars.githubusercontent.com/u/8213913?u=22b68b7a0d5bf5e09c02084c0f5f53d7503114cd&v=4
+ url: https://github.com/binbjz
+- login: chyok
count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/25699289?u=b5d219277b4d001ac26fb8be357fddd88c29d51b&v=4
- url: https://github.com/abhint
-- login: anthonycepeda
+ avatarUrl: https://avatars.githubusercontent.com/u/32629225?u=3b7c30e8a09426a1b9284f6e8a0ae53a525596bf&v=4
+ url: https://github.com/chyok
+- login: TomFaulkner
count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=60bdf46240cff8fca482ff0fc07d963fd5e1a27c&v=4
- url: https://github.com/anthonycepeda
-- login: GodMoonGoodman
+ avatarUrl: https://avatars.githubusercontent.com/u/14956620?v=4
+ url: https://github.com/TomFaulkner
+- login: yokwejuste
count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/29688727?u=7b251da620d999644c37c1feeb292d033eed7ad6&v=4
- url: https://github.com/GodMoonGoodman
+ avatarUrl: https://avatars.githubusercontent.com/u/71908316?u=592c1e42aa0ee5cb94890e0b863e2acc78cc3bbc&v=4
+ url: https://github.com/yokwejuste
+- login: DeoLeung
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/3764720?u=4c222ef513814de4c7fb3736d0a7adf11d953d43&v=4
+ url: https://github.com/DeoLeung
- login: flo-at
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/564288?v=4
url: https://github.com/flo-at
-- login: yinziyan1206
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
- url: https://github.com/yinziyan1206
-- login: amacfie
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/889657?u=d70187989940b085bcbfa3bedad8dbc5f3ab1fe7&v=4
- url: https://github.com/amacfie
-- login: commonism
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/164513?v=4
- url: https://github.com/commonism
-- login: dmontagu
+- login: GodMoonGoodman
count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
- url: https://github.com/dmontagu
-- login: sanzoghenzo
+ avatarUrl: https://avatars.githubusercontent.com/u/29688727?u=7b251da620d999644c37c1feeb292d033eed7ad6&v=4
+ url: https://github.com/GodMoonGoodman
+- login: bertomaniac
count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/977953?v=4
- url: https://github.com/sanzoghenzo
-- login: lucasgadams
+ avatarUrl: https://avatars.githubusercontent.com/u/10235051?u=14484a96833228a7b29fee4a7916d411c242c4f6&v=4
+ url: https://github.com/bertomaniac
+- login: alv2017
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/36425095?v=4
- url: https://github.com/lucasgadams
-- login: NeilBotelho
+ avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
+ url: https://github.com/alv2017
+- login: msehnout
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/39030675?u=16fea2ff90a5c67b974744528a38832a6d1bb4f7&v=4
- url: https://github.com/NeilBotelho
-- login: hhartzer
+ avatarUrl: https://avatars.githubusercontent.com/u/9369632?u=8c988f1b008a3f601385a3616f9327820f66e3a5&v=4
+ url: https://github.com/msehnout
+- login: viniciusCalcantara
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/100533792?v=4
- url: https://github.com/hhartzer
-- login: binbjz
+ avatarUrl: https://avatars.githubusercontent.com/u/108818737?u=3d7ffe5808843ee4372f9cc5a559ff1674cf1792&v=4
+ url: https://github.com/viniciusCalcantara
+- login: pawelad
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/8213913?u=22b68b7a0d5bf5e09c02084c0f5f53d7503114cd&v=4
- url: https://github.com/binbjz
-- login: PREPONDERANCE
+ avatarUrl: https://avatars.githubusercontent.com/u/7062874?u=d27dc220545a8401ad21840590a97d474d7101e6&v=4
+ url: https://github.com/pawelad
+- login: ThirVondukr
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/112809059?u=30ab12dc9ddba2f94ab90e6ad4ad8bc5cfa7fccd&v=4
- url: https://github.com/PREPONDERANCE
-- login: nameer
+ avatarUrl: https://avatars.githubusercontent.com/u/50728601?u=167c0bd655e52817082e50979a86d2f98f95b1a3&v=4
+ url: https://github.com/ThirVondukr
+- login: dbfreem
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/9778569?u=f2f1e9135b5e4f1b0c6821a548b17f97572720fc&v=4
+ url: https://github.com/dbfreem
+- login: Isuxiz
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/3931725?u=6199fb065df098fc13ac0a5e649f89672b586732&v=4
- url: https://github.com/nameer
+ avatarUrl: https://avatars.githubusercontent.com/u/48672727?u=34d7b4ade252687d22a27cf53037b735b244bfc1&v=4
+ url: https://github.com/Isuxiz
- login: angely-dev
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/4362224?v=4
url: https://github.com/angely-dev
-- login: fmelihh
+- login: deight93
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/37678115?u=a608798b5bd0034183a9c430ebb42fb266db86ce&v=4
+ url: https://github.com/deight93
+- login: mmzeynalli
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/99879453?u=671117dba9022db2237e3da7a39cbc2efc838db0&v=4
- url: https://github.com/fmelihh
+ avatarUrl: https://avatars.githubusercontent.com/u/33568903?u=19efd0c0722730b83a70b7c86c36e5b7d83e07d2&v=4
+ url: https://github.com/mmzeynalli
+- login: Minibrams
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/8108085?u=b028dbc308fa8485e0e2e9402b3d03d8deb22bf9&v=4
+ url: https://github.com/Minibrams
- login: ryanisn
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/53449841?v=4
url: https://github.com/ryanisn
-- login: theobouwman
+- login: svlandeg
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/16098190?u=dc70db88a7a99b764c9a89a6e471e0b7ca478a35&v=4
- url: https://github.com/theobouwman
-- login: methane
+ avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4
+ url: https://github.com/svlandeg
+- login: alexandercronin
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/199592?v=4
- url: https://github.com/methane
-top_contributors:
-- login: nilslindemann
- count: 130
- avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
- url: https://github.com/nilslindemann
-- login: jaystone776
- count: 49
- avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
- url: https://github.com/jaystone776
-- login: waynerv
- count: 25
- avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
- url: https://github.com/waynerv
-- login: tokusumi
- count: 24
- avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
- url: https://github.com/tokusumi
-- login: SwftAlpc
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
- url: https://github.com/SwftAlpc
-- login: Kludex
- count: 22
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: hasansezertasan
- count: 22
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: dmontagu
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
- url: https://github.com/dmontagu
-- login: Xewus
- count: 14
- avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
- url: https://github.com/Xewus
-- login: euri10
- count: 13
- avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
- url: https://github.com/euri10
-- login: mariacamilagl
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
- url: https://github.com/mariacamilagl
-- login: AlertRED
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4
- url: https://github.com/AlertRED
-- login: Smlep
- count: 11
- avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
- url: https://github.com/Smlep
-- login: alejsdev
- count: 11
- avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=9ca449ad5161af12766ddd1a22988e9b14315f5c&v=4
- url: https://github.com/alejsdev
-- login: hard-coders
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
- url: https://github.com/hard-coders
-- login: KaniKim
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/19832624?u=40f8f7f3f36d5f2365ba2ad0b40693e60958ce70&v=4
- url: https://github.com/KaniKim
-- login: xzmeng
- count: 9
- avatarUrl: https://avatars.githubusercontent.com/u/40202897?v=4
- url: https://github.com/xzmeng
-- login: Serrones
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
- url: https://github.com/Serrones
-- login: rjNemo
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
- url: https://github.com/rjNemo
-- login: pablocm83
- count: 8
- avatarUrl: https://avatars.githubusercontent.com/u/28315068?u=3310fbb05bb8bfc50d2c48b6cb64ac9ee4a14549&v=4
- url: https://github.com/pablocm83
-- login: RunningIkkyu
- count: 7
- avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
- url: https://github.com/RunningIkkyu
-- login: Alexandrhub
- count: 7
- avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
- url: https://github.com/Alexandrhub
-- login: NinaHwang
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=eee6bfe9224c71193025ab7477f4f96ceaa05c62&v=4
- url: https://github.com/NinaHwang
-- login: batlopes
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4
- url: https://github.com/batlopes
-- login: wshayes
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
- url: https://github.com/wshayes
-- login: samuelcolvin
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4
- url: https://github.com/samuelcolvin
-- login: Attsun1031
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/1175560?v=4
- url: https://github.com/Attsun1031
-- login: ComicShrimp
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=d2fbf412e7730183ce91686ca48d4147e1b7dc74&v=4
- url: https://github.com/ComicShrimp
-- login: rostik1410
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/11443899?u=e26a635c2ba220467b308a326a579b8ccf4a8701&v=4
- url: https://github.com/rostik1410
-- login: tamtam-fitness
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/62091034?u=8da19a6bd3d02f5d6ba30c7247d5b46c98dd1403&v=4
- url: https://github.com/tamtam-fitness
-- login: jekirl
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/2546697?u=a027452387d85bd4a14834e19d716c99255fb3b7&v=4
- url: https://github.com/jekirl
-- login: jfunez
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/805749?v=4
- url: https://github.com/jfunez
-- login: ycd
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=29682e4b6ac7d5293742ccf818188394b9a82972&v=4
- url: https://github.com/ycd
-- login: komtaki
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
- url: https://github.com/komtaki
-- login: hitrust
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/3360631?u=5fa1f475ad784d64eb9666bdd43cc4d285dcc773&v=4
- url: https://github.com/hitrust
-- login: JulianMaurin
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/63545168?u=b7d15ac865268cbefc2d739e2f23d9aeeac1a622&v=4
- url: https://github.com/JulianMaurin
-- login: lsglucas
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
- url: https://github.com/lsglucas
-- login: BilalAlpaslan
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
- url: https://github.com/BilalAlpaslan
-- login: adriangb
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
- url: https://github.com/adriangb
-- login: iudeen
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
- url: https://github.com/iudeen
-- login: axel584
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
- url: https://github.com/axel584
-- login: ivan-abc
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4
- url: https://github.com/ivan-abc
-- login: divums
+ avatarUrl: https://avatars.githubusercontent.com/u/8014288?u=69580504c51a0cdd756fc47b23bb7f404bd694e7&v=4
+ url: https://github.com/alexandercronin
+- login: aanchlia
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/1397556?v=4
- url: https://github.com/divums
-- login: prostomarkeloff
+ avatarUrl: https://avatars.githubusercontent.com/u/2835374?u=3c3ed29aa8b09ccaf8d66def0ce82bc2f7e5aab6&v=4
+ url: https://github.com/aanchlia
+- login: chrisK824
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=72309cc1f2e04e40fa38b29969cb4e9d3f722e7b&v=4
- url: https://github.com/prostomarkeloff
-- login: nsidnev
+ avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
+ url: https://github.com/chrisK824
+- login: omarcruzpantoja
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
- url: https://github.com/nsidnev
-- login: pawamoy
+ avatarUrl: https://avatars.githubusercontent.com/u/15116058?u=4b64c643fad49225d854e1aaecd1ffc6f9071a1b&v=4
+ url: https://github.com/omarcruzpantoja
+- login: ahmedabdou14
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
- url: https://github.com/pawamoy
-top_reviewers:
-- login: Kludex
- count: 158
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: BilalAlpaslan
- count: 86
- avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
- url: https://github.com/BilalAlpaslan
-- login: yezz123
- count: 85
- avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=d7062cbc6eb7671d5dc9cc0e32a24ae335e0f225&v=4
- url: https://github.com/yezz123
-- login: iudeen
- count: 55
- avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
- url: https://github.com/iudeen
-- login: tokusumi
- count: 51
- avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
- url: https://github.com/tokusumi
-- login: Xewus
- count: 50
- avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
- url: https://github.com/Xewus
-- login: hasansezertasan
- count: 50
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: waynerv
- count: 47
- avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
- url: https://github.com/waynerv
-- login: Laineyzhang55
- count: 47
- avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4
- url: https://github.com/Laineyzhang55
-- login: ycd
- count: 45
- avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=29682e4b6ac7d5293742ccf818188394b9a82972&v=4
- url: https://github.com/ycd
-- login: cikay
- count: 41
- avatarUrl: https://avatars.githubusercontent.com/u/24587499?u=e772190a051ab0eaa9c8542fcff1892471638f2b&v=4
- url: https://github.com/cikay
-- login: alejsdev
- count: 38
- avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=9ca449ad5161af12766ddd1a22988e9b14315f5c&v=4
- url: https://github.com/alejsdev
-- login: JarroVGIT
- count: 34
- avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
- url: https://github.com/JarroVGIT
-- login: AdrianDeAnda
- count: 33
- avatarUrl: https://avatars.githubusercontent.com/u/1024932?u=b2ea249c6b41ddf98679c8d110d0f67d4a3ebf93&v=4
- url: https://github.com/AdrianDeAnda
-- login: ArcLightSlavik
- count: 31
- avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4
- url: https://github.com/ArcLightSlavik
-- login: cassiobotaro
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4
- url: https://github.com/cassiobotaro
-- login: lsglucas
- count: 27
- avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
- url: https://github.com/lsglucas
-- login: komtaki
- count: 27
- avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
- url: https://github.com/komtaki
-- login: YuriiMotov
- count: 25
- avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
- url: https://github.com/YuriiMotov
-- login: Ryandaydev
- count: 25
- avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=48f68868db8886fce31a1d802c1003914c6cd7c6&v=4
- url: https://github.com/Ryandaydev
-- login: LorhanSohaky
- count: 24
- avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
- url: https://github.com/LorhanSohaky
-- login: dmontagu
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
- url: https://github.com/dmontagu
-- login: nilslindemann
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
- url: https://github.com/nilslindemann
-- login: hard-coders
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
- url: https://github.com/hard-coders
-- login: rjNemo
- count: 21
- avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
- url: https://github.com/rjNemo
-- login: odiseo0
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=241a71f6b7068738b81af3e57f45ffd723538401&v=4
- url: https://github.com/odiseo0
-- login: 0417taehyun
- count: 19
- avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
- url: https://github.com/0417taehyun
-- login: JavierSanchezCastro
- count: 19
- avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
- url: https://github.com/JavierSanchezCastro
-- login: Smlep
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
- url: https://github.com/Smlep
-- login: zy7y
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/67154681?u=5d634834cc514028ea3f9115f7030b99a1f4d5a4&v=4
- url: https://github.com/zy7y
-- login: junah201
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/75025529?u=2451c256e888fa2a06bcfc0646d09b87ddb6a945&v=4
- url: https://github.com/junah201
-- login: peidrao
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/32584628?u=a66902b40c13647d0ed0e573d598128240a4dd04&v=4
- url: https://github.com/peidrao
-- login: yanever
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/21978760?v=4
- url: https://github.com/yanever
-- login: SwftAlpc
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
- url: https://github.com/SwftAlpc
-- login: axel584
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
- url: https://github.com/axel584
-- login: codespearhead
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/72931357?u=0fce6b82219b604d58adb614a761556425579cb5&v=4
- url: https://github.com/codespearhead
-- login: Alexandrhub
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
- url: https://github.com/Alexandrhub
-- login: DevDae
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/87962045?u=08e10fa516e844934f4b3fc7c38b33c61697e4a1&v=4
- url: https://github.com/DevDae
-- login: Aruelius
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/25380989?u=574f8cfcda3ea77a3f81884f6b26a97068e36a9d&v=4
- url: https://github.com/Aruelius
-- login: OzgunCaglarArslan
- count: 16
- avatarUrl: https://avatars.githubusercontent.com/u/86166426?v=4
- url: https://github.com/OzgunCaglarArslan
-- login: pedabraham
- count: 15
- avatarUrl: https://avatars.githubusercontent.com/u/16860088?u=abf922a7b920bf8fdb7867d8b43e091f1e796178&v=4
- url: https://github.com/pedabraham
-- login: delhi09
- count: 15
- avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
- url: https://github.com/delhi09
-- login: wdh99
- count: 14
- avatarUrl: https://avatars.githubusercontent.com/u/108172295?u=8a8fb95d5afe3e0fa33257b2aecae88d436249eb&v=4
- url: https://github.com/wdh99
-- login: sh0nk
- count: 13
- avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
- url: https://github.com/sh0nk
-- login: r0b2g1t
- count: 13
- avatarUrl: https://avatars.githubusercontent.com/u/5357541?u=6428442d875d5d71aaa1bb38bb11c4be1a526bc2&v=4
- url: https://github.com/r0b2g1t
-- login: RunningIkkyu
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
- url: https://github.com/RunningIkkyu
-- login: ivan-abc
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4
- url: https://github.com/ivan-abc
-- login: AlertRED
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4
- url: https://github.com/AlertRED
-- login: solomein-sv
- count: 11
- avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=789927ee09cfabd752d3bd554fa6baf4850d2777&v=4
- url: https://github.com/solomein-sv
-top_translations_reviewers:
-- login: s111d
- count: 146
- avatarUrl: https://avatars.githubusercontent.com/u/4954856?v=4
- url: https://github.com/s111d
-- login: Xewus
- count: 128
- avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
- url: https://github.com/Xewus
-- login: tokusumi
- count: 104
- avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
- url: https://github.com/tokusumi
-- login: hasansezertasan
- count: 91
- avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
- url: https://github.com/hasansezertasan
-- login: AlertRED
- count: 70
- avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4
- url: https://github.com/AlertRED
-- login: Alexandrhub
- count: 68
- avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
- url: https://github.com/Alexandrhub
-- login: waynerv
- count: 63
- avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
- url: https://github.com/waynerv
-- login: hard-coders
- count: 53
- avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
- url: https://github.com/hard-coders
-- login: Laineyzhang55
- count: 48
- avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4
- url: https://github.com/Laineyzhang55
-- login: Kludex
- count: 46
- avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
- url: https://github.com/Kludex
-- login: komtaki
- count: 45
- avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
- url: https://github.com/komtaki
-- login: alperiox
- count: 42
- avatarUrl: https://avatars.githubusercontent.com/u/34214152?u=2c5acad3461d4dbc2d48371ba86cac56ae9b25cc&v=4
- url: https://github.com/alperiox
-- login: Winand
- count: 40
- avatarUrl: https://avatars.githubusercontent.com/u/53390?u=bb0e71a2fc3910a8e0ee66da67c33de40ea695f8&v=4
- url: https://github.com/Winand
-- login: solomein-sv
- count: 38
- avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=789927ee09cfabd752d3bd554fa6baf4850d2777&v=4
- url: https://github.com/solomein-sv
-- login: lsglucas
- count: 36
- avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
- url: https://github.com/lsglucas
-- login: SwftAlpc
- count: 36
- avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
- url: https://github.com/SwftAlpc
-- login: nilslindemann
- count: 35
- avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
- url: https://github.com/nilslindemann
-- login: rjNemo
- count: 34
- avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
- url: https://github.com/rjNemo
-- login: akarev0
- count: 33
- avatarUrl: https://avatars.githubusercontent.com/u/53393089?u=6e528bb4789d56af887ce6fe237bea4010885406&v=4
- url: https://github.com/akarev0
-- login: romashevchenko
- count: 32
- avatarUrl: https://avatars.githubusercontent.com/u/132477732?v=4
- url: https://github.com/romashevchenko
-- login: wdh99
- count: 31
- avatarUrl: https://avatars.githubusercontent.com/u/108172295?u=8a8fb95d5afe3e0fa33257b2aecae88d436249eb&v=4
- url: https://github.com/wdh99
-- login: LorhanSohaky
- count: 30
- avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
- url: https://github.com/LorhanSohaky
-- login: cassiobotaro
- count: 29
- avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4
- url: https://github.com/cassiobotaro
-- login: pedabraham
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/16860088?u=abf922a7b920bf8fdb7867d8b43e091f1e796178&v=4
- url: https://github.com/pedabraham
-- login: Smlep
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
- url: https://github.com/Smlep
-- login: dedkot01
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/26196675?u=e2966887124e67932853df4f10f86cb526edc7b0&v=4
- url: https://github.com/dedkot01
-- login: hsuanchi
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/24913710?u=0b094ae292292fee093818e37ceb645c114d2bff&v=4
- url: https://github.com/hsuanchi
-- login: dpinezich
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/3204540?u=a2e1465e3ee10d537614d513589607eddefde09f&v=4
- url: https://github.com/dpinezich
-- login: maoyibo
- count: 27
- avatarUrl: https://avatars.githubusercontent.com/u/7887703?v=4
- url: https://github.com/maoyibo
-- login: 0417taehyun
- count: 27
- avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
- url: https://github.com/0417taehyun
-- login: BilalAlpaslan
- count: 26
- avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
- url: https://github.com/BilalAlpaslan
-- login: zy7y
- count: 25
- avatarUrl: https://avatars.githubusercontent.com/u/67154681?u=5d634834cc514028ea3f9115f7030b99a1f4d5a4&v=4
- url: https://github.com/zy7y
-- login: mycaule
- count: 25
- avatarUrl: https://avatars.githubusercontent.com/u/6161385?u=e3cec75bd6d938a0d73fae0dc5534d1ab2ed1b0e&v=4
- url: https://github.com/mycaule
-- login: sh0nk
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
- url: https://github.com/sh0nk
-- login: axel584
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
- url: https://github.com/axel584
-- login: AGolicyn
- count: 21
- avatarUrl: https://avatars.githubusercontent.com/u/86262613?u=3c21606ab8d210a061a1673decff1e7d5592b380&v=4
- url: https://github.com/AGolicyn
-- login: OzgunCaglarArslan
- count: 21
- avatarUrl: https://avatars.githubusercontent.com/u/86166426?v=4
- url: https://github.com/OzgunCaglarArslan
-- login: Attsun1031
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/1175560?v=4
- url: https://github.com/Attsun1031
-- login: ycd
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=29682e4b6ac7d5293742ccf818188394b9a82972&v=4
- url: https://github.com/ycd
-- login: delhi09
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
- url: https://github.com/delhi09
-- login: rogerbrinkmann
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/5690226?v=4
- url: https://github.com/rogerbrinkmann
-- login: DevDae
- count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/87962045?u=08e10fa516e844934f4b3fc7c38b33c61697e4a1&v=4
- url: https://github.com/DevDae
-- login: sattosan
- count: 19
- avatarUrl: https://avatars.githubusercontent.com/u/20574756?u=b0d8474d2938189c6954423ae8d81d91013f80a8&v=4
- url: https://github.com/sattosan
-- login: ComicShrimp
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=d2fbf412e7730183ce91686ca48d4147e1b7dc74&v=4
- url: https://github.com/ComicShrimp
-- login: junah201
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/75025529?u=2451c256e888fa2a06bcfc0646d09b87ddb6a945&v=4
- url: https://github.com/junah201
-- login: simatheone
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/78508673?u=1b9658d9ee0bde33f56130dd52275493ddd38690&v=4
- url: https://github.com/simatheone
-- login: ivan-abc
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4
- url: https://github.com/ivan-abc
-- login: JavierSanchezCastro
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
- url: https://github.com/JavierSanchezCastro
-- login: bezaca
- count: 17
- avatarUrl: https://avatars.githubusercontent.com/u/69092910?u=4ac58eab99bd37d663f3d23551df96d4fbdbf760&v=4
- url: https://github.com/bezaca
+ avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=d87b866e7c1db970d6f8e8031643818349b046d5&v=4
+ url: https://github.com/ahmedabdou14
+- login: nbx3
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/34649527?u=943812f69e0d40adbd3fa1c9b8ef50dd971a2a45&v=4
+ url: https://github.com/nbx3
+- login: yanggeorge
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/2434407?v=4
+ url: https://github.com/yanggeorge
+- login: XiaoXinYo
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/56395004?u=b3b7cb758997f283c271a581833e407229dab82c&v=4
+ url: https://github.com/XiaoXinYo
+- login: anantgupta129
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/66518357?u=6e25dcd84638f17d2c6df5dc26f07fd7c6dc118e&v=4
+ url: https://github.com/anantgupta129
+- login: slafs
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4
+ url: https://github.com/slafs
+- login: CarlosOliveira-23
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/102637302?u=cf350a4db956f30cbb2c27d3be0d15c282e32b14&v=4
+ url: https://github.com/CarlosOliveira-23
+- login: monchin
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/18521800?v=4
+ url: https://github.com/monchin
+- login: AmirHmZz
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/38752106?u=07f80e451bda00a9492bbc764e49d24ad3ada8cc&v=4
+ url: https://github.com/AmirHmZz
+- login: Leon0824
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/1922026?v=4
+ url: https://github.com/Leon0824
+- login: iloveitaly
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/150855?v=4
+ url: https://github.com/iloveitaly
+- login: msukmanowsky
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/362755?u=782e6bf5b9f0356c3f74b4d894fda9f179252086&v=4
+ url: https://github.com/msukmanowsky
+- login: shurshilov
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/11828278?u=6bcadc5ce4f2f56a514331c9f68eb987d4afe29a&v=4
+ url: https://github.com/shurshilov
+- login: LincolnPuzey
+ count: 2
+ avatarUrl: https://avatars.githubusercontent.com/u/18750802?v=4
+ url: https://github.com/LincolnPuzey
From 8525b879edfd85c1739e513139aab7120b780044 Mon Sep 17 00:00:00 2001
From: github-actions
Date: Tue, 28 Jan 2025 20:41:36 +0000
Subject: [PATCH 008/423] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[skip ci]
---
docs/en/docs/release-notes.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md
index 3db7042fb..20f800d85 100644
--- a/docs/en/docs/release-notes.md
+++ b/docs/en/docs/release-notes.md
@@ -7,6 +7,10 @@ hide:
## Latest Changes
+### Docs
+
+* 👥 Update FastAPI People - Experts. PR [#13269](https://github.com/fastapi/fastapi/pull/13269) by [@tiangolo](https://github.com/tiangolo).
+
### Translations
* 🌐 Add Japanese translation for `docs/ja/docs/environment-variables.md`. PR [#13226](https://github.com/fastapi/fastapi/pull/13226) by [@k94-ishi](https://github.com/k94-ishi).
From 326fec16b9e3d61c584182b212436f88d81d0acb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?=
Date: Tue, 28 Jan 2025 21:47:33 +0000
Subject: [PATCH 009/423] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20and=20m?=
=?UTF-8?q?ove=20`scripts/notify=5Ftranslations.py`,=20no=20need=20for=20a?=
=?UTF-8?q?=20custom=20GitHub=20Action=20(#13270)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
.../actions/notify-translations/Dockerfile | 7 ---
.../actions/notify-translations/action.yml | 10 ---
.github/workflows/notify-translations.yml | 20 +++---
.../main.py => scripts/notify_translations.py | 63 +++++++++++--------
4 files changed, 51 insertions(+), 49 deletions(-)
delete mode 100644 .github/actions/notify-translations/Dockerfile
delete mode 100644 .github/actions/notify-translations/action.yml
rename .github/actions/notify-translations/app/main.py => scripts/notify_translations.py (89%)
diff --git a/.github/actions/notify-translations/Dockerfile b/.github/actions/notify-translations/Dockerfile
deleted file mode 100644
index b68b4bb1a..000000000
--- a/.github/actions/notify-translations/Dockerfile
+++ /dev/null
@@ -1,7 +0,0 @@
-FROM python:3.9
-
-RUN pip install httpx PyGithub "pydantic==1.5.1" "pyyaml>=5.3.1,<6.0.0"
-
-COPY ./app /app
-
-CMD ["python", "/app/main.py"]
diff --git a/.github/actions/notify-translations/action.yml b/.github/actions/notify-translations/action.yml
deleted file mode 100644
index c3579977c..000000000
--- a/.github/actions/notify-translations/action.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: "Notify Translations"
-description: "Notify in the issue for a translation when there's a new PR available"
-author: "Sebastián Ramírez "
-inputs:
- token:
- description: 'Token, to read the GitHub API. Can be passed in using {{ secrets.GITHUB_TOKEN }}'
- required: true
-runs:
- using: 'docker'
- image: 'Dockerfile'
diff --git a/.github/workflows/notify-translations.yml b/.github/workflows/notify-translations.yml
index 187322bca..c96992689 100644
--- a/.github/workflows/notify-translations.yml
+++ b/.github/workflows/notify-translations.yml
@@ -15,15 +15,14 @@ on:
required: false
default: 'false'
-permissions:
- discussions: write
-
env:
UV_SYSTEM_PYTHON: 1
jobs:
- notify-translations:
+ job:
runs-on: ubuntu-latest
+ permissions:
+ discussions: write
steps:
- name: Dump GitHub context
env:
@@ -42,12 +41,19 @@ jobs:
cache-dependency-glob: |
requirements**.txt
pyproject.toml
+ - name: Install Dependencies
+ run: uv pip install -r requirements-github-actions.txt
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
with:
limit-access-to-actor: true
- - uses: ./.github/actions/notify-translations
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Notify Translations
+ run: python ./scripts/notify_translations.py
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ NUMBER: ${{ github.event.inputs.number || null }}
+ DEBUG: ${{ github.event.inputs.debug_enabled || 'false' }}
diff --git a/.github/actions/notify-translations/app/main.py b/scripts/notify_translations.py
similarity index 89%
rename from .github/actions/notify-translations/app/main.py
rename to scripts/notify_translations.py
index 716232d49..7a43019a6 100644
--- a/.github/actions/notify-translations/app/main.py
+++ b/scripts/notify_translations.py
@@ -7,12 +7,13 @@ from typing import Any, Dict, List, Union, cast
import httpx
from github import Github
-from pydantic import BaseModel, BaseSettings, SecretStr
+from pydantic import BaseModel, SecretStr
+from pydantic_settings import BaseSettings
awaiting_label = "awaiting-review"
lang_all_label = "lang-all"
approved_label = "approved-1"
-translations_path = Path(__file__).parent / "translations.yml"
+
github_graphql_url = "https://api.github.com/graphql"
questions_translations_category_id = "DIC_kwDOCZduT84CT5P9"
@@ -176,19 +177,20 @@ class AllDiscussionsResponse(BaseModel):
class Settings(BaseSettings):
github_repository: str
- input_token: SecretStr
+ github_token: SecretStr
github_event_path: Path
github_event_name: Union[str, None] = None
httpx_timeout: int = 30
- input_debug: Union[bool, None] = False
+ debug: Union[bool, None] = False
+ number: int | None = None
class PartialGitHubEventIssue(BaseModel):
- number: int
+ number: int | None = None
class PartialGitHubEvent(BaseModel):
- pull_request: PartialGitHubEventIssue
+ pull_request: PartialGitHubEventIssue | None = None
def get_graphql_response(
@@ -202,9 +204,7 @@ def get_graphql_response(
comment_id: Union[str, None] = None,
body: Union[str, None] = None,
) -> Dict[str, Any]:
- headers = {"Authorization": f"token {settings.input_token.get_secret_value()}"}
- # some fields are only used by one query, but GraphQL allows unused variables, so
- # keep them here for simplicity
+ headers = {"Authorization": f"token {settings.github_token.get_secret_value()}"}
variables = {
"after": after,
"category_id": category_id,
@@ -228,37 +228,40 @@ def get_graphql_response(
data = response.json()
if "errors" in data:
logging.error(f"Errors in response, after: {after}, category_id: {category_id}")
+ logging.error(data["errors"])
logging.error(response.text)
raise RuntimeError(response.text)
return cast(Dict[str, Any], data)
-def get_graphql_translation_discussions(*, settings: Settings):
+def get_graphql_translation_discussions(
+ *, settings: Settings
+) -> List[AllDiscussionsDiscussionNode]:
data = get_graphql_response(
settings=settings,
query=all_discussions_query,
category_id=questions_translations_category_id,
)
- graphql_response = AllDiscussionsResponse.parse_obj(data)
+ graphql_response = AllDiscussionsResponse.model_validate(data)
return graphql_response.data.repository.discussions.nodes
def get_graphql_translation_discussion_comments_edges(
*, settings: Settings, discussion_number: int, after: Union[str, None] = None
-):
+) -> List[CommentsEdge]:
data = get_graphql_response(
settings=settings,
query=translation_discussion_query,
discussion_number=discussion_number,
after=after,
)
- graphql_response = CommentsResponse.parse_obj(data)
+ graphql_response = CommentsResponse.model_validate(data)
return graphql_response.data.repository.discussion.comments.edges
def get_graphql_translation_discussion_comments(
*, settings: Settings, discussion_number: int
-):
+) -> list[Comment]:
comment_nodes: List[Comment] = []
discussion_edges = get_graphql_translation_discussion_comments_edges(
settings=settings, discussion_number=discussion_number
@@ -276,43 +279,49 @@ def get_graphql_translation_discussion_comments(
return comment_nodes
-def create_comment(*, settings: Settings, discussion_id: str, body: str):
+def create_comment(*, settings: Settings, discussion_id: str, body: str) -> Comment:
data = get_graphql_response(
settings=settings,
query=add_comment_mutation,
discussion_id=discussion_id,
body=body,
)
- response = AddCommentResponse.parse_obj(data)
+ response = AddCommentResponse.model_validate(data)
return response.data.addDiscussionComment.comment
-def update_comment(*, settings: Settings, comment_id: str, body: str):
+def update_comment(*, settings: Settings, comment_id: str, body: str) -> Comment:
data = get_graphql_response(
settings=settings,
query=update_comment_mutation,
comment_id=comment_id,
body=body,
)
- response = UpdateCommentResponse.parse_obj(data)
+ response = UpdateCommentResponse.model_validate(data)
return response.data.updateDiscussionComment.comment
-if __name__ == "__main__":
+def main() -> None:
settings = Settings()
- if settings.input_debug:
+ if settings.debug:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
- logging.debug(f"Using config: {settings.json()}")
- g = Github(settings.input_token.get_secret_value())
+ logging.debug(f"Using config: {settings.model_dump_json()}")
+ g = Github(settings.github_token.get_secret_value())
repo = g.get_repo(settings.github_repository)
if not settings.github_event_path.is_file():
raise RuntimeError(
f"No github event file available at: {settings.github_event_path}"
)
contents = settings.github_event_path.read_text()
- github_event = PartialGitHubEvent.parse_raw(contents)
+ github_event = PartialGitHubEvent.model_validate_json(contents)
+ logging.info(f"Using GitHub event: {github_event}")
+ number = (
+ github_event.pull_request and github_event.pull_request.number
+ ) or settings.number
+ if number is None:
+ raise RuntimeError("No PR number available")
# Avoid race conditions with multiple labels
sleep_time = random.random() * 10 # random number between 0 and 10 seconds
@@ -323,8 +332,8 @@ if __name__ == "__main__":
time.sleep(sleep_time)
# Get PR
- logging.debug(f"Processing PR: #{github_event.pull_request.number}")
- pr = repo.get_pull(github_event.pull_request.number)
+ logging.debug(f"Processing PR: #{number}")
+ pr = repo.get_pull(number)
label_strs = {label.name for label in pr.get_labels()}
langs = []
for label in label_strs:
@@ -415,3 +424,7 @@ if __name__ == "__main__":
f"There doesn't seem to be anything to be done about PR #{pr.number}"
)
logging.info("Finished")
+
+
+if __name__ == "__main__":
+ main()
From a058d8ecbc41b329bad2f5ca0575c50b7bc171b2 Mon Sep 17 00:00:00 2001
From: github-actions
Date: Tue, 28 Jan 2025 21:47:55 +0000
Subject: [PATCH 010/423] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[skip ci]
---
docs/en/docs/release-notes.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md
index 20f800d85..2abee78dc 100644
--- a/docs/en/docs/release-notes.md
+++ b/docs/en/docs/release-notes.md
@@ -20,6 +20,7 @@ hide:
### Internal
+* ♻️ Refactor and move `scripts/notify_translations.py`, no need for a custom GitHub Action. PR [#13270](https://github.com/fastapi/fastapi/pull/13270) by [@tiangolo](https://github.com/tiangolo).
* 🔨 Update FastAPI People Experts script, refactor and optimize data fetching to handle rate limits. PR [#13267](https://github.com/fastapi/fastapi/pull/13267) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4. PR [#13251](https://github.com/fastapi/fastapi/pull/13251) by [@dependabot[bot]](https://github.com/apps/dependabot).
From 0a2b24653b2b0e2a4753a1e239013b26fa1516b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?=
Date: Tue, 28 Jan 2025 22:30:15 +0000
Subject: [PATCH 011/423] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Fix=20`notify=5Ftr?=
=?UTF-8?q?anslations.py`=20empty=20env=20var=20handling=20for=20PR=20labe?=
=?UTF-8?q?l=20events=20vs=20workflow=5Fdispatch=20(#13272)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
scripts/notify_translations.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/scripts/notify_translations.py b/scripts/notify_translations.py
index 7a43019a6..c300624db 100644
--- a/scripts/notify_translations.py
+++ b/scripts/notify_translations.py
@@ -176,6 +176,8 @@ class AllDiscussionsResponse(BaseModel):
class Settings(BaseSettings):
+ model_config = {"env_ignore_empty": True}
+
github_repository: str
github_token: SecretStr
github_event_path: Path
From 92b745461cb280aea3c5905c5e241502207064fa Mon Sep 17 00:00:00 2001
From: github-actions
Date: Tue, 28 Jan 2025 22:30:38 +0000
Subject: [PATCH 012/423] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[skip ci]
---
docs/en/docs/release-notes.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md
index 2abee78dc..e65fe9848 100644
--- a/docs/en/docs/release-notes.md
+++ b/docs/en/docs/release-notes.md
@@ -20,6 +20,7 @@ hide:
### Internal
+* ♻️ Fix `notify_translations.py` empty env var handling for PR label events vs workflow_dispatch. PR [#13272](https://github.com/fastapi/fastapi/pull/13272) by [@tiangolo](https://github.com/tiangolo).
* ♻️ Refactor and move `scripts/notify_translations.py`, no need for a custom GitHub Action. PR [#13270](https://github.com/fastapi/fastapi/pull/13270) by [@tiangolo](https://github.com/tiangolo).
* 🔨 Update FastAPI People Experts script, refactor and optimize data fetching to handle rate limits. PR [#13267](https://github.com/fastapi/fastapi/pull/13267) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4. PR [#13251](https://github.com/fastapi/fastapi/pull/13251) by [@dependabot[bot]](https://github.com/apps/dependabot).
From e747f1938a3853c6af695f6447fd8f8749e1ba9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?=
Date: Tue, 28 Jan 2025 22:36:15 +0000
Subject: [PATCH 013/423] =?UTF-8?q?=F0=9F=94=A7=20Update=20Sponsors=20badg?=
=?UTF-8?q?es=20(#13271)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/en/data/sponsors_badge.yml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/docs/en/data/sponsors_badge.yml b/docs/en/data/sponsors_badge.yml
index 3e885a2f7..d507a500f 100644
--- a/docs/en/data/sponsors_badge.yml
+++ b/docs/en/data/sponsors_badge.yml
@@ -29,7 +29,12 @@ logins:
- andrew-propelauth
- svix
- zuplo-oss
+ - zuplo
- Kong
- speakeasy-api
- jess-render
- blockbee-io
+ - liblaber
+ - render-sponsorships
+ - renderinc
+ - stainless-api
From 93e9fed2e84554197a0b480104a9e3c5b8897545 Mon Sep 17 00:00:00 2001
From: github-actions
Date: Tue, 28 Jan 2025 22:36:38 +0000
Subject: [PATCH 014/423] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[skip ci]
---
docs/en/docs/release-notes.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md
index e65fe9848..c0cb88e21 100644
--- a/docs/en/docs/release-notes.md
+++ b/docs/en/docs/release-notes.md
@@ -20,6 +20,7 @@ hide:
### Internal
+* 🔧 Update Sponsors badges. PR [#13271](https://github.com/fastapi/fastapi/pull/13271) by [@tiangolo](https://github.com/tiangolo).
* ♻️ Fix `notify_translations.py` empty env var handling for PR label events vs workflow_dispatch. PR [#13272](https://github.com/fastapi/fastapi/pull/13272) by [@tiangolo](https://github.com/tiangolo).
* ♻️ Refactor and move `scripts/notify_translations.py`, no need for a custom GitHub Action. PR [#13270](https://github.com/fastapi/fastapi/pull/13270) by [@tiangolo](https://github.com/tiangolo).
* 🔨 Update FastAPI People Experts script, refactor and optimize data fetching to handle rate limits. PR [#13267](https://github.com/fastapi/fastapi/pull/13267) by [@tiangolo](https://github.com/tiangolo).
From 8c6f10b64a333cb1395da5cb714fd1aa28e7b5ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?=
Date: Tue, 28 Jan 2025 23:35:19 +0000
Subject: [PATCH 015/423] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20AnyIO=20?=
=?UTF-8?q?max=20version=20for=20tests,=20new=20range:=20`>=3D3.2.1,<5.0.0?=
=?UTF-8?q?`=20(#13273)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.github/workflows/test.yml | 4 ++++
requirements-tests.txt | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index e0daf7472..793e789e2 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -81,6 +81,10 @@ jobs:
- name: Install Pydantic v2
if: matrix.pydantic-version == 'pydantic-v2'
run: uv pip install --upgrade "pydantic>=2.0.2,<3.0.0"
+ # TODO: Remove this once Python 3.8 is no longer supported
+ - name: Install older AnyIO in Python 3.8
+ if: matrix.python-version == '3.8'
+ run: uv pip install "anyio[trio]<4.0.0"
- run: mkdir coverage
- name: Test
run: bash scripts/test.sh
diff --git a/requirements-tests.txt b/requirements-tests.txt
index 5be052307..91e7fb7aa 100644
--- a/requirements-tests.txt
+++ b/requirements-tests.txt
@@ -6,7 +6,7 @@ mypy ==1.8.0
dirty-equals ==0.8.0
sqlmodel==0.0.22
flask >=1.1.2,<4.0.0
-anyio[trio] >=3.2.1,<4.0.0
+anyio[trio] >=3.2.1,<5.0.0
PyJWT==2.8.0
pyyaml >=5.3.1,<7.0.0
passlib[bcrypt] >=1.7.2,<2.0.0
From eab0653a346196bff6928710410890a300aee4ae Mon Sep 17 00:00:00 2001
From: github-actions
Date: Tue, 28 Jan 2025 23:35:44 +0000
Subject: [PATCH 016/423] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[skip ci]
---
docs/en/docs/release-notes.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md
index c0cb88e21..ad8b85d0e 100644
--- a/docs/en/docs/release-notes.md
+++ b/docs/en/docs/release-notes.md
@@ -20,6 +20,7 @@ hide:
### Internal
+* ⬆️ Upgrade AnyIO max version for tests, new range: `>=3.2.1,<5.0.0`. PR [#13273](https://github.com/fastapi/fastapi/pull/13273) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update Sponsors badges. PR [#13271](https://github.com/fastapi/fastapi/pull/13271) by [@tiangolo](https://github.com/tiangolo).
* ♻️ Fix `notify_translations.py` empty env var handling for PR label events vs workflow_dispatch. PR [#13272](https://github.com/fastapi/fastapi/pull/13272) by [@tiangolo](https://github.com/tiangolo).
* ♻️ Refactor and move `scripts/notify_translations.py`, no need for a custom GitHub Action. PR [#13270](https://github.com/fastapi/fastapi/pull/13270) by [@tiangolo](https://github.com/tiangolo).
From bd106fc750fb0d4b114b69e1f1f9ae5dabaada44 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?=
Date: Wed, 29 Jan 2025 18:02:27 +0000
Subject: [PATCH 017/423] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Add=20support=20fo?=
=?UTF-8?q?r=20Python=203.13=20(#13274)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
.github/workflows/test.yml | 1 +
pyproject.toml | 3 +++
2 files changed, 4 insertions(+)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 793e789e2..5e8092641 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -48,6 +48,7 @@ jobs:
strategy:
matrix:
python-version:
+ - "3.13"
- "3.12"
- "3.11"
- "3.10"
diff --git a/pyproject.toml b/pyproject.toml
index 381eb50bf..51d63fd44 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -38,6 +38,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
"Topic :: Internet :: WWW/HTTP",
]
@@ -162,6 +163,8 @@ filterwarnings = [
# Ref: https://github.com/python-trio/trio/pull/3054
# Remove once there's a new version of Trio
'ignore:The `hash` argument is deprecated*:DeprecationWarning:trio',
+ # Ignore flaky coverage / pytest warning about SQLite connection, only applies to Python 3.13 and Pydantic v1
+ 'ignore:Exception ignored in.
Date: Wed, 29 Jan 2025 18:02:50 +0000
Subject: [PATCH 018/423] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[skip ci]
---
docs/en/docs/release-notes.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md
index ad8b85d0e..e21d2521f 100644
--- a/docs/en/docs/release-notes.md
+++ b/docs/en/docs/release-notes.md
@@ -20,6 +20,7 @@ hide:
### Internal
+* ⬆️ Add support for Python 3.13. PR [#13274](https://github.com/fastapi/fastapi/pull/13274) by [@tiangolo](https://github.com/tiangolo).
* ⬆️ Upgrade AnyIO max version for tests, new range: `>=3.2.1,<5.0.0`. PR [#13273](https://github.com/fastapi/fastapi/pull/13273) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update Sponsors badges. PR [#13271](https://github.com/fastapi/fastapi/pull/13271) by [@tiangolo](https://github.com/tiangolo).
* ♻️ Fix `notify_translations.py` empty env var handling for PR label events vs workflow_dispatch. PR [#13272](https://github.com/fastapi/fastapi/pull/13272) by [@tiangolo](https://github.com/tiangolo).
From c5b5af7c532e66d3ac25bd36630e2cd89f24d049 Mon Sep 17 00:00:00 2001
From: Alejandra <90076947+alejsdev@users.noreply.github.com>
Date: Thu, 30 Jan 2025 12:04:34 +0000
Subject: [PATCH 019/423] =?UTF-8?q?=E2=9C=85=20Simplify=20tests=20for=20re?=
=?UTF-8?q?quest=5Ffiles=20(#13182)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Sebastián Ramírez
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
.../test_tutorial003.py | 21 +-
.../test_tutorial003_an.py | 273 -----------------
.../test_tutorial003_an_py310.py | 279 ------------------
.../test_tutorial003_an_py39.py | 279 ------------------
.../test_tutorial003_py310.py | 279 ------------------
.../test_request_files/test_tutorial001.py | 42 +--
.../test_request_files/test_tutorial001_02.py | 35 ++-
.../test_tutorial001_02_an.py | 208 -------------
.../test_tutorial001_02_an_py310.py | 220 --------------
.../test_tutorial001_02_an_py39.py | 220 --------------
.../test_tutorial001_02_py310.py | 220 --------------
.../test_request_files/test_tutorial001_03.py | 28 +-
.../test_tutorial001_03_an.py | 159 ----------
.../test_tutorial001_03_an_py39.py | 167 -----------
.../test_request_files/test_tutorial001_an.py | 218 --------------
.../test_tutorial001_an_py39.py | 228 --------------
.../test_request_files/test_tutorial002.py | 39 ++-
.../test_request_files/test_tutorial002_an.py | 249 ----------------
.../test_tutorial002_an_py39.py | 268 -----------------
.../test_tutorial002_py39.py | 279 ------------------
.../test_request_files/test_tutorial003.py | 35 ++-
.../test_request_files/test_tutorial003_an.py | 194 ------------
.../test_tutorial003_an_py39.py | 222 --------------
.../test_tutorial003_py39.py | 222 --------------
24 files changed, 146 insertions(+), 4238 deletions(-)
delete mode 100644 tests/test_tutorial/test_body_multiple_params/test_tutorial003_an.py
delete mode 100644 tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py310.py
delete mode 100644 tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py39.py
delete mode 100644 tests/test_tutorial/test_body_multiple_params/test_tutorial003_py310.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial001_02_an.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial001_02_an_py310.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial001_02_an_py39.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial001_02_py310.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial001_03_an.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial001_03_an_py39.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial001_an.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial001_an_py39.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial002_an.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial002_an_py39.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial002_py39.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial003_an.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial003_an_py39.py
delete mode 100644 tests/test_tutorial/test_request_files/test_tutorial003_py39.py
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py
index c26f8b89b..d18ceae48 100644
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py
+++ b/tests/test_tutorial/test_body_multiple_params/test_tutorial003.py
@@ -1,13 +1,26 @@
+import importlib
+
import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
+from ...utils import needs_py39, needs_py310
+
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial003 import app
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial003",
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ "tutorial003_an",
+ pytest.param("tutorial003_an_py39", marks=needs_py39),
+ pytest.param("tutorial003_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_multiple_params.{request.param}")
- client = TestClient(app)
+ client = TestClient(mod.app)
return client
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an.py
deleted file mode 100644
index 62c7e2fad..000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an.py
+++ /dev/null
@@ -1,273 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial003_an import app
-
- client = TestClient(app)
- return client
-
-
-def test_post_body_valid(client: TestClient):
- response = client.put(
- "/items/5",
- json={
- "importance": 2,
- "item": {"name": "Foo", "price": 50.5},
- "user": {"username": "Dave"},
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "importance": 2,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "user": {"username": "Dave", "full_name": None},
- }
-
-
-def test_post_body_no_data(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_post_body_empty_list(client: TestClient):
- response = client.put("/items/5", json=[])
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "User": {
- "title": "User",
- "required": ["username"],
- "type": "object",
- "properties": {
- "username": {"title": "Username", "type": "string"},
- "full_name": IsDict(
- {
- "title": "Full Name",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Full Name", "type": "string"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item", "user", "importance"],
- "type": "object",
- "properties": {
- "item": {"$ref": "#/components/schemas/Item"},
- "user": {"$ref": "#/components/schemas/User"},
- "importance": {"title": "Importance", "type": "integer"},
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py310.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py310.py
deleted file mode 100644
index f46430fb5..000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py310.py
+++ /dev/null
@@ -1,279 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial003_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_post_body_valid(client: TestClient):
- response = client.put(
- "/items/5",
- json={
- "importance": 2,
- "item": {"name": "Foo", "price": 50.5},
- "user": {"username": "Dave"},
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "importance": 2,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "user": {"username": "Dave", "full_name": None},
- }
-
-
-@needs_py310
-def test_post_body_no_data(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py310
-def test_post_body_empty_list(client: TestClient):
- response = client.put("/items/5", json=[])
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "User": {
- "title": "User",
- "required": ["username"],
- "type": "object",
- "properties": {
- "username": {"title": "Username", "type": "string"},
- "full_name": IsDict(
- {
- "title": "Full Name",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Full Name", "type": "string"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item", "user", "importance"],
- "type": "object",
- "properties": {
- "item": {"$ref": "#/components/schemas/Item"},
- "user": {"$ref": "#/components/schemas/User"},
- "importance": {"title": "Importance", "type": "integer"},
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py39.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py39.py
deleted file mode 100644
index 29071cddc..000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_an_py39.py
+++ /dev/null
@@ -1,279 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial003_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_post_body_valid(client: TestClient):
- response = client.put(
- "/items/5",
- json={
- "importance": 2,
- "item": {"name": "Foo", "price": 50.5},
- "user": {"username": "Dave"},
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "importance": 2,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "user": {"username": "Dave", "full_name": None},
- }
-
-
-@needs_py39
-def test_post_body_no_data(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_post_body_empty_list(client: TestClient):
- response = client.put("/items/5", json=[])
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "User": {
- "title": "User",
- "required": ["username"],
- "type": "object",
- "properties": {
- "username": {"title": "Username", "type": "string"},
- "full_name": IsDict(
- {
- "title": "Full Name",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Full Name", "type": "string"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item", "user", "importance"],
- "type": "object",
- "properties": {
- "item": {"$ref": "#/components/schemas/Item"},
- "user": {"$ref": "#/components/schemas/User"},
- "importance": {"title": "Importance", "type": "integer"},
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_py310.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial003_py310.py
deleted file mode 100644
index 133afe9b5..000000000
--- a/tests/test_tutorial/test_body_multiple_params/test_tutorial003_py310.py
+++ /dev/null
@@ -1,279 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.body_multiple_params.tutorial003_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_post_body_valid(client: TestClient):
- response = client.put(
- "/items/5",
- json={
- "importance": 2,
- "item": {"name": "Foo", "price": 50.5},
- "user": {"username": "Dave"},
- },
- )
- assert response.status_code == 200
- assert response.json() == {
- "item_id": 5,
- "importance": 2,
- "item": {
- "name": "Foo",
- "price": 50.5,
- "description": None,
- "tax": None,
- },
- "user": {"username": "Dave", "full_name": None},
- }
-
-
-@needs_py310
-def test_post_body_no_data(client: TestClient):
- response = client.put("/items/5", json=None)
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py310
-def test_post_body_empty_list(client: TestClient):
- response = client.put("/items/5", json=[])
- assert response.status_code == 422
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "item"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "user"],
- "msg": "Field required",
- "input": None,
- },
- {
- "type": "missing",
- "loc": ["body", "importance"],
- "msg": "Field required",
- "input": None,
- },
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "item"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "user"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- {
- "loc": ["body", "importance"],
- "msg": "field required",
- "type": "value_error.missing",
- },
- ]
- }
- )
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/items/{item_id}": {
- "put": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Update Item",
- "operationId": "update_item_items__item_id__put",
- "parameters": [
- {
- "required": True,
- "schema": {"title": "Item Id", "type": "integer"},
- "name": "item_id",
- "in": "path",
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
- }
- }
- },
- "required": True,
- },
- }
- }
- },
- "components": {
- "schemas": {
- "Item": {
- "title": "Item",
- "required": ["name", "price"],
- "type": "object",
- "properties": {
- "name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
- },
- },
- "User": {
- "title": "User",
- "required": ["username"],
- "type": "object",
- "properties": {
- "username": {"title": "Username", "type": "string"},
- "full_name": IsDict(
- {
- "title": "Full Name",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Full Name", "type": "string"}
- ),
- },
- },
- "Body_update_item_items__item_id__put": {
- "title": "Body_update_item_items__item_id__put",
- "required": ["item", "user", "importance"],
- "type": "object",
- "properties": {
- "item": {"$ref": "#/components/schemas/Item"},
- "user": {"$ref": "#/components/schemas/User"},
- "importance": {"title": "Importance", "type": "integer"},
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001.py b/tests/test_tutorial/test_request_files/test_tutorial001.py
index f5817593b..b06919961 100644
--- a/tests/test_tutorial/test_request_files/test_tutorial001.py
+++ b/tests/test_tutorial/test_request_files/test_tutorial001.py
@@ -1,23 +1,28 @@
+import importlib
+
+import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.request_files.tutorial001 import app
+from ...utils import needs_py39
-client = TestClient(app)
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001",
+ "tutorial001_an",
+ pytest.param("tutorial001_an_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.request_files.{request.param}")
-file_required = {
- "detail": [
- {
- "loc": ["body", "file"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
-}
+ client = TestClient(mod.app)
+ return client
-def test_post_form_no_body():
+def test_post_form_no_body(client: TestClient):
response = client.post("/files/")
assert response.status_code == 422, response.text
assert response.json() == IsDict(
@@ -45,7 +50,7 @@ def test_post_form_no_body():
)
-def test_post_body_json():
+def test_post_body_json(client: TestClient):
response = client.post("/files/", json={"file": "Foo"})
assert response.status_code == 422, response.text
assert response.json() == IsDict(
@@ -73,41 +78,38 @@ def test_post_body_json():
)
-def test_post_file(tmp_path):
+def test_post_file(tmp_path, client: TestClient):
path = tmp_path / "test.txt"
path.write_bytes(b"")
- client = TestClient(app)
with path.open("rb") as file:
response = client.post("/files/", files={"file": file})
assert response.status_code == 200, response.text
assert response.json() == {"file_size": 14}
-def test_post_large_file(tmp_path):
+def test_post_large_file(tmp_path, client: TestClient):
default_pydantic_max_size = 2**16
path = tmp_path / "test.txt"
path.write_bytes(b"x" * (default_pydantic_max_size + 1))
- client = TestClient(app)
with path.open("rb") as file:
response = client.post("/files/", files={"file": file})
assert response.status_code == 200, response.text
assert response.json() == {"file_size": default_pydantic_max_size + 1}
-def test_post_upload_file(tmp_path):
+def test_post_upload_file(tmp_path, client: TestClient):
path = tmp_path / "test.txt"
path.write_bytes(b"")
- client = TestClient(app)
with path.open("rb") as file:
response = client.post("/uploadfile/", files={"file": file})
assert response.status_code == 200, response.text
assert response.json() == {"filename": "test.txt"}
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_02.py b/tests/test_tutorial/test_request_files/test_tutorial001_02.py
index 42f75442a..9075a1756 100644
--- a/tests/test_tutorial/test_request_files/test_tutorial001_02.py
+++ b/tests/test_tutorial/test_request_files/test_tutorial001_02.py
@@ -1,46 +1,63 @@
+import importlib
+from pathlib import Path
+
+import pytest
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.request_files.tutorial001_02 import app
+from ...utils import needs_py39, needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001_02",
+ pytest.param("tutorial001_02_py310", marks=needs_py310),
+ "tutorial001_02_an",
+ pytest.param("tutorial001_02_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_02_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.request_files.{request.param}")
-client = TestClient(app)
+ client = TestClient(mod.app)
+ return client
-def test_post_form_no_body():
+def test_post_form_no_body(client: TestClient):
response = client.post("/files/")
assert response.status_code == 200, response.text
assert response.json() == {"message": "No file sent"}
-def test_post_uploadfile_no_body():
+def test_post_uploadfile_no_body(client: TestClient):
response = client.post("/uploadfile/")
assert response.status_code == 200, response.text
assert response.json() == {"message": "No upload file sent"}
-def test_post_file(tmp_path):
+def test_post_file(tmp_path: Path, client: TestClient):
path = tmp_path / "test.txt"
path.write_bytes(b"")
- client = TestClient(app)
with path.open("rb") as file:
response = client.post("/files/", files={"file": file})
assert response.status_code == 200, response.text
assert response.json() == {"file_size": 14}
-def test_post_upload_file(tmp_path):
+def test_post_upload_file(tmp_path: Path, client: TestClient):
path = tmp_path / "test.txt"
path.write_bytes(b"")
- client = TestClient(app)
with path.open("rb") as file:
response = client.post("/uploadfile/", files={"file": file})
assert response.status_code == 200, response.text
assert response.json() == {"filename": "test.txt"}
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_02_an.py b/tests/test_tutorial/test_request_files/test_tutorial001_02_an.py
deleted file mode 100644
index f63eb339c..000000000
--- a/tests/test_tutorial/test_request_files/test_tutorial001_02_an.py
+++ /dev/null
@@ -1,208 +0,0 @@
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.request_files.tutorial001_02_an import app
-
-client = TestClient(app)
-
-
-def test_post_form_no_body():
- response = client.post("/files/")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "No file sent"}
-
-
-def test_post_uploadfile_no_body():
- response = client.post("/uploadfile/")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "No upload file sent"}
-
-
-def test_post_file(tmp_path):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- client = TestClient(app)
- with path.open("rb") as file:
- response = client.post("/files/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"file_size": 14}
-
-
-def test_post_upload_file(tmp_path):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- client = TestClient(app)
- with path.open("rb") as file:
- response = client.post("/uploadfile/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"filename": "test.txt"}
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/files/": {
- "post": {
- "summary": "Create File",
- "operationId": "create_file_files__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- )
- }
- }
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/uploadfile/": {
- "post": {
- "summary": "Create Upload File",
- "operationId": "create_upload_file_uploadfile__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- )
- }
- }
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- },
- "components": {
- "schemas": {
- "Body_create_file_files__post": {
- "title": "Body_create_file_files__post",
- "type": "object",
- "properties": {
- "file": IsDict(
- {
- "title": "File",
- "anyOf": [
- {"type": "string", "format": "binary"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "File", "type": "string", "format": "binary"}
- )
- },
- },
- "Body_create_upload_file_uploadfile__post": {
- "title": "Body_create_upload_file_uploadfile__post",
- "type": "object",
- "properties": {
- "file": IsDict(
- {
- "title": "File",
- "anyOf": [
- {"type": "string", "format": "binary"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "File", "type": "string", "format": "binary"}
- )
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_02_an_py310.py b/tests/test_tutorial/test_request_files/test_tutorial001_02_an_py310.py
deleted file mode 100644
index 94b6ac67e..000000000
--- a/tests/test_tutorial/test_request_files/test_tutorial001_02_an_py310.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from pathlib import Path
-
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.request_files.tutorial001_02_an_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_post_form_no_body(client: TestClient):
- response = client.post("/files/")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "No file sent"}
-
-
-@needs_py310
-def test_post_uploadfile_no_body(client: TestClient):
- response = client.post("/uploadfile/")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "No upload file sent"}
-
-
-@needs_py310
-def test_post_file(tmp_path: Path, client: TestClient):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- with path.open("rb") as file:
- response = client.post("/files/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"file_size": 14}
-
-
-@needs_py310
-def test_post_upload_file(tmp_path: Path, client: TestClient):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- with path.open("rb") as file:
- response = client.post("/uploadfile/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"filename": "test.txt"}
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/files/": {
- "post": {
- "summary": "Create File",
- "operationId": "create_file_files__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- )
- }
- }
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/uploadfile/": {
- "post": {
- "summary": "Create Upload File",
- "operationId": "create_upload_file_uploadfile__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- )
- }
- }
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- },
- "components": {
- "schemas": {
- "Body_create_file_files__post": {
- "title": "Body_create_file_files__post",
- "type": "object",
- "properties": {
- "file": IsDict(
- {
- "title": "File",
- "anyOf": [
- {"type": "string", "format": "binary"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "File", "type": "string", "format": "binary"}
- )
- },
- },
- "Body_create_upload_file_uploadfile__post": {
- "title": "Body_create_upload_file_uploadfile__post",
- "type": "object",
- "properties": {
- "file": IsDict(
- {
- "title": "File",
- "anyOf": [
- {"type": "string", "format": "binary"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "File", "type": "string", "format": "binary"}
- )
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_02_an_py39.py b/tests/test_tutorial/test_request_files/test_tutorial001_02_an_py39.py
deleted file mode 100644
index fcb39f8f1..000000000
--- a/tests/test_tutorial/test_request_files/test_tutorial001_02_an_py39.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from pathlib import Path
-
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.request_files.tutorial001_02_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_post_form_no_body(client: TestClient):
- response = client.post("/files/")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "No file sent"}
-
-
-@needs_py39
-def test_post_uploadfile_no_body(client: TestClient):
- response = client.post("/uploadfile/")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "No upload file sent"}
-
-
-@needs_py39
-def test_post_file(tmp_path: Path, client: TestClient):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- with path.open("rb") as file:
- response = client.post("/files/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"file_size": 14}
-
-
-@needs_py39
-def test_post_upload_file(tmp_path: Path, client: TestClient):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- with path.open("rb") as file:
- response = client.post("/uploadfile/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"filename": "test.txt"}
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/files/": {
- "post": {
- "summary": "Create File",
- "operationId": "create_file_files__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- )
- }
- }
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/uploadfile/": {
- "post": {
- "summary": "Create Upload File",
- "operationId": "create_upload_file_uploadfile__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- )
- }
- }
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- },
- "components": {
- "schemas": {
- "Body_create_file_files__post": {
- "title": "Body_create_file_files__post",
- "type": "object",
- "properties": {
- "file": IsDict(
- {
- "title": "File",
- "anyOf": [
- {"type": "string", "format": "binary"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "File", "type": "string", "format": "binary"}
- )
- },
- },
- "Body_create_upload_file_uploadfile__post": {
- "title": "Body_create_upload_file_uploadfile__post",
- "type": "object",
- "properties": {
- "file": IsDict(
- {
- "title": "File",
- "anyOf": [
- {"type": "string", "format": "binary"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "File", "type": "string", "format": "binary"}
- )
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_02_py310.py b/tests/test_tutorial/test_request_files/test_tutorial001_02_py310.py
deleted file mode 100644
index a700752a3..000000000
--- a/tests/test_tutorial/test_request_files/test_tutorial001_02_py310.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from pathlib import Path
-
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.request_files.tutorial001_02_py310 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py310
-def test_post_form_no_body(client: TestClient):
- response = client.post("/files/")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "No file sent"}
-
-
-@needs_py310
-def test_post_uploadfile_no_body(client: TestClient):
- response = client.post("/uploadfile/")
- assert response.status_code == 200, response.text
- assert response.json() == {"message": "No upload file sent"}
-
-
-@needs_py310
-def test_post_file(tmp_path: Path, client: TestClient):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- with path.open("rb") as file:
- response = client.post("/files/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"file_size": 14}
-
-
-@needs_py310
-def test_post_upload_file(tmp_path: Path, client: TestClient):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- with path.open("rb") as file:
- response = client.post("/uploadfile/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"filename": "test.txt"}
-
-
-@needs_py310
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/files/": {
- "post": {
- "summary": "Create File",
- "operationId": "create_file_files__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- )
- }
- }
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/uploadfile/": {
- "post": {
- "summary": "Create Upload File",
- "operationId": "create_upload_file_uploadfile__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": IsDict(
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- ],
- "title": "Body",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- )
- }
- }
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- },
- "components": {
- "schemas": {
- "Body_create_file_files__post": {
- "title": "Body_create_file_files__post",
- "type": "object",
- "properties": {
- "file": IsDict(
- {
- "title": "File",
- "anyOf": [
- {"type": "string", "format": "binary"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "File", "type": "string", "format": "binary"}
- )
- },
- },
- "Body_create_upload_file_uploadfile__post": {
- "title": "Body_create_upload_file_uploadfile__post",
- "type": "object",
- "properties": {
- "file": IsDict(
- {
- "title": "File",
- "anyOf": [
- {"type": "string", "format": "binary"},
- {"type": "null"},
- ],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "File", "type": "string", "format": "binary"}
- )
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_03.py b/tests/test_tutorial/test_request_files/test_tutorial001_03.py
index f02170814..9fbe2166c 100644
--- a/tests/test_tutorial/test_request_files/test_tutorial001_03.py
+++ b/tests/test_tutorial/test_request_files/test_tutorial001_03.py
@@ -1,33 +1,47 @@
+import importlib
+
+import pytest
from fastapi.testclient import TestClient
-from docs_src.request_files.tutorial001_03 import app
+from ...utils import needs_py39
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001_03",
+ "tutorial001_03_an",
+ pytest.param("tutorial001_03_an_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.request_files.{request.param}")
-client = TestClient(app)
+ client = TestClient(mod.app)
+ return client
-def test_post_file(tmp_path):
+def test_post_file(tmp_path, client: TestClient):
path = tmp_path / "test.txt"
path.write_bytes(b"")
- client = TestClient(app)
with path.open("rb") as file:
response = client.post("/files/", files={"file": file})
assert response.status_code == 200, response.text
assert response.json() == {"file_size": 14}
-def test_post_upload_file(tmp_path):
+def test_post_upload_file(tmp_path, client: TestClient):
path = tmp_path / "test.txt"
path.write_bytes(b"")
- client = TestClient(app)
with path.open("rb") as file:
response = client.post("/uploadfile/", files={"file": file})
assert response.status_code == 200, response.text
assert response.json() == {"filename": "test.txt"}
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_03_an.py b/tests/test_tutorial/test_request_files/test_tutorial001_03_an.py
deleted file mode 100644
index acfb749ce..000000000
--- a/tests/test_tutorial/test_request_files/test_tutorial001_03_an.py
+++ /dev/null
@@ -1,159 +0,0 @@
-from fastapi.testclient import TestClient
-
-from docs_src.request_files.tutorial001_03_an import app
-
-client = TestClient(app)
-
-
-def test_post_file(tmp_path):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- client = TestClient(app)
- with path.open("rb") as file:
- response = client.post("/files/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"file_size": 14}
-
-
-def test_post_upload_file(tmp_path):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- client = TestClient(app)
- with path.open("rb") as file:
- response = client.post("/uploadfile/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"filename": "test.txt"}
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/files/": {
- "post": {
- "summary": "Create File",
- "operationId": "create_file_files__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- }
- },
- "required": True,
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/uploadfile/": {
- "post": {
- "summary": "Create Upload File",
- "operationId": "create_upload_file_uploadfile__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- }
- },
- "required": True,
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- },
- "components": {
- "schemas": {
- "Body_create_file_files__post": {
- "title": "Body_create_file_files__post",
- "required": ["file"],
- "type": "object",
- "properties": {
- "file": {
- "title": "File",
- "type": "string",
- "description": "A file read as bytes",
- "format": "binary",
- }
- },
- },
- "Body_create_upload_file_uploadfile__post": {
- "title": "Body_create_upload_file_uploadfile__post",
- "required": ["file"],
- "type": "object",
- "properties": {
- "file": {
- "title": "File",
- "type": "string",
- "description": "A file read as UploadFile",
- "format": "binary",
- }
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_03_an_py39.py b/tests/test_tutorial/test_request_files/test_tutorial001_03_an_py39.py
deleted file mode 100644
index 36e5faac1..000000000
--- a/tests/test_tutorial/test_request_files/test_tutorial001_03_an_py39.py
+++ /dev/null
@@ -1,167 +0,0 @@
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.request_files.tutorial001_03_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_post_file(tmp_path, client: TestClient):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- with path.open("rb") as file:
- response = client.post("/files/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"file_size": 14}
-
-
-@needs_py39
-def test_post_upload_file(tmp_path, client: TestClient):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- with path.open("rb") as file:
- response = client.post("/uploadfile/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"filename": "test.txt"}
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/files/": {
- "post": {
- "summary": "Create File",
- "operationId": "create_file_files__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- }
- },
- "required": True,
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- "/uploadfile/": {
- "post": {
- "summary": "Create Upload File",
- "operationId": "create_upload_file_uploadfile__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- }
- },
- "required": True,
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- }
- },
- },
- "components": {
- "schemas": {
- "Body_create_file_files__post": {
- "title": "Body_create_file_files__post",
- "required": ["file"],
- "type": "object",
- "properties": {
- "file": {
- "title": "File",
- "type": "string",
- "description": "A file read as bytes",
- "format": "binary",
- }
- },
- },
- "Body_create_upload_file_uploadfile__post": {
- "title": "Body_create_upload_file_uploadfile__post",
- "required": ["file"],
- "type": "object",
- "properties": {
- "file": {
- "title": "File",
- "type": "string",
- "description": "A file read as UploadFile",
- "format": "binary",
- }
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_an.py b/tests/test_tutorial/test_request_files/test_tutorial001_an.py
deleted file mode 100644
index 1c78e3679..000000000
--- a/tests/test_tutorial/test_request_files/test_tutorial001_an.py
+++ /dev/null
@@ -1,218 +0,0 @@
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from docs_src.request_files.tutorial001_an import app
-
-client = TestClient(app)
-
-
-def test_post_form_no_body():
- response = client.post("/files/")
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "file"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "file"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-def test_post_body_json():
- response = client.post("/files/", json={"file": "Foo"})
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "file"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "file"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-def test_post_file(tmp_path):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- client = TestClient(app)
- with path.open("rb") as file:
- response = client.post("/files/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"file_size": 14}
-
-
-def test_post_large_file(tmp_path):
- default_pydantic_max_size = 2**16
- path = tmp_path / "test.txt"
- path.write_bytes(b"x" * (default_pydantic_max_size + 1))
-
- client = TestClient(app)
- with path.open("rb") as file:
- response = client.post("/files/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"file_size": default_pydantic_max_size + 1}
-
-
-def test_post_upload_file(tmp_path):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- client = TestClient(app)
- with path.open("rb") as file:
- response = client.post("/uploadfile/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"filename": "test.txt"}
-
-
-def test_openapi_schema():
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/files/": {
- "post": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Create File",
- "operationId": "create_file_files__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- }
- },
- "required": True,
- },
- }
- },
- "/uploadfile/": {
- "post": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Create Upload File",
- "operationId": "create_upload_file_uploadfile__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- }
- },
- "required": True,
- },
- }
- },
- },
- "components": {
- "schemas": {
- "Body_create_upload_file_uploadfile__post": {
- "title": "Body_create_upload_file_uploadfile__post",
- "required": ["file"],
- "type": "object",
- "properties": {
- "file": {"title": "File", "type": "string", "format": "binary"}
- },
- },
- "Body_create_file_files__post": {
- "title": "Body_create_file_files__post",
- "required": ["file"],
- "type": "object",
- "properties": {
- "file": {"title": "File", "type": "string", "format": "binary"}
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_an_py39.py b/tests/test_tutorial/test_request_files/test_tutorial001_an_py39.py
deleted file mode 100644
index 843fcec28..000000000
--- a/tests/test_tutorial/test_request_files/test_tutorial001_an_py39.py
+++ /dev/null
@@ -1,228 +0,0 @@
-import pytest
-from dirty_equals import IsDict
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py39
-
-
-@pytest.fixture(name="client")
-def get_client():
- from docs_src.request_files.tutorial001_an_py39 import app
-
- client = TestClient(app)
- return client
-
-
-@needs_py39
-def test_post_form_no_body(client: TestClient):
- response = client.post("/files/")
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "file"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "file"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-@needs_py39
-def test_post_body_json(client: TestClient):
- response = client.post("/files/", json={"file": "Foo"})
- assert response.status_code == 422, response.text
- assert response.json() == IsDict(
- {
- "detail": [
- {
- "type": "missing",
- "loc": ["body", "file"],
- "msg": "Field required",
- "input": None,
- }
- ]
- }
- ) | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "detail": [
- {
- "loc": ["body", "file"],
- "msg": "field required",
- "type": "value_error.missing",
- }
- ]
- }
- )
-
-
-@needs_py39
-def test_post_file(tmp_path, client: TestClient):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- with path.open("rb") as file:
- response = client.post("/files/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"file_size": 14}
-
-
-@needs_py39
-def test_post_large_file(tmp_path, client: TestClient):
- default_pydantic_max_size = 2**16
- path = tmp_path / "test.txt"
- path.write_bytes(b"x" * (default_pydantic_max_size + 1))
-
- with path.open("rb") as file:
- response = client.post("/files/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"file_size": default_pydantic_max_size + 1}
-
-
-@needs_py39
-def test_post_upload_file(tmp_path, client: TestClient):
- path = tmp_path / "test.txt"
- path.write_bytes(b"")
-
- with path.open("rb") as file:
- response = client.post("/uploadfile/", files={"file": file})
- assert response.status_code == 200, response.text
- assert response.json() == {"filename": "test.txt"}
-
-
-@needs_py39
-def test_openapi_schema(client: TestClient):
- response = client.get("/openapi.json")
- assert response.status_code == 200, response.text
- assert response.json() == {
- "openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
- "paths": {
- "/files/": {
- "post": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Create File",
- "operationId": "create_file_files__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": {
- "$ref": "#/components/schemas/Body_create_file_files__post"
- }
- }
- },
- "required": True,
- },
- }
- },
- "/uploadfile/": {
- "post": {
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {"application/json": {"schema": {}}},
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- },
- },
- },
- "summary": "Create Upload File",
- "operationId": "create_upload_file_uploadfile__post",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": {
- "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
- }
- }
- },
- "required": True,
- },
- }
- },
- },
- "components": {
- "schemas": {
- "Body_create_upload_file_uploadfile__post": {
- "title": "Body_create_upload_file_uploadfile__post",
- "required": ["file"],
- "type": "object",
- "properties": {
- "file": {"title": "File", "type": "string", "format": "binary"}
- },
- },
- "Body_create_file_files__post": {
- "title": "Body_create_file_files__post",
- "required": ["file"],
- "type": "object",
- "properties": {
- "file": {"title": "File", "type": "string", "format": "binary"}
- },
- },
- "ValidationError": {
- "title": "ValidationError",
- "required": ["loc", "msg", "type"],
- "type": "object",
- "properties": {
- "loc": {
- "title": "Location",
- "type": "array",
- "items": {
- "anyOf": [{"type": "string"}, {"type": "integer"}]
- },
- },
- "msg": {"title": "Message", "type": "string"},
- "type": {"title": "Error Type", "type": "string"},
- },
- },
- "HTTPValidationError": {
- "title": "HTTPValidationError",
- "type": "object",
- "properties": {
- "detail": {
- "title": "Detail",
- "type": "array",
- "items": {"$ref": "#/components/schemas/ValidationError"},
- }
- },
- },
- }
- },
- }
diff --git a/tests/test_tutorial/test_request_files/test_tutorial002.py b/tests/test_tutorial/test_request_files/test_tutorial002.py
index db1552e5c..446a87657 100644
--- a/tests/test_tutorial/test_request_files/test_tutorial002.py
+++ b/tests/test_tutorial/test_request_files/test_tutorial002.py
@@ -1,12 +1,35 @@
+import importlib
+
+import pytest
from dirty_equals import IsDict
+from fastapi import FastAPI
from fastapi.testclient import TestClient
-from docs_src.request_files.tutorial002 import app
+from ...utils import needs_py39
+
+
+@pytest.fixture(
+ name="app",
+ params=[
+ "tutorial002",
+ "tutorial002_an",
+ pytest.param("tutorial002_py39", marks=needs_py39),
+ pytest.param("tutorial002_an_py39", marks=needs_py39),
+ ],
+)
+def get_app(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.request_files.{request.param}")
-client = TestClient(app)
+ return mod.app
+
+
+@pytest.fixture(name="client")
+def get_client(app: FastAPI):
+ client = TestClient(app)
+ return client
-def test_post_form_no_body():
+def test_post_form_no_body(client: TestClient):
response = client.post("/files/")
assert response.status_code == 422, response.text
assert response.json() == IsDict(
@@ -34,7 +57,7 @@ def test_post_form_no_body():
)
-def test_post_body_json():
+def test_post_body_json(client: TestClient):
response = client.post("/files/", json={"file": "Foo"})
assert response.status_code == 422, response.text
assert response.json() == IsDict(
@@ -62,7 +85,7 @@ def test_post_body_json():
)
-def test_post_files(tmp_path):
+def test_post_files(tmp_path, app: FastAPI):
path = tmp_path / "test.txt"
path.write_bytes(b"")
path2 = tmp_path / "test2.txt"
@@ -81,7 +104,7 @@ def test_post_files(tmp_path):
assert response.json() == {"file_sizes": [14, 15]}
-def test_post_upload_file(tmp_path):
+def test_post_upload_file(tmp_path, app: FastAPI):
path = tmp_path / "test.txt"
path.write_bytes(b"")
path2 = tmp_path / "test2.txt"
@@ -100,14 +123,14 @@ def test_post_upload_file(tmp_path):
assert response.json() == {"filenames": ["test.txt", "test2.txt"]}
-def test_get_root():
+def test_get_root(app: FastAPI):
client = TestClient(app)
response = client.get("/")
assert response.status_code == 200, response.text
assert b"