12 |
404
13 |
14 | Sorry, we couldn't find the page you're looking for.
15 |
16 |
17 | The page you requested might have been moved or doesn't exist.
18 |
19 |
20 | Go back to the homepage
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/edgy/contrib/admin/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/edgy/contrib/admin/utils/__init__.py
--------------------------------------------------------------------------------
/edgy/contrib/admin/utils/messages.py:
--------------------------------------------------------------------------------
1 | from typing import cast
2 |
3 | from lilya.context import session
4 |
5 |
6 | def add_message(level: str, message: str) -> None:
7 | """
8 | Stores a message in the session for rendering in the next request.
9 | Level can be: success, info, warning, error.
10 | """
11 | if not hasattr(session, "messages"):
12 | session.messages = []
13 | cast(list, session.messages).append({"level": level, "text": message})
14 |
15 |
16 | def get_messages(peek: bool = False) -> list:
17 | """
18 | Retrieves and clears messages from the session.
19 | """
20 | if not hasattr(session, "messages"):
21 | return []
22 | messages = cast(list, session.messages)
23 | if peek:
24 | return messages
25 | _messages = messages.copy()
26 | messages.clear()
27 | return _messages
28 |
--------------------------------------------------------------------------------
/edgy/contrib/autoreflection/__init__.py:
--------------------------------------------------------------------------------
1 | from .models import AutoReflectModel
2 |
3 | __all__ = ["AutoReflectModel"]
4 |
--------------------------------------------------------------------------------
/edgy/contrib/autoreflection/models.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, Any, ClassVar
2 |
3 | import edgy
4 |
5 | from .metaclasses import AutoReflectionMeta, AutoReflectionMetaInfo
6 |
7 | if TYPE_CHECKING:
8 | from edgy.core.db.models.types import BaseModelType
9 |
10 |
11 | class AutoReflectModel(edgy.ReflectModel, metaclass=AutoReflectionMeta):
12 | meta: ClassVar[AutoReflectionMetaInfo]
13 |
14 | @classmethod
15 | def real_add_to_registry(cls, **kwargs: Any) -> type["BaseModelType"]:
16 | if isinstance(cls.meta, AutoReflectionMetaInfo):
17 | kwargs.setdefault("registry_type_name", "pattern_models")
18 | return super().real_add_to_registry(**kwargs)
19 |
--------------------------------------------------------------------------------
/edgy/contrib/contenttypes/__init__.py:
--------------------------------------------------------------------------------
1 | from .fields import ContentTypeField
2 | from .models import ContentType
3 |
4 | __all__ = ["ContentTypeField", "ContentType"]
5 |
--------------------------------------------------------------------------------
/edgy/contrib/contenttypes/metaclasses.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 |
3 | from edgy.core.db.models.metaclasses import (
4 | BaseModelMeta,
5 | )
6 |
7 |
8 | class ContentTypeMeta(BaseModelMeta):
9 | def __new__(
10 | cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any], **kwargs: Any
11 | ) -> type:
12 | new_model = super().__new__(cls, name, bases, attrs, **kwargs)
13 | if new_model.no_constraint:
14 | new_model.__require_model_based_deletion__ = True
15 | return new_model
16 |
--------------------------------------------------------------------------------
/edgy/contrib/multi_tenancy/__init__.py:
--------------------------------------------------------------------------------
1 | from .base import TenantModel
2 | from .registry import TenantRegistry
3 | from .settings import TenancySettings
4 |
5 | __all__ = ["TenantModel", "TenantRegistry", "TenancySettings"]
6 |
--------------------------------------------------------------------------------
/edgy/contrib/multi_tenancy/registry.py:
--------------------------------------------------------------------------------
1 | from edgy.core.connection.registry import Registry as TenantRegistry
2 |
3 | __all__ = ["TenantRegistry"]
4 |
--------------------------------------------------------------------------------
/edgy/contrib/multi_tenancy/settings.py:
--------------------------------------------------------------------------------
1 | import os
2 | from functools import cached_property
3 | from typing import Any
4 |
5 | from pydantic_settings import SettingsConfigDict
6 |
7 | from edgy.conf.global_settings import EdgySettings
8 |
9 |
10 | class TenancySettings(EdgySettings):
11 | """
12 | BaseSettings used for the contrib of Edgy tenancy
13 | """
14 |
15 | model_config = SettingsConfigDict(extra="allow", ignored_types=(cached_property,))
16 | auto_create_schema: bool = True
17 | auto_drop_schema: bool = False
18 | tenant_schema_default: str = "public"
19 | tenant_model: str | None = None
20 | domain: Any = os.getenv("DOMAIN")
21 | domain_name: str = "localhost"
22 | auth_user_model: str | None = None
23 |
--------------------------------------------------------------------------------
/edgy/contrib/multi_tenancy/utils.py:
--------------------------------------------------------------------------------
1 | from edgy.core.tenancy.utils import create_schema, create_tables
2 |
3 | __all__ = [
4 | "create_tables",
5 | "create_schema",
6 | ]
7 |
--------------------------------------------------------------------------------
/edgy/contrib/pagination/__init__.py:
--------------------------------------------------------------------------------
1 | from .base import BasePage, BasePaginator, Page, Paginator
2 | from .cursor import CursorPage, CursorPaginator
3 |
4 | __all__ = ["BasePage", "BasePaginator", "Page", "Paginator", "CursorPage", "CursorPaginator"]
5 |
--------------------------------------------------------------------------------
/edgy/contrib/permissions/__init__.py:
--------------------------------------------------------------------------------
1 | from .managers import PermissionManager
2 | from .models import BasePermission
3 |
4 | __all__ = ["PermissionManager", "BasePermission"]
5 |
--------------------------------------------------------------------------------
/edgy/contrib/permissions/models.py:
--------------------------------------------------------------------------------
1 | from typing import ClassVar
2 |
3 | import edgy
4 |
5 | from .managers import PermissionManager
6 |
7 |
8 | class BasePermission(edgy.Model):
9 | users_field_group: ClassVar[str] = "users"
10 | name: str = edgy.fields.CharField(max_length=100, null=False)
11 | description: str | None = edgy.fields.ComputedField( # type: ignore
12 | getter="get_description",
13 | setter="set_description",
14 | fallback_getter=lambda field, instance, owner: instance.name,
15 | )
16 |
17 | query = PermissionManager()
18 |
19 | class Meta:
20 | abstract = True
21 |
--------------------------------------------------------------------------------
/edgy/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/edgy/core/__init__.py
--------------------------------------------------------------------------------
/edgy/core/connection/__init__.py:
--------------------------------------------------------------------------------
1 | from .database import Database, DatabaseURL
2 | from .registry import Registry
3 |
4 | __all__ = ["Database", "DatabaseURL", "Registry"]
5 |
--------------------------------------------------------------------------------
/edgy/core/connection/database.py:
--------------------------------------------------------------------------------
1 | from databasez import Database, DatabaseURL
2 |
3 | __all__ = ["Database", "DatabaseURL"]
4 |
--------------------------------------------------------------------------------
/edgy/core/datastructures.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 |
3 | from pydantic import BaseModel
4 |
5 |
6 | class HashableBaseModel(BaseModel):
7 | """
8 | Pydantic BaseModel by default doesn't handle with hashable types the same way
9 | a python object would and therefore there are types that are mutable (list, set)
10 | not hashable and those need to be handled properly.
11 |
12 | HashableBaseModel handles those corner cases.
13 | """
14 |
15 | __slots__ = ["__weakref__"]
16 |
17 | def __hash__(self) -> Any:
18 | values: Any = {}
19 | for key, value in self.__dict__.items():
20 | values[key] = None
21 | if isinstance(value, list | set):
22 | values[key] = tuple(value)
23 | else:
24 | values[key] = value
25 | return hash((type(self),) + tuple(values))
26 |
27 |
28 | class ArbitraryHashableBaseModel(HashableBaseModel):
29 | """
30 | Same as HashableBaseModel but allowing arbitrary values
31 | """
32 |
33 | class Config:
34 | extra = "allow"
35 | arbitrary_types_allowed = True
36 |
--------------------------------------------------------------------------------
/edgy/core/db/__init__.py:
--------------------------------------------------------------------------------
1 | from .context_vars import set_schema, set_tenant, with_schema, with_tenant
2 |
3 | __all__ = ["set_tenant", "with_tenant", "with_schema", "set_schema"]
4 |
--------------------------------------------------------------------------------
/edgy/core/db/constants.py:
--------------------------------------------------------------------------------
1 | CASCADE = "CASCADE"
2 | RESTRICT = "RESTRICT"
3 | DO_NOTHING = "DO NOTHING"
4 | SET_NULL = "SET NULL"
5 | SET_DEFAULT = "SET DEFAULT"
6 | PROTECT = "PROTECT"
7 |
8 |
9 | class OLD_M2M_NAMING: ...
10 |
11 |
12 | class NEW_M2M_NAMING: ...
13 |
14 |
15 | class ConditionalRedirect(dict): ...
16 |
17 |
18 | __all__ = [
19 | "CASCADE",
20 | "RESTRICT",
21 | "DO_NOTHING",
22 | "SET_NULL",
23 | "SET_DEFAULT",
24 | "PROTECT",
25 | "ConditionalRedirect",
26 | ]
27 |
--------------------------------------------------------------------------------
/edgy/core/db/fields/_internal.py:
--------------------------------------------------------------------------------
1 | import ipaddress
2 | from typing import Any
3 |
4 | import sqlalchemy
5 |
6 |
7 | class IPAddress(sqlalchemy.TypeDecorator):
8 | impl: Any = sqlalchemy.String
9 | cache_ok: bool = True
10 |
11 | def load_dialect_impl(self, dialect: Any) -> Any:
12 | if dialect.name not in {"postgres", "postgresql"}:
13 | return dialect.type_descriptor(sqlalchemy.String(length=45))
14 | return dialect.type_descriptor(sqlalchemy.dialects.postgresql.INET())
15 |
16 | def process_bind_param(self, value: Any, dialect: Any) -> Any:
17 | if value is None:
18 | return value
19 | return str(value)
20 |
21 | def process_result_value(self, value: Any, dialect: Any) -> Any:
22 | if value is None:
23 | return value
24 | if not isinstance(value, ipaddress.IPv4Address | ipaddress.IPv6Address):
25 | value = ipaddress.ip_address(value)
26 | return value
27 |
--------------------------------------------------------------------------------
/edgy/core/db/fields/one_to_one_keys.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, Any
4 |
5 | from edgy.core.db.fields.foreign_keys import ForeignKey
6 | from edgy.core.terminal import Print
7 |
8 | if TYPE_CHECKING:
9 | from edgy.core.db.fields.types import BaseFieldType
10 | from edgy.core.db.models.types import BaseModelType
11 |
12 |
13 | terminal = Print()
14 |
15 |
16 | class OneToOneField(ForeignKey):
17 | """
18 | Representation of a one to one field.
19 | """
20 |
21 | def __new__(
22 | cls,
23 | to: type[BaseModelType] | str,
24 | **kwargs: Any,
25 | ) -> BaseFieldType:
26 | for argument in ["index", "unique"]:
27 | if argument in kwargs:
28 | terminal.write_warning(f"Declaring {argument} on a OneToOneField has no effect.")
29 | kwargs["unique"] = True
30 |
31 | return super().__new__(cls, to=to, **kwargs)
32 |
33 |
34 | OneToOne = OneToOneField
35 |
--------------------------------------------------------------------------------
/edgy/core/db/fields/place_holder_field.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 |
3 | from edgy.core.db.fields.factories import FieldFactory
4 | from edgy.core.db.fields.types import BaseFieldType
5 |
6 |
7 | class PlaceholderField(FieldFactory):
8 | """Placeholder field, without db column"""
9 |
10 | def __new__(
11 | cls,
12 | *,
13 | pydantic_field_type: Any = Any,
14 | **kwargs: Any,
15 | ) -> BaseFieldType:
16 | kwargs.setdefault("exclude", True)
17 | return super().__new__(cls, pydantic_field_type=pydantic_field_type, **kwargs)
18 |
19 | def clean(
20 | self,
21 | name: str,
22 | value: Any,
23 | for_query: bool = False,
24 | ) -> dict[str, Any]:
25 | return {}
26 |
27 | @classmethod
28 | def get_pydantic_type(cls, kwargs: dict[str, Any]) -> Any:
29 | """Returns the type for pydantic"""
30 | return kwargs.pop("pydantic_field_type")
31 |
--------------------------------------------------------------------------------
/edgy/core/db/models/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from monkay import Monkay
4 |
5 | if TYPE_CHECKING:
6 | from .managers import Manager, RedirectManager
7 | from .model import Model, ReflectModel, StrictModel
8 | from .model_reference import ModelRef
9 |
10 | __all__ = ["Model", "StrictModel", "ModelRef", "ReflectModel", "Manager", "RedirectManager"]
11 |
12 | Monkay(
13 | globals(),
14 | lazy_imports={
15 | "Model": ".model.Model",
16 | "ReflectModel": ".model.ReflectModel",
17 | "StrictModel": ".model.StrictModel",
18 | "ModelRef": ".model_reference.ModelRef",
19 | "Manager": ".managers.Manager",
20 | "RedirectManager": ".managers.RedirectManager",
21 | },
22 | )
23 |
--------------------------------------------------------------------------------
/edgy/core/db/models/mixins/__init__.py:
--------------------------------------------------------------------------------
1 | from .db import DatabaseMixin
2 | from .generics import DeclarativeMixin
3 | from .reflection import ReflectedModelMixin
4 | from .row import ModelRowMixin
5 |
6 | __all__ = ["DeclarativeMixin", "ModelRowMixin", "ReflectedModelMixin", "DatabaseMixin"]
7 |
--------------------------------------------------------------------------------
/edgy/core/db/models/model_reference.py:
--------------------------------------------------------------------------------
1 | from typing import ClassVar
2 |
3 | from pydantic import BaseModel
4 |
5 |
6 | class ModelRef(BaseModel):
7 | __related_name__: ClassVar[str]
8 |
--------------------------------------------------------------------------------
/edgy/core/db/querysets/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from monkay import Monkay
4 |
5 | if TYPE_CHECKING:
6 | from .base import QuerySet
7 | from .clauses import Q, and_, not_, or_
8 | from .prefetch import Prefetch
9 |
10 | __all__ = ["QuerySet", "Q", "and_", "not_", "or_", "Prefetch"]
11 |
12 | Monkay(
13 | globals(),
14 | lazy_imports={
15 | "QuerySet": ".base.QuerySet",
16 | "Q": ".clauses.Q",
17 | "and_": ".clauses.and_",
18 | "not_": ".clauses.not_",
19 | "or_": ".clauses.or_",
20 | "Prefetch": ".prefetch.Prefetch",
21 | },
22 | )
23 |
--------------------------------------------------------------------------------
/edgy/core/db/relationships/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/edgy/core/db/relationships/__init__.py
--------------------------------------------------------------------------------
/edgy/core/files/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from monkay import Monkay
4 |
5 | if TYPE_CHECKING:
6 | from .base import ContentFile, FieldFile, File, ImageFieldFile
7 | from .storage import Storage, storages
8 | Monkay(
9 | globals(),
10 | lazy_imports={
11 | "Storage": ".base.Storage",
12 | "storages": ".storage.storages",
13 | "ContentFile": ".base.ContentFile",
14 | "File": ".base.File",
15 | "FieldFile": ".base.FieldFile",
16 | "ImageFieldFile": ".base.ImageFieldFile",
17 | },
18 | uncached_imports={"storages"},
19 | )
20 |
21 | __all__ = ["File", "ContentFile", "FieldFile", "ImageFieldFile", "Storage", "storages"]
22 |
--------------------------------------------------------------------------------
/edgy/core/files/storage/__init__.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from functools import lru_cache
4 | from typing import TYPE_CHECKING
5 |
6 | from monkay import Monkay
7 |
8 | from .handler import StorageHandler
9 |
10 | if TYPE_CHECKING:
11 | from edgy import EdgySettings, Instance
12 |
13 | from .base import Storage
14 | storages: StorageHandler
15 |
16 |
17 | _fallback_storage = StorageHandler()
18 |
19 |
20 | @lru_cache
21 | def _get_monkay() -> Monkay[Instance, EdgySettings]:
22 | from edgy import monkay
23 |
24 | return monkay
25 |
26 |
27 | def _get_storages() -> StorageHandler:
28 | instance = _get_monkay().instance
29 | return _fallback_storage if instance is None else instance.storages
30 |
31 |
32 | Monkay(
33 | globals(),
34 | lazy_imports={
35 | "Storage": ".base.Storage",
36 | "storages": _get_storages,
37 | },
38 | uncached_imports={"storages"},
39 | )
40 |
41 | __all__ = ["Storage", "StorageHandler", "storages"]
42 |
--------------------------------------------------------------------------------
/edgy/core/marshalls/__init__.py:
--------------------------------------------------------------------------------
1 | from .base import Marshall
2 | from .config import ConfigMarshall
3 |
4 | __all__ = ["ConfigMarshall", "Marshall"]
5 |
--------------------------------------------------------------------------------
/edgy/core/marshalls/config.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, TypedDict
4 |
5 | if TYPE_CHECKING:
6 | from edgy.core.db.models import Model
7 |
8 |
9 | class ConfigMarshall(TypedDict, total=False):
10 | """A TypedDict for configuring Marshall behaviour."""
11 |
12 | model: type[Model] | str
13 | """The model from there the marshall will read from."""
14 |
15 | fields: list[str] | None = None
16 | """A list of fields to be serialized"""
17 |
18 | exclude: list[str] | None = None
19 | """A list of fields to be excluded from the serialization."""
20 |
--------------------------------------------------------------------------------
/edgy/core/tenancy/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/edgy/core/tenancy/__init__.py
--------------------------------------------------------------------------------
/edgy/core/terminal/__init__.py:
--------------------------------------------------------------------------------
1 | from .base import OutputColour
2 | from .print import Print
3 | from .terminal import Terminal
4 |
5 | __all__ = ["OutputColour", "Print", "Terminal"]
6 |
--------------------------------------------------------------------------------
/edgy/core/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/edgy/core/utils/__init__.py
--------------------------------------------------------------------------------
/edgy/protocols/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/edgy/protocols/__init__.py
--------------------------------------------------------------------------------
/edgy/protocols/many_relationship.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, Any, Optional, Protocol, runtime_checkable
2 |
3 | if TYPE_CHECKING: # pragma: nocover
4 | from edgy.core.db.models.types import BaseModelType
5 |
6 |
7 | @runtime_checkable
8 | class ManyRelationProtocol(Protocol):
9 | instance: "BaseModelType"
10 |
11 | """Defines the what needs to be implemented when using the ManyRelationProtocol"""
12 |
13 | async def save_related(self) -> None: ...
14 |
15 | async def create(self, *args: Any, **kwargs: Any) -> Optional["BaseModelType"]: ...
16 |
17 | async def add(self, child: "BaseModelType") -> Optional["BaseModelType"]: ...
18 |
19 | def stage(self, *children: "BaseModelType") -> None:
20 | """Lazy add children"""
21 |
22 | async def remove(self, child: Optional["BaseModelType"] = None) -> None: ...
23 |
--------------------------------------------------------------------------------
/edgy/protocols/transaction_call.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING, Any, Protocol
4 |
5 | if TYPE_CHECKING:
6 | from databasez.core.transaction import Transaction
7 |
8 |
9 | class TransactionCallProtocol(Protocol):
10 | def __call__(instance: Any, *, force_rollback: bool = False, **kwargs: Any) -> Transaction: ...
11 |
--------------------------------------------------------------------------------
/edgy/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/edgy/py.typed
--------------------------------------------------------------------------------
/edgy/testclient.py:
--------------------------------------------------------------------------------
1 | from edgy.testing.client import DatabaseTestClient
2 |
3 | __all__ = ["DatabaseTestClient"]
4 |
--------------------------------------------------------------------------------
/edgy/testing/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from monkay import Monkay
4 |
5 | if TYPE_CHECKING:
6 | from .client import DatabaseTestClient
7 | from .factory import (
8 | FactoryField,
9 | ListSubFactory,
10 | ModelFactory,
11 | ModelFactoryContext,
12 | SubFactory,
13 | )
14 |
15 | __all__ = [
16 | "DatabaseTestClient",
17 | "ModelFactory",
18 | "SubFactory",
19 | "ListSubFactory",
20 | "FactoryField",
21 | "ModelFactoryContext",
22 | ]
23 |
24 |
25 | Monkay(
26 | globals(),
27 | lazy_imports={
28 | "ModelFactory": ".factory.ModelFactory",
29 | "ModelFactoryContext": ".factory.ModelFactoryContext",
30 | "SubFactory": ".factory.SubFactory",
31 | "ListSubFactory": ".factory.ListSubFactory",
32 | "FactoryField": ".factory.FactoryField",
33 | "DatabaseTestClient": ".client.DatabaseTestClient",
34 | },
35 | )
36 | del Monkay
37 |
--------------------------------------------------------------------------------
/edgy/testing/exceptions.py:
--------------------------------------------------------------------------------
1 | from edgy.exceptions import EdgyException
2 |
3 |
4 | class InvalidModelError(EdgyException): ...
5 |
6 |
7 | class ExcludeValue(BaseException): ...
8 |
--------------------------------------------------------------------------------
/edgy/testing/factory/__init__.py:
--------------------------------------------------------------------------------
1 | from .base import ModelFactory
2 | from .fields import FactoryField
3 | from .subfactory import ListSubFactory, SubFactory
4 | from .types import ModelFactoryContext
5 |
6 | __all__ = ["ModelFactory", "FactoryField", "ListSubFactory", "SubFactory", "ModelFactoryContext"]
7 |
--------------------------------------------------------------------------------
/edgy/testing/factory/context_vars.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from contextvars import ContextVar
4 | from typing import TYPE_CHECKING
5 |
6 | if TYPE_CHECKING:
7 | from .types import ModelFactoryContext
8 |
9 | model_factory_context: ContextVar[ModelFactoryContext] = ContextVar("model_factory_context")
10 |
--------------------------------------------------------------------------------
/edgy/testing/factory/types.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from collections.abc import Callable
4 | from typing import TYPE_CHECKING, Any, Protocol, TypeAlias, TypedDict, Union
5 |
6 | if TYPE_CHECKING:
7 | from faker import Faker
8 |
9 | from edgy.core.db.fields.types import BaseFieldType
10 |
11 | from .fields import FactoryField
12 |
13 |
14 | class _ModelFactoryContext(TypedDict):
15 | faker: Faker
16 | exclude_autoincrement: bool
17 | depth: int
18 | callcounts: dict[int, int]
19 |
20 |
21 | if TYPE_CHECKING:
22 |
23 | class ModelFactoryContext(Faker, _ModelFactoryContext, Protocol):
24 | pass
25 | else:
26 | ModelFactoryContext = _ModelFactoryContext
27 |
28 |
29 | FactoryParameterCallback = Callable[
30 | [
31 | "FactoryField",
32 | ModelFactoryContext,
33 | str,
34 | ],
35 | Any,
36 | ]
37 | FactoryParameters: TypeAlias = dict[str, Any | FactoryParameterCallback]
38 | FactoryCallback: TypeAlias = Callable[["FactoryField", ModelFactoryContext, dict[str, Any]], Any]
39 | FieldFactoryCallback: TypeAlias = str | FactoryCallback
40 | FactoryFieldType: TypeAlias = Union[str, "BaseFieldType", type["BaseFieldType"]]
41 |
--------------------------------------------------------------------------------
/edgy/types.py:
--------------------------------------------------------------------------------
1 | from pydantic_core import PydanticUndefined
2 |
3 | Undefined = PydanticUndefined
4 |
--------------------------------------------------------------------------------
/edgy/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/edgy/utils/__init__.py
--------------------------------------------------------------------------------
/edgy/utils/compat.py:
--------------------------------------------------------------------------------
1 | from inspect import isclass
2 | from typing import Any, get_origin
3 |
4 |
5 | def is_class_and_subclass(value: Any, _type: Any) -> bool:
6 | """
7 | Checks if a `value` is of type class and subclass.
8 | by checking the origin of the value against the type being
9 | verified.
10 | """
11 | original = get_origin(value)
12 | if not original and not isclass(value):
13 | return False
14 |
15 | try:
16 | if original:
17 | return original and issubclass(original, _type)
18 | return issubclass(value, _type)
19 | except TypeError:
20 | return False
21 |
--------------------------------------------------------------------------------
/scripts/clean:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | if [ -d 'dist' ] ; then
4 | rm -r dist
5 | fi
6 | if [ -d 'site' ] ; then
7 | rm -r site
8 | fi
9 | if [ -d 'htmlcov' ] ; then
10 | rm -r htmlcov
11 | fi
12 | if [ -d 'edgy.egg-info' ] ; then
13 | rm -r edgy.egg-info
14 | fi
15 | if [ -d '.hypothesis' ] ; then
16 | rm -r .hypothesis
17 | fi
18 | if [ -d '.mypy_cache' ] ; then
19 | rm -r .mypy_cache
20 | fi
21 | if [ -d '.pytest_cache' ] ; then
22 | rm -r .pytest_cache
23 | fi
24 | if [ -d '.ruff_cache' ] ; then
25 | rm -r .ruff_cache
26 | fi
27 |
28 | find edgy -type f -name "*.py[co]" -delete
29 | find edgy -type d -name __pycache__ -delete
30 |
--------------------------------------------------------------------------------
/scripts/install:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # Use the Python executable provided from the `-p` option, or a default.
4 | [ "$1" = "-p" ] && PYTHON=$2 || PYTHON="python3"
5 |
6 | VENV="venv"
7 |
8 | set -x
9 |
10 | if [ "$VIRTUAL_ENV" != '' ]; then
11 | PIP="$VIRTUAL_ENV/bin/pip"
12 | elif [ -z "$GITHUB_ACTIONS" ]; then
13 | "$PYTHON" -m venv "$VENV"
14 | PIP="$VENV/bin/pip"
15 | else
16 | PIP="pip"
17 | fi
18 |
19 | "$PIP" install -U pip
20 | "$PIP" install -e .[all,testing,test,docs]
21 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/tests/__init__.py
--------------------------------------------------------------------------------
/tests/clauses/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/tests/clauses/__init__.py
--------------------------------------------------------------------------------
/tests/cli/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/tests/cli/__init__.py
--------------------------------------------------------------------------------
/tests/cli/conftest.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dymmond/edgy/39c639967eb016f87674bc2ee728fb1e1f3a9eee/tests/cli/conftest.py
--------------------------------------------------------------------------------
/tests/cli/custom_multidb/README:
--------------------------------------------------------------------------------
1 | Custom template
2 |
--------------------------------------------------------------------------------
/tests/cli/custom_multidb/alembic.ini.mako:
--------------------------------------------------------------------------------
1 | # A custom generic database configuration.
2 |
3 | [alembic]
4 | # template used to generate migration files
5 | # file_template = %%(rev)s_%%(slug)s
6 |
7 | # set to 'true' to run the environment during
8 | # the 'revision' command, regardless of autogenerate
9 | # revision_environment = false
10 |
11 |
12 | # Logging configuration
13 | [loggers]
14 | keys = root,sqlalchemy,alembic,saffier
15 |
16 | [handlers]
17 | keys = console
18 |
19 | [formatters]
20 | keys = generic
21 |
22 | [logger_root]
23 | level = WARN
24 | handlers = console
25 | qualname =
26 |
27 | [logger_sqlalchemy]
28 | level = WARN
29 | handlers =
30 | qualname = sqlalchemy.engine
31 |
32 | [logger_alembic]
33 | level = INFO
34 | handlers =
35 | qualname = alembic
36 |
37 | [logger_saffier]
38 | level = INFO
39 | handlers =
40 | qualname = saffier
41 |
42 | [handler_console]
43 | class = StreamHandler
44 | args = (sys.stderr,)
45 | level = NOTSET
46 | formatter = generic
47 |
48 | [formatter_generic]
49 | format = %(levelname)-5.5s [%(name)s] %(message)s
50 | datefmt = %H:%M:%S
51 |
--------------------------------------------------------------------------------
/tests/cli/custom_multidb/script.py.mako:
--------------------------------------------------------------------------------
1 | # Custom mako template
2 | """${message}
3 |
4 | Revision ID: ${up_revision}
5 | Revises: ${down_revision | comma,n}
6 | Create Date: ${create_date}
7 |
8 | """
9 | from alembic import op
10 | import sqlalchemy as sa
11 | ${imports if imports else ""}
12 |
13 | # revision identifiers, used by Alembic.
14 | revision = ${repr(up_revision)}
15 | down_revision = ${repr(down_revision)}
16 | branch_labels = ${repr(branch_labels)}
17 | depends_on = ${repr(depends_on)}
18 |
19 | def upgrade(edgy_dbname: str = "") -> None:
20 | globals()[f"upgrade_{edgy_dbname}"]()
21 |
22 |
23 | def downgrade(edgy_dbname: str = "") -> None:
24 | globals()[f"downgrade_{edgy_dbname}"]()
25 |
26 |
27 | <%
28 | from edgy import monkay
29 | db_names = monkay.settings.migrate_databases
30 | %>
31 |
32 | ## generate an "upgrade_