committed by
GitHub
1464 changed files with 76946 additions and 69933 deletions
@ -4,13 +4,13 @@ contact_links: |
|||
about: Please report security vulnerabilities to [email protected] |
|||
- name: Question or Problem |
|||
about: Ask a question or ask about a problem in GitHub Discussions. |
|||
url: https://github.com/tiangolo/fastapi/discussions/categories/questions |
|||
url: https://github.com/fastapi/fastapi/discussions/categories/questions |
|||
- name: Feature Request |
|||
about: To suggest an idea or ask about a feature, please start with a question saying what you would like to achieve. There might be a way to do it already. |
|||
url: https://github.com/tiangolo/fastapi/discussions/categories/questions |
|||
url: https://github.com/fastapi/fastapi/discussions/categories/questions |
|||
- name: Show and tell |
|||
about: Show what you built with FastAPI or to be used with FastAPI. |
|||
url: https://github.com/tiangolo/fastapi/discussions/categories/show-and-tell |
|||
url: https://github.com/fastapi/fastapi/discussions/categories/show-and-tell |
|||
- name: Translations |
|||
about: Coordinate translations in GitHub Discussions. |
|||
url: https://github.com/tiangolo/fastapi/discussions/categories/translations |
|||
url: https://github.com/fastapi/fastapi/discussions/categories/translations |
|||
|
@ -1,9 +0,0 @@ |
|||
FROM python:3.10 |
|||
|
|||
COPY ./requirements.txt /app/requirements.txt |
|||
|
|||
RUN pip install -r /app/requirements.txt |
|||
|
|||
COPY ./app /app |
|||
|
|||
CMD ["python", "/app/main.py"] |
@ -1,13 +0,0 @@ |
|||
name: Comment Docs Preview in PR |
|||
description: Comment with the docs URL preview in the PR |
|||
author: Sebastián Ramírez <[email protected]> |
|||
inputs: |
|||
token: |
|||
description: Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }} |
|||
required: true |
|||
deploy_url: |
|||
description: The deployment URL to comment in the PR |
|||
required: true |
|||
runs: |
|||
using: docker |
|||
image: Dockerfile |
@ -1,69 +0,0 @@ |
|||
import logging |
|||
import sys |
|||
from pathlib import Path |
|||
from typing import Union |
|||
|
|||
import httpx |
|||
from github import Github |
|||
from github.PullRequest import PullRequest |
|||
from pydantic import BaseModel, SecretStr, ValidationError |
|||
from pydantic_settings import BaseSettings |
|||
|
|||
github_api = "https://api.github.com" |
|||
|
|||
|
|||
class Settings(BaseSettings): |
|||
github_repository: str |
|||
github_event_path: Path |
|||
github_event_name: Union[str, None] = None |
|||
input_token: SecretStr |
|||
input_deploy_url: str |
|||
|
|||
|
|||
class PartialGithubEventHeadCommit(BaseModel): |
|||
id: str |
|||
|
|||
|
|||
class PartialGithubEventWorkflowRun(BaseModel): |
|||
head_commit: PartialGithubEventHeadCommit |
|||
|
|||
|
|||
class PartialGithubEvent(BaseModel): |
|||
workflow_run: PartialGithubEventWorkflowRun |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
logging.basicConfig(level=logging.INFO) |
|||
settings = Settings() |
|||
logging.info(f"Using config: {settings.json()}") |
|||
g = Github(settings.input_token.get_secret_value()) |
|||
repo = g.get_repo(settings.github_repository) |
|||
try: |
|||
event = PartialGithubEvent.parse_file(settings.github_event_path) |
|||
except ValidationError as e: |
|||
logging.error(f"Error parsing event file: {e.errors()}") |
|||
sys.exit(0) |
|||
use_pr: Union[PullRequest, None] = None |
|||
for pr in repo.get_pulls(): |
|||
if pr.head.sha == event.workflow_run.head_commit.id: |
|||
use_pr = pr |
|||
break |
|||
if not use_pr: |
|||
logging.error(f"No PR found for hash: {event.workflow_run.head_commit.id}") |
|||
sys.exit(0) |
|||
github_headers = { |
|||
"Authorization": f"token {settings.input_token.get_secret_value()}" |
|||
} |
|||
url = f"{github_api}/repos/{settings.github_repository}/issues/{use_pr.number}/comments" |
|||
logging.info(f"Using comments URL: {url}") |
|||
response = httpx.post( |
|||
url, |
|||
headers=github_headers, |
|||
json={ |
|||
"body": f"📝 Docs preview for commit {use_pr.head.sha} at: {settings.input_deploy_url}" |
|||
}, |
|||
) |
|||
if not (200 <= response.status_code <= 300): |
|||
logging.error(f"Error posting comment: {response.text}") |
|||
sys.exit(1) |
|||
logging.info("Finished") |
@ -1,4 +0,0 @@ |
|||
PyGithub |
|||
pydantic>=2.5.3,<3.0.0 |
|||
pydantic-settings>=2.1.0,<3.0.0 |
|||
httpx |
@ -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"] |
@ -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 <[email protected]>" |
|||
inputs: |
|||
token: |
|||
description: 'Token, to read the GitHub API. Can be passed in using {{ secrets.GITHUB_TOKEN }}' |
|||
required: true |
|||
runs: |
|||
using: 'docker' |
|||
image: 'Dockerfile' |
@ -1,7 +0,0 @@ |
|||
FROM python:3.9 |
|||
|
|||
RUN pip install httpx PyGithub "pydantic==2.0.2" pydantic-settings "pyyaml>=5.3.1,<6.0.0" |
|||
|
|||
COPY ./app /app |
|||
|
|||
CMD ["python", "/app/main.py"] |
@ -1,10 +0,0 @@ |
|||
name: "Generate FastAPI People" |
|||
description: "Generate the data for the FastAPI People page" |
|||
author: "Sebastián Ramírez <[email protected]>" |
|||
inputs: |
|||
token: |
|||
description: 'User token, to read the GitHub API. Can be passed in using {{ secrets.FASTAPI_PEOPLE }}' |
|||
required: true |
|||
runs: |
|||
using: 'docker' |
|||
image: 'Dockerfile' |
@ -1,682 +0,0 @@ |
|||
import logging |
|||
import subprocess |
|||
import sys |
|||
from collections import Counter, defaultdict |
|||
from datetime import datetime, timedelta, timezone |
|||
from pathlib import Path |
|||
from typing import Any, Container, DefaultDict, Dict, List, Set, 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: "tiangolo") { |
|||
discussions(first: 100, after: $after, categoryId: $category_id) { |
|||
edges { |
|||
cursor |
|||
node { |
|||
number |
|||
author { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
title |
|||
createdAt |
|||
comments(first: 100) { |
|||
nodes { |
|||
createdAt |
|||
author { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
isAnswer |
|||
replies(first: 10) { |
|||
nodes { |
|||
createdAt |
|||
author { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
""" |
|||
|
|||
|
|||
prs_query = """ |
|||
query Q($after: String) { |
|||
repository(name: "fastapi", owner: "tiangolo") { |
|||
pullRequests(first: 100, after: $after) { |
|||
edges { |
|||
cursor |
|||
node { |
|||
number |
|||
labels(first: 100) { |
|||
nodes { |
|||
name |
|||
} |
|||
} |
|||
author { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
title |
|||
createdAt |
|||
state |
|||
comments(first: 100) { |
|||
nodes { |
|||
createdAt |
|||
author { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
} |
|||
} |
|||
reviews(first:100) { |
|||
nodes { |
|||
author { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
state |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
""" |
|||
|
|||
sponsors_query = """ |
|||
query Q($after: String) { |
|||
user(login: "tiangolo") { |
|||
sponsorshipsAsMaintainer(first: 100, after: $after) { |
|||
edges { |
|||
cursor |
|||
node { |
|||
sponsorEntity { |
|||
... on Organization { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
... on User { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
} |
|||
tier { |
|||
name |
|||
monthlyPriceInDollars |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
""" |
|||
|
|||
|
|||
class Author(BaseModel): |
|||
login: str |
|||
avatarUrl: str |
|||
url: str |
|||
|
|||
|
|||
# Discussions |
|||
|
|||
|
|||
class CommentsNode(BaseModel): |
|||
createdAt: datetime |
|||
author: Union[Author, None] = None |
|||
|
|||
|
|||
class Replies(BaseModel): |
|||
nodes: List[CommentsNode] |
|||
|
|||
|
|||
class DiscussionsCommentsNode(CommentsNode): |
|||
replies: Replies |
|||
|
|||
|
|||
class Comments(BaseModel): |
|||
nodes: List[CommentsNode] |
|||
|
|||
|
|||
class DiscussionsComments(BaseModel): |
|||
nodes: List[DiscussionsCommentsNode] |
|||
|
|||
|
|||
class DiscussionsNode(BaseModel): |
|||
number: int |
|||
author: Union[Author, None] = None |
|||
title: str |
|||
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 |
|||
|
|||
|
|||
# PRs |
|||
|
|||
|
|||
class LabelNode(BaseModel): |
|||
name: str |
|||
|
|||
|
|||
class Labels(BaseModel): |
|||
nodes: List[LabelNode] |
|||
|
|||
|
|||
class ReviewNode(BaseModel): |
|||
author: Union[Author, None] = None |
|||
state: str |
|||
|
|||
|
|||
class Reviews(BaseModel): |
|||
nodes: List[ReviewNode] |
|||
|
|||
|
|||
class PullRequestNode(BaseModel): |
|||
number: int |
|||
labels: Labels |
|||
author: Union[Author, None] = None |
|||
title: str |
|||
createdAt: datetime |
|||
state: str |
|||
comments: Comments |
|||
reviews: Reviews |
|||
|
|||
|
|||
class PullRequestEdge(BaseModel): |
|||
cursor: str |
|||
node: PullRequestNode |
|||
|
|||
|
|||
class PullRequests(BaseModel): |
|||
edges: List[PullRequestEdge] |
|||
|
|||
|
|||
class PRsRepository(BaseModel): |
|||
pullRequests: PullRequests |
|||
|
|||
|
|||
class PRsResponseData(BaseModel): |
|||
repository: PRsRepository |
|||
|
|||
|
|||
class PRsResponse(BaseModel): |
|||
data: PRsResponseData |
|||
|
|||
|
|||
# Sponsors |
|||
|
|||
|
|||
class SponsorEntity(BaseModel): |
|||
login: str |
|||
avatarUrl: str |
|||
url: str |
|||
|
|||
|
|||
class Tier(BaseModel): |
|||
name: str |
|||
monthlyPriceInDollars: float |
|||
|
|||
|
|||
class SponsorshipAsMaintainerNode(BaseModel): |
|||
sponsorEntity: SponsorEntity |
|||
tier: Tier |
|||
|
|||
|
|||
class SponsorshipAsMaintainerEdge(BaseModel): |
|||
cursor: str |
|||
node: SponsorshipAsMaintainerNode |
|||
|
|||
|
|||
class SponsorshipAsMaintainer(BaseModel): |
|||
edges: List[SponsorshipAsMaintainerEdge] |
|||
|
|||
|
|||
class SponsorsUser(BaseModel): |
|||
sponsorshipsAsMaintainer: SponsorshipAsMaintainer |
|||
|
|||
|
|||
class SponsorsResponseData(BaseModel): |
|||
user: SponsorsUser |
|||
|
|||
|
|||
class SponsorsResponse(BaseModel): |
|||
data: SponsorsResponseData |
|||
|
|||
|
|||
class Settings(BaseSettings): |
|||
input_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.input_token.get_secret_value()}"} |
|||
# category_id is only used by one query, but GraphQL allows unused variables, so |
|||
# keep it here for simplicity |
|||
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, |
|||
): |
|||
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 |
|||
|
|||
|
|||
def get_graphql_pr_edges(*, settings: Settings, after: Union[str, None] = None): |
|||
data = get_graphql_response(settings=settings, query=prs_query, after=after) |
|||
graphql_response = PRsResponse.model_validate(data) |
|||
return graphql_response.data.repository.pullRequests.edges |
|||
|
|||
|
|||
def get_graphql_sponsor_edges(*, settings: Settings, after: Union[str, None] = None): |
|||
data = get_graphql_response(settings=settings, query=sponsors_query, after=after) |
|||
graphql_response = SponsorsResponse.model_validate(data) |
|||
return graphql_response.data.user.sponsorshipsAsMaintainer.edges |
|||
|
|||
|
|||
class DiscussionExpertsResults(BaseModel): |
|||
commenters: Counter |
|||
last_month_commenters: Counter |
|||
three_months_commenters: Counter |
|||
six_months_commenters: Counter |
|||
one_year_commenters: Counter |
|||
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] |
|||
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() |
|||
last_month_commenters = Counter() |
|||
three_months_commenters = Counter() |
|||
six_months_commenters = Counter() |
|||
one_year_commenters = Counter() |
|||
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_pr_nodes(settings: Settings) -> List[PullRequestNode]: |
|||
pr_nodes: List[PullRequestNode] = [] |
|||
pr_edges = get_graphql_pr_edges(settings=settings) |
|||
|
|||
while pr_edges: |
|||
for edge in pr_edges: |
|||
pr_nodes.append(edge.node) |
|||
last_edge = pr_edges[-1] |
|||
pr_edges = get_graphql_pr_edges(settings=settings, after=last_edge.cursor) |
|||
return pr_nodes |
|||
|
|||
|
|||
class ContributorsResults(BaseModel): |
|||
contributors: Counter |
|||
commenters: Counter |
|||
reviewers: Counter |
|||
translation_reviewers: Counter |
|||
authors: Dict[str, Author] |
|||
|
|||
|
|||
def get_contributors(pr_nodes: List[PullRequestNode]) -> ContributorsResults: |
|||
contributors = Counter() |
|||
commenters = Counter() |
|||
reviewers = Counter() |
|||
translation_reviewers = Counter() |
|||
authors: Dict[str, Author] = {} |
|||
|
|||
for pr in pr_nodes: |
|||
author_name = None |
|||
if pr.author: |
|||
authors[pr.author.login] = pr.author |
|||
author_name = pr.author.login |
|||
pr_commentors: Set[str] = set() |
|||
pr_reviewers: Set[str] = set() |
|||
for comment in pr.comments.nodes: |
|||
if comment.author: |
|||
authors[comment.author.login] = comment.author |
|||
if comment.author.login == author_name: |
|||
continue |
|||
pr_commentors.add(comment.author.login) |
|||
for author_name in pr_commentors: |
|||
commenters[author_name] += 1 |
|||
for review in pr.reviews.nodes: |
|||
if review.author: |
|||
authors[review.author.login] = review.author |
|||
pr_reviewers.add(review.author.login) |
|||
for label in pr.labels.nodes: |
|||
if label.name == "lang-all": |
|||
translation_reviewers[review.author.login] += 1 |
|||
break |
|||
for reviewer in pr_reviewers: |
|||
reviewers[reviewer] += 1 |
|||
if pr.state == "MERGED" and pr.author: |
|||
contributors[pr.author.login] += 1 |
|||
return ContributorsResults( |
|||
contributors=contributors, |
|||
commenters=commenters, |
|||
reviewers=reviewers, |
|||
translation_reviewers=translation_reviewers, |
|||
authors=authors, |
|||
) |
|||
|
|||
|
|||
def get_individual_sponsors(settings: Settings): |
|||
nodes: List[SponsorshipAsMaintainerNode] = [] |
|||
edges = get_graphql_sponsor_edges(settings=settings) |
|||
|
|||
while edges: |
|||
for edge in edges: |
|||
nodes.append(edge.node) |
|||
last_edge = edges[-1] |
|||
edges = get_graphql_sponsor_edges(settings=settings, after=last_edge.cursor) |
|||
|
|||
tiers: DefaultDict[float, Dict[str, SponsorEntity]] = defaultdict(dict) |
|||
for node in nodes: |
|||
tiers[node.tier.monthlyPriceInDollars][ |
|||
node.sponsorEntity.login |
|||
] = node.sponsorEntity |
|||
return tiers |
|||
|
|||
|
|||
def get_top_users( |
|||
*, |
|||
counter: Counter, |
|||
authors: Dict[str, Author], |
|||
skip_users: Container[str], |
|||
min_count: int = 2, |
|||
): |
|||
users = [] |
|||
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 |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
logging.basicConfig(level=logging.INFO) |
|||
settings = Settings() |
|||
logging.info(f"Using config: {settings.model_dump_json()}") |
|||
g = Github(settings.input_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) |
|||
pr_nodes = get_pr_nodes(settings=settings) |
|||
contributors_results = get_contributors(pr_nodes=pr_nodes) |
|||
authors = {**experts_results.authors, **contributors_results.authors} |
|||
maintainers_logins = {"tiangolo"} |
|||
bot_names = {"codecov", "github-actions", "pre-commit-ci", "dependabot"} |
|||
maintainers = [] |
|||
for login in maintainers_logins: |
|||
user = authors[login] |
|||
maintainers.append( |
|||
{ |
|||
"login": login, |
|||
"answers": experts_results.commenters[login], |
|||
"prs": contributors_results.contributors[login], |
|||
"avatarUrl": user.avatarUrl, |
|||
"url": user.url, |
|||
} |
|||
) |
|||
|
|||
skip_users = maintainers_logins | bot_names |
|||
experts = get_top_users( |
|||
counter=experts_results.commenters, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
last_month_experts = get_top_users( |
|||
counter=experts_results.last_month_commenters, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
three_months_experts = get_top_users( |
|||
counter=experts_results.three_months_commenters, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
six_months_experts = get_top_users( |
|||
counter=experts_results.six_months_commenters, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
one_year_experts = get_top_users( |
|||
counter=experts_results.one_year_commenters, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
top_contributors = get_top_users( |
|||
counter=contributors_results.contributors, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
top_reviewers = get_top_users( |
|||
counter=contributors_results.reviewers, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
top_translations_reviewers = get_top_users( |
|||
counter=contributors_results.translation_reviewers, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
|
|||
tiers = get_individual_sponsors(settings=settings) |
|||
keys = list(tiers.keys()) |
|||
keys.sort(reverse=True) |
|||
sponsors = [] |
|||
for key in keys: |
|||
sponsor_group = [] |
|||
for login, sponsor in tiers[key].items(): |
|||
sponsor_group.append( |
|||
{"login": login, "avatarUrl": sponsor.avatarUrl, "url": sponsor.url} |
|||
) |
|||
sponsors.append(sponsor_group) |
|||
|
|||
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, |
|||
"top_contributors": top_contributors, |
|||
"top_reviewers": top_reviewers, |
|||
"top_translations_reviewers": top_translations_reviewers, |
|||
} |
|||
github_sponsors = { |
|||
"sponsors": sponsors, |
|||
} |
|||
# For local development |
|||
# people_path = Path("../../../../docs/en/data/people.yml") |
|||
people_path = Path("./docs/en/data/people.yml") |
|||
github_sponsors_path = Path("./docs/en/data/github_sponsors.yml") |
|||
people_old_content = people_path.read_text(encoding="utf-8") |
|||
github_sponsors_old_content = github_sponsors_path.read_text(encoding="utf-8") |
|||
new_people_content = yaml.dump( |
|||
people, sort_keys=False, width=200, allow_unicode=True |
|||
) |
|||
new_github_sponsors_content = yaml.dump( |
|||
github_sponsors, sort_keys=False, width=200, allow_unicode=True |
|||
) |
|||
if ( |
|||
people_old_content == new_people_content |
|||
and github_sponsors_old_content == new_github_sponsors_content |
|||
): |
|||
logging.info("The FastAPI People data hasn't changed, finishing.") |
|||
sys.exit(0) |
|||
people_path.write_text(new_people_content, encoding="utf-8") |
|||
github_sponsors_path.write_text(new_github_sponsors_content, encoding="utf-8") |
|||
logging.info("Setting up GitHub Actions git user") |
|||
subprocess.run(["git", "config", "user.name", "github-actions"], check=True) |
|||
subprocess.run( |
|||
["git", "config", "user.email", "[email protected]"], check=True |
|||
) |
|||
branch_name = "fastapi-people" |
|||
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), str(github_sponsors_path)], check=True |
|||
) |
|||
logging.info("Committing updated file") |
|||
message = "👥 Update FastAPI People" |
|||
result = 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") |
@ -0,0 +1,38 @@ |
|||
docs: |
|||
- all: |
|||
- changed-files: |
|||
- any-glob-to-any-file: |
|||
- docs/en/docs/** |
|||
- docs_src/** |
|||
- all-globs-to-all-files: |
|||
- '!fastapi/**' |
|||
- '!pyproject.toml' |
|||
- '!docs/en/data/sponsors.yml' |
|||
- '!docs/en/overrides/main.html' |
|||
|
|||
lang-all: |
|||
- all: |
|||
- changed-files: |
|||
- any-glob-to-any-file: |
|||
- docs/*/docs/** |
|||
- all-globs-to-all-files: |
|||
- '!docs/en/docs/**' |
|||
- '!fastapi/**' |
|||
- '!pyproject.toml' |
|||
|
|||
internal: |
|||
- all: |
|||
- changed-files: |
|||
- any-glob-to-any-file: |
|||
- .github/** |
|||
- scripts/** |
|||
- .gitignore |
|||
- .pre-commit-config.yaml |
|||
- pdm_build.py |
|||
- requirements*.txt |
|||
- docs/en/data/sponsors.yml |
|||
- docs/en/overrides/main.html |
|||
- all-globs-to-all-files: |
|||
- '!docs/*/docs/**' |
|||
- '!fastapi/**' |
|||
- '!pyproject.toml' |
@ -0,0 +1,18 @@ |
|||
name: Add to Project |
|||
|
|||
on: |
|||
pull_request_target: |
|||
issues: |
|||
types: |
|||
- opened |
|||
- reopened |
|||
|
|||
jobs: |
|||
add-to-project: |
|||
name: Add to project |
|||
runs-on: ubuntu-latest |
|||
steps: |
|||
- uses: actions/[email protected] |
|||
with: |
|||
project-url: https://github.com/orgs/fastapi/projects/2 |
|||
github-token: ${{ secrets.PROJECTS_TOKEN }} |
@ -0,0 +1,53 @@ |
|||
name: FastAPI People Contributors |
|||
|
|||
on: |
|||
schedule: |
|||
- cron: "0 3 1 * *" |
|||
workflow_dispatch: |
|||
inputs: |
|||
debug_enabled: |
|||
description: "Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)" |
|||
required: false |
|||
default: "false" |
|||
|
|||
env: |
|||
UV_SYSTEM_PYTHON: 1 |
|||
|
|||
jobs: |
|||
job: |
|||
if: github.repository_owner == 'fastapi' |
|||
runs-on: ubuntu-latest |
|||
permissions: |
|||
contents: write |
|||
steps: |
|||
- name: Dump GitHub context |
|||
env: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
run: echo "$GITHUB_CONTEXT" |
|||
- uses: actions/checkout@v4 |
|||
- name: Set up Python |
|||
uses: actions/setup-python@v5 |
|||
with: |
|||
python-version: "3.11" |
|||
- name: Setup uv |
|||
uses: astral-sh/setup-uv@v5 |
|||
with: |
|||
version: "0.4.15" |
|||
enable-cache: true |
|||
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 |
|||
env: |
|||
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }} |
|||
- name: FastAPI People Contributors |
|||
run: python ./scripts/contributors.py |
|||
env: |
|||
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }} |
@ -6,6 +6,15 @@ on: |
|||
types: |
|||
- completed |
|||
|
|||
permissions: |
|||
deployments: write |
|||
issues: write |
|||
pull-requests: write |
|||
statuses: write |
|||
|
|||
env: |
|||
UV_SYSTEM_PYTHON: 1 |
|||
|
|||
jobs: |
|||
deploy-docs: |
|||
runs-on: ubuntu-latest |
|||
@ -15,34 +24,57 @@ jobs: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
run: echo "$GITHUB_CONTEXT" |
|||
- uses: actions/checkout@v4 |
|||
- name: Set up Python |
|||
uses: actions/setup-python@v5 |
|||
with: |
|||
python-version: "3.11" |
|||
- name: Setup uv |
|||
uses: astral-sh/setup-uv@v5 |
|||
with: |
|||
version: "0.4.15" |
|||
enable-cache: true |
|||
cache-dependency-glob: | |
|||
requirements**.txt |
|||
pyproject.toml |
|||
- name: Install GitHub Actions dependencies |
|||
run: uv pip install -r requirements-github-actions.txt |
|||
- name: Deploy Docs Status Pending |
|||
run: python ./scripts/deploy_docs_status.py |
|||
env: |
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
|||
COMMIT_SHA: ${{ github.event.workflow_run.head_sha }} |
|||
RUN_ID: ${{ github.run_id }} |
|||
- name: Clean site |
|||
run: | |
|||
rm -rf ./site |
|||
mkdir ./site |
|||
- name: Download Artifact Docs |
|||
id: download |
|||
uses: dawidd6/[email protected] |
|||
- uses: actions/download-artifact@v4 |
|||
with: |
|||
if_no_artifact_found: ignore |
|||
github_token: ${{ secrets.FASTAPI_PREVIEW_DOCS_DOWNLOAD_ARTIFACTS }} |
|||
workflow: build-docs.yml |
|||
run_id: ${{ github.event.workflow_run.id }} |
|||
name: docs-site |
|||
path: ./site/ |
|||
pattern: docs-site-* |
|||
merge-multiple: true |
|||
github-token: ${{ secrets.GITHUB_TOKEN }} |
|||
run-id: ${{ github.event.workflow_run.id }} |
|||
- name: Deploy to Cloudflare Pages |
|||
if: steps.download.outputs.found_artifact == 'true' |
|||
# hashFiles returns an empty string if there are no files |
|||
if: hashFiles('./site/*') |
|||
id: deploy |
|||
uses: cloudflare/pages-action@v1 |
|||
env: |
|||
PROJECT_NAME: fastapitiangolo |
|||
BRANCH: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'master' && 'main' ) || ( github.event.workflow_run.head_sha ) }} |
|||
# TODO: Use v3 when it's fixed, probably in v3.11 |
|||
# https://github.com/cloudflare/wrangler-action/issues/307 |
|||
uses: cloudflare/[email protected] |
|||
# uses: cloudflare/wrangler-action@v3 |
|||
with: |
|||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} |
|||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} |
|||
projectName: fastapitiangolo |
|||
directory: './site' |
|||
gitHubToken: ${{ secrets.GITHUB_TOKEN }} |
|||
branch: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'master' && 'main' ) || ( github.event.workflow_run.head_sha ) }} |
|||
command: pages deploy ./site --project-name=${{ env.PROJECT_NAME }} --branch=${{ env.BRANCH }} |
|||
- name: Comment Deploy |
|||
if: steps.deploy.outputs.url != '' |
|||
uses: ./.github/actions/comment-docs-preview-in-pr |
|||
with: |
|||
token: ${{ secrets.FASTAPI_PREVIEW_DOCS_COMMENT_DEPLOY }} |
|||
deploy_url: "${{ steps.deploy.outputs.url }}" |
|||
run: python ./scripts/deploy_docs_status.py |
|||
env: |
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
|||
DEPLOY_URL: ${{ steps.deploy.outputs.deployment-url }} |
|||
COMMIT_SHA: ${{ github.event.workflow_run.head_sha }} |
|||
RUN_ID: ${{ github.run_id }} |
|||
IS_DONE: "true" |
|||
|
@ -2,7 +2,7 @@ name: Issue Manager |
|||
|
|||
on: |
|||
schedule: |
|||
- cron: "10 3 * * *" |
|||
- cron: "13 22 * * *" |
|||
issue_comment: |
|||
types: |
|||
- created |
|||
@ -14,26 +14,34 @@ on: |
|||
- labeled |
|||
workflow_dispatch: |
|||
|
|||
permissions: |
|||
issues: write |
|||
pull-requests: write |
|||
|
|||
jobs: |
|||
issue-manager: |
|||
if: github.repository_owner == 'tiangolo' |
|||
if: github.repository_owner == 'fastapi' |
|||
runs-on: ubuntu-latest |
|||
steps: |
|||
- name: Dump GitHub context |
|||
env: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
run: echo "$GITHUB_CONTEXT" |
|||
- uses: tiangolo/[email protected].0 |
|||
- uses: tiangolo/[email protected].1 |
|||
with: |
|||
token: ${{ secrets.FASTAPI_ISSUE_MANAGER }} |
|||
token: ${{ secrets.GITHUB_TOKEN }} |
|||
config: > |
|||
{ |
|||
"answered": { |
|||
"delay": 864000, |
|||
"message": "Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs." |
|||
}, |
|||
"changes-requested": { |
|||
"waiting": { |
|||
"delay": 2628000, |
|||
"message": "As this PR had requested changes to be applied but has been inactive for a while, it's now going to be closed. But if there's anyone interested, feel free to create a new PR." |
|||
"message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR." |
|||
}, |
|||
"invalid": { |
|||
"delay": 0, |
|||
"message": "This was marked as invalid and will be closed now. If this is an error, please provide additional details." |
|||
} |
|||
} |
|||
|
@ -0,0 +1,33 @@ |
|||
name: Labels |
|||
on: |
|||
pull_request_target: |
|||
types: |
|||
- opened |
|||
- synchronize |
|||
- reopened |
|||
# For label-checker |
|||
- labeled |
|||
- unlabeled |
|||
|
|||
jobs: |
|||
labeler: |
|||
permissions: |
|||
contents: read |
|||
pull-requests: write |
|||
runs-on: ubuntu-latest |
|||
steps: |
|||
- uses: actions/labeler@v5 |
|||
if: ${{ github.event.action != 'labeled' && github.event.action != 'unlabeled' }} |
|||
- run: echo "Done adding labels" |
|||
# Run this after labeler applied labels |
|||
check-labels: |
|||
needs: |
|||
- labeler |
|||
permissions: |
|||
pull-requests: read |
|||
runs-on: ubuntu-latest |
|||
steps: |
|||
- uses: docker://agilepathway/pull-request-label-checker:latest |
|||
with: |
|||
one_of: breaking,security,feature,bug,refactor,upgrade,docs,lang-all,internal |
|||
repo_token: ${{ secrets.GITHUB_TOKEN }} |
@ -34,8 +34,7 @@ jobs: |
|||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }} |
|||
with: |
|||
limit-access-to-actor: true |
|||
- uses: docker://tiangolo/latest-changes:0.3.0 |
|||
# - uses: tiangolo/latest-changes@main |
|||
- uses: tiangolo/[email protected] |
|||
with: |
|||
token: ${{ secrets.GITHUB_TOKEN }} |
|||
latest_changes_file: docs/en/docs/release-notes.md |
|||
|
@ -35,7 +35,7 @@ jobs: |
|||
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }} |
|||
run: python -m build |
|||
- name: Publish |
|||
uses: pypa/gh-action-pypi-publish@v1.8.14 |
|||
uses: pypa/[email protected]2.4 |
|||
- name: Dump GitHub context |
|||
env: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
|
@ -8,6 +8,9 @@ on: |
|||
permissions: |
|||
statuses: write |
|||
|
|||
env: |
|||
UV_SYSTEM_PYTHON: 1 |
|||
|
|||
jobs: |
|||
smokeshow: |
|||
if: ${{ github.event.workflow_run.conclusion == 'success' }} |
|||
@ -18,23 +21,40 @@ jobs: |
|||
env: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
run: echo "$GITHUB_CONTEXT" |
|||
- uses: actions/checkout@v4 |
|||
- uses: actions/setup-python@v5 |
|||
with: |
|||
python-version: '3.9' |
|||
|
|||
- run: pip install smokeshow |
|||
|
|||
- uses: dawidd6/[email protected] |
|||
- name: Setup uv |
|||
uses: astral-sh/setup-uv@v5 |
|||
with: |
|||
github_token: ${{ secrets.FASTAPI_SMOKESHOW_DOWNLOAD_ARTIFACTS }} |
|||
workflow: test.yml |
|||
commit: ${{ github.event.workflow_run.head_sha }} |
|||
|
|||
- run: smokeshow upload coverage-html |
|||
version: "0.4.15" |
|||
enable-cache: true |
|||
cache-dependency-glob: | |
|||
requirements**.txt |
|||
pyproject.toml |
|||
- run: uv pip install -r requirements-github-actions.txt |
|||
- uses: actions/download-artifact@v4 |
|||
with: |
|||
name: coverage-html |
|||
path: htmlcov |
|||
github-token: ${{ secrets.GITHUB_TOKEN }} |
|||
run-id: ${{ github.event.workflow_run.id }} |
|||
# Try 5 times to upload coverage to smokeshow |
|||
- name: Upload coverage to Smokeshow |
|||
run: | |
|||
for i in 1 2 3 4 5; do |
|||
if smokeshow upload htmlcov; then |
|||
echo "Smokeshow upload success!" |
|||
break |
|||
fi |
|||
echo "Smokeshow upload error, sleep 1 sec and try again." |
|||
sleep 1 |
|||
done |
|||
env: |
|||
SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Coverage {coverage-percentage} |
|||
SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 100 |
|||
SMOKESHOW_GITHUB_CONTEXT: coverage |
|||
SMOKESHOW_GITHUB_TOKEN: ${{ secrets.FASTAPI_SMOKESHOW_UPLOAD }} |
|||
SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
|||
SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.workflow_run.head_sha }} |
|||
SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }} |
|||
|
@ -0,0 +1,52 @@ |
|||
name: FastAPI People Sponsors |
|||
|
|||
on: |
|||
schedule: |
|||
- cron: "0 6 1 * *" |
|||
workflow_dispatch: |
|||
inputs: |
|||
debug_enabled: |
|||
description: "Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)" |
|||
required: false |
|||
default: "false" |
|||
|
|||
env: |
|||
UV_SYSTEM_PYTHON: 1 |
|||
|
|||
jobs: |
|||
job: |
|||
if: github.repository_owner == 'fastapi' |
|||
runs-on: ubuntu-latest |
|||
permissions: |
|||
contents: write |
|||
steps: |
|||
- name: Dump GitHub context |
|||
env: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
run: echo "$GITHUB_CONTEXT" |
|||
- uses: actions/checkout@v4 |
|||
- name: Set up Python |
|||
uses: actions/setup-python@v5 |
|||
with: |
|||
python-version: "3.11" |
|||
- name: Setup uv |
|||
uses: astral-sh/setup-uv@v5 |
|||
with: |
|||
version: "0.4.15" |
|||
enable-cache: true |
|||
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 |
|||
- name: FastAPI People Sponsors |
|||
run: python ./scripts/sponsors.py |
|||
env: |
|||
SPONSORS_TOKEN: ${{ secrets.SPONSORS_TOKEN }} |
|||
PR_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }} |
@ -0,0 +1,40 @@ |
|||
name: Update Topic Repos |
|||
|
|||
on: |
|||
schedule: |
|||
- cron: "0 12 1 * *" |
|||
workflow_dispatch: |
|||
|
|||
env: |
|||
UV_SYSTEM_PYTHON: 1 |
|||
|
|||
jobs: |
|||
topic-repos: |
|||
if: github.repository_owner == 'fastapi' |
|||
runs-on: ubuntu-latest |
|||
permissions: |
|||
contents: write |
|||
steps: |
|||
- name: Dump GitHub context |
|||
env: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
run: echo "$GITHUB_CONTEXT" |
|||
- uses: actions/checkout@v4 |
|||
- name: Set up Python |
|||
uses: actions/setup-python@v5 |
|||
with: |
|||
python-version: "3.11" |
|||
- name: Setup uv |
|||
uses: astral-sh/setup-uv@v5 |
|||
with: |
|||
version: "0.4.15" |
|||
enable-cache: true |
|||
cache-dependency-glob: | |
|||
requirements**.txt |
|||
pyproject.toml |
|||
- name: Install GitHub Actions dependencies |
|||
run: uv pip install -r requirements-github-actions.txt |
|||
- name: Update Topic Repos |
|||
run: python ./scripts/topic_repos.py |
|||
env: |
|||
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }} |
@ -12,7 +12,7 @@ authors: |
|||
family-names: Ramírez |
|||
email: [email protected] |
|||
identifiers: |
|||
repository-code: 'https://github.com/tiangolo/fastapi' |
|||
repository-code: 'https://github.com/fastapi/fastapi' |
|||
url: 'https://fastapi.tiangolo.com' |
|||
abstract: >- |
|||
FastAPI framework, high performance, easy to learn, fast to code, |
|||
|
@ -1,185 +0,0 @@ |
|||
--- |
|||
hide: |
|||
- navigation |
|||
--- |
|||
|
|||
# FastAPI İnsanlar |
|||
|
|||
FastAPI-ın bütün mənşəli insanları qəbul edən heyrətamiz icması var. |
|||
|
|||
|
|||
|
|||
## Yaradıcı - İcraçı |
|||
|
|||
Salam! 👋 |
|||
|
|||
Bu mənəm: |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.maintainers %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Cavablar: {{ user.answers }}</div><div class="count">Pull Request-lər: {{ user.prs }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
Mən **FastAPI**-ın yaradıcısı və icraçısıyam. Əlavə məlumat almaq üçün [Yardım FastAPI - Yardım alın - Müəlliflə əlaqə qurun](help-fastapi.md#connect-with-the-author){.internal-link target=_blank} səhifəsinə baxa bilərsiniz. |
|||
|
|||
...Burada isə sizə icmanı göstərmək istəyirəm. |
|||
|
|||
--- |
|||
|
|||
**FastAPI** icmadan çoxlu dəstək alır və mən onların əməyini vurğulamaq istəyirəm. |
|||
|
|||
Bu insanlar: |
|||
|
|||
* [GitHub-da başqalarının suallarına kömək edirlər](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}. |
|||
* [Pull Request-lər yaradırlar](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}. |
|||
* Pull Request-ləri ([xüsusilə tərcümələr üçün vacib olan](contributing.md#translations){.internal-link target=_blank}.) nəzərdən keçirirlər. |
|||
|
|||
Bu insanlara təşəkkür edirəm. 👏 🙇 |
|||
|
|||
## Keçən ayın ən fəal istifadəçiləri |
|||
|
|||
Bu istifadəçilər keçən ay [GitHub-da başqalarının suallarına](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} ən çox kömək edənlərdir. ☕ |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.last_month_experts[:10] %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Cavablandırılmış suallar: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Mütəxəssislər |
|||
|
|||
Burada **FastAPI Mütəxəssisləri** var. 🤓 |
|||
|
|||
Bu istifadəçilər indiyə qədər [GitHub-da başqalarının suallarına](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} ən çox kömək edənlərdir. |
|||
|
|||
Onlar bir çox insanlara kömək edərək mütəxəssis olduqlarını sübut ediblər. ✨ |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.experts[:50] %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Cavablandırılmış suallar: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Ən yaxşı əməkdaşlar |
|||
|
|||
Burada **Ən yaxşı əməkdaşlar** var. 👷 |
|||
|
|||
Bu istifadəçilərin ən çox *birləşdirilmiş* [Pull Request-ləri var](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}. |
|||
|
|||
Onlar mənbə kodu, sənədləmə, tərcümələr və s. barədə əmək göstərmişlər. 📦 |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.top_contributors[:50] %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Pull Request-lər: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
Bundan başqa bir neçə (yüzdən çox) əməkdaş var ki, onları <a href="https://github.com/tiangolo/fastapi/graphs/contributors" class="external-link" target="_blank">FastAPI GitHub Əməkdaşlar səhifəsində</a> görə bilərsiniz. 👷 |
|||
|
|||
## Ən çox rəy verənlər |
|||
|
|||
Bu istifadəçilər **ən çox rəy verənlər**dir. |
|||
|
|||
### Tərcümələr üçün rəylər |
|||
|
|||
Mən yalnız bir neçə dildə danışıram (və çox da yaxşı deyil 😅). Bu səbəbdən, rəy verənlər sənədlərin [**tərcümələrini təsdiqləmək üçün gücə malik olanlar**](contributing.md#translations){.internal-link target=_blank}dır. Onlar olmadan, bir çox dilə tərcümə olunmuş sənədlər olmazdı. |
|||
|
|||
--- |
|||
|
|||
Başqalarının Pull Request-lərinə **Ən çox rəy verənlər** 🕵️ kodun, sənədlərin və xüsusilə də **tərcümələrin** keyfiyyətini təmin edirlər. |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.top_translations_reviewers[:50] %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Rəylər: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Sponsorlar |
|||
|
|||
Bunlar **Sponsorlar**dır. 😎 |
|||
|
|||
Onlar mənim **FastAPI** (və digər) işlərimi əsasən <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub Sponsorlar</a> vasitəsilə dəstəkləyirlər. |
|||
|
|||
{% if sponsors %} |
|||
|
|||
{% if sponsors.gold %} |
|||
|
|||
### Qızıl Sponsorlar |
|||
|
|||
{% for sponsor in sponsors.gold -%} |
|||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a> |
|||
{% endfor %} |
|||
{% endif %} |
|||
|
|||
{% if sponsors.silver %} |
|||
|
|||
### Gümüş Sponsorlar |
|||
|
|||
{% for sponsor in sponsors.silver -%} |
|||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a> |
|||
{% endfor %} |
|||
{% endif %} |
|||
|
|||
{% if sponsors.bronze %} |
|||
|
|||
### Bürünc Sponsorlar |
|||
|
|||
{% for sponsor in sponsors.bronze -%} |
|||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a> |
|||
{% endfor %} |
|||
{% endif %} |
|||
|
|||
{% endif %} |
|||
|
|||
### Fərdi Sponsorlar |
|||
|
|||
{% if github_sponsors %} |
|||
{% for group in github_sponsors.sponsors %} |
|||
|
|||
<div class="user-list user-list-center"> |
|||
|
|||
{% for user in group %} |
|||
{% if user.login not in sponsors_badge.logins %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div> |
|||
|
|||
{% endif %} |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
|
|||
{% endfor %} |
|||
{% endif %} |
|||
|
|||
## Məlumatlar haqqında - texniki detallar |
|||
|
|||
Bu səhifənin əsas məqsədi, icmanın başqalarına kömək etmək üçün göstərdiyi əməyi vurğulamaqdır. |
|||
|
|||
Xüsusilə də normalda daha az görünən və bir çox hallarda daha çətin olan, başqalarının suallarına kömək etmək və tərcümələrlə bağlı Pull Request-lərə rəy vermək kimi səy göstərmək. |
|||
|
|||
Bu səhifənin məlumatları hər ay hesablanır və siz <a href="https://github.com/tiangolo/fastapi/blob/master/.github/actions/people/app/main.py" class="external-link" target="_blank">buradan mənbə kodunu</a> oxuya bilərsiniz. |
|||
|
|||
Burada sponsorların əməyini də vurğulamaq istəyirəm. |
|||
|
|||
Mən həmçinin alqoritmi, bölmələri, eşikləri və s. yeniləmək hüququnu da qoruyuram (hər ehtimala qarşı 🤷). |
@ -8,14 +8,17 @@ Aus diesem Grund werden diese üblicherweise in Umgebungsvariablen bereitgestell |
|||
|
|||
## Umgebungsvariablen |
|||
|
|||
!!! tip "Tipp" |
|||
/// tip | Tipp |
|||
|
|||
Wenn Sie bereits wissen, was „Umgebungsvariablen“ sind und wie man sie verwendet, können Sie gerne mit dem nächsten Abschnitt weiter unten fortfahren. |
|||
|
|||
/// |
|||
|
|||
Eine <a href="https://de.wikipedia.org/wiki/Umgebungsvariable" class="external-link" target="_blank">Umgebungsvariable</a> (auch bekannt als „env var“) ist eine Variable, die sich außerhalb des Python-Codes im Betriebssystem befindet und von Ihrem Python-Code (oder auch von anderen Programmen) gelesen werden kann. |
|||
|
|||
Sie können Umgebungsvariablen in der Shell erstellen und verwenden, ohne Python zu benötigen: |
|||
|
|||
=== "Linux, macOS, Windows Bash" |
|||
//// tab | Linux, macOS, Windows Bash |
|||
|
|||
<div class="termy"> |
|||
|
|||
@ -31,7 +34,9 @@ Sie können Umgebungsvariablen in der Shell erstellen und verwenden, ohne Python |
|||
|
|||
</div> |
|||
|
|||
=== "Windows PowerShell" |
|||
//// |
|||
|
|||
//// tab | Windows PowerShell |
|||
|
|||
<div class="termy"> |
|||
|
|||
@ -47,6 +52,8 @@ Sie können Umgebungsvariablen in der Shell erstellen und verwenden, ohne Python |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
### Umgebungsvariablen mit Python auslesen |
|||
|
|||
Sie können Umgebungsvariablen auch außerhalb von Python im Terminal (oder mit einer anderen Methode) erstellen und diese dann mit Python auslesen. |
|||
@ -60,11 +67,14 @@ name = os.getenv("MY_NAME", "World") |
|||
print(f"Hello {name} from Python") |
|||
``` |
|||
|
|||
!!! tip "Tipp" |
|||
/// tip | Tipp |
|||
|
|||
Das zweite Argument für <a href="https://docs.python.org/3.8/library/os.html#os.getenv" class="external-link" target="_blank">`os.getenv()`</a> ist der zurückzugebende Defaultwert. |
|||
|
|||
Wenn nicht angegeben, ist er standardmäßig `None`. Hier übergeben wir `"World"` als Defaultwert. |
|||
|
|||
/// |
|||
|
|||
Dann könnten Sie dieses Python-Programm aufrufen: |
|||
|
|||
<div class="termy"> |
|||
@ -114,9 +124,12 @@ Hello World from Python |
|||
|
|||
</div> |
|||
|
|||
!!! tip "Tipp" |
|||
/// tip | Tipp |
|||
|
|||
Weitere Informationen dazu finden Sie unter <a href="https://12factor.net/config" class="external-link" target="_blank">The Twelve-Factor App: Config</a>. |
|||
|
|||
/// |
|||
|
|||
### Typen und Validierung |
|||
|
|||
Diese Umgebungsvariablen können nur Text-Zeichenketten verarbeiten, da sie außerhalb von Python liegen und mit anderen Programmen und dem Rest des Systems (und sogar mit verschiedenen Betriebssystemen wie Linux, Windows, macOS) kompatibel sein müssen. |
|||
@ -151,9 +164,12 @@ $ pip install "fastapi[all]" |
|||
|
|||
</div> |
|||
|
|||
!!! info |
|||
/// info |
|||
|
|||
In Pydantic v1 war das im Hauptpackage enthalten. Jetzt wird es als unabhängiges Package verteilt, sodass Sie wählen können, ob Sie es installieren möchten oder nicht, falls Sie die Funktionalität nicht benötigen. |
|||
|
|||
/// |
|||
|
|||
### Das `Settings`-Objekt erstellen |
|||
|
|||
Importieren Sie `BaseSettings` aus Pydantic und erstellen Sie eine Unterklasse, ganz ähnlich wie bei einem Pydantic-Modell. |
|||
@ -162,24 +178,30 @@ Auf die gleiche Weise wie bei Pydantic-Modellen deklarieren Sie Klassenattribute |
|||
|
|||
Sie können dieselben Validierungs-Funktionen und -Tools verwenden, die Sie für Pydantic-Modelle verwenden, z. B. verschiedene Datentypen und zusätzliche Validierungen mit `Field()`. |
|||
|
|||
=== "Pydantic v2" |
|||
//// tab | Pydantic v2 |
|||
|
|||
```Python hl_lines="2 5-8 11" |
|||
{!> ../../../docs_src/settings/tutorial001.py!} |
|||
``` |
|||
{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *} |
|||
|
|||
//// |
|||
|
|||
=== "Pydantic v1" |
|||
//// tab | Pydantic v1 |
|||
|
|||
/// info |
|||
|
|||
!!! info |
|||
In Pydantic v1 würden Sie `BaseSettings` direkt von `pydantic` statt von `pydantic_settings` importieren. |
|||
|
|||
```Python hl_lines="2 5-8 11" |
|||
{!> ../../../docs_src/settings/tutorial001_pv1.py!} |
|||
``` |
|||
/// |
|||
|
|||
{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *} |
|||
|
|||
//// |
|||
|
|||
/// tip | Tipp |
|||
|
|||
!!! tip "Tipp" |
|||
Für ein schnelles Copy-and-paste verwenden Sie nicht dieses Beispiel, sondern das letzte unten. |
|||
|
|||
/// |
|||
|
|||
Wenn Sie dann eine Instanz dieser `Settings`-Klasse erstellen (in diesem Fall als `settings`-Objekt), liest Pydantic die Umgebungsvariablen ohne Berücksichtigung der Groß- und Kleinschreibung. Eine Variable `APP_NAME` in Großbuchstaben wird also als Attribut `app_name` gelesen. |
|||
|
|||
Als Nächstes werden die Daten konvertiert und validiert. Wenn Sie also dieses `settings`-Objekt verwenden, verfügen Sie über Daten mit den von Ihnen deklarierten Typen (z. B. ist `items_per_user` ein `int`). |
|||
@ -188,9 +210,7 @@ Als Nächstes werden die Daten konvertiert und validiert. Wenn Sie also dieses ` |
|||
|
|||
Dann können Sie das neue `settings`-Objekt in Ihrer Anwendung verwenden: |
|||
|
|||
```Python hl_lines="18-20" |
|||
{!../../../docs_src/settings/tutorial001.py!} |
|||
``` |
|||
{* ../../docs_src/settings/tutorial001.py hl[18:20] *} |
|||
|
|||
### Den Server ausführen |
|||
|
|||
@ -206,9 +226,12 @@ $ ADMIN_EMAIL="[email protected]" APP_NAME="ChimichangApp" uvicorn main:app |
|||
|
|||
</div> |
|||
|
|||
!!! tip "Tipp" |
|||
/// tip | Tipp |
|||
|
|||
Um mehrere Umgebungsvariablen für einen einzelnen Befehl festzulegen, trennen Sie diese einfach durch ein Leerzeichen und fügen Sie alle vor dem Befehl ein. |
|||
|
|||
/// |
|||
|
|||
Und dann würde die Einstellung `admin_email` auf `"[email protected]"` gesetzt. |
|||
|
|||
Der `app_name` wäre `"ChimichangApp"`. |
|||
@ -221,19 +244,18 @@ Sie könnten diese Einstellungen in eine andere Moduldatei einfügen, wie Sie in |
|||
|
|||
Sie könnten beispielsweise eine Datei `config.py` haben mit: |
|||
|
|||
```Python |
|||
{!../../../docs_src/settings/app01/config.py!} |
|||
``` |
|||
{* ../../docs_src/settings/app01/config.py *} |
|||
|
|||
Und dann verwenden Sie diese in einer Datei `main.py`: |
|||
|
|||
```Python hl_lines="3 11-13" |
|||
{!../../../docs_src/settings/app01/main.py!} |
|||
``` |
|||
{* ../../docs_src/settings/app01/main.py hl[3,11:13] *} |
|||
|
|||
/// tip | Tipp |
|||
|
|||
!!! tip "Tipp" |
|||
Sie benötigen außerdem eine Datei `__init__.py`, wie in [Größere Anwendungen – mehrere Dateien](../tutorial/bigger-applications.md){.internal-link target=_blank} gesehen. |
|||
|
|||
/// |
|||
|
|||
## Einstellungen in einer Abhängigkeit |
|||
|
|||
In manchen Fällen kann es nützlich sein, die Einstellungen mit einer Abhängigkeit bereitzustellen, anstatt ein globales Objekt `settings` zu haben, das überall verwendet wird. |
|||
@ -244,9 +266,7 @@ Dies könnte besonders beim Testen nützlich sein, da es sehr einfach ist, eine |
|||
|
|||
Ausgehend vom vorherigen Beispiel könnte Ihre Datei `config.py` so aussehen: |
|||
|
|||
```Python hl_lines="10" |
|||
{!../../../docs_src/settings/app02/config.py!} |
|||
``` |
|||
{* ../../docs_src/settings/app02/config.py hl[10] *} |
|||
|
|||
Beachten Sie, dass wir jetzt keine Standardinstanz `settings = Settings()` erstellen. |
|||
|
|||
@ -254,62 +274,25 @@ Beachten Sie, dass wir jetzt keine Standardinstanz `settings = Settings()` erste |
|||
|
|||
Jetzt erstellen wir eine Abhängigkeit, die ein neues `config.Settings()` zurückgibt. |
|||
|
|||
=== "Python 3.9+" |
|||
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *} |
|||
|
|||
```Python hl_lines="6 12-13" |
|||
{!> ../../../docs_src/settings/app02_an_py39/main.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="6 12-13" |
|||
{!> ../../../docs_src/settings/app02_an/main.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="5 11-12" |
|||
{!> ../../../docs_src/settings/app02/main.py!} |
|||
``` |
|||
/// tip | Tipp |
|||
|
|||
!!! tip "Tipp" |
|||
Wir werden das `@lru_cache` in Kürze besprechen. |
|||
|
|||
Im Moment nehmen Sie an, dass `get_settings()` eine normale Funktion ist. |
|||
|
|||
Und dann können wir das von der *Pfadoperation-Funktion* als Abhängigkeit einfordern und es überall dort verwenden, wo wir es brauchen. |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="17 19-21" |
|||
{!> ../../../docs_src/settings/app02_an_py39/main.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
/// |
|||
|
|||
```Python hl_lines="17 19-21" |
|||
{!> ../../../docs_src/settings/app02_an/main.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ nicht annotiert" |
|||
Und dann können wir das von der *Pfadoperation-Funktion* als Abhängigkeit einfordern und es überall dort verwenden, wo wir es brauchen. |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="16 18-20" |
|||
{!> ../../../docs_src/settings/app02/main.py!} |
|||
``` |
|||
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *} |
|||
|
|||
### Einstellungen und Tests |
|||
|
|||
Dann wäre es sehr einfach, beim Testen ein anderes Einstellungsobjekt bereitzustellen, indem man eine Abhängigkeitsüberschreibung für `get_settings` erstellt: |
|||
|
|||
```Python hl_lines="9-10 13 21" |
|||
{!../../../docs_src/settings/app02/test_main.py!} |
|||
``` |
|||
{* ../../docs_src/settings/app02/test_main.py hl[9:10,13,21] *} |
|||
|
|||
Bei der Abhängigkeitsüberschreibung legen wir einen neuen Wert für `admin_email` fest, wenn wir das neue `Settings`-Objekt erstellen, und geben dann dieses neue Objekt zurück. |
|||
|
|||
@ -321,16 +304,22 @@ Wenn Sie viele Einstellungen haben, die sich möglicherweise oft ändern, vielle |
|||
|
|||
Diese Praxis ist so weit verbreitet, dass sie einen Namen hat. Diese Umgebungsvariablen werden üblicherweise in einer Datei `.env` abgelegt und die Datei wird „dotenv“ genannt. |
|||
|
|||
!!! tip "Tipp" |
|||
/// tip | Tipp |
|||
|
|||
Eine Datei, die mit einem Punkt (`.`) beginnt, ist eine versteckte Datei in Unix-ähnlichen Systemen wie Linux und macOS. |
|||
|
|||
Aber eine dotenv-Datei muss nicht unbedingt genau diesen Dateinamen haben. |
|||
|
|||
/// |
|||
|
|||
Pydantic unterstützt das Lesen dieser Dateitypen mithilfe einer externen Bibliothek. Weitere Informationen finden Sie unter <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support" class="external-link" target="_blank">Pydantic Settings: Dotenv (.env) support</a>. |
|||
|
|||
!!! tip "Tipp" |
|||
/// tip | Tipp |
|||
|
|||
Damit das funktioniert, müssen Sie `pip install python-dotenv` ausführen. |
|||
|
|||
/// |
|||
|
|||
### Die `.env`-Datei |
|||
|
|||
Sie könnten eine `.env`-Datei haben, mit: |
|||
@ -344,27 +333,36 @@ APP_NAME="ChimichangApp" |
|||
|
|||
Und dann aktualisieren Sie Ihre `config.py` mit: |
|||
|
|||
=== "Pydantic v2" |
|||
//// tab | Pydantic v2 |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../../docs_src/settings/app03_an/config.py!} |
|||
``` |
|||
{* ../../docs_src/settings/app03_an/config.py hl[9] *} |
|||
|
|||
/// tip | Tipp |
|||
|
|||
!!! tip "Tipp" |
|||
Das Attribut `model_config` wird nur für die Pydantic-Konfiguration verwendet. Weitere Informationen finden Sie unter <a href="https://docs.pydantic.dev/latest/concepts/config/" class="external-link" target="_blank">Pydantic: Configuration</a>. |
|||
|
|||
=== "Pydantic v1" |
|||
/// |
|||
|
|||
```Python hl_lines="9-10" |
|||
{!> ../../../docs_src/settings/app03_an/config_pv1.py!} |
|||
``` |
|||
//// |
|||
|
|||
//// tab | Pydantic v1 |
|||
|
|||
{* ../../docs_src/settings/app03_an/config_pv1.py hl[9:10] *} |
|||
|
|||
/// tip | Tipp |
|||
|
|||
!!! tip "Tipp" |
|||
Die Klasse `Config` wird nur für die Pydantic-Konfiguration verwendet. Weitere Informationen finden Sie unter <a href="https://docs.pydantic.dev/1.10/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>. |
|||
|
|||
!!! info |
|||
/// |
|||
|
|||
//// |
|||
|
|||
/// info |
|||
|
|||
In Pydantic Version 1 erfolgte die Konfiguration in einer internen Klasse `Config`, in Pydantic Version 2 erfolgt sie in einem Attribut `model_config`. Dieses Attribut akzeptiert ein `dict`. Um automatische Codevervollständigung und Inline-Fehlerberichte zu erhalten, können Sie `SettingsConfigDict` importieren und verwenden, um dieses `dict` zu definieren. |
|||
|
|||
/// |
|||
|
|||
Hier definieren wir die Konfiguration `env_file` innerhalb Ihrer Pydantic-`Settings`-Klasse und setzen den Wert auf den Dateinamen mit der dotenv-Datei, die wir verwenden möchten. |
|||
|
|||
### Die `Settings` nur einmal laden mittels `lru_cache` |
|||
@ -390,26 +388,7 @@ würden wir dieses Objekt für jeden Request erstellen und die `.env`-Datei für |
|||
|
|||
Da wir jedoch den `@lru_cache`-Dekorator oben verwenden, wird das `Settings`-Objekt nur einmal erstellt, nämlich beim ersten Aufruf. ✔️ |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="1 11" |
|||
{!> ../../../docs_src/settings/app03_an_py39/main.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="1 11" |
|||
{!> ../../../docs_src/settings/app03_an/main.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ nicht annotiert" |
|||
|
|||
!!! tip "Tipp" |
|||
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
|||
|
|||
```Python hl_lines="1 10" |
|||
{!> ../../../docs_src/settings/app03/main.py!} |
|||
``` |
|||
{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *} |
|||
|
|||
Dann wird bei allen nachfolgenden Aufrufen von `get_settings()`, in den Abhängigkeiten für darauffolgende Requests, dasselbe Objekt zurückgegeben, das beim ersten Aufruf zurückgegeben wurde, anstatt den Code von `get_settings()` erneut auszuführen und ein neues `Settings`-Objekt zu erstellen. |
|||
|
|||
|
@ -1,447 +0,0 @@ |
|||
# Entwicklung – Mitwirken |
|||
|
|||
Vielleicht möchten Sie sich zuerst die grundlegenden Möglichkeiten anschauen, [FastAPI zu helfen und Hilfe zu erhalten](help-fastapi.md){.internal-link target=_blank}. |
|||
|
|||
## Entwicklung |
|||
|
|||
Wenn Sie das <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">fastapi Repository</a> bereits geklont haben und tief in den Code eintauchen möchten, hier einen Leitfaden zum Einrichten Ihrer Umgebung. |
|||
|
|||
### Virtuelle Umgebung mit `venv` |
|||
|
|||
Sie können mit dem Python-Modul `venv` in einem Verzeichnis eine isolierte virtuelle lokale Umgebung erstellen. Machen wir das im geklonten Repository (da wo sich die `requirements.txt` befindet): |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ python -m venv env |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Das erstellt ein Verzeichnis `./env/` mit den Python-Binärdateien und Sie können dann Packages in dieser lokalen Umgebung installieren. |
|||
|
|||
### Umgebung aktivieren |
|||
|
|||
Aktivieren Sie die neue Umgebung mit: |
|||
|
|||
=== "Linux, macOS" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ source ./env/bin/activate |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
=== "Windows PowerShell" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ .\env\Scripts\Activate.ps1 |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
=== "Windows Bash" |
|||
|
|||
Oder, wenn Sie Bash für Windows verwenden (z. B. <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>): |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ source ./env/Scripts/activate |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Um zu überprüfen, ob es funktioniert hat, geben Sie ein: |
|||
|
|||
=== "Linux, macOS, Windows Bash" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ which pip |
|||
|
|||
some/directory/fastapi/env/bin/pip |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
=== "Windows PowerShell" |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ Get-Command pip |
|||
|
|||
some/directory/fastapi/env/bin/pip |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Wenn die `pip` Binärdatei unter `env/bin/pip` angezeigt wird, hat es funktioniert. 🎉 |
|||
|
|||
Stellen Sie sicher, dass Sie über die neueste Version von pip in Ihrer lokalen Umgebung verfügen, um Fehler bei den nächsten Schritten zu vermeiden: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ python -m pip install --upgrade pip |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! tip "Tipp" |
|||
Aktivieren Sie jedes Mal, wenn Sie ein neues Package mit `pip` in dieser Umgebung installieren, die Umgebung erneut. |
|||
|
|||
Dadurch wird sichergestellt, dass Sie, wenn Sie ein von diesem Package installiertes Terminalprogramm verwenden, das Programm aus Ihrer lokalen Umgebung verwenden und kein anderes, das global installiert sein könnte. |
|||
|
|||
### Benötigtes mit pip installieren |
|||
|
|||
Nachdem Sie die Umgebung wie oben beschrieben aktiviert haben: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install -r requirements.txt |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Das installiert alle Abhängigkeiten und Ihr lokales FastAPI in Ihrer lokalen Umgebung. |
|||
|
|||
#### Das lokale FastAPI verwenden |
|||
|
|||
Wenn Sie eine Python-Datei erstellen, die FastAPI importiert und verwendet, und diese mit dem Python aus Ihrer lokalen Umgebung ausführen, wird Ihr geklonter lokaler FastAPI-Quellcode verwendet. |
|||
|
|||
Und wenn Sie diesen lokalen FastAPI-Quellcode aktualisieren und dann die Python-Datei erneut ausführen, wird die neue Version von FastAPI verwendet, die Sie gerade bearbeitet haben. |
|||
|
|||
Auf diese Weise müssen Sie Ihre lokale Version nicht „installieren“, um jede Änderung testen zu können. |
|||
|
|||
!!! note "Technische Details" |
|||
Das geschieht nur, wenn Sie die Installation mit der enthaltenen `requirements.txt` durchführen, anstatt `pip install fastapi` direkt auszuführen. |
|||
|
|||
Das liegt daran, dass in der Datei `requirements.txt` die lokale Version von FastAPI mit der Option `-e` für die Installation im „editierbaren“ Modus markiert ist. |
|||
|
|||
### Den Code formatieren |
|||
|
|||
Es gibt ein Skript, das, wenn Sie es ausführen, Ihren gesamten Code formatiert und bereinigt: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ bash scripts/format.sh |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Es sortiert auch alle Ihre Importe automatisch. |
|||
|
|||
Damit es sie richtig sortiert, muss FastAPI lokal in Ihrer Umgebung installiert sein, mit dem Befehl vom obigen Abschnitt, welcher `-e` verwendet. |
|||
|
|||
## Dokumentation |
|||
|
|||
Stellen Sie zunächst sicher, dass Sie Ihre Umgebung wie oben beschrieben einrichten, was alles Benötigte installiert. |
|||
|
|||
### Dokumentation live |
|||
|
|||
Während der lokalen Entwicklung gibt es ein Skript, das die Site erstellt, auf Änderungen prüft und direkt neu lädt (Live Reload): |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ python ./scripts/docs.py live |
|||
|
|||
<span style="color: green;">[INFO]</span> Serving on http://127.0.0.1:8008 |
|||
<span style="color: green;">[INFO]</span> Start watching changes |
|||
<span style="color: green;">[INFO]</span> Start detecting changes |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Das stellt die Dokumentation unter `http://127.0.0.1:8008` bereit. |
|||
|
|||
Auf diese Weise können Sie die Dokumentation/Quelldateien bearbeiten und die Änderungen live sehen. |
|||
|
|||
!!! tip "Tipp" |
|||
Alternativ können Sie die Schritte des Skripts auch manuell ausführen. |
|||
|
|||
Gehen Sie in das Verzeichnis für die entsprechende Sprache. Das für die englischsprachige Hauptdokumentation befindet sich unter `docs/en/`: |
|||
|
|||
```console |
|||
$ cd docs/en/ |
|||
``` |
|||
|
|||
Führen Sie dann `mkdocs` in diesem Verzeichnis aus: |
|||
|
|||
```console |
|||
$ mkdocs serve --dev-addr 8008 |
|||
``` |
|||
|
|||
#### Typer-CLI (optional) |
|||
|
|||
Die Anleitung hier zeigt Ihnen, wie Sie das Skript unter `./scripts/docs.py` direkt mit dem `python` Programm verwenden. |
|||
|
|||
Sie können aber auch <a href="https://typer.tiangolo.com/typer-cli/" class="external-link" target="_blank">Typer CLI</a> verwenden und erhalten dann Autovervollständigung für Kommandos in Ihrem Terminal, nach dem Sie dessen Vervollständigung installiert haben. |
|||
|
|||
Wenn Sie Typer CLI installieren, können Sie die Vervollständigung installieren mit: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ typer --install-completion |
|||
|
|||
zsh completion installed in /home/user/.bashrc. |
|||
Completion will take effect once you restart the terminal. |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
### Dokumentationsstruktur |
|||
|
|||
Die Dokumentation verwendet <a href="https://www.mkdocs.org/" class="external-link" target="_blank">MkDocs</a>. |
|||
|
|||
Und es gibt zusätzliche Tools/Skripte für Übersetzungen, in `./scripts/docs.py`. |
|||
|
|||
!!! tip "Tipp" |
|||
Sie müssen sich den Code in `./scripts/docs.py` nicht anschauen, verwenden Sie ihn einfach in der Kommandozeile. |
|||
|
|||
Die gesamte Dokumentation befindet sich im Markdown-Format im Verzeichnis `./docs/en/`. |
|||
|
|||
Viele der Tutorials enthalten Codeblöcke. |
|||
|
|||
In den meisten Fällen handelt es sich bei diesen Codeblöcken um vollständige Anwendungen, die unverändert ausgeführt werden können. |
|||
|
|||
Tatsächlich sind diese Codeblöcke nicht Teil des Markdowns, sondern Python-Dateien im Verzeichnis `./docs_src/`. |
|||
|
|||
Und diese Python-Dateien werden beim Generieren der Site in die Dokumentation eingefügt. |
|||
|
|||
### Dokumentation für Tests |
|||
|
|||
Tatsächlich arbeiten die meisten Tests mit den Beispielquelldateien in der Dokumentation. |
|||
|
|||
Dadurch wird sichergestellt, dass: |
|||
|
|||
* Die Dokumentation aktuell ist. |
|||
* Die Dokumentationsbeispiele ohne Änderung ausgeführt werden können. |
|||
* Die meisten Funktionalitäten durch die Dokumentation abgedeckt werden, sichergestellt durch die Testabdeckung. |
|||
|
|||
#### Gleichzeitig Apps und Dokumentation |
|||
|
|||
Wenn Sie die Beispiele ausführen, mit z. B.: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ uvicorn tutorial001:app --reload |
|||
|
|||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
wird das, da Uvicorn standardmäßig den Port `8000` verwendet, mit der Dokumentation auf dem Port `8008` nicht in Konflikt geraten. |
|||
|
|||
### Übersetzungen |
|||
|
|||
Hilfe bei Übersetzungen wird SEHR geschätzt! Und es kann nicht getan werden, ohne die Hilfe der Gemeinschaft. 🌎 🚀 |
|||
|
|||
Hier sind die Schritte, die Ihnen bei Übersetzungen helfen. |
|||
|
|||
#### Tipps und Richtlinien |
|||
|
|||
* Schauen Sie nach <a href="https://github.com/tiangolo/fastapi/pulls" class="external-link" target="_blank">aktuellen Pull Requests</a> für Ihre Sprache. Sie können die Pull Requests nach dem Label für Ihre Sprache filtern. Für Spanisch lautet das Label beispielsweise <a href="https://github.com/tiangolo/fastapi/pulls?q=is%3Aopen+sort%3Aupdated-desc+label%3Alang-es+label%3Aawaiting-review" class="external-link" target="_blank">`lang-es`</a>. |
|||
|
|||
* Sehen Sie diese Pull Requests durch (Review), schlagen Sie Änderungen vor, oder segnen Sie sie ab (Approval). Bei den Sprachen, die ich nicht spreche, warte ich, bis mehrere andere die Übersetzung durchgesehen haben, bevor ich den Pull Request merge. |
|||
|
|||
!!! tip "Tipp" |
|||
Sie können <a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request" class="external-link" target="_blank">Kommentare mit Änderungsvorschlägen</a> zu vorhandenen Pull Requests hinzufügen. |
|||
|
|||
Schauen Sie sich die Dokumentation an, <a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-request-reviews" class="external-link" target="_blank">wie man ein Review zu einem Pull Request hinzufügt</a>, welches den PR absegnet oder Änderungen vorschlägt. |
|||
|
|||
* Überprüfen Sie, ob es eine <a href="https://github.com/tiangolo/fastapi/discussions/categories/translations" class="external-link" target="_blank">GitHub-Diskussion</a> gibt, die Übersetzungen für Ihre Sprache koordiniert. Sie können sie abonnieren, und wenn ein neuer Pull Request zum Review vorliegt, wird der Diskussion automatisch ein Kommentar hinzugefügt. |
|||
|
|||
* Wenn Sie Seiten übersetzen, fügen Sie einen einzelnen Pull Request pro übersetzter Seite hinzu. Dadurch wird es für andere viel einfacher, ihn zu durchzusehen. |
|||
|
|||
* Um den Zwei-Buchstaben-Code für die Sprache zu finden, die Sie übersetzen möchten, schauen Sie sich die Tabelle <a href="https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" class="external-link" target= verwenden "_blank">List of ISO 639-1 codes</a> an. |
|||
|
|||
#### Vorhandene Sprache |
|||
|
|||
Angenommen, Sie möchten eine Seite für eine Sprache übersetzen, die bereits Übersetzungen für einige Seiten hat, beispielsweise für Spanisch. |
|||
|
|||
Im Spanischen lautet der Zwei-Buchstaben-Code `es`. Das Verzeichnis für spanische Übersetzungen befindet sich also unter `docs/es/`. |
|||
|
|||
!!! tip "Tipp" |
|||
Die Haupt („offizielle“) Sprache ist Englisch und befindet sich unter `docs/en/`. |
|||
|
|||
Führen Sie nun den Live-Server für die Dokumentation auf Spanisch aus: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// Verwenden Sie das Kommando „live“ und fügen Sie den Sprach-Code als Argument hinten an |
|||
$ python ./scripts/docs.py live es |
|||
|
|||
<span style="color: green;">[INFO]</span> Serving on http://127.0.0.1:8008 |
|||
<span style="color: green;">[INFO]</span> Start watching changes |
|||
<span style="color: green;">[INFO]</span> Start detecting changes |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
!!! tip "Tipp" |
|||
Alternativ können Sie die Schritte des Skripts auch manuell ausführen. |
|||
|
|||
Gehen Sie in das Sprachverzeichnis, für die spanischen Übersetzungen ist das `docs/es/`: |
|||
|
|||
```console |
|||
$ cd docs/es/ |
|||
``` |
|||
|
|||
Dann führen Sie in dem Verzeichnis `mkdocs` aus: |
|||
|
|||
```console |
|||
$ mkdocs serve --dev-addr 8008 |
|||
``` |
|||
|
|||
Jetzt können Sie auf <a href="http://127.0.0.1:8008" class="external-link" target="_blank">http://127.0.0.1:8008</a> gehen und Ihre Änderungen live sehen. |
|||
|
|||
Sie werden sehen, dass jede Sprache alle Seiten hat. Einige Seiten sind jedoch nicht übersetzt und haben oben eine Info-Box, dass die Übersetzung noch fehlt. |
|||
|
|||
Nehmen wir nun an, Sie möchten eine Übersetzung für den Abschnitt [Features](features.md){.internal-link target=_blank} hinzufügen. |
|||
|
|||
* Kopieren Sie die Datei: |
|||
|
|||
``` |
|||
docs/en/docs/features.md |
|||
``` |
|||
|
|||
* Fügen Sie sie an genau derselben Stelle ein, jedoch für die Sprache, die Sie übersetzen möchten, z. B.: |
|||
|
|||
``` |
|||
docs/es/docs/features.md |
|||
``` |
|||
|
|||
!!! tip "Tipp" |
|||
Beachten Sie, dass die einzige Änderung in Pfad und Dateiname der Sprachcode ist, von `en` zu `es`. |
|||
|
|||
Wenn Sie in Ihrem Browser nachsehen, werden Sie feststellen, dass die Dokumentation jetzt Ihren neuen Abschnitt anzeigt (die Info-Box oben ist verschwunden). 🎉 |
|||
|
|||
Jetzt können Sie alles übersetzen und beim Speichern sehen, wie es aussieht. |
|||
|
|||
#### Neue Sprache |
|||
|
|||
Nehmen wir an, Sie möchten Übersetzungen für eine Sprache hinzufügen, die noch nicht übersetzt ist, nicht einmal einige Seiten. |
|||
|
|||
Angenommen, Sie möchten Übersetzungen für Kreolisch hinzufügen, diese sind jedoch noch nicht in den Dokumenten enthalten. |
|||
|
|||
Wenn Sie den Link von oben überprüfen, lautet der Sprachcode für Kreolisch `ht`. |
|||
|
|||
Der nächste Schritt besteht darin, das Skript auszuführen, um ein neues Übersetzungsverzeichnis zu erstellen: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// Verwenden Sie das Kommando new-lang und fügen Sie den Sprach-Code als Argument hinten an |
|||
$ python ./scripts/docs.py new-lang ht |
|||
|
|||
Successfully initialized: docs/ht |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Jetzt können Sie in Ihrem Code-Editor das neu erstellte Verzeichnis `docs/ht/` sehen. |
|||
|
|||
Obiges Kommando hat eine Datei `docs/ht/mkdocs.yml` mit einer Minimal-Konfiguration erstellt, die alles von der `en`-Version erbt: |
|||
|
|||
```yaml |
|||
INHERIT: ../en/mkdocs.yml |
|||
``` |
|||
|
|||
!!! tip "Tipp" |
|||
Sie können diese Datei mit diesem Inhalt auch einfach manuell erstellen. |
|||
|
|||
Das Kommando hat auch eine Dummy-Datei `docs/ht/index.md` für die Hauptseite erstellt. Sie können mit der Übersetzung dieser Datei beginnen. |
|||
|
|||
Sie können nun mit den obigen Instruktionen für eine „vorhandene Sprache“ fortfahren. |
|||
|
|||
Fügen Sie dem ersten Pull Request beide Dateien `docs/ht/mkdocs.yml` und `docs/ht/index.md` bei. 🎉 |
|||
|
|||
#### Vorschau des Ergebnisses |
|||
|
|||
Wie bereits oben erwähnt, können Sie `./scripts/docs.py` mit dem Befehl `live` verwenden, um eine Vorschau der Ergebnisse anzuzeigen (oder `mkdocs serve`). |
|||
|
|||
Sobald Sie fertig sind, können Sie auch alles so testen, wie es online aussehen würde, einschließlich aller anderen Sprachen. |
|||
|
|||
Bauen Sie dazu zunächst die gesamte Dokumentation: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// Verwenden Sie das Kommando „build-all“, das wird ein wenig dauern |
|||
$ python ./scripts/docs.py build-all |
|||
|
|||
Building docs for: en |
|||
Building docs for: es |
|||
Successfully built docs for: es |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Dadurch werden alle diese unabhängigen MkDocs-Sites für jede Sprache erstellt, kombiniert und das endgültige Resultat unter `./site/` gespeichert. |
|||
|
|||
Dieses können Sie dann mit dem Befehl `serve` bereitstellen: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// Verwenden Sie das Kommando „serve“ nachdem Sie „build-all“ ausgeführt haben. |
|||
$ python ./scripts/docs.py serve |
|||
|
|||
Warning: this is a very simple server. For development, use mkdocs serve instead. |
|||
This is here only to preview a site with translations already built. |
|||
Make sure you run the build-all command first. |
|||
Serving at: http://127.0.0.1:8008 |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
#### Übersetzungsspezifische Tipps und Richtlinien |
|||
|
|||
* Übersetzen Sie nur die Markdown-Dokumente (`.md`). Übersetzen Sie nicht die Codebeispiele unter `./docs_src`. |
|||
|
|||
* In Codeblöcken innerhalb des Markdown-Dokuments, übersetzen Sie Kommentare (`# ein Kommentar`), aber lassen Sie den Rest unverändert. |
|||
|
|||
* Ändern Sie nichts, was in "``" (Inline-Code) eingeschlossen ist. |
|||
|
|||
* In Zeilen, die mit `===` oder `!!!` beginnen, übersetzen Sie nur den ` "... Text ..."`-Teil. Lassen Sie den Rest unverändert. |
|||
|
|||
* Sie können Info-Boxen wie `!!! warning` mit beispielsweise `!!! warning "Achtung"` übersetzen. Aber ändern Sie nicht das Wort direkt nach dem `!!!`, es bestimmt die Farbe der Info-Box. |
|||
|
|||
* Ändern Sie nicht die Pfade in Links zu Bildern, Codedateien, Markdown Dokumenten. |
|||
|
|||
* Wenn ein Markdown-Dokument übersetzt ist, ändern sich allerdings unter Umständen die `#hash-teile` in Links zu dessen Überschriften. Aktualisieren Sie diese Links, wenn möglich. |
|||
* Suchen Sie im übersetzten Dokument nach solchen Links mit dem Regex `#[^# ]`. |
|||
* Suchen Sie in allen bereits in ihre Sprache übersetzen Dokumenten nach `ihr-ubersetztes-dokument.md`. VS Code hat beispielsweise eine Option „Bearbeiten“ -> „In Dateien suchen“. |
|||
* Übersetzen Sie bei der Übersetzung eines Dokuments nicht „im Voraus“ `#hash-teile`, die zu Überschriften in noch nicht übersetzten Dokumenten verlinken. |
|||
|
|||
## Tests |
|||
|
|||
Es gibt ein Skript, das Sie lokal ausführen können, um den gesamten Code zu testen und Code Coverage Reporte in HTML zu generieren: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ bash scripts/test-cov-html.sh |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Dieses Kommando generiert ein Verzeichnis `./htmlcov/`. Wenn Sie die Datei `./htmlcov/index.html` in Ihrem Browser öffnen, können Sie interaktiv die Codebereiche erkunden, die von den Tests abgedeckt werden, und feststellen, ob Bereiche fehlen. |
@ -1,36 +0,0 @@ |
|||
# Externe Links und Artikel |
|||
|
|||
**FastAPI** hat eine großartige Community, die ständig wächst. |
|||
|
|||
Es gibt viele Beiträge, Artikel, Tools und Projekte zum Thema **FastAPI**. |
|||
|
|||
Hier ist eine unvollständige Liste einiger davon. |
|||
|
|||
!!! tip "Tipp" |
|||
Wenn Sie einen Artikel, ein Projekt, ein Tool oder irgendetwas im Zusammenhang mit **FastAPI** haben, was hier noch nicht aufgeführt ist, erstellen Sie einen <a href="https://github.com/tiangolo/fastapi/edit/master/docs/en/data/external_links.yml" class="external-link" target="_blank">Pull Request und fügen Sie es hinzu</a>. |
|||
|
|||
!!! note "Hinweis Deutsche Übersetzung" |
|||
Die folgenden Überschriften und Links werden aus einer <a href="https://github.com/tiangolo/fastapi/blob/master/docs/en/data/external_links.yml" class="external-link" target="_blank">anderen Datei</a> gelesen und sind daher nicht ins Deutsche übersetzt. |
|||
|
|||
{% for section_name, section_content in external_links.items() %} |
|||
|
|||
## {{ section_name }} |
|||
|
|||
{% for lang_name, lang_content in section_content.items() %} |
|||
|
|||
### {{ lang_name }} |
|||
|
|||
{% for item in lang_content %} |
|||
|
|||
* <a href="{{ item.link }}" class="external-link" target="_blank">{{ item.title }}</a> by <a href="{{ item.author_link }}" class="external-link" target="_blank">{{ item.author }}</a>. |
|||
|
|||
{% endfor %} |
|||
{% endfor %} |
|||
{% endfor %} |
|||
|
|||
## Projekte |
|||
|
|||
Die neuesten GitHub-Projekte zum Thema `fastapi`: |
|||
|
|||
<div class="github-topic-projects"> |
|||
</div> |
@ -1,176 +0,0 @@ |
|||
--- |
|||
hide: |
|||
- navigation |
|||
--- |
|||
|
|||
# FastAPI Leute |
|||
|
|||
FastAPI hat eine großartige Gemeinschaft, die Menschen mit unterschiedlichstem Hintergrund willkommen heißt. |
|||
|
|||
## Erfinder - Betreuer |
|||
|
|||
Hey! 👋 |
|||
|
|||
Das bin ich: |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.maintainers %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Answers: {{ user.answers }}</div><div class="count">Pull Requests: {{ user.prs }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
Ich bin der Erfinder und Betreuer von **FastAPI**. Sie können mehr darüber in [FastAPI helfen – Hilfe erhalten – Mit dem Autor vernetzen](help-fastapi.md#mit-dem-autor-vernetzen){.internal-link target=_blank} erfahren. |
|||
|
|||
... Aber hier möchte ich Ihnen die Gemeinschaft vorstellen. |
|||
|
|||
--- |
|||
|
|||
**FastAPI** erhält eine Menge Unterstützung aus der Gemeinschaft. Und ich möchte ihre Beiträge hervorheben. |
|||
|
|||
Das sind die Menschen, die: |
|||
|
|||
* [Anderen bei Fragen auf GitHub helfen](help-fastapi.md#anderen-bei-fragen-auf-github-helfen){.internal-link target=_blank}. |
|||
* [<abbr title='Pull Request – „Zieh-Anfrage“: Geänderten Quellcode senden, mit dem Vorschlag, ihn mit dem aktuellen Quellcode zu verschmelzen'>Pull Requests</abbr> erstellen](help-fastapi.md#einen-pull-request-erstellen){.internal-link target=_blank}. |
|||
* Pull Requests überprüfen (Review), [besonders wichtig für Übersetzungen](contributing.md#ubersetzungen){.internal-link target=_blank}. |
|||
|
|||
Eine Runde Applaus für sie. 👏 🙇 |
|||
|
|||
## Aktivste Benutzer im letzten Monat |
|||
|
|||
Hier die Benutzer, die im letzten Monat am meisten [anderen mit Fragen auf Github](help-fastapi.md#anderen-bei-fragen-auf-github-helfen){.internal-link target=_blank} geholfen haben. ☕ |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.last_month_active %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Fragen beantwortet: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Experten |
|||
|
|||
Hier die **FastAPI-Experten**. 🤓 |
|||
|
|||
Das sind die Benutzer, die *insgesamt* [anderen am meisten mit Fragen auf GitHub geholfen haben](help-fastapi.md#anderen-bei-fragen-auf-github-helfen){.internal-link target=_blank}. |
|||
|
|||
Sie haben bewiesen, dass sie Experten sind, weil sie vielen anderen geholfen haben. ✨ |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.experts %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Fragen beantwortet: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Top-Mitwirkende |
|||
|
|||
Hier sind die **Top-Mitwirkenden**. 👷 |
|||
|
|||
Diese Benutzer haben [die meisten Pull Requests erstellt](help-fastapi.md#einen-pull-request-erstellen){.internal-link target=_blank} welche *<abbr title="Mergen – Zusammenführen: Unterschiedliche Versionen eines Quellcodes zusammenführen">gemerged</abbr>* wurden. |
|||
|
|||
Sie haben Quellcode, Dokumentation, Übersetzungen, usw. beigesteuert. 📦 |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.top_contributors %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Pull Requests: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
Es gibt viele andere Mitwirkende (mehr als hundert), Sie können sie alle auf der <a href="https://github.com/tiangolo/fastapi/graphs/contributors" class="external-link" target="_blank">FastAPI GitHub Contributors-Seite</a> sehen. 👷 |
|||
|
|||
## Top-Rezensenten |
|||
|
|||
Diese Benutzer sind die **Top-Rezensenten**. 🕵️ |
|||
|
|||
### Rezensionen für Übersetzungen |
|||
|
|||
Ich spreche nur ein paar Sprachen (und nicht sehr gut 😅). Daher bestätigen Reviewer [**Übersetzungen der Dokumentation**](contributing.md#ubersetzungen){.internal-link target=_blank}. Ohne sie gäbe es keine Dokumentation in mehreren anderen Sprachen. |
|||
|
|||
--- |
|||
|
|||
Die **Top-Reviewer** 🕵️ haben die meisten Pull Requests von anderen überprüft und stellen die Qualität des Codes, der Dokumentation und insbesondere der **Übersetzungen** sicher. |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.top_reviewers %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Reviews: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Sponsoren |
|||
|
|||
Dies sind die **Sponsoren**. 😎 |
|||
|
|||
Sie unterstützen meine Arbeit an **FastAPI** (und andere), hauptsächlich durch <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub-Sponsoren</a>. |
|||
|
|||
### Gold Sponsoren |
|||
|
|||
{% if sponsors %} |
|||
{% for sponsor in sponsors.gold -%} |
|||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}"></a> |
|||
{% endfor %} |
|||
{% endif %} |
|||
|
|||
### Silber Sponsoren |
|||
|
|||
{% if sponsors %} |
|||
{% for sponsor in sponsors.silver -%} |
|||
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}"></a> |
|||
{% endfor %} |
|||
{% endif %} |
|||
|
|||
{% if people %} |
|||
{% if people.sponsors_50 %} |
|||
|
|||
### Bronze Sponsoren |
|||
|
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.sponsors_50 %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
|
|||
{% endif %} |
|||
{% endif %} |
|||
|
|||
### Individuelle Sponsoren |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.sponsors %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Über diese Daten - technische Details |
|||
|
|||
Der Hauptzweck dieser Seite ist es zu zeigen, wie die Gemeinschaft anderen hilft. |
|||
|
|||
Das beinhaltet auch Hilfe, die normalerweise weniger sichtbar und in vielen Fällen mühsamer ist, wie, anderen bei Problemen zu helfen und Pull Requests mit Übersetzungen zu überprüfen. |
|||
|
|||
Diese Daten werden jeden Monat berechnet, Sie können den <a href="https://github.com/tiangolo/fastapi/blob/master/.github/actions/people/app/main.py" class="external-link" target="_blank">Quellcode hier lesen</a>. |
|||
|
|||
Hier weise ich auch auf Beiträge von Sponsoren hin. |
|||
|
|||
Ich behalte mir auch das Recht vor, den Algorithmus, die Abschnitte, die Schwellenwerte usw. zu aktualisieren (nur für den Fall 🤷). |
@ -1,3 +0,0 @@ |
|||
# Hilfe |
|||
|
|||
Helfen und Hilfe erhalten, beitragen, mitmachen. 🤝 |
@ -1,5 +0,0 @@ |
|||
# FastAPI und Freunde Newsletter |
|||
|
|||
<iframe data-w-type="embedded" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://xr4n4.mjt.lu/wgt/xr4n4/hj5/form?c=40a44fa4" width="100%" style="height: 0;"></iframe> |
|||
|
|||
<script type="text/javascript" src="https://app.mailjet.com/pas-nc-embedded-v1.js"></script> |
@ -1,24 +0,0 @@ |
|||
# `APIRouter`-Klasse |
|||
|
|||
Hier sind die Referenzinformationen für die Klasse `APIRouter` mit all ihren Parametern, Attributen und Methoden. |
|||
|
|||
Sie können die `APIRouter`-Klasse direkt von `fastapi` importieren: |
|||
|
|||
```python |
|||
from fastapi import APIRouter |
|||
``` |
|||
|
|||
::: fastapi.APIRouter |
|||
options: |
|||
members: |
|||
- websocket |
|||
- include_router |
|||
- get |
|||
- put |
|||
- post |
|||
- delete |
|||
- options |
|||
- head |
|||
- patch |
|||
- trace |
|||
- on_event |
@ -1,11 +0,0 @@ |
|||
# Hintergrundtasks – `BackgroundTasks` |
|||
|
|||
Sie können einen Parameter in einer *Pfadoperation-Funktion* oder einer Abhängigkeitsfunktion mit dem Typ `BackgroundTasks` deklarieren und diesen danach verwenden, um die Ausführung von Hintergrundtasks nach dem Senden der Response zu definieren. |
|||
|
|||
Sie können `BackgroundTasks` direkt von `fastapi` importieren: |
|||
|
|||
```python |
|||
from fastapi import BackgroundTasks |
|||
``` |
|||
|
|||
::: fastapi.BackgroundTasks |
@ -1,29 +0,0 @@ |
|||
# Abhängigkeiten – `Depends()` und `Security()` |
|||
|
|||
## `Depends()` |
|||
|
|||
Abhängigkeiten werden hauptsächlich mit der speziellen Funktion `Depends()` behandelt, die ein Callable entgegennimmt. |
|||
|
|||
Hier finden Sie deren Referenz und Parameter. |
|||
|
|||
Sie können sie direkt von `fastapi` importieren: |
|||
|
|||
```python |
|||
from fastapi import Depends |
|||
``` |
|||
|
|||
::: fastapi.Depends |
|||
|
|||
## `Security()` |
|||
|
|||
In vielen Szenarien können Sie die Sicherheit (Autorisierung, Authentifizierung usw.) mit Abhängigkeiten handhaben, indem Sie `Depends()` verwenden. |
|||
|
|||
Wenn Sie jedoch auch OAuth2-Scopes deklarieren möchten, können Sie `Security()` anstelle von `Depends()` verwenden. |
|||
|
|||
Sie können `Security()` direkt von `fastapi` importieren: |
|||
|
|||
```python |
|||
from fastapi import Security |
|||
``` |
|||
|
|||
::: fastapi.Security |
@ -1,3 +0,0 @@ |
|||
# Encoder – `jsonable_encoder` |
|||
|
|||
::: fastapi.encoders.jsonable_encoder |
@ -1,20 +0,0 @@ |
|||
# Exceptions – `HTTPException` und `WebSocketException` |
|||
|
|||
Dies sind die <abbr title="Exception – Ausnahme, Fehler: Python-Objekt, das einen Fehler nebst Metadaten repräsentiert">Exceptions</abbr>, die Sie auslösen können, um dem Client Fehler zu berichten. |
|||
|
|||
Wenn Sie eine Exception auslösen, wird, wie es bei normalem Python der Fall wäre, der Rest der Ausführung abgebrochen. Auf diese Weise können Sie diese Exceptions von überall im Code werfen, um einen Request abzubrechen und den Fehler dem Client anzuzeigen. |
|||
|
|||
Sie können Folgendes verwenden: |
|||
|
|||
* `HTTPException` |
|||
* `WebSocketException` |
|||
|
|||
Diese Exceptions können direkt von `fastapi` importiert werden: |
|||
|
|||
```python |
|||
from fastapi import HTTPException, WebSocketException |
|||
``` |
|||
|
|||
::: fastapi.HTTPException |
|||
|
|||
::: fastapi.WebSocketException |
@ -1,31 +0,0 @@ |
|||
# `FastAPI`-Klasse |
|||
|
|||
Hier sind die Referenzinformationen für die Klasse `FastAPI` mit all ihren Parametern, Attributen und Methoden. |
|||
|
|||
Sie können die `FastAPI`-Klasse direkt von `fastapi` importieren: |
|||
|
|||
```python |
|||
from fastapi import FastAPI |
|||
``` |
|||
|
|||
::: fastapi.FastAPI |
|||
options: |
|||
members: |
|||
- openapi_version |
|||
- webhooks |
|||
- state |
|||
- dependency_overrides |
|||
- openapi |
|||
- websocket |
|||
- include_router |
|||
- get |
|||
- put |
|||
- post |
|||
- delete |
|||
- options |
|||
- head |
|||
- patch |
|||
- trace |
|||
- on_event |
|||
- middleware |
|||
- exception_handler |
@ -1,11 +0,0 @@ |
|||
# `HTTPConnection`-Klasse |
|||
|
|||
Wenn Sie Abhängigkeiten definieren möchten, die sowohl mit HTTP als auch mit WebSockets kompatibel sein sollen, können Sie einen Parameter definieren, der eine `HTTPConnection` anstelle eines `Request` oder eines `WebSocket` akzeptiert. |
|||
|
|||
Sie können diese von `fastapi.requests` importieren: |
|||
|
|||
```python |
|||
from fastapi.requests import HTTPConnection |
|||
``` |
|||
|
|||
::: fastapi.requests.HTTPConnection |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue