deply 0.2.2__tar.gz → 0.2.4__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.
- {deply-0.2.2/deply.egg-info → deply-0.2.4}/PKG-INFO +1 -1
- deply-0.2.4/deply/__init__.py +1 -0
- {deply-0.2.2 → deply-0.2.4}/deply/main.py +41 -4
- deply-0.2.4/deply/models/violation.py +21 -0
- {deply-0.2.2 → deply-0.2.4}/deply/reports/formats/text_report.py +7 -3
- {deply-0.2.2 → deply-0.2.4}/deply/rules/dependency_rule.py +5 -5
- {deply-0.2.2 → deply-0.2.4/deply.egg-info}/PKG-INFO +1 -1
- deply-0.2.2/deply/__init__.py +0 -1
- deply-0.2.2/deply/models/violation.py +0 -12
- {deply-0.2.2 → deply-0.2.4}/LICENSE +0 -0
- {deply-0.2.2 → deply-0.2.4}/MANIFEST.in +0 -0
- {deply-0.2.2 → deply-0.2.4}/README.md +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/code_analyzer.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/collectors/__init__.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/collectors/base_collector.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/collectors/bool_collector.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/collectors/class_inherits_collector.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/collectors/class_name_regex_collector.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/collectors/collector_factory.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/collectors/decorator_usage_collector.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/collectors/directory_collector.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/collectors/file_regex_collector.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/config_parser.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/models/__init__.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/models/code_element.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/models/dependency.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/models/layer.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/reports/__init__.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/reports/formats/__init__.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/reports/report_generator.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/rules/__init__.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/rules/base_rule.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/rules/rule_factory.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/utils/__init__.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply/utils/ast_utils.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply.egg-info/SOURCES.txt +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply.egg-info/dependency_links.txt +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply.egg-info/entry_points.txt +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply.egg-info/requires.txt +0 -0
- {deply-0.2.2 → deply-0.2.4}/deply.egg-info/top_level.txt +0 -0
- {deply-0.2.2 → deply-0.2.4}/pyproject.toml +0 -0
- {deply-0.2.2 → deply-0.2.4}/requirements.txt +0 -0
- {deply-0.2.2 → deply-0.2.4}/setup.cfg +0 -0
- {deply-0.2.2 → deply-0.2.4}/setup.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/tests/__init__.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/tests/test.py +0 -0
- {deply-0.2.2 → deply-0.2.4}/tests/test_collectors.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.2.4'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import sys
|
|
3
|
+
import logging
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
from deply import __version__
|
|
@@ -16,13 +17,14 @@ from .rules.dependency_rule import DependencyRule
|
|
|
16
17
|
def main():
|
|
17
18
|
parser = argparse.ArgumentParser(prog="deply", description='Deply - A dependency analysis tool')
|
|
18
19
|
parser.add_argument('-V', '--version', action='store_true', help='Show the version number and exit')
|
|
20
|
+
parser.add_argument('-v', '--verbose', action='count', default=1, help='Increase output verbosity')
|
|
19
21
|
|
|
20
22
|
subparsers = parser.add_subparsers(dest='command', help='Sub-commands')
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
parser_analyze = subparsers.add_parser('analyze', help='Analyze the project dependencies')
|
|
24
|
+
parser_analyze.add_argument('--config', type=str, default="deply.yaml", help="Path to the configuration YAML file")
|
|
25
|
+
parser_analyze.add_argument('--report-format', type=str, choices=["text", "json", "html"], default="text",
|
|
24
26
|
help="Format of the output report")
|
|
25
|
-
|
|
27
|
+
parser_analyze.add_argument('--output', type=str, help="Output file for the report")
|
|
26
28
|
args = parser.parse_args()
|
|
27
29
|
|
|
28
30
|
if args.version:
|
|
@@ -30,23 +32,47 @@ def main():
|
|
|
30
32
|
print(f"deply {version}")
|
|
31
33
|
sys.exit(0)
|
|
32
34
|
|
|
35
|
+
# Set up logging
|
|
36
|
+
log_level = logging.WARNING # Default log level
|
|
37
|
+
if args.verbose == 1:
|
|
38
|
+
log_level = logging.INFO
|
|
39
|
+
elif args.verbose >= 2:
|
|
40
|
+
log_level = logging.DEBUG
|
|
41
|
+
|
|
42
|
+
logging.basicConfig(
|
|
43
|
+
level=log_level,
|
|
44
|
+
format='[%(asctime)s] [%(levelname)s] %(message)s',
|
|
45
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if args.command is None:
|
|
49
|
+
args = parser.parse_args(['analyze'] + sys.argv[1:])
|
|
50
|
+
|
|
51
|
+
logging.info("Starting Deply analysis...")
|
|
52
|
+
|
|
33
53
|
# Parse configuration
|
|
34
54
|
config_path = Path(args.config)
|
|
55
|
+
logging.info(f"Using configuration file: {config_path}")
|
|
35
56
|
config = ConfigParser(config_path).parse()
|
|
36
57
|
|
|
37
58
|
# Collect code elements and organize them by layers
|
|
38
59
|
layers: dict[str, Layer] = {}
|
|
39
60
|
code_element_to_layer: dict[CodeElement, str] = {}
|
|
40
61
|
|
|
62
|
+
logging.info("Collecting code elements for each layer...")
|
|
41
63
|
for layer_config in config['layers']:
|
|
42
64
|
layer_name = layer_config['name']
|
|
65
|
+
logging.debug(f"Processing layer: {layer_name}")
|
|
43
66
|
collectors = layer_config.get('collectors', [])
|
|
44
67
|
collected_elements: set[CodeElement] = set()
|
|
45
68
|
|
|
46
69
|
for collector_config in collectors:
|
|
70
|
+
collector_type = collector_config.get('type', 'unknown')
|
|
71
|
+
logging.debug(f"Using collector: {collector_type} for layer: {layer_name}")
|
|
47
72
|
collector = CollectorFactory.create(collector_config, config['paths'], config['exclude_files'])
|
|
48
73
|
collected = collector.collect()
|
|
49
74
|
collected_elements.update(collected)
|
|
75
|
+
logging.debug(f"Collected {len(collected)} elements for collector {collector_type}")
|
|
50
76
|
|
|
51
77
|
# Initialize Layer with collected code elements
|
|
52
78
|
layer = Layer(
|
|
@@ -55,26 +81,33 @@ def main():
|
|
|
55
81
|
dependencies=set()
|
|
56
82
|
)
|
|
57
83
|
layers[layer_name] = layer
|
|
84
|
+
logging.info(f"Layer '{layer_name}' collected {len(collected_elements)} code elements.")
|
|
58
85
|
|
|
59
86
|
# Map each code element to its layer
|
|
60
87
|
for element in collected_elements:
|
|
61
88
|
code_element_to_layer[element] = layer_name
|
|
62
89
|
|
|
63
90
|
# Analyze code to find dependencies
|
|
91
|
+
logging.info("Analyzing code to find dependencies...")
|
|
64
92
|
analyzer = CodeAnalyzer(set(code_element_to_layer.keys()))
|
|
65
93
|
dependencies: set[Dependency] = analyzer.analyze()
|
|
94
|
+
logging.info(f"Found {len(dependencies)} dependencies.")
|
|
66
95
|
|
|
67
96
|
# Assign dependencies to respective layers
|
|
97
|
+
logging.info("Assigning dependencies to layers...")
|
|
68
98
|
for dependency in dependencies:
|
|
69
99
|
source_layer_name = code_element_to_layer.get(dependency.code_element)
|
|
70
100
|
if source_layer_name and source_layer_name in layers:
|
|
71
101
|
layers[source_layer_name].dependencies.add(dependency)
|
|
72
102
|
|
|
73
103
|
# Apply rules
|
|
104
|
+
logging.info("Applying dependency rules...")
|
|
74
105
|
rule = DependencyRule(config['ruleset'])
|
|
75
106
|
violations = rule.check(layers=layers)
|
|
107
|
+
logging.info(f"Analysis complete. Found {len(violations)} violation(s).")
|
|
76
108
|
|
|
77
109
|
# Generate report
|
|
110
|
+
logging.info("Generating report...")
|
|
78
111
|
report_generator = ReportGenerator(violations)
|
|
79
112
|
report = report_generator.generate(format=args.report_format)
|
|
80
113
|
|
|
@@ -82,13 +115,17 @@ def main():
|
|
|
82
115
|
if args.output:
|
|
83
116
|
output_path = Path(args.output)
|
|
84
117
|
output_path.write_text(report)
|
|
118
|
+
logging.info(f"Report written to {output_path}")
|
|
85
119
|
else:
|
|
120
|
+
print("\n")
|
|
86
121
|
print(report)
|
|
87
122
|
|
|
88
123
|
# Exit with appropriate status
|
|
89
124
|
if violations:
|
|
125
|
+
print(f"\n\nTotal violation(s): {len(violations)}")
|
|
90
126
|
exit(1)
|
|
91
127
|
else:
|
|
128
|
+
logging.info("No violations detected.")
|
|
92
129
|
exit(0)
|
|
93
130
|
|
|
94
131
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass(frozen=True)
|
|
6
|
+
class Violation:
|
|
7
|
+
file: Path
|
|
8
|
+
element_name: str
|
|
9
|
+
element_type: str # 'class', 'function', or 'variable'
|
|
10
|
+
line: int
|
|
11
|
+
column: int
|
|
12
|
+
message: str
|
|
13
|
+
|
|
14
|
+
def __hash__(self):
|
|
15
|
+
return hash((self.file, self.line, self.column, self.message))
|
|
16
|
+
|
|
17
|
+
def __eq__(self, other):
|
|
18
|
+
return (
|
|
19
|
+
(self.file, self.line, self.column, self.message)
|
|
20
|
+
== (other.file, other.line, other.column, other.message)
|
|
21
|
+
)
|
|
@@ -6,9 +6,13 @@ class TextReport:
|
|
|
6
6
|
self.violations = violations
|
|
7
7
|
|
|
8
8
|
def generate(self) -> str:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
sorted_violations = sorted(
|
|
10
|
+
self.violations,
|
|
11
|
+
key=lambda v: (v.file, v.line, v.column)
|
|
12
|
+
)
|
|
13
|
+
lines = []
|
|
14
|
+
for violation in sorted_violations:
|
|
15
|
+
lines.append(
|
|
12
16
|
f"{violation.file}:{violation.line}:{violation.column} - {violation.message}"
|
|
13
17
|
# + f" ({violation.element_type} '{violation.element_name}')"
|
|
14
18
|
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# rules/dependency_rule.py
|
|
2
|
-
from typing import Dict, List
|
|
2
|
+
from typing import Dict, List, Set
|
|
3
3
|
|
|
4
4
|
from deply.models.code_element import CodeElement
|
|
5
5
|
from deply.models.layer import Layer
|
|
@@ -12,7 +12,7 @@ class DependencyRule(BaseRule):
|
|
|
12
12
|
self.ruleset = ruleset
|
|
13
13
|
|
|
14
14
|
def check(self, layers: Dict[str, Layer]) -> List[Violation]:
|
|
15
|
-
violations:
|
|
15
|
+
violations: Set[Violation] = set()
|
|
16
16
|
|
|
17
17
|
# Build a mapping from CodeElement to Layer name for quick lookup
|
|
18
18
|
code_element_to_layer: Dict[CodeElement, str] = {}
|
|
@@ -51,7 +51,7 @@ class DependencyRule(BaseRule):
|
|
|
51
51
|
column=dependency.column,
|
|
52
52
|
message=message,
|
|
53
53
|
)
|
|
54
|
-
violations.
|
|
54
|
+
violations.add(violation)
|
|
55
55
|
|
|
56
56
|
# Check against allowed layers if "allow" is specified
|
|
57
57
|
if allowed_layers and target_layer not in allowed_layers:
|
|
@@ -67,6 +67,6 @@ class DependencyRule(BaseRule):
|
|
|
67
67
|
column=dependency.column,
|
|
68
68
|
message=message,
|
|
69
69
|
)
|
|
70
|
-
violations.
|
|
70
|
+
violations.add(violation)
|
|
71
71
|
|
|
72
|
-
return violations
|
|
72
|
+
return list(violations)
|
deply-0.2.2/deply/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.2.2'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|