From 32df55dd0d0d21f8d019c3b0875be0ef55ab22ba Mon Sep 17 00:00:00 2001 From: Ryan Brooks Date: Thu, 19 Jun 2025 20:39:46 -0700 Subject: [PATCH 1/2] Minor insight tweak --- src/launchpad/analyzers/apple.py | 5 ++-- src/launchpad/insights/common.py | 49 +++---------------------------- src/launchpad/insights/insight.py | 35 ++++++++++++++++++++++ 3 files changed, 42 insertions(+), 47 deletions(-) create mode 100644 src/launchpad/insights/insight.py diff --git a/src/launchpad/analyzers/apple.py b/src/launchpad/analyzers/apple.py index 0b4c01c0..bf707822 100644 --- a/src/launchpad/analyzers/apple.py +++ b/src/launchpad/analyzers/apple.py @@ -11,7 +11,8 @@ import lief from ..artifacts import AppleArtifact, ZippedXCArchive -from ..insights.common import DuplicateFilesInsight, InsightsInput +from ..insights.common import DuplicateFilesInsight +from ..insights.insight import InsightsInput from ..models import AppleAnalysisResults, AppleAppInfo, FileAnalysis, FileInfo, MachOBinaryAnalysis from ..models.apple import AppleInsightResults from ..models.treemap import FILE_TYPE_TO_TREEMAP_TYPE, TreemapType @@ -128,7 +129,7 @@ def analyze(self, artifact: AppleArtifact) -> AppleAnalysisResults: treemap=treemap, ) insights = AppleInsightResults( - duplicate_files=DuplicateFilesInsight().__call__(insights_input), + duplicate_files=DuplicateFilesInsight().get_results(insights_input), ) results = AppleAnalysisResults( diff --git a/src/launchpad/insights/common.py b/src/launchpad/insights/common.py index 6a2c5044..83e7c097 100644 --- a/src/launchpad/insights/common.py +++ b/src/launchpad/insights/common.py @@ -3,57 +3,16 @@ from __future__ import annotations from collections import defaultdict -from dataclasses import dataclass -from typing import Dict, List, Protocol, TypeVar +from typing import Dict, List -from launchpad.models.common import FileAnalysis, FileInfo +from launchpad.insights.insight import Insight, InsightsInput +from launchpad.models.common import FileInfo from launchpad.models.insights import DuplicateFilesInsightResult -from launchpad.models.treemap import TreemapResults - -from ..models.apple import AppleAppInfo, MachOBinaryAnalysis - -T_co = TypeVar("T_co", covariant=True) - - -@dataclass -class InsightsInput: - app_info: AppleAppInfo - file_analysis: FileAnalysis - treemap: TreemapResults | None - binary_analysis: List[MachOBinaryAnalysis] - - -class Insight(Protocol[T_co]): - """Protocol for insight functions. - - Insights are functions that take analysis results and return typed insight results. - All data needed for the insight must be collected during the main analysis phase. - """ - - def __call__(self, input: InsightsInput) -> T_co: - """Generate insights from analysis results. - - Args: - results: The analysis results to generate insights from - - Returns: - Typed insight results - """ - ... class DuplicateFilesInsight(Insight[DuplicateFilesInsightResult]): - """Insight for duplicate files analysis.""" - - def __call__(self, input: InsightsInput) -> DuplicateFilesInsightResult: - """Generate insights about duplicate files. - - Args: - results: The analysis results to generate insights from - Returns: - Duplicate files insight results - """ + def get_results(self, input: InsightsInput) -> DuplicateFilesInsightResult: # Group files by hash files_by_hash: Dict[str, List[FileInfo]] = defaultdict(list) for file in input.file_analysis.files: diff --git a/src/launchpad/insights/insight.py b/src/launchpad/insights/insight.py new file mode 100644 index 00000000..8923f5b1 --- /dev/null +++ b/src/launchpad/insights/insight.py @@ -0,0 +1,35 @@ +from dataclasses import dataclass +from typing import Protocol, TypeVar + +from ..models.apple import AppleAppInfo, MachOBinaryAnalysis +from ..models.common import FileAnalysis +from ..models.treemap import TreemapResults + +T_co = TypeVar("T_co", covariant=True) + + +@dataclass +class InsightsInput: + app_info: AppleAppInfo + file_analysis: FileAnalysis + treemap: TreemapResults | None + binary_analysis: list[MachOBinaryAnalysis] + + +class Insight(Protocol[T_co]): + """Protocol for insight functions. + + Insights are functions that take analysis results and return typed insight results. + All data needed for the insight must be collected during the main analysis phase. + """ + + def get_results(self, input: InsightsInput) -> T_co: + """Generate insights from analysis results. + + Args: + results: The analysis results to generate insights from + + Returns: + Typed insight results + """ + raise NotImplementedError("Not implemented") From 94c0afba7fd099704d5f27215b2fd41c425c2390 Mon Sep 17 00:00:00 2001 From: Ryan Brooks Date: Fri, 20 Jun 2025 09:55:35 -0700 Subject: [PATCH 2/2] generate --- src/launchpad/analyzers/apple.py | 2 +- src/launchpad/insights/common.py | 2 +- src/launchpad/insights/insight.py | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/launchpad/analyzers/apple.py b/src/launchpad/analyzers/apple.py index bf707822..ef90bfd9 100644 --- a/src/launchpad/analyzers/apple.py +++ b/src/launchpad/analyzers/apple.py @@ -129,7 +129,7 @@ def analyze(self, artifact: AppleArtifact) -> AppleAnalysisResults: treemap=treemap, ) insights = AppleInsightResults( - duplicate_files=DuplicateFilesInsight().get_results(insights_input), + duplicate_files=DuplicateFilesInsight().generate(insights_input), ) results = AppleAnalysisResults( diff --git a/src/launchpad/insights/common.py b/src/launchpad/insights/common.py index 83e7c097..379071d7 100644 --- a/src/launchpad/insights/common.py +++ b/src/launchpad/insights/common.py @@ -12,7 +12,7 @@ class DuplicateFilesInsight(Insight[DuplicateFilesInsightResult]): - def get_results(self, input: InsightsInput) -> DuplicateFilesInsightResult: + def generate(self, input: InsightsInput) -> DuplicateFilesInsightResult: # Group files by hash files_by_hash: Dict[str, List[FileInfo]] = defaultdict(list) for file in input.file_analysis.files: diff --git a/src/launchpad/insights/insight.py b/src/launchpad/insights/insight.py index 8923f5b1..58ba53c1 100644 --- a/src/launchpad/insights/insight.py +++ b/src/launchpad/insights/insight.py @@ -1,3 +1,4 @@ +from abc import abstractmethod from dataclasses import dataclass from typing import Protocol, TypeVar @@ -23,7 +24,8 @@ class Insight(Protocol[T_co]): All data needed for the insight must be collected during the main analysis phase. """ - def get_results(self, input: InsightsInput) -> T_co: + @abstractmethod + def generate(self, input: InsightsInput) -> T_co: """Generate insights from analysis results. Args: