Skip to content

Commit

Permalink
refactor(otel): ♻️ integrating otel plugin in app
Browse files Browse the repository at this point in the history
  • Loading branch information
miragecentury committed Aug 11, 2024
1 parent 54d3646 commit 432d570
Show file tree
Hide file tree
Showing 8 changed files with 368 additions and 275 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@
"files.encoding": "utf8",
"files.trimTrailingWhitespace": true,
"files.trimTrailingWhitespaceInRegexAndStrings": true,
"files.insertFinalNewline": true
"files.insertFinalNewline": true,
"editor.tabSize": 4
}
3 changes: 2 additions & 1 deletion src/python_factory/core/app/base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
Package for the base application, abstract config classes and related exceptions.
"""

from .application import BaseApplication, GenericBaseApplicationModule
from .application import BaseApplication
from .config_abstract import AppConfigAbstract
from .exceptions import (
ApplicationConfigFactoryException,
ApplicationFactoryException,
BaseApplicationException,
)
from .module import GenericBaseApplicationModule

__all__: list[str] = [
"BaseApplication",
Expand Down
118 changes: 1 addition & 117 deletions src/python_factory/core/app/base/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,9 @@
Provides the abstract class for the application.
"""

from typing import Any, Generic, TypeVar, cast, get_args

import injector

from python_factory.core.utils.importlib import get_path_file_in_package
from python_factory.core.utils.yaml_reader import (
UnableToReadYamlFileError,
YamlFileReader,
)
from typing import cast

from .config_abstract import AppConfigAbstract
from .exceptions import ApplicationConfigFactoryException
from .fastapi_application_abstract import FastAPIAbstract


Expand All @@ -39,110 +30,3 @@ def get_config(self) -> AppConfigAbstract:
Get the application configuration.
"""
return self._config


APP_T = TypeVar("APP_T", bound=BaseApplication) # pylint: disable=invalid-name
CONFIG_T = TypeVar("CONFIG_T", bound=AppConfigAbstract) # pylint: disable=invalid-name


class GenericBaseApplicationModule(Generic[APP_T, CONFIG_T], injector.Module):
"""
Generic application module.
"""

def configure(self, binder: injector.Binder) -> None:
"""
Configure injection bindings for the application
in a generic way.
"""

# Retrieve the concrete application class and configuration class
app_concrete_class, app_config_concrete_class = get_args(
self.__orig_bases__[0] # type: ignore[attr-defined]
)

# Bind the concreate application type and the base application type
# to the same provider.
application_class_provider = injector.ClassProvider(app_concrete_class)
binder.bind(
interface=app_concrete_class,
to=application_class_provider,
scope=injector.SingletonScope,
)
binder.bind(
interface=BaseApplication,
to=application_class_provider,
scope=injector.SingletonScope,
)

# Like the application class, bind the concrete application configuration
# and the application configuration abstract class to the same provider.
application_config_callable_provider = injector.CallableProvider(
self._build_generic_application_config
)
binder.bind(
interface=app_config_concrete_class,
to=application_config_callable_provider,
scope=injector.SingletonScope,
)
binder.bind(
interface=AppConfigAbstract,
to=application_config_callable_provider,
scope=injector.SingletonScope,
)

def _build_generic_application_config(self) -> CONFIG_T | None:
"""
Generic Builder for the application configuration.
Use the concrete application class to build the concrete application
configuration class.
Raises:
ApplicationFactoryException: If the package name is not set
in the concrete application class.
ApplicationFactoryException: If the application configuration file
cannot be read.
ApplicationFactoryException: If the application configuration model
cannot be created.
Returns:
The application configuration
"""

# Retrieve the concrete application class
# and the concrete application configuration class
app_concrete_class, app_config_concrete_class = get_args(
self.__orig_bases__[0] # type: ignore[attr-defined]
)

# Ensure that the package name is set in the concrete application class
if app_concrete_class.PACKAGE_NAME == "":
raise ApplicationConfigFactoryException(
"The package name must be set in the concrete application class."
)

# Read the application configuration file
try:
yaml_file_content: dict[str, Any] = YamlFileReader(
file_path=get_path_file_in_package(
filename="application.yaml",
package=app_concrete_class.PACKAGE_NAME,
),
yaml_base_key="application",
use_environment_injection=True,
).read()
except (FileNotFoundError, ImportError, UnableToReadYamlFileError) as exception:
raise ApplicationConfigFactoryException(
"Unable to read the application configuration file."
) from exception

# Create the application configuration model
try:
config = app_config_concrete_class(**yaml_file_content)
except ValueError as exception:
raise ApplicationConfigFactoryException(
"Unable to create the application configuration model."
) from exception

return config
126 changes: 126 additions & 0 deletions src/python_factory/core/app/base/module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""
Provide a generic module for injection bindings for the application.
"""

