Skip to content

Commit

Permalink
Merge branch 'rl-1.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
amenezes committed Oct 24, 2023
2 parents 60a6ffb + 367a5b3 commit 7eb1c12
Show file tree
Hide file tree
Showing 32 changed files with 499 additions and 191 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
tests:
strategy:
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11']
os: [ubuntu]
fail-fast: true
runs-on: ${{ matrix.os }}-latest
Expand Down
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python 3.11.2
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ ifeq ($(SKIP_STYLE), )
black config
black tests
endif
@echo "> running bandit"
bandit -r -ll -ii -s B104 config
@echo "> running radon"
radon cc -s -n C config tests
@echo "> running flake8..."
flake8 config
flake8 tests
Expand All @@ -23,7 +27,7 @@ tests:
docs:
@echo "> generate project documentation..."
@cp README.md docs/index.md
mkdocs serve
mkdocs serve -a 0.0.0.0:8000

install-deps:
@echo "> installing dependencies..."
Expand Down
2 changes: 1 addition & 1 deletion config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from .cfenv import CFenv
from .spring import ConfigClient, config_client, create_config_client

__version__ = "1.3.0"
__version__ = "1.4.0"
__all__ = [
"__version__",
"ConfigClient",
Expand Down
2 changes: 1 addition & 1 deletion config/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def merge_list(config: dict) -> None:
config.pop(f"{key}[{i}]")


def _merge(config: dict):
def _merge(config: dict) -> None:
merge_list(config)
for k, v in config.items():
merge_list(config[k])
Expand Down
10 changes: 6 additions & 4 deletions config/auth.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Dict

from attrs import field, mutable, validators
from requests.auth import HTTPBasicAuth
from requests.exceptions import HTTPError, MissingSchema
Expand Down Expand Up @@ -28,7 +30,7 @@ def token(self, value) -> None:
logger.debug(f"set: [access_token='{self._token}']")

@property
def authorization_header(self) -> dict:
def authorization_header(self) -> Dict[str, str]:
return {"Authorization": f"Bearer {self.token}"}

def request_token(self, client_auth: HTTPBasicAuth, data: dict, **kwargs) -> None:
Expand All @@ -37,11 +39,11 @@ def request_token(self, client_auth: HTTPBasicAuth, data: dict, **kwargs) -> Non
self.access_token_uri, auth=client_auth, data=data, **kwargs
)
except MissingSchema:
raise RequestFailedException("Access token URL it's empty")
raise RequestFailedException("empty")
except HTTPError:
raise RequestTokenException("Failed to retrieve oauth2 access_token.")
raise RequestTokenException
self.token = response.json().get("access_token")
logger.info("Access token successfully obtained.")
logger.debug("Access token successfully obtained.")

def configure(self, **kwargs) -> None:
client_auth = HTTPBasicAuth(self.client_id, self.client_secret)
Expand Down
41 changes: 40 additions & 1 deletion config/cf.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,55 @@ def vcap_application(self):
return self.cfenv.vcap_application

def get_config(self, **kwargs) -> None:
"""Request the configuration to the config server.
Usage:
# Example 1:
cf.get_config()
# Example 2:
cf.get_config(verify=False)
:param kwargs: any keyword argument used to configure oauth2 or request for the server.
"""
self.client.get_config(**kwargs)

async def get_config_async(self, **kwargs) -> None:
"""Request the configuration to the config server.
Usage:
# Example 1:
await cf.get_config_async()
# Example 2:
await cf.get_config_async(verify=False)
:param kwargs: any keyword argument used to configure oauth2 or request for the server.
"""
await self.client.get_config_async(**kwargs)

@property
def config(self) -> Dict:
"""Getter from configurations retrieved from ConfigClient."""
return self.client.config

def get(self, key, default: Any = ""):
def get(self, key: str, default: Any = "") -> Any:
"""Loads a configuration from a key.
Usage:
# Example 1:
cf.get('spring')
# Exampel 2:
cf.get('spring.cloud.consul')
:param key: configuration key.
:param default: default value if key does not exist. [default=''].
"""
return self.client.get(key, default)

def keys(self) -> KeysView:
Expand Down
45 changes: 27 additions & 18 deletions config/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from rich.panel import Panel
from rich.status import Status
from rich.table import Table
from trogon import tui

from config import __version__
from config.exceptions import RequestFailedException
Expand All @@ -26,6 +27,7 @@
console = Console()


@tui(command="terminal-ui", help="Open terminal UI")
@click.group(context_settings=CONTEXT_SETTINGS)
@click.version_option(version=__version__)
def cli():
Expand Down Expand Up @@ -82,16 +84,7 @@ def client(
)

