cod8a 0.1.0__py3-none-any.whl

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.
cod8a/models/models.py ADDED
@@ -0,0 +1,60 @@
1
+ from typing import List, Optional
2
+
3
+ from pydantic import BaseModel, Field
4
+ from pydantic.dataclasses import dataclass
5
+
6
+ @dataclass
7
+ class UsingDirective:
8
+ id: int
9
+ name: str
10
+
11
+ @dataclass
12
+ class ParameterStructure:
13
+ name: str
14
+ modifier: str
15
+ type: str
16
+ summary: str
17
+
18
+ @dataclass
19
+ class FieldStructure:
20
+ id: int
21
+ name: str
22
+ modifier: str
23
+ type: str
24
+ summary: str
25
+
26
+ @dataclass
27
+ class MethodStructure:
28
+ id: int
29
+ name: str
30
+ modifier: str
31
+ return_type: str
32
+ parameters: List[ParameterStructure]
33
+ summary: str
34
+
35
+ @dataclass
36
+ class Relationship:
37
+ id: int
38
+ type: str
39
+ parent_name: str
40
+
41
+ @dataclass
42
+ class ClassStructure:
43
+ id: int
44
+ name: str
45
+ methods: List[MethodStructure]
46
+ fields: List[FieldStructure]
47
+ type: str
48
+ associated_item: List[Relationship]
49
+ summary: str
50
+
51
+ class FileStructure(BaseModel):
52
+ id: int
53
+ name: str
54
+ using_directives: Optional[List[UsingDirective]] = []
55
+ classes: Optional[List[ClassStructure]] = []
56
+
57
+
58
+ class ProjectStructure(BaseModel):
59
+ name: str
60
+ files: List[FileStructure] = []
@@ -0,0 +1,100 @@
1
+ import subprocess
2
+ import json
3
+ import os
4
+ import shutil
5
+ from typing import Union, List
6
+ from cod8a.models.models import (
7
+ FileStructure, ProjectStructure, ClassStructure,
8
+ MethodStructure, FieldStructure, ParameterStructure,
9
+ UsingDirective, Relationship
10
+ )
11
+
12
+ class DotnetParser:
13
+ def __init__(self, analyzer_path: str):
14
+ self.analyzer_path = analyzer_path
15
+
16
+ def parse(self, path: str) -> Union[FileStructure, ProjectStructure]:
17
+ if not shutil.which("dotnet"):
18
+ raise Exception("The .NET SDK/Runtime is required to analyze C# files. Please install it or ensure it is in your PATH.")
19
+
20
+ abs_path = os.path.abspath(path)
21
+ cwd = os.path.dirname(self.analyzer_path)
22
+
23
+ # Look for a compiled DLL first (production/distribution mode)
24
+ # Expected path: src/cod8a/dotnet/CodeAnalysis/bin/Release/net10.0/CodeAnalyzer.dll
25
+ dll_path = os.path.join(cwd, "bin", "Release", "net10.0", "CodeAnalyzer.dll")
26
+
27
+ if os.path.exists(dll_path):
28
+ args = ["dotnet", dll_path, abs_path]
29
+ else:
30
+ # Fallback to source-level execution (development mode)
31
+ args = ["dotnet", "run", "--project", self.analyzer_path, "--", abs_path]
32
+
33
+ result = subprocess.run(args, capture_output=True, text=True, cwd=cwd)
34
+ if result.returncode != 0:
35
+ raise Exception(f"Dotnet analyzer failed: {result.stderr}")
36
+
37
+ # The C# analyzer might still print some messages to stdout.
38
+ # We find the last line which should be our JSON.
39
+ lines = result.stdout.strip().splitlines()
40
+ json_str = next((line for line in reversed(lines) if line.startswith('{') and line.endswith('}')), None)
41
+
42
+ if not json_str:
43
+ raise Exception(f"Could not find JSON in dotnet analyzer output: {result.stdout}")
44
+
45
+ data = json.loads(json_str)
46
+
47
+ if "Files" in data:
48
+ return self._map_project(data)
49
+ return self._map_file(data)
50
+
51
+ def _map_project(self, data: dict) -> ProjectStructure:
52
+ return ProjectStructure(
53
+ name=data.get("Name", ""),
54
+ files=[self._map_file(f) for f in data.get("Files", [])]
55
+ )
56
+
57
+ def _map_file(self, data: dict) -> FileStructure:
58
+ return FileStructure(
59
+ id=data.get("Id", 0),
60
+ name=data.get("Name", ""),
61
+ using_directives=[UsingDirective(u.get("Id", 0), u.get("Name", "")) for u in data.get("UsingDirectives", []) or []],
62
+ classes=[self._map_class(c) for c in data.get("Classes", []) or []],
63
+ )
64
+
65
+ def _map_class(self, data: dict) -> ClassStructure:
66
+ return ClassStructure(
67
+ id=data.get("Id", 0),
68
+ name=data.get("Name", ""),
69
+ methods=[self._map_method(m) for m in data.get("Methods", []) or []],
70
+ fields=[self._map_field(f) for f in data.get("Fields", []) or []],
71
+ type=data.get("Type", "class"),
72
+ associated_item=[self._map_relationship(r) for r in data.get("Relationships", []) or []],
73
+ summary=data.get("Summary", "")
74
+ )
75
+
76
+ def _map_method(self, data: dict) -> MethodStructure:
77
+ return MethodStructure(
78
+ id=data.get("Id", 0),
79
+ name=data.get("Name", ""),
80
+ modifier=data.get("Modifier", ""),
81
+ return_type=data.get("ReturnType", ""),
82
+ parameters=[ParameterStructure(p.get("Name", ""), p.get("Modifier", ""), p.get("Type", ""), p.get("Summary", "")) for p in data.get("Parameters", []) or []],
83
+ summary=data.get("Summary", "")
84
+ )
85
+
86
+ def _map_field(self, data: dict) -> FieldStructure:
87
+ return FieldStructure(
88
+ id=data.get("Id", 0),
89
+ name=data.get("Name", ""),
90
+ modifier=data.get("Modifier", ""),
91
+ type=data.get("Type", ""),
92
+ summary=data.get("Summary", "")
93
+ )
94
+
95
+ def _map_relationship(self, data: dict) -> Relationship:
96
+ return Relationship(
97
+ id=data.get("Id", 0),
98
+ type=data.get("Type", ""),
99
+ parent_name=data.get("AssociatedItem", "")
100
+ )
@@ -0,0 +1,138 @@
1
+ import ast
2
+ import json
3
+ import os
4
+ from typing import List, Union
5
+ from cod8a.models.models import (
6
+ FileStructure, ClassStructure, MethodStructure,
7
+ FieldStructure, ParameterStructure, ProjectStructure, UsingDirective,
8
+ Relationship
9
+ )
10
+ from dataclasses import asdict
11
+
12
+ class PythonParser:
13
+ def __init__(self):
14
+ self._id_counter = 0
15
+
16
+ def _next_id(self):
17
+ self._id_counter += 1
18
+ return self._id_counter
19
+
20
+ def parse_file(self, file_path: str) -> FileStructure:
21
+ with open(file_path, "r", encoding="utf-8") as f:
22
+ tree = ast.parse(f.read())
23
+
24
+ file_name = os.path.basename(file_path)
25
+
26
+ file_struct = FileStructure(id=self._next_id(), name=file_name)
27
+
28
+ for node in tree.body:
29
+ if isinstance(node, (ast.Import, ast.ImportFrom)):
30
+ file_struct.using_directives.extend(self._parse_import(node))
31
+ elif isinstance(node, ast.ClassDef):
32
+ file_struct.classes.append(self._parse_class(node))
33
+
34
+ return file_struct
35
+
36
+ def _parse_import(self, node) -> List[UsingDirective]:
37
+ directives = []
38
+ if isinstance(node, ast.Import):
39
+ for alias in node.names:
40
+ directives.append(UsingDirective(id=self._next_id(), name=alias.name))
41
+ elif isinstance(node, ast.ImportFrom):
42
+ module = node.module or ""
43
+ for alias in node.names:
44
+ directives.append(UsingDirective(id=self._next_id(), name=f"{module}.{alias.name}"))
45
+ return directives
46
+
47
+ def _parse_class(self, node: ast.ClassDef) -> ClassStructure:
48
+ methods = []
49
+ fields = []
50
+ relationships = []
51
+ summary = ast.get_docstring(node) or ""
52
+
53
+ for base in node.bases:
54
+ if isinstance(base, ast.Subscript):
55
+ base_name = ast.unparse(base.value)
56
+ else:
57
+ base_name = ast.unparse(base)
58
+
59
+ # PEP 544 protocols, standard ABCs, or conventional I-prefixed interfaces
60
+ is_interface = base_name in ("Protocol", "typing.Protocol", "ABC") or (len(base_name) > 1 and base_name[0] == 'I' and base_name[1].isupper())
61
+ rel_type = "Interface" if is_interface else "Class"
62
+
63
+ relationships.append(Relationship(
64
+ id=self._next_id(),
65
+ type=rel_type,
66
+ parent_name=base_name
67
+ ))
68
+
69
+ for item in node.body:
70
+ if isinstance(item, ast.FunctionDef):
71
+ methods.append(self._parse_method(item))
72
+ elif isinstance(item, ast.Assign):
73
+ # Basic field extraction from class-level assignments
74
+ for target in item.targets:
75
+ if isinstance(target, ast.Name):
76
+ fields.append(FieldStructure(
77
+ id=self._next_id(),
78
+ name=target.id,
79
+ modifier="private" if target.id.startswith("_") else "public",
80
+ type="", # Type inference is complex, leaving empty for now
81
+ summary=""
82
+ ))
83
+ elif isinstance(item, ast.AnnAssign):
84
+ if isinstance(item.target, ast.Name):
85
+ fields.append(FieldStructure(
86
+ id=self._next_id(),
87
+ name=item.target.id,
88
+ modifier="private" if item.target.id.startswith("_") else "public",
89
+ type=ast.unparse(item.annotation),
90
+ summary=""
91
+ ))
92
+
93
+ return ClassStructure(
94
+ id=self._next_id(),
95
+ name=node.name,
96
+ methods=methods,
97
+ fields=fields,
98
+ type="class",
99
+ associated_item=relationships,
100
+ summary=summary
101
+ )
102
+
103
+ def _parse_method(self, node: ast.FunctionDef) -> MethodStructure:
104
+ summary = ast.get_docstring(node) or ""
105
+ parameters = []
106
+
107
+ for arg in node.args.args:
108
+ arg_type = ast.unparse(arg.annotation) if arg.annotation else ""
109
+ parameters.append(ParameterStructure(
110
+ name=arg.arg,
111
+ modifier="",
112
+ type=arg_type,
113
+ summary=""
114
+ ))
115
+
116
+ return MethodStructure(
117
+ id=self._next_id(),
118
+ name=node.name,
119
+ modifier="private" if node.name.startswith("_") else "public",
120
+ return_type=ast.unparse(node.returns) if node.returns else "",
121
+ parameters=parameters,
122
+ summary=summary
123
+ )
124
+
125
+ def parse_project(self, project_path: str) -> List[FileStructure]:
126
+ files = []
127
+ for root, _, filenames in os.walk(project_path):
128
+ for filename in filenames:
129
+ if filename.endswith(".py"):
130
+ files.append(self.parse_file(os.path.join(root, filename)))
131
+ return files
132
+
133
+ def parse(self, path: str) -> Union[FileStructure | ProjectStructure | List[FileStructure]]:
134
+ isFile = os.path.isfile(path)
135
+ if isFile:
136
+ return self.parse_file(path)
137
+ else:
138
+ return self.parse_project(path)
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: cod8a
3
+ Version: 0.1.0
4
+ Summary: cod8a is a code documentation and visualization tool for Python and .NET projects
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Keywords: visualization,uml,docs,flowchart,class,sequence,diagram
8
+ Author: Akumbom
9
+ Author-email: akumbom5ma@gmail.com
10
+ Maintainer: Marietta Akumbom
11
+ Requires-Python: >=3.13
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Dist: click (>=8.3.3,<9.0.0)
16
+ Requires-Dist: pydantic (>=2.13.3,<3.0.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+ # cod8a
20
+
21
+ cod8a pronounced codetta, is a code documentation and visualization tool for **Python** and **.NET** projects.
22
+
23
+ ## Installation
24
+
25
+ ### Prerequisites
26
+
27
+ - **[Python 3.9+](https://www.python.org/downloads/)**
28
+ - **[.NET 10.0 Runtime or SDK](https://dotnet.microsoft.com/download/dotnet/10.0)** (Required **only** for C#/.NET analysis)
29
+
30
+ ### From PyPI (Recommended for Users)
31
+
32
+ Once published, you can install `cod8a` directly using pip:
33
+
34
+ ```bash
35
+ pip install cod8a
36
+ ```
37
+
38
+ ## Development & Contribution
39
+
40
+ If you want to contribute to the project or run it from source:
41
+
42
+ ### Setup
43
+
44
+ 1. **Fork** the repository on GitHub.
45
+ 2. **Clone** your fork:
46
+
47
+ ```bash
48
+ git clone https://github.com/yourusername/cod8a.git
49
+ cd cod8a
50
+ ```
51
+
52
+ ### Development Options
53
+
54
+ #### 1. Local Setup
55
+ 3. **Install** Python dependencies using **[Poetry](https://python-poetry.org/docs/#installation)**:
56
+
57
+ ```bash
58
+ poetry install
59
+ ```
60
+
61
+ 4. **Optional**: Ensure .NET 10.0 is installed (if documenting C#)
62
+
63
+ ```bash
64
+ dotnet --version
65
+ ```
66
+
67
+ #### 2. VS Code Dev Container (Recommended)
68
+ If you use VS Code, you can skip the local setup by using the provided **Dev Container**. It comes pre-configured with Python 3.13, .NET 10.0, and Poetry.
69
+ - Simply open the project in VS Code and click **"Reopen in Container"** when prompted.
70
+
71
+ #### 3. Docker
72
+ You can also run `cod8a` as a standalone container without installing any dependencies locally:
73
+
74
+ ```bash
75
+ # Build the image
76
+ docker build -t cod8a .
77
+
78
+ # Run the tool
79
+ docker run -v $(pwd):/app/data cod8a uml -p /app/data/src
80
+ ```
81
+
82
+ ## Usage
83
+
84
+ The tool uses a CLI interface to analyze code and generate visualizations.
85
+
86
+ ### UML Diagrams
87
+
88
+ Generate Mermaid-compatible diagrams (Class, Sequence, Flowchart):
89
+
90
+ ```bash
91
+ # General syntax
92
+ cod8a uml -p <path_to_source> -d <diagram_type>
93
+
94
+ # Generate a class diagram (default)
95
+ cod8a uml -p src/my_project
96
+
97
+ # Generate a sequence diagram
98
+ cod8a uml -p src/my_file.py -d sequence
99
+
100
+ # Generate a flowchart
101
+ cod8a uml -p src/my_logic -d flow
102
+ ```
103
+
104
+ #### Diagram Type Options:
105
+ - **Class:** `class`, `c`
106
+ - **Sequence:** `sequence`, `seq`, `s`
107
+ - **Flowchart:** `flowchart`, `flow`, `f`
108
+
109
+ ### CLI Options
110
+
111
+ - `-p, --path`: Path to the file or directory to analyze.
112
+ - `-d, --diagram`: Type of diagram to generate (default: `class`).
113
+ - `-o, --output`: Output file path (saves as `.mmd`).
114
+
115
+ ### Documentation (In development)
116
+
117
+ *Note: This feature is currently in development.*
118
+
119
+ Generate Markdown documentation from code structure:
120
+
121
+ ```bash
122
+ # Not yet implemented
123
+ cod8a doc -p path/to/source
124
+ ```
125
+
126
+ ## Features
127
+
128
+ - **Mermaid Integration:** Generates high-quality Mermaid.js diagrams for visualization.
129
+ - **Supported Diagrams:** Class, Sequence, and Flowchart diagrams.
130
+ - **C# Support:** Uses a Roslyn-based analyzer targeting **.NET 10.0** to extract deep structural information.
131
+ - **Python Support:** Uses AST-based parsing for Python files.
132
+ - **Markdown Documentation (TODO):** Detailed markdown generation for code documentation is currently in development.
133
+
134
+
@@ -0,0 +1,31 @@
1
+ cod8a/__init__.py,sha256=2G4dr62W9xvYmUDbiAB9Y3aMAHEioMOfgJckiD94pQs,54
2
+ cod8a/cli.py,sha256=0HmL65qdaQhwAiiutSIo8USgs6nvGgJLy17Wu44OD5w,3566
3
+ cod8a/cod8a.py,sha256=gsTYm5tYUf8PXKSWHeQ0VhXYnLSITvkVMgqWLstXdZA,63
4
+ cod8a/dotnet/CodeAnalysis/CodeAnalyzer.csproj,sha256=sVG2GYimAmjczJlgbruBqk9zTe5xcY2rj8LR0NuvT3E,368
5
+ cod8a/dotnet/CodeAnalysis/CodeAnalyzer.csproj.lscache,sha256=rCYzc0WoWAA78d4tXG8-1ocyE4TOaOCJzb-xH2uwd1E,8581
6
+ cod8a/dotnet/CodeAnalysis/models/FileStructure.cs,sha256=uZExZpv7gGXbgd2oSG8y3O_O_XY4ZQaZwLTlugczrl8,4862
7
+ cod8a/dotnet/CodeAnalysis/models/ProjectStructure.cs,sha256=sScQ0FVu9b4cSPyer6nMMnqG5X7jbejSK1x8adQ6PCs,715
8
+ cod8a/dotnet/CodeAnalysis/models/SolutionStructure.cs,sha256=Zlnm9fgpeqiXR0aG71W5UgP41rGip2NvHTuWgxD6NQw,789
9
+ cod8a/dotnet/CodeAnalysis/Parsers/BaseParser.cs,sha256=R8AYxrApeB4_MVNBdLT_XfHQMpBV7u5arQkFMvD8824,446
10
+ cod8a/dotnet/CodeAnalysis/Parsers/FileParser.cs,sha256=rTwz2qZjADEfQWnMonLjjH49Jx9X32F1HPB8eOuVchw,6884
11
+ cod8a/dotnet/CodeAnalysis/Parsers/ProjectParser.cs,sha256=jn80iSgfxK329X1jIzw_vbMb-qbWynPnxVf1pHU3_FM,1040
12
+ cod8a/dotnet/CodeAnalysis/Parsers/SolutionParser.cs,sha256=nPuCS-NjOwCckmlscs4AYN8eK16PYrKb99qdYoO5HEI,411
13
+ cod8a/dotnet/CodeAnalysis/Program.cs,sha256=yf85xUT_bRvzXndjVMG6hV8TmD4k-0lO1KToZezDHNY,2052
14
+ cod8a/dotnet/Test/CodeAnalyzerTest.csproj,sha256=lyoChgy51SvkE7RXn-Hr5l-mdqxHtoS42KpZfwivvtA,739
15
+ cod8a/dotnet/Test/Mermaid/ClassDiagramTest.cs,sha256=U8gCKh7MOJP2ZONQxFtroPzwKuMGWCjjbwuK8BI764I,2242
16
+ cod8a/enums/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ cod8a/enums/diagram_type.py,sha256=b5YQJNbHoGVsR9bKVneSWuo90pbS84h3bfMZGgnbLtk,240
18
+ cod8a/generators/mermaid/class_diagram.py,sha256=n7R14LkpYguAc0OUreTuCDi4hQ-E9zOkK9LggplcyK4,6518
19
+ cod8a/generators/mermaid/flowchart_diagram.py,sha256=PM59a1_Enx73SIfILIzeUl1VqOqGpjV8S6_u6ecjJmo,2037
20
+ cod8a/generators/mermaid/sequence_diagram.py,sha256=OZIdj8kPmNhup1rtp3hdFfT6MsHBsFaVDE8NS4vHrC0,3469
21
+ cod8a/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ cod8a/helpers/cli_helper.py,sha256=aBqHOEUms6EunAvUTT4u_6811VpxlFjZy8dhet1Ebu0,3316
23
+ cod8a/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ cod8a/models/models.py,sha256=nZXTTf4xEvWahOVSM1Bm8nfk1-QzVC_xHAU4GBX74GE,1135
25
+ cod8a/parsers/dotnet_parser.py,sha256=di-3lyccQIuRyg4HgW2YMI0_wPIBJYJU3fymDIatX24,4226
26
+ cod8a/parsers/python_parser.py,sha256=z2eRRgg6ajbZEJfGpWLI_Kc4-QNriils-75xqNFEKcw,5366
27
+ cod8a-0.1.0.dist-info/entry_points.txt,sha256=6p5la6hORzIXOUyNIgvEOrpd_WKAtzM2K6f9c1_KsVQ,65
28
+ cod8a-0.1.0.dist-info/licenses/LICENSE,sha256=V7c7VSiN0ITBMW_RLOzN65ku49BtA8tNYsbyS2427Vg,1099
29
+ cod8a-0.1.0.dist-info/METADATA,sha256=IES0VV_q6R8EhVeLRBIoVw-rtLbFe83uLftJnwF8vgc,3588
30
+ cod8a-0.1.0.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
31
+ cod8a-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.4.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ cde8a=cod8a.cli:doc_main
3
+ cod8a=cod8a.cli:main
4
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marietta Ngwe Akumbom
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.