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

[project]
name = "my-monorepo"
version = "1.0.0"

[tool.hatch.envs.default]
workspace.members = ["packages/*"]
workspace.exclude = ["packages/experimental*"]
workspace.parallel = true
dependencies = ["pytest", "black", "ruff"]

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 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.

@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.

@cjames23
Copy link
Contributor Author

Refactored the code based on our discussion
Here is an example configuration now

[project]
name = "my-monorepo"
version = "1.0.0"

[tool.hatch.envs.default]
workspace.members = ["packages/*"]
workspace.exclude = ["packages/experimental*"]
workspace.parallel = true
dependencies = ["pytest", "black", "ruff"]

[tool.hatch.envs.test]
workspace.members = [
  {path = "packages/core", features = ["test"]},
  {path = "packages/utils", features = ["test"]},
  "packages/cli"
]
dependencies = ["pytest", "coverage", "pytest-cov"]
scripts.test = "pytest --cov {args}"

[tool.hatch.envs.lint]
detached = true
workspace.members = ["packages/*"]
dependencies = ["ruff", "black", "mypy"]
scripts.check = ["ruff check .", "black --check .", "mypy ."]
scripts.fmt = ["ruff check --fix .", "black ."]

[[tool.hatch.envs.ci.matrix]]
python = ["3.9", "3.10", "3.11", "3.12"]

[tool.hatch.envs.ci]
template = "test"
workspace.parallel = false  # Disable for CI stability

I think the parallel piece likely needs a good look and potentially some additional work.

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