Skip to content

Commit f669220

Browse files
committed
doc: application.py
1 parent 2d20d47 commit f669220

File tree

1 file changed

+127
-12
lines changed

1 file changed

+127
-12
lines changed

gitevo/application.py

Lines changed: 127 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,46 @@
1515
from gitevo.utils import is_git_dir, stdout_msg, stdout_link, as_str, aggregate_stat, ensure_file_extension
1616
from gitevo.exceptions import *
1717

18+
"""
19+
This module contains the main GitEvo classes: GitEvo, ParsedCommit, and ParsedFile.
20+
It provides functionality to analyze Git repositories, compute metrics, and generate reports.
21+
22+
See: https://github.com/andrehora/gitevo/tree/main/examples
23+
24+
Example:
25+
26+
from gitevo import GitEvo, ParsedCommit
27+
28+
git_url = 'https://github.com/pallets/flask'
29+
evo = GitEvo(repo=git_url, extension='.py')
30+
31+
@evo.metric('Lines of code (LOC)')
32+
def loc(commit: ParsedCommit):
33+
return commit.loc
34+
35+
@evo.metric('Python files')
36+
def python_files(commit: ParsedCommit):
37+
return len(commit.parsed_files)
38+
39+
@evo.metric('Test files')
40+
def test_files(commit: ParsedCommit):
41+
test_files = [f for f in commit.parsed_files if 'test_' in f.name.lower()]
42+
return len(test_files)
43+
44+
@evo.metric('LOC / Python files')
45+
def loc_per_file(commit: ParsedCommit):
46+
python_files = len(commit.parsed_files)
47+
if python_files == 0: return 0
48+
return commit.loc / python_files
49+
50+
evo.run()
51+
52+
"""
1853

1954
class GitEvo:
2055

