Skip to content

Commit

Permalink
Disallow direct references for dependency definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
ofek committed Mar 6, 2022
1 parent f411852 commit 5a42c42
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 16 deletions.
17 changes: 15 additions & 2 deletions backend/src/hatchling/metadata/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -991,9 +991,16 @@ def dependencies_complex(self):
raise TypeError('Dependency #{} of field `project.dependencies` must be a string'.format(i))

try:
dependencies_complex.append(Requirement(entry))
dependency = Requirement(entry)
except InvalidRequirement as e:
raise ValueError('Dependency #{} of field `project.dependencies` is invalid: {}'.format(i, e))
else:
if dependency.url:
raise ValueError(
'Dependency #{} of field `project.dependencies` cannot be a direct reference'.format(i)
)

dependencies_complex.append(dependency)

self._dependencies_complex = dependencies_complex

Expand Down Expand Up @@ -1049,13 +1056,19 @@ def optional_dependencies(self):
)

try:
Requirement(entry)
dependency = Requirement(entry)
except InvalidRequirement as e:
raise ValueError(
'Dependency #{} of option `{}` of field `project.optional-dependencies` '
'is invalid: {}'.format(i, option, e)
)
else:
if dependency.url:
raise ValueError(
'Dependency #{} of option `{}` of field `project.optional-dependencies` '
'cannot be a direct reference'.format(i, option)
)

entries.append(entry)

