annotate rust/hg-core/src/checkexec.rs @ 51929:f2832de2a46c

interfaces: introduce and use a protocol class for the `bdiff` module This is allowed by PEP 544[1], and we basically follow the example there. The class here is copied from `mercurial.pure.bdiff`, and the implementation removed. There are several modules that have a few different implementations, and the implementation chosen is controlled by `HGMODULEPOLICY`. The module is loaded via `mercurial/policy.py`, and has been inferred by pytype as `Any` up to this point. Therefore it and PyCharm were blind to all functions on the module, and their signatures. Also, having multiple instances of the same module allows their signatures to get out of sync. Introducing a protocol class allows the loaded module that is stored in a variable to be given type info, which cascades through the various places it is used. This change alters 11 *.pyi files, for example. In theory, this would also allow us to ensure the various implementations of the same module are kept in alignment- simply import the module in a test module, attempt to pass it to a function that uses the corresponding protocol as an argument, and run pytype on it. In practice, this doesn't work (yet). PyCharm (erroneously) flags imported modules being passed where a protocol class is used[2]. Pytype has problems the other way- it fails to detect when a module that doesn't adhere to the protocol is passed to a protocol argument. The good news is that mypy properly detects this case. The bad news is that mypy spews a bunch of other errors when importing even simple modules, like the various `bdiff` modules. Therefore I'm punting on the tests for now because the type info around a loaded module in PyCharm is a clear win by itself. [1] https://peps.python.org/pep-0544/#modules-as-implementations-of-protocols [2] https://youtrack.jetbrains.com/issue/PY-58679/Support-modules-implementing-protocols
author Matt Harbison <matt_harbison@yahoo.com>
date Sat, 28 Sep 2024 19:12:18 -0400
parents e2c8b30ab4e7
children 66e34bc44280
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
49894
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
1 use std::fs;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
2 use std::io;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
3 use std::os::unix::fs::{MetadataExt, PermissionsExt};
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
4 use std::path::Path;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
5
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
6 const EXECFLAGS: u32 = 0o111;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
7
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
8 fn is_executable(path: impl AsRef<Path>) -> Result<bool, io::Error> {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
9 let metadata = fs::metadata(path)?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
10 let mode = metadata.mode();
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
11 Ok(mode & EXECFLAGS != 0)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
12 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
13
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
14 fn make_executable(path: impl AsRef<Path>) -> Result<(), io::Error> {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
15 let mode = fs::metadata(path.as_ref())?.mode();
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
16 fs::set_permissions(
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
17 path,
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
18 fs::Permissions::from_mode((mode & 0o777) | EXECFLAGS),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
19 )?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
20 Ok(())
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
21 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
22
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
23 fn copy_mode(
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
24 src: impl AsRef<Path>,
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
25 dst: impl AsRef<Path>,
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
26 ) -> Result<(), io::Error> {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
27 let mode = match fs::symlink_metadata(src) {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
28 Ok(metadata) => metadata.mode(),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
29 Err(e) if e.kind() == io::ErrorKind::NotFound =>
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
30 // copymode in python has a more complicated handling of FileNotFound
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
31 // error, which we don't need because all it does is applying
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
32 // umask, which the OS already does when we mkdir.
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
33 {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
34 return Ok(())
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
35 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
36 Err(e) => return Err(e),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
37 };
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
38 fs::set_permissions(dst, fs::Permissions::from_mode(mode))?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
39 Ok(())
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
40 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
41
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
42 fn check_exec_impl(path: impl AsRef<Path>) -> Result<bool, io::Error> {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
43 let basedir = path.as_ref().join(".hg");
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
44 let cachedir = basedir.join("wcache");
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
45 let storedir = basedir.join("store");
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
46
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
47 if !cachedir.exists() {
49895
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
48 // we want to create the 'cache' directory, not the '.hg' one.
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
49 // Automatically creating '.hg' directory could silently spawn
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
50 // invalid Mercurial repositories. That seems like a bad idea.
49894
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
51 fs::create_dir(&cachedir)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
52 .and_then(|()| {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
53 if storedir.exists() {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
54 copy_mode(&storedir, &cachedir)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
55 } else {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
56 copy_mode(&basedir, &cachedir)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
57 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
58 })
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
59 .ok();
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
60 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
61
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
62 let leave_file: bool;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
63 let checkdir: &Path;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
64 let checkisexec = cachedir.join("checkisexec");
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
65 let checknoexec = cachedir.join("checknoexec");
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
66 if cachedir.is_dir() {
49895
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
67 // Check if both files already exist in cache and have correct
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
68 // permissions. if so, we assume that permissions work.
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
69 // If not, we delete the files and try again.
49894
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
70 match is_executable(&checkisexec) {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
71 Err(e) if e.kind() == io::ErrorKind::NotFound => (),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
72 Err(e) => return Err(e),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
73 Ok(is_exec) => {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
74 if is_exec {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
75 let noexec_is_exec = match is_executable(&checknoexec) {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
76 Err(e) if e.kind() == io::ErrorKind::NotFound => {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
77 fs::write(&checknoexec, "")?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
78 is_executable(&checknoexec)?
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
79 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
80 Err(e) => return Err(e),
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
81 Ok(exec) => exec,
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
82 };
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
83 if !noexec_is_exec {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
84 // check-exec is exec and check-no-exec is not exec
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
85 return Ok(true);
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
86 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
87 fs::remove_file(&checknoexec)?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
88 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
89 fs::remove_file(&checkisexec)?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
90 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
91 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
92 checkdir = &cachedir;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
93 leave_file = true;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
94 } else {
49895
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
95 // no cache directory (probably because .hg doesn't exist):
07792fd1837f doc: add a few comments
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 49894
diff changeset
96 // check directly in `path` and don't leave the temp file behind
49894
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
97 checkdir = path.as_ref();
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
98 leave_file = false;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
99 };
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
100
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
101 let tmp_file = tempfile::NamedTempFile::new_in(checkdir)?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
102 if !is_executable(tmp_file.path())? {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
103 make_executable(tmp_file.path())?;
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
104 if is_executable(tmp_file.path())? {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
105 if leave_file {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
106 tmp_file.persist(checkisexec).ok();
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
107 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
108 return Ok(true);
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
109 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
110 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
111
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
112 Ok(false)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
113 }
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
114
50413
e2c8b30ab4e7 rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents: 50412
diff changeset
115 /// This function is a Rust rewrite of the `checkexec` function from
e2c8b30ab4e7 rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents: 50412
diff changeset
116 /// `posix.py`.
e2c8b30ab4e7 rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents: 50412
diff changeset
117 ///
e2c8b30ab4e7 rustdoc: wording for checkexec
Georges Racinet <georges.racinet@octobus.net>
parents: 50412
diff changeset
118 /// Returns `true` if the filesystem supports execute permissions.
49894
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
119 pub fn check_exec(path: impl AsRef<Path>) -> bool {
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
120 check_exec_impl(path).unwrap_or(false)
678588b01af1 rhg: implement checkexec to support weird filesystems
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
diff changeset
121 }