2156
"""
22-
GitEvo app class, the main entrypoint to use GitEvo.
57+
GitEvo main class, the entrypoint to use GitEvo.
2358
2459
Args:
2560
repo (str): Git repository URL or local path
@@ -182,7 +217,7 @@ def _compute_metrics(self, git_repo: str) -> GitEvoResult:
182217
project_commits.add(selected_date)
183218

184219
# Chache parsed commits for each file extension, eg, .py, .js, .java, etc
185-
parsed_commits = ParsedCommitCache(commit, self._all_file_extensions())
220+
parsed_commits = _ParsedCommitCache(commit, self._all_file_extensions())
186221
print(f'- Date: {selected_date}, commit: {commit.hash[0:10]}, files: {parsed_commits.file_stats()}')
187222

188223
# Iterate on each metric
@@ -298,14 +333,23 @@ def _export_csv(self, result: GitEvoResult):
298333

299334
class ParsedFile:
300335

336+
"""
337+
Represents a parsed file in a commit, containing its name, path, tree-sitter nodes, and lines of code (LOC).
338+
"""
339+
301340
def __init__(self, name: str, path: str, nodes: list[Node], loc: int):
302341
self.name = name
303342
self.path = path
304343
self.nodes = nodes
305344
self.loc = loc
306345

307346
class ParsedCommit:
308-
347+
348+
"""
349+
Represents a parsed commit in a repository, containing its hash, date, file extension, parsed files,
350+
tree-sitter nodes, and lines of code (LOC).
351+
"""
352+
309353
def __init__(self, hash: str, date: datetime, file_extension: str, parsed_files: list[ParsedFile]):
310354
self.hash = hash
311355
self.date = date
@@ -316,27 +360,63 @@ def __init__(self, hash: str, date: datetime, file_extension: str, parsed_files:
316360

317361
@property
318362
def parsed_files(self) -> list[ParsedFile]:
363+
"""
364+
Returns the list of parsed files in the commit.
365+
Returns:
366+
list[ParsedFile]: The list of parsed files.
367+
"""
319368
return self._parsed_files
320369

321370
@property
322371
def nodes(self) -> list[Node]:
372+
"""
373+
Returns the list of tree-sitter nodes in the commit.
374+
Returns:
375+
list[Node]: The list of nodes.
376+
"""
323377
if self._nodes is None:
324378
self._nodes = [node for file in self.parsed_files for node in file.nodes]
325379
return self._nodes
326380

327-
def count_nodes(self, node_types: str | list[str] | None = None) -> int:
328-
if node_types is None:
329-
return len(self.nodes)
330-
return len(self.find_nodes_by_type(node_types))
331-
332381
@property
333382
def loc(self) -> int:
383+
"""
384+
Returns the total lines of code (LOC) in the commit.
385+
Returns:
386+
int: The total LOC.
387+
"""
334388
if self._loc is None:
335389
self._loc = sum([file.loc for file in self.parsed_files])
336390
return self._loc
337-
338-
def loc_by_type(self, node_type: str, aggregate: str = 'median') -> int | float | list[int]:
339391

392+
def count_nodes(self, node_types: str | list[str] | None = None) -> int:
393+
"""
394+
Counts the number of tree-sitter nodes in the commit, optionally filtered by node types.
395+
396+
See node types examples at:
397+
https://github.com/tree-sitter/tree-sitter-python/blob/master/src/node-types.json
398+
https://github.com/tree-sitter/tree-sitter-javascript/blob/master/src/node-types.json
399+
https://github.com/tree-sitter/tree-sitter-typescript/blob/master/typescript/src/node-types.json
400+
https://github.com/tree-sitter/tree-sitter-java/blob/master/src/node-types.json
401+
402+
Returns:
403+
int: The count of nodes.
404+
"""
405+
if node_types is None:
406+
return len(self.nodes)
407+
return len(self.find_nodes_by_type(node_types))
408+
409+
def loc_by_type(self, node_type: str, aggregate: str = 'median') -> int | float:
410+
"""
411+
Calculates the lines of code (LOC) for nodes of a specific type, using the specified aggregation method.
412+
Args:
413+
node_type (str): The type of node to calculate LOC for.
414+
aggregate (str): The aggregation method to use ('median', 'mean', 'mode').
415+
Returns:
416+
int | float: The aggregated LOC value.
417+
Raises:
418+
BadLOCAggregate: If the aggregate method is invalid.
419+
"""
340420
if aggregate is not None and aggregate not in ['median', 'mean', 'mode']:
341421
raise BadLOCAggregate(f'LOC aggregate should be median, mean, or mode')
342422

@@ -348,6 +428,13 @@ def loc_by_type(self, node_type: str, aggregate: str = 'median') -> int | float
348428
return aggregate_stat(locs, aggregate)
349429

350430
def find_node_types(self, node_types: str | list[str] = None) -> list[str]:
431+
"""
432+
Finds the types of tree-sitter nodes in the commit, optionally filtered by node types.
433+
Args:
434+
node_types (str | list[str], optional): The node types to filter by.
435+
Returns:
436+
list[str]: The list of node types.
437+
"""
351438
if node_types is None:
352439
return [node.type for node in self.nodes]
353440

@@ -357,16 +444,36 @@ def find_node_types(self, node_types: str | list[str] = None) -> list[str]:
357444
return [node.type for node in self.nodes if node.type in node_types]
358445

359446
def find_nodes_by_type(self, node_types: str | list[str]) -> list[Node]:
360-
447+
"""
448+
Finds the tree-sitter nodes in the commit by their types.
449+
Args:
450+
node_types (str | list[str]): The node types to filter by.
451+
Returns:
452+
list[Node]: The list of nodes.
453+
"""
361454
if isinstance(node_types, str):
362455
node_types = [node_types]
363456

364457
return [node for node in self.nodes if node.type in node_types]
365458

366459
def named_children_for(self, node: Node) -> list[Node]:
460+
"""
461+
Returns the named children of a given tree-sitter node.
462+
Args:
463+
node (Node): The tree-sitter node.
464+
Returns:
465+
list[Node]: The list of named child nodes.
466+
"""
367467
return [each for each in node.children if each.is_named]
368468

369469
def descendant_nodes_for(self, node: Node) -> list[Node]:
470+
"""
471+
Returns the descendant nodes of a given tree-sitter node.
472+
Args:
473+
node (Node): The tree-sitter node.
474+
Returns:
475+
list[Node]: The list of descendant nodes.
476+
"""
370477
descendants = []
371478
def traverse_node(current_node):
372479
descendants.append(current_node)
@@ -377,13 +484,21 @@ def traverse_node(current_node):
377484
return descendants
378485

379486
def descendant_node_by_field_name(self, node: Node, name: str) -> Node | None:
487+
"""
488+
Finds a descendant node of a given tree-sitter node by its field name.
489+
Args:
490+
node (Node): The tree-sitter node.
491+
name (str): The field name to search for.
492+
Returns:
493+
Node | None: The descendant node with the specified field name, or None if not found.
494+
"""
380495
for desc_node in self.descendant_nodes_for(node):
381496
target_node = desc_node.child_by_field_name(name)
382497
if target_node is not None:
383498
return target_node
384499
return None
385500

386-
class ParsedCommitCache:
501+
class _ParsedCommitCache:
387502

388503
def __init__(self, commit: Commit, file_extensions: list[str]):
389504
self.commit = commit

0 commit comments

Comments
 (0)