Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions scripts/list_outdated_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from pathlib import Path
from typing import Any, NamedTuple

import httpx
import httpx2
from packaging.requirements import Requirement

from script_utils import cli, deps, lock_deps
Expand Down Expand Up @@ -86,13 +86,13 @@ def get_deps_dev() -> list[Requirement]:
return [Requirement(dependency) for dependency in dependencies]


def get_version_from_pypi(package_name: str, client: httpx.Client) -> str:
def get_version_from_pypi(package_name: str, client: httpx2.Client) -> str:
"""Make a call to PyPI to get the version information about `package_name`."""
try:
response = client.get(f"https://pypi.org/pypi/{package_name}/json")
body = response.json()
version = body["info"]["version"]
except (httpx.RequestError, KeyError):
except (httpx2.RequestError, KeyError):
cli.echo_failure(f"Unable to retrieve information for package '{package_name}'")
sys.exit(1)

Expand All @@ -104,7 +104,7 @@ def get_outdated_deps(
) -> list[OutdatedDep]:
"""Determine which packages have updates available outside of pinned ranges."""
outdated: list[OutdatedDep] = []
with httpx.Client(timeout=10) as client:
with httpx2.Client(timeout=10) as client:
for requirement in requirements:
pypi_version = get_version_from_pypi(requirement.name, client)

Expand Down
34 changes: 33 additions & 1 deletion src/hexkit/providers/testing/dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,31 @@ def evaluate(self, resource: dict[str, Any]) -> bool:
return self._fn(value, self._target_value)


class ConjunctionPredicate(Predicate):
"""A Predicate that is satisfied only if all of its sub-predicates are satisfied.

Represents a single branch of a logical operator that contains multiple
conditions, which MongoDB treats as an implicit $and.
"""

def __init__(self, *, conditions: list[Predicate]):
"""Initialize the predicate with the list of sub-predicates to AND together."""
self._conditions = conditions

def __repr__(self) -> str:
return f"{self.__class__.__name__}(conditions={self._conditions})"

def __eq__(self, other) -> bool:
"""Two ConjunctionPredicates are equivalent if all conditions are equivalent."""
return (
isinstance(other, ConjunctionPredicate)
and self._conditions == other._conditions
)

def evaluate(self, resource: dict[str, Any]) -> bool:
return all(condition.evaluate(resource) for condition in self._conditions)


class LogicalPredicate(Predicate):
"""A Predicate that handles MQL logical operators"""

Expand Down Expand Up @@ -215,7 +240,14 @@ def __init__(
if not isinstance(mapping, list) or len(mapping) == 0:
raise MQLError(f"The {op} operator must be used with a non-empty list.")
for condition in mapping:
self._conditions.extend(build_predicates(mapping=condition))
branch_predicates = build_predicates(mapping=condition)
if len(branch_predicates) == 1:
self._conditions.extend(branch_predicates)
else:
# A branch with multiple conditions is an implicit $and
self._conditions.append(
ConjunctionPredicate(conditions=branch_predicates)
)

def __repr__(self) -> str:
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,20 @@ async def test_with_category_dao(
["apples"],
id="DateEqualityWithNe",
),
pytest.param(
{
"count": {"$gte": 40},
"$or": [
{"other_data.sold_last_week": {"$in": [12, 25]}},
{
"other_data.sold_last_week": 55,
"other_data.next_restock": {"$ne": None},
},
],
},
["apples", "celery", "chain"],
id="FieldCombinedWithOrOfInAndNe",
),
],
)
async def test_with_item_dao(
Expand Down
Loading