from typing import Any, Generic, TypeVar, get_args

import injector

from python_factory.core.plugins.opentelemetry_plugin import OpenTelemetryPluginModule
from python_factory.core.utils.importlib import get_path_file_in_package
from python_factory.core.utils.yaml_reader import (
UnableToReadYamlFileError,
YamlFileReader,
)

from .application import BaseApplication
from .config_abstract import AppConfigAbstract
from .exceptions import ApplicationConfigFactoryException

APP_T = TypeVar("APP_T", bound=BaseApplication) # pylint: disable=invalid-name
CONFIG_T = TypeVar("CONFIG_T", bound=AppConfigAbstract) # pylint: disable=invalid-name


class GenericBaseApplicationModule(Generic[APP_T, CONFIG_T], injector.Module):
"""
Generic application module.
"""

def configure(self, binder: injector.Binder) -> None:
"""
Configure injection bindings for the application
in a generic way.
"""

# Retrieve the concrete application class and configuration class
app_concrete_class, app_config_concrete_class = get_args(
self.__orig_bases__[0] # type: ignore[attr-defined]
)

# Bind the concreate application type and the base application type
# to the same provider.
application_class_provider = injector.ClassProvider(app_concrete_class)
binder.bind(
interface=app_concrete_class,
to=application_class_provider,
scope=injector.SingletonScope,
)
binder.bind(
interface=BaseApplication,
to=application_class_provider,
scope=injector.SingletonScope,
)

# Like the application class, bind the concrete application configuration
# and the application configuration abstract class to the same provider.
application_config_callable_provider = injector.CallableProvider(
self._build_generic_application_config
)
binder.bind(
interface=app_config_concrete_class,
to=application_config_callable_provider,
scope=injector.SingletonScope,
)
binder.bind(
interface=AppConfigAbstract,
to=application_config_callable_provider,
scope=injector.SingletonScope,
)

binder.install(module=OpenTelemetryPluginModule)

def _build_generic_application_config(self) -> CONFIG_T | None:
"""
Generic Builder for the application configuration.
Use the concrete application class to build the concrete application
configuration class.
Raises:
ApplicationFactoryException: If the package name is not set
in the concrete application class.
ApplicationFactoryException: If the application configuration file
cannot be read.
ApplicationFactoryException: If the application configuration model
cannot be created.
Returns:
The application configuration
"""

# Retrieve the concrete application class
# and the concrete application configuration class
app_concrete_class, app_config_concrete_class = get_args(
self.__orig_bases__[0] # type: ignore[attr-defined]
)

# Ensure that the package name is set in the concrete application class
if app_concrete_class.PACKAGE_NAME == "":
raise ApplicationConfigFactoryException(
"The package name must be set in the concrete application class."
)

# Read the application configuration file
try:
yaml_file_content: dict[str, Any] = YamlFileReader(
file_path=get_path_file_in_package(
filename="application.yaml",
package=app_concrete_class.PACKAGE_NAME,
),
yaml_base_key="application",
use_environment_injection=True,
).read()
except (FileNotFoundError, ImportError, UnableToReadYamlFileError) as exception:
raise ApplicationConfigFactoryException(
"Unable to read the application configuration file."
) from exception

# Create the application configuration model
try:
config = app_config_concrete_class(**yaml_file_content)
except ValueError as exception:
raise ApplicationConfigFactoryException(
"Unable to create the application configuration model."
) from exception

return config
14 changes: 14 additions & 0 deletions src/python_factory/core/plugins/opentelemetry_plugin/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""
OpenTelemetry Plugin Module
"""

from .configs import OpenTelemetryConfig
from .exceptions import OpenTelemetryPluginBaseException, OpenTelemetryPluginConfigError
from .providers import OpenTelemetryPluginModule

__all__: list[str] = [
"OpenTelemetryConfig",
"OpenTelemetryPluginBaseException",
"OpenTelemetryPluginConfigError",
"OpenTelemetryPluginModule",
]
Loading

0 comments on commit 432d570

Please sign in to comment.