Skip to content

Commit

Permalink
Expand on xtask (#2176)
Browse files Browse the repository at this point in the history
* Fix Windows OSError

* Ignore .pyd files

* Put things in modules

* Rename functions to `run`

* Expand on cargo xtask

* Try to work around missing installs

* Run all things by default, but not llvm-cov

* Test msrv

* Fix more OSErrors

* Various refinements and docs

* Various refinements

* Various refinements
  • Loading branch information
mejrs committed Mar 18, 2022
1 parent 3eb654c commit 16ad15e
Show file tree
Hide file tree
Showing 17 changed files with 622 additions and 209 deletions.
8 changes: 1 addition & 7 deletions .cargo/config
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
[alias]
xtask = "run --package xtask --"
pyo3_doc = "doc --lib --no-default-features --features=full --no-deps --workspace --open --exclude pyo3-macros --exclude pyo3-macros-backend"
pyo3_doc_scrape = "doc --lib --no-default-features --features=full --no-deps --workspace --open --exclude pyo3-macros --exclude pyo3-macros-backend -Z unstable-options -Z rustdoc-scrape-examples=examples"
pyo3_doc_internal = "doc --lib --no-default-features --features=full --no-deps --workspace --open --document-private-items -Z unstable-options -Z rustdoc-scrape-examples=examples"

[build]
rustdocflags = ["--cfg", "docsrs"]

[target.'cfg(feature = "cargo-clippy")']
rustflags = [
Expand All @@ -21,4 +15,4 @@ rustflags = [
"-Dclippy::todo",
"-Dclippy::unnecessary_wraps",
"-Dclippy::useless_transmute",
]
]
9 changes: 3 additions & 6 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ Please consider adding the following to your pull request:
- docs to all new functions and / or detail in the guide
- tests for all new or changed functions

Be aware the CI pipeline will check your pull request for the following. This is done using `nox` (you can install with `pip install nox`):
- Rust tests (`cargo test` or `nox -s test-rust`)
- Examples (`nox -s test-py`)
- Rust lints (`nox -s clippy`)
- Rust formatting (`nox -s fmt-rust`)
- Python formatting (`nox -s fmt-py`)
PyO3's CI pipeline will check your pull request. To run its tests
locally, you can run ```cargo xtask ci```. See its documentation
[here](https://github.com/PyO3/pyo3/tree/main/xtask#readme).
4 changes: 2 additions & 2 deletions .github/workflows/guide.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
mkdir target
mkdir -p gh-pages-build/internal
echo "<div class='internal-banner' style='position:fixed; z-index: 99999; color:red;border:3px solid red;margin-left: auto; margin-right: auto; width: 430px;left:0;right: 0;'><div style='display: flex; align-items: center; justify-content: center;'> ⚠️ Internal Docs ⚠️ Not Public API 👉 <a href='https://pyo3.rs/main/doc/pyo3/index.html' style='color:red;text-decoration:underline;'>Official Docs Here</a></div></div>" > target/banner.html
cargo +nightly pyo3_doc_internal
cargo xtask doc --internal
cp -r target/doc gh-pages-build/internal
env:
RUSTDOCFLAGS: "--cfg docsrs --Z unstable-options --document-hidden-items --html-before-content target/banner.html"
Expand All @@ -71,7 +71,7 @@ jobs:
# This adds the docs to gh-pages-build/doc
- name: Build the doc
run: |
cargo +nightly pyo3_doc_scrape
cargo xtask doc
cp -r target/doc gh-pages-build/doc
echo "<meta http-equiv=refresh content=0;url=pyo3/index.html>" > gh-pages-build/doc/index.html
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ guide/book/
extensions/stamps/
pip-wheel-metadata
valgrind-python.supp
*.pyd
6 changes: 5 additions & 1 deletion Contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ There are some specific areas of focus where help is currently needed for the do
- Not all APIs had docs or examples when they were made. The goal is to have documentation on all PyO3 APIs ([#306](https://github.com/PyO3/pyo3/issues/306)). If you see an API lacking a doc, please write one and open a PR!

You can build the docs (including all features) with
```cargo +nightly pyo3_doc_scrape```
```cargo xtask doc --open```

#### Doctests

Expand Down Expand Up @@ -87,6 +87,10 @@ Tests run with all supported Python versions with the latest stable Rust compile

If you are adding a new feature, you should add it to the `full` feature in our *Cargo.toml** so that it is tested in CI.

You can run these tests yourself with
```cargo xtask ci```
See [it's documentation](https://github.com/PyO3/pyo3/tree/main/xtask#readme)for more commands you can run.

## Python and Rust version support policy

PyO3 aims to keep sufficient compatibility to make packaging Python extensions built with PyO3 feasible on most common package managers.
Expand Down
5 changes: 3 additions & 2 deletions pytests/tests/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ def tzname(self, dt):
else:
raise RuntimeError("unexpected pointer size: " + repr(_pointer_size))
IS_WINDOWS = sys.platform == "win32"

if IS_WINDOWS:
MIN_DATETIME = pdt.datetime(1970, 1, 2, 0, 0)
MIN_DATETIME = pdt.datetime(1971, 1, 2, 0, 0)
if IS_32_BIT:
MAX_DATETIME = pdt.datetime(3001, 1, 19, 4, 59, 59)
else:
Expand Down Expand Up @@ -227,7 +228,7 @@ def test_datetime_typeerror():


@given(dt=st.datetimes(MIN_DATETIME, MAX_DATETIME))
@example(dt=pdt.datetime(1970, 1, 2, 0, 0))
@example(dt=pdt.datetime(1971, 1, 2, 0, 0))
def test_datetime_from_timestamp(dt):
if PYPY and dt < pdt.datetime(1900, 1, 1):
pytest.xfail("pdt.datetime.timestamp will raise on PyPy with dates before 1900")
Expand Down
4 changes: 4 additions & 0 deletions xtask/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ name = "xtask"
version = "0.1.0"
edition = "2018"

[[bin]]
name = "xtask"

[dependencies]
anyhow = "1.0.51"

# Clap 3 requires MSRV 1.54
rustversion = "1.0"
structopt = { version = "0.3", default-features = false }
clap = { version = "2" }
23 changes: 23 additions & 0 deletions xtask/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## Commands to test PyO3.

To run these commands, you should be in PyO3's root directory, and run (for example) `cargo xtask ci`.

```
USAGE:
xtask.exe <SUBCOMMAND>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
SUBCOMMANDS:
ci Runs everything
clippy Runs `clippy`, denying all warnings
coverage Runs `cargo llvm-cov` for the PyO3 codebase
default Only runs the fast things (this is used if no command is specified)
doc Attempts to render the documentation
fmt Checks Rust and Python code formatting with `rustfmt` and `black`
help Prints this message or the help of the given subcommand(s)
test Runs various variations on `cargo test`
test-py Runs the tests in examples/ and pytests/
```
201 changes: 201 additions & 0 deletions xtask/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
use crate::utils::*;
use anyhow::{ensure, Result};
use std::io;
use std::process::{Command, Stdio};
use std::time::Instant;
use structopt::StructOpt;

pub const MSRV: &str = "1.48";

#[derive(StructOpt)]
pub enum Subcommand {
/// Only runs the fast things (this is used if no command is specified)
Default,
/// Runs everything
Ci,
/// Checks Rust and Python code formatting with `rustfmt` and `black`
Fmt,
/// Runs `clippy`, denying all warnings.
Clippy,
/// Runs `cargo llvm-cov` for the PyO3 codebase.
Coverage(CoverageOpts),
/// Attempts to render the documentation.
Doc(DocOpts),
/// Runs various variations on `cargo test`
Test,
/// Runs the tests in examples/ and pytests/
TestPy,
}

impl Default for Subcommand {
fn default() -> Self {
Self::Default
}
}

#[derive(StructOpt, Default)]
pub struct CoverageOpts {
/// Creates an lcov output instead of printing to the terminal.
#[structopt(long)]
pub output_lcov: Option<String>,
}

#[derive(StructOpt)]
pub struct DocOpts {
/// Whether to run the docs using nightly rustdoc
#[structopt(long)]
pub stable: bool,
/// Whether to open the docs after rendering.
#[structopt(long)]
pub open: bool,
/// Whether to show the private and hidden API.
#[structopt(long)]
pub internal: bool,
}

impl Default for DocOpts {
fn default() -> Self {
Self {
stable: true,
open: false,
internal: false,
}
}
}

impl Subcommand {
pub fn execute(self) -> Result<()> {
print_metadata()?;

let start = Instant::now();

match self {
Subcommand::Default => {
crate::fmt::rust::run()?;
crate::clippy::run()?;
crate::test::run()?;
crate::doc::run(DocOpts::default())?;
}
Subcommand::Ci => {
let installed = Installed::new()?;
crate::fmt::rust::run()?;
if installed.black {
crate::fmt::python::run()?;
} else {
Installed::warn_black()
};
crate::clippy::run()?;
crate::test::run()?;
crate::doc::run(DocOpts::default())?;
if installed.nox {
crate::pytests::run(None)?;
} else {
Installed::warn_nox()
};
crate::llvm_cov::run(CoverageOpts::default())?;
installed.assert()?
}

Subcommand::Doc(opts) => crate::doc::run(opts)?,
Subcommand::Fmt => {
crate::fmt::rust::run()?;
crate::fmt::python::run()?;
}
Subcommand::Clippy => crate::clippy::run()?,
Subcommand::Coverage(opts) => crate::llvm_cov::run(opts)?,
Subcommand::TestPy => crate::pytests::run(None)?,
Subcommand::Test => crate::test::run()?,
};

let dt = start.elapsed().as_secs();
let minutes = dt / 60;
let seconds = dt % 60;
println!("\nxtask finished in {}m {}s.", minutes, seconds);

Ok(())
}
}

pub fn run(command: &mut Command) -> Result<()> {
println!("Running: {}", format_command(command));

let output = command
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?
.wait_with_output()?;

ensure! {
output.status.success(),
"process did not run successfully ({exit}): {command}/n {out} {err}",
exit = match output.status.code() {
Some(code) => format!("exit code {}", code),
None => "terminated by signal".into(),
},
command = format_command(command),
out = String::from_utf8_lossy(&output.stdout),
err = String::from_utf8_lossy(&output.stderr)

};
Ok(())
}

#[derive(Copy, Clone, Debug)]
pub struct Installed {
pub nox: bool,
pub black: bool,
}

impl Installed {
pub fn new() -> anyhow::Result<Self> {
Ok(Self {
nox: Self::nox()?,
black: Self::black()?,
})
}

pub fn nox() -> anyhow::Result<bool> {
let output = std::process::Command::new("nox").arg("--version").output();
match output {
Ok(_) => Ok(true),
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(false),
Err(other) => Err(other.into()),
}
}

pub fn warn_nox() {
eprintln!("Skipping: formatting Python code, because `nox` was not found");
}

pub fn black() -> anyhow::Result<bool> {
let output = std::process::Command::new("black")
.arg("--version")
.output();
match output {
Ok(_) => Ok(true),
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(false),
Err(other) => Err(other.into()),
}
}

pub fn warn_black() {
eprintln!("Skipping: Python code formatting, because `black` was not found.");
}

pub fn assert(&self) -> anyhow::Result<()> {
if self.nox && self.black {
Ok(())
} else {
let mut err =
String::from("\n\nxtask was unable to run all tests due to some missing programs:");
if !self.black {
err.push_str("\n`black` was not installed. (`pip install black`)");
}
if !self.nox {
err.push_str("\n`nox` was not installed. (`pip install nox`)");
}

Err(anyhow::anyhow!(err))
}
}
}
25 changes: 25 additions & 0 deletions xtask/src/clippy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use crate::cli;
use std::process::Command;

pub fn run() -> anyhow::Result<()> {
cli::run(
Command::new("cargo")
.arg("clippy")
.arg("--features=full")
.arg("--all-targets")
.arg("--workspace")
.arg("--")
.arg("-Dwarnings"),
)?;
cli::run(
Command::new("cargo")
.arg("clippy")
.arg("--all-targets")
.arg("--workspace")
.arg("--features=abi3,full")
.arg("--")
.arg("-Dwarnings"),
)?;

Ok(())
}

0 comments on commit 16ad15e

Please sign in to comment.