apt¶
Abstractions for the system’s Debian/Ubuntu package information and repositories.
This module contains abstractions and wrappers around Debian/Ubuntu-style repositories and packages, in order to easily provide an idiomatic and Pythonic mechanism for adding packages and/or repositories to systems for use in machine charms.
A sane default configuration is attainable through nothing more than instantiation of the
appropriate classes. DebianPackage objects provide information about the architecture,
version, name, and status of a package.
DebianPackage will try to look up a package either from dpkg -L or from apt-cache
when provided with a string indicating the package name. If it cannot be located,
PackageNotFoundError will be raised, as apt and dpkg otherwise return 100 for
all errors, and a meaningful error message if the package is not known is desirable.
To install packages with convenience methods:
try:
# Run `apt-get update`
apt.update()
apt.add_package("zsh")
apt.add_package(["vim", "htop", "wget"])
except PackageError as e:
logger.error("could not install package. Reason: %s", e.message)
The convenience methods don’t raise PackageNotFoundError. If any packages aren’t found in
the cache, apt.add_package raises PackageError with a message ‘Failed to install
packages: foo, bar’.
To find details of a specific package:
try:
vim = apt.DebianPackage.from_system("vim")
# To find from the apt cache only
# apt.DebianPackage.from_apt_cache("vim")
# To find from installed packages only
# apt.DebianPackage.from_installed_package("vim")
vim.ensure(PackageState.Latest)
logger.info("updated vim to version: %s", vim.fullversion)
except PackageNotFoundError:
logger.error("a specified package not found in package cache or on system")
except PackageError as e:
logger.error("could not install package. Reason: %s", e.message)
RepositoryMapping will return a dict-like object containing enabled system repositories
and their properties (available groups, URI, GPG key). This class can add, disable, or
manipulate repositories. Items can be retrieved as DebianRepository objects.
In order to add a new repository with explicit details for fields, a new DebianRepository
can be added to RepositoryMapping
RepositoryMapping provides an abstraction around the existing repositories on the system,
and can be accessed and iterated over like any Mapping object, to retrieve values by key,
iterate, or perform other operations.
Keys are constructed as {repo_type}-{}-{release} in order to uniquely identify a repository.
Repositories can be added with explicit values through a Python constructor.
Example:
repositories = apt.RepositoryMapping()
if "deb-example.com-focal" not in repositories:
repositories.add(
DebianRepository(
enabled=True,
repotype="deb",
uri="https://example.com",
release="focal",
groups=["universe"],
)
)
Alternatively, any valid sources.list line may be used to construct a new
DebianRepository.
Example:
repositories = apt.RepositoryMapping()
if "deb-us.archive.ubuntu.com-xenial" not in repositories:
line = "deb http://us.archive.ubuntu.com/ubuntu xenial main restricted"
repo = DebianRepository.from_repo_line(line)
repositories.add(repo)
- exception Error¶
Bases:
ExceptionBase class of most errors raised by this library.
- property name¶
Return a string representation of the model plus class.
- property message¶
Return the message passed as an argument.
- exception PackageError¶
Bases:
ErrorRaised when there’s an error installing or removing a package.
Additionally,
apt.add_packageraisesPackageErrorif any packages aren’t found in the cache.
- exception PackageNotFoundError¶
Bases:
ErrorRaised by
DebianPackagemethods if a requested package is not found.
- class PackageState(*values)¶
Bases:
EnumA class to represent possible package states.
- Present = 'present'¶
- Absent = 'absent'¶
- Latest = 'latest'¶
- Available = 'available'¶
- class DebianPackage(
- name: str,
- version: str,
- epoch: str,
- arch: str,
- state: PackageState,
Bases:
objectRepresents a traditional Debian package and its utility functions.
DebianPackagewraps information and functionality around a known package, whether installed or available. The version, epoch, name, and architecture can be easily queried and compared against otherDebianPackageobjects to determine the latest version or to install a specific version.The representation of this object as a string mimics the output from
dpkgfor familiarity.Installation and removal of packages is handled through the
stateproperty orensuremethod, with the following options:When
DebianPackageis initialised, the state of a givenDebianPackageobject will be set toAvailable,Present, orLatest, withAbsentimplemented as a convenience for removal (though it operates essentially the same asAvailable).- ensure(state: PackageState)¶
Ensure that a package is in a given state.
- Parameters:
state – a
PackageStateto reconcile the package to- Raises:
PackageError – from the underlying call to
apt
- property state: PackageState¶
Return the current package state.
- classmethod from_system( ) DebianPackage¶
Locate a package, either on the system or known to apt, and serialises the information.
- Parameters:
package – a string representing the package
version – an optional string if a specific version is requested
arch – an optional architecture, defaulting to
dpkg --print-architecture. If an architecture is not specified, this will be used for selection.
- classmethod from_installed_package( ) DebianPackage¶
Check whether the package is already installed and return an instance.
- Parameters:
package – a string representing the package
version – an optional string if a specific version is requested
arch – an optional architecture, defaulting to
dpkg --print-architecture. If an architecture is not specified, this will be used for selection.
- classmethod from_apt_cache( ) DebianPackage¶
Check whether the package is already installed and return an instance.
- Parameters:
package – a string representing the package
version – an optional string if a specific version is requested
arch – an optional architecture, defaulting to
dpkg --print-architecture. If an architecture is not specified, this will be used for selection.
- class Version(version: str, epoch: str)¶
Bases:
objectAn abstraction around package versions.
This seems like it should be strictly unnecessary, except that
apt_pkgis not usable inside avenv, and wedging version comparisons intoDebianPackagewould over complicate it.This class implements the algorithm found here: https://www.debian.org/doc/debian-policy/ch-controlfields.html#version
- property epoch¶
Return the epoch for a package. May be empty.
- add_package( ) DebianPackage¶
- add_package(
- package_names: list[str],
- version: str | None = '',
- arch: str | None = '',
- update_cache: bool = False,
Add a package or list of packages to the system.
- Parameters:
package_names – single package name, or list of package names
name – the name(s) of the package(s)
version – an (optional) version as a string. Defaults to the latest known
arch – an optional architecture for the package
update_cache – whether or not to run apt-get update prior to operating
- Raises:
TypeError – if no package name is given, or explicit version is set for multiple packages
PackageError – if packages fail to install, including if any packages aren’t found in the cache
- remove_package(package_names: str) DebianPackage¶
- remove_package( ) DebianPackage | list[DebianPackage]
Remove package(s) from the system.
- Parameters:
package_names – the name of a package
- Raises:
TypeError – if no packages are provided
- import_key(key: str) str¶
Import an ASCII Armor key.
A Radix64 format key ID is also supported for backwards compatibility. In this case Ubuntu key server will be queried for a key via HTTPS by its key ID. This method is less preferable because HTTPS proxy servers may require traffic decryption which is equivalent to a man-in-the-middle attack (a proxy server impersonates key server TLS certificates and has to be explicitly trusted by the system).
- Parameters:
key – A GPG key in ASCII armor format, including BEGIN and END markers or a key ID.
- Returns:
The GPG key filename written.
- Raises:
GPGKeyError – if the key could not be imported
- class DebianRepository(
- enabled: bool,
- repotype: str,
- uri: str,
- release: str,
- groups: list[str],
- filename: str = '',
- gpg_key_filename: str = '',
- options: dict[str, str] | None = None,
Bases:
objectAn abstraction to represent a repository.
- property enabled¶
Return whether or not the repository is enabled.
- property repotype¶
Return whether it is binary or source.
- property uri¶
Return the URI.
- property release¶
Return which Debian/Ubuntu releases it is valid for.
- property groups¶
Return the enabled package groups.
- property filename¶
Returns the filename for a repository.
- property gpg_key¶
Returns the path to the GPG key for this repository.
- property options¶
Returns any additional repo options which are set.
- make_options_string(include_signed_by: bool = True) str¶
Generate the complete one-line-style options string for a repository.
Combining
gpg_key, if set (andinclude_signed_byisTrue), with any other provided options to form the options section of a one-line-style definition.
- static prefix_from_uri(uri: str) str¶
Get a repo list prefix from the URI, depending on whether a path is set.
- static from_repo_line( ) DebianRepository¶
Instantiate a new
DebianRepositoryfrom asources.listentry line.- Parameters:
repo_line – a string representing a repository entry
write_file – Boolean to enable writing the new repo to disk. True by default. Expect it to result in an add-apt-repository call under the hood, like
add-apt-repository --no-update --sourceslist="$repo_line"
- disable() None¶
Remove this repository by disabling it in the source file.
WARNING: This method does NOT alter the
DebianRepository.enabledflag.WARNING: disable is currently not implemented for repositories defined by a
deb822stanza. Raises aNotImplementedErrorin this case.
- import_key(key: str) None¶
Import an ASCII Armor key.
A Radix64 format key ID is also supported for backwards compatibility. In this case Ubuntu key server will be queried for a key via HTTPS by its key ID. This method is less preferable because HTTPS proxy servers may require traffic decryption which is equivalent to a man-in-the-middle attack (a proxy server impersonates key server TLS certificates and has to be explicitly trusted by the system).
- Parameters:
key – A GPG key in ASCII armor format, including BEGIN and END markers or a key ID.
- Raises:
GPGKeyError – if the key could not be imported
- class RepositoryMapping¶
Bases:
Mapping[str,DebianRepository]An representation of known repositories.
Instantiation of
RepositoryMappingwill iterate through the filesystem, parse out repository files in /etc/apt/…, and createDebianRepositoryobjects in this list.Typical usage:
repositories = apt.RepositoryMapping() repositories.add( DebianRepository( enabled=True, repotype="deb", uri="https://example.com", release="focal", groups=["universe"], ) )
- __contains__(key: Any) bool¶
Magic method for checking presence of repo in mapping.
Checks against the string names used to identify repositories.
- __iter__() Iterator[DebianRepository]¶
Return iterator for
RepositoryMapping.Iterates over the
DebianRepositoryvalues rather than the string names.Note: this breaks the expectations of the
Mappingabstract base class for example when it provides methods like keys and items
- __getitem__(repository_uri: str) DebianRepository¶
Return a given
DebianRepository.
- __setitem__(
- repository_uri: str,
- repository: DebianRepository,
Add a
DebianRepositoryto the cache.
- load_deb822(filename: str) None¶
Load a
deb822format repository source file into the cache.In contrast to one-line-style, the
deb822format specifies a repository using a multi-line stanza. Stanzas are separated by white space, and each definition consists of lines that are either key: value pairs, or continuations of the previous value.- Read more about the
deb822format here: https://manpages.ubuntu.com/manpages/noble/en/man5/sources.list.5.html
- For instance, Ubuntu 24.04 (noble) lists its sources using
deb822style in: /etc/apt/sources.list.d/ubuntu.sources
- Read more about the
- load(filename: str)¶
Load a one-line-style format repository source file into the cache.
- Parameters:
filename – the path to the repository file
- add(
- repo: DebianRepository,
- default_filename: bool | None = False,
Add a new repository to the system using add-apt-repository.
- Parameters:
repo – a
DebianRepositoryobject ifDebianRepository.enabledis false, will return without adding the repo- Raises:
CalledProcessError – if there’s an error running apt-add-repository
WARNING: Does not associate the repository with a signing key. Use
import_keyto add a signing key globally.WARNING: if
DebianRepository.enabledis false, will return without adding the repoWARNING: Don’t forget to call
updatebefore installing any packages! Or calladd_packagewithupdate_cache=True.WARNING: the
default_filenamekeyword argument is provided for backwards compatibility only. It is not used, and was not used in the previous revision of this library.
- disable(repo: DebianRepository) None¶
Remove a repository by disabling it in the source file.
WARNING: disable is currently not implemented for repositories defined by a
deb822stanza, and will raise aNotImplementedErrorif called on one.WARNING: This method does NOT alter the
DebianRepository.enabledflag.
- exception MissingRequiredKeyError( )¶
Bases:
InvalidSourceErrorMissing a required value in a source file.