"""Contains the Package class and related helper classes and functions.""" from jinja2 import Template import logging import os PACKAGE_LOGGER = logging.getLogger(__name__) PKGINFO_TEMPLATE = Template(""" # Generated by APK Kit for Adélie Linux # {{ builduser }}@{{ buildhost }} {{ builddate }} pkgname = {{ package.name }} pkgver = {{ package.version }} pkgdesc = {{ package.description }} arch = {{ package.arch }} size = {{ package.size }} {%- if package.url %} url = {{ package.url }} {%- endif %} {%- if package.provides %}{%- for provided in package.provides %} provides = {{ provided }} {%- endfor %}{%- endif %} {%- if package.depends %}{%- for depend in package.depends %} depend = {{ depend }} {%- endfor %}{%- endif %} {%- if package.data_hash %} datahash = {{ package.data_hash }} {%- endif %} """) """The template used for generating .PKGINFO""" class Package: """The base package class.""" def __init__(self, name, version, arch, description=None, url=None, size=0, provides=None, depends=None, **kwargs): """Initialise a package object. :param str name: The name of the package. :param str version: The version of the package. :param str arch: The architecture of the package. :param str description: (Recommended) The description of the package. Defaults to the name if not set. :param int size: (Recommended) The installed size of the package. You almost always want to set this to something other than 0 if you don't want unhappy users. :) :param str url: (Optional) The URL of the homepage for the package. :param list provides: (Optional) One or more virtuals that this package provides. :param list depends: (Optional) One or more packages that are required to be installed to use this package. """ self._pkgname = name self._pkgver = str(version) self._pkgdesc = description or name self._url = url self._size = int(size) self._arch = arch self._provides = provides or list() self._depends = depends or list() if '_datahash' in kwargs: self._datahash = kwargs.pop('_datahash') if len(kwargs) > 0: PACKAGE_LOGGER.warning("unknown kwargs in Package: %r", kwargs) @property def name(self): """The name of the package.""" return self._pkgname @property def version(self): """The version of the package.""" return self._pkgver @property def description(self): """The description of the package.""" return self._pkgdesc @property def url(self): """The URL of the homepage of the package.""" return self._url @property def size(self): """The installed size of the package in bytes.""" return self._size @size.setter def size(self, new_size): """Change the installed size of the package in bytes. .. warning: Internal use only! :param int new_size: The new installed size of the package. """ self._size = new_size @property def arch(self): """The architecture of the package.""" return self._arch @property def provides(self): """The libraries and/or virtuals provided by this package.""" return self._provides @property def depends(self): """The dependencies of the package.""" return self._depends @property def data_hash(self): """The hash of the package's data, or None if not available.""" return getattr(self, '_datahash', None) @data_hash.setter def data_hash(self, hash_): """Set the hash of the package's data.""" self._datahash = hash_ def __repr__(self): return 'Package(name="{name}", version="{ver}", arch="{arch}", '\ 'description="{desc}", url="{url}", size={size}, '\ 'provides={prov}, depends={dep})'.format( name=self._pkgname, ver=self._pkgver, arch=self._arch, desc=self._pkgdesc, prov=self._provides, dep=self._depends, url=self._url, size=self._size) def to_pkginfo(self): """Serialises the package's information into the PKGINFO format. :returns str: The PKGINFO for this package. Unicode str, ready to be written to a file. .. note:: To write a file, see the :py:meth:`.write_pkginfo` helper method. """ return PKGINFO_TEMPLATE.render(builduser=os.getenv('USER', '?'), buildhost=os.uname().nodename, package=self) @classmethod def from_pkginfo(cls, buf): """Create a new :py:class:`Package` object from an existing PKGINFO. :param buf: The buffer to read from (whether file, StringIO, etc). :returns: A :py:class:`Package` object with the details from the PKGINFO. :throws ValueError: If a required field is missing from the PKGINFO. """ params = {} param_map = {'pkgname': 'name', 'pkgver': 'version', 'arch': 'arch', 'pkgdesc': 'description', 'provides': 'provides', 'depend': 'depends', 'url': 'url', 'size': 'size', 'datahash': '_datahash'} list_keys = {'provides', 'depend'} params['provides'] = list() params['depends'] = list() for line in buf.readlines(): if not isinstance(line, str): line = line.decode('utf-8') # Skip comments. if len(line) == 0 or line[0] == '#': continue if line.index('=') == -1: PACKAGE_LOGGER.warning('!!! malformed line? "%s" !!!', line) continue (key, value) = line.split('=', 1) key = key.strip() value = value.strip() if key in param_map: if key in list_keys: params[param_map[key]].append(value) else: params[param_map[key]] = value else: PACKAGE_LOGGER.info('!!! unrecognised PKGINFO key %s !!!', key) return cls(**params)