Skip to content

Commit

Permalink
Merge pull request #210 from opsmill/develop
Browse files Browse the repository at this point in the history
Release 1.4
  • Loading branch information
FragmentedPacket authored Jan 3, 2025
2 parents 613ed53 + c067262 commit e33444a
Show file tree
Hide file tree
Showing 64 changed files with 1,675 additions and 1,286 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the chang

<!-- towncrier release notes start -->

## [1.4.0](https://github.com/opsmill/infrahub-sdk-python/tree/v1.4.0) - 2025-01-03

### Changed

- The inclusion of properties for Attribute and Relationships by default has been disabled when querying nodes from Infrahub.
it can be enabled by using the parameter `property` in `client.get|all|filters` method ([#191](https://github.com/opsmill/infrahub-sdk-python/issues/191))
- Fix an issue with python-transform-unit-process failing to run ([#198](https://github.com/opsmill/infrahub-sdk-python/issues/198))
- Add processing of nodes when no variable are passed into the command to run generators ([#176](https://github.com/opsmill/infrahub-sdk-python/issues/176))

## [1.3.0](https://github.com/opsmill/infrahub-sdk-python/tree/v1.3.0) - 2024-12-30

### Added
Expand Down
6 changes: 2 additions & 4 deletions infrahub_sdk/_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import importlib
import sys
from pathlib import Path
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING

from .exceptions import ModuleImportError

Expand All @@ -13,9 +13,7 @@
module_mtime_cache: dict[str, float] = {}


def import_module(
module_path: Path, import_root: Optional[str] = None, relative_path: Optional[str] = None
) -> ModuleType:
def import_module(module_path: Path, import_root: str | None = None, relative_path: str | None = None) -> ModuleType:
"""Imports a python module.
Args:
Expand Down
16 changes: 9 additions & 7 deletions infrahub_sdk/analyzer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from typing import Any, Optional
from __future__ import annotations

from typing import Any

from graphql import (
DocumentNode,
Expand All @@ -19,23 +21,23 @@ class GraphQLQueryVariable(BaseModel):
name: str
type: str
required: bool = False
default_value: Optional[Any] = None
default_value: Any | None = None


class GraphQLOperation(BaseModel):
name: Optional[str] = None
name: str | None = None
operation_type: OperationType


class GraphQLQueryAnalyzer:
def __init__(self, query: str, schema: Optional[GraphQLSchema] = None):
def __init__(self, query: str, schema: GraphQLSchema | None = None):
self.query: str = query
self.schema: Optional[GraphQLSchema] = schema
self.schema: GraphQLSchema | None = schema
self.document: DocumentNode = parse(self.query)
self._fields: Optional[dict] = None
self._fields: dict | None = None

@property
def is_valid(self) -> tuple[bool, Optional[list[GraphQLError]]]:
def is_valid(self) -> tuple[bool, list[GraphQLError] | None]:
if self.schema is None:
return False, [GraphQLError("Schema is not provided")]

Expand Down
25 changes: 14 additions & 11 deletions infrahub_sdk/batch.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
from __future__ import annotations

import asyncio
from collections.abc import AsyncGenerator, Awaitable
from collections.abc import AsyncGenerator, Awaitable, Generator
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass
from typing import Any, Callable, Generator, Optional
from typing import TYPE_CHECKING, Any, Callable

from .node import InfrahubNode, InfrahubNodeSync
if TYPE_CHECKING:
from .node import InfrahubNode, InfrahubNodeSync


@dataclass
class BatchTask:
task: Callable[[Any], Awaitable[Any]]
args: tuple[Any, ...]
kwargs: dict[str, Any]
node: Optional[Any] = None
node: Any | None = None


@dataclass
class BatchTaskSync:
task: Callable[..., Any]
args: tuple[Any, ...]
kwargs: dict[str, Any]
node: Optional[InfrahubNodeSync] = None
node: InfrahubNodeSync | None = None

def execute(self, return_exceptions: bool = False) -> tuple[Optional[InfrahubNodeSync], Any]:
def execute(self, return_exceptions: bool = False) -> tuple[InfrahubNodeSync | None, Any]:
"""Executes the stored task."""
result = None
try:
Expand All @@ -37,7 +40,7 @@ def execute(self, return_exceptions: bool = False) -> tuple[Optional[InfrahubNod

async def execute_batch_task_in_pool(
task: BatchTask, semaphore: asyncio.Semaphore, return_exceptions: bool = False
) -> tuple[Optional[InfrahubNode], Any]:
) -> tuple[InfrahubNode | None, Any]:
async with semaphore:
try:
result = await task.task(*task.args, **task.kwargs)
Expand All @@ -52,7 +55,7 @@ async def execute_batch_task_in_pool(
class InfrahubBatch:
def __init__(
self,
semaphore: Optional[asyncio.Semaphore] = None,
semaphore: asyncio.Semaphore | None = None,
max_concurrent_execution: int = 5,
return_exceptions: bool = False,
):
Expand All @@ -64,7 +67,7 @@ def __init__(
def num_tasks(self) -> int:
return len(self._tasks)

def add(self, *args: Any, task: Callable, node: Optional[Any] = None, **kwargs: Any) -> None:
def add(self, *args: Any, task: Callable, node: Any | None = None, **kwargs: Any) -> None:
self._tasks.append(BatchTask(task=task, node=node, args=args, kwargs=kwargs))

async def execute(self) -> AsyncGenerator:
Expand Down Expand Up @@ -96,10 +99,10 @@ def __init__(self, max_concurrent_execution: int = 5, return_exceptions: bool =
def num_tasks(self) -> int:
return len(self._tasks)

def add(self, *args: Any, task: Callable[..., Any], node: Optional[Any] = None, **kwargs: Any) -> None:
def add(self, *args: Any, task: Callable[..., Any], node: Any | None = None, **kwargs: Any) -> None:
self._tasks.append(BatchTaskSync(task=task, node=node, args=args, kwargs=kwargs))

def execute(self) -> Generator[tuple[Optional[InfrahubNodeSync], Any], None, None]:
def execute(self) -> Generator[tuple[InfrahubNodeSync | None, Any], None, None]:
with ThreadPoolExecutor(max_workers=self.max_concurrent_execution) as executor:
futures = [executor.submit(task.execute, return_exceptions=self.return_exceptions) for task in self._tasks]
for future in futures:
Expand Down
36 changes: 18 additions & 18 deletions infrahub_sdk/branch.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import warnings
from typing import TYPE_CHECKING, Any, Literal, Optional, Union, overload
from typing import TYPE_CHECKING, Any, Literal, overload
from urllib.parse import urlencode

from pydantic import BaseModel
Expand All @@ -17,11 +17,11 @@
class BranchData(BaseModel):
id: str
name: str
description: Optional[str] = None
description: str | None = None
sync_with_git: bool
is_default: bool
has_schema_changes: bool
origin_branch: Optional[str] = None
origin_branch: str | None = None
branched_from: str


Expand Down Expand Up @@ -50,11 +50,11 @@ class InfraHubBranchManagerBase:
@classmethod
def generate_diff_data_url(
cls,
client: Union[InfrahubClient, InfrahubClientSync],
client: InfrahubClient | InfrahubClientSync,
branch_name: str,
branch_only: bool = True,
time_from: Optional[str] = None,
time_to: Optional[str] = None,
time_from: str | None = None,
time_to: str | None = None,
) -> str:
"""Generate the URL for the diff_data function."""
url = f"{client.address}/api/diff/data"
Expand All @@ -80,7 +80,7 @@ async def create(
sync_with_git: bool = True,
description: str = "",
wait_until_completion: Literal[True] = True,
background_execution: Optional[bool] = False,
background_execution: bool | None = False,
) -> BranchData: ...

@overload
Expand All @@ -90,7 +90,7 @@ async def create(
sync_with_git: bool = True,
description: str = "",
wait_until_completion: Literal[False] = False,
background_execution: Optional[bool] = False,
background_execution: bool | None = False,
) -> str: ...

async def create(
Expand All @@ -99,8 +99,8 @@ async def create(
sync_with_git: bool = True,
description: str = "",
wait_until_completion: bool = True,
background_execution: Optional[bool] = False,
) -> Union[BranchData, str]:
background_execution: bool | None = False,
) -> BranchData | str:
if background_execution is not None:
warnings.warn(
"`background_execution` is deprecated, please use `wait_until_completion` instead.",
Expand Down Expand Up @@ -206,8 +206,8 @@ async def diff_data(
self,
branch_name: str,
branch_only: bool = True,
time_from: Optional[str] = None,
time_to: Optional[str] = None,
time_from: str | None = None,
time_to: str | None = None,
) -> dict[Any, Any]:
url = self.generate_diff_data_url(
client=self.client,
Expand Down Expand Up @@ -251,7 +251,7 @@ def create(
sync_with_git: bool = True,
description: str = "",
wait_until_completion: Literal[True] = True,
background_execution: Optional[bool] = False,
background_execution: bool | None = False,
) -> BranchData: ...

@overload
Expand All @@ -261,7 +261,7 @@ def create(
sync_with_git: bool = True,
description: str = "",
wait_until_completion: Literal[False] = False,
background_execution: Optional[bool] = False,
background_execution: bool | None = False,
) -> str: ...

def create(
Expand All @@ -270,8 +270,8 @@ def create(
sync_with_git: bool = True,
description: str = "",
wait_until_completion: bool = True,
background_execution: Optional[bool] = False,
) -> Union[BranchData, str]:
background_execution: bool | None = False,
) -> BranchData | str:
if background_execution is not None:
warnings.warn(
"`background_execution` is deprecated, please use `wait_until_completion` instead.",
Expand Down Expand Up @@ -313,8 +313,8 @@ def diff_data(
self,
branch_name: str,
branch_only: bool = True,
time_from: Optional[str] = None,
time_to: Optional[str] = None,
time_from: str | None = None,
time_to: str | None = None,
) -> dict[Any, Any]:
url = self.generate_diff_data_url(
client=self.client,
Expand Down
55 changes: 14 additions & 41 deletions infrahub_sdk/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@
import os
import warnings
from abc import abstractmethod
from typing import TYPE_CHECKING, Any, Optional
from typing import TYPE_CHECKING, Any

import ujson
from git.repo import Repo
from pydantic import BaseModel, Field

from .exceptions import InfrahubCheckNotFoundError, UninitializedError
from .exceptions import UninitializedError

if TYPE_CHECKING:
from pathlib import Path

from . import InfrahubClient
from .schema.repository import InfrahubCheckDefinitionConfig

INFRAHUB_CHECK_VARIABLE_TO_IMPORT = "INFRAHUB_CHECKS"

Expand All @@ -33,20 +30,20 @@ class InfrahubCheckInitializer(BaseModel):


class InfrahubCheck:
name: Optional[str] = None
name: str | None = None
query: str = ""
timeout: int = 10

def __init__(
self,
branch: Optional[str] = None,
branch: str | None = None,
root_directory: str = "",
output: Optional[str] = None,
initializer: Optional[InfrahubCheckInitializer] = None,
params: Optional[dict] = None,
client: Optional[InfrahubClient] = None,
output: str | None = None,
initializer: InfrahubCheckInitializer | None = None,
params: dict | None = None,
client: InfrahubClient | None = None,
):
self.git: Optional[Repo] = None
self.git: Repo | None = None
self.initializer = initializer or InfrahubCheckInitializer()

self.logs: list[dict[str, Any]] = []
Expand Down Expand Up @@ -82,7 +79,7 @@ def client(self, value: InfrahubClient) -> None:
self._client = value

@classmethod
async def init(cls, client: Optional[InfrahubClient] = None, *args: Any, **kwargs: Any) -> InfrahubCheck:
async def init(cls, client: InfrahubClient | None = None, *args: Any, **kwargs: Any) -> InfrahubCheck:
"""Async init method, If an existing InfrahubClient client hasn't been provided, one will be created automatically."""
warnings.warn(
"InfrahubCheck.init has been deprecated and will be removed in the version in Infrahub SDK 2.0.0",
Expand All @@ -101,7 +98,7 @@ def errors(self) -> list[dict[str, Any]]:
return [log for log in self.logs if log["level"] == "ERROR"]

def _write_log_entry(
self, message: str, level: str, object_id: Optional[str] = None, object_type: Optional[str] = None
self, message: str, level: str, object_id: str | None = None, object_type: str | None = None
) -> None:
log_message = {"level": level, "message": message, "branch": self.branch_name}
if object_id:
Expand All @@ -113,10 +110,10 @@ def _write_log_entry(
if self.output == "stdout":
print(ujson.dumps(log_message))

def log_error(self, message: str, object_id: Optional[str] = None, object_type: Optional[str] = None) -> None:
def log_error(self, message: str, object_id: str | None = None, object_type: str | None = None) -> None:
self._write_log_entry(message=message, level="ERROR", object_id=object_id, object_type=object_type)

def log_info(self, message: str, object_id: Optional[str] = None, object_type: Optional[str] = None) -> None:
def log_info(self, message: str, object_id: str | None = None, object_type: str | None = None) -> None:
self._write_log_entry(message=message, level="INFO", object_id=object_id, object_type=object_type)

@property
Expand Down Expand Up @@ -155,7 +152,7 @@ async def collect_data(self) -> dict:

return await self.client.query_gql_query(name=self.query, branch_name=self.branch_name, variables=self.params)

async def run(self, data: Optional[dict] = None) -> bool:
async def run(self, data: dict | None = None) -> bool:
"""Execute the check after collecting the data from the GraphQL query.
The result of the check is determined based on the presence or not of ERROR log messages."""

Expand All @@ -176,27 +173,3 @@ async def run(self, data: Optional[dict] = None) -> bool:
self.log_info("Check succesfully completed")

return self.passed


def get_check_class_instance(
check_config: InfrahubCheckDefinitionConfig, search_path: Optional[Path] = None
) -> InfrahubCheck:
if check_config.file_path.is_absolute() or search_path is None:
search_location = check_config.file_path
else:
search_location = search_path / check_config.file_path

try:
spec = importlib.util.spec_from_file_location(check_config.class_name, search_location)
module = importlib.util.module_from_spec(spec) # type: ignore[arg-type]
spec.loader.exec_module(module) # type: ignore[union-attr]

# Get the specified class from the module
check_class = getattr(module, check_config.class_name)

# Create an instance of the class
check_instance = check_class()
except (FileNotFoundError, AttributeError) as exc:
raise InfrahubCheckNotFoundError(name=check_config.name) from exc

return check_instance
Loading

0 comments on commit e33444a

Please sign in to comment.