Skip to content

Conversation

@cjames23
Copy link
Contributor

@cjames23 cjames23 commented Oct 4, 2025

This PR finalizes implementation of workspaces. Some of the diff appears to be because I had the same commits locally that have already been merged for the failing tests.

The model that I went with was the same as what uv makes available so users can define

[tools.hatch.workspace] 
members = ["Packages/*"]
exclude = ["Bar"]

Things to note for feedback, spend a lot of time trying to ensure that every dependency was properly converted from the Requirement class to the child Dependency class and could not find where it kept leaking through as a Requirement instance. The result is that there are a few places where I used list comprehension to convert the entire complex list before returning it.

The workspace functionality test file definitely need a better name than configuration which is the name currently under tests/workspace/

closes: #1639

ofek and others added 30 commits October 14, 2024 00:34
# Conflicts:
#	tests/cli/self/test_self.py
@cjames23
Copy link
Contributor Author

cjames23 commented Oct 5, 2025

The diff should be much cleaner now with the additional merge I did from master.

@cjames23 cjames23 marked this pull request as ready for review October 9, 2025 01:16
…tion warnings, remove virtualenv upper bounds
Copy link
Contributor

@ofek ofek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for picking this up! I'm going to finish reviewing some time today but have you thought about the interaction between packages that are both workspace members and dependencies? I forgot how I handled that in my branch or intended on handling that if I didn't yet.


# Then check pyproject.toml
pyproject = current / "pyproject.toml"
if pyproject.exists() and _has_workspace_config(load_toml_file, str(pyproject), "tool.hatch.workspace"):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We never actually use the config_path parameter here. I'd suggest that we remove the helper function and just load in this function directly or keep the helper function if you're concerned about random errors and have it return an empty dictionary instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the primary reason for doing this was just to catch errors separately for the file loading to check if there is a workspace configuration. There might be a better way to do this but this seemed clean to avoid catching errors of the same type that might not be related to the file loading.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if you think this should still be changed

@cjames23
Copy link
Contributor Author

Thank you so much for picking this up! I'm going to finish reviewing some time today but have you thought about the interaction between packages that are both workspace members and dependencies? I forgot how I handled that in my branch or intended on handling that if I didn't yet.

There was not appropriate logic yet for handling conflicts and ensuring order. This is one idea for handling conflicts that could happen

@cached_property
    def all_dependencies_complex(self) -> list[Dependency]:
        from hatch.dep.core import Dependency

        local_deps = list(self.local_dependencies_complex)
        other_deps = list(self.dependencies_complex)

        # Create workspace member name set for conflict detection
        workspace_names = {dep.name.lower() for dep in local_deps}

        # Filter out conflicting dependencies, keeping only workspace versions
        filtered_deps = [
            dep if isinstance(dep, Dependency) else Dependency(str(dep))
            for dep in other_deps
            if dep.name.lower() not in workspace_names
        ]
        # Workspace members first to ensure precedence
        return local_deps + filtered_deps

and then to ensure workspace members take precedence in virtual environments the sync would look like this

    def sync_dependencies(self):
        with self.safe_activation():
            # Get workspace member names for conflict resolution
            workspace_names = {
                dep.name.lower() for dep in self.local_dependencies_complex
            }

            # Separate dependencies by type and filter conflicts
            standard_dependencies: list[str] = []
            editable_dependencies: list[str] = []

            for dependency in self.missing_dependencies:
                # Skip if workspace member exists
                if dependency.name.lower() in workspace_names:
                    continue

                if not dependency.editable or dependency.path is None:
                    standard_dependencies.append(str(dependency))
                else:
                    editable_dependencies.append(str(dependency.path))

            # Install workspace members first
            workspace_deps = [str(dep.path) for dep in self.local_dependencies_complex]
            if workspace_deps:
                editable_args = []
                for dep_path in workspace_deps:
                    editable_args.extend(["--editable", dep_path])
                self.platform.check_command(self.construct_pip_install_command(editable_args))

            # Then install other dependencies
            if standard_dependencies:
                self.platform.check_command(self.construct_pip_install_command(standard_dependencies))

            if editable_dependencies:
                editable_args = []
                for dependency in editable_dependencies:
                    editable_args.extend(["--editable", dependency])
                self.platform.check_command(self.construct_pip_install_command(editable_args))

Let me know your thoughts on this approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for uv workspace

2 participants