Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 25 additions & 19 deletions src/sphinx_codelinks/analyse/analyse.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from collections.abc import Generator
from dataclasses import dataclass
import json
import logging
from pathlib import Path
from typing import Any, TypedDict

Expand All @@ -26,14 +25,14 @@
OneLineCommentStyle,
SourceAnalyseConfig,
)
from sphinx_codelinks.logger import get_logger

# initialize logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# log to the console
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logger.addHandler(console)
logger = get_logger(__name__)


def _count(n: int, noun: str) -> str:
"""Format ``n noun`` with a naive (append-s) plural for progress summaries."""
return f"{n} {noun}" if n == 1 else f"{n} {noun}s"


class AnalyseWarningType(TypedDict):
Expand All @@ -57,7 +56,10 @@ class SourceAnalyse:
def __init__(
self,
analyse_config: SourceAnalyseConfig,
*,
name: str = "",
) -> None:
self.name = name
self.analyse_config = analyse_config
self.src_files: list[SourceFile] = []
self.src_comments: list[SourceComment] = []
Expand Down Expand Up @@ -110,9 +112,6 @@ def create_src_objects(self) -> None:
self.src_files.append(src_file)
self.src_comments.extend(src_comments)

logger.info(f"Source files loaded: {len(self.src_files)}")
logger.info(f"Source comments extracted: {len(self.src_comments)}")

def extract_marker(
self,
text: str,
Expand Down Expand Up @@ -353,13 +352,6 @@ def extract_marked_content(self) -> None:
if marked_rst:
self.marked_rst.append(marked_rst)

if self.analyse_config.get_need_id_refs:
logger.info(f"Need-id-refs extracted: {len(self.need_id_refs)}")
if self.analyse_config.get_oneline_needs:
logger.info(f"Oneline needs extracted: {len(self.oneline_needs)}")
if self.analyse_config.get_rst:
logger.info(f"Marked rst extracted: {len(self.marked_rst)}")

def merge_marked_content(self) -> None:
self.all_marked_content.extend(self.need_id_refs)
self.oneline_needs.sort(key=lambda x: x.source_map["start"]["row"])
Expand All @@ -378,9 +370,23 @@ def dump_marked_content(self, outdir: Path) -> None:
]
with output_path.open("w") as f:
json.dump(to_dump, f)
logger.info(f"Marked content dumped to {output_path}")

def run(self) -> None:
self.create_src_objects()
self.extract_marked_content()
self.merge_marked_content()
self._log_summary()

def _log_summary(self) -> None:
"""Emit a per-project marker (default-visible) plus a -v breakdown."""
label = f"codelinks [{self.name}]" if self.name else "codelinks"
logger.info(
f"{label}: {_count(len(self.src_files), 'file')}, "
f"{_count(len(self.all_marked_content), 'marker')}"
)
logger.debug(
f"{label}: {_count(len(self.src_comments), 'comment')}, "
f"{_count(len(self.oneline_needs), 'oneline need')}, "
f"{_count(len(self.need_id_refs), 'id-ref')}, "
f"{_count(len(self.marked_rst), 'marked-rst block')}"
)
9 changes: 0 additions & 9 deletions src/sphinx_codelinks/analyse/oneline_parser.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
from dataclasses import dataclass
from enum import Enum
import logging

from sphinx_codelinks.config import ESCAPE, UNIX_NEWLINE, OneLineCommentStyle

# initialize logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# log to the console
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logger.addHandler(console)


class WarningSubTypeEnum(str, Enum):
"""Enum for warning sub types."""
Expand Down
14 changes: 4 additions & 10 deletions src/sphinx_codelinks/analyse/projects.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
import logging
from pathlib import Path
from typing import cast

Expand All @@ -9,14 +8,9 @@
SourceAnalyse,
)
from sphinx_codelinks.config import CodeLinksConfig, CodeLinksProjectConfigType
from sphinx_codelinks.logger import get_logger

# initialize logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# log to the console
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logger.addHandler(console)
logger = get_logger(__name__)


class AnalyseProjects:
Expand All @@ -32,7 +26,7 @@ def __init__(self, codelink_config: CodeLinksConfig) -> None:

def run(self) -> None:
for project, config in self.projects_configs.items():
src_analyse = SourceAnalyse(config["analyse_config"])
src_analyse = SourceAnalyse(config["analyse_config"], name=project)
src_analyse.run()
self.projects_analyse[project] = src_analyse

Expand All @@ -46,7 +40,7 @@ def dump_markers(self) -> None:
}
with output_path.open("w") as f:
json.dump(to_dump, f)
logger.info(f"Marked content dumped to {output_path}")
logger.debug(f"codelinks: marked content dumped to {output_path}")

