Workspace Pyproject.toml: No [project].name Required?
Hey guys! Let's dive into a fascinating discussion around pyproject.toml
files, specifically when dealing with workspace-only setups. Have you ever run into the issue where you're working on a monorepo using uv workspaces, and you're forced to add a dummy root package just to satisfy some validation? Well, this article is for you! We're going to explore a proposal that aims to make our lives a little easier when managing Python projects in a monorepo setup. Buckle up, and let’s get started!
The Problem: pyproject.toml
Missing [project].name
So, the core issue here is that when you have a repository root acting as a workspace-only coordinator (think [tool.uv.workspace]
), but without a root distribution, tools like jackpkgs.python
throw an error. This error, “pyproject.toml
is missing [project].name,”
can be a real headache. It essentially forces you to create a placeholder root package just to keep the validation happy. It's like having to wear a tie to a Zoom meeting – technically required, but kinda pointless, right? This problem becomes even more pronounced when you're trying to maintain a clean and streamlined monorepo structure, where the root isn't meant to be a distributable package itself.
Current Pain Points
Currently, if you stumble into this scenario, you'll see the dreaded error message: jackpkgs.python: pyproject.toml is missing [project].name
. It's not a friendly message, is it? Furthermore, if you do have a [project]
section and you're using an editable environment for your devshell, things can get even trickier. Tools like uv_build
will try to build the root, expecting a src/<name>/__init__.py
file, which, of course, fails spectacularly when your root isn't a real distribution. This situation is not ideal because it forces developers to either create fake root distributions or move away from editable dev environments, which slows down the development feedback loop. We want to avoid clutter and keep things efficient, just like a well-organized desk – or in this case, a well-organized monorepo.
Why This Matters
The motivation behind solving this issue is pretty straightforward: we want to support monorepos using uv workspaces where only the member packages are meant to be built and distributed. We want to avoid the unnecessary overhead of creating fake root distributions. Plus, we definitely want to stick with editable dev environments because they're just so much more convenient for development. It’s all about streamlining the process and making our development workflow smoother and more enjoyable. Who wants to fight with tooling when you could be writing code, am I right?
The Proposed Solution: A More Flexible Approach
Okay, so how do we fix this? The proposal here is to introduce a more flexible approach that understands when the root is meant to be a workspace coordinator and not a distributable package. The core idea revolves around these key points:
- Treating the root as workspace-only: When
[tool.uv.workspace]
exists and[project]
is absent, we should recognize this as a workspace-only setup. No errors, no attempts to build a root distribution. Simple and effective. - Workspace-level configuration: We can add optional workspace-level configurations in the root
pyproject.toml
file. This allows us to specify things like a workspace name (for derivation naming) and whether the root should be considered editable. - Flake module overrides: For those using Nix, we can provide flake module overrides for even more control. This lets you override settings like the workspace name and whether the root is editable directly from your Nix configuration.
Diving Deeper into the Proposal
Let's break down each of these points a bit further.
1. Workspace-Only Recognition
The first step is to recognize when a root project is intended solely as a workspace coordinator. This is achieved by checking for the presence of [tool.uv.workspace]
and the absence of [project]
. If this condition is met, the tool should simply skip any attempts to build the root distribution. This is a straightforward change that avoids unnecessary errors and build attempts.
2. Workspace-Level Configuration
To add more flexibility, the proposal suggests adding an optional [tool.jackpkgs]
section in the root pyproject.toml
file. This section can contain configuration options specific to workspace behavior. Here’s an example:
[tool.jackpkgs]
workspace-name = "zeus" # used for derivation naming when [project] is absent
editable-root = false # skip editable install of the root in workspace-only mode
In this example, workspace-name
is used for naming derivations when [project]
is absent, and editable-root
allows you to skip the editable install of the root in workspace-only mode. This provides a clean way to configure workspace-specific behavior without cluttering the [project]
section.
3. Flake Module Overrides
For those of you who are Nix enthusiasts, this part is especially exciting. The proposal suggests providing flake module overrides for full control from Nix. This means you can override settings like workspaceName
and editableRoot
directly from your Nix configuration. This gives you a high degree of control over the build process and allows you to tailor it to your specific needs. The proposed overrides are:
jackpkgs.python.workspaceName
(string, default: repo basename)jackpkgs.python.editableRoot
(bool, default:true
if[project]
exists;false
otherwise)
Derivation Naming Logic
To give you a clearer picture, let’s look at a sketch of the proposed derivation naming logic:
let
py = pyproject-lib.load workspaceRoot; # parse pyproject.toml
hasProject = py.project or null != null && py.project ? name;
workspaceCfg = py.tool.jackpkgs or {};
workspaceName =
config.jackpkgs.python.workspaceName or
(workspaceCfg.workspace-name or (lib.baseNameOf workspaceRoot));
projectName = if hasProject then py.project.name else workspaceName;
editableRoot =
config.jackpkgs.python.editableRoot or
(workspaceCfg.editable-root or hasProject);
in {
# Use `projectName` for naming python env derivations.
# Only include the root distribution in an editable env if `editableRoot` is true.
# When `hasProject` is false, skip root distribution entirely and only wire up workspace members.
}
This logic ensures that the projectName
is used for naming Python environment derivations, and the root distribution is only included in an editable environment if editableRoot
is true. When hasProject
is false, the root distribution is skipped entirely, and only the workspace members are wired up. This keeps things clean and efficient.
Backwards Compatibility: No Breaking Changes!
One of the critical aspects of any proposal is how it affects existing projects. The good news here is that this proposal is designed to be backwards compatible. If [project]
exists in your pyproject.toml
, the behavior remains unchanged. This means you can adopt this new approach gradually without worrying about breaking your existing setups. If [project]
is absent and [tool.uv.workspace]
exists, the system will treat the project as workspace-only, using workspaceName
for derivation naming and skipping the root build. This ensures a smooth transition for everyone.
The Impact on Your Workflow
Imagine you're setting up a new monorepo. With this proposal, you can create a clean workspace-only root without needing to add a dummy package. Your development environment setup becomes simpler, and you can focus on the actual packages you're developing. This streamlined approach reduces clutter and confusion, making your development process more efficient.
Alternatives Considered: Why This Approach?
Of course, there are always alternative solutions to consider. Let’s briefly touch on a couple of alternatives and why this proposal is a better fit.
Alternative 1: Add a Stub src/<name>/__init__.py
One alternative would be to add a stub src/<name>/__init__.py
file to the root, just to satisfy the build process. However, this adds clutter and confusion. It’s a workaround that doesn’t really address the underlying issue. It’s like putting a band-aid on a broken leg – it might cover the problem, but it doesn’t fix it.
Alternative 2: Switch Devshell to Non-Editable Env
Another option is to switch your devshell to a non-editable environment. While this would avoid the build errors, it comes at the cost of a slower feedback loop when developing member packages. Editable environments are incredibly convenient because they allow you to see changes instantly without having to rebuild and reinstall packages. Giving that up would be a significant step backward in terms of developer experience.
Why This Proposal Wins
This proposal addresses the root cause of the issue without introducing unnecessary clutter or sacrificing developer convenience. By recognizing workspace-only roots and providing flexible configuration options, it offers a clean and efficient solution. It's like upgrading from a manual to an automatic transmission – smoother, more efficient, and less hassle.
Acceptance Criteria: How We Know It Works
So, how do we know if this proposal is successful? There are a few key acceptance criteria we can look at:
- Workspace-only repos can build dev envs without error: This is the most fundamental criterion. We need to ensure that workspace-only repositories (those without
[project]
but with[tool.uv.workspace]
) can build development environments without encountering errors. - Python env derivations are named consistently: We want to make sure that Python environment derivations are named consistently using the
workspaceName
. This makes it easier to manage and identify your environments. - Root is not built in editable mode unless explicitly enabled: The root should not be built in editable mode unless
editableRoot
is explicitly enabled or[project]
exists. This prevents unnecessary build attempts and keeps the process clean.
Example Error for Reference: The Ghost of Errors Past
To really drive the point home, let’s revisit the error message we’re trying to avoid:
error: jackpkgs.python: pyproject.toml is missing [project].name
This is the ghost we're trying to banish from our development workflows. With this proposal, we can hopefully lay this error to rest for good.
Conclusion: A Brighter Future for Monorepos
In conclusion, this proposal offers a thoughtful and practical solution to the challenges of managing workspace-only pyproject.toml
files. By recognizing workspace-only roots, providing flexible configuration options, and ensuring backwards compatibility, it promises to streamline our development workflows and make working with monorepos a more enjoyable experience. It’s all about making our tools work for us, not against us.
I’m happy to open a PR if this direction sounds good to you guys! Let's continue the discussion and make our development lives a little bit easier.