libpfu_fixers/python/
pep517.rs1use anyhow::Result;
4use async_trait::async_trait;
5use libabbs::apml::{ast, lst, value::array::StringArray};
6use libpfu::{
7 Linter, Session, declare_lint, declare_linter,
8 message::{LintMessage, Snippet},
9 walk_defines,
10};
11use log::debug;
12
13declare_linter! {
14 pub PEP517_LINTER,
15 Pep517Linter,
16 [
17 "upgrade-to-pep517",
18 "pep517-nopython2",
19 "pep517-python2-dep",
20 "pep517-python3-dep",
21 ]
22}
23
24declare_lint! {
25 pub UPGRADE_TO_PEP517_LINT,
26 "upgrade-to-pep517",
27 Warning,
28 "use PEP-517 build backend"
29}
30
31declare_lint! {
32 pub PEP517_NOPYTHON2_LINT,
33 "pep517-nopython2",
34 Error,
35 "PEP-517 build template requires NOPYTHON2=1"
36}
37
38declare_lint! {
39 pub PEP517_PYTHON2_DEP_LINT,
40 "pep517-python2-dep",
41 Warning,
42 "python-2 should not be included in dependencies of PEP-517 package"
43}
44
45declare_lint! {
46 pub PEP517_PYTHON3_DEP_LINT,
47 "pep517-python3-dep",
48 Error,
49 "python-3 must be included as a runtime dependency of PEP-517 package"
50}
51
52#[async_trait]
53impl Linter for Pep517Linter {
54 async fn apply(&self, sess: &Session) -> Result<()> {
55 if !sess.offline
56 && sess.source_fs().await?.exists("pyproject.toml").await?
57 {
58 debug!(
59 "pyproject.toml found, checking PEP-517 lints for {:?}",
60 sess.package
61 );
62
63 for mut apml in walk_defines(sess) {
64 let abtype = apml.with_upgraded(|apml| {
65 apml.ctx()
66 .map(|ctx| ctx.get("ABTYPE").map(|val| val.as_string()))
67 })?;
68 if let Some(abtype) = abtype {
69 if abtype == "python" {
70 apml.with_upgraded(|apml| {
71 LintMessage::new(UPGRADE_TO_PEP517_LINT)
72 .note("remove ABTYPE=python to allow automatic template detection".to_string())
73 .snippet(Snippet::new_variable(sess, apml, "ABTYPE"))
74 .emit(sess);
75 if !sess.dry {
76 apml.with_editor(|apml| {
77 apml.remove_var(
78 apml.find_var_index("ABTYPE").unwrap(),
79 )
80 })
81 }
82 })
83 } else if abtype != "pep517" {
84 debug!(
85 "Explicit ABTYPE '{abtype}' is not pep517, skipping PEP-517 lints"
86 );
87 continue;
88 }
89 }
90
91 let nopy2 = apml.with_upgraded(|apml| {
92 apml.ctx()
93 .map(|ctx| ctx.read("NOPYTHON2").into_string() == "1")
94 })?;
95 if !nopy2 {
96 LintMessage::new(PEP517_NOPYTHON2_LINT)
97 .snippet(Snippet::new_index(sess, &apml, 0))
98 .emit(sess);
99 if !sess.dry {
100 apml.with_upgraded(|apml| {
101 apml.with_editor(|apml| {
102 apml.append_var_ast(
103 "NOPYTHON2".to_string(),
104 &ast::VariableValue::String(
105 ast::Text::from("1"),
106 ),
107 Some("ABTYPE"),
108 );
109 })
110 })
111 }
112 }
113
114 let pkgdep = apml.with_upgraded(|apml| {
115 apml.ctx().map(|ctx| {
116 ctx.get("PKGDEP")
117 .map(|val| val.as_string())
118 .unwrap_or_default()
119 })
120 })?;
121 let mut pkgdep = StringArray::from(pkgdep);
122 let mut pkgdep_dirty = false;
123
124 if pkgdep.iter().any(|dep| dep == "python-2") {
125 apml.with_upgraded(|apml| {
126 LintMessage::new(PEP517_PYTHON2_DEP_LINT)
127 .snippet(Snippet::new_variable(
128 sess, apml, "PKGDEP",
129 ))
130 .emit(sess);
131 });
132 if !sess.dry {
133 let pos = pkgdep
134 .iter()
135 .position(|dep| dep == "python-2")
136 .unwrap();
137 pkgdep.remove(pos);
138 pkgdep_dirty = true;
139 }
140 }
141 if !pkgdep.iter().any(|dep| dep == "python-3") {
142 apml.with_upgraded(|apml| {
143 LintMessage::new(PEP517_PYTHON3_DEP_LINT)
144 .snippet(Snippet::new_variable(
145 sess, apml, "PKGDEP",
146 ))
147 .emit(sess);
148 });
149 if !sess.dry {
150 pkgdep.push("python-3".to_string());
151 pkgdep_dirty = true;
152 }
153 }
154 if pkgdep_dirty {
155 apml.with_upgraded(|apml| {
156 apml.with_editor(|apml| {
157 apml.replace_var_lst(
158 "PKGDEP",
159 lst::VariableValue::String(
160 pkgdep.print().into(),
161 ),
162 );
163 })
164 });
165 }
166 }
167 }
168 Ok(())
169 }
170}