@classmethod
def load_warnings(cls, warnings_dir: Path) -> list[AnalyseWarning] | None:
Expand Down
50 changes: 34 additions & 16 deletions src/sphinx_codelinks/analyse/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from collections.abc import ByteString, Callable
import configparser
import logging
from pathlib import Path
from typing import TypedDict
from urllib.request import pathname2url
Expand All @@ -10,6 +9,7 @@
from tree_sitter import Node as TreeSitterNode

from sphinx_codelinks.config import UNIX_NEWLINE, CommentCategory
from sphinx_codelinks.logger import get_logger
from sphinx_codelinks.source_discover.config import CommentType

# Language-specific node types for scope detection
Expand All @@ -31,13 +31,7 @@
},
}

# initialize logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# log to the console
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logger.addHandler(console)
logger = get_logger(__name__)

GIT_HOST_URL_TEMPLATE = {
"github": "https://github.com/{owner}/{repo}/blob/{rev}/{path}#L{lineno}",
Expand Down Expand Up @@ -270,15 +264,23 @@ def locate_git_root(src_dir: Path) -> Path | None:
for parent in parents:
if (parent / ".git").exists() and (parent / ".git").is_dir():
return parent
logger.warning(f"git root is not found in the parent of {src_dir}")
logger.warning(
f"git root is not found in the parent of {src_dir}",
subtype="git_root",
location=str(src_dir),
)
return None


def get_remote_url(git_root: Path, remote_name: str = "origin") -> str | None:
"""Get remote url from .git/config."""
config_path = git_root / ".git" / "config"
if not config_path.exists():
logging.warning(f"{config_path} does not exist")
logger.warning(
f"{config_path} does not exist",
subtype="git_config",
location=str(config_path),
)
return None

config = configparser.ConfigParser(allow_no_value=True, strict=False)
Expand All @@ -287,24 +289,37 @@ def get_remote_url(git_root: Path, remote_name: str = "origin") -> str | None:
if section in config and "url" in config[section]:
url: str = config[section]["url"]
return url
logger.warning(f"remote-url is not found in {config_path}")
logger.warning(
f"remote-url is not found in {config_path}",
subtype="git_remote",
location=str(config_path),
)
return None


def get_current_rev(git_root: Path) -> str | None:
"""Get current commit rev from .git/HEAD."""
head_path = git_root / ".git" / "HEAD"
if not head_path.exists():
logging.warning(f"{head_path} does not exist")
logger.warning(
f"{head_path} does not exist",
subtype="git_head",
location=str(head_path),
)
return None
head_content = head_path.read_text().strip()
if not head_content.startswith("ref: "):
logging.warning(f"Expect starting with 'ref: ' in {head_path}")
return None
# Detached HEAD (e.g. CI checkouts): .git/HEAD holds the commit SHA
# directly, which is exactly the rev we want.
return head_content

ref_path = git_root / ".git" / head_content.split(":", 1)[1].strip()
if not ref_path.exists():
logging.warning(f"{ref_path} does not exist")
logger.warning(
f"{ref_path} does not exist",
subtype="git_ref",
location=str(ref_path),
)
return None
return ref_path.read_text().strip()

Expand All @@ -315,7 +330,10 @@ def form_https_url(
parsed_url = parse(git_url)
template = GIT_HOST_URL_TEMPLATE.get(parsed_url.platform)
if not template:
logging.warning(f"Unsupported Git host: {parsed_url.platform}")
logger.warning(
f"Unsupported Git host: {parsed_url.platform}",
subtype="git_host",
)
return git_url
https_url = template.format(
owner=parsed_url.owner,
Expand Down
7 changes: 5 additions & 2 deletions src/sphinx_codelinks/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
CodeLinksProjectConfigType,
generate_project_configs,
)
from sphinx_codelinks.logger import logger
from sphinx_codelinks.logger import configure_cli, logger
from sphinx_codelinks.needextend_write import MarkedObjType, convert_marked_content
from sphinx_codelinks.source_discover.config import (
CommentType,
Expand Down Expand Up @@ -88,9 +88,12 @@ def analyse( # noqa: PLR0912 # for CLI, so it needs the branches
exists=True,
),
] = None,
verbose: OptVerbose = False,
quiet: OptQuiet = False,
) -> None:
"""Analyse marked content in source code."""
# @CLI command to analyse source code and extract traceability markers, IMPL_CLI_ANALYZE, impl, [FE_CLI_ANALYZE]
configure_cli(verbose, quiet)

data: CodeLinksConfigType = load_config_from_toml(config)

Expand Down Expand Up @@ -291,7 +294,7 @@ def write_rst( # noqa: PLR0913 # for CLI, so it takes as many as it requires
quiet: OptQuiet = False,
) -> None:
"""Generate needextend.rst from the extracted obj in JSON."""
logger.configure(verbose, quiet)
configure_cli(verbose, quiet)
try:
with jsonpath.open("r") as f:
marked_content = json.load(f)
Expand Down
Loading
Loading