@@ -176,6 +176,7 @@ disabled_backends: [] # backends that pacdef should not manage, e.g. ["python"]
|
|||||||
|
|
||||||
warn_not_symlinks: true # warn if a group file is not a symlink
|
warn_not_symlinks: true # warn if a group file is not a symlink
|
||||||
flatpak_systemwide: true # whether flatpak packages should be installed system-wide or per user
|
flatpak_systemwide: true # whether flatpak packages should be installed system-wide or per user
|
||||||
|
pip_binary: pip # choose whether to use pipx instead of pip for python package management [See [Pitfalls while using pipx](#pitfalls-while-using-pipx)]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -219,3 +220,6 @@ Pacdef is supported by [topgrade](https://github.com/topgrade-rs/topgrade).
|
|||||||
MSRV is 1.70.0 due to dependencies that require this specific version. Development is conducted against the latest stable version.
|
MSRV is 1.70.0 due to dependencies that require this specific version. Development is conducted against the latest stable version.
|
||||||
|
|
||||||
|
|
||||||
|
### Pitfalls while using pipx
|
||||||
|
|
||||||
|
Some packages like [mdformat-myst](https://github.com/executablebooks/mdformat-myst) do not provide an executable themselves but rather act as a plugin to their dependency, which is mdformat in this case. Please install such packages explicitly by running `pipx install <package-name> --include-deps`.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use crate::{Group, Package};
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Python {
|
pub struct Python {
|
||||||
|
pub(crate) binary: String,
|
||||||
pub(crate) packages: HashSet<Package>,
|
pub(crate) packages: HashSet<Package>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,19 +27,30 @@ const SWITCHES_REMOVE: Switches = &["uninstall"];
|
|||||||
|
|
||||||
const SUPPORTS_AS_DEPENDENCY: bool = false;
|
const SUPPORTS_AS_DEPENDENCY: bool = false;
|
||||||
|
|
||||||
|
macro_rules! ERROR{
|
||||||
|
($bin:expr) => {
|
||||||
|
panic!("Cannot use {} for package management in python. Please use a valid package manager like pip or pipx.", $bin)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl Backend for Python {
|
impl Backend for Python {
|
||||||
impl_backend_constants!();
|
impl_backend_constants!();
|
||||||
|
|
||||||
fn get_all_installed_packages(&self) -> Result<HashSet<Package>> {
|
fn get_binary(&self) -> Text {
|
||||||
let output = run_pip_command(&["list", "--format", "json", "--user"])?;
|
let r#box = self.binary.clone().into_boxed_str();
|
||||||
|
Box::leak(r#box)
|
||||||
|
}
|
||||||
|
|
||||||
extract_pacdef_packages(output)
|
fn get_all_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||||
|
let mut cmd = Command::new(self.get_binary());
|
||||||
|
let output = run_pip_command(&mut cmd, self.get_switches_runtime())?;
|
||||||
|
self.extract_packages(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>> {
|
fn get_explicitly_installed_packages(&self) -> Result<HashSet<Package>> {
|
||||||
let output = run_pip_command(&["list", "--format", "json", "--not-required", "--user"])?;
|
let mut cmd = Command::new(self.get_binary());
|
||||||
|
let output = run_pip_command(&mut cmd, self.get_switches_explicit())?;
|
||||||
extract_pacdef_packages(output)
|
self.extract_packages(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_dependency(&self, _packages: &[Package]) -> Result<ExitStatus> {
|
fn make_dependency(&self, _packages: &[Package]) -> Result<ExitStatus> {
|
||||||
@@ -46,8 +58,7 @@ impl Backend for Python {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_pip_command(args: &[&str]) -> Result<Value> {
|
fn run_pip_command(cmd: &mut Command, args: &[&str]) -> Result<Value> {
|
||||||
let mut cmd = Command::new(BINARY);
|
|
||||||
cmd.args(args);
|
cmd.args(args);
|
||||||
let output = String::from_utf8(cmd.output()?.stdout)?;
|
let output = String::from_utf8(cmd.output()?.stdout)?;
|
||||||
let val: Value = serde_json::from_str(&output)?;
|
let val: Value = serde_json::from_str(&output)?;
|
||||||
@@ -57,9 +68,33 @@ fn run_pip_command(args: &[&str]) -> Result<Value> {
|
|||||||
impl Python {
|
impl Python {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
binary: BINARY.to_string(),
|
||||||
packages: HashSet::new(),
|
packages: HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_switches_runtime(&self) -> Switches {
|
||||||
|
match self.get_binary() {
|
||||||
|
"pip" => &["list", "--format", "json", "--not-required", "--user"],
|
||||||
|
"pipx" => &["list", "--json"],
|
||||||
|
_ => ERROR!(self.get_binary()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn get_switches_explicit(&self) -> Switches {
|
||||||
|
match self.get_binary() {
|
||||||
|
"pip" => &["list", "--format", "json", "--user"],
|
||||||
|
"pipx" => &["list", "--json"],
|
||||||
|
_ => ERROR!(self.get_binary()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_packages(&self, output: Value) -> Result<HashSet<Package>> {
|
||||||
|
match self.get_binary() {
|
||||||
|
"pip" => extract_pacdef_packages(output),
|
||||||
|
"pipx" => extract_pacdef_packages_pipx(output),
|
||||||
|
_ => ERROR!(self.get_binary()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_pacdef_packages(value: Value) -> Result<HashSet<Package>> {
|
fn extract_pacdef_packages(value: Value) -> Result<HashSet<Package>> {
|
||||||
@@ -72,3 +107,13 @@ fn extract_pacdef_packages(value: Value) -> Result<HashSet<Package>> {
|
|||||||
.collect();
|
.collect();
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract_pacdef_packages_pipx(value: Value) -> Result<HashSet<Package>> {
|
||||||
|
let result = value["venvs"]
|
||||||
|
.as_object()
|
||||||
|
.context("getting inner json object")?
|
||||||
|
.iter()
|
||||||
|
.map(|(name, _)| Package::from(name.as_str()))
|
||||||
|
.collect();
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ pub struct Config {
|
|||||||
/// Backends the user does not want to use even though the binary exists.
|
/// Backends the user does not want to use even though the binary exists.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub disabled_backends: Vec<String>,
|
pub disabled_backends: Vec<String>,
|
||||||
|
/// Choose whether to use pipx instead of pip for python package management
|
||||||
|
#[serde(default)]
|
||||||
|
pub pip_binary: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn yes() -> bool {
|
fn yes() -> bool {
|
||||||
@@ -86,6 +89,7 @@ impl Default for Config {
|
|||||||
flatpak_systemwide: true,
|
flatpak_systemwide: true,
|
||||||
warn_not_symlinks: true,
|
warn_not_symlinks: true,
|
||||||
disabled_backends: vec![],
|
disabled_backends: vec![],
|
||||||
|
pip_binary: "pip".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,13 @@ impl Pacdef {
|
|||||||
{
|
{
|
||||||
flatpak.systemwide = self.config.flatpak_systemwide;
|
flatpak.systemwide = self.config.flatpak_systemwide;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(python) = backend
|
||||||
|
.as_any_mut()
|
||||||
|
.downcast_mut::<crate::backend::Python>()
|
||||||
|
{
|
||||||
|
python.binary = self.config.pip_binary.clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_packages(&mut self, noconfirm: bool) -> Result<()> {
|
fn install_packages(&mut self, noconfirm: bool) -> Result<()> {
|
||||||
|
|||||||
Reference in New Issue
Block a user