Skip to content

Commit 63ceefb

Browse files
committed
fix manually
1 parent 78db1e2 commit 63ceefb

File tree

8 files changed

+109
-99
lines changed

8 files changed

+109
-99
lines changed

docs/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ az resource update \
114114
--set properties.disableLocalAuth=false
115115
```
116116

117+
### Azure Blob Storage
118+
119+
- [クイック スタート: Python 用 Azure Blob Storage クライアント ライブラリ](https://learn.microsoft.com/ja-jp/azure/storage/blobs/storage-quickstart-blobs-python?tabs=connection-string%2Croles-azure-portal%2Csign-in-azure-cli&pivots=blob-storage-quickstart-scratch)
120+
117121
### Application Insights
118122

119123
- [Application Insights の概要 - OpenTelemetry の可観測性](https://learn.microsoft.com/ja-jp/azure/azure-monitor/app/app-insights-overview)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ dependencies = [
88
"azure-cosmos>=4.9.0",
99
"azure-functions>=1.23.0",
1010
"azure-monitor-opentelemetry>=1.6.10",
11+
"azure-storage-blob>=12.25.1",
1112
"fastapi-mcp>=0.3.4",
1213
"fastapi[standard]>=0.115.12",
1314
"langchain-community>=0.3.27",

scripts/files.py

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
#!/usr/bin/env python
22
# filepath: /home/runner/work/template-fastapi/template-fastapi/scripts/files.py
33

4-
import os
54
from pathlib import Path
6-
from typing import List, Optional
75

86
import typer
97
from rich.console import Console
@@ -18,89 +16,89 @@
1816

1917
@app.command()
2018
def list_files(
21-
prefix: Optional[str] = typer.Option(None, "--prefix", "-p", help="ファイル名のプレフィックス"),
19+
prefix: str | None = typer.Option(None, "--prefix", "-p", help="ファイル名のプレフィックス"),
2220
):
2321
"""ファイル一覧を取得する"""
24-
console.print(f"[bold green]ファイル一覧[/bold green]を取得します")
25-
22+
console.print("[bold green]ファイル一覧[/bold green]を取得します")
23+
2624
if prefix:
2725
console.print(f"プレフィックス: {prefix}")
28-
26+
2927
try:
3028
files = file_repo.list_files(prefix=prefix)
31-
29+
3230
if not files:
3331
console.print("[yellow]ファイルが見つかりませんでした[/yellow]")
3432
return
35-
33+
3634
# テーブルで表示
3735
table = Table(title="ファイル一覧")
3836
table.add_column("ファイル名", style="cyan")
3937
table.add_column("サイズ (bytes)", style="green")
4038
table.add_column("コンテンツタイプ", style="yellow")
4139
table.add_column("最終更新日時", style="magenta")
42-
40+
4341
for file in files:
4442
table.add_row(
4543
file.name,
4644
str(file.size) if file.size else "N/A",
4745
file.content_type or "N/A",
48-
file.last_modified.strftime("%Y-%m-%d %H:%M:%S") if file.last_modified else "N/A"
46+
file.last_modified.strftime("%Y-%m-%d %H:%M:%S") if file.last_modified else "N/A",
4947
)
50-
48+
5149
console.print(table)
5250
console.print(f"[bold blue]合計: {len(files)}件[/bold blue]")
53-
51+
5452
except Exception as e:
5553
console.print(f"[bold red]エラー[/bold red]: {str(e)}")
5654

5755

5856
@app.command()
5957
def upload_file(
6058
file_path: str = typer.Argument(..., help="アップロードするファイルのパス"),
61-
blob_name: Optional[str] = typer.Option(None, "--name", "-n", help="Blob名(指定しない場合はファイル名を使用)"),
59+
blob_name: str | None = typer.Option(None, "--name", "-n", help="Blob名(指定しない場合はファイル名を使用)"),
6260
):
6361
"""単一のファイルをアップロードする"""
6462
file_path_obj = Path(file_path)
65-
63+
6664
if not file_path_obj.exists():
6765
console.print(f"[bold red]エラー[/bold red]: ファイル '{file_path}' が見つかりません")
6866
return
69-
67+
7068
if not file_path_obj.is_file():
7169
console.print(f"[bold red]エラー[/bold red]: '{file_path}' はファイルではありません")
7270
return
73-
71+
7472
blob_name = blob_name or file_path_obj.name
7573
console.print(f"[bold green]ファイル[/bold green]: {file_path} -> {blob_name}")
76-
74+
7775
try:
7876
with open(file_path_obj, "rb") as f:
7977
file_data = f.read()
80-
78+
8179
uploaded_file = file_repo.upload_file(
8280
file_name=blob_name,
8381
file_data=file_data,
84-
content_type=None # Let Azure detect the content type
82+
content_type=None, # Let Azure detect the content type
8583
)
86-
87-
console.print(f"[bold green]アップロード成功[/bold green]")
84+
85+
console.print("[bold green]アップロード成功[/bold green]")
8886
console.print(f" ファイル名: {uploaded_file.name}")
8987
console.print(f" サイズ: {uploaded_file.size} bytes")
9088
console.print(f" コンテンツタイプ: {uploaded_file.content_type}")
9189
console.print(f" URL: {uploaded_file.url}")
92-
90+
9391
except Exception as e:
9492
console.print(f"[bold red]エラー[/bold red]: {str(e)}")
9593

9694

9795
@app.command()
9896
def upload_multiple_files(
99-
file_paths: List[str] = typer.Argument(..., help="アップロードするファイルのパス(複数指定可能)"),
97+
file_paths: list[str] = typer.Argument(..., help="アップロードするファイルのパス(複数指定可能)"),
10098
):
10199
"""複数のファイルを同時にアップロードする"""
102-
console.print(f"[bold green]複数ファイル[/bold green]をアップロードします")
103-
100+
console.print("[bold green]複数ファイル[/bold green]をアップロードします")
101+
104102
# ファイルの存在チェック
105103
valid_files = []
106104
for file_path in file_paths:
@@ -112,49 +110,49 @@ def upload_multiple_files(
112110
console.print(f"[bold red]警告[/bold red]: '{file_path}' はファイルではありません - スキップします")
113111
continue
114112
valid_files.append(file_path_obj)
115-
113+
116114
if not valid_files:
117115
console.print("[bold red]エラー[/bold red]: 有効なファイルが見つかりませんでした")
118116
return
119-
117+
120118
try:
121119
file_data_list = []
122120
for file_path_obj in valid_files:
123121
with open(file_path_obj, "rb") as f:
124122
file_data = f.read()
125123
file_data_list.append((file_path_obj.name, file_data, None))
126-
124+
127125
uploaded_files = file_repo.upload_files(file_data_list)
128-
126+
129127
console.print(f"[bold green]アップロード成功[/bold green]: {len(uploaded_files)}件")
130128
for uploaded_file in uploaded_files:
131129
console.print(f" - {uploaded_file.name} ({uploaded_file.size} bytes)")
132-
130+
133131
except Exception as e:
134132
console.print(f"[bold red]エラー[/bold red]: {str(e)}")
135133

136134

137135
@app.command()
138136
def download_file(
139137
blob_name: str = typer.Argument(..., help="ダウンロードするBlobの名前"),
140-
output_path: Optional[str] = typer.Option(None, "--output", "-o", help="出力ファイルのパス"),
138+
output_path: str | None = typer.Option(None, "--output", "-o", help="出力ファイルのパス"),
141139
):
142140
"""ファイルをダウンロードする"""
143141
console.print(f"[bold green]ファイル[/bold green]: {blob_name} をダウンロードします")
144-
142+
145143
try:
146144
file_data = file_repo.download_file(blob_name)
147-
145+
148146
output_path = output_path or blob_name
149147
output_path_obj = Path(output_path)
150-
148+
151149
with open(output_path_obj, "wb") as f:
152150
f.write(file_data)
153-
154-
console.print(f"[bold green]ダウンロード成功[/bold green]")
151+
152+
console.print("[bold green]ダウンロード成功[/bold green]")
155153
console.print(f" 出力先: {output_path_obj.absolute()}")
156154
console.print(f" サイズ: {len(file_data)} bytes")
157-
155+
158156
except Exception as e:
159157
console.print(f"[bold red]エラー[/bold red]: {str(e)}")
160158

@@ -165,16 +163,16 @@ def get_file_info(
165163
):
166164
"""ファイル情報を取得する"""
167165
console.print(f"[bold green]ファイル情報[/bold green]: {blob_name}")
168-
166+
169167
try:
170168
file_info = file_repo.get_file_info(blob_name)
171-
169+
172170
console.print(f" ファイル名: {file_info.name}")
173171
console.print(f" サイズ: {file_info.size} bytes")
174172
console.print(f" コンテンツタイプ: {file_info.content_type}")
175173
console.print(f" 最終更新日時: {file_info.last_modified}")
176174
console.print(f" URL: {file_info.url}")
177-
175+
178176
except Exception as e:
179177
console.print(f"[bold red]エラー[/bold red]: {str(e)}")
180178

@@ -186,7 +184,7 @@ def delete_file(
186184
):
187185
"""ファイルを削除する"""
188186
console.print(f"[bold green]ファイル削除[/bold green]: {blob_name}")
189-
187+
190188
try:
191189
# 削除前に確認
192190
if not force:
@@ -195,45 +193,45 @@ def delete_file(
195193
console.print(f" ファイル名: {file_info.name}")
196194
console.print(f" サイズ: {file_info.size} bytes")
197195
console.print(f" 最終更新日時: {file_info.last_modified}")
198-
196+
199197
if not typer.confirm("削除してもよろしいですか?"):
200198
console.print("[yellow]削除をキャンセルしました[/yellow]")
201199
return
202-
200+
203201
file_repo.delete_file(blob_name)
204202
console.print(f"[bold green]削除成功[/bold green]: {blob_name}")
205-
203+
206204
except Exception as e:
207205
console.print(f"[bold red]エラー[/bold red]: {str(e)}")
208206

209207

210208
@app.command()
211209
def delete_multiple_files(
212-
blob_names: List[str] = typer.Argument(..., help="削除するBlobの名前(複数指定可能)"),
210+
blob_names: list[str] = typer.Argument(..., help="削除するBlobの名前(複数指定可能)"),
213211
force: bool = typer.Option(False, "--force", "-f", help="確認なしで削除する"),
214212
):
215213
"""複数のファイルを同時に削除する"""
216214
console.print(f"[bold green]複数ファイル削除[/bold green]: {len(blob_names)}件")
217-
215+
218216
try:
219217
# 削除前に確認
220218
if not force:
221219
console.print("以下のファイルを削除しようとしています:")
222220
for blob_name in blob_names:
223221
console.print(f" - {blob_name}")
224-
222+
225223
if not typer.confirm("削除してもよろしいですか?"):
226224
console.print("[yellow]削除をキャンセルしました[/yellow]")
227225
return
228-
226+
229227
deleted_files = file_repo.delete_files(blob_names)
230228
console.print(f"[bold green]削除成功[/bold green]: {len(deleted_files)}件")
231229
for deleted_file in deleted_files:
232230
console.print(f" - {deleted_file}")
233-
231+
234232
except Exception as e:
235233
console.print(f"[bold red]エラー[/bold red]: {str(e)}")
236234

237235

238236
if __name__ == "__main__":
239-
app()
237+
app()

template_fastapi/models/file.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
from datetime import datetime
2-
from typing import Optional
32

43
from pydantic import BaseModel, ConfigDict
54

65

76
class File(BaseModel):
87
"""ファイル情報を表すモデル"""
9-
8+
109
model_config = ConfigDict(extra="ignore")
11-
10+
1211
name: str
13-
size: Optional[int] = None
14-
content_type: Optional[str] = None
15-
last_modified: Optional[datetime] = None
16-
url: Optional[str] = None
12+
size: int | None = None
13+
content_type: str | None = None
14+
last_modified: datetime | None = None
15+
url: str | None = None

template_fastapi/repositories/files.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
from typing import List, Optional
2-
3-
from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient
41
from azure.core.exceptions import ResourceNotFoundError
2+
from azure.storage.blob import BlobServiceClient, ContainerClient
53

64
from template_fastapi.models.file import File
75
from template_fastapi.settings.azure_blob_storage import get_azure_blob_storage_settings
@@ -35,7 +33,7 @@ def container_client(self) -> ContainerClient:
3533
)
3634
return self._container_client
3735

38-
def list_files(self, prefix: Optional[str] = None) -> List[File]:
36+
def list_files(self, prefix: str | None = None) -> list[File]:
3937
"""ファイル一覧を取得する"""
4038
try:
4139
blobs = self.container_client.list_blobs(name_starts_with=prefix)
@@ -52,29 +50,29 @@ def list_files(self, prefix: Optional[str] = None) -> List[File]:
5250
except Exception as e:
5351
raise Exception(f"ファイル一覧の取得に失敗しました: {str(e)}")
5452

55-
def upload_file(self, file_name: str, file_data: bytes, content_type: Optional[str] = None) -> File:
53+
def upload_file(self, file_name: str, file_data: bytes, content_type: str | None = None) -> File:
5654
"""ファイルをアップロードする"""
5755
try:
5856
blob_client = self.container_client.get_blob_client(file_name)
5957
blob_client.upload_blob(
60-
file_data,
61-
overwrite=True,
62-
content_settings={'content_type': content_type} if content_type else None
58+
file_data, overwrite=True, content_settings={"content_type": content_type} if content_type else None
6359
)
64-
60+
6561
# アップロードされたファイル情報を取得
6662
blob_properties = blob_client.get_blob_properties()
6763
return File(
6864
name=blob_properties.name,
6965
size=blob_properties.size,
70-
content_type=blob_properties.content_settings.content_type if blob_properties.content_settings else None,
66+
content_type=blob_properties.content_settings.content_type
67+
if blob_properties.content_settings
68+
else None,
7169
last_modified=blob_properties.last_modified,
72-
url=blob_client.url
70+
url=blob_client.url,
7371
)
7472
except Exception as e:
7573
raise Exception(f"ファイルのアップロードに失敗しました: {str(e)}")
7674

77-
def upload_files(self, files: List[tuple[str, bytes, Optional[str]]]) -> List[File]:
75+
def upload_files(self, files: list[tuple[str, bytes, str | None]]) -> list[File]:
7876
"""複数のファイルを同時にアップロードする"""
7977
uploaded_files = []
8078
for file_name, file_data, content_type in files:
@@ -100,9 +98,11 @@ def get_file_info(self, file_name: str) -> File:
10098
return File(
10199
name=blob_properties.name,
102100
size=blob_properties.size,
103-
content_type=blob_properties.content_settings.content_type if blob_properties.content_settings else None,
101+
content_type=blob_properties.content_settings.content_type
102+
if blob_properties.content_settings
103+
else None,
104104
last_modified=blob_properties.last_modified,
105-
url=blob_client.url
105+
url=blob_client.url,
106106
)
107107
except ResourceNotFoundError:
108108
raise Exception(f"ファイル '{file_name}' が見つかりません")
@@ -120,10 +120,10 @@ def delete_file(self, file_name: str) -> bool:
120120
except Exception as e:
121121
raise Exception(f"ファイルの削除に失敗しました: {str(e)}")
122122

123-
def delete_files(self, file_names: List[str]) -> List[str]:
123+
def delete_files(self, file_names: list[str]) -> list[str]:
124124
"""複数のファイルを同時に削除する"""
125125
deleted_files = []
126126
for file_name in file_names:
127127
if self.delete_file(file_name):
128128
deleted_files.append(file_name)
129-
return deleted_files
129+
return deleted_files

0 commit comments

Comments
 (0)