-
Notifications
You must be signed in to change notification settings - Fork 206
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
feat: check duplicate dependencies #717
feat: check duplicate dependencies #717
Conversation
924d7b0
to
de5fe9d
Compare
de5fe9d
to
3361b76
Compare
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.
Im wondering if it would make more sense to add a custom deserializer implementation for this (using serde_with) instead. Because that would allow you to error out while deserializing the fields which provides in context error messages.
I'm not sure if it is possible to collect different fields (run, build, host dependencies) and check for duplicates in them 🤔 |
Could just throw an error if capitalization is used right? |
In that case I think its fine, because these can overwrite each other. This is OK (although weird): [dependencies]
foobar = 1
[build-dependencies]
foobar = 2 This is not OK: [dependencies]
foobar = 1
Foobar = 2 |
Thats an alternative approach, to only allow normalized names in the |
I added a function for deserializing dependencies in a1df903 I can add tests if you think it looks good. |
I think it is best to removed to chaining of the different dependency fields. As I just understood bas' comment. We broke the functionality with this changed. My plan was to error on:
But we now also error, but shouldn't on:
The plan to avoid any capitalization is still a thought but might be breaking. Lets throw it in the community. |
I see, so just checking the uppercase for each dependency section is enough? |
No its not. Especially Pypi dependencies have more rules than just lowercasing (dashes, dots and underscores). Would this work? #[serde(default)]
#[serde_as(as = "IndexMap<DisplayFromStr, PickFirst<(DisplayFromStr, _)>>")]
dependencies: IndexMap<NormalizedPackageName, NamelessMatchSpec>, |
Not really.
impl FromStr for NormalizedPackageName {
type Err = ParsePackageNameError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(PackageName::from_str(s)?.into())
}
} And it is basically the same thing as |
A maybe thats something we can change! We could implement a custom |
Thanks for the tip @baszalmstra! I went with a waaaay simpler approach in c70586c |
I hate to say this but I'm still not entirely sure about this approach. The downside of this approach is that existing I see two routes that we can take:
Id prefer we take the second route. If you don't feel as confident as I in the second route I'd be happy to take a stab at it. |
That's totally reasonable. I'm happy to take the second route and ping you in case I need any guidance 🐻 |
6de9743
to
904e832
Compare
@baszalmstra I took a look at this again and there are a couple of points that I want to clarify:
I came up with this: fn deserialize_package_map<'de, D>(
deserializer: D,
) -> Result<IndexMap<PackageName, NamelessMatchSpec>, D::Error>
where
D: Deserializer<'de>,
{
struct PackageMapVisitor(PhantomData<()>);
impl<'de> Visitor<'de> for PackageMapVisitor {
type Value = IndexMap<PackageName, NamelessMatchSpec>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a map")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut result = IndexMap::new();
while let Some((k, v)) = map.next_entry::<String, String>()? {
let package_name = PackageName::from_str(&k).map_err(serde::de::Error::custom)?;
let match_spec =
NamelessMatchSpec::from_str(&v).map_err(serde::de::Error::custom)?;
if result.insert(package_name, match_spec).is_some() {
return Err(serde::de::Error::custom(
"invalid dependency: please avoid using capitalized names for the dependencies",
));
}
}
Ok(result)
}
}
let visitor = PackageMapVisitor(PhantomData);
deserializer.deserialize_seq(visitor)
} However, this is used with - #[serde_as(as = "IndexMap<_, PickFirst<(DisplayFromStr, _)>>")]
+ #[serde(default, deserialize_with = "deserialize_package_map")]
dependencies: IndexMap<PackageName, NamelessMatchSpec>, Also, in the case of
Right now we get:
What do you mean by allowing non-normalized names? Isn't this a contextual error?
I took a look at the Maybe I got it wrong though. Do we have any usages of |
@orhun that's indeed what I had in mind. We indeed lose Instead of deserializing the key value pairs as Strings you could directly parse them as The only remaining issue is that now any error is emitted at the level of the entire |
Made the requested changes in ea724bf 2 things:
|
@ruben-arts Please test to see if you liky! |
What do you mean? The error indicates there is a duplicate flask entry which is indeed the problem? |
The add still lets you add a duplicate, leaving the toml in a broken state. I think we should never do that. |
Aaah! Yeah now I get it! That makes sense. |
Should we also check duplicates for the |
I would ask you to start on that immediately but we can merge this for now. |
fixes #715
e.g. if you add:
pixi run
fails as: