-
Notifications
You must be signed in to change notification settings - Fork 5
Introduce superproject class #915
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds a SuperProject abstraction, renames VCS → SubProject (GitSubProject/SvnSubProject), moves SVN utilities into Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Cmd as Command (check/update/diff/report)
participant SP as SuperProject
participant M as Manifest
participant Factory as Project factory
participant PJ as SubProject
participant Repo as Repo helpers (SvnRepo/Git helpers)
Cmd->>SP: instantiate SuperProject()
SP-->>Cmd: root_directory, manifest
Cmd->>M: selected_projects(...) %% via SP.manifest
loop per project
Cmd->>Factory: make(project_entry)
Factory-->>PJ: SubProject instance
Cmd->>PJ: check()/metadata_revision()/current_revision()
Cmd->>PJ: diff/update/fetch request
alt SVN backend
PJ->>Repo: SvnRepo / SvnRemote (export/create_diff/files_in_path)
Repo-->>PJ: patches/files/info
else Git backend
PJ->>Repo: Git helpers (diff/export)
Repo-->>PJ: patches/files/info
end
PJ-->>Cmd: result / patch (written under SP.root_directory)
end
Note over Cmd,SP: Working directory anchored at SuperProject.root_directory
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (7)
dfetch/vcs/svn.py (2)
17-22: Consider adding error handling for version parsing.The function assumes
svn --versionoutput follows a specific format. If the format is unexpected or the split doesn't produce the expected results, this could raise an exception. Consider adding validation or error handling.Additionally, the
str()conversions on line 22 appear redundant sincesplit()already returns strings.🔎 Suggested improvements
def get_svn_version() -> tuple[str, str]: """Get the name and version of svn.""" result = run_on_cmdline(logger, ["svn", "--version", "--non-interactive"]) first_line = result.stdout.decode().split("\n")[0] - tool, version = first_line.replace(",", "").split("version", maxsplit=1) - return (str(tool), str(version)) + parts = first_line.replace(",", "").split("version", maxsplit=1) + if len(parts) != 2: + raise RuntimeError(f"Unexpected svn version format: {first_line}") + tool, version = parts + return (tool.strip(), version.strip())
248-256: Consider documenting or validating therevparameter format.The
rev.split(" ")approach on line 254 assumes the revision parameter is provided in a specific format (e.g., "-r 123"). This works but could be fragile if the format changes. Consider documenting the expected format or adding validation.dfetch/project/git.py (1)
76-79: Update comment to use "SubProject" terminology.The docstring still references "VCS" but should be updated to "SubProject" for consistency with the refactoring.
🔎 Proposed fix
@staticmethod def revision_is_enough() -> bool: - """See if this VCS can uniquely distinguish branch with revision only.""" + """See if this SubProject can uniquely distinguish branch with revision only.""" return Truedfetch/project/subproject.py (1)
260-263: Update comment to use "SubProject" terminology.The docstring still references "VCS" but should be updated to "SubProject" for consistency with the refactoring.
🔎 Proposed fix
@staticmethod @abstractmethod def revision_is_enough() -> bool: - """See if this VCS can uniquely distinguish branch with revision only.""" + """See if this SubProject can uniquely distinguish branch with revision only."""dfetch/project/__init__.py (1)
11-22: Consider updating error message for terminology consistency.The function signature and docstring correctly use the new
SubProjectterminology. However, the error message on line 22 still references "vcs type" which is inconsistent with the new naming convention.🔎 Proposed fix
- raise RuntimeError("vcs type unsupported") + raise RuntimeError("subproject type unsupported")tests/test_svn.py (1)
225-236: Minor: Consider renaming fixture and test for clarity.The fixture
svn_subprojectand testtest_svn_repo_nametestSvnSubProject, notSvnRepo. The naming could be slightly confusing since there's also ansvn_repofixture for the actualSvnRepoclass. Consider renamingtest_svn_repo_nametotest_svn_subproject_namefor consistency.🔎 Proposed fix
-def test_svn_repo_name(svn_subproject): - assert svn_subproject.NAME == "svn" +def test_svn_subproject_name(svn_subproject): + assert svn_subproject.NAME == "svn"dfetch/project/svn.py (1)
130-130: Static methods called via instance - works but unconventional.Several
SvnRepostatic methods (export,files_in_path,get_last_changed_revision,untracked_files) are being called viaself._repoinstance. While this works in Python, it's unconventional and could be confusing. Consider calling via the class name for static methods, or converting them to instance methods if they should useself._path.For example, line 130 could be:
- self._repo.export(complete_path, rev_arg, self.local_path) + SvnRepo.export(complete_path, rev_arg, self.local_path)Alternatively, if these methods should operate on the repo's path, consider making them instance methods that use
self._path.Also applies to: 149-149, 177-177, 186-186, 190-190, 203-203
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (25)
CHANGELOG.rstdfetch/commands/check.pydfetch/commands/common.pydfetch/commands/diff.pydfetch/commands/environment.pydfetch/commands/freeze.pydfetch/commands/import_.pydfetch/commands/report.pydfetch/commands/update.pydfetch/manifest/manifest.pydfetch/project/__init__.pydfetch/project/git.pydfetch/project/subproject.pydfetch/project/superproject.pydfetch/project/svn.pydfetch/vcs/svn.pydoc/getting_started.rstdoc/internal.rstdoc/static/uml/c3_dfetch_components_project.pumltests/test_check.pytests/test_import.pytests/test_report.pytests/test_subproject.pytests/test_svn.pytests/test_update.py
💤 Files with no reviewable changes (1)
- dfetch/manifest/manifest.py
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:20.972Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
📚 Learning: 2025-12-31T10:55:20.972Z
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:20.972Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
Applied to files:
doc/getting_started.rstdoc/static/uml/c3_dfetch_components_project.pumldfetch/project/superproject.pydoc/internal.rstdfetch/commands/check.pytests/test_subproject.pydfetch/commands/environment.pydfetch/commands/common.pydfetch/commands/diff.pydfetch/project/git.pydfetch/commands/update.pytests/test_check.pydfetch/commands/report.pydfetch/project/__init__.pydfetch/commands/freeze.pydfetch/commands/import_.pydfetch/project/subproject.py
📚 Learning: 2026-01-01T08:56:36.734Z
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 898
File: pyproject.toml:204-206
Timestamp: 2026-01-01T08:56:36.734Z
Learning: In dfetch's Nuitka configuration, `include-module="infer_license.licenses"` is required because this submodule contains license text files (resources) that infer_license needs at runtime. While Nuitka auto-discovers other infer_license modules like `infer_license.types` and `infer_license.api`, it does not auto-discover resource directories, so they must be explicitly included.
Applied to files:
dfetch/commands/report.py
📚 Learning: 2026-01-02T22:46:45.589Z
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 904
File: script/package.py:10-18
Timestamp: 2026-01-02T22:46:45.589Z
Learning: In dfetch's script/package.py, importing dfetch.__version__ directly is acceptable and intentional; if dfetch is not installed in the environment, the packaging job should fail explicitly rather than handling the import error gracefully.
Applied to files:
dfetch/commands/freeze.py
🧬 Code graph analysis (13)
dfetch/commands/check.py (4)
dfetch/project/superproject.py (3)
SuperProject(20-46)manifest(44-46)root_directory(39-41)dfetch/util/util.py (2)
in_directory(66-75)catch_runtime_exceptions(79-87)dfetch/manifest/manifest.py (3)
selected_projects(270-284)projects(266-268)path(251-253)dfetch/commands/common.py (1)
files_to_ignore(71-79)
tests/test_subproject.py (1)
dfetch/project/subproject.py (3)
check_wanted_with_local(47-77)_are_there_local_changes(349-361)_running_in_ci(42-45)
dfetch/commands/environment.py (2)
dfetch/project/svn.py (1)
list_tool_info(62-71)dfetch/project/git.py (1)
list_tool_info(82-91)
tests/test_svn.py (1)
dfetch/vcs/svn.py (5)
External(25-35)SvnRemote(38-67)is_svn(45-58)is_svn(82-89)get_info_from_target(175-194)
dfetch/commands/common.py (1)
dfetch/vcs/svn.py (3)
SvnRepo(70-310)is_svn(45-58)is_svn(82-89)
dfetch/commands/diff.py (5)
dfetch/project/git.py (1)
GitSubProject(19-145)dfetch/project/subproject.py (1)
SubProject(24-394)dfetch/project/superproject.py (3)
SuperProject(20-46)root_directory(39-41)manifest(44-46)dfetch/project/svn.py (1)
SvnSubProject(25-212)dfetch/util/util.py (2)
catch_runtime_exceptions(79-87)in_directory(66-75)
dfetch/project/git.py (2)
dfetch/project/subproject.py (2)
SubProject(24-394)_log_tool(223-224)dfetch/vcs/git.py (1)
get_git_version(32-36)
dfetch/commands/update.py (5)
dfetch/project/superproject.py (3)
SuperProject(20-46)manifest(44-46)root_directory(39-41)dfetch/manifest/manifest.py (3)
path(251-253)projects(266-268)selected_projects(270-284)dfetch/manifest/project.py (1)
destination(405-407)dfetch/util/util.py (1)
in_directory(66-75)dfetch/commands/common.py (1)
check_child_manifests(17-36)
dfetch/project/svn.py (1)
dfetch/vcs/svn.py (12)
SvnRemote(38-67)SvnRepo(70-310)get_svn_version(17-22)is_svn(45-58)is_svn(82-89)list_of_tags(60-67)export(249-256)get_info_from_target(175-194)files_in_path(259-268)get_last_changed_revision(197-224)create_diff(288-310)untracked_files(227-246)
tests/test_check.py (5)
dfetch/manifest/project.py (1)
patch(410-412)dfetch/project/superproject.py (2)
manifest(44-46)root_directory(39-41)dfetch/reporting/reporter.py (1)
manifest(24-26)tests/manifest_mock.py (1)
mock_manifest(9-22)dfetch/manifest/manifest.py (1)
projects(266-268)
dfetch/commands/report.py (4)
dfetch/project/subproject.py (2)
SubProject(24-394)is_license_file(389-394)dfetch/project/superproject.py (3)
SuperProject(20-46)root_directory(39-41)manifest(44-46)dfetch/util/util.py (1)
in_directory(66-75)dfetch/manifest/manifest.py (2)
selected_projects(270-284)projects(266-268)
dfetch/commands/import_.py (1)
dfetch/vcs/svn.py (2)
is_svn(45-58)is_svn(82-89)
tests/test_update.py (4)
dfetch/manifest/project.py (1)
patch(410-412)dfetch/project/superproject.py (2)
manifest(44-46)root_directory(39-41)tests/manifest_mock.py (1)
mock_manifest(9-22)dfetch/manifest/manifest.py (1)
projects(266-268)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
- GitHub Check: Publish Python distribution 📦 to TestPyPI
- GitHub Check: build / build (macos-latest)
- GitHub Check: build / build (windows-latest)
- GitHub Check: build / build (ubuntu-latest)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: run / run (windows-latest, 3.14)
- GitHub Check: run / run (macos-latest, 3.13)
- GitHub Check: run / run (macos-latest, 3.11)
- GitHub Check: run / run (windows-latest, 3.11)
- GitHub Check: run / run (macos-latest, 3.12)
- GitHub Check: run / run (windows-latest, 3.10)
- GitHub Check: run / run (windows-latest, 3.9)
- GitHub Check: run / run (windows-latest, 3.13)
- GitHub Check: run / run (windows-latest, 3.12)
- GitHub Check: DevContainer Build & Test
- GitHub Check: test / test
- GitHub Check: run / test-cygwin
🔇 Additional comments (57)
CHANGELOG.rst (1)
4-4: LGTM!The changelog entry appropriately documents the internal refactoring and references the related issue.
dfetch/commands/common.py (2)
12-12: LGTM!The import path change aligns with the module reorganization moving SVN functionality from
dfetch.project.svntodfetch.vcs.svn.
75-75: LGTM!The API change from
SvnRepo.check_path()toSvnRepo().is_svn()is consistent with the new instance-based pattern. The default instantiation correctly uses the current directory to detect the superproject's VCS type before querying ignored files from the project path. Based on learnings, this is the correct approach.doc/getting_started.rst (1)
8-34: LGTM!The expanded "Main concepts" section clearly introduces the superproject and subproject terminology and explains how Dfetch operates. The documentation provides valuable context for users to understand the architecture and workflow.
doc/static/uml/c3_dfetch_components_project.puml (2)
15-24: LGTM!The UML diagram correctly models the new architecture with SubProject as an abstract component and Git/Svn as implementations. The relationships accurately reflect the design where Commands operate on the SubProject abstraction.
30-36: LGTM!The updated relationships correctly show Commands using SubProject and the SubProject-Manifest relationship, which aligns with the new architecture introduced in this PR.
dfetch/vcs/svn.py (13)
25-36: LGTM!The
ExternalNamedTuple provides a clean data structure for representing SVN external information with descriptive field names.
45-58: LGTM!The
is_svn()method provides good error handling with specific treatment for unreachable URLs (E170013), raising a descriptive RuntimeError, while returning False for other cases to allow graceful fallback.
60-67: LGTM!The
list_of_tags()method correctly retrieves and parses SVN tags, handling line endings appropriately.
70-80: LGTM!The
SvnRepoinitialization is straightforward with an appropriate default path of current directory.
82-89: LGTM!The instance method correctly checks if the path is an SVN working copy using the appropriate context manager and exception handling.
91-141: Complex parsing logic - verify regex patterns handle all SVN external formats.The
externals()method parses SVN external definitions using regex patterns. The logic handles multiple formats (pinned revisions and unpinned, different orderings), but the complexity could make maintenance challenging. Ensure the regex patterns are thoroughly tested against various SVN external formats.Consider if there are additional edge cases in SVN external syntax that should be tested, particularly around:
- Relative paths (../, ^/, //, /)
- URLs with special characters
- Peg revisions vs operative revisions
143-172: LGTM!The
_split_url()helper correctly handles SVN URL normalization including relative path prefixes and standard repository layouts. The comments explaining the SVN path conventions are helpful.
174-194: LGTM!The
get_info_from_target()method correctly parses SVN info output with appropriate error handling for unreachable targets and proper parsing of key-value pairs.
196-224: LGTM!The
get_last_changed_revision()method appropriately handles both directory (using svnversion) and non-directory targets (using svn info), with good error handling for unexpected output.
226-246: LGTM!The
untracked_files()method correctly identifies untracked files using SVN status and applies ignore patterns using pathlib pattern matching.
258-268: LGTM!The
files_in_path()method correctly lists files at a remote SVN URL path.
270-286: LGTM!The
ignored_files()method correctly retrieves ignored files using SVN status with the --no-ignore flag, with appropriate path existence checking.
288-310: LGTM!The
create_diff()method correctly generates SVN diffs with support for both single revisions and revision ranges, and properly filters ignored files from the patch.dfetch/commands/environment.py (1)
27-28: LGTM! Variable rename improves clarity.The renaming from
vcstoproject_typebetter reflects the new SubProject-based architecture and improves code readability.dfetch/commands/import_.py (2)
96-96: LGTM! Import path updated correctly.The import path change from
dfetch.project.svntodfetch.vcs.svnaligns with the SVN module reorganization in this PR.
142-142: TheSvnRepo()constructor correctly defaults to the current directory withpath: str = ".", and theis_svn()method properly uses this path within anin_directory(self._path)context manager. The implementation is sound.doc/internal.rst (1)
5-32: Excellent documentation addition!The new Glossary section clearly defines the core concepts (Superproject, Subproject, Remote, Child Manifest, and Metadata) introduced in this refactoring. This will help developers understand the architecture and terminology.
tests/test_report.py (1)
7-7: LGTM! Test correctly updated for SuperProject abstraction.The test properly creates a mock
SuperProjectwith the requiredmanifestandroot_directoryattributes, maintaining test coverage while aligning with the new architecture.Also applies to: 31-35
tests/test_import.py (2)
13-13: LGTM! Import path updated correctly.The import path change from
dfetch.project.svn.Externaltodfetch.vcs.svn.Externalcorrectly aligns with the SVN module reorganization.
108-108: LGTM! Test correctly updated for new API.The test properly updates the mock target from
SvnRepo.check_pathtoSvnRepo.is_svn, aligning with the API change in the production code. The mock correctly returnsTrueto simulate an SVN repository.Also applies to: 113-113
tests/test_subproject.py (1)
1-169: LGTM! Consistent refactoring from VCS to SubProject.The test file has been thoroughly updated to reflect the new SubProject abstraction. All imports, class names, patch targets, and variable references are correctly updated.
tests/test_update.py (1)
31-35: LGTM! Correct SuperProject mock setup.The test correctly creates a fake SuperProject with the expected
manifestandroot_directoryattributes, and patches the SuperProject class appropriately. This aligns with the new SuperProject abstraction introduced in the PR.Also applies to: 54-58
dfetch/project/subproject.py (2)
1-1: LGTM! Consistent terminology updates.The class has been correctly renamed from VCS to SubProject, and all related docstrings have been appropriately updated to reflect the new terminology.
Also applies to: 24-29, 35-35, 98-98, 233-233, 243-243, 248-248, 253-253
365-365: LGTM! Correct updates to method signatures and references.The docstring and LICENSE_GLOBS reference have been properly updated to use SubProject. The formatting adjustment to
get_diffimproves readability without changing functionality.Also applies to: 377-381, 392-393
tests/test_check.py (1)
31-35: LGTM! Correct SuperProject mock setup.The test correctly creates a fake SuperProject with the expected
manifestandroot_directoryattributes, and patches the SuperProject class appropriately. This mirrors the pattern in test_update.py and aligns with the new SuperProject abstraction.dfetch/commands/update.py (2)
42-42: LGTM!The SuperProject import is correctly placed and necessary for the refactoring.
80-100: LGTM!The SuperProject integration is well-executed. The refactoring correctly centralizes manifest and root directory access through the SuperProject abstraction, maintaining the same functionality while improving code organization.
dfetch/commands/check.py (2)
38-38: LGTM!The SuperProject import is correctly placed.
93-106: LGTM!The SuperProject integration is correctly implemented, centralizing manifest and root directory access. The changes maintain the proper behavior where VCS detection uses the superproject's VCS type, as per established patterns.
Based on learnings, this refactoring correctly maintains the superproject VCS context for ignored file detection.
dfetch/commands/freeze.py (3)
50-52: LGTM!The imports are correctly updated to support the SuperProject abstraction.
73-79: LGTM!The SuperProject integration is correctly implemented in the freeze workflow.
101-107: LGTM!The Manifest construction correctly references
superproject.manifest.remotesto preserve remote definitions from the original manifest.dfetch/commands/report.py (3)
16-17: LGTM!The imports are correctly added to support both SuperProject and SubProject abstractions.
66-76: LGTM!The SuperProject integration in the report workflow is correctly implemented.
93-93: LGTM!The license file detection correctly uses
SubProject.is_license_file, centralizing this logic in the SubProject abstraction.dfetch/project/superproject.py (3)
1-17: LGTM!The module docstring clearly explains the SuperProject abstraction, and the imports are well-organized.
28-36: LGTM!The initialization sequence (find → validate → load manifest → derive root directory) is logical and correct. The code appropriately allows exceptions from
find_manifest(),validate(), andManifest.from_file()to propagate, which is the expected behavior when no valid manifest exists.
38-46: LGTM!The read-only properties provide clean, encapsulated access to the manifest and root directory. The implementation follows good OOP practices.
dfetch/project/__init__.py (1)
4-8: LGTM!The import updates and
SUPPORTED_PROJECT_TYPESlist correctly reflect the rename fromGitRepo/SvnRepotoGitSubProject/SvnSubProject, aligning with the PR's objective to introduce clearer subproject terminology.tests/test_svn.py (4)
12-14: LGTM!Import paths correctly updated to reflect the new module structure:
SvnSubProjectfrom the project layer and SVN utilities (External,SvnRemote,SvnRepo) from the VCS layer.
133-146: LGTM!Patch targets correctly point to
dfetch.vcs.svnmodule, and the test properly exercisesSvnRepo.externals()through the new module path.
157-161: LGTM!Test correctly verifies
SvnRepo().is_svn()behavior with appropriate mock side effects for success and error cases.
180-184: LGTM!Test correctly verifies
SvnRemote.is_svn()for remote URL validation, properly separated from the local repo check intest_check_path.dfetch/commands/diff.py (4)
103-110: LGTM!Import updates correctly bring in the new
SuperProjectandSubProjectabstractions along with specific subproject implementations and VCS detection utilities.
143-163: LGTM!The command now properly uses
SuperProjectto access the manifest and root directory context. This encapsulation improves clarity by centralizing manifest discovery and validation.
169-182: LGTM - VCS detection correctly uses superproject's VCS type.The logic properly detects the superproject's VCS (where the manifest lives) rather than the fetched project's VCS, then creates the appropriate
SubProjectwrapper. This aligns with the project's design where diff generation uses the superproject's version control system. Based on learnings, this is the intended behavior.
185-204: LGTM!The
_diff_from_repofunction correctly uses theSubProjectabstraction, delegating torepo.metadata_revision()andrepo.get_diff()which are implemented by the concrete subproject types.dfetch/project/svn.py (4)
25-38: LGTM!Good separation of concerns:
SvnSubProjectwrapsSvnRemote(for remote operations) andSvnRepo(for local operations), properly delegating VCS-specific logic to the VCS layer while maintaining the project abstraction.
45-59: LGTM!Branch and tag handling correctly delegates to the wrapped
_repoand_remote_repoinstances, usingDEFAULT_BRANCHconstant from the VCS layer.
61-71: LGTM!Tool info logging correctly uses
get_svn_version()from the VCS module andSubProject._log_tool()for consistent output formatting across subproject types.
192-208: LGTM!The
get_diffimplementation correctly combines tracked changes fromcreate_diff(an instance method) with untracked files, usingcombine_patchesto produce a unified diff output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
dfetch/commands/diff.py (1)
168-181: Consider clarifying the error message terminology.The error message on line 180 says "if your project is an SVN or Git repo", but for clarity it should reference "superproject" since the check is performed on
superproject.root_directory.🔎 Suggested clarification
raise RuntimeError( - "Can only create patch if your project is an SVN or Git repo", + "Can only create patch if your superproject is an SVN or Git repo", )dfetch/project/subproject.py (1)
263-263: Update docstring terminology for consistency.The docstring still references "VCS" but should say "SubProject" for consistency with the refactoring.
🔎 Suggested fix
- """See if this VCS can uniquely distinguish branch with revision only.""" + """See if this SubProject can uniquely distinguish branch with revision only."""
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
dfetch/commands/diff.pydfetch/project/git.pydfetch/project/subproject.pydfetch/project/svn.pytests/test_subproject.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:20.972Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
📚 Learning: 2025-12-31T10:55:20.972Z
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:20.972Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
Applied to files:
dfetch/commands/diff.pydfetch/project/git.pydfetch/project/subproject.pytests/test_subproject.pydfetch/project/svn.py
🧬 Code graph analysis (3)
dfetch/commands/diff.py (2)
dfetch/project/superproject.py (3)
manifest(44-46)SuperProject(20-46)root_directory(39-41)dfetch/project/subproject.py (1)
diff(396-415)
dfetch/project/git.py (6)
dfetch/project/subproject.py (2)
SubProject(24-415)_diff_impl(376-382)dfetch/util/util.py (1)
safe_rmtree(53-62)dfetch/manifest/project.py (4)
vcs(430-432)ProjectEntry(304-475)version(363-368)version(371-377)dfetch/vcs/git.py (2)
GitLocalRepo(232-564)GitRemote(85-229)dfetch/log.py (1)
get_logger(60-63)dfetch/project/metadata.py (1)
version(84-86)
dfetch/project/subproject.py (4)
dfetch/project/git.py (2)
_diff_impl(54-74)metadata_revision(46-48)dfetch/project/svn.py (2)
_diff_impl(192-208)metadata_revision(184-186)tests/test_subproject.py (2)
_diff_impl(52-53)metadata_revision(49-50)dfetch/project/metadata.py (1)
Metadata(31-169)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
- GitHub Check: Publish Python distribution 📦 to TestPyPI
- GitHub Check: build / build (windows-latest)
- GitHub Check: build / build (ubuntu-latest)
- GitHub Check: build / build (macos-latest)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: run / run (macos-latest, 3.9)
- GitHub Check: run / run (windows-latest, 3.14)
- GitHub Check: run / run (windows-latest, 3.10)
- GitHub Check: run / run (windows-latest, 3.13)
- GitHub Check: run / run (ubuntu-latest, 3.12)
- GitHub Check: run / run (windows-latest, 3.11)
- GitHub Check: run / run (ubuntu-latest, 3.11)
- GitHub Check: run / run (windows-latest, 3.9)
- GitHub Check: run / run (windows-latest, 3.12)
- GitHub Check: run / test-cygwin
- GitHub Check: test / test
- GitHub Check: DevContainer Build & Test
🔇 Additional comments (7)
tests/test_subproject.py (1)
1-169: LGTM! Test updates are consistent with the SubProject refactor.The test file has been correctly updated to reflect the VCS → SubProject refactoring, including:
- Updated class names (ConcreteVCS → ConcreteSubProject)
- Updated method names (get_diff → _diff_impl)
- Updated patch targets to reference dfetch.project.subproject
- Updated all test invocations to use the new SubProject-based API
dfetch/project/git.py (1)
1-145: LGTM! Clean refactoring from GitRepo(VCS) to GitSubProject(SubProject).The changes correctly:
- Rename the class and update its base class
- Update imports and logging references
- Rename
get_diffto_diff_implto align with the new abstract interface- Preserve all existing logic and behavior
dfetch/commands/diff.py (2)
145-165: LGTM! Correct transition to SuperProject-centric flow.The refactored code correctly:
- Instantiates SuperProject to access manifest and root directory
- Uses
superproject.root_directoryas the working directory context- Accesses projects via
superproject.manifest- Delegates diff generation to
SubProject.diff()This aligns with the PR objective of introducing a distinct superproject concept.
194-194: LGTM! Modernized file I/O.Using
pathlib.Path(...).write_text(...)is more idiomatic and concise than the traditionalopen()/write()approach.dfetch/project/subproject.py (2)
24-37: LGTM! Class renamed and updated to SubProject.The class rename from VCS to SubProject correctly reflects the new abstraction. Constructor and initialization logic remain unchanged.
376-415: LGTM! Well-designed diff orchestration.The new
diff()method provides a clean public interface that:
- Validates revision count (max 2)
- Defaults to
metadata_revision()when no revisions provided- Handles single revision by appending empty string for current state
- Delegates to
_diff_impl()with proper ignore listThis separation of concerns (orchestration vs. implementation) is a good design pattern.
dfetch/project/svn.py (1)
1-212: LGTM! Comprehensive refactoring to SvnSubProject with wrapper-based design.The changes correctly:
- Rename the class from SvnRepo to SvnSubProject inheriting from SubProject
- Introduce wrapper objects (
_remote_repo: SvnRemote,_repo: SvnRepo) for cleaner separation of concerns- Update all method implementations to delegate to these wrappers
- Rename
get_diffto_diff_implto align with the abstract interface- Update references to use
SubProject._log_toolfor consistencyThis refactoring improves modularity by moving SVN-specific operations into dedicated wrapper classes in
dfetch/vcs/svn.py.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Fix all issues with AI Agents 🤖
In @dfetch/commands/diff.py:
- Around line 177-180: _parse_revs currently indexes revs[0] and revs[1]
unconditionally, causing IndexError when revs_arg yields fewer than two
elements; change it to safely parse and normalize the list to exactly two
entries (e.g., pad with an empty string or None) before building rev_range, then
compute rev_range conditionally (if second entry present use "from X to Y" else
"since X"), and return the normalized two-element list and the string. Ensure
you update the logic in the _parse_revs function so it never assumes revs has
two elements and preserves the declared return types.
In @dfetch/project/superproject.py:
- Around line 64-73: The validation condition in ignored_files is inverted and
the VCS detectors are instantiated without the superproject root; change the
check to raise when os.path.commonprefix((path, self.root_directory)) !=
self.root_directory, instantiate GitLocalRepo and SvnRepo with
self.root_directory (e.g., GitLocalRepo(self.root_directory).is_git() and
GitLocalRepo(self.root_directory).ignored_files(path)), and similarly for
SvnRepo, and ensure the method returns an empty list (or sensible default) if no
VCS matches.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
dfetch/commands/check.pydfetch/commands/common.pydfetch/commands/diff.pydfetch/commands/update.pydfetch/project/superproject.py
💤 Files with no reviewable changes (1)
- dfetch/commands/common.py
🚧 Files skipped from review as they are similar to previous changes (1)
- dfetch/commands/update.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:20.972Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
📚 Learning: 2025-12-31T10:55:20.972Z
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:20.972Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
Applied to files:
dfetch/commands/check.pydfetch/commands/diff.pydfetch/project/superproject.py
🧬 Code graph analysis (2)
dfetch/commands/diff.py (5)
dfetch/log.py (1)
print_info_line(15-17)dfetch/project/superproject.py (3)
root_directory(46-48)manifest(51-53)get_sub_project(55-62)dfetch/util/util.py (2)
in_directory(66-75)catch_runtime_exceptions(79-87)dfetch/manifest/manifest.py (3)
projects(266-268)selected_projects(270-284)path(251-253)dfetch/project/subproject.py (1)
diff(396-415)
dfetch/project/superproject.py (2)
dfetch/manifest/manifest.py (2)
find_manifest(365-376)path(251-253)dfetch/manifest/validate.py (1)
validate(12-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
- GitHub Check: Publish Python distribution 📦 to TestPyPI
- GitHub Check: build / build (macos-latest)
- GitHub Check: build / build (windows-latest)
- GitHub Check: build / build (ubuntu-latest)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: run / run (windows-latest, 3.11)
- GitHub Check: run / run (ubuntu-latest, 3.10)
- GitHub Check: run / run (windows-latest, 3.14)
- GitHub Check: run / run (windows-latest, 3.10)
- GitHub Check: run / run (macos-latest, 3.12)
- GitHub Check: run / run (windows-latest, 3.13)
- GitHub Check: run / run (windows-latest, 3.9)
- GitHub Check: run / run (windows-latest, 3.12)
- GitHub Check: run / run (macos-latest, 3.9)
- GitHub Check: run / run (ubuntu-latest, 3.9)
- GitHub Check: Analyze
- GitHub Check: run / test-cygwin
- GitHub Check: DevContainer Build & Test
🔇 Additional comments (4)
dfetch/commands/check.py (1)
93-107: LGTM! Clean migration to SuperProject-based flow.The refactoring correctly centralizes manifest handling and VCS detection through the
SuperProjectabstraction. The use ofsuperproject.ignored_files(project.destination)properly delegates ignore-file detection to the superproject's VCS type, which aligns with the expected behavior. Based on learnings, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type.dfetch/commands/diff.py (1)
136-172: Clean refactoring to SuperProject-based diff generation.The migration properly validates project existence before attempting diff, correctly obtains the subproject via
superproject.get_sub_project(), and handles the patch output appropriately. The error messages are clear and actionable.dfetch/project/superproject.py (2)
55-62: LGTM! Correct VCS-based subproject resolution.The method correctly uses
self.root_directoryto detect the superproject's VCS type and returns the appropriate subproject type. This aligns with the learning that VCS detection should use the superproject's VCS type, not the fetched project's VCS type.
35-53: Well-structured initialization and clean property accessors.The initialization correctly sequences manifest discovery, validation, and loading. Deriving
root_directoryfrom the manifest path ensures consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Fix all issues with AI Agents 🤖
In @dfetch/project/subproject.py:
- Around line 396-405: Fix the typo in the RuntimeError message inside
diff(old_rev, new_rev) by replacing "Please either revision this" with a proper
phrase such as "Please either commit this" or "Please add a revision for this";
update the string that combines the descriptive context (including
Metadata.FILENAME and self.local_path) so the full message reads clearly (e.g.,
"Please either commit this, or specify a revision to start from with --revs").
In @dfetch/vcs/svn.py:
- Around line 17-22: The get_svn_version function assumes svn output contains
"version" on the first line; add robust parsing and error handling: decode
result.stdout safely, get the first non-empty line, check that it contains the
substring "version" before splitting, and trim whitespace; catch and handle
UnicodeDecodeError, IndexError, ValueError from unexpected formats and raise a
clear RuntimeError (or return a sensible default tuple) that includes the raw
first_line (or full output) for diagnostics; reference variables/functions
result, first_line, tool, version and the get_svn_version function when applying
the changes.
- Around line 248-256: The export method builds the svn command using
rev.split(" ") which adds an empty string when rev is empty; update the logic in
export (the rev.split(" ") usage) to only extend the command list when rev is
non-empty (e.g., replace rev.split(" ") with a conditional that yields an empty
list when rev is falsy and rev.split(" ") otherwise) so the run_on_cmdline call
receives no spurious "" argument.
♻️ Duplicate comments (2)
dfetch/commands/diff.py (1)
177-185: Previous IndexError issue has been addressed.The
_parse_revsmethod now correctly handles all cases (0, 1, or 2 revisions) without risk ofIndexError.dfetch/project/superproject.py (1)
64-74: Critical: Inverted validation logic and inconsistent VCS path detection remain unaddressed.This issue was flagged in a previous review and appears to still be present:
Line 66 - Inverted condition: The double negation (
not ... != ...) makes the condition true when path is underroot_directory, which is the opposite of the intended validation.Lines 69, 71 - Missing path argument:
GitLocalRepo()andSvnRepo()are instantiated without theroot_directoryargument, unlike inget_sub_project()(lines 57, 59). This causes VCS detection to use the current working directory instead of the superproject root.🔎 Proposed fix
def ignored_files(self, path: str) -> Sequence[str]: """Return a list of files that can be ignored in a given path.""" - if not os.path.commonprefix((path, self.root_directory)) != self.root_directory: + if os.path.commonprefix((path, self.root_directory)) != self.root_directory: raise RuntimeError(f"{path} not in superproject {self.root_directory}!") - if GitLocalRepo().is_git(): + if GitLocalRepo(self.root_directory).is_git(): return GitLocalRepo.ignored_files(path) - if SvnRepo().is_svn(): + if SvnRepo(self.root_directory).is_svn(): return SvnRepo.ignored_files(path) return []
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (25)
CHANGELOG.rstdfetch/commands/check.pydfetch/commands/common.pydfetch/commands/diff.pydfetch/commands/environment.pydfetch/commands/freeze.pydfetch/commands/import_.pydfetch/commands/report.pydfetch/commands/update.pydfetch/manifest/manifest.pydfetch/project/__init__.pydfetch/project/git.pydfetch/project/subproject.pydfetch/project/superproject.pydfetch/project/svn.pydfetch/vcs/svn.pydoc/getting_started.rstdoc/internal.rstdoc/static/uml/c3_dfetch_components_project.pumltests/test_check.pytests/test_import.pytests/test_report.pytests/test_subproject.pytests/test_svn.pytests/test_update.py
💤 Files with no reviewable changes (2)
- dfetch/commands/common.py
- dfetch/manifest/manifest.py
🚧 Files skipped from review as they are similar to previous changes (6)
- CHANGELOG.rst
- dfetch/commands/update.py
- doc/internal.rst
- tests/test_check.py
- tests/test_report.py
- dfetch/project/git.py
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:20.972Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
📚 Learning: 2025-12-31T10:55:20.972Z
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:20.972Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
Applied to files:
doc/getting_started.rstdfetch/project/superproject.pydfetch/commands/diff.pydfetch/project/subproject.pytests/test_subproject.pydfetch/commands/report.pydfetch/commands/check.pydfetch/commands/freeze.pydfetch/project/__init__.pydfetch/commands/import_.py
📚 Learning: 2026-01-01T08:56:36.734Z
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 898
File: pyproject.toml:204-206
Timestamp: 2026-01-01T08:56:36.734Z
Learning: In dfetch's Nuitka configuration, `include-module="infer_license.licenses"` is required because this submodule contains license text files (resources) that infer_license needs at runtime. While Nuitka auto-discovers other infer_license modules like `infer_license.types` and `infer_license.api`, it does not auto-discover resource directories, so they must be explicitly included.
Applied to files:
dfetch/commands/report.py
📚 Learning: 2026-01-02T22:46:45.589Z
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 904
File: script/package.py:10-18
Timestamp: 2026-01-02T22:46:45.589Z
Learning: In dfetch's script/package.py, importing dfetch.__version__ directly is acceptable and intentional; if dfetch is not installed in the environment, the packaging job should fail explicitly rather than handling the import error gracefully.
Applied to files:
dfetch/commands/freeze.py
🧬 Code graph analysis (11)
dfetch/commands/diff.py (4)
dfetch/project/superproject.py (4)
SuperProject(27-74)root_directory(46-48)manifest(51-53)get_sub_project(55-62)dfetch/util/util.py (2)
in_directory(66-75)catch_runtime_exceptions(79-87)dfetch/manifest/manifest.py (3)
projects(266-268)selected_projects(270-284)path(251-253)dfetch/project/subproject.py (1)
diff(396-405)
dfetch/project/subproject.py (4)
dfetch/project/git.py (1)
_diff_impl(54-74)dfetch/project/svn.py (1)
_diff_impl(192-208)tests/test_subproject.py (1)
_diff_impl(52-53)dfetch/project/metadata.py (1)
Metadata(31-169)
tests/test_update.py (3)
dfetch/project/superproject.py (2)
manifest(51-53)root_directory(46-48)tests/manifest_mock.py (1)
mock_manifest(9-22)dfetch/manifest/manifest.py (1)
projects(266-268)
dfetch/vcs/svn.py (4)
dfetch/log.py (1)
get_logger(60-63)dfetch/util/cmdline.py (2)
SubprocessCommandError(10-36)run_on_cmdline(39-68)dfetch/util/util.py (1)
in_directory(66-75)dfetch/vcs/patch.py (1)
filter_patch(26-38)
tests/test_subproject.py (1)
dfetch/project/subproject.py (5)
_diff_impl(376-382)ignore(252-254)check_wanted_with_local(47-77)_are_there_local_changes(349-361)_running_in_ci(42-45)
tests/test_import.py (1)
dfetch/vcs/svn.py (3)
External(25-35)is_svn(45-58)is_svn(82-89)
dfetch/commands/report.py (4)
dfetch/project/subproject.py (2)
SubProject(24-405)is_license_file(389-394)dfetch/project/superproject.py (3)
SuperProject(27-74)root_directory(46-48)manifest(51-53)dfetch/util/util.py (1)
in_directory(66-75)dfetch/manifest/manifest.py (2)
selected_projects(270-284)projects(266-268)
dfetch/commands/check.py (4)
dfetch/project/superproject.py (4)
manifest(51-53)SuperProject(27-74)root_directory(46-48)ignored_files(64-74)dfetch/manifest/manifest.py (4)
Manifest(109-362)selected_projects(270-284)projects(266-268)path(251-253)dfetch/util/util.py (2)
in_directory(66-75)catch_runtime_exceptions(79-87)dfetch/manifest/project.py (1)
destination(405-407)
dfetch/project/__init__.py (3)
dfetch/project/git.py (1)
GitSubProject(19-145)dfetch/project/subproject.py (1)
SubProject(24-405)dfetch/manifest/project.py (1)
ProjectEntry(304-475)
dfetch/commands/environment.py (2)
dfetch/project/git.py (1)
list_tool_info(82-91)dfetch/project/svn.py (1)
list_tool_info(62-71)
dfetch/commands/import_.py (1)
dfetch/vcs/svn.py (3)
SvnRepo(70-310)is_svn(45-58)is_svn(82-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
- GitHub Check: Publish Python distribution 📦 to TestPyPI
- GitHub Check: build / build (macos-latest)
- GitHub Check: build / build (ubuntu-latest)
- GitHub Check: build / build (windows-latest)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: run / run (ubuntu-latest, 3.10)
- GitHub Check: run / run (windows-latest, 3.14)
- GitHub Check: run / run (windows-latest, 3.11)
- GitHub Check: run / run (windows-latest, 3.12)
- GitHub Check: run / run (windows-latest, 3.10)
- GitHub Check: run / run (macos-latest, 3.10)
- GitHub Check: run / run (macos-latest, 3.11)
- GitHub Check: run / run (ubuntu-latest, 3.13)
- GitHub Check: run / run (windows-latest, 3.9)
- GitHub Check: run / run (ubuntu-latest, 3.12)
- GitHub Check: run / run (windows-latest, 3.13)
- GitHub Check: run / test-cygwin
- GitHub Check: DevContainer Build & Test
🔇 Additional comments (30)
doc/getting_started.rst (1)
8-34: LGTM! Clear introduction of superproject/sub-project concepts.The updated documentation clearly distinguishes between the superproject (which contains the manifest) and sub-projects (external dependencies). The explanation of mixed VCS support, metadata storage, and update workflow is well-structured and aligns with the architectural changes in this PR.
dfetch/commands/environment.py (1)
27-28: LGTM! Variable rename improves clarity.The rename from
vcstoproject_typebetter reflects the new SubProject-based architecture introduced in this PR.tests/test_update.py (3)
7-7: LGTM! Mock import added for test refactoring.
31-35: LGTM! Test updated to use fake SuperProject.The test now constructs a fake SuperProject with manifest and root_directory properties, then patches the SuperProject constructor. This aligns well with the new SuperProject-centric architecture.
54-58: LGTM! Consistent fake SuperProject pattern applied.The forced update test follows the same pattern as the standard update test, maintaining consistency across the test suite.
dfetch/commands/import_.py (2)
96-96: LGTM! Import path updated to reflect module reorganization.The import path change from
dfetch.project.svntodfetch.vcs.svnaligns with the PR's extraction of SVN utilities into a dedicated VCS module.
142-142: LGTM! SVN detection updated to use instance method.The change from
SvnRepo.check_path()toSvnRepo().is_svn()is functionally equivalent—both check if the current directory is an SVN repository. The instance-based approach aligns with the new API design indfetch/vcs/svn.py.dfetch/vcs/svn.py (1)
45-58: Unreachable code after raising RuntimeError.Line 52 raises a
RuntimeErrorwhen encountering error code E170013, making thereturn Falsestatement on line 56 unreachable. Either remove the unreachable return or reconsider the error handling strategy.🔎 Proposed fix
def is_svn(self) -> bool: """Check if is SVN.""" try: run_on_cmdline(logger, ["svn", "info", self._remote, "--non-interactive"]) return True except SubprocessCommandError as exc: if exc.stderr.startswith("svn: E170013"): raise RuntimeError( f">>>{exc.cmd}<<< failed!\n" + f"'{self._remote}' is not a valid URL or unreachable:\n{exc.stdout or exc.stderr}" ) from exc - return False + # For other SVN errors, treat as non-SVN + return False except RuntimeError: return FalseLikely an incorrect or invalid review comment.
dfetch/commands/freeze.py (3)
50-52: LGTM! Clean refactoring to SuperProject abstraction.The imports correctly reflect the shift from
get_manifest()to the SuperProject-based workflow, aligning with the PR's goal of introducing explicit superproject/subproject concepts.
73-79: LGTM! Proper use of SuperProject for manifest access.The refactoring correctly instantiates a SuperProject and uses its
root_directoryfor the working directory context, replacing the previous direct manifest access pattern. The iteration oversuperproject.manifest.projectsis consistent with the new abstraction.
101-107: LGTM! Manifest construction updated correctly.The remotes are now sourced from
superproject.manifest.remotes, maintaining the same logical flow while using the new SuperProject abstraction.dfetch/commands/report.py (2)
16-17: LGTM! Consistent refactoring to SuperProject abstraction.The command correctly instantiates a SuperProject and uses its
root_directoryandmanifestproperties throughout. The logic flow is preserved while adopting the new abstractions.Also applies to: 66-71
93-93: LGTM! License file detection updated to SubProject.The change from
VCS.is_license_filetoSubProject.is_license_fileis correct and aligns with the SubProject abstraction introduced in this PR.Based on learnings, the VCS detection now correctly uses the superproject's VCS type for ignored files and other operations.
tests/test_import.py (2)
13-13: LGTM! Import updated for SVN reorganization.The External import path correctly reflects the move of SVN utilities to
dfetch.vcs.svn, consistent with the broader refactoring.
108-113: LGTM! Test updated for new SvnRepo API.The patch target and mock correctly updated from
check_pathtois_svn, matching the refactored SvnRepo interface. The test logic is preserved.tests/test_subproject.py (3)
1-1: LGTM! Test file properly refactored to SubProject.The docstring, imports, and class declaration correctly updated from VCS to SubProject, reflecting the core abstraction change in this PR.
Also applies to: 13-13, 16-16
52-53: LGTM! Method renamed to follow private convention.The method name change from
get_diffto_diff_implis consistent with the SubProject's abstract method signature and follows Python's private method naming convention.
107-144: LGTM! All patch targets and references updated correctly.The test code comprehensively updates all module paths to
dfetch.project.subprojectand all class references toConcreteSubProject, maintaining test coverage while adopting the new abstractions.tests/test_svn.py (4)
12-14: LGTM! Imports reorganized correctly.The import split is logical:
SvnSubProjectremains indfetch.project.svn(project-level abstractions), whileExternal,SvnRemote, andSvnRepomove todfetch.vcs.svn(VCS utilities). This separation clarifies the architectural boundaries.
133-136: LGTM! Test updated for new SvnRepo instantiation and API.The patch targets correctly reference
dfetch.vcs.svn, and the test now usesSvnRepo().is_svn()with instance creation, matching the refactored API whereSvnRepotakes an optional path parameter.Also applies to: 158-161
180-184: LGTM! New test for SvnRemote abstraction.The test correctly exercises
SvnRemote.is_svn(), validating the new remote handling abstraction introduced in this refactoring.
225-241: LGTM! Fixtures updated for new SvnRepo API.The
svn_subprojectandsvn_repofixtures correctly instantiate their respective classes with the updated constructors.SvnRepo()with no arguments aligns with the optional path parameter in the refactored API.dfetch/commands/check.py (1)
93-107: LGTM! Clean migration to SuperProject abstraction.The refactoring correctly uses the superproject's VCS type for determining ignored files, which aligns with the intended design where ignored files are queried from the project's destination path using the superproject's VCS system. Based on learnings, this is the correct approach.
dfetch/project/__init__.py (1)
11-22: LGTM! Well-structured factory pattern.The two-phase lookup (first by explicit
vcsname, then by probingcheck()) provides a clean fallback mechanism for VCS auto-detection while allowing explicit type specification in the manifest.dfetch/project/subproject.py (1)
24-35: LGTM! Clean abstraction for subproject representation.The
SubProjectclass properly extendsABCwith well-defined abstract methods. The class encapsulates project metadata and provides a consistent interface for both Git and SVN implementations.dfetch/commands/diff.py (1)
136-172: LGTM! Clean refactoring to SuperProject-based diff generation.The logic correctly:
- Validates project destination exists before attempting diff
- Falls back to metadata revision when no old revision is specified
- Delegates diff generation to the SubProject abstraction
- Handles patch file output consistently
doc/static/uml/c3_dfetch_components_project.puml (1)
15-24: LGTM! UML diagram accurately reflects the new SubProject architecture.The component relationships correctly show
GitandSvnimplementingSubProject, with proper usage dependencies onAbstractCheckReporterandMetadata.dfetch/project/superproject.py (1)
35-62: LGTM! SuperProject initialization and subproject creation are well-implemented.The
__init__correctly discovers and validates the manifest, andget_sub_projectproperly usesself.root_directoryfor VCS type detection.dfetch/project/svn.py (2)
25-38: LGTM! Clean composition-based refactoring.The
SvnSubProjectclass correctly uses composition withSvnRemotefor remote operations andSvnRepofor local operations. Thecheck()method appropriately delegates to the remote repo for SVN detection.
192-208: LGTM! Consistent diff implementation.The
_diff_implmethod follows the same pattern asGitSubProject._diff_impl: returning early for revision-to-revision diffs and including untracked files when diffing against the working copy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Fix all issues with AI Agents 🤖
In @dfetch/vcs/svn.py:
- Around line 176-196: get_info_from_target currently assumes every non-empty
line from the `svn info` output has a colon and unpacks `line.split(":",
maxsplit=1)` into `key, value`, which raises on lines without colons; update the
comprehension to skip or defensively handle such lines (e.g., only process lines
where ":" in line or use `key, _, value = line.partition(":")` and continue if
`_` is empty) so you don't attempt to unpack single-element results—apply this
change inside `get_info_from_target` where the dict is built to ensure only
valid key/value pairs are included.
🧹 Nitpick comments (4)
dfetch/vcs/svn.py (1)
93-143: Consider adding a docstring explaining the external parsing regex patterns.The
externals()method contains complex regex patterns for parsing SVN externals. While the code is functional, the regex patterns are non-trivial and could benefit from inline documentation explaining what each pattern matches.🔎 Suggested documentation addition
@staticmethod def externals() -> list[External]: """Get list of externals.""" result = run_on_cmdline( logger, [ "svn", "--non-interactive", "propget", "svn:externals", "-R", ], ) repo_root = SvnRepo.get_info_from_target()["Repository Root"] externals: list[External] = [] + # Pattern matches: "path - ..." where path is the local directory path_pattern = r"([^\s^-]+)\s+-" for entry in result.stdout.decode().split(os.linesep * 2): match: Optional[re.Match[str]] = None local_path: str = "" for match in re.finditer(path_pattern, entry): pass if match: local_path = match.group(1) entry = re.sub(path_pattern, "", entry) + # Pattern matches either: + # - url@revision name (pinned) + # - url name (unpinned) for match in re.finditer( r"([^-\s\d][^\s]+)(?:@)(\d+)\s+([^\s]+)|([^-\s\d][^\s]+)\s+([^\s]+)", entry, ):tests/test_update.py (1)
69-75: Consider isolating test state -DEFAULT_ARGS.forcemutation persists.The test mutates
DEFAULT_ARGS.force = Trueat line 70, butDEFAULT_ARGSis defined at module level. This mutation persists across tests, potentially causing test pollution if test execution order changes.🔎 Proposed fix to isolate test arguments
def test_forced_update(): update = Update() fake_superproject = Mock() fake_superproject.manifest = mock_manifest([{"name": "some_project"}]) fake_superproject.root_directory = "/tmp" fake_superproject.ignored_files.return_value = [] with patch("dfetch.commands.update.SuperProject", return_value=fake_superproject): with patch( "dfetch.manifest.manifest.get_childmanifests" ) as mocked_get_childmanifests: with patch("dfetch.project.make") as mocked_make: with patch("os.path.exists"): with patch("dfetch.commands.update.in_directory"): with patch("dfetch.commands.update.Update._check_destination"): mocked_get_childmanifests.return_value = [] - args = DEFAULT_ARGS - args.force = True + args = argparse.Namespace( + no_recommendations=False, + force=True, + projects=[], + ) update(args) mocked_make.return_value.update.assert_called_once_with( force=True, files_to_ignore=[] )dfetch/project/svn.py (2)
169-179: Inconsistent method call:_get_infouses static method on instance.Line 170 calls
self._repo.get_info_from_target(), butget_info_from_targetis a@staticmethodinSvnRepo(as shown in relevant snippets). While Python allows calling static methods on instances, this is inconsistent with other static method usages in this file (e.g.,SvnRepo.export()at line 130,SvnRepo.files_in_path()at line 177).Consider using the class reference for consistency.
🔎 Proposed consistency fix
def _get_info(self, branch: str) -> dict[str, str]: - return self._repo.get_info_from_target(f"{self.remote}/{branch}") + return SvnRepo.get_info_from_target(f"{self.remote}/{branch}")
184-190: Inconsistent static method calls on instance.Similar to
_get_info, bothmetadata_revision()andcurrent_revision()callself._repo.get_last_changed_revision(), butget_last_changed_revisionis a@staticmethodinSvnRepo. Consider usingSvnRepo.get_last_changed_revision()for consistency with other static method calls in this file.🔎 Proposed consistency fix
def metadata_revision(self) -> str: """Get the revision of the metadata file.""" - return self._repo.get_last_changed_revision(self.metadata_path) + return SvnRepo.get_last_changed_revision(self.metadata_path) def current_revision(self) -> str: """Get the current revision of the repo.""" - return self._repo.get_last_changed_revision(self.local_path) + return SvnRepo.get_last_changed_revision(self.local_path)
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
dfetch/project/subproject.pydfetch/project/svn.pydfetch/vcs/svn.pytests/test_svn.pytests/test_update.py
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:20.972Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
📚 Learning: 2025-12-31T10:55:20.972Z
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:20.972Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
Applied to files:
dfetch/project/subproject.py
📚 Learning: 2026-01-01T11:05:39.533Z
Learnt from: ben-edna
Repo: dfetch-org/dfetch PR: 898
File: script/package.py:201-205
Timestamp: 2026-01-01T11:05:39.533Z
Learning: In the dfetch project's script/package.py, prefer explicit failures ("crash and burn") over silently handling missing configuration values, ensuring that builds fail if required metadata (e.g., URLs in pyproject.toml) is not properly configured.
Applied to files:
dfetch/project/subproject.py
🧬 Code graph analysis (4)
dfetch/project/subproject.py (4)
dfetch/project/svn.py (1)
_diff_impl(192-208)tests/test_subproject.py (1)
_diff_impl(52-53)dfetch/project/git.py (1)
_diff_impl(54-74)dfetch/project/metadata.py (1)
Metadata(31-169)
tests/test_svn.py (2)
dfetch/util/cmdline.py (1)
SubprocessCommandError(10-36)dfetch/vcs/svn.py (6)
External(27-37)SvnRemote(40-69)SvnRepo(72-312)is_svn(47-60)is_svn(84-91)get_info_from_target(177-196)
dfetch/vcs/svn.py (4)
dfetch/log.py (1)
get_logger(60-63)dfetch/util/cmdline.py (2)
SubprocessCommandError(10-36)run_on_cmdline(39-68)dfetch/util/util.py (1)
in_directory(66-75)dfetch/vcs/patch.py (1)
filter_patch(26-38)
dfetch/project/svn.py (4)
dfetch/log.py (1)
get_logger(60-63)dfetch/project/subproject.py (3)
SubProject(24-405)remote(242-244)ignore(252-254)dfetch/vcs/patch.py (2)
combine_patches(95-105)create_svn_patch_for_new_file(58-61)dfetch/vcs/svn.py (8)
SvnRemote(40-69)get_svn_version(17-24)is_svn(47-60)is_svn(84-91)list_of_tags(62-69)get_info_from_target(177-196)create_diff(290-312)untracked_files(229-248)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
- GitHub Check: build / Build wheel 📦
- GitHub Check: build / build (ubuntu-latest)
- GitHub Check: build / build (macos-latest)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: run / run (macos-latest, 3.13)
- GitHub Check: run / run (macos-latest, 3.11)
- GitHub Check: run / run (windows-latest, 3.11)
- GitHub Check: run / run (windows-latest, 3.10)
- GitHub Check: run / run (windows-latest, 3.14)
- GitHub Check: run / run (windows-latest, 3.12)
- GitHub Check: run / run (windows-latest, 3.13)
- GitHub Check: run / run (ubuntu-latest, 3.11)
- GitHub Check: run / run (windows-latest, 3.9)
- GitHub Check: run / run (macos-latest, 3.9)
- GitHub Check: run / run (macos-latest, 3.10)
- GitHub Check: run / run (ubuntu-latest, 3.10)
- GitHub Check: run / test-cygwin
- GitHub Check: test / test
- GitHub Check: DevContainer Build & Test
🔇 Additional comments (16)
dfetch/project/subproject.py (4)
24-36: LGTM! Clean class rename and initialization.The
SubProjectabstraction correctly replaces the previousVCSclass, with proper docstrings and initialization. The constructor properly initializes metadata from the project entry and handles CI detection for animations.
375-382: Abstract method signature looks correct.The
_diff_implabstract method signature withold_revision: str,new_revision: Optional[str], andignore: Sequence[str]aligns with the implementations inGitSubProjectandSvnSubProjectshown in the relevant code snippets.
388-394: LGTM! License file check correctly uses class reference.The static method properly references
SubProject.LICENSE_GLOBSfor the pattern matching, maintaining consistency with the class rename.
396-405: LGTM! The diff orchestration method is well-structured.The public
diff()method correctly:
- Validates that
old_revis provided with a clear error message- Delegates to
_diff_implwith appropriate parameters- Passes
Metadata.FILENAMEas the ignore listThe typo fix from the past review ("Please either commit this") has been addressed.
dfetch/vcs/svn.py (4)
17-24: LGTM! Robust version parsing with proper error handling.The error handling for unexpected
svn --versionoutput format has been addressed as noted in the past review. The function now properly validates that "version" appears in the output before parsing.
40-60: LGTM! Clean SvnRemote wrapper with proper error handling.The
is_svn()method correctly handles the E170013 error case by raising a descriptive RuntimeError, while returningFalsefor other subprocess errors or runtime errors. This aligns with the expected behavior for VCS detection.
250-258: LGTM! The empty revision handling has been fixed.The past review concern about
rev.split(" ")adding empty strings to the command has been addressed. The conditional(rev.split(" ") if rev else [])correctly handles the empty revision case.
290-312: LGTM! Clean diff generation with proper filtering.The
create_diffmethod correctly builds the SVN diff command, handles optional new_revision, and filters the patch output throughfilter_patch.tests/test_update.py (2)
31-35: LGTM! Clean SuperProject mocking approach.The fake superproject setup correctly provides the required attributes (
manifest,root_directory) and patches theSuperProjectconstructor to return the mock. This aligns with the PR's shift to SuperProject-based architecture.
54-59: LGTM! Forced update test properly mocks ignored_files.The test correctly sets up
fake_superproject.ignored_files.return_value = []which is needed for the forced update path that checks for files to ignore.tests/test_svn.py (3)
12-14: LGTM! Import paths correctly reflect module reorganization.The imports correctly separate concerns:
SvnSubProjectfromdfetch.project.svn(the subproject implementation)External,SvnRemote,SvnRepofromdfetch.vcs.svn(the VCS utilities)
133-141: LGTM! Patch targets correctly updated for new module structure.The patches now target
dfetch.vcs.svn.run_on_cmdline,dfetch.vcs.svn.SvnRepo.get_info_from_target, anddfetch.vcs.svn.os.getcwd, correctly reflecting the module reorganization.
225-241: LGTM! New fixtures and tests verify the SubProject abstraction.The new
svn_subprojectandsvn_repofixtures along withtest_svn_subproject_nameandtest_svn_repo_default_branchcorrectly verify that:
SvnSubProject.NAME == "svn"SvnRepo.DEFAULT_BRANCH == "trunk"This ensures the new class structure maintains expected constants.
dfetch/project/svn.py (3)
25-34: LGTM! Clean SvnSubProject initialization with proper wrapper setup.The constructor correctly:
- Calls
super().__init__(project)to initialize the baseSubProject- Creates
SvnRemotewrapper withself.remote(from base class property)- Creates
SvnRepowrapper withself.local_path(from base class property)This properly separates remote and local repository concerns.
61-71: LGTM! Proper delegation to helper function with fallback.The
list_tool_info()method correctly delegates toget_svn_version()and usesSubProject._log_tool()for consistent logging. The fallback to"<not found in PATH>"is appropriate for missing SVN installations.
192-208: LGTM! The_diff_implcorrectly handles filtered diffs and untracked files.The implementation properly:
- Delegates to
self._repo.create_diff()for the base diff- Returns early if
new_revisionis provided (comparing specific revisions)- For working copy diffs, appends patches for untracked files via
create_svn_patch_for_new_file- Combines all patches using
combine_patchesThis aligns with the pattern shown in the relevant snippet for
dfetch/project/svn.pylines 191-207.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI Agents
In @dfetch/vcs/svn.py:
- Around line 93-147: externals() is currently a @staticmethod that operates on
the current working directory, but SvnRepo is instance-based (see __init__(self,
path), self._path, and methods like is_svn() / create_diff() that use
in_directory(self._path)); change externals() to an instance method (remove
@staticmethod, add self parameter), update its internals to use self._path (wrap
command execution and repo_root lookup in in_directory(self._path) or pass
cwd=self._path to run_on_cmdline) and any os.getcwd() usages to self._path, and
update tests (e.g., tests/test_svn.py line ~141) to call
SvnRepo(path).externals() or SvnRepo().externals() as appropriate.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
dfetch/project/svn.pydfetch/vcs/svn.pytests/test_svn.pytests/test_update.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:20.972Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
🧬 Code graph analysis (2)
tests/test_update.py (3)
dfetch/project/superproject.py (3)
manifest(52-54)root_directory(47-49)ignored_files(65-78)tests/manifest_mock.py (1)
mock_manifest(9-22)dfetch/manifest/manifest.py (1)
projects(266-268)
tests/test_svn.py (3)
dfetch/project/svn.py (1)
SvnSubProject(25-212)dfetch/util/cmdline.py (1)
SubprocessCommandError(10-36)dfetch/vcs/svn.py (6)
External(27-37)SvnRemote(40-69)SvnRepo(72-318)is_svn(47-60)is_svn(84-91)get_info_from_target(181-202)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
- GitHub Check: build / build (windows-latest)
- GitHub Check: build / build (macos-latest)
- GitHub Check: build / Build wheel 📦
- GitHub Check: build / build (ubuntu-latest)
- GitHub Check: Publish Python distribution 📦 to TestPyPI
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: run / run (windows-latest, 3.12)
- GitHub Check: run / run (windows-latest, 3.11)
- GitHub Check: run / run (windows-latest, 3.14)
- GitHub Check: run / run (windows-latest, 3.13)
- GitHub Check: run / run (macos-latest, 3.10)
- GitHub Check: run / run (macos-latest, 3.14)
- GitHub Check: run / run (windows-latest, 3.10)
- GitHub Check: run / run (macos-latest, 3.9)
- GitHub Check: run / run (ubuntu-latest, 3.12)
- GitHub Check: run / run (windows-latest, 3.9)
- GitHub Check: run / run (ubuntu-latest, 3.10)
- GitHub Check: DevContainer Build & Test
- GitHub Check: docs / Landing page
- GitHub Check: run / test-cygwin
- GitHub Check: test / test
🔇 Additional comments (6)
dfetch/vcs/svn.py (3)
17-24: LGTM!The error handling for unexpected
svn --versionoutput format has been properly implemented, addressing the previous review feedback.
256-264: LGTM!The conditional logic for handling empty
revparameter has been properly implemented, preventing empty strings from being added to the command list as noted in previous review feedback.
180-202: LGTM!The defensive check for lines containing colons has been properly implemented in the dictionary comprehension, preventing
ValueErrorwhen unpacking as noted in previous review feedback.tests/test_update.py (1)
31-35: LGTM!The test adaptations properly align with the new SuperProject abstraction. The mocking strategy is clear and maintains the same test coverage while working with the refactored architecture.
Also applies to: 54-59, 69-73
tests/test_svn.py (1)
12-14: LGTM!The test adaptations correctly reflect the new module structure and API. The imports and test fixtures have been properly updated to use
SvnSubProject,SvnRemote, andSvnRepofrom their new locations.Also applies to: 133-135, 161-161, 184-184, 226-236
dfetch/project/svn.py (1)
25-34: LGTM!The refactoring from
SvnRepo(VCS)toSvnSubProject(SubProject)with wrapper-based delegation is well-executed. The logic is preserved while cleanly separating concerns through theSvnRemoteandSvnRepowrapper classes, aligning with the PR's objective to introduce explicit superproject/subproject abstractions.Also applies to: 38-38, 47-47, 59-59, 65-66, 92-96, 130-130, 143-143, 149-149, 170-170, 177-177, 186-186, 190-190, 196-196, 203-203, 212-212
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
dfetch/vcs/svn.py (1)
93-147: Consider adding defensive error handling for malformed SVN externals output.The externals parsing logic (lines 111-146) uses multiple regex patterns to extract URL, revision, and path information. While the current implementation handles known SVN external formats, it may silently skip or misparse entries if the SVN output format is unexpected or malformed.
Consider adding validation or logging when:
- The regex patterns don't match expected entries
- Required fields (like
urlorname) are empty after parsing- The externals output contains unexpected format variations
This would help diagnose issues if SVN output format changes or if users have non-standard external declarations.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
dfetch/commands/import_.pydfetch/vcs/svn.pytests/test_svn.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:28.644Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
📚 Learning: 2025-12-31T10:55:28.644Z
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:28.644Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
Applied to files:
dfetch/commands/import_.py
🧬 Code graph analysis (3)
dfetch/commands/import_.py (1)
dfetch/vcs/svn.py (4)
SvnRepo(72-318)is_svn(47-60)is_svn(84-91)externals(93-147)
dfetch/vcs/svn.py (4)
dfetch/log.py (1)
get_logger(60-63)dfetch/util/cmdline.py (2)
SubprocessCommandError(10-36)run_on_cmdline(39-68)dfetch/util/util.py (1)
in_directory(66-75)dfetch/vcs/patch.py (1)
filter_patch(26-38)
tests/test_svn.py (2)
dfetch/project/svn.py (1)
SvnSubProject(25-212)dfetch/vcs/svn.py (7)
External(27-37)SvnRemote(40-69)SvnRepo(72-318)externals(93-147)is_svn(47-60)is_svn(84-91)get_info_from_target(181-202)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
- GitHub Check: build / build (macos-latest)
- GitHub Check: build / build (ubuntu-latest)
- GitHub Check: build / build (windows-latest)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: run / run (windows-latest, 3.13)
- GitHub Check: run / run (windows-latest, 3.9)
- GitHub Check: run / run (windows-latest, 3.10)
- GitHub Check: run / run (windows-latest, 3.11)
- GitHub Check: run / run (macos-latest, 3.10)
- GitHub Check: run / run (windows-latest, 3.14)
- GitHub Check: run / run (macos-latest, 3.9)
- GitHub Check: run / run (windows-latest, 3.12)
- GitHub Check: run / run (ubuntu-latest, 3.10)
- GitHub Check: DevContainer Build & Test
- GitHub Check: test / test
- GitHub Check: run / test-cygwin
🔇 Additional comments (11)
dfetch/vcs/svn.py (5)
17-24: LGTM! Error handling for unexpected output format is properly implemented.The validation check for "version" in the output (lines 21-22) ensures robust parsing of the
svn --versioncommand.
149-178: LGTM! URL parsing correctly handles SVN relative URL patterns.The implementation properly handles SVN-specific URL patterns (
^/,../, etc.) and parses branch/tag/trunk conventions. The sentinel value approach (space" "at line 156) works correctly to distinguish between "not yet determined" and "no branch".
180-202: LGTM! Defensive parsing prevents errors from unexpected SVN info output.The dictionary comprehension (lines 195-202) now correctly filters out lines without colons, addressing the previous review concern. Error handling for E170013 provides clear feedback for invalid URLs.
256-264: LGTM! Conditional split prevents empty strings in command list.The revision handling (line 262) correctly uses conditional logic to avoid adding empty strings to the command list when
revis empty, addressing the previous review concern.
296-318: LGTM! Proper use of instance method with directory context.The
create_diffmethod correctly usesin_directory(self._path)to ensure the diff is generated in the correct repository location, and delegates filtering to thefilter_patchutility.dfetch/commands/import_.py (2)
96-96: LGTM! Instance-based API usage aligns with the refactoring.The import path has been correctly updated to
dfetch.vcs.svn, and the usage ofSvnRepo().is_svn()at line 142 properly uses the instance method with the default path to detect SVN repositories.Based on learnings, this correctly uses the superproject's VCS type for detection.
Also applies to: 142-142
155-155: LGTM! Explicit path argument improves code clarity.Line 155 explicitly passes
os.getcwd()to theSvnRepoconstructor before callingexternals(). This makes the intent clear and aligns with the instance-based API where the path is provided at construction time.tests/test_svn.py (4)
12-14: LGTM! Import paths correctly reflect the module reorganization.The imports properly separate:
SvnSubProjectfromdfetch.project.svn(project-level abstraction)External,SvnRemote,SvnRepofromdfetch.vcs.svn(VCS-level utilities)This aligns with the SuperProject/SubProject refactoring objectives.
133-147: LGTM! Test properly adapted to instance-based API.The test correctly:
- Updates all patches to the new
dfetch.vcs.svnmodule location- Uses
SvnRepo(CWD).externals()with an explicit path (line 142)- Maintains comprehensive validation of parsed externals
159-162: LGTM! Tests properly distinguish between local and remote SVN checks.The tests correctly validate:
SvnRepo().is_svn()for local repository detection (line 162)SvnRemote("some_url").is_svn()for remote URL validation (line 185)This properly tests the separation between local and remote SVN operations.
Also applies to: 181-185
227-237: LGTM! New fixtures properly support the refactored test structure.The fixtures correctly separate concerns:
svn_subproject: provides aSvnSubProjectinstance for project-level testssvn_repo: provides aSvnRepoinstance for VCS-level testsThe test at lines 231-232 validates the correct NAME attribute for SVN subprojects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
dfetch/vcs/svn.py (1)
156-178: Consider using a more explicit sentinel value for non-standard branch layouts.Using a single space
" "as a sentinel value (lines 156, 174) to indicate a non-standard SVN layout is subtle and could be confusing. Consider usingNoneor a named constant for clarity.This is a minor readability concern - the current approach is functional.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
CHANGELOG.rstdfetch/commands/import_.pydfetch/vcs/svn.pytests/test_svn.py
🚧 Files skipped from review as they are similar to previous changes (1)
- CHANGELOG.rst
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:28.644Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
📚 Learning: 2025-12-31T10:55:28.644Z
Learnt from: spoorcc
Repo: dfetch-org/dfetch PR: 895
File: dfetch/commands/check.py:133-142
Timestamp: 2025-12-31T10:55:28.644Z
Learning: In dfetch, when determining ignored files for a project, the VCS detection should use the superproject's VCS type (where the manifest is located), not the fetched project's VCS type. The ignored files are then queried from the project's destination path using the superproject's VCS system.
Applied to files:
dfetch/commands/import_.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
- GitHub Check: Publish Python distribution 📦 to TestPyPI
- GitHub Check: build / build (ubuntu-latest)
- GitHub Check: build / build (windows-latest)
- GitHub Check: build / build (macos-latest)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: run / run (windows-latest, 3.10)
- GitHub Check: run / run (windows-latest, 3.12)
- GitHub Check: run / run (windows-latest, 3.14)
- GitHub Check: run / run (windows-latest, 3.13)
- GitHub Check: run / run (macos-latest, 3.10)
- GitHub Check: run / run (windows-latest, 3.9)
- GitHub Check: run / run (macos-latest, 3.14)
- GitHub Check: run / run (windows-latest, 3.11)
- GitHub Check: run / run (ubuntu-latest, 3.12)
- GitHub Check: run / test-cygwin
- GitHub Check: test / test
- GitHub Check: DevContainer Build & Test
🔇 Additional comments (12)
dfetch/vcs/svn.py (4)
27-38: LGTM!Clean data structure definition for SVN externals with appropriate fields.
204-232: LGTM!The
get_last_changed_revisionmethod correctly handles both directory targets (usingsvnversion) and URL/file targets (usingsvn info). The regex parsing with named groups is clear.
296-318: LGTM!The
create_diffmethod properly uses instance context within_directory(self._path)and handles optionalnew_revisionparameter correctly.
52-58: Verifyexc.stderrtype consistency.
SubprocessCommandError.stderris checked withstartswith("svn: E170013"), implying a string. Ensure this is consistent with the exception's definition. Ifstderrcould be bytes, this comparison may fail silently.dfetch/commands/import_.py (2)
96-96: LGTM!Import path correctly updated to reflect the module reorganization moving
SvnRepotodfetch.vcs.svn.
140-155: LGTM!The changes correctly adapt to the new instance-based API:
SvnRepo().is_svn()for VCS detection (line 142)SvnRepo(os.getcwd()).externals()for retrieving externals (line 155)This aligns with the refactor making
externals()an instance method that respects the path context.tests/test_svn.py (6)
12-14: LGTM!Import structure correctly reflects the module reorganization with
SvnSubProjectfrom the project layer andExternal,SvnRemote,SvnRepofrom the VCS layer.
132-145: LGTM!Test correctly updated with:
- Patch paths targeting
dfetch.vcs.svnmodule- Instance-based call
SvnRepo(CWD).externals()
156-160: LGTM!Test correctly uses
SvnRepo().is_svn()as an instance method with the updated patch path.
163-183: LGTM!New test for
SvnRemote.is_svn()correctly covers the three cases: successful SVN check, subprocess error, and runtime error. The use of exception classes asside_effectworks correctly withunittest.mock.
224-235: LGTM!Well-structured fixtures for
SvnSubProjectandSvnRepowith clear test cases for theNAMEconstant andDEFAULT_BRANCHvalues.
242-273: LGTM!Comprehensive test coverage for
_split_urlcovering:
- Standard trunk layout
- Branch layout
- Non-standard layout (sentinel branch value
" ")- Tag layout
The test correctly validates all four return values (url, branch, tag, src) for each case.
Fixes #896
Summary by CodeRabbit
Documentation
New Features
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.