Commit d5cdad27 authored by David Runge's avatar David Runge
Browse files

Merge branch 'issues/6' into 'master'

Add documentation and validators to models

Closes #6

See merge request !6
parents 3c443e22 1a61c3d1
Pipeline #5940 passed with stage
in 5 minutes and 49 seconds
import io
from typing import List, Optional, Tuple
from pydantic import BaseModel
from pyalpm import vercmp
from pydantic import BaseModel, validator
from repo_management import defaults
class Arch(BaseModel):
"""A model describing a single 'arch' attribute
Attributes
----------
arch: str
The attribute can be used to describe the (required) data below an %ARCH% identifier in a 'desc' file, which
identifies a package's architecture
"""
arch: str
class Backup(BaseModel):
"""A model describing a single 'backup' attribute
Attributes
----------
backup: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %BACKUP% identifier in a 'desc' file, which
identifies which file(s) of a package pacman will create backups for
"""
backup: Optional[List[str]]
class Base(BaseModel):
"""A model describing the %BASE% header in a 'desc' file, which type it represents and whether it is required or
not"""
"""A model describing a single 'base' attribute
Attributes
----------
base: str
The attribute can be used to describe the (required) data below a %BASE% identifier in a 'desc' file, which
identifies a package's pkgbase
"""
base: str
class Version(BaseModel):
"""A model describing the %VERSION% header in a 'desc' file, which type it represents and whether it is required or
not"""
version: str
class BuildDate(BaseModel):
"""A model describing a single 'builddate' attribute
Attributes
----------
builddate: int
The attribute can be used to describe the (required) data below a %BUILDDATE% identifier in a 'desc' file,
which identifies a package's build date (represented in seconds since the epoch)
"""
class MakeDepends(BaseModel):
"""A model describing the %MAKEDEPENDS% header in a 'desc' file, which type it represents and whether it is required
or not"""
builddate: int
makedepends: Optional[List[str]]
@validator("builddate")
def builddate_greater_zero(cls, builddate: int) -> int:
if builddate < 0:
raise ValueError("The build date must be greater than zero.")
return builddate
class CheckDepends(BaseModel):
"""A model describing the %CHECKDEPENDS% header in a 'desc' file, which type it represents and whether it is
required or not"""
"""A model describing a single 'checkdepends' attribute
Attributes
----------
checkdepends: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %CHECKDEPENDS% identifier in a 'desc' file,
which identifies a package's checkdepends
"""
checkdepends: Optional[List[str]]
class FileName(BaseModel):
"""A model describing the %FILENAME% header in a 'desc' file, which type it represents and whether it is required or
not"""
filename: str
class Conflicts(BaseModel):
"""A model describing a single 'conflicts' attribute
Attributes
----------
conflicts: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %CONFLICTS% identifier in a 'desc' file, which
identifies what other package(s) a package conflicts with
"""
class Name(BaseModel):
"""A model describing the %NAME% header in a 'desc' file, which type it represents and whether it is required or
not"""
conflicts: Optional[List[str]]
name: str
class CSize(BaseModel):
"""A model describing a single 'csize' attribute
Attributes
----------
csize: int
The attribute can be used to describe the (required) data below a %CSIZE% identifier in a 'desc' file, which
identifies a package's size
"""
csize: int
@validator("csize")
def csize_greater_equal_zero(cls, csize: int) -> int:
if csize < 0:
raise ValueError("The csize must be greater than or equal zero.")
return csize
class Depends(BaseModel):
"""A model describing a single 'depends' attribute
Attributes
----------
depends: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %DEPENDS% identifier in a 'desc' file, which
identifies what other package(s) a package depends on
"""
depends: Optional[List[str]]
class Desc(BaseModel):
"""A model describing the %DESC% header in a 'desc' file, which type it represents and whether it is required or
not"""
"""A model describing a single 'desc' attribute
Attributes
----------
desc: str
The attribute can be used to describe the (required) data below a %DESC% identifier in a 'desc' file, which
identifies a package's description
"""
desc: str
class Groups(BaseModel):
"""A model describing the %GROUPS% header in a 'desc' file, which type it represents and whether it is required or
not"""
groups: Optional[List[str]]
class FileName(BaseModel):
"""A model describing a single 'filename' attribute
Attributes
----------
filename: str
The attribute can be used to describe the (required) data below a %FILENAME% identifier in a 'desc' file, which
identifies a package's file name
"""
filename: str
class CSize(BaseModel):
"""A model describing the %CSIZE% header in a 'desc' file, which type it represents and whether it is required or
not"""
csize: int
class Files(BaseModel):
"""A model describing a single 'files' attribute
Attributes
----------
files: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %FILES% identifier in a 'files' file, which
identifies which file(s) belong to a package
"""
files: Optional[List[str]]
class Groups(BaseModel):
"""A model describing a single 'groups' attribute
Attributes
----------
groups: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %GROUPS% identifier in a 'desc' file, which
identifies a package's groups
"""
groups: Optional[List[str]]
class ISize(BaseModel):
"""A model describing the %ISIZE% header in a 'desc' file, which type it represents and whether it is required or
not"""
"""A model describing a single 'isize' attribute
Attributes
----------
isize: int
The attribute can be used to describe the (required) data below an %ISIZE% identifier in a 'desc' file, which
identifies a package's installed size
"""
isize: int
class Md5Sum(BaseModel):
"""A model describing the %MD5SUM% header in a 'desc' file, which type it represents and whether it is required or
not"""
@validator("isize")
def isize_greater_equal_zero(cls, isize: int) -> int:
if isize < 0:
raise ValueError("The isize must be greater than or equal zero.")
md5sum: str
return isize
class Sha256Sum(BaseModel):
"""A model describing the %SHA256SUM% header in a 'desc' file, which type it represents and whether it is required
or not"""
class License(BaseModel):
"""A model describing a single 'license' attribute
sha256sum: str
Attributes
----------
license: List[str]
The attribute can be used to describe the (required) data below a %LICENSE% identifier in a 'desc' file, which
identifies a package's license(s)
"""
license: List[str]
class PgpSig(BaseModel):
"""A model describing the %PGPSIG% header in a 'desc' file, which type it represents and whether it is required or
not"""
pgpsig: str
class MakeDepends(BaseModel):
"""A model describing a single 'makedepends' attribute
Attributes
----------
makedepends: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %MAKEDEPENDS% identifier in a 'desc' file,
which identifies a package's makedepends
"""
class Url(BaseModel):
"""A model describing the %URL% header in a 'desc' file, which type it represents and whether it is required or
not"""
makedepends: Optional[List[str]]
url: str
class Md5Sum(BaseModel):
"""A model describing a single 'md5sum' attribute
class License(BaseModel):
"""A model describing the %LICENSE% header in a 'desc' file, which type it represents and whether it is required or
not"""
Attributes
----------
md5sum: str
The attribute can be used to describe the (required) data below an %MD5SUM% identifier in a 'desc' file, which
identifies a package's md5 checksum
"""
license: Optional[List[str]]
md5sum: str
class Arch(BaseModel):
"""A model describing the %ARCH% header in a 'desc' file, which type it represents and whether it is required or
not"""
class Name(BaseModel):
"""A model describing a single 'name' attribute
arch: str
Attributes
----------
name: str
The attribute can be used to describe the (required) data below a %NAME% identifier in a 'desc' file, which
identifies a package's name
"""
name: str
class BuildDate(BaseModel):
"""A model describing the %BUILDDATE% header in a 'desc' file, which type it represents and whether it is required
or not"""
@validator("name")
def name_contains_only_allowed_chars(cls, name: str) -> str:
disallowed_start_chars = [".", "-"]
for char in disallowed_start_chars:
if name.startswith(char):
raise ValueError(f"The package name '{name}' can not start with any of '{disallowed_start_chars}'.")
allowed_chars = ["@", ".", "_", "+", "-"]
remaining_chars: List[str] = []
for char in name:
if (not char.isalnum() or (not char.isdigit() and not char.islower())) and char not in allowed_chars:
remaining_chars += [char]
if remaining_chars:
raise ValueError(
f"The package name '{name}' can not contain '{remaining_chars}' but must consist only of alphanumeric "
f"chars and any of '{allowed_chars}'."
)
builddate: int
return name
class Packager(BaseModel):
"""A model describing the %PACKAGER% header in a 'desc' file, which type it represents and whether it is required
or not"""
"""A model describing a single 'packager' attribute
Attributes
----------
packager: str
The attribute can be used to describe the (required) data below a %PACKAGER% identifier in a 'desc' file, which
identifies a package's packager
"""
packager: str
class PgpSig(BaseModel):
"""A model describing a single 'pgpsig' attribute
Attributes
----------
pgpsig: str
The attribute can be used to describe the (required) data below a %PGPSIG% identifier in a 'desc' file, which
identifies a package's PGP signature
"""
pgpsig: str
class Provides(BaseModel):
"""A model describing a single 'provides' attribute
Attributes
----------
provides: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %PROVIDES% identifier in a 'desc' file, which
identifies what other package(s) a package provides
"""
provides: Optional[List[str]]
class Replaces(BaseModel):
"""A model describing the %REPLACES% header in a 'desc' file, which type it represents and whether it is required or
not"""
"""A model describing a single 'replaces' attribute
Attributes
----------
replaces: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %REPLACES% identifier in a 'desc' file, which
identifies what other package(s) a package replaces
"""
replaces: Optional[List[str]]
class Conflicts(BaseModel):
"""A model describing the %CONFLICTS% header in a 'desc' file, which type it represents and whether it is required or
not"""
conflicts: Optional[List[str]]
class RepoDbMemberType(BaseModel):
"""A model describing a single 'member_type' attribute, which is used to identify/ distinguish different types of
repository database file types (e.g. 'desc' and 'files' files, which are contained in a repository database file).
Attributes
----------
member_type: defaults.RepoDbMemberType
A member of the IntEnum defaults.RepoDbMemberType
"""
class Provides(BaseModel):
"""A model describing the %PROVIDES% header in a 'desc' file, which type it represents and whether it is required or
not"""
member_type: defaults.RepoDbMemberType
provides: Optional[List[str]]
class Sha256Sum(BaseModel):
"""A model describing a single 'sha256sum' attribute
class Depends(BaseModel):
"""A model describing the %DEPENDS% header in a 'desc' file, which type it represents and whether it is required or
not"""
Attributes
----------
sha256sum: str
The attribute can be used to describe the (required) data below an %SHA256SUM% identifier in a 'desc' file,
which identifies a package's sha256 checksum
"""
depends: Optional[List[str]]
sha256sum: str
class OptDepends(BaseModel):
"""A model describing the %OPTDEPENDS% header in a 'desc' file, which type it represents and whether it is required
or not"""
"""A model describing a single 'optdepends' attribute
Attributes
----------
optdepends: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %OPTDEPENDS% identifier in a 'desc' file,
which identifies what other package(s) a package optionally depends on
"""
optdepends: Optional[List[str]]
class Backup(BaseModel):
"""A model describing the %BACKUP% header in a 'desc' file, which type it represents and whether it is required
or not"""
backup: Optional[List[str]]
class Url(BaseModel):
"""A model describing a single 'url' attribute
Attributes
----------
url: str
The attribute can be used to describe the (required) data below a %URL% identifier in a 'desc' file, which
identifies a package's URL
"""
url: str
class Files(BaseModel):
"""A model describing the %FILES% header in a 'files' file, which type it represents and whether it is required or
not"""
files: Optional[List[str]]
class Version(BaseModel):
"""A model describing a single 'version' attribute
Attributes
----------
version: str
The attribute can be used to describe the (required) data below a %VERSION% identifier in a 'desc' file, which
identifies a package's version (this is the accumulation of epoch, pkgver and pkgrel)
"""
version: str
class PackageFiles(Name, Files):
pass
@validator("version")
def version_is_valid(cls, version: str) -> str:
allowed_chars = [":", ".", "_", "+", "-"]
if version.endswith("-0"):
raise ValueError("The first pkgrel of a package release always needs to start at 1.")
for char in allowed_chars:
if version.startswith(char):
raise ValueError("The first character of a package version must not be '{char}'.")
remaining_chars: List[str] = []
for char in version:
if not char.isalnum() and char not in allowed_chars:
remaining_chars += [char]
if remaining_chars:
raise ValueError(
f"Package versions can not contain '{remaining_chars}' but must consist of alphanumeric chars and any "
f"of '{allowed_chars}'."
)
return version
def is_older_than(self, version: str) -> bool:
"""Check whether the version is older than a provided version
Parameters
----------
version: str
Another version string to compare that of self to
Returns
-------
True if self.version is older than the provided version, False otherwise.
"""
if vercmp(self.version, version) < 0:
return True
else:
return False
def is_newer_than(self, version: str) -> bool:
"""Check whether the version is newer than a provided version
Parameters
----------
version: str
Another version string to compare that of self to
Returns
-------
True if self.version is newer than the provided version, False otherwise.
"""
if vercmp(self.version, version) > 0:
return True
else:
return False
class OutputPackage(
Arch,
Backup,
BuildDate,
CheckDepends,
Conflicts,
CSize,
Depends,
Desc,
CheckDepends,
FileName,
Files,
Groups,
......@@ -208,8 +467,73 @@ class OutputPackage(
Sha256Sum,
Url,
):
"""A model describing all required attributes for a package in the context of an output file, that describes a
(potential) list of packages based upon its pkgbase
"""A model describing all required attributes that define a package in the context of an output file
Attributes
----------
arch: str
The attribute can be used to describe the (required) data below an %ARCH% identifier in a 'desc' file, which
identifies a package's architecture
backup: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %BACKUP% identifier in a 'desc' file, which
identifies which file(s) of a package pacman will create backups for
builddate: int
The attribute can be used to describe the (required) data below a %BUILDDATE% identifier in a 'desc' file,
which identifies a package's build date (represented in seconds since the epoch)
checkdepends: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %CHECKDEPENDS% identifier in a 'desc' file,
which identifies a package's checkdepends
conflicts: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %CONFLICTS% identifier in a 'desc' file, which
identifies what other package(s) a package conflicts with
csize: int
The attribute can be used to describe the (required) data below a %CSIZE% identifier in a 'desc' file, which
identifies a package's size
depends: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %DEPENDS% identifier in a 'desc' file, which
identifies what other package(s) a package depends on
desc: str
The attribute can be used to describe the (required) data below a %DESC% identifier in a 'desc' file, which
identifies a package's description
filename: str
The attribute can be used to describe the (required) data below a %FILENAME% identifier in a 'desc' file, which
identifies a package's file name
files: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %FILES% identifier in a 'files' file, which
identifies which file(s) belong to a package
groups: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %GROUPS% identifier in a 'desc' file, which
identifies a package's groups
isize: int
The attribute can be used to describe the (required) data below an %ISIZE% identifier in a 'desc' file, which
identifies a package's installed size
license: List[str]
The attribute can be used to describe the (required) data below a %LICENSE% identifier in a 'desc' file, which
identifies a package's license(s)
md5sum: str
The attribute can be used to describe the (required) data below an %MD5SUM% identifier in a 'desc' file, which
identifies a package's md5 checksum
name: str
The attribute can be used to describe the (required) data below a %NAME% identifier in a 'desc' file, which
identifies a package's name
optdepends: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %OPTDEPENDS% identifier in a 'desc' file,
which identifies what other package(s) a package optionally depends on
pgpsig: str
The attribute can be used to describe the (required) data below a %PGPSIG% identifier in a 'desc' file, which
identifies a package's PGP signature
provides: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %PROVIDES% identifier in a 'desc' file, which
identifies what other package(s) a package provides
replaces: Optional[List[str]]
The attribute can be used to describe the (optional) data below a %REPLACES% identifier in a 'desc' file, which
identifies what other package(s) a package replaces
sha256sum: str
The attribute can be used to describe the (required) data below an %SHA256SUM% identifier in a 'desc' file,
which identifies a package's sha256 checksum
url: str
The attribute can be used to describe the (required) data below a %URL% identifier in a 'desc' file, which
identifies a package's URL
"""
pass
......@@ -220,11 +544,11 @@ class PackageDesc(
Backup,
Base,
BuildDate,
CheckDepends,
Conflicts,
CSize,
Depends,
Desc,
CheckDepends,
FileName,