Skip to content

Commit 51b8b7e

Browse files
committed
feature: Implement keyring support to store deepl API key
1 parent 744482c commit 51b8b7e

File tree

8 files changed

+301
-12
lines changed

8 files changed

+301
-12
lines changed

.gitlab-ci.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ include:
33
- project: '${CI_PROJECT_NAMESPACE}/ci-libs-for-client-libraries'
44
file:
55
- '/${CI_PROJECT_NAME}/.gitlab-ci.yml'
6-
6+
- project: 'deepl/ops/ci-cd-infrastructure/gitlab-ci-lib'
7+
file:
8+
- '/templates/.buildkit.yml'
9+
710
# Global --------------------------
811

912
image: python:3.11
@@ -13,7 +16,7 @@ variables:
1316
XDG_CACHE_HOME: "${CI_PROJECT_DIR}/.cache"
1417
POETRY_VIRTUALENVS_IN_PROJECT: "true"
1518
REQUESTS_CA_BUNDLE: "/etc/ssl/certs/ca-certificates.crt"
16-
19+
1720
cache:
1821
key:
1922
prefix: ${CI_JOB_IMAGE}
@@ -30,6 +33,7 @@ stages:
3033
- test
3134
- publish
3235

36+
3337
before_script:
3438
- python3 -m venv .venv
3539
- source .venv/bin/activate
@@ -140,4 +144,4 @@ pypi upload:
140144
rules:
141145
- if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/'
142146
script:
143-
- poetry publish --verbose --no-interaction
147+
- poetry publish --verbose --no-interaction

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99
### Added
1010
* Add [example script](examples/mustache) to translate Mustache templates.
11+
* Add support for storing your API Key in a keyring via the `keyring` module.
1112
### Changed
1213
### Deprecated
1314
### Removed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,14 +491,16 @@ python3 -m deepl --help
491491
```
492492

493493
The CLI requires your DeepL authentication key specified either as the
494-
`DEEPL_AUTH_KEY` environment variable, or using the `--auth-key` option, for
495-
example:
494+
`DEEPL_AUTH_KEY` environment variable, through the `keyring` module, or
495+
using the `--auth-key` option, for example:
496496

497497
```shell
498498
python3 -m deepl --auth-key=YOUR_AUTH_KEY usage
499499
```
500500

501501
Note that the `--auth-key` argument must appear *before* the command argument.
502+
To use the [keyring](https://pypi.org/project/keyring/) module, set the
503+
*DEEPL_AUTH_KEY* field in the service *deepl* to your API key.
502504
The recognized commands are:
503505

504506
| Command | Description |

deepl/__main__.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import pathlib
1010
import sys
1111
from typing import List
12+
from deepl.util import _optional_import
1213

1314
# Program name for integration with click.testing
1415
name = "python -m deepl"
@@ -17,6 +18,9 @@
1718
env_server_url = "DEEPL_SERVER_URL"
1819
env_proxy_url = "DEEPL_PROXY_URL"
1920

21+
keyring_key_folder = "deepl"
22+
keyring_key_name = env_auth_key
23+
2024

2125
def action_usage(translator: deepl.Translator):
2226
"""Action function for the usage command."""
@@ -208,7 +212,9 @@ def get_parser(prog_name):
208212
"--auth-key",
209213
default=None,
210214
help="authentication key as given in your DeepL account; the "
211-
f"{env_auth_key} environment variable is used as secondary fallback",
215+
f"{env_auth_key} environment variable is used as secondary fallback; "
216+
f"the key {keyring_key_name} in {keyring_key_folder} is used "
217+
"as tertiary fallback",
212218
)
213219
parser.add_argument(
214220
"--server-url",
@@ -500,14 +506,26 @@ def main(args=None, prog_name=None):
500506
logger.setLevel(logging.WARNING)
501507

502508
server_url = args.server_url or os.getenv(env_server_url)
503-
auth_key = args.auth_key or os.getenv(env_auth_key)
504509
proxy_url = args.proxy_url or os.getenv(env_proxy_url)
505510

511+
auth_key = args.auth_key or os.getenv(env_auth_key)
512+
keyring = _optional_import("keyring")
513+
if keyring:
514+
keyring_pw = None
515+
try:
516+
keyring_pw = keyring.get_password(
517+
keyring_key_folder, keyring_key_name
518+
)
519+
except keyring.errors.NoKeyringError:
520+
pass
521+
auth_key = auth_key or keyring_pw
522+
506523
try:
507524
if auth_key is None:
508525
raise Exception(
509526
f"Please provide authentication key via the {env_auth_key} "
510-
"environment variable or --auth_key argument"
527+
"environment variable or --auth_key argument or via "
528+
f"{keyring_key_name} in {keyring_key_folder} in keyring"
511529
)
512530

513531
# Note: the get_languages() call to verify language codes is skipped

deepl/util.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,36 @@
11
# Copyright 2022 DeepL SE (https://www.deepl.com)
22
# Use of this source code is governed by an MIT
33
# license that can be found in the LICENSE file.
4+
import importlib
45
import itertools
56
import logging
67
from typing import Dict, Optional
78

89
logger = logging.getLogger("deepl")
910

1011

12+
def _optional_import(module_name: str):
13+
"""Tries importing the specified module and returns it if successful,
14+
else None.
15+
Example:
16+
17+
keyring = _optional_import('keyring')
18+
if keyring:
19+
keyring.get_password(...)
20+
else:
21+
# Code to handle the module not being present
22+
pass
23+
24+
:param module_name: str containing the exact module name
25+
:return: The module, if the import was successful, or None
26+
"""
27+
try:
28+
module = importlib.import_module(module_name)
29+
return module
30+
except ImportError:
31+
return None
32+
33+
1134
def _get_log_text(message, **kwargs):
1235
return (
1336
message

0 commit comments

Comments
 (0)