Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 6 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ https://pypi.org/project/annofabapi/

## インスタンス生成

認証にはパーソナルアクセストークン(PAT)の利用を推奨します。

### user_id, passwordをコンストラクタ引数に渡す場合

```python
Expand All @@ -77,31 +79,6 @@ service = build(pat = pat)
```


### `.netrc`に認証情報を記載する場合
`.netrc`ファイルに、AnnofabのユーザIDとパスワードを記載します。

```plain:.netrc
machine annofab.com
login annofab_user_id
password annofab_password
```

```python
from annofabapi import build_from_netrc
service = build_from_netrc()
```


#### For Linux
* パスは`$HOME/.netrc`
* `$ chmod 600 $HOME/.netrc`でパーミッションを変更する



#### For Windows
* パスは`%USERPROFILE%\.netrc`


### 環境変数に認証情報を設定する場合


Expand All @@ -117,19 +94,17 @@ service = build_from_env()
```


### `.netrc`または環境変数に認証情報を設定する場合
``build()`` を実行すると、環境変数または `.netrc` ファイルから認証情報を読み込みます。
### 引数なしでインスタンスを生成する場合
``build()`` を実行すると、環境変数から認証情報を読み込みます。

```python
from annofabapi import build
service = build()
```

優先順位は以下の通りです。
1. 環境変数
1. `ANNOFAB_PAT`
2. `ANNOFAB_USER_ID`及び`ANNOFAB_PASSWORD`
2. `.netrc`
1. `ANNOFAB_PAT`
2. `ANNOFAB_USER_ID`及び`ANNOFAB_PASSWORD`


## APIメソッドの引数
Expand Down
2 changes: 1 addition & 1 deletion README_for_developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ $ make format && make lint
# Test

## テストの実行方法
1. Annofabの認証情報を、`.netrc`ファイルまたは環境変数に設定する。
1. 環境変数`ANNOFAB_PAT`にAnnofabのパーソナルアクセストークンを設定する。
2. 以下のコマンドを実行して、テスト用のプロジェクトとタスクを作成する。
* `uv run python tests/create_test_project.py --organization ${MY_ORGANIZATION}`
3. `pytest.ini`に、テスト対象の`project_id`と`task_id`を指定する。
Expand Down
3 changes: 1 addition & 2 deletions annofabapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
from annofabapi.api import AnnofabApi
from annofabapi.api2 import AnnofabApi2
from annofabapi.wrapper import Wrapper
from annofabapi.resource import build, build_from_netrc, build_from_env, Resource
from annofabapi.resource import build, build_from_env, Resource

__all__ = [
"AnnofabApi",
"AnnofabApi2",
"Wrapper",
"build",
"build_from_netrc",
"build_from_env",
"Resource",
]
Expand Down
50 changes: 3 additions & 47 deletions annofabapi/resource.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import logging
import netrc
import os
from urllib.parse import urlparse

from annofabapi import AnnofabApi, AnnofabApi2, Wrapper
from annofabapi.api import DEFAULT_ENDPOINT_URL
Expand Down Expand Up @@ -56,8 +54,7 @@ def build(
AnnofabApi, Wrapperのインスタンスを保持するインスタンスを生成する。

``pat``が渡された場合はそれが優先して利用される。
``pat`` / ``login_user_id`` / ``login_password`` の全てがNoneの場合は、``.netrc`` ファイルまたは環境変数から認証情報を取得する。
認証情報は、環境変数, ``.netrc`` ファイルの順に読み込む。
``pat`` / ``login_user_id`` / ``login_password`` の全てがNoneの場合は、環境変数から認証情報を取得する。

環境変数は``ANNOFAB_USER_ID`` , ``ANNOFAB_PASSWORD``, ``ANNOFAB_PAT`` を参照し、``ANNOFAB_PAT``が設定されている場合はそれ以外を無視する。

Expand All @@ -73,7 +70,7 @@ def build(
AnnofabApi, Wrapperのインスタンスを保持するインスタンス

Raises:
CredentialsNotFoundError: `.netrc`ファイルまたは環境変数にAnnofabの認証情報がなかった
CredentialsNotFoundError: 環境変数にAnnofabの認証情報がなかった

"""
if pat is not None:
Expand All @@ -84,52 +81,11 @@ def build(
)

elif login_user_id is None and login_password is None and pat is None:
try:
return build_from_env(endpoint_url=endpoint_url, input_mfa_code_via_stdin=input_mfa_code_via_stdin)
except CredentialsNotFoundError:
try:
return build_from_netrc(endpoint_url=endpoint_url, input_mfa_code_via_stdin=input_mfa_code_via_stdin)
except CredentialsNotFoundError as e:
raise CredentialsNotFoundError("環境変数または`.netrc`ファイルにAnnofab認証情報はありませんでした。") from e
return build_from_env(endpoint_url=endpoint_url, input_mfa_code_via_stdin=input_mfa_code_via_stdin)
else:
raise ValueError("引数`login_user_id`か`login_password`のどちらか一方がNoneです。両方Noneでないか、両方Noneである必要があります。")


def build_from_netrc(*, endpoint_url: str = DEFAULT_ENDPOINT_URL, input_mfa_code_via_stdin: bool = False) -> Resource:
"""
``.netrc`` ファイルから、annofabapi.Resourceインスタンスを生成する。

Args:
endpoint_url: Annofab APIのエンドポイント。
input_mfa_code_via_stdin: MFAコードを標準入力から入力するかどうか
Falseを渡して且つMFAコードの入力を求められるアカウントを利用する場合、mfa_codeを引数にloginメソッドを直接呼び出さなければならず、そうしない場合は例外を送出する

Returns:
annofabapi.Resourceインスタンス

Raises:
CredentialsNotFoundError: `.netrc`ファイルにAnnofabの認証情報がなかった

"""
try:
netrc_hosts = netrc.netrc().hosts
except FileNotFoundError as e:
raise CredentialsNotFoundError("`.netrc`ファイルは見つかりません。") from e

annofab_hostname = (urlparse(endpoint_url)).hostname

if annofab_hostname not in netrc_hosts:
raise CredentialsNotFoundError(f"The `.netrc` file does not contain the machine name '{annofab_hostname}'")

host = netrc_hosts[annofab_hostname]
login_user_id = host[0]
login_password = host[2]
if login_user_id is None or login_password is None:
raise CredentialsNotFoundError("User ID or password in the .netrc file are None.")

return Resource(credentials=IdPass(login_user_id, login_password), endpoint_url=endpoint_url, input_mfa_code_via_stdin=input_mfa_code_via_stdin)


def build_from_env(*, endpoint_url: str = DEFAULT_ENDPOINT_URL, input_mfa_code_via_stdin: bool = False) -> Resource:
"""
環境変数 ``ANNOFAB_USER_ID`` , ``ANNOFAB_PASSWORD``, ``ANNOFAB_PAT`` から、annofabapi.Resourceインスタンスを生成する。
Expand Down
3 changes: 1 addition & 2 deletions docs/user_guide/basic_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,7 @@ annofabapiは、pythonのloggingモジュールを利用してログメッセー
logging.getLogger("annofabapi").setLevel(level=logging.DEBUG)

from annofabapi import build
servcie = build()
# DEBUG : 2020-12-09 21:09:04,728 : resource.py : annofabapi.resource : build_from_netrc : .netrcファイルからAnnofab認証情報を読み込みました。
service = build()



36 changes: 22 additions & 14 deletions tests/test_build.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
import os

import pytest

from annofabapi.credentials import IdPass, Pat
from annofabapi.exceptions import CredentialsNotFoundError
from annofabapi.resource import build


class Test引数なしでbuildした時:
@pytest.fixture(autouse=True)
def clear_env(self):
os.environ.pop("ANNOFAB_USER_ID", None)
os.environ.pop("ANNOFAB_PASSWORD", None)
os.environ.pop("ANNOFAB_PAT", None)
yield
def clear_env(self, monkeypatch):
for name in ("ANNOFAB_USER_ID", "ANNOFAB_PASSWORD", "ANNOFAB_PAT"):
monkeypatch.delenv(name, raising=False)

def test_netrcに認証情報があっても環境変数に認証情報がなければエラーになること(self, tmp_path, monkeypatch):
netrc_path = tmp_path / ".netrc"
netrc_path.write_text("machine annofab.com login DUMMY_USER_ID password DUMMY_PASSWORD\n", encoding="utf-8")
netrc_path.chmod(0o600)
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("USERPROFILE", str(tmp_path))
monkeypatch.setenv("NETRC", str(netrc_path))

with pytest.raises(CredentialsNotFoundError):
build()

def test_環境変数ANNOFAB_PATが設定されている場合はredentialsがPatになっていること(self):
os.environ["ANNOFAB_PAT"] = "DUMMY_PAT"
os.environ["ANNOFAB_USER_ID"] = "DUMMY_USER_ID"
os.environ["ANNOFAB_PASSWORD"] = "DUMMY_PASSWORD"
def test_環境変数ANNOFAB_PATが設定されている場合はredentialsがPatになっていること(self, monkeypatch):
monkeypatch.setenv("ANNOFAB_PAT", "DUMMY_PAT")
monkeypatch.setenv("ANNOFAB_USER_ID", "DUMMY_USER_ID")
monkeypatch.setenv("ANNOFAB_PASSWORD", "DUMMY_PASSWORD")
resource = build()
assert isinstance(resource.api.credentials, Pat)
assert resource.api.credentials.token == "DUMMY_PAT"

def test_環境変数ANNOFAB_USER_IDとANNOFAB_PASSWORDのみが設定されている場合はredentialsがIdPathになっていること(self):
os.environ["ANNOFAB_USER_ID"] = "DUMMY_USER_ID"
os.environ["ANNOFAB_PASSWORD"] = "DUMMY_PASSWORD"
def test_環境変数ANNOFAB_USER_IDとANNOFAB_PASSWORDのみが設定されている場合はredentialsがIdPathになっていること(self, monkeypatch):
monkeypatch.setenv("ANNOFAB_USER_ID", "DUMMY_USER_ID")
monkeypatch.setenv("ANNOFAB_PASSWORD", "DUMMY_PASSWORD")
resource = build()
assert isinstance(resource.api.credentials, IdPass)
assert resource.api.credentials.user_id == "DUMMY_USER_ID"
Expand Down
Loading