if file:
# get file from server and exit
with Status("Contacting server...", spinner="dots4") as status:
try:
resp = client.get_file(file)
except RequestFailedException:
raise click.ClickException("💥 Failed to contact server!")
Path(file).write_text(resp)
status.update("OK!")
console.print(f"File saved: [cyan]{file}[/cyan]", style="bold")
raise SystemExit
_download_file(file, client)

if verbose:
table = Table.grid(padding=(0, 1))
Expand All @@ -114,14 +107,7 @@ def client(
with Status("Contacting server...", spinner="dots4") as status:
emoji = random.choice(EMOJI_ERRORS)
try:
if auth:
username, password = auth.split(":")
auth = HTTPBasicAuth(username, password)
elif digest:
username, password = digest.split(":")
auth = HTTPDigestAuth(username, password)
else:
auth = None
auth = _configure_auth(auth, digest)
client.get_config(auth=auth)
except ValueError:
raise click.ClickException(
Expand Down Expand Up @@ -160,6 +146,29 @@ def client(
)


def _download_file(file, client):
"""Get file from server and exit."""
with Status("Contacting server...", spinner="dots4") as status:
try:
resp = client.get_file(file)
except RequestFailedException:
raise click.ClickException("💥 Failed to contact server!")
Path(file).write_text(resp)
status.update("OK!")
console.print(f"File saved: [cyan]{file}[/cyan]", style="bold")
raise SystemExit


def _configure_auth(basic_auth, digest_auth):
if basic_auth:
username, password = basic_auth.split(":")
return HTTPBasicAuth(username, password)
elif digest_auth:
username, password = digest_auth.split(":")
return HTTPDigestAuth(username, password)
return None


@cli.command()
@click.argument("text")
@click.option(
Expand Down
8 changes: 6 additions & 2 deletions config/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
class RequestTokenException(Exception):
pass
def __init__(self, message: str = "Faield to retrieve oauth2 access_token") -> None:
super().__init__(message)


class RequestFailedException(Exception):
pass
def __init__(
self, url: str, message: str = "Failed to perform request: [URL='{url}']"
) -> None:
super().__init__(message.format(url=url))
18 changes: 18 additions & 0 deletions config/ext/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,24 @@ def __init__(
client: Optional[ConfigClient] = None,
**kwargs,
) -> None:
"""Configure AIOHTTP application with config-client.
Usage:
from config.ext import AioHttpConfig
from aiohttp import web
app = web.Application()
AioHttpConfig(app)
web.run_app(app)
:param app: AIOHTTP web.Application.
:param key: key prefix to access config.
:param client: custom ConfigClient.
"""
self._validate_app(app)
if not client:
client = ConfigClient()
Expand Down
34 changes: 34 additions & 0 deletions config/ext/fastapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@


async def fastapi_config_client(request: Request):
"""Configure FastAPI application with config-client.
Usage:
from fastapi import Depends, FastAPI
from config.ext.fastapi import fastapi_config_client
app = FastAPI(dependencies=[Depends(fastapi_config_client)])
@app.get("/info")
def consul(request: Request):
return dict(
description=request.app.config_client.get("info.app.description"),
url=request.app.config_client.get("info.app.name"),
)
"""
try:
request.app.config_client
logger.debug("ConfigClient already initialized")
Expand All @@ -17,6 +34,23 @@ async def fastapi_config_client(request: Request):


async def fastapi_cloud_foundry(request: Request):
"""Configure FastAPI application with config-client.
Usage:
from fastapi import Depends, FastAPI
from config.ext.fastapi import fastapi_cloud_foundry
app = FastAPI(dependencies=[Depends(fastapi_cloud_foundry)])
@app.get("/info")
def consul(request: Request):
return dict(
description=request.app.config_client.get("info.app.description"),
url=request.app.config_client.get("info.app.name"),
)
"""
try:
logger.debug("ConfigClient already initialized")
except AttributeError:
Expand Down
16 changes: 16 additions & 0 deletions config/ext/flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ class FlaskConfig:
def __init__(
self, app: Flask, client: Optional[ConfigClient] = None, **kwargs
) -> None:
"""Configure Flask application with config-client.
Usage:
from config.ext.flask import FlaskConfig
from flask import Flask
app = Flask(__name__)
FlaskConfig(app)
:param app: Flask application.
:param client: custom ConfigClient.
:param kwargs: any keyword argument used to request config from the server.
"""
if not isinstance(app, Flask):
raise TypeError("app must be Flask instance")

Expand Down
Loading

0 comments on commit 7eb1c12

Please sign in to comment.