optional_dependency_entries[option] = sorted(entries, key=lambda s: s.lower())
Expand Down
23 changes: 12 additions & 11 deletions docs/config/dependency.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ Various version control systems (VCS) are [supported](#supported-vcs) as long as
VCS direct references are defined using one of the following formats:

```
<SCHEME>://<PATH>
<SCHEME>://<PATH>@<REVISION>
<NAME> @ <SCHEME>://<PATH>
<NAME> @ <SCHEME>://<PATH>@<REVISION>
```

You may also append a `#subdirectory=<PATH>` component for specifying the relative path to the Python package when it is not located at the root e.g. `#subdirectory=lib/foo`.
Expand All @@ -173,22 +173,22 @@ For more information, refer to [this](https://pip.pypa.io/en/stable/topics/vcs-s
=== "Git"
| Executable | Schemes | Revisions | Example |
| --- | --- | --- | --- |
| `git` | <ul><li><code>git+file</code></li><li><code>git+https</code></li><li><code>git+ssh</code></li><li><code>git+http</code> :warning:</li><li><code>git+git</code> :warning:</li><li><code>git</code> :warning:</li></ul> | <ul><li>Commit hash</li><li>Tag name</li><li>Branch name</li></ul> | `git+https://github.com/org/proj.git@v1` |
| `git` | <ul><li><code>git+file</code></li><li><code>git+https</code></li><li><code>git+ssh</code></li><li><code>git+http</code> :warning:</li><li><code>git+git</code> :warning:</li><li><code>git</code> :warning:</li></ul> | <ul><li>Commit hash</li><li>Tag name</li><li>Branch name</li></ul> | `proj @ git+https://github.com/org/proj.git@v1` |

=== "Mercurial"
| Executable | Schemes | Revisions | Example |
| --- | --- | --- | --- |
| `hg` | <ul><li><code>hg+file</code></li><li><code>hg+https</code></li><li><code>hg+ssh</code></li><li><code>hg+http</code> :warning:</li><li><code>hg+static-http</code> :warning:</li></ul> | <ul><li>Revision hash</li><li>Revision number</li><li>Tag name</li><li>Branch name</li></ul> | `hg+file:///path/to/proj@v1` |
| `hg` | <ul><li><code>hg+file</code></li><li><code>hg+https</code></li><li><code>hg+ssh</code></li><li><code>hg+http</code> :warning:</li><li><code>hg+static-http</code> :warning:</li></ul> | <ul><li>Revision hash</li><li>Revision number</li><li>Tag name</li><li>Branch name</li></ul> | `proj @ hg+file:///path/to/proj@v1` |

=== "Subversion"
| Executable | Schemes | Revisions | Example |
| --- | --- | --- | --- |
| `svn` | <ul><li><code>svn+https</code></li><li><code>svn+ssh</code></li><li><code>svn+http</code> :warning:</li><li><code>svn+svn</code> :warning:</li><li><code>svn</code> :warning:</li></ul> | <ul><li>Revision number</li></ul> | `svn+file:///path/to/proj` |
| `svn` | <ul><li><code>svn+https</code></li><li><code>svn+ssh</code></li><li><code>svn+http</code> :warning:</li><li><code>svn+svn</code> :warning:</li><li><code>svn</code> :warning:</li></ul> | <ul><li>Revision number</li></ul> | `proj @ svn+file:///path/to/proj` |

=== "Bazaar"
| Executable | Schemes | Revisions | Example |
| --- | --- | --- | --- |
| `bzr` | <ul><li><code>bzr+https</code></li><li><code>bzr+ssh</code></li><li><code>bzr+sftp</code></li><li><code>bzr+lp</code></li><li><code>bzr+http</code> :warning:</li><li><code>bzr+ftp</code> :warning:</li></ul> | <ul><li>Revision number</li><li>Tag name</li></ul> | `bzr+lp:proj@v1` |
| `bzr` | <ul><li><code>bzr+https</code></li><li><code>bzr+ssh</code></li><li><code>bzr+sftp</code></li><li><code>bzr+lp</code></li><li><code>bzr+http</code> :warning:</li><li><code>bzr+ftp</code> :warning:</li></ul> | <ul><li>Revision number</li><li>Tag name</li></ul> | `proj @ bzr+lp:proj@v1` |

### Local

Expand All @@ -208,12 +208,13 @@ The `<PATH>` can refer to a source archive, a wheel, or a directory containing a
| Wheel | `file:///path/to/pkg.whl` | `file:///c:/path/to/pkg.whl` |
| Directory | `file:///path/to/pkg` | `file:///c:/path/to/pkg` |

You may also specify paths [relative](https://www.rfc-editor.org/rfc/rfc3986#section-4.2) to your project's root directory on all platforms by omitting the leading slashes and beginning the path with a dot:
!!! tip
When running [commands](environment.md#commands) that invoke `pip` directly, you may also specify paths [relative](https://www.rfc-editor.org/rfc/rfc3986#section-4.2) to your project's root directory on all platforms by omitting the leading slashes and beginning the path with a dot:

```
file:./pkg_inside_project
file:../pkg_alongside_project
```
```
file:./pkg_inside_project
file:../pkg_alongside_project
```

### Remote

Expand Down
4 changes: 4 additions & 0 deletions docs/meta/history.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ This is the first release candidate for Hatch v1, a complete rewrite.

### Unreleased

***Fixed:***

- Disallow direct references for dependency definitions

### [0.18.0](https://github.com/ofek/hatch/releases/tag/hatchling-v0.18.0) - 2022-02-27 ### {: #hatchling-v0.18.0 }

***Added:***
Expand Down
29 changes: 26 additions & 3 deletions tests/backend/metadata/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1047,12 +1047,22 @@ def test_entry_not_string(self, isolation):
with pytest.raises(TypeError, match='Dependency #1 of field `project.dependencies` must be a string'):
_ = metadata.core.dependencies

def test_invalid_specifier(self, isolation):
def test_invalid(self, isolation):
metadata = ProjectMetadata(str(isolation), None, {'project': {'dependencies': ['foo^1']}})

with pytest.raises(ValueError, match='Dependency #1 of field `project.dependencies` is invalid: .+'):
_ = metadata.core.dependencies

def test_direct_reference(self, isolation):
metadata = ProjectMetadata(
str(isolation), None, {'project': {'dependencies': ['proj @ git+https://github.com/org/proj.git@v1']}}
)

with pytest.raises(
ValueError, match='Dependency #1 of field `project.dependencies` cannot be a direct reference'
):
_ = metadata.core.dependencies

def test_correct(self, isolation):
metadata = ProjectMetadata(str(isolation), None, {'project': {'dependencies': ['foo', 'bar', 'Baz']}})

Expand Down Expand Up @@ -1081,7 +1091,7 @@ def test_not_table(self, isolation):
with pytest.raises(TypeError, match='Field `project.optional-dependencies` must be a table'):
_ = metadata.core.optional_dependencies

def test_specifiers_not_array(self, isolation):
def test_definitions_not_array(self, isolation):
metadata = ProjectMetadata(str(isolation), None, {'project': {'optional-dependencies': {'foo': 5}}})

with pytest.raises(
Expand All @@ -1097,14 +1107,27 @@ def test_entry_not_string(self, isolation):
):
_ = metadata.core.optional_dependencies

def test_invalid_specifier(self, isolation):
def test_invalid(self, isolation):
metadata = ProjectMetadata(str(isolation), None, {'project': {'optional-dependencies': {'foo': ['bar^1']}}})

with pytest.raises(
ValueError, match='Dependency #1 of option `foo` of field `project.optional-dependencies` is invalid: .+'
):
_ = metadata.core.optional_dependencies

def test_direct_reference(self, isolation):
metadata = ProjectMetadata(
str(isolation),
None,
{'project': {'optional-dependencies': {'foo': ['proj @ git+https://github.com/org/proj.git@v1']}}},
)

with pytest.raises(
ValueError,
match='Dependency #1 of option `foo` of field `project.optional-dependencies` cannot be a direct reference',
):
_ = metadata.core.optional_dependencies

def test_correct(self, isolation):
metadata = ProjectMetadata(
str(isolation),
Expand Down

0 comments on commit 5a42c42

Please sign in to comment.