kubex-core 0.1.0a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,171 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
110
+ .pdm.toml
111
+ .pdm-python
112
+ .pdm-build/
113
+
114
+ uv.lock
115
+ .python-version
116
+
117
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
118
+ __pypackages__/
119
+
120
+ # Celery stuff
121
+ celerybeat-schedule
122
+ celerybeat.pid
123
+
124
+ # SageMath parsed files
125
+ *.sage.py
126
+
127
+ # Environments
128
+ .env
129
+ .venv
130
+ env/
131
+ venv/
132
+ ENV/
133
+ env.bak/
134
+ venv.bak/
135
+
136
+ # Spyder project settings
137
+ .spyderproject
138
+ .spyproject
139
+
140
+ # Rope project settings
141
+ .ropeproject
142
+
143
+ # mkdocs documentation
144
+ /site
145
+
146
+ # mypy
147
+ .mypy_cache/
148
+ .dmypy.json
149
+ dmypy.json
150
+
151
+ # Pyre type checker
152
+ .pyre/
153
+
154
+ # pytype static type analyzer
155
+ .pytype/
156
+
157
+ # Cython debug symbols
158
+ cython_debug/
159
+
160
+ # PyCharm
161
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
162
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
163
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
164
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
165
+ #.idea/
166
+ *.code-workspace
167
+ .vscode/
168
+
169
+ # Ruff
170
+ .ruff_cache/
171
+ scratches/
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: kubex-core
3
+ Version: 0.1.0a1
4
+ Summary: Core model types shared by Kubex and its generated resource packages
5
+ Author-email: Codemageddon <dev@codemageddon.me>
6
+ License: MIT License
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: pydantic<3,>=2.0
18
+ Requires-Dist: pyyaml>=6.0.2
19
+ Description-Content-Type: text/markdown
20
+
21
+ # kubex-core
22
+
23
+ Shared foundation types for [Kubex](https://github.com/codemageddon/kubex) and its generated Kubernetes resource packages (`kubex-k8s-*`).
24
+
25
+ Contains:
26
+
27
+ - `kubex_core.models.base.BaseK8sModel` — Pydantic base with camelCase aliasing.
28
+ - `kubex_core.models.base_entity.BaseEntity` — base class for all K8s resources.
29
+ - `kubex_core.models.interfaces` — marker interfaces (`NamespaceScopedEntity`, `ClusterScopedEntity`, `HasLogs`, `HasStatusSubresource`, `HasScaleSubresource`, `Evictable`).
30
+ - `kubex_core.models.resource_config` — `ResourceConfig` descriptor + `Scope`.
31
+ - `kubex_core.models.metadata` — `ObjectMetadata`, `ListMetadata`, `OwnerReference`.
32
+ - `kubex_core.models.watch_event`, `list_entity`, `partial_object_meta`, `status`, `scale`.
33
+
34
+ This package is a runtime dependency of the `kubex` client and every generated resource package. End users generally install `kubex` + one `kubex-k8s-*` of choice; this package is pulled in transitively.
@@ -0,0 +1,14 @@
1
+ # kubex-core
2
+
3
+ Shared foundation types for [Kubex](https://github.com/codemageddon/kubex) and its generated Kubernetes resource packages (`kubex-k8s-*`).
4
+
5
+ Contains:
6
+
7
+ - `kubex_core.models.base.BaseK8sModel` — Pydantic base with camelCase aliasing.
8
+ - `kubex_core.models.base_entity.BaseEntity` — base class for all K8s resources.
9
+ - `kubex_core.models.interfaces` — marker interfaces (`NamespaceScopedEntity`, `ClusterScopedEntity`, `HasLogs`, `HasStatusSubresource`, `HasScaleSubresource`, `Evictable`).
10
+ - `kubex_core.models.resource_config` — `ResourceConfig` descriptor + `Scope`.
11
+ - `kubex_core.models.metadata` — `ObjectMetadata`, `ListMetadata`, `OwnerReference`.
12
+ - `kubex_core.models.watch_event`, `list_entity`, `partial_object_meta`, `status`, `scale`.
13
+
14
+ This package is a runtime dependency of the `kubex` client and every generated resource package. End users generally install `kubex` + one `kubex-k8s-*` of choice; this package is pulled in transitively.
File without changes
File without changes
@@ -0,0 +1,8 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+ from pydantic.alias_generators import to_camel
5
+
6
+
7
+ class BaseK8sModel(BaseModel):
8
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
@@ -0,0 +1,15 @@
1
+ from typing import ClassVar, Self
2
+
3
+ from kubex_core.models.base import BaseK8sModel
4
+ from kubex_core.models.metadata import ObjectMetadata
5
+ from kubex_core.models.resource_config import ResourceConfig
6
+
7
+
8
+ class BaseEntity(BaseK8sModel):
9
+ """BaseEntity is the common fields for all entities."""
10
+
11
+ __RESOURCE_CONFIG__: ClassVar[ResourceConfig[Self]] = ResourceConfig()
12
+
13
+ api_version: str | None = None
14
+ kind: str | None = None
15
+ metadata: ObjectMetadata
@@ -0,0 +1,19 @@
1
+ from kubex_core.models.base_entity import BaseEntity
2
+
3
+
4
+ class ClusterScopedEntity(BaseEntity): ...
5
+
6
+
7
+ class NamespaceScopedEntity(BaseEntity): ...
8
+
9
+
10
+ class HasStatusSubresource(BaseEntity): ...
11
+
12
+
13
+ class HasScaleSubresource(BaseEntity): ...
14
+
15
+
16
+ class HasLogs(BaseEntity): ...
17
+
18
+
19
+ class Evictable(BaseEntity): ...
@@ -0,0 +1,14 @@
1
+ from typing import Generic
2
+
3
+ from kubex_core.models.base import BaseK8sModel
4
+ from kubex_core.models.metadata import ListMetadata
5
+ from kubex_core.models.typing import ResourceType
6
+
7
+
8
+ class ListEntity(BaseK8sModel, Generic[ResourceType]):
9
+ """ListEntity is the common fields for all list entities."""
10
+
11
+ api_version: str
12
+ kind: str
13
+ metadata: ListMetadata
14
+ items: list[ResourceType]
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime
4
+
5
+ from pydantic import Field
6
+
7
+ from kubex_core.models.base import BaseK8sModel
8
+
9
+
10
+ class OwnerReference(BaseK8sModel):
11
+ """OwnerReference contains enough information to let you identify an owning object."""
12
+
13
+ api_version: str
14
+ kind: str
15
+ name: str
16
+ uid: str
17
+ controller: bool | None = None
18
+ block_owner_deletion: bool | None = None
19
+
20
+
21
+ class ObjectMetadata(BaseK8sModel):
22
+ """CommonMetadata is the common metadata for all Kubernetes API objects."""
23
+
24
+ labels: dict[str, str] | None = None
25
+ annotations: dict[str, str] | None = None
26
+ finalizers: list[str] | None = None
27
+ creation_timestamp: datetime.datetime | None = None
28
+ deletion_timestamp: datetime.datetime | None = None
29
+ deletion_grace_period_seconds: int | None = None
30
+ generation: int | None = None
31
+ resource_version: str | None = None
32
+ uid: str | None = None
33
+ name: str | None = None
34
+ namespace: str | None = None
35
+ generate_name: str | None = None
36
+ owner_references: list[OwnerReference] | None = None
37
+
38
+
39
+ class ListMetadata(BaseK8sModel):
40
+ """ListMeta describes metadata that synthetic resources must have, including lists and various status objects."""
41
+
42
+ continue_: str | None = Field(None, alias="continue")
43
+ remaining_item_count: int | None = None
44
+ resource_version: str | None = None
45
+ self_link: str | None = None
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal
4
+
5
+ from kubex_core.models.base_entity import BaseEntity
6
+ from kubex_core.models.metadata import ObjectMetadata
7
+
8
+
9
+ class PartialObjectMetadata(BaseEntity):
10
+ """PartialObjectMetadata is the common metadata for all Kubernetes API objects."""
11
+
12
+ api_version: Literal["meta.k8s.io/v1"] = "meta.k8s.io/v1"
13
+ kind: Literal["PartialObjectMetadata"] = "PartialObjectMetadata"
14
+ metadata: ObjectMetadata
@@ -0,0 +1,158 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+ from functools import cached_property
5
+ from typing import TYPE_CHECKING, Any, Generic, Literal, Self, Type, cast
6
+
7
+ from pydantic import create_model
8
+
9
+ from kubex_core.models.typing import ResourceType
10
+
11
+ if TYPE_CHECKING:
12
+ from kubex_core.models.list_entity import ListEntity
13
+
14
+
15
+ class Scope(Enum):
16
+ CLUSTER = "cluster"
17
+ NAMESPACE = "namespace"
18
+
19
+
20
+ class ResourceConfig(Generic[ResourceType]):
21
+ """ResourceConfig is the configuration for a resource."""
22
+
23
+ def __init__(
24
+ self,
25
+ version: str | None = None,
26
+ kind: str | None = None,
27
+ plural: str | None = None,
28
+ scope: Scope | None = None,
29
+ group: str | None = None,
30
+ list_model: Type[ListEntity[ResourceType]] | None = None,
31
+ ) -> None:
32
+ self._version = version
33
+ self._kind = kind
34
+ self._plural = plural
35
+ self._scope = scope
36
+ self._group = group
37
+ self._list_model = list_model
38
+
39
+ def __get__(self, instance: Any, owner: Type[ResourceType]) -> Self:
40
+ """Fill in the missing values from the owner."""
41
+ if kind_field := owner.model_fields.get("kind"):
42
+ if kind_field.default is not None:
43
+ self._kind = kind_field.default
44
+ else:
45
+ return self
46
+ if self._version is None or self._group is None:
47
+ if api_version_field := owner.model_fields.get("api_version"):
48
+ if api_version_field.default is not None:
49
+ self._version, self._group = get_version_and_froup_from_api_version(
50
+ api_version_field.default
51
+ )
52
+ else:
53
+ return self
54
+ if self._list_model is None:
55
+ self._list_model = create_list_model(owner, self)
56
+ if self._scope is None:
57
+ self._scope = Scope.NAMESPACE
58
+ if self._plural is None:
59
+ if self._kind is None:
60
+ raise ValueError("kind is not set")
61
+ if self._kind.endswith("y"):
62
+ self._plural = f"{self._kind[:-1].lower()}ies"
63
+ elif self._kind.endswith("s") or self._kind.endswith("x"):
64
+ self._plural = f"{self._kind.lower()}es"
65
+ else:
66
+ self._plural = f"{self._kind.lower()}s"
67
+ return self
68
+
69
+ @property
70
+ def version(self) -> str:
71
+ if self._version is None:
72
+ raise ValueError("version is not set")
73
+ return self._version
74
+
75
+ @property
76
+ def kind(self) -> str:
77
+ if self._kind is None:
78
+ raise ValueError("kind is not set")
79
+ return self._kind
80
+
81
+ @property
82
+ def plural(self) -> str:
83
+ if self._plural is None:
84
+ raise ValueError("plural is not set")
85
+ return self._plural
86
+
87
+ @property
88
+ def scope(self) -> Scope:
89
+ if self._scope is None:
90
+ raise ValueError("scope is not set")
91
+ return self._scope
92
+
93
+ @property
94
+ def group(self) -> str:
95
+ if self._group is None:
96
+ raise ValueError("group is not set")
97
+ return self._group
98
+
99
+ @property
100
+ def list_model(self) -> Type[ListEntity[ResourceType]]:
101
+ if self._list_model is None:
102
+ raise ValueError("list_model is not set")
103
+ return self._list_model
104
+
105
+ def url(self, namespace: str | None = None, name: str | None = None) -> str:
106
+ """url returns the URL for the resource."""
107
+ if self.group and self.group != "core":
108
+ api_version = f"/apis/{self.group}/{self.version}"
109
+ else:
110
+ api_version = f"/api/{self.version}"
111
+
112
+ url: str
113
+ if namespace is None:
114
+ url = f"{api_version}/{self.plural}"
115
+ elif self.scope == Scope.CLUSTER:
116
+ raise ValueError("resource is cluster-scoped but namespace is set")
117
+ else:
118
+ url = f"{api_version}/namespaces/{namespace}/{self.plural}"
119
+
120
+ if name is None:
121
+ return url
122
+ return f"{url}/{name}"
123
+
124
+ @cached_property
125
+ def api_version(self) -> str:
126
+ if self.group and self.group != "core":
127
+ return f"{self.group}/{self.version}"
128
+ return self.version
129
+
130
+
131
+ def create_list_model(
132
+ single_model: Type[ResourceType], resource_config: ResourceConfig[ResourceType]
133
+ ) -> Type[ListEntity[ResourceType]]:
134
+ from kubex_core.models.list_entity import ListEntity
135
+ from kubex_core.models.metadata import ListMetadata
136
+
137
+ kind = f"{resource_config.kind}List"
138
+ list_model = create_model(
139
+ kind,
140
+ api_version=(Literal[resource_config.api_version], resource_config.api_version),
141
+ kind=(Literal[kind], kind),
142
+ metadata=(ListMetadata, ...),
143
+ items=(list[single_model], ...), # type: ignore[valid-type]
144
+ __base__=ListEntity[single_model], # type: ignore[valid-type]
145
+ )
146
+ # pydantic.create_model's return type is `type[BaseModel]`; we know the
147
+ # __base__ narrows it to ListEntity[ResourceType], but mypy cannot prove it.
148
+ return cast("Type[ListEntity[ResourceType]]", list_model)
149
+
150
+
151
+ def get_version_and_froup_from_api_version(api_version: str | None) -> tuple[str, str]:
152
+ """get_version_and_group_from_api_version returns the version and group from the apiVersion."""
153
+ if api_version is None:
154
+ raise ValueError("api_version is not set")
155
+ parts = api_version.split("/")
156
+ if len(parts) == 1:
157
+ return parts[0], "core"
158
+ return parts[1], parts[0]
@@ -0,0 +1,22 @@
1
+ from typing import Literal
2
+
3
+ from .base import BaseK8sModel
4
+ from .base_entity import BaseEntity
5
+ from .metadata import ObjectMetadata
6
+
7
+
8
+ class ScaleSpec(BaseK8sModel):
9
+ replicas: int
10
+
11
+
12
+ class ScaleStatus(BaseK8sModel):
13
+ replicas: int
14
+ selector: str | None = None
15
+
16
+
17
+ class Scale(BaseEntity):
18
+ api_version: Literal["autoscaling/v1"] = "autoscaling/v1"
19
+ kind: Literal["Scale"] = "Scale"
20
+ metadata: ObjectMetadata
21
+ spec: ScaleSpec
22
+ status: dict[str, int] | None = None
@@ -0,0 +1,60 @@
1
+ from typing import Literal
2
+
3
+ from .base import BaseK8sModel
4
+ from .base_entity import BaseEntity
5
+ from .resource_config import ResourceConfig, Scope
6
+
7
+
8
+ class StatusCause(BaseK8sModel):
9
+ """StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered."""
10
+
11
+ message: str
12
+ """A human-readable description of the cause of the error. This field may be presented as-is to a reader."""
13
+ field: str | None = None
14
+ """The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional. Examples: "name" - the field "name" on the current resource "items[0].name" - the field "name" on the first array entry in \"items\"
15
+ """
16
+ reason: str | None = None
17
+ """A machine-readable description of the cause of the error. If this value is empty there is no information available."""
18
+
19
+
20
+ class StatusDetails(BaseK8sModel):
21
+ """tatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined."""
22
+
23
+ causes: list[StatusCause] | None = None
24
+ """The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes."""
25
+ name: str | None = None
26
+ """The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described)."""
27
+ group: str | None = None
28
+ """The group attribute of the resource associated with the status StatusReason."""
29
+ kind: str | None = None
30
+ """The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"""
31
+ retry_after_seconds: int | None = None
32
+ """If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action."""
33
+ uid: str | None = None
34
+ """UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids"""
35
+
36
+
37
+ class Status(BaseEntity):
38
+ """Status is a return value for calls that don't return other objects."""
39
+
40
+ api_version: Literal["meta/v1"] | None = "meta/v1"
41
+ """APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources"""
42
+ kind: Literal["Status"] | None = "Status"
43
+ """Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"""
44
+ code: int = 0
45
+ """Suggested HTTP return code for this status, 0 if not set."""
46
+ details: StatusDetails | None = None
47
+ """Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type."""
48
+ message: str | None = None
49
+ """A human-readable description of the status of this operation."""
50
+ reason: str | None = None
51
+ """A machine-readable description of why this operation is in the "Failure" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it."""
52
+ status: Literal["Success", "Failure"]
53
+ """Status of the operation. One of: "Success" or "Failure". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status"""
54
+
55
+ __RESOURCE_CONFIG__ = ResourceConfig["Status"](
56
+ version="meta/v1",
57
+ kind="Status",
58
+ plural="statuses",
59
+ scope=Scope.CLUSTER,
60
+ )
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, TypeVar
4
+
5
+ if TYPE_CHECKING:
6
+ from kubex_core.models.base_entity import BaseEntity # noqa: F401
7
+
8
+
9
+ ResourceType = TypeVar("ResourceType", bound="BaseEntity")
@@ -0,0 +1,36 @@
1
+ from enum import Enum
2
+ from typing import Any, Generic, Type
3
+
4
+ from .base_entity import BaseEntity
5
+ from .typing import ResourceType
6
+
7
+
8
+ class EventType(str, Enum):
9
+ """EventType is the type of the watch event."""
10
+
11
+ ADDED = "ADDED"
12
+ MODIFIED = "MODIFIED"
13
+ DELETED = "DELETED"
14
+ BOOKMARK = "BOOKMARK"
15
+
16
+
17
+ class Bookmark(BaseEntity):
18
+ """Bookmark is a pointer to a resource in a stream."""
19
+
20
+
21
+ class WatchEvent(Generic[ResourceType]):
22
+ """WatchEvent represents a single event from a watch stream."""
23
+
24
+ def __init__(
25
+ self, resource_type: Type[ResourceType], raw_event: dict[str, Any]
26
+ ) -> None:
27
+ self._resource_type = resource_type
28
+ self.type = EventType(raw_event["type"])
29
+ self.object: ResourceType | Bookmark
30
+ if self.type == EventType.BOOKMARK:
31
+ self.object = Bookmark.model_validate(raw_event["object"])
32
+ else:
33
+ self.object = self._resource_type.model_validate(raw_event["object"])
34
+
35
+ def __repr__(self) -> str:
36
+ return f"WatchEvent(type={self.type}, object={self.object})"
File without changes
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "kubex-core"
3
+ version = "0.1.0-alpha.1"
4
+ description = "Core model types shared by Kubex and its generated resource packages"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = {text = "MIT License"}
8
+ authors = [
9
+ {name = "Codemageddon", email = "dev@codemageddon.me"},
10
+ ]
11
+ dependencies = [
12
+ "pydantic>=2.0,<3",
13
+ "pyyaml>=6.0.2",
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
+ ]
26
+
27
+ [build-system]
28
+ requires = ["hatchling"]
29
+ build-backend = "hatchling.build"
30
+
31
+ [tool.hatch.build.targets.sdist]
32
+ only-include = ["kubex_core"]
33
+
34
+ [tool.hatch.build.targets.wheel]
35
+ packages = ["kubex_core"]