From f01e231e7a2c5843dc93c74a65c0868b6d980fca Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sun, 20 Sep 2026 08:01:26 +0200 Subject: [PATCH] config: use pydantic_settings; apitest: with logging, config improvements --- apidemo/config.py | 45 ++++++++++++++++++++++++++-------------- pyproject.toml | 6 ++---- runtests.sh | 2 +- scopes/api/main.py | 10 ++++++--- scopes/tests/config.py | 42 ++++++++++++++++++++----------------- scopes/tests/test_api.py | 10 +++++---- 6 files changed, 68 insertions(+), 47 deletions(-) diff --git a/apidemo/config.py b/apidemo/config.py index 0f1c89f..951e5d1 100644 --- a/apidemo/config.py +++ b/apidemo/config.py @@ -1,31 +1,44 @@ # py-scopes/apidemo/config.py +from pydantic_settings import BaseSettings, SettingsConfigDict + +class Settings(BaseSettings): + model_config = SettingsConfigDict(env_file='.env', extra='ignore') + #, env_prefix='SCOPES_') + + base_url: str = 'https://test.cy7.eu' + + log_file: str = 'log/scopes.log' + log_level: str = 'INFO' + log_format: str = '%(asctime)s %(levelname)s %(name)s %(message)s' + log_dateformat: str = '%Y-%m-%dT%H:%M:%S' + + dbengine: str = 'postgresql+psycopg' + dbname: str = 'demo' + dbuser: str = 'demo' + dbpassword: str = 'secret' + dbschema: str = 'demo' + +settings = Settings() from dotenv import load_dotenv import logging from os import getenv -from scopes.web.app import zope_app_factory load_dotenv() -log_file = 'log/scopes.log' -log_level = logging.DEBUG -log_format = '%(asctime)s %(levelname)s %(name)s %(message)s' -log_dateformat = '%Y-%m-%dT%H:%M:%S' -logging.basicConfig(filename=log_file, level=log_level, - format=log_format, datefmt=log_dateformat) +base_url = settings.base_url -server_port = getenv('SERVER_PORT', '8099') -base_url = getenv('BASE_URL', 'https://demo.cy7.de') - -app_factory = zope_app_factory +logging.basicConfig(filename=settings.log_file, level=settings.log_level, + format=settings.log_format, datefmt=settings.log_dateformat) +logging.getLogger('uvicorn.error').propagate = True # storage settings from scopes.storage.db.postgres import StorageFactory -dbengine = 'postgresql+psycopg' -dbname = getenv('DBNAME', 'demo') -dbuser = getenv('DBUSER', 'demo') -dbpassword = getenv('DBPASSWORD', 'secret') -dbschema = getenv('DBSCHEMA', 'demo') +dbengine = settings.dbengine +dbname = settings.dbname +dbuser = settings.dbuser +dbpassword = settings.dbpassword +dbschema = settings.dbschema # authentication settings oidc_provider = getenv('OIDC_PROVIDER', 'https://a1.cy7.de') diff --git a/pyproject.toml b/pyproject.toml index 4686dc6..64f2375 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,9 +11,7 @@ license = {text = "MIT"} keywords = ["scopes"] authors = [{name = "Helmut Merz", email = "helmutm@cy55.de"}] -dependencies = [ - "SQLAlchemy", -] +dependencies = ["SQLAlchemy", "pydantic-settings"] [project.optional-dependencies] postgres = [ @@ -31,7 +29,7 @@ app = [ ] auth = ["pyjwt[crypto]", "cryptography", "requests"] api = ["fastapi[standard]"] -test = ["zope.testrunner"] +test = ["zope.testrunner", "pydantic-settings"] apitest = ["pytest", "httpx2"] [tool.setuptools] diff --git a/runtests.sh b/runtests.sh index 6e514f2..b9df66b 100755 --- a/runtests.sh +++ b/runtests.sh @@ -1,5 +1,5 @@ # runtests.sh # run all unit / doc tests -zope-testrunner --test-path=. $* +zope-testrunner --test-path=. -m !test_api $* diff --git a/scopes/api/main.py b/scopes/api/main.py index 702f03e..272017c 100644 --- a/scopes/api/main.py +++ b/scopes/api/main.py @@ -5,19 +5,20 @@ if '' not in sys.path: sys.path = [''] + sys.path import config -config.storageFactory = config.StorageFactory(config) from scopes.storage import tracking from scopes.storage.common import Storage import transaction from fastapi import Depends, FastAPI +from fastapi.responses import HTMLResponse from typing import Annotated # dependencies def storage(): - return config.storageFactory(config.dbschema) + stf = config.StorageFactory(config) + return stf(config.dbschema) # app and routes @@ -27,13 +28,16 @@ app = FastAPI() async def read_root(): return {'Hello': 'World'} +@app.get('/demo') +async def read_demo(): + return HTMLResponse('Hello World') + @app.get('/persons/{name}') async def read_person(name: str, q: str | None = None): return {'name': name, 'query': q} @app.get('/tracks') async def get_tracks(storage: Annotated[Storage, Depends(storage)]): - #storage = config.storageFactory(config.dbschema) tracks = storage.create(tracking.Container) result = [tr.asDict() for tr in tracks.query()] transaction.commit() diff --git a/scopes/tests/config.py b/scopes/tests/config.py index bfcd7ee..1bd0243 100644 --- a/scopes/tests/config.py +++ b/scopes/tests/config.py @@ -1,29 +1,33 @@ # py-scopes/tests/config.py import logging +from pydantic_settings import BaseSettings, SettingsConfigDict + +class Settings(BaseSettings): + model_config = SettingsConfigDict(env_file='.env') + + base_url: str = 'https://test.cy7.eu' + log_level: str = 'INFO' + + dbengine: str = 'sqlite' + dbname: str = 'var/test.db' + dbuser: str | None = None + dbpassword: str | None = None + dbschema: str | None = None + +settings = Settings() + from os import getenv import sys -#from scopes.web.app import demo_app, zope_app +base_url = settings.base_url -log_file = 'scopes/tests/log/scopes-test.log' -log_level = logging.INFO -log_format = '%(asctime)s %(levelname)s %(name)s %(message)s' -log_dateformat = '%Y-%m-%dT%H:%M:%S' - -# server / app settings -server_port = '8999' -base_url = 'testing:' -#app = zope_app - -# storage settings - -# SQLite -dbengine = 'sqlite' -dbname = 'var/test.db' -dbuser = None -dbpassword = None -dbschema = None +# SQLite storage settings +dbengine = settings.dbengine +dbname = settings.dbname +dbuser = settings.dbuser +dbpassword = settings.dbpassword +dbschema = settings.dbschema # special testing stuff from scopes.tests import data_auth # add oidc URIs and keys to dummy_requests data diff --git a/scopes/tests/test_api.py b/scopes/tests/test_api.py index 11eb66b..fa87504 100644 --- a/scopes/tests/test_api.py +++ b/scopes/tests/test_api.py @@ -6,7 +6,7 @@ import os, sys sys.path = [os.path.dirname(__file__)] + sys.path import config -from scopes.storage.db.postgres import StorageFactory +from scopes.storage.db import postgres from scopes.storage import concept, folder, message, topic, tracking import transaction @@ -15,7 +15,7 @@ config.dbname = 'testdb' config.dbuser = 'testuser' config.dbpassword = 'secret' config.dbschema = 'testing' -config.storageFactory = StorageFactory(config) +config.StorageFactory = postgres.StorageFactory import pytest from fastapi.testclient import TestClient @@ -26,7 +26,7 @@ from scopes.api.main import app @pytest.fixture(scope='module') def storage(): - #storage = config.storageFactory(config.dbschema) + #storage = config.StorageFactory(config)(config.dbschema) storage = main.storage() storage.dropTable('tracks') tracks = storage.create(tracking.Container) @@ -50,12 +50,14 @@ def test_root(storage): assert response.status_code == 200 assert response.json() == {'Hello': 'World'} -def test_tracking(storage): +def test_tracking(storage, caplog): with TestClient(app) as clt: + caplog.set_level(config.settings.log_level) tracks = clt.get('/tracks').json() assert len(tracks) == 1 tr = tracks[0] tr['timeStamp'] = '...' assert tr == {'uid': 'rec-1', 'head': {'taskId': 't01', 'userName': 'john'}, 'data': {'activity': 'testing'}, 'timeStamp': '...'} + print('\nLogs: ', caplog.text)