extended-data-types 6.2.1__tar.gz → 6.2.2__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.
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/CHANGELOG.md +7 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/PKG-INFO +5 -10
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/README.md +2 -2
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/pyproject.toml +4 -15
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/__init__.py +1 -17
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/map_data_type.py +3 -1
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/splitter_utils.py +10 -4
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/stack_utils.py +2 -1
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/type_utils.py +5 -4
- extended_data_types-6.2.1/src/extended_data_types/development_integration/__init__.py +0 -6
- extended_data_types-6.2.1/src/extended_data_types/development_integration/integration.py +0 -269
- extended_data_types-6.2.1/src/extended_data_types/ecosystem_status/__init__.py +0 -6
- extended_data_types-6.2.1/src/extended_data_types/ecosystem_status/monitor.py +0 -280
- extended_data_types-6.2.1/src/extended_data_types/mcp_server/__init__.py +0 -6
- extended_data_types-6.2.1/src/extended_data_types/mcp_server/server.py +0 -303
- extended_data_types-6.2.1/src/extended_data_types/package_discovery/__init__.py +0 -6
- extended_data_types-6.2.1/src/extended_data_types/package_discovery/discovery.py +0 -61
- extended_data_types-6.2.1/src/extended_data_types/release_coordination/__init__.py +0 -6
- extended_data_types-6.2.1/src/extended_data_types/release_coordination/coordinator.py +0 -178
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/.gitignore +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/examples/README.md +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/examples/basic_usage.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/examples/composed_workflows.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/examples/file_operations.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/examples/serialization.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/examples/string_transformations.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/project.json +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/base64_utils.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/export_utils.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/file_data_type.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/hcl2_utils.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/import_utils.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/json_utils.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/list_data_type.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/matcher_utils.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/number_transformations.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/py.typed +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/serialization_utils.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/state_utils.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/string_data_type.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/string_transformations.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/toml_utils.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/transformations/__init__.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/transformations/numbers/__init__.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/transformations/numbers/notation.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/transformations/numbers/words.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/transformations/strings/__init__.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/transformations/strings/inflection.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/yaml_utils/__init__.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/yaml_utils/constructors.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/yaml_utils/dumpers.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/yaml_utils/loaders.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/yaml_utils/representers.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/yaml_utils/tag_classes.py +0 -0
- {extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/yaml_utils/utils.py +0 -0
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
<!-- version list -->
|
|
6
6
|
|
|
7
|
+
## [6.2.2](https://github.com/jbcom/extended-data-library/compare/edt-v6.2.1...edt-v6.2.2) (2026-04-16)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* **connectors:** align exports and clean api docs warnings ([#133](https://github.com/jbcom/extended-data-library/issues/133)) ([ea125f8](https://github.com/jbcom/extended-data-library/commit/ea125f8360026c7404f750f636d41345ef682a3f))
|
|
13
|
+
|
|
7
14
|
## [6.2.1](https://github.com/jbcom/extended-data-library/compare/edt-v6.2.0...edt-v6.2.1) (2026-04-16)
|
|
8
15
|
|
|
9
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: extended-data-types
|
|
3
|
-
Version: 6.2.
|
|
3
|
+
Version: 6.2.2
|
|
4
4
|
Summary: Extended functionality for data types
|
|
5
5
|
Project-URL: Documentation, https://extendeddata.dev/core/data-types/
|
|
6
6
|
Project-URL: Issues, https://github.com/jbcom/extended-data-library/issues
|
|
@@ -34,15 +34,10 @@ Requires-Dist: tomlkit>=0.13.2
|
|
|
34
34
|
Requires-Dist: validators>=0.22.0
|
|
35
35
|
Requires-Dist: wrapt>=1.16.0
|
|
36
36
|
Provides-Extra: docs
|
|
37
|
-
Requires-Dist: docutils>=0.17; extra == 'docs'
|
|
38
37
|
Requires-Dist: myst-parser>=3.0.1; extra == 'docs'
|
|
39
|
-
Requires-Dist: sphinx-
|
|
40
|
-
Requires-Dist: sphinx-
|
|
38
|
+
Requires-Dist: sphinx-autodoc2>=0.5.0; extra == 'docs'
|
|
39
|
+
Requires-Dist: sphinx-markdown-builder>=0.6.0; extra == 'docs'
|
|
41
40
|
Requires-Dist: sphinx>=7.0; extra == 'docs'
|
|
42
|
-
Provides-Extra: mcp
|
|
43
|
-
Requires-Dist: mcp>=1.26.0; (python_version >= '3.10') and extra == 'mcp'
|
|
44
|
-
Requires-Dist: pyjwt[crypto]>=2.12.0; (python_version >= '3.10') and extra == 'mcp'
|
|
45
|
-
Requires-Dist: python-multipart>=0.0.26; (python_version >= '3.10') and extra == 'mcp'
|
|
46
41
|
Provides-Extra: tests
|
|
47
42
|
Requires-Dist: coverage[toml]>=7.6.0; extra == 'tests'
|
|
48
43
|
Requires-Dist: hypothesis>=6.0.0; extra == 'tests'
|
|
@@ -114,8 +109,8 @@ print(hcl_text)
|
|
|
114
109
|
|
|
115
110
|
## Contributing
|
|
116
111
|
|
|
117
|
-
Contributions are welcome. See the shared
|
|
118
|
-
[
|
|
112
|
+
Contributions are welcome. See the shared
|
|
113
|
+
[Contributing Guidelines](https://github.com/jbcom/extended-data-library/blob/main/CONTRIBUTING.md).
|
|
119
114
|
|
|
120
115
|
## Project Links
|
|
121
116
|
|
|
@@ -55,8 +55,8 @@ print(hcl_text)
|
|
|
55
55
|
|
|
56
56
|
## Contributing
|
|
57
57
|
|
|
58
|
-
Contributions are welcome. See the shared
|
|
59
|
-
[
|
|
58
|
+
Contributions are welcome. See the shared
|
|
59
|
+
[Contributing Guidelines](https://github.com/jbcom/extended-data-library/blob/main/CONTRIBUTING.md).
|
|
60
60
|
|
|
61
61
|
## Project Links
|
|
62
62
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "extended-data-types"
|
|
7
|
-
version = "6.2.
|
|
7
|
+
version = "6.2.2"
|
|
8
8
|
description = "Extended functionality for data types"
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -46,12 +46,6 @@ Source = "https://github.com/jbcom/extended-data-library/tree/main/packages/exte
|
|
|
46
46
|
Changelog = "https://github.com/jbcom/extended-data-library/blob/main/packages/extended-data-types/CHANGELOG.md"
|
|
47
47
|
|
|
48
48
|
[project.optional-dependencies]
|
|
49
|
-
mcp = [
|
|
50
|
-
"mcp>=1.26.0; python_version >= '3.10'",
|
|
51
|
-
# Intentionally pinned here to enforce safe transitive minimums used by `mcp`.
|
|
52
|
-
"PyJWT[crypto]>=2.12.0; python_version >= '3.10'",
|
|
53
|
-
"python-multipart>=0.0.26; python_version >= '3.10'",
|
|
54
|
-
]
|
|
55
49
|
tests = [
|
|
56
50
|
"pytest>=8.2.2",
|
|
57
51
|
"pytest-asyncio>=0.23.0",
|
|
@@ -68,17 +62,13 @@ typing = [
|
|
|
68
62
|
]
|
|
69
63
|
docs = [
|
|
70
64
|
"sphinx>=7.0",
|
|
71
|
-
"sphinx-rtd-theme>=2.0.0",
|
|
72
|
-
"sphinx-autodoc-typehints>=2.0.0",
|
|
73
65
|
"myst-parser>=3.0.1",
|
|
74
|
-
"
|
|
66
|
+
"sphinx-autodoc2>=0.5.0",
|
|
67
|
+
"sphinx-markdown-builder>=0.6.0",
|
|
75
68
|
]
|
|
76
69
|
|
|
77
|
-
[project.scripts]
|
|
78
|
-
edt-mcp = "extended_data_types.mcp_server:main"
|
|
79
|
-
|
|
80
70
|
[tool.hatch.build.targets.sdist]
|
|
81
|
-
exclude = [".github", "/docs", "/
|
|
71
|
+
exclude = [".github", "/docs", "/tests", "/tox.ini"]
|
|
82
72
|
|
|
83
73
|
[tool.hatch.build.targets.wheel]
|
|
84
74
|
packages = ["src/extended_data_types"]
|
|
@@ -100,7 +90,6 @@ source = ["src/extended_data_types"]
|
|
|
100
90
|
branch = true
|
|
101
91
|
parallel = true
|
|
102
92
|
source = ["extended_data_types"]
|
|
103
|
-
relative_files = true
|
|
104
93
|
|
|
105
94
|
[tool.coverage.xml]
|
|
106
95
|
output = "coverage.xml"
|
|
@@ -8,8 +8,6 @@ lists, maps, and more.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
from extended_data_types.base64_utils import base64_decode, base64_encode
|
|
11
|
-
from extended_data_types.development_integration import DevelopmentIntegration
|
|
12
|
-
from extended_data_types.ecosystem_status import EcosystemStatusMonitor
|
|
13
11
|
from extended_data_types.export_utils import (
|
|
14
12
|
make_raw_data_export_safe,
|
|
15
13
|
wrap_raw_data_for_export,
|
|
@@ -49,15 +47,6 @@ from extended_data_types.map_data_type import (
|
|
|
49
47
|
zipmap,
|
|
50
48
|
)
|
|
51
49
|
from extended_data_types.matcher_utils import is_non_empty_match, is_partial_match
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
try:
|
|
55
|
-
from extended_data_types.mcp_server import main as mcp_server_main
|
|
56
|
-
except ImportError:
|
|
57
|
-
mcp_server_main = None # type: ignore[assignment]
|
|
58
|
-
|
|
59
|
-
from extended_data_types.package_discovery import EcosystemPackageDiscovery
|
|
60
|
-
from extended_data_types.release_coordination import ReleaseCoordinator
|
|
61
50
|
from extended_data_types.splitter_utils import split_dict_by_type, split_list_by_type
|
|
62
51
|
from extended_data_types.stack_utils import (
|
|
63
52
|
filter_methods,
|
|
@@ -119,14 +108,10 @@ from extended_data_types.type_utils import (
|
|
|
119
108
|
from extended_data_types.yaml_utils import decode_yaml, encode_yaml, is_yaml_data
|
|
120
109
|
|
|
121
110
|
|
|
122
|
-
__version__ = "6.2.
|
|
111
|
+
__version__ = "6.2.2"
|
|
123
112
|
|
|
124
113
|
__all__ = [
|
|
125
|
-
"DevelopmentIntegration",
|
|
126
|
-
"EcosystemPackageDiscovery",
|
|
127
|
-
"EcosystemStatusMonitor",
|
|
128
114
|
"FilePath",
|
|
129
|
-
"ReleaseCoordinator",
|
|
130
115
|
"SortedDefaultDict",
|
|
131
116
|
"all_non_empty",
|
|
132
117
|
"all_non_empty_in_dict",
|
|
@@ -183,7 +168,6 @@ __all__ = [
|
|
|
183
168
|
"make_hashable",
|
|
184
169
|
"make_raw_data_export_safe",
|
|
185
170
|
"match_file_extensions",
|
|
186
|
-
"mcp_server_main",
|
|
187
171
|
"ordinalize",
|
|
188
172
|
"pluralize",
|
|
189
173
|
"read_file",
|
{extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/map_data_type.py
RENAMED
|
@@ -6,6 +6,8 @@ dictionaries, and to convert keys from camelCase to snake_case.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import builtins
|
|
10
|
+
|
|
9
11
|
from collections import defaultdict
|
|
10
12
|
from collections.abc import Callable, Mapping, MutableMapping
|
|
11
13
|
from typing import Any, TypeVar
|
|
@@ -296,7 +298,7 @@ class SortedDefaultDict(defaultdict[KT, VT], SortedDict[KT, VT]): # type: ignor
|
|
|
296
298
|
|
|
297
299
|
def get_default_dict(
|
|
298
300
|
use_sorted_dict: bool = False,
|
|
299
|
-
default_type: type[dict[Any, Any]] = dict,
|
|
301
|
+
default_type: builtins.type[dict[Any, Any]] = dict,
|
|
300
302
|
levels: int = 2,
|
|
301
303
|
) -> defaultdict[str, Any] | Any:
|
|
302
304
|
"""Create a nested `defaultdict` with the specified number of levels.
|
{extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/splitter_utils.py
RENAMED
|
@@ -10,13 +10,17 @@ Functions:
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import builtins
|
|
14
|
+
|
|
13
15
|
from collections import defaultdict
|
|
14
16
|
from typing import Any
|
|
15
17
|
|
|
16
18
|
from extended_data_types.type_utils import typeof
|
|
17
19
|
|
|
18
20
|
|
|
19
|
-
def split_list_by_type(
|
|
21
|
+
def split_list_by_type(
|
|
22
|
+
input_list: list[Any], primitive_only: bool = False
|
|
23
|
+
) -> defaultdict[builtins.type[Any], list[Any]]:
|
|
20
24
|
"""Split a list by the type of its items, with an option to categorize by primitive types only.
|
|
21
25
|
|
|
22
26
|
Args:
|
|
@@ -27,13 +31,15 @@ def split_list_by_type(input_list: list[Any], primitive_only: bool = False) -> d
|
|
|
27
31
|
Returns:
|
|
28
32
|
DefaultDict[type, List[Any]]: A defaultdict storing lists of elements categorized by their type.
|
|
29
33
|
"""
|
|
30
|
-
result: defaultdict[type, list[Any]] = defaultdict(list)
|
|
34
|
+
result: defaultdict[builtins.type[Any], list[Any]] = defaultdict(list)
|
|
31
35
|
for item in input_list:
|
|
32
36
|
result[typeof(item, primitive_only=primitive_only)].append(item)
|
|
33
37
|
return result
|
|
34
38
|
|
|
35
39
|
|
|
36
|
-
def split_dict_by_type(
|
|
40
|
+
def split_dict_by_type(
|
|
41
|
+
input_dict: dict[Any, Any], primitive_only: bool = False
|
|
42
|
+
) -> defaultdict[builtins.type[Any], dict[Any, Any]]:
|
|
37
43
|
"""Split a dictionary by the type of its values, with an option to categorize by primitive types only.
|
|
38
44
|
|
|
39
45
|
Args:
|
|
@@ -44,7 +50,7 @@ def split_dict_by_type(input_dict: dict[Any, Any], primitive_only: bool = False)
|
|
|
44
50
|
Returns:
|
|
45
51
|
DefaultDict[type, Dict[Any, Any]]: A defaultdict storing dictionaries of elements categorized by their type.
|
|
46
52
|
"""
|
|
47
|
-
result: defaultdict[type, dict[Any, Any]] = defaultdict(dict)
|
|
53
|
+
result: defaultdict[builtins.type[Any], dict[Any, Any]] = defaultdict(dict)
|
|
48
54
|
for key, value in input_dict.items():
|
|
49
55
|
result[typeof(value, primitive_only=primitive_only)][key] = value
|
|
50
56
|
return result
|
{extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/stack_utils.py
RENAMED
|
@@ -6,6 +6,7 @@ methods and their docstrings for a class.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import builtins
|
|
9
10
|
import re
|
|
10
11
|
import sys
|
|
11
12
|
|
|
@@ -47,7 +48,7 @@ def filter_methods(methods: list[str]) -> list[str]:
|
|
|
47
48
|
return [method for method in methods if not method.startswith("_")]
|
|
48
49
|
|
|
49
50
|
|
|
50
|
-
def get_available_methods(cls: type[Any]) -> dict[str, str | None]:
|
|
51
|
+
def get_available_methods(cls: builtins.type[Any]) -> dict[str, str | None]:
|
|
51
52
|
"""Gets available methods and their docstrings for a class.
|
|
52
53
|
|
|
53
54
|
An "available method" is a public method that:
|
{extended_data_types-6.2.1 → extended_data_types-6.2.2}/src/extended_data_types/type_utils.py
RENAMED
|
@@ -31,6 +31,7 @@ Constants:
|
|
|
31
31
|
|
|
32
32
|
from __future__ import annotations
|
|
33
33
|
|
|
34
|
+
import builtins
|
|
34
35
|
import datetime
|
|
35
36
|
import os
|
|
36
37
|
import pathlib
|
|
@@ -88,7 +89,7 @@ class ConversionError(ValueError):
|
|
|
88
89
|
in different Python versions.
|
|
89
90
|
"""
|
|
90
91
|
|
|
91
|
-
def __init__(self, expected_type: type, value: Any):
|
|
92
|
+
def __init__(self, expected_type: builtins.type[Any], value: Any):
|
|
92
93
|
"""Initialize the ConversionError with expected type and value.
|
|
93
94
|
|
|
94
95
|
Args:
|
|
@@ -311,7 +312,7 @@ def strtotime(val: str, raise_on_error: bool = False) -> datetime.time | None:
|
|
|
311
312
|
return None
|
|
312
313
|
|
|
313
314
|
|
|
314
|
-
def get_default_value_for_type(input_type: type) -> Any:
|
|
315
|
+
def get_default_value_for_type(input_type: builtins.type[Any]) -> Any:
|
|
315
316
|
"""Returns the default value for a given type."""
|
|
316
317
|
if input_type is list:
|
|
317
318
|
return []
|
|
@@ -322,7 +323,7 @@ def get_default_value_for_type(input_type: type) -> Any:
|
|
|
322
323
|
return None
|
|
323
324
|
|
|
324
325
|
|
|
325
|
-
def get_primitive_type_for_instance_type(value: Any) -> type:
|
|
326
|
+
def get_primitive_type_for_instance_type(value: Any) -> builtins.type[Any]:
|
|
326
327
|
"""Gets the primitive type for a given value."""
|
|
327
328
|
if isinstance(value, (bool, int, float, str, bytes, bytearray)):
|
|
328
329
|
return type(value)
|
|
@@ -335,7 +336,7 @@ def get_primitive_type_for_instance_type(value: Any) -> type:
|
|
|
335
336
|
return type(None) if value is None else object
|
|
336
337
|
|
|
337
338
|
|
|
338
|
-
def typeof(item: Any, primitive_only: bool = False) -> type:
|
|
339
|
+
def typeof(item: Any, primitive_only: bool = False) -> builtins.type[Any]:
|
|
339
340
|
"""Determines either the primitive or exact type of a given value."""
|
|
340
341
|
return get_primitive_type_for_instance_type(item) if primitive_only else type(item)
|
|
341
342
|
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
"""Development Integration component for ecosystem project validation."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
import extended_data_types
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class DevelopmentIntegration:
|
|
12
|
-
"""Validates and inspects Python projects for ecosystem compliance.
|
|
13
|
-
|
|
14
|
-
Checks that projects follow the extended-data-types ecosystem conventions:
|
|
15
|
-
hatchling build backend, src layout, tests directory, and proper config.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
def __init__(self, project_template_path: Path | None = None) -> None:
|
|
19
|
-
"""Initialize with optional project template path.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
project_template_path: Path to template files for project generation.
|
|
23
|
-
"""
|
|
24
|
-
self.project_template_path = project_template_path
|
|
25
|
-
|
|
26
|
-
def configure_development_environment(self, project_path: Path) -> dict[str, Any]:
|
|
27
|
-
"""Validate that a project has the expected config files.
|
|
28
|
-
|
|
29
|
-
Checks for pyproject.toml, ruff configuration (pyproject.toml,
|
|
30
|
-
ruff.toml, or .ruff.toml), and mypy configuration (pyproject.toml,
|
|
31
|
-
mypy.ini, or .mypy.ini).
|
|
32
|
-
|
|
33
|
-
Args:
|
|
34
|
-
project_path: Root path of the project.
|
|
35
|
-
|
|
36
|
-
Returns:
|
|
37
|
-
Dict with per-config validation results and overall status.
|
|
38
|
-
"""
|
|
39
|
-
project_path = Path(project_path)
|
|
40
|
-
configs: dict[str, dict[str, Any]] = {}
|
|
41
|
-
|
|
42
|
-
# Check pyproject.toml
|
|
43
|
-
pyproject = project_path / "pyproject.toml"
|
|
44
|
-
configs["pyproject_toml"] = {
|
|
45
|
-
"found": pyproject.is_file(),
|
|
46
|
-
"path": str(pyproject),
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
# Load tool sections from pyproject.toml (reused for ruff/mypy checks)
|
|
50
|
-
tools: dict[str, Any] = {}
|
|
51
|
-
if pyproject.is_file():
|
|
52
|
-
tools = self._load_tool_section(pyproject)
|
|
53
|
-
|
|
54
|
-
# Check ruff config
|
|
55
|
-
configs["ruff"] = self._find_ruff_config(project_path, tools)
|
|
56
|
-
|
|
57
|
-
# Check mypy config
|
|
58
|
-
configs["mypy"] = self._find_mypy_config(project_path, tools)
|
|
59
|
-
|
|
60
|
-
all_found = all(cfg.get("found", False) for cfg in configs.values())
|
|
61
|
-
issues: list[str] = []
|
|
62
|
-
if not configs["pyproject_toml"]["found"]:
|
|
63
|
-
issues.append("pyproject.toml not found")
|
|
64
|
-
if not configs["ruff"]["found"]:
|
|
65
|
-
issues.append("No ruff configuration found")
|
|
66
|
-
if not configs["mypy"]["found"]:
|
|
67
|
-
issues.append("No mypy configuration found")
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
"configured": all_found,
|
|
71
|
-
"configs": configs,
|
|
72
|
-
"issues": issues,
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
def validate_project_structure(self, project_path: Path) -> dict[str, Any]:
|
|
76
|
-
"""Check that a project follows ecosystem conventions.
|
|
77
|
-
|
|
78
|
-
Validates:
|
|
79
|
-
- pyproject.toml exists with hatchling build backend
|
|
80
|
-
- src/ layout is used
|
|
81
|
-
- tests/ directory exists
|
|
82
|
-
- Proper __init__.py files in src packages
|
|
83
|
-
|
|
84
|
-
Args:
|
|
85
|
-
project_path: Root path of the project to validate.
|
|
86
|
-
|
|
87
|
-
Returns:
|
|
88
|
-
Dict with validation results including passed checks and issues.
|
|
89
|
-
"""
|
|
90
|
-
project_path = Path(project_path)
|
|
91
|
-
checks: list[dict[str, Any]] = []
|
|
92
|
-
|
|
93
|
-
# Check pyproject.toml exists
|
|
94
|
-
pyproject = project_path / "pyproject.toml"
|
|
95
|
-
checks.append(
|
|
96
|
-
{
|
|
97
|
-
"name": "pyproject.toml exists",
|
|
98
|
-
"passed": pyproject.is_file(),
|
|
99
|
-
}
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
# Check build backend if pyproject.toml exists
|
|
103
|
-
if pyproject.is_file():
|
|
104
|
-
info = self.get_project_info(project_path)
|
|
105
|
-
build_backend = info.get("build_backend", "")
|
|
106
|
-
checks.append(
|
|
107
|
-
{
|
|
108
|
-
"name": "hatchling build backend",
|
|
109
|
-
"passed": build_backend.startswith("hatchling"),
|
|
110
|
-
}
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
# Check src layout
|
|
114
|
-
src_dir = project_path / "src"
|
|
115
|
-
has_src = src_dir.is_dir()
|
|
116
|
-
checks.append(
|
|
117
|
-
{
|
|
118
|
-
"name": "src/ layout",
|
|
119
|
-
"passed": has_src,
|
|
120
|
-
}
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
# Check tests directory
|
|
124
|
-
tests_dir = project_path / "tests"
|
|
125
|
-
checks.append(
|
|
126
|
-
{
|
|
127
|
-
"name": "tests/ directory",
|
|
128
|
-
"passed": tests_dir.is_dir(),
|
|
129
|
-
}
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
# Check __init__.py files in src packages
|
|
133
|
-
if has_src:
|
|
134
|
-
missing_inits = self._find_packages_missing_init(src_dir)
|
|
135
|
-
checks.append(
|
|
136
|
-
{
|
|
137
|
-
"name": "__init__.py in src packages",
|
|
138
|
-
"passed": len(missing_inits) == 0,
|
|
139
|
-
"details": missing_inits or None,
|
|
140
|
-
}
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
issues = [c["name"] for c in checks if not c["passed"]]
|
|
144
|
-
return {
|
|
145
|
-
"valid": len(issues) == 0,
|
|
146
|
-
"checks": checks,
|
|
147
|
-
"issues": issues,
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
def get_project_info(self, project_path: Path) -> dict[str, Any]:
|
|
151
|
-
"""Read pyproject.toml and return project metadata.
|
|
152
|
-
|
|
153
|
-
Args:
|
|
154
|
-
project_path: Root path of the project.
|
|
155
|
-
|
|
156
|
-
Returns:
|
|
157
|
-
Dict with project name, version, dependencies, and build backend.
|
|
158
|
-
Returns an empty dict if pyproject.toml cannot be read.
|
|
159
|
-
"""
|
|
160
|
-
project_path = Path(project_path)
|
|
161
|
-
pyproject = project_path / "pyproject.toml"
|
|
162
|
-
if not pyproject.exists():
|
|
163
|
-
return {}
|
|
164
|
-
|
|
165
|
-
try:
|
|
166
|
-
content = extended_data_types.read_file(pyproject)
|
|
167
|
-
if not isinstance(content, (str, bytes, bytearray, memoryview)):
|
|
168
|
-
return {}
|
|
169
|
-
data = extended_data_types.decode_toml(content)
|
|
170
|
-
except (OSError, ValueError, TypeError):
|
|
171
|
-
return {}
|
|
172
|
-
|
|
173
|
-
project = data.get("project", {})
|
|
174
|
-
build_system = data.get("build-system", {})
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
"name": project.get("name", ""),
|
|
178
|
-
"version": project.get("version", ""),
|
|
179
|
-
"description": project.get("description", ""),
|
|
180
|
-
"dependencies": project.get("dependencies", []),
|
|
181
|
-
"optional_dependencies": project.get("optional-dependencies", {}),
|
|
182
|
-
"build_backend": build_system.get("build-backend", ""),
|
|
183
|
-
"build_requires": build_system.get("requires", []),
|
|
184
|
-
"python_requires": project.get("requires-python", ""),
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
def _load_tool_section(self, pyproject_path: Path) -> dict[str, Any]:
|
|
188
|
-
"""Load the [tool] section from a pyproject.toml file.
|
|
189
|
-
|
|
190
|
-
Args:
|
|
191
|
-
pyproject_path: Path to pyproject.toml.
|
|
192
|
-
|
|
193
|
-
Returns:
|
|
194
|
-
The tool section as a dict, or empty dict on failure.
|
|
195
|
-
"""
|
|
196
|
-
try:
|
|
197
|
-
content = extended_data_types.read_file(pyproject_path)
|
|
198
|
-
if not isinstance(content, (str, bytes, bytearray, memoryview)):
|
|
199
|
-
return {}
|
|
200
|
-
data = extended_data_types.decode_toml(content)
|
|
201
|
-
return dict(data.get("tool", {}))
|
|
202
|
-
except (OSError, ValueError):
|
|
203
|
-
return {}
|
|
204
|
-
|
|
205
|
-
def _find_ruff_config(self, project_path: Path, tools: dict[str, Any]) -> dict[str, Any]:
|
|
206
|
-
"""Locate ruff configuration across standard locations.
|
|
207
|
-
|
|
208
|
-
Args:
|
|
209
|
-
project_path: Root directory of the project.
|
|
210
|
-
tools: Pre-loaded [tool] section from pyproject.toml.
|
|
211
|
-
|
|
212
|
-
Returns:
|
|
213
|
-
Status dict with found flag and source location.
|
|
214
|
-
"""
|
|
215
|
-
if "ruff" in tools:
|
|
216
|
-
return {
|
|
217
|
-
"found": True,
|
|
218
|
-
"source": "pyproject.toml [tool.ruff]",
|
|
219
|
-
}
|
|
220
|
-
for filename in ("ruff.toml", ".ruff.toml"):
|
|
221
|
-
candidate = project_path / filename
|
|
222
|
-
if candidate.is_file():
|
|
223
|
-
return {"found": True, "source": filename}
|
|
224
|
-
return {"found": False, "source": None}
|
|
225
|
-
|
|
226
|
-
def _find_mypy_config(self, project_path: Path, tools: dict[str, Any]) -> dict[str, Any]:
|
|
227
|
-
"""Locate mypy configuration across standard locations.
|
|
228
|
-
|
|
229
|
-
Args:
|
|
230
|
-
project_path: Root directory of the project.
|
|
231
|
-
tools: Pre-loaded [tool] section from pyproject.toml.
|
|
232
|
-
|
|
233
|
-
Returns:
|
|
234
|
-
Status dict with found flag and source location.
|
|
235
|
-
"""
|
|
236
|
-
if "mypy" in tools:
|
|
237
|
-
return {
|
|
238
|
-
"found": True,
|
|
239
|
-
"source": "pyproject.toml [tool.mypy]",
|
|
240
|
-
}
|
|
241
|
-
for filename in ("mypy.ini", ".mypy.ini", "setup.cfg"):
|
|
242
|
-
candidate = project_path / filename
|
|
243
|
-
if candidate.is_file():
|
|
244
|
-
if filename == "setup.cfg":
|
|
245
|
-
# Only count setup.cfg if it has a [mypy] section
|
|
246
|
-
try:
|
|
247
|
-
content = extended_data_types.read_file(candidate)
|
|
248
|
-
if isinstance(content, str) and "[mypy]" in content:
|
|
249
|
-
return {"found": True, "source": "setup.cfg [mypy]"}
|
|
250
|
-
except OSError:
|
|
251
|
-
continue
|
|
252
|
-
else:
|
|
253
|
-
return {"found": True, "source": filename}
|
|
254
|
-
return {"found": False, "source": None}
|
|
255
|
-
|
|
256
|
-
def _find_packages_missing_init(self, src_dir: Path) -> list[str]:
|
|
257
|
-
"""Find package directories under src/ that lack __init__.py.
|
|
258
|
-
|
|
259
|
-
Args:
|
|
260
|
-
src_dir: The src/ directory to inspect.
|
|
261
|
-
|
|
262
|
-
Returns:
|
|
263
|
-
List of directory names missing __init__.py.
|
|
264
|
-
"""
|
|
265
|
-
missing: list[str] = []
|
|
266
|
-
for item in sorted(src_dir.iterdir()):
|
|
267
|
-
if item.is_dir() and not item.name.startswith((".", "_")) and not (item / "__init__.py").is_file():
|
|
268
|
-
missing.append(item.name)
|
|
269
|
-
return missing
|