Skip to content

Commit

Permalink
Deny missing docs (#22)
Browse files Browse the repository at this point in the history
Added deny missing docs and code comments in a lot of places!
  • Loading branch information
tdejager authored Sep 29, 2023
1 parent 379eb19 commit 73f65e6
Show file tree
Hide file tree
Showing 16 changed files with 182 additions and 54 deletions.
1 change: 1 addition & 0 deletions .codespell-whitelist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
crate
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ repos:
- repo: https://github.com/codespell-project/codespell
rev: v2.2.5
hooks:
- id: codespell
- id: codespell
args: [--ignore-words=.codespell-whitelist.txt]
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
[crates-badge]: https://img.shields.io/crates/v/rattler_installs_packages.svg


`RIP` is a library that allows the resolving and installing of Python [PyPi](https://pypi.org/) packages from Rust into a virtual environment.
`RIP` is a library that allows the resolving and installing of Python [PyPI](https://pypi.org/) packages from Rust into a virtual environment.
It's based on our experience with building [Rattler](https://github.com/mamba-org/rattler) and aims to provide the same
experience but for PyPi instead of Conda.
experience but for PyPI instead of Conda.
It should be fast and easy to use. Like Rattler, this library is not a package manager itself but provides the low-level plumbing to be used in one.

`RIP` is based on the quite excellent work of [posy](https://github.com/njsmith/posy) and we have tried to credit
Expand All @@ -41,19 +41,19 @@ We've added a small binary to showcase this:

![flask-install](https://github.com/prefix-dev/rip/assets/4995967/5b0356b6-8e06-47bb-9424-94b3fdd9da09)

This showcases the downloading and caching of metadata from PyPi. As well as the package resolution using our solver, more on this below.
This showcases the downloading and caching of metadata from PyPI. As well as the package resolution using our solver, more on this below.
We cache everything in a local directory so that we can re-use the metadata and don't have to download it again.

## Features

This is a list of current and planned features of `RIP`, the biggest are listed below:

* [x] Downloading and aggressive caching of PyPi metadata.
* [x] Resolving of PyPi packages using [Resolvo](https://github.com/mamba-org/resolvo).
* [x] Downloading and aggressive caching of PyPI metadata.
* [x] Resolving of PyPI packages using [Resolvo](https://github.com/mamba-org/resolvo).
* [ ] Installation of wheel files (planned)
* [ ] Support sdist files (planned)

More intricacies of the PyPi ecosystem need to be implemented, see our GitHub issues for more details.
More intricacies of the PyPI ecosystem need to be implemented, see our GitHub issues for more details.


# Solver
Expand All @@ -65,6 +65,6 @@ This feature can be enabled with the `resolvo-pypi` feature flag.

## Contributing 😍

We would love to have you contribute!
See the CONTRIBUTION.md for more info. For questions, requests or a casual chat, we are very active on our discord server.
We would love to have you contribute!
See the CONTRIBUTION.md for more info. For questions, requests or a casual chat, we are very active on our discord server.
You can [join our discord server via this link][chat-url].
8 changes: 8 additions & 0 deletions crates/rattler_installs_packages/src/artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use std::io::Read;
use std::str::FromStr;
use zip::ZipArchive;

/// Trait that represents an artifact type in the PyPI ecosystem.
/// Currently implemented for [`Wheel`] files.
#[async_trait]
pub trait Artifact: Sized {
/// The name of the artifact which describes the artifact.
Expand All @@ -26,6 +28,9 @@ pub trait Artifact: Sized {
fn name(&self) -> &Self::Name;
}

/// Wheel file in the PyPI ecosystem.
/// See the [Reference Page](https://packaging.python.org/en/latest/specifications/binary-distribution-format/#binary-distribution-format)
/// for more information.
pub struct Wheel {
name: WheelName,
archive: Mutex<ZipArchive<Box<dyn ReadAndSeek + Send>>>,
Expand All @@ -47,8 +52,11 @@ impl Artifact for Wheel {
}
}

/// Trait that represents an artifact that contains metadata.
/// Currently implemented for [`Wheel`] files.
#[async_trait]
pub trait MetadataArtifact: Artifact {
/// Associated type for the metadata of this artifact.
type Metadata;

/// Parses the metadata associated with an artifact.
Expand Down
33 changes: 32 additions & 1 deletion crates/rattler_installs_packages/src/artifact_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ use thiserror::Error;
/// providing flexibility and clarity when working with Python package distributions.
#[derive(Debug, Clone, PartialOrd, Ord, Eq, PartialEq, DeserializeFromStr, SerializeDisplay)]
pub enum ArtifactName {
/// Wheel artifact
Wheel(WheelName),
/// Sdist artifact
SDist(SDistName),
}

Expand Down Expand Up @@ -70,7 +72,10 @@ impl Display for ArtifactName {
}
}

// https://packaging.python.org/specifications/binary-distribution-format/#file-name-convention
/// Structure that contains the information that is contained in a wheel name
/// See: [File Name Convention](https://www.python.org/dev/peps/pep-0427/#file-name-convention),
/// and: [PyPA Conventions](https://packaging.python.org/en/latest/specifications/),
/// for more details regarding the structure of a wheel name.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct WheelName {
/// Distribution name, e.g. ‘django’, ‘pyramid’.
Expand All @@ -83,14 +88,20 @@ pub struct WheelName {
pub build_tag: Option<BuildTag>,

/// Language implementation and version tag
/// E.g. ‘py27’, ‘py2’, ‘py3’.
pub py_tags: Vec<String>,

/// ABI specific tags
/// E.g. ‘cp33m’, ‘abi3’, ‘none’.
pub abi_tags: Vec<String>,

/// Architecture specific tags
/// E.g. ‘linux_x86_64’, ‘any’, ‘manylinux_2_17_x86_64’
pub arch_tags: Vec<String>,
}

impl WheelName {
/// Creates a set of all tags that are contained in this wheel name.
pub fn all_tags(&self) -> HashSet<String> {
let mut retval = HashSet::new();
for py in &self.py_tags {
Expand Down Expand Up @@ -139,6 +150,7 @@ impl Display for BuildTag {
}
}

/// Structure that contains the information that is contained in a source distribution name
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct SDistName {
/// Distribution name, e.g. ‘django’, ‘pyramid’.
Expand Down Expand Up @@ -172,6 +184,7 @@ impl Display for SDistName {

/// Describes the format in which the source distribution is shipped.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[allow(missing_docs)]
pub enum SDistFormat {
Zip,
TarGz,
Expand All @@ -181,7 +194,9 @@ pub enum SDistFormat {
Tar,
}

/// An error that can occur when parsing an artifact name
#[derive(Debug, Clone, Error)]
#[allow(missing_docs)]
pub enum ParseArtifactNameError {
#[error("invalid artifact name")]
InvalidName,
Expand Down Expand Up @@ -332,7 +347,10 @@ impl FromStr for ArtifactName {

/// A trait to convert the general [`ArtifactName`] into a specialized artifact name. This is useful
/// to generically fetch the underlying specialized name.
///
/// Currently we provide implementations for wheels and sdists.
pub trait InnerAsArtifactName {
/// Tries to convert the general [`ArtifactName`] into a specialized artifact name.
fn try_as(name: &ArtifactName) -> Option<&Self>;
}

Expand Down Expand Up @@ -361,6 +379,19 @@ mod test {
assert_eq!(sn.to_string(), "trio-0.19a0.tar.gz");
}

#[test]
fn test_many_linux() {
let n: WheelName =
"numpy-1.26.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
.parse()
.unwrap();

assert_eq!(
n.arch_tags,
vec!["manylinux_2_17_x86_64", "manylinux2014_x86_64"]
);
}

#[test]
fn test_wheel_name_from_str() {
let n: WheelName = "trio-0.18.0-py3-none-any.whl".parse().unwrap();
Expand Down
1 change: 1 addition & 0 deletions crates/rattler_installs_packages/src/extra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use std::str::FromStr;
use thiserror::Error;

#[derive(Debug, Clone, Eq, DeserializeFromStr)]
/// Structure that holds both the source string and the normalized version of an extra.
pub struct Extra {
/// The original string this instance was created from
source: Box<str>,
Expand Down
6 changes: 4 additions & 2 deletions crates/rattler_installs_packages/src/file_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use std::{

/// Types that implement this can be used as keys of the [`FileStore`].
pub trait CacheKey {
/// Returns the path prefix that should be used to store the data for this key.
fn key(&self) -> PathBuf;
}

Expand Down Expand Up @@ -62,6 +63,7 @@ impl CacheKey for ArtifactHashes {
}

#[derive(Debug)]
/// A cache that stores its data as cbor files on the filesystem.
pub struct FileStore {
base: PathBuf,
tmp: PathBuf,
Expand Down Expand Up @@ -259,7 +261,7 @@ fn lock(path: &Path, mode: LockMode) -> io::Result<File> {
open_options.write(true);

// Only create the parent directories if the lock mode is set to `Lock`. In the other case we
// don't care if the file doesnt exist.
// don't care if the file doesn't exist.
if mode == LockMode::Lock {
let dir = lock_path
.parent()
Expand All @@ -271,7 +273,7 @@ fn lock(path: &Path, mode: LockMode) -> io::Result<File> {
// Open the lock file
let lock = open_options.open(&lock_path)?;

// Lock the file. On unix this is apparently a thin wrapper around flock(2) and it doesnt
// Lock the file. On unix this is apparently a thin wrapper around flock(2) and it doesn't
// properly handle EINTR so we keep retrying when that happens.
retry_interrupted(|| lock.lock_exclusive())?;

Expand Down
17 changes: 3 additions & 14 deletions crates/rattler_installs_packages/src/html.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
// Derived from
// https://github.com/servo/html5ever/blob/master/html5ever/examples/noop-tree-builder.rs
// Which has the following copyright header:
//
// Copyright 2014-2017 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Module for parsing different HTML pages from PyPI repository
use std::{borrow::Borrow, default::Default};

use crate::{ArtifactHashes, ArtifactName};
Expand All @@ -33,7 +21,7 @@ fn parse_hash(s: &str) -> Option<ArtifactHashes> {
}
}

pub fn into_artifact_info(base: &Url, tag: &HTMLTag) -> Option<ArtifactInfo> {
fn into_artifact_info(base: &Url, tag: &HTMLTag) -> Option<ArtifactInfo> {
let attributes = tag.attributes();
// Get first href attribute to use as filename
let href = attributes.get("href").flatten()?.as_utf8_str();
Expand Down Expand Up @@ -95,6 +83,7 @@ pub fn into_artifact_info(base: &Url, tag: &HTMLTag) -> Option<ArtifactInfo> {
})
}

/// Parses information regarding the different artifacts for a project
pub fn parse_project_info_html(base: &Url, body: &str) -> miette::Result<ProjectInfo> {
let dom = tl::parse(body, tl::ParserOptions::default()).into_diagnostic()?;
let variants = dom.query_selector("a");
Expand Down
8 changes: 7 additions & 1 deletion crates/rattler_installs_packages/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! RIP is a library that allows the resolving and installing of Python PyPI packages from Rust into a virtual environment.
//! It's based on our experience with building Rattler and aims to provide the same experience but for PyPI instead of Conda.
//! It should be fast and easy to use.
//! Like Rattler, this library is not a package manager itself but provides the low-level plumbing to be used in one.
#![deny(missing_docs)]
mod artifact;
mod artifact_name;
mod core_metadata;
Expand Down Expand Up @@ -34,7 +40,7 @@ pub use package_name::{NormalizedPackageName, PackageName, ParsePackageNameError
pub use pep440::Version;
pub use project_info::{ArtifactHashes, ArtifactInfo, DistInfoMetadata, Meta, ProjectInfo, Yanked};
pub use requirement::{
marker, PackageRequirement, ParseExtra, PythonRequirement, Requirement, UserRequirement,
marker, PackageRequirement, ParseExtraInEnv, PythonRequirement, Requirement, UserRequirement,
};
pub use specifier::{CompareOp, Specifier, Specifiers};

Expand Down
1 change: 1 addition & 0 deletions crates/rattler_installs_packages/src/package_database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use std::io::Read;
use std::path::PathBuf;
use url::Url;

/// Cache of the available packages, artifacts and their metadata.
pub struct PackageDb {
http: Http,

Expand Down
4 changes: 4 additions & 0 deletions crates/rattler_installs_packages/src/package_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ impl PackageName {
}

#[derive(Debug, Clone, Error, Diagnostic)]
/// Error when parsing a package name
#[allow(missing_docs)]
pub enum ParsePackageNameError {
#[error("invalid package name '{0}'")]
InvalidPackageName(String),
Expand Down Expand Up @@ -99,6 +101,8 @@ impl Serialize for PackageName {
}
}

/// A normalized package name. This is a string that is guaranteed to be a valid python package string
/// this is described in [PEP 503 (Normalized Names)](https://www.python.org/dev/peps/pep-0503/#normalized-names).
#[repr(transparent)]
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct NormalizedPackageName(Box<str>);
Expand Down
15 changes: 15 additions & 0 deletions crates/rattler_installs_packages/src/project_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,24 @@ pub struct ProjectInfo {
pub files: Vec<ArtifactInfo>,
}

/// Describes a single artifact that is available for download.
#[serde_as]
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct ArtifactInfo {
/// Artifact name
pub filename: ArtifactName,
/// Url to download the artifact
pub url: url::Url,
/// Hashes of the artifact
pub hashes: Option<ArtifactHashes>,
/// Python requirement
pub requires_python: Option<String>,
#[serde(default)]
/// This attribute specified if the metadata is available
/// as a separate download described in [PEP 658](https://www.python.org/dev/peps/pep-0658/)
pub dist_info_metadata: DistInfoMetadata,
/// Yanked information
#[serde(default)]
pub yanked: Yanked,
}
Expand All @@ -46,6 +54,7 @@ impl ArtifactInfo {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct ArtifactHashes {
#[serde_as(as = "Option<SerializableHash<Sha256>>")]
/// Contains the optional sha256 hash of the artifact
pub sha256: Option<rattler_digest::Sha256Hash>,
}

Expand All @@ -61,7 +70,9 @@ impl ArtifactHashes {
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
#[serde(from = "Option<RawDistInfoMetadata>")]
pub struct DistInfoMetadata {
/// True if the metadata is available
pub available: bool,
/// Hashes to verify the metadata file
pub hashes: ArtifactHashes,
}

Expand Down Expand Up @@ -99,6 +110,7 @@ impl From<Option<RawDistInfoMetadata>> for DistInfoMetadata {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Meta {
#[serde(rename = "api-version")]
/// Version of the API
pub version: String,
}

Expand All @@ -117,10 +129,13 @@ enum RawYanked {
WithReason(String),
}

/// Struct that describes whether a package is yanked or not.
#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
#[serde(from = "RawYanked")]
pub struct Yanked {
/// This is true if the package is yanked.
pub yanked: bool,
/// Optional reason why the package is yanked.
pub reason: Option<String>,
}

Expand Down
Loading

0 comments on commit 73f65e6

Please sign in to comment.