Skip to content

Commit 1db620e

Browse files
Refactor ABOUT file mapping in d2d for efficiency
Reference: #1004 Signed-off-by: Ayan Sinha Mahapatra <ayansmahapatra@gmail.com>
1 parent 074a53a commit 1db620e

2 files changed

Lines changed: 116 additions & 69 deletions

File tree

scanpipe/models.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,14 +1779,17 @@ def path_pattern(self, pattern):
17791779
"""Resources with a path that match the provided ``pattern``."""
17801780
return self.filter(path__regex=posix_regex_to_django_regex_lookup(pattern))
17811781

1782-
def path_patterns(self, patterns):
1782+
def path_patterns(self, patterns, ignore=False):
17831783
"""Resources with a path that match the provided ``pattern``."""
17841784
lookups = Q()
17851785
for resource_pattern in patterns:
17861786
lookups |= Q(
17871787
**{"path__regex": posix_regex_to_django_regex_lookup(resource_pattern)}
17881788
)
1789-
return self.filter(~lookups)
1789+
if ignore:
1790+
return self.filter(~lookups)
1791+
else:
1792+
return self.filter(lookups)
17901793

17911794
def has_directory_content_fingerprint(self):
17921795
"""

scanpipe/pipes/d2d.py

Lines changed: 111 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from collections import defaultdict
2525
from contextlib import suppress
2626
from pathlib import Path
27+
from re import match as regex_match
2728

2829
from django.contrib.postgres.aggregates.general import ArrayAgg
2930
from django.core.exceptions import MultipleObjectsReturned
@@ -43,6 +44,7 @@
4344
from scanpipe import pipes
4445
from scanpipe.models import CodebaseRelation
4546
from scanpipe.models import CodebaseResource
47+
from scanpipe.models import posix_regex_to_django_regex_lookup
4648
from scanpipe.pipes import LoopProgress
4749
from scanpipe.pipes import flag
4850
from scanpipe.pipes import get_resource_diff_ratio
@@ -774,87 +776,129 @@ def _map_javascript_resource(
774776
resource.update(status=flag.MAPPED)
775777

776778

777-
def _map_about_file_resource(project, about_file_resource, to_resources):
778-
about_file_location = str(about_file_resource.location_path)
779-
package_data = resolve.resolve_about_package(about_file_location)
780-
781-
error_message_details = {
782-
"path": about_file_resource.path,
783-
"package_data": package_data,
784-
}
785-
if not package_data:
786-
project.add_error(
787-
description="Cannot create package from ABOUT file",
788-
model="map_about_files",
789-
details=error_message_details,
790-
)
779+
def map_about_files(project, logger=None):
780+
"""Map ``from/`` .ABOUT files to their related ``to/`` resources."""
781+
project_resources = project.codebaseresources
782+
from_about_files = (
783+
project_resources.files().from_codebase().filter(extension=".ABOUT")
784+
)
785+
if not from_about_files.exists():
791786
return
792787

793-
files_pattern = package_data.get("filename")
794-
if not files_pattern:
795-
# Cannot map anything without the about_resource value.
796-
project.add_error(
797-
description="ABOUT file does not have about_resource",
798-
model="map_about_files",
799-
details=error_message_details,
800-
)
801-
return
788+
to_resources = project_resources.to_codebase().no_status()
802789

803-
ignored_resource_patterns = []
804-
codebase_resources = to_resources.path_pattern(pattern=files_pattern)
805-
if extra_data := package_data.get("extra_data"):
806-
ignored_resource_patterns = extra_data.get("ignored_resources")
807-
808-
# Fetch all resources that are covered by the .ABOUT file.
809-
if not codebase_resources:
810-
# If there's nothing to map on the ``to/`` do not create the package.
811-
project.add_warning(
812-
description=(
813-
"Resource paths listed at about_resource is not found"
814-
" in the to/ codebase"
815-
),
816-
model="map_about_files",
817-
details=error_message_details,
790+
if logger:
791+
logger(
792+
f"Mapping {from_about_files.count():,d} .ABOUT files found in the from/ "
793+
f"codebase."
818794
)
819-
return
820795

821-
# Ignore resources for paths in `ignored_resources` attribute
822-
if ignored_resource_patterns:
823-
codebase_resources = codebase_resources.path_patterns(
824-
patterns=ignored_resource_patterns
796+
regex_by_about_path = {}
797+
ignore_regex_by_about_path = {}
798+
about_resources_by_path = {}
799+
about_pkgdata_by_path = {}
800+
mapped_resources_by_aboutpath = {}
801+
802+
for about_file_resource in from_about_files:
803+
package_data = resolve.resolve_about_package(
804+
input_location=str(about_file_resource.location_path)
825805
)
806+
error_message_details = {
807+
"path": about_file_resource.path,
808+
"package_data": package_data,
809+
}
810+
if not package_data:
811+
project.add_error(
812+
description="Cannot create package from ABOUT file",
813+
model="map_about_files",
814+
details=error_message_details,
815+
)
816+
continue
826817

827-
# Create the Package using .ABOUT data and assigned related codebase_resources
828-
pipes.update_or_create_package(project, package_data, codebase_resources)
818+
about_pkgdata_by_path[about_file_resource.path] = package_data
819+
files_pattern = package_data.get("filename")
820+
if not files_pattern:
821+
# Cannot map anything without the about_resource value.
822+
project.add_error(
823+
description="ABOUT file does not have about_resource",
824+
model="map_about_files",
825+
details=error_message_details,
826+
)
827+
continue
828+
else:
829+
regex = posix_regex_to_django_regex_lookup(files_pattern)
830+
regex_by_about_path[about_file_resource.path] = regex
831+
832+
if extra_data := package_data.get("extra_data"):
833+
ignore_regex = []
834+
for pattern in extra_data.get("ignored_resources", []):
835+
ignore_regex.append(posix_regex_to_django_regex_lookup(pattern))
836+
if ignore_regex:
837+
ignore_regex_by_about_path[about_file_resource.path] = ignore_regex
838+
839+
about_resources_by_path[about_file_resource.path] = about_file_resource
840+
mapped_resources_by_aboutpath[about_file_resource.path] = []
841+
842+
for to_resource in to_resources:
843+
resource_matched = False
844+
for about_path, regex_pattern in regex_by_about_path.items():
845+
if regex_match(pattern=regex_pattern, string=to_resource.path):
846+
resource_matched = True
847+
break
848+
849+
if not resource_matched:
850+
continue
829851

830-
# Map the .ABOUT file resource to all related resources in the ``to/`` side.
831-
for to_resource in codebase_resources:
832-
pipes.make_relation(
833-
from_resource=about_file_resource,
834-
to_resource=to_resource,
835-
map_type="about_file",
836-
)
852+
ignore_regex_patterns = ignore_regex_by_about_path.get(about_path, [])
853+
ignore_resource = False
854+
for ignore_regex_pattern in ignore_regex_patterns:
855+
if regex_match(pattern=ignore_regex_pattern, string=to_resource.path):
856+
ignore_resource = True
857+
break
837858

838-
codebase_resources.update(status=flag.ABOUT_MAPPED)
839-
about_file_resource.update(status=flag.ABOUT_MAPPED)
859+
if ignore_resource:
860+
continue
840861

862+
mapped_resources_about = mapped_resources_by_aboutpath.get(about_path)
863+
if mapped_resources_about:
864+
mapped_resources_about.append(to_resource)
865+
else:
866+
mapped_resources_by_aboutpath[about_path] = [to_resource]
867+
to_resource.update(status=flag.ABOUT_MAPPED)
868+
869+
for about_path, mapped_resources in mapped_resources_by_aboutpath.items():
870+
about_file_resource = about_resources_by_path[about_path]
871+
package_data = about_pkgdata_by_path[about_file_resource.path]
872+
873+
if not mapped_resources:
874+
error_message_details = {
875+
"path": about_file_resource.path,
876+
"package_data": package_data,
877+
}
878+
project.add_warning(
879+
description=(
880+
"Resource paths listed at about_resource is not found"
881+
" in the to/ codebase"
882+
),
883+
model="map_about_files",
884+
details=error_message_details,
885+
)
886+
continue
841887

842-
def map_about_files(project, logger=None):
843-
"""Map ``from/`` .ABOUT files to their related ``to/`` resources."""
844-
project_resources = project.codebaseresources
845-
from_files = project_resources.files().from_codebase()
846-
from_about_files = from_files.filter(extension=".ABOUT")
847-
to_resources = project_resources.to_codebase()
888+
# Create the Package using .ABOUT data and assigned related codebase_resources
889+
pipes.update_or_create_package(project, package_data, mapped_resources)
848890

849-
if logger:
850-
logger(
851-
f"Mapping {from_about_files.count():,d} .ABOUT files found in the from/ "
852-
f"codebase."
853-
)
891+
# Map the .ABOUT file resource to all related resources in the ``to/`` side.
892+
for mapped_resource in mapped_resources:
893+
pipes.make_relation(
894+
from_resource=about_file_resource,
895+
to_resource=mapped_resource,
896+
map_type="about_file",
897+
)
854898

855-
for about_file_resource in from_about_files:
856-
_map_about_file_resource(project, about_file_resource, to_resources)
899+
about_file_resource.update(status=flag.ABOUT_MAPPED)
857900

901+
for about_file_resource in about_resources_by_path.values():
858902
about_file_companions = (
859903
about_file_resource.siblings()
860904
.filter(name__startswith=about_file_resource.name_without_extension)

0 commit comments

Comments
 (0)