Skip to content

Commit

Permalink
pyc: print source hash/size/mtime in verbose mode
Browse files Browse the repository at this point in the history
  • Loading branch information
keszybz committed Jul 17, 2024
1 parent d13f063 commit baaa0da
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 5 deletions.
53 changes: 49 additions & 4 deletions src/handlers/pyc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,14 +382,55 @@ impl PycParser {
let mut data = Vec::from(&buf);
input.read_to_end(&mut data)?;

Ok(PycParser {
if data.len() < header_length {
return Err(super::Error::Other(
format!("pyc file is too short ({} < {})", data.len(), header_length)
).into());
}

let pyc = PycParser {
input_path: input_path.to_path_buf(),
version,
data,
read_offset: header_length,
irefs: Vec::new(),
flag_refs: Vec::new(),
})
};

let mtime = pyc.py_content_mtime();
debug!("{}: from py with mtime={} ({}), size={} bytes, {}",
input_path.display(),
mtime,
chrono::DateTime::from_timestamp(mtime as i64, 0).unwrap(),
pyc.py_content_size(),
match pyc.py_content_hash() {
None | Some(0) => "no hash invalidation".to_string(),
Some(hash) => format!("hash={hash}"),
}
);

Ok(pyc)
}

pub fn py_content_hash(&self) -> Option<u32> {
if self.version < (3, 7) { // The first version supporting PEP 552
None
} else {
match self._read_long_at(4) {
0 => None, // Let's always map 0 to None.
v => Some(v),
}
}
}

pub fn py_content_mtime(&self) -> u32 {
let offset = if self.version < (3, 7) { 4 } else { 8 };
self._read_long_at(offset)
}

pub fn py_content_size(&self) -> u32 {
let offset = if self.version < (3, 7) { 8 } else { 12 };
self._read_long_at(offset)
}

fn take(&mut self, count: usize) -> Result<usize> {
Expand Down Expand Up @@ -530,10 +571,14 @@ impl PycParser {
})
}

fn _read_long_at(&self, offset: usize) -> u32 {
let bytes = &self.data[offset .. offset + 4];
u32::from_le_bytes(bytes.try_into().unwrap())
}

fn _read_long(&mut self) -> Result<u32> {
let offset = self.take(4)?;
let bytes = &self.data[offset .. offset + 4];
Ok(u32::from_le_bytes(bytes.try_into().unwrap()))
Ok(self._read_long_at(offset))
}

fn _read_long_signed(&mut self) -> Result<i32> {
Expand Down
27 changes: 26 additions & 1 deletion tests/test_handlers/test_pyc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use add_determinism::handlers::pyc;
use super::{prepare_dir, make_handler, test_corpus_file};

#[test]
fn test_pyc_python_version() {
fn test_pyc_header() {
for p in [
"tests/cases/adapters.cpython-312.pyc",
"tests/cases/adapters.cpython-312.opt-1.pyc",
Expand All @@ -21,9 +21,34 @@ fn test_pyc_python_version() {

let parser = pyc::PycParser::from_file(p, File::open(p).unwrap()).unwrap();
assert_eq!(parser.version, (3, 12));
assert_eq!(parser.py_content_hash(), None);
assert_eq!(parser.py_content_mtime(), 1710422792);
assert_eq!(parser.py_content_size(), 16602);
}
}

#[test]
fn test_pyc_header_mtime_36() {
let p = Path::new("tests/cases/adapters.cpython-36~mtime.pyc");

let parser = pyc::PycParser::from_file(p, File::open(p).unwrap()).unwrap();
assert_eq!(parser.version, (3, 6));
assert_eq!(parser.py_content_hash(), None);
assert_eq!(parser.py_content_mtime(), 1720707393);
assert_eq!(parser.py_content_size(), 21);
}

#[test]
fn test_pyc_header_mtime_311() {
let p = Path::new("tests/cases/adapters.cpython-311~mtime.pyc");

let parser = pyc::PycParser::from_file(p, File::open(p).unwrap()).unwrap();
assert_eq!(parser.version, (3, 11));
assert_eq!(parser.py_content_hash(), None);
assert_eq!(parser.py_content_mtime(), 1720707393);
assert_eq!(parser.py_content_size(), 21);
}

#[test]
fn test_adapters() {
let (_dir, input) = prepare_dir("tests/cases/adapters.cpython-312.pyc").unwrap();
Expand Down

0 comments on commit baaa0da

Please sign in to comment.