Skip to content

Commit

Permalink
Parse METADATA from path dependencies, and fix a bug in parsing metad…
Browse files Browse the repository at this point in the history
…ata that has extra but no version
  • Loading branch information
David-OConnor committed Nov 3, 2019
1 parent b7d953a commit 4b4e583
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 28 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
@@ -1,6 +1,7 @@
# Changelog

## v0.1.9
- Can now parse subdependencies of `path` requirements from built-wheels
- Fixed a bug where subdep contraints specified on multiple lines would
cause resolution to fail

Expand Down
3 changes: 2 additions & 1 deletion src/dep_resolution.rs
Expand Up @@ -345,7 +345,8 @@ fn guess_graph(
util::abort(&format!(
"Problem getting dependency data - this is \
likely a bug in the cacheing process. Please try again in a few minutes. \
Reqs: {:#?}", &reqs
Reqs: {:#?}",
&reqs
));
unreachable!()
};
Expand Down
39 changes: 29 additions & 10 deletions src/dep_types.rs
Expand Up @@ -741,9 +741,9 @@ impl Req {
}

pub fn from_str(s: &str, pypi_fmt: bool) -> Result<Self, DependencyError> {
// Todo: DRY between versioned and unversioned.
let re = if pypi_fmt {
// eg saturn (>=0.3.4) or argon2-cffi (>=16.1.0) ; extra == 'argon2'
// todo deal with extra etc
// Note: We specify what chars are acceptable in a name instead of using
// wildcard, so we don't accidentally match a semicolon here if a
// set of parens appears later. The non-greedy ? in the version-matching
Expand All @@ -755,10 +755,22 @@ impl Req {
Regex::new(r#"^(.*?)\s*=\s*["'](.*)["']$"#).unwrap()
};

// Check if no version is specified.
let novers_re = if pypi_fmt {
Regex::new(r"^([a-zA-Z\-0-9._]+)(?:(?:\s*;\s*)(.*))?$").unwrap()
} else {
// todo extras
Regex::new(r"^([a-zA-Z\-0-9._]+)$").unwrap()
};

if let Some(caps) = re.captures(s) {
let name = caps.get(1).unwrap().as_str().to_owned();
let reqs_m = caps.get(2).unwrap();
let constraints = Constraint::from_str_multiple(reqs_m.as_str())?;
let constraints = if reqs_m.as_str().is_empty() {
vec![]
} else {
Constraint::from_str_multiple(reqs_m.as_str())?
};

let (extra, sys_platform, python_version) = parse_extras(caps.get(3));

Expand All @@ -774,14 +786,6 @@ impl Req {
});
};

// Check if no version is specified.
let novers_re = if pypi_fmt {
Regex::new(r"^([a-zA-Z\-0-9._]+)(?:(?:\s*;\s*)(.*))?$").unwrap()
} else {
// todo extras
Regex::new(r"^([a-zA-Z\-0-9._]+)$").unwrap()
};

if let Some(caps) = novers_re.captures(s) {
let (extra, sys_platform, python_version) = parse_extras(caps.get(2));

Expand Down Expand Up @@ -1185,9 +1189,24 @@ pub mod tests {
git: None,
};

let actual4 = Req::from_str("envisage ; extra == 'app'", true).unwrap();

// Test with extras, but no version
let expected4 = Req {
name: "envisage".into(),
constraints: vec![],
extra: Some("app".into()),
sys_platform: None,
python_version: None,
install_with_extras: None,
path: None,
git: None,
};

assert_eq!(actual, expected);
assert_eq!(actual2, expected2);
assert_eq!(actual3, expected3);
assert_eq!(actual4, expected4);
}

// Non-standard format I've come across; more like the non-pypi fmt.
Expand Down
9 changes: 9 additions & 0 deletions src/install.rs
Expand Up @@ -237,10 +237,19 @@ pub fn download_and_install_package(
util::extract_zip(&archive_file, &paths.lib, &rename);
}
PackageType::Source => {
// todo: Support .tar.bz2
if archive_path.extension().unwrap() == "bz2" {
util::abort(&format!(
"Extracting source packages in the `.bz2` format isn't supported \
at this time: {:?}",
&archive_path
));
}
// Extract the tar.gz source code.
let tar = GzDecoder::new(&archive_file);
let mut archive = Archive::new(tar);

// Perhaps we're dealing with a zip.
if archive.unpack(&paths.lib).is_err() {
// The extract_wheel function just extracts a zip file, so it's appropriate here.
// We'll then continue with this leg, and build/move/cleanup.
Expand Down
12 changes: 12 additions & 0 deletions src/main.rs
Expand Up @@ -188,6 +188,18 @@ fn pop_reqs_helper(reqs: &[Req], dev: bool) -> Vec<Req> {
.unwrap_or_else(|| panic!("Problem parsing`pyproject.toml`: {:?}", &pyproj));
result.append(&mut req_cfg.reqs)
}

// Check for metadata of a built wheel
for folder_name in util::find_folders(&req_path) {
// todo: Dry from `util` and `install`.
let re_dist = Regex::new(r"^(.*?)-(.*?)\.dist-info$").unwrap();
if let Some(_) = re_dist.captures(&folder_name) {
let metadata_path = req_path.join(folder_name).join("METADATA");
let mut metadata = util::parse_metadata(&metadata_path);

result.append(&mut metadata.requires_dist);
}
}
}
result
}
Expand Down
40 changes: 23 additions & 17 deletions src/util.rs
Expand Up @@ -233,29 +233,13 @@ pub fn show_installed(lib_path: &Path, path_reqs: &[Req]) {
/// Find the packages installed, by browsing the lib folder for metadata.
/// Returns package-name, version, folder names
pub fn find_installed(lib_path: &Path) -> Vec<(String, Version, Vec<String>)> {
let mut package_folders = vec![];

if !lib_path.exists() {
return vec![];
}
for entry in lib_path.read_dir().expect("Can't open lib path") {
if let Ok(entry) = entry {
if entry
.file_type()
.expect("Problem reading lib path file type")
.is_dir()
{
package_folders.push(entry.file_name())
}
}
}

let mut result = vec![];

for folder in &package_folders {
let folder_name = folder
.to_str()
.expect("Problem converting folder name to string");
for folder_name in &find_folders(&lib_path) {
let re_dist = Regex::new(r"^(.*?)-(.*?)\.dist-info$").unwrap();

if let Some(caps) = re_dist.captures(folder_name) {
Expand Down Expand Up @@ -808,3 +792,25 @@ pub fn parse_metadata(path: &Path) -> Metadata {
// todo: For now, just pull version and requires_dist. Add more as-required.
result
}

pub fn find_folders(path: &Path) -> Vec<String> {
let mut result = vec![];
for entry in path.read_dir().expect("Can't open lib path") {
if let Ok(entry) = entry {
if entry
.file_type()
.expect("Problem reading lib path file type")
.is_dir()
{
result.push(
entry
.file_name()
.to_str()
.expect("Problem converting folder name to string")
.to_owned(),
);
}
}
}
result
}

0 comments on commit 4b4e583

Please sign in to comment.