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.
Files changed (47) hide show
  1. {deply-0.2.2/deply.egg-info → deply-0.2.4}/PKG-INFO +1 -1
  2. deply-0.2.4/deply/__init__.py +1 -0
  3. {deply-0.2.2 → deply-0.2.4}/deply/main.py +41 -4
  4. deply-0.2.4/deply/models/violation.py +21 -0
  5. {deply-0.2.2 → deply-0.2.4}/deply/reports/formats/text_report.py +7 -3
  6. {deply-0.2.2 → deply-0.2.4}/deply/rules/dependency_rule.py +5 -5
  7. {deply-0.2.2 → deply-0.2.4/deply.egg-info}/PKG-INFO +1 -1
  8. deply-0.2.2/deply/__init__.py +0 -1
  9. deply-0.2.2/deply/models/violation.py +0 -12
  10. {deply-0.2.2 → deply-0.2.4}/LICENSE +0 -0
  11. {deply-0.2.2 → deply-0.2.4}/MANIFEST.in +0 -0
  12. {deply-0.2.2 → deply-0.2.4}/README.md +0 -0
  13. {deply-0.2.2 → deply-0.2.4}/deply/code_analyzer.py +0 -0
  14. {deply-0.2.2 → deply-0.2.4}/deply/collectors/__init__.py +0 -0
  15. {deply-0.2.2 → deply-0.2.4}/deply/collectors/base_collector.py +0 -0
  16. {deply-0.2.2 → deply-0.2.4}/deply/collectors/bool_collector.py +0 -0
  17. {deply-0.2.2 → deply-0.2.4}/deply/collectors/class_inherits_collector.py +0 -0
  18. {deply-0.2.2 → deply-0.2.4}/deply/collectors/class_name_regex_collector.py +0 -0
  19. {deply-0.2.2 → deply-0.2.4}/deply/collectors/collector_factory.py +0 -0
  20. {deply-0.2.2 → deply-0.2.4}/deply/collectors/decorator_usage_collector.py +0 -0
  21. {deply-0.2.2 → deply-0.2.4}/deply/collectors/directory_collector.py +0 -0
  22. {deply-0.2.2 → deply-0.2.4}/deply/collectors/file_regex_collector.py +0 -0
  23. {deply-0.2.2 → deply-0.2.4}/deply/config_parser.py +0 -0
  24. {deply-0.2.2 → deply-0.2.4}/deply/models/__init__.py +0 -0
  25. {deply-0.2.2 → deply-0.2.4}/deply/models/code_element.py +0 -0
  26. {deply-0.2.2 → deply-0.2.4}/deply/models/dependency.py +0 -0
  27. {deply-0.2.2 → deply-0.2.4}/deply/models/layer.py +0 -0
  28. {deply-0.2.2 → deply-0.2.4}/deply/reports/__init__.py +0 -0
  29. {deply-0.2.2 → deply-0.2.4}/deply/reports/formats/__init__.py +0 -0
  30. {deply-0.2.2 → deply-0.2.4}/deply/reports/report_generator.py +0 -0
  31. {deply-0.2.2 → deply-0.2.4}/deply/rules/__init__.py +0 -0
  32. {deply-0.2.2 → deply-0.2.4}/deply/rules/base_rule.py +0 -0
  33. {deply-0.2.2 → deply-0.2.4}/deply/rules/rule_factory.py +0 -0
  34. {deply-0.2.2 → deply-0.2.4}/deply/utils/__init__.py +0 -0
  35. {deply-0.2.2 → deply-0.2.4}/deply/utils/ast_utils.py +0 -0
  36. {deply-0.2.2 → deply-0.2.4}/deply.egg-info/SOURCES.txt +0 -0
  37. {deply-0.2.2 → deply-0.2.4}/deply.egg-info/dependency_links.txt +0 -0
  38. {deply-0.2.2 → deply-0.2.4}/deply.egg-info/entry_points.txt +0 -0
  39. {deply-0.2.2 → deply-0.2.4}/deply.egg-info/requires.txt +0 -0
  40. {deply-0.2.2 → deply-0.2.4}/deply.egg-info/top_level.txt +0 -0
  41. {deply-0.2.2 → deply-0.2.4}/pyproject.toml +0 -0
  42. {deply-0.2.2 → deply-0.2.4}/requirements.txt +0 -0
  43. {deply-0.2.2 → deply-0.2.4}/setup.cfg +0 -0
  44. {deply-0.2.2 → deply-0.2.4}/setup.py +0 -0
  45. {deply-0.2.2 → deply-0.2.4}/tests/__init__.py +0 -0
  46. {deply-0.2.2 → deply-0.2.4}/tests/test.py +0 -0
  47. {deply-0.2.2 → deply-0.2.4}/tests/test_collectors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deply
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: A tool to enforce architectural patterns in python projects
5
5
  Home-page: https://github.com/vashkatsi/deply
6
6
  Author: Archil Abuladze
@@ -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
- parser_analyse = subparsers.add_parser('analyze', help='Analyze the project dependencies')
22
- parser_analyse.add_argument('--config', type=str, default="deply.yaml", help="Path to the configuration YAML file")
23
- parser_analyse.add_argument('--report-format', type=str, choices=["text", "json", "html"], default="text",
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
- parser_analyse.add_argument('--output', type=str, help="Output file for the report")
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
- lines = set()
10
- for violation in self.violations:
11
- lines.add(
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: List[Violation] = []
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.append(violation)
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.append(violation)
70
+ violations.add(violation)
71
71
 
72
- return violations
72
+ return list(violations)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deply
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: A tool to enforce architectural patterns in python projects
5
5
  Home-page: https://github.com/vashkatsi/deply
6
6
  Author: Archil Abuladze
@@ -1 +0,0 @@
1
- __version__ = '0.2.2'
@@ -1,12 +0,0 @@
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
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