gyomu-concept 0.5.0__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.
- gyomu_concept-0.5.0/.gitignore +29 -0
- gyomu_concept-0.5.0/PKG-INFO +24 -0
- gyomu_concept-0.5.0/README.md +3 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/pyproject.toml +38 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/src/service/__init__.py +0 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/src/service/common/__init__.py +0 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/src/service/common/result.py +19 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/src/service/order/__init__.py +2 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/src/service/order/model.py +11 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/src/service/order/repository.py +18 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/src/service/order/service.py +11 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/src/service/user/__init__.py +2 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/src/service/user/model.py +8 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/src/service/user/repository.py +13 -0
- gyomu_concept-0.5.0/concept_test_support/fixtures/directory/src/service/user/service.py +11 -0
- gyomu_concept-0.5.0/concept_test_support/helpers.py +38 -0
- gyomu_concept-0.5.0/pyproject.toml +40 -0
- gyomu_concept-0.5.0/src/gyomu_concept/__init__.py +0 -0
- gyomu_concept-0.5.0/src/gyomu_concept/directory/build.py +34 -0
- gyomu_concept-0.5.0/src/gyomu_concept/directory/internal/build.py +168 -0
- gyomu_concept-0.5.0/src/gyomu_concept/directory/internal/file_summary.py +90 -0
- gyomu_concept-0.5.0/src/gyomu_concept/directory/internal/load.py +27 -0
- gyomu_concept-0.5.0/src/gyomu_concept/directory/internal/path.py +21 -0
- gyomu_concept-0.5.0/src/gyomu_concept/directory/internal/process.py +26 -0
- gyomu_concept-0.5.0/src/gyomu_concept/directory/internal/save.py +28 -0
- gyomu_concept-0.5.0/src/gyomu_concept/directory/types.py +16 -0
- gyomu_concept-0.5.0/src/gyomu_concept/error/concept.py +38 -0
- gyomu_concept-0.5.0/tests/integration/__init__.py +0 -0
- gyomu_concept-0.5.0/tests/integration/directory/__init__.py +0 -0
- gyomu_concept-0.5.0/tests/integration/directory/test_build.py +71 -0
- gyomu_concept-0.5.0/tests/unit/__init__.py +0 -0
- gyomu_concept-0.5.0/tests/unit/directory/__init__.py +0 -0
- gyomu_concept-0.5.0/tests/unit/directory/internal/__init__.py +0 -0
- gyomu_concept-0.5.0/tests/unit/directory/internal/test_build.py +432 -0
- gyomu_concept-0.5.0/tests/unit/directory/internal/test_file_summary.py +200 -0
- gyomu_concept-0.5.0/tests/unit/directory/internal/test_load.py +150 -0
- gyomu_concept-0.5.0/tests/unit/directory/internal/test_process.py +118 -0
- gyomu_concept-0.5.0/tests/unit/directory/internal/test_save.py +103 -0
- gyomu_concept-0.5.0/tests/unit/directory/test_build.py +197 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
venv/
|
|
2
|
+
.idea/
|
|
3
|
+
|
|
4
|
+
/build/
|
|
5
|
+
/dist/
|
|
6
|
+
/src/gyomu.egg-info/
|
|
7
|
+
/.coverage
|
|
8
|
+
/htmlcov/
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
|
|
12
|
+
.env
|
|
13
|
+
.env.*
|
|
14
|
+
!.env.example
|
|
15
|
+
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
htmlcov/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
log/
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# gyomu ai related
|
|
24
|
+
**/.gyomu/cache
|
|
25
|
+
**/.gyomu/snapshot
|
|
26
|
+
**/.gyomu/checkpoint
|
|
27
|
+
|
|
28
|
+
# snapshot
|
|
29
|
+
__snapshots__
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gyomu-concept
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Gyomu Concept
|
|
5
|
+
Author: Yoshihisa Matsumoto
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Requires-Python: >=3.13
|
|
15
|
+
Requires-Dist: gyomu-ai-compiler
|
|
16
|
+
Requires-Dist: gyomu-infra
|
|
17
|
+
Requires-Dist: gyomu-python-analysis
|
|
18
|
+
Requires-Dist: gyomu-schema
|
|
19
|
+
Requires-Dist: returns
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# gyomu-concept
|
|
23
|
+
|
|
24
|
+
concept components for Gyomu Python.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "directory-concept-fixture"
|
|
3
|
+
version = "1.0"
|
|
4
|
+
description = "Gyomu Test Fixture"
|
|
5
|
+
requires-python = ">=3.13"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"pydantic"
|
|
8
|
+
]
|
|
9
|
+
license = "MIT"
|
|
10
|
+
|
|
11
|
+
[build-system]
|
|
12
|
+
requires = ["hatchling"]
|
|
13
|
+
build-backend = "hatchling.build"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
[tool.ruff]
|
|
17
|
+
target-version = "py313"
|
|
18
|
+
line-length = 88
|
|
19
|
+
exclude = [
|
|
20
|
+
"packages/infra/src/gyomu_infra/db/model/generated",
|
|
21
|
+
"packages/python_analysis/python_analysis_test_support/fixtures",
|
|
22
|
+
"packages/python_analysis/experiments",
|
|
23
|
+
"version",
|
|
24
|
+
"docs"
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[tool.ruff.lint]
|
|
28
|
+
select = [
|
|
29
|
+
"E",
|
|
30
|
+
"F",
|
|
31
|
+
"I",
|
|
32
|
+
"UP",
|
|
33
|
+
"B",
|
|
34
|
+
"SIM"
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[tool.ruff.format]
|
|
38
|
+
quote-style = "double"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# result.py
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Generic, TypeVar
|
|
4
|
+
|
|
5
|
+
T = TypeVar("T")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class Result(Generic[T]):
|
|
10
|
+
value: T | None
|
|
11
|
+
error: str | None
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def success(cls, value: T) -> "Result[T]":
|
|
15
|
+
return cls(value=value, error=None)
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def failure(cls, error: str) -> "Result[T]":
|
|
19
|
+
return cls(value=None, error=error)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# repository.py
|
|
2
|
+
from service.order.model import Order
|
|
3
|
+
from service.user.model import User
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class OrderRepository:
|
|
7
|
+
def find_by_id(self, order_id: int) -> Order | None:
|
|
8
|
+
if order_id <= 0:
|
|
9
|
+
return None
|
|
10
|
+
|
|
11
|
+
return Order(
|
|
12
|
+
order_id=order_id,
|
|
13
|
+
user=User(
|
|
14
|
+
user_id=1,
|
|
15
|
+
name="user-1",
|
|
16
|
+
),
|
|
17
|
+
amount=1000,
|
|
18
|
+
)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# service.py
|
|
2
|
+
from service.order.model import Order
|
|
3
|
+
from service.order.repository import OrderRepository
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class OrderService:
|
|
7
|
+
def __init__(self, repository: OrderRepository) -> None:
|
|
8
|
+
self._repository = repository
|
|
9
|
+
|
|
10
|
+
def find_order(self, order_id: int) -> Order | None:
|
|
11
|
+
return self._repository.find_by_id(order_id)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# service.py
|
|
2
|
+
from service.user.model import User
|
|
3
|
+
from service.user.repository import UserRepository
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class UserService:
|
|
7
|
+
def __init__(self, repository: UserRepository) -> None:
|
|
8
|
+
self._repository = repository
|
|
9
|
+
|
|
10
|
+
def find_user(self, user_id: int) -> User | None:
|
|
11
|
+
return self._repository.find_by_id(user_id)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from difflib import unified_diff
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from gyomu_python_analysis.analysis.initialize import initialize_project_context
|
|
5
|
+
from gyomu_python_analysis.project.context import ProjectContext
|
|
6
|
+
from gyomu_schema.schemas.python.types import (
|
|
7
|
+
ProjectRelativePath,
|
|
8
|
+
)
|
|
9
|
+
from gyomu_schema.schemas.types import FullPath
|
|
10
|
+
|
|
11
|
+
FIXTURES_ROOT = FullPath(Path(__file__).parent / "fixtures")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def assert_text_file_equals(
|
|
15
|
+
root_path: Path, source_path: Path, expected_path: Path
|
|
16
|
+
) -> None:
|
|
17
|
+
actual_path = root_path / source_path
|
|
18
|
+
expected_path = root_path / expected_path
|
|
19
|
+
actual = actual_path.read_text(encoding="utf-8")
|
|
20
|
+
expected = expected_path.read_text(encoding="utf-8")
|
|
21
|
+
|
|
22
|
+
diff = "".join(
|
|
23
|
+
unified_diff(
|
|
24
|
+
expected.splitlines(keepends=True),
|
|
25
|
+
actual.splitlines(keepends=True),
|
|
26
|
+
fromfile=str(expected_path),
|
|
27
|
+
tofile=str(actual_path),
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
assert actual == expected, f"\n{diff}"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _create_directory_project_context() -> ProjectContext:
|
|
35
|
+
return initialize_project_context(
|
|
36
|
+
project_root=FIXTURES_ROOT / "directory",
|
|
37
|
+
source_root=ProjectRelativePath(Path("src")),
|
|
38
|
+
).unwrap()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gyomu-concept"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "Gyomu Concept"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"gyomu-schema",
|
|
9
|
+
"gyomu-infra",
|
|
10
|
+
"gyomu-python-analysis",
|
|
11
|
+
"gyomu-ai-compiler",
|
|
12
|
+
"returns",
|
|
13
|
+
]
|
|
14
|
+
license = "MIT"
|
|
15
|
+
authors = [
|
|
16
|
+
{ name = "Yoshihisa Matsumoto" },
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[build-system]
|
|
29
|
+
requires = ["hatchling"]
|
|
30
|
+
build-backend = "hatchling.build"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
[tool.uv.sources]
|
|
34
|
+
gyomu-schema = { workspace = true }
|
|
35
|
+
gyomu-infra = { workspace = true }
|
|
36
|
+
gyomu-python-analysis = { workspace = true }
|
|
37
|
+
gyomu-ai-compiler = {workspace = true}
|
|
38
|
+
|
|
39
|
+
[tool.hatch.version]
|
|
40
|
+
path = "../../version/__about__.py"
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from gyomu_concept.directory.internal.build import build_directory_concept_from_path
|
|
2
|
+
from gyomu_concept.directory.types import BuildRootResult
|
|
3
|
+
from gyomu_concept.error.concept import ConceptError
|
|
4
|
+
from gyomu_python_analysis.project.context import ProjectContext
|
|
5
|
+
from gyomu_schema.option.concept import ConceptOption
|
|
6
|
+
from gyomu_schema.schemas.concept.directory.concept import DirectoryConcept
|
|
7
|
+
from gyomu_schema.schemas.types import FullPath
|
|
8
|
+
from returns.result import Failure, Result, Success
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def build_directory_concept(
|
|
12
|
+
context: ProjectContext, option: ConceptOption | None = None
|
|
13
|
+
) -> Result[BuildRootResult, ConceptError]:
|
|
14
|
+
root_path_items: tuple[FullPath, ...] = (
|
|
15
|
+
(FullPath(context.project_root / option.target_folder),)
|
|
16
|
+
if option is not None and option.target_folder is not None
|
|
17
|
+
else tuple(
|
|
18
|
+
FullPath(context.project_root / root) for root in context.package_roots
|
|
19
|
+
)
|
|
20
|
+
)
|
|
21
|
+
is_changed = False
|
|
22
|
+
concepts: list[DirectoryConcept] = []
|
|
23
|
+
for root_path in root_path_items:
|
|
24
|
+
result = await build_directory_concept_from_path(context, root_path, option)
|
|
25
|
+
if isinstance(result, Failure):
|
|
26
|
+
return result
|
|
27
|
+
|
|
28
|
+
item_result = result.unwrap()
|
|
29
|
+
concepts.append(item_result.concept)
|
|
30
|
+
|
|
31
|
+
if item_result.changed:
|
|
32
|
+
is_changed = True
|
|
33
|
+
|
|
34
|
+
return Success(BuildRootResult(concepts=tuple(concepts), changed=is_changed))
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from gyomu_concept.directory.internal.file_summary import build_file_summary_record
|
|
2
|
+
from gyomu_concept.directory.internal.load import load_directory_concept
|
|
3
|
+
from gyomu_concept.directory.internal.process import process_directory_concept
|
|
4
|
+
from gyomu_concept.directory.internal.save import save_directory_concept
|
|
5
|
+
from gyomu_concept.directory.types import BuildResult
|
|
6
|
+
from gyomu_concept.error.concept import ConceptError
|
|
7
|
+
from gyomu_python_analysis.analysis.load_file_context import load_file_analysis_context
|
|
8
|
+
from gyomu_python_analysis.project.context import ProjectContext
|
|
9
|
+
from gyomu_schema.option.concept import ConceptOption
|
|
10
|
+
from gyomu_schema.schemas.concept.directory.input import (
|
|
11
|
+
DirectoryConceptInput,
|
|
12
|
+
SubDirectoryInput,
|
|
13
|
+
)
|
|
14
|
+
from gyomu_schema.schemas.python.types import (
|
|
15
|
+
DirectoryRelativePath,
|
|
16
|
+
ProjectRelativePath,
|
|
17
|
+
)
|
|
18
|
+
from gyomu_schema.schemas.types import FullPath
|
|
19
|
+
from returns.result import Failure, Result, Success
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def build_directory_concept_from_path(
|
|
23
|
+
context: ProjectContext,
|
|
24
|
+
target_directory: FullPath,
|
|
25
|
+
option: ConceptOption | None = None,
|
|
26
|
+
) -> Result[BuildResult, ConceptError]:
|
|
27
|
+
target_directory_relative_path = ProjectRelativePath(
|
|
28
|
+
target_directory.relative_to(context.project_root)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
entries = sorted(
|
|
32
|
+
target_directory.iterdir(),
|
|
33
|
+
key=lambda path: path.name,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
folders = [path for path in entries if path.is_dir()]
|
|
37
|
+
|
|
38
|
+
files = [
|
|
39
|
+
path
|
|
40
|
+
for path in entries
|
|
41
|
+
if path.is_file()
|
|
42
|
+
and ProjectRelativePath(path.relative_to(context.project_root))
|
|
43
|
+
in context.included_files
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
is_changed = _is_directory_changed(
|
|
47
|
+
target_directory_relative_path=target_directory_relative_path,
|
|
48
|
+
option=option,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if not is_changed:
|
|
52
|
+
loaded = load_directory_concept(
|
|
53
|
+
context=context,
|
|
54
|
+
target_directory=target_directory_relative_path,
|
|
55
|
+
option=option,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if isinstance(loaded, Success):
|
|
59
|
+
concept = loaded.unwrap()
|
|
60
|
+
|
|
61
|
+
if concept is not None:
|
|
62
|
+
return Success(
|
|
63
|
+
BuildResult(
|
|
64
|
+
concept=concept,
|
|
65
|
+
changed=False,
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
directory_concepts: list[SubDirectoryInput] = []
|
|
70
|
+
|
|
71
|
+
for folder in folders:
|
|
72
|
+
directory_result = await build_directory_concept_from_path(
|
|
73
|
+
context=context,
|
|
74
|
+
target_directory=folder,
|
|
75
|
+
option=option,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if isinstance(directory_result, Failure):
|
|
79
|
+
return directory_result
|
|
80
|
+
|
|
81
|
+
build_result = directory_result.unwrap()
|
|
82
|
+
|
|
83
|
+
if build_result.changed:
|
|
84
|
+
is_changed = True
|
|
85
|
+
|
|
86
|
+
directory_concepts.append(
|
|
87
|
+
SubDirectoryInput(
|
|
88
|
+
path=DirectoryRelativePath(folder.relative_to(target_directory)),
|
|
89
|
+
concept=build_result.concept,
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
file_summaries = []
|
|
94
|
+
|
|
95
|
+
for file in files:
|
|
96
|
+
file_relative_path = ProjectRelativePath(file.relative_to(context.project_root))
|
|
97
|
+
|
|
98
|
+
file_result = load_file_analysis_context(
|
|
99
|
+
context=context,
|
|
100
|
+
file_path=file_relative_path,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if isinstance(file_result, Failure):
|
|
104
|
+
return Failure(
|
|
105
|
+
ConceptError(
|
|
106
|
+
message="fail to load file analysis context",
|
|
107
|
+
file_path=file_relative_path,
|
|
108
|
+
package_name=context.config.name,
|
|
109
|
+
phase="directory-summary",
|
|
110
|
+
identity=None,
|
|
111
|
+
).chain(file_result.failure())
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
file_summaries.append(
|
|
115
|
+
build_file_summary_record(
|
|
116
|
+
project_context=context,
|
|
117
|
+
file_context=file_result.unwrap(),
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
input_context = DirectoryConceptInput(
|
|
122
|
+
files=tuple(file_summaries),
|
|
123
|
+
sub_directories=tuple(directory_concepts),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
generated = await process_directory_concept(
|
|
127
|
+
package_name=context.config.name,
|
|
128
|
+
target_directory=target_directory_relative_path,
|
|
129
|
+
concept=input_context,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if isinstance(generated, Failure):
|
|
133
|
+
return generated
|
|
134
|
+
|
|
135
|
+
concept = generated.unwrap()
|
|
136
|
+
|
|
137
|
+
saved = save_directory_concept(
|
|
138
|
+
context=context,
|
|
139
|
+
target_directory=target_directory_relative_path,
|
|
140
|
+
concept=concept,
|
|
141
|
+
option=option,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if isinstance(saved, Failure):
|
|
145
|
+
return saved
|
|
146
|
+
|
|
147
|
+
return Success(
|
|
148
|
+
BuildResult(
|
|
149
|
+
concept=concept,
|
|
150
|
+
changed=is_changed,
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _is_directory_changed(
|
|
156
|
+
target_directory_relative_path: ProjectRelativePath,
|
|
157
|
+
option: ConceptOption | None,
|
|
158
|
+
) -> bool:
|
|
159
|
+
if option is None or option.changed_files is None:
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
return any(
|
|
163
|
+
changed_file.project_relative_path == target_directory_relative_path
|
|
164
|
+
or changed_file.project_relative_path.is_relative_to(
|
|
165
|
+
target_directory_relative_path
|
|
166
|
+
)
|
|
167
|
+
for changed_file in option.changed_files
|
|
168
|
+
)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from gyomu_python_analysis.path.conversion import (
|
|
2
|
+
source_relative_path_to_project_relative_path,
|
|
3
|
+
)
|
|
4
|
+
from gyomu_python_analysis.project.context import ProjectContext
|
|
5
|
+
from gyomu_schema.schemas.concept.file_summary import (
|
|
6
|
+
DependencySummary,
|
|
7
|
+
FileSummary,
|
|
8
|
+
PublicDeclarationSummary,
|
|
9
|
+
)
|
|
10
|
+
from gyomu_schema.schemas.python.dependency import ImportedSymbolDependency
|
|
11
|
+
from gyomu_schema.schemas.python.file_analysis import FileAnalysisContext
|
|
12
|
+
from gyomu_schema.schemas.python.symbol import SymbolAnalysis
|
|
13
|
+
from gyomu_schema.schemas.python.symbol_base import DeclarationKind
|
|
14
|
+
from gyomu_schema.schemas.python.visibility import Visibility
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def build_file_summary_record(
|
|
18
|
+
project_context: ProjectContext, file_context: FileAnalysisContext
|
|
19
|
+
) -> FileSummary:
|
|
20
|
+
package_name = file_context.analysis.module_name.split(".")[0]
|
|
21
|
+
return FileSummary(
|
|
22
|
+
path=source_relative_path_to_project_relative_path(
|
|
23
|
+
path=file_context.analysis.path, context=project_context
|
|
24
|
+
),
|
|
25
|
+
exports=tuple(
|
|
26
|
+
[
|
|
27
|
+
_build_export_summary(item)
|
|
28
|
+
for item in file_context.analysis.symbols
|
|
29
|
+
if item.visibility == Visibility.PUBLIC
|
|
30
|
+
]
|
|
31
|
+
),
|
|
32
|
+
dependencies=_aggregate_dependencies(
|
|
33
|
+
file_context=file_context, package_name=package_name
|
|
34
|
+
),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _build_export_summary(item: SymbolAnalysis) -> PublicDeclarationSummary:
|
|
39
|
+
summary = ""
|
|
40
|
+
if (
|
|
41
|
+
item.docstring is not None
|
|
42
|
+
and item.docstring.summary is not None
|
|
43
|
+
and item.docstring.summary != ""
|
|
44
|
+
):
|
|
45
|
+
summary = item.docstring.summary
|
|
46
|
+
elif (
|
|
47
|
+
item.kind == DeclarationKind.VARIABLE
|
|
48
|
+
and item.pydantic is not None
|
|
49
|
+
and item.pydantic.description is not None
|
|
50
|
+
):
|
|
51
|
+
summary = item.pydantic.description
|
|
52
|
+
return PublicDeclarationSummary(
|
|
53
|
+
symbol=item.identity.symbol_id, kind=item.kind, summary=summary
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _aggregate_dependencies(
|
|
58
|
+
file_context: FileAnalysisContext, package_name: str
|
|
59
|
+
) -> tuple[DependencySummary, ...]:
|
|
60
|
+
dependencies = [
|
|
61
|
+
dependency.target
|
|
62
|
+
for symbol in file_context.analysis.symbols
|
|
63
|
+
for dependency in symbol.dependencies
|
|
64
|
+
if isinstance(dependency.target, ImportedSymbolDependency)
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
summaries = {
|
|
68
|
+
(
|
|
69
|
+
summary.target,
|
|
70
|
+
summary.external,
|
|
71
|
+
): summary
|
|
72
|
+
for dependency in dependencies
|
|
73
|
+
for summary in [
|
|
74
|
+
_build_dependency_summary(
|
|
75
|
+
import_item=dependency,
|
|
76
|
+
package_name=package_name,
|
|
77
|
+
)
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return tuple(summaries.values())
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _build_dependency_summary(
|
|
85
|
+
import_item: ImportedSymbolDependency, package_name: str
|
|
86
|
+
) -> DependencySummary:
|
|
87
|
+
return DependencySummary(
|
|
88
|
+
target=import_item.symbol_id,
|
|
89
|
+
external=not import_item.symbol_id.startswith(package_name),
|
|
90
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from gyomu_concept.directory.internal.path import get_directory_concept_path
|
|
2
|
+
from gyomu_concept.error.concept import ConceptError
|
|
3
|
+
from gyomu_infra.filesystem.file_io import read_json
|
|
4
|
+
from gyomu_python_analysis.project.context import ProjectContext
|
|
5
|
+
from gyomu_schema.option.concept import ConceptOption
|
|
6
|
+
from gyomu_schema.schemas.concept.directory.concept import DirectoryConcept
|
|
7
|
+
from gyomu_schema.schemas.python.types import ProjectRelativePath
|
|
8
|
+
from returns.result import Result, Success
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def load_directory_concept(
|
|
12
|
+
context: ProjectContext,
|
|
13
|
+
target_directory: ProjectRelativePath,
|
|
14
|
+
option: ConceptOption | None = None,
|
|
15
|
+
) -> Result[DirectoryConcept | None, ConceptError]:
|
|
16
|
+
concept_full_path = get_directory_concept_path(context, target_directory, option)
|
|
17
|
+
if not concept_full_path.exists():
|
|
18
|
+
return Success(None)
|
|
19
|
+
return read_json(path=concept_full_path, model_type=DirectoryConcept).alt(
|
|
20
|
+
lambda error: ConceptError(
|
|
21
|
+
message="fail to load Directory Concept",
|
|
22
|
+
file_path=target_directory,
|
|
23
|
+
package_name=context.config.name,
|
|
24
|
+
phase="directory-summary",
|
|
25
|
+
identity=None,
|
|
26
|
+
).chain(error)
|
|
27
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from gyomu_python_analysis.project.context import ProjectContext
|
|
2
|
+
from gyomu_schema.option.concept import ConceptOption
|
|
3
|
+
from gyomu_schema.schemas.python.types import ProjectRelativePath
|
|
4
|
+
from gyomu_schema.schemas.types import FullPath
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_directory_concept_path(
|
|
8
|
+
context: ProjectContext,
|
|
9
|
+
target_directory: ProjectRelativePath,
|
|
10
|
+
option: ConceptOption | None = None,
|
|
11
|
+
) -> FullPath:
|
|
12
|
+
return FullPath(
|
|
13
|
+
context.project_root
|
|
14
|
+
/ (
|
|
15
|
+
"cache"
|
|
16
|
+
if option and option.action and option.action.write_to_temp_folder
|
|
17
|
+
else "concept"
|
|
18
|
+
)
|
|
19
|
+
/ target_directory
|
|
20
|
+
/ "$Directory.json"
|
|
21
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from gyomu_ai_compiler.pipelines.directory_concept.executor.generate import (
|
|
2
|
+
generate_directory_concept,
|
|
3
|
+
)
|
|
4
|
+
from gyomu_concept.error.concept import ConceptError
|
|
5
|
+
from gyomu_schema.schemas.concept.directory.concept import DirectoryConcept
|
|
6
|
+
from gyomu_schema.schemas.concept.directory.input import DirectoryConceptInput
|
|
7
|
+
from gyomu_schema.schemas.python.types import ProjectRelativePath
|
|
8
|
+
from returns.result import Result
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def process_directory_concept(
|
|
12
|
+
package_name: str,
|
|
13
|
+
target_directory: ProjectRelativePath,
|
|
14
|
+
concept: DirectoryConceptInput,
|
|
15
|
+
) -> Result[DirectoryConcept, ConceptError]:
|
|
16
|
+
result = await generate_directory_concept(context=concept)
|
|
17
|
+
return result.alt(
|
|
18
|
+
lambda error: ConceptError(
|
|
19
|
+
message="fail to generate Directory Concept",
|
|
20
|
+
file_path=target_directory,
|
|
21
|
+
package_name=package_name,
|
|
22
|
+
phase="directory-summary",
|
|
23
|
+
identity=None,
|
|
24
|
+
details=vars(concept),
|
|
25
|
+
).chain(error)
|
|
26
|
+
)
|