Skip to content

Commit

Permalink
Merge pull request #484 from ravenexp/upload
Browse files Browse the repository at this point in the history
Implement `maturin upload` command
  • Loading branch information
konstin authored Apr 9, 2021
2 parents c3ed69f + 6e06d58 commit 179d6f5
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 40 deletions.
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (for the cli, not for the crate).

## Unreleased

* The `upload` command is now implemented, it is mostly similar to `twine upload`. [#484](https://github.com/PyO3/maturin/pull/484)

## 0.10.2 - 2021-04-03

* Fix `--target` being silently ignored
Expand Down
38 changes: 38 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,44 @@ OPTIONS:
Use as `--rustc-extra-args="--my-arg"`
```

### Upload

```
Uploads python packages to pypi
It is mostly similar to `twine upload`, but can only upload python wheels and source distributions.
USAGE:
maturin upload [FLAGS] [OPTIONS] [FILE]...
FLAGS:
-h, --help
Prints help information
--skip-existing
Continue uploading files if one already exists. (Only valid when uploading to PyPI. Other implementations
may not support this.)
-V, --version
Prints version information
OPTIONS:
-p, --password <password>
Password for pypi or your custom registry. Note that you can also pass the password through MATURIN_PASSWORD
-r, --repository-url <registry>
The url of registry where the wheels are uploaded to [default: https://upload.pypi.org/legacy/]
-u, --username <username>
Username for pypi or your custom registry
ARGS:
<FILE>...
The python packages to upload
```

## Code

The main part is the maturin library, which is completely documented and should be well integrable. The accompanying `main.rs` takes care username and password for the pypi upload and otherwise calls into the library.
Expand Down
104 changes: 64 additions & 40 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use human_panic::setup_panic;
#[cfg(feature = "password-storage")]
use keyring::{Keyring, KeyringError};
use maturin::{
develop, get_metadata_for_distribution, get_pyproject_toml, source_distribution,
write_dist_info, BridgeModel, BuildOptions, CargoToml, Manylinux, Metadata21, PathWriter,
develop, get_metadata_for_distribution, get_pyproject_toml,
get_supported_version_for_distribution, source_distribution, write_dist_info, BridgeModel,
BuildOptions, BuiltWheelMetadata, CargoToml, Manylinux, Metadata21, PathWriter,
PythonInterpreter, Target,
};
use std::env;
Expand Down Expand Up @@ -166,12 +167,6 @@ struct PublishOpt {
/// Password for pypi or your custom registry. Note that you can also pass the password
/// through MATURIN_PASSWORD
password: Option<String>,
/// Do not pass --release to cargo
#[structopt(long)]
debug: bool,
/// Do not strip the library for minimum file size
#[structopt(long = "no-strip")]
no_strip: bool,
/// Continue uploading files if one already exists.
/// (Only valid when uploading to PyPI. Other implementations may not support this.)
#[structopt(long = "skip-existing")]
Expand Down Expand Up @@ -205,11 +200,17 @@ enum Opt {
Publish {
#[structopt(flatten)]
build: BuildOptions,
#[structopt(flatten)]
publish: PublishOpt,
/// Do not pass --release to cargo
#[structopt(long)]
debug: bool,
/// Do not strip the library for minimum file size
#[structopt(long = "no-strip")]
no_strip: bool,
/// Don't build a source distribution
#[structopt(long = "no-sdist")]
no_sdist: bool,
#[structopt(flatten)]
publish: PublishOpt,
},
#[structopt(name = "list-python")]
/// Searches and lists the available python installations
Expand Down Expand Up @@ -267,14 +268,15 @@ enum Opt {
#[structopt(short, long, parse(from_os_str))]
out: Option<PathBuf>,
},
/// WIP Upload command, similar to twine
/// Uploads python packages to pypi
///
/// ```
/// maturin upload dist/*
/// ```
#[structopt(name = "upload", setting = structopt::clap::AppSettings::Hidden)]
/// It is mostly similar to `twine upload`, but can only upload python wheels
/// and source distributions.
#[structopt(name = "upload")]
Upload {
/// Wheels and source distributions to upload
#[structopt(flatten)]
publish: PublishOpt,
/// The python packages to upload
#[structopt(name = "FILE", parse(from_os_str))]
files: Vec<PathBuf>,
},
Expand Down Expand Up @@ -409,32 +411,17 @@ fn pep517(subcommand: Pep517Command) -> Result<()> {

/// Handles authentication/keyring integration and retrying of the publish subcommand
#[cfg(feature = "upload")]
fn upload_ui(build: BuildOptions, publish: &PublishOpt, no_sdist: bool) -> Result<()> {
let build_context = build.into_build_context(!publish.debug, !publish.no_strip)?;

if !build_context.release {
eprintln!("⚠ Warning: You're publishing debug wheels");
}

let mut wheels = build_context.build_wheels()?;

if !no_sdist {
if let Some(source_distribution) = build_context.build_source_distribution()? {
wheels.push(source_distribution);
}
}

fn upload_ui(
wheels: &[BuiltWheelMetadata],
metadata: &[(String, String)],
publish: &PublishOpt,
) -> Result<()> {
let registry = complete_registry(&publish)?;

println!("🚀 Uploading {} packages", wheels.len());

for (wheel_path, supported_versions) in wheels {
let upload_result = upload(
&registry,
&wheel_path,
&build_context.metadata21.to_vec(),
&supported_versions,
);
let upload_result = upload(&registry, wheel_path, metadata, supported_versions);
match upload_result {
Ok(()) => (),
Err(UploadError::AuthenticationError) => {
Expand Down Expand Up @@ -521,9 +508,24 @@ fn run() -> Result<()> {
Opt::Publish {
build,
publish,
debug,
no_strip,
no_sdist,
} => {
upload_ui(build, &publish, no_sdist)?;
let build_context = build.into_build_context(!debug, !no_strip)?;

if !build_context.release {
eprintln!("⚠ Warning: You're publishing debug wheels");
}

let mut wheels = build_context.build_wheels()?;
if !no_sdist {
if let Some(sd) = build_context.build_source_distribution()? {
wheels.push(sd);
}
}

upload_ui(&wheels, &build_context.metadata21.to_vec(), &publish)?
}
Opt::ListPython => {
let target = Target::from_target_triple(None)?;
Expand Down Expand Up @@ -602,17 +604,39 @@ fn run() -> Result<()> {
.context("Failed to build source distribution")?;
}
Opt::Pep517(subcommand) => pep517(subcommand)?,
Opt::Upload { files } => {
Opt::Upload { publish, files } => {
if files.is_empty() {
println!("⚠ Warning: No files given, exiting.");
return Ok(());
}

let metadata: Vec<Vec<(String, String)>> = files
.iter()
.map(|path| get_metadata_for_distribution(&path))
.collect::<Result<_>>()?;

println!("{:?}", metadata);
// All uploaded files are expected to share the build context
// and to have identical package metadata as a result.
if !metadata.iter().all(|x| *x == metadata[0]) {
bail!(
"Attempting to upload wheel and/or source distribution files \
that belong to different python packages."
);
}

let supported_versions: Result<Vec<String>> = files
.iter()
.map(|path| get_supported_version_for_distribution(&path))
.collect();

// zip() works because `BuiltWheelMetadata` is a tuple type
let wheels: Vec<BuiltWheelMetadata> = files
.into_iter()
.zip(supported_versions?.into_iter())
.collect();

// All wheels have identical metadata - get it from metadata[0]
upload_ui(&wheels, &metadata[0], &publish)?
}
}

Expand Down

0 comments on commit 179d6f5

Please sign in to comment.