--- a/doc/client_api_versions.txt Wed Aug 03 17:49:35 2011 -0700
+++ b/doc/client_api_versions.txt Thu Aug 04 13:17:33 2011 -0700
@@ -1,3 +1,14 @@
+Version 65:
+Incompatible with clients using versions 0-64.
+
+ pkg.client.api.ImageInterface has changed as follows:
+ * get_pkg_list() yields five values instead of four; the fifth is a
+ dictionary of package attributes.
+
+ pkg.client.api.PackageInfo has changed as follows:
+ * added get_attr_values() to return the values of named package
+ attributes.
+
Version 64:
Incompatible with clients using versions 0-63.
--- a/doc/guide-metadata-conventions.rst Wed Aug 03 17:49:35 2011 -0700
+++ b/doc/guide-metadata-conventions.rst Thu Aug 04 13:17:33 2011 -0700
@@ -119,6 +119,14 @@
A value of "true" indicates the package is obsolete and should be
removed on upgrade.
+ pkg.human-version
+ For components whose upstream version isn't a dot-separated sequence
+ of nonnegative integers (OpenSSL's 0.9.8r, for example), this
+ attribute can be set to that string, and will be displayed when
+ appropriate. It cannot be used in an FMRI to install a particular
+ version; package authors must still convert the version into a
+ sequence of integers.
+
variant.*
See facets.txt
--- a/doc/tags-and-attributes.txt Wed Aug 03 17:49:35 2011 -0700
+++ b/doc/tags-and-attributes.txt Thu Aug 04 13:17:33 2011 -0700
@@ -90,6 +90,14 @@
A value of "true" indicates the package is obsolete and should be
removed on upgrade.
+ pkg.human-version
+ For components whose upstream version isn't a dot-separated sequence
+ of nonnegative integers (OpenSSL's 0.9.8r, for example), this
+ attribute can be set to that string, and will be displayed when
+ appropriate. It cannot be used in an FMRI to install a particular
+ version; package authors must still convert the version into a
+ sequence of integers.
+
variant.*
See facets.txt
--- a/src/client.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/client.py Thu Aug 04 13:17:33 2011 -0700
@@ -90,7 +90,7 @@
import sys
sys.exit(1)
-CLIENT_API_VERSION = 64
+CLIENT_API_VERSION = 65
PKG_CLIENT_NAME = "pkg"
JUST_UNKNOWN = 0
@@ -470,7 +470,7 @@
try:
res = api_inst.get_pkg_list(pkg_list, patterns=pargs,
raise_unmatched=True, repos=origins, variants=variants)
- for pt, summ, cats, states in res:
+ for pt, summ, cats, states, attrs in res:
found = True
if not omit_headers:
if verbose:
@@ -3017,7 +3017,12 @@
# XXX even more info on the publisher would be nice?
msg(_(" Publisher:"), pi.publisher)
- msg(_(" Version:"), pi.version)
+ hum_ver = pi.get_attr_values("pkg.human-version")
+ if hum_ver and hum_ver[0] != pi.version:
+ msg(_(" Version:"), "%s (%s)" %
+ (pi.version, hum_ver[0]))
+ else:
+ msg(_(" Version:"), pi.version)
msg(_(" Build Release:"), pi.build_release)
msg(_(" Branch:"), pi.branch)
msg(_("Packaging Date:"), pi.packaging_date)
@@ -3423,7 +3428,7 @@
repos=origins)
manifests = []
- for pfmri, summ, cats, states in res:
+ for pfmri, summ, cats, states, pattrs in res:
manifests.append(api_inst.get_manifest(pfmri,
all_variants=display_raw, repos=origins))
except api_errors.ImageFormatUpdateNeeded, e:
--- a/src/gui/modules/misc_non_gui.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/gui/modules/misc_non_gui.py Thu Aug 04 13:17:33 2011 -0700
@@ -41,7 +41,7 @@
# The current version of the Client API the PM, UM and
# WebInstall GUIs have been tested against and are known to work with.
-CLIENT_API_VERSION = 64
+CLIENT_API_VERSION = 65
LOG_DIR = "/var/tmp"
LOG_ERROR_EXT = "_error.log"
LOG_INFO_EXT = "_info.log"
--- a/src/modules/api_common.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/modules/api_common.py Thu Aug 04 13:17:33 2011 -0700
@@ -122,10 +122,10 @@
UNSUPPORTED = image.Image.PKG_STATE_UNSUPPORTED
FROZEN = image.Image.PKG_STATE_FROZEN
- __NUM_PROPS = 12
+ __NUM_PROPS = 13
IDENTITY, SUMMARY, CATEGORIES, STATE, SIZE, LICENSES, LINKS, \
- HARDLINKS, FILES, DIRS, DEPENDENCIES, DESCRIPTION = \
- range(__NUM_PROPS)
+ HARDLINKS, FILES, DIRS, DEPENDENCIES, DESCRIPTION, \
+ ALL_ATTRIBUTES = range(__NUM_PROPS)
ALL_OPTIONS = frozenset(range(__NUM_PROPS))
ACTION_OPTIONS = frozenset([LINKS, HARDLINKS, FILES, DIRS,
DEPENDENCIES])
@@ -134,7 +134,7 @@
category_info_list=None, states=None, publisher=None,
version=None, build_release=None, branch=None, packaging_date=None,
size=None, licenses=None, links=None, hardlinks=None, files=None,
- dirs=None, dependencies=None, description=None):
+ dirs=None, dependencies=None, description=None, attrs=None):
self.pkg_stem = pkg_stem
self.summary = summary
@@ -156,6 +156,7 @@
self.dirs = dirs
self.dependencies = dependencies
self.description = description
+ self.attrs = attrs or {}
def __str__(self):
return self.fmri
@@ -172,6 +173,28 @@
packaging_date=version.get_timestamp().strftime("%c"),
pfmri=f)
+ def get_attr_values(self, name, modifiers=()):
+ """Returns a list of the values of the package attribute 'name'.
+
+ The 'modifiers' parameter, if present, is a dict containing
+ key/value pairs, all of which must be present on an action in
+ order for the values to be returned.
+
+ Returns an empty list if there are no values.
+ """
+
+ # XXX should the modifiers parameter be allowed to be a subset
+ # of an action's modifiers?
+ if isinstance(modifiers, dict):
+ modifiers = tuple(
+ (k, isinstance(modifiers[k], basestring) and
+ tuple([sorted(modifiers[k])]) or
+ tuple(sorted(modifiers[k])))
+ for k in sorted(modifiers.iterkeys())
+ )
+ return self.attrs.get(name, {modifiers: []}).get(
+ modifiers, [])
+
def _get_pkg_cat_data(cat, info_needed, actions=None,
excludes=misc.EmptyI, pfmri=None):
--- a/src/modules/client/api.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/modules/client/api.py Thu Aug 04 13:17:33 2011 -0700
@@ -73,7 +73,7 @@
from pkg.client.pkgdefs import *
from pkg.smf import NonzeroExitException
-CURRENT_API_VERSION = 64
+CURRENT_API_VERSION = 65
CURRENT_P5I_VERSION = 1
# Image type constants.
@@ -818,20 +818,20 @@
patterns=["release/name"], return_fmris=True)
results = [e for e in results]
if results:
- pfmri, summary, categories, states = \
- results[0]
+ pfmri, summary, categories, states, attrs = results[0]
mfst = self._img.get_manifest(pfmri)
osname = mfst.get("pkg.release.osname", None)
if osname == "sunos":
return True
# Otherwise, see if we can find package/pkg (or SUNWipkg) and
- # SUNWcs.
+ # system/core-os (or SUNWcs).
results = self.__get_pkg_list(self.LIST_INSTALLED,
- patterns=["pkg:/package/pkg", "SUNWipkg", "SUNWcs"])
+ patterns=["/package/pkg", "SUNWipkg", "/system/core-os",
+ "SUNWcs"])
installed = set(e[0][1] for e in results)
- if "SUNWcs" in installed and ("SUNWipkg" in installed or
- "package/pkg" in installed):
+ if ("SUNWcs" in installed or "system/core-os" in installed) and \
+ ("SUNWipkg" in installed or "package/pkg" in installed):
return True
return False
@@ -2654,9 +2654,9 @@
self._img.cleanup_downloads()
@_LockedGenerator()
- def get_pkg_list(self, pkg_list, cats=None, patterns=misc.EmptyI,
- pubs=misc.EmptyI, raise_unmatched=False, repos=None,
- return_fmris=False, variants=False):
+ def get_pkg_list(self, pkg_list, cats=None, collect_attrs=False,
+ patterns=misc.EmptyI, pubs=misc.EmptyI, raise_unmatched=False,
+ repos=None, return_fmris=False, variants=False):
"""A generator function that produces tuples of the form:
(
@@ -2667,7 +2667,8 @@
),
summary, - (string) the package summary
categories, - (list) string tuples of (scheme, category)
- states - (list) PackageInfo states
+ states, - (list) PackageInfo states
+ attributes - (dict) package attributes
)
Results are always sorted by stem, publisher, and then in
@@ -2704,6 +2705,11 @@
to any package category. A value of None indicates that no
package category filtering should be applied.
+ 'collect_attrs' is an optional boolean that indicates whether
+ all package attributes should be collected and returned in the
+ fifth element of the return tuple. If False, that element will
+ be an empty dictionary.
+
'patterns' is an optional list of FMRI wildcard strings to
filter results by.
@@ -2731,14 +2737,14 @@
to retrieve the requested package information."""
return self.__get_pkg_list(pkg_list, cats=cats,
- patterns=patterns, pubs=pubs,
+ collect_attrs=collect_attrs, patterns=patterns, pubs=pubs,
raise_unmatched=raise_unmatched, repos=repos,
return_fmris=return_fmris, variants=variants)
- def __get_pkg_list(self, pkg_list, cats=None, inst_cat=None,
- known_cat=None, patterns=misc.EmptyI, pubs=misc.EmptyI,
- raise_unmatched=False, repos=None, return_fmris=False,
- variants=False):
+ def __get_pkg_list(self, pkg_list, cats=None, collect_attrs=False,
+ inst_cat=None, known_cat=None, patterns=misc.EmptyI,
+ pubs=misc.EmptyI, raise_unmatched=False, repos=None,
+ return_fmris=False, variants=False):
"""This is the implementation of get_pkg_list. The other
function is a wrapper that uses locking. The separation was
necessary because of API functions that already perform locking
@@ -3054,6 +3060,8 @@
omit_var = False
states = entry["metadata"]["states"]
pkgi = self._img.PKG_STATE_INSTALLED in states
+ ddm = lambda: collections.defaultdict(list)
+ attrs = collections.defaultdict(ddm)
try:
for a in actions:
if a.name == "depend" and \
@@ -3065,6 +3073,18 @@
atname = a.attrs["name"]
atvalue = a.attrs["value"]
+ if collect_attrs:
+ atvlist = a.attrlist("value")
+
+ # XXX Need to describe this data
+ # structure sanely somewhere.
+ mods = tuple(
+ (k, tuple(sorted(a.attrlist(k))))
+ for k in sorted(a.attrs.iterkeys())
+ if k not in ("name", "value")
+ )
+ attrs[atname][mods].extend(atvlist)
+
if atname == "pkg.summary":
summ = atvalue
continue
@@ -3074,6 +3094,10 @@
# Historical summary
# field.
summ = atvalue
+ collect_attrs and \
+ attrs["pkg.summary"] \
+ [mods]. \
+ extend(atvlist)
continue
if atname == "info.classification":
@@ -3174,9 +3198,9 @@
if return_fmris:
pfmri = fmri.PkgFmri("%s@%s" % (stem, ver),
build_release=brelease, publisher=pub)
- yield (pfmri, summ, pcats, states)
+ yield (pfmri, summ, pcats, states, attrs)
else:
- yield (t, summ, pcats, states)
+ yield (t, summ, pcats, states, attrs)
if raise_unmatched:
# Caller has requested that non-matching patterns or
@@ -3249,6 +3273,8 @@
act_opts = PackageInfo.ACTION_OPTIONS - \
frozenset([PackageInfo.DEPENDENCIES])
+ collect_attrs = PackageInfo.ALL_ATTRIBUTES in info_needed
+
pis = []
rval = {
self.INFO_FOUND: pis,
@@ -3257,8 +3283,9 @@
}
try:
- for pfmri, summary, cats, states in self.__get_pkg_list(
- ilist, inst_cat=inst_cat, known_cat=known_cat,
+ for pfmri, summary, cats, states, attrs in self.__get_pkg_list(
+ ilist, collect_attrs=collect_attrs,
+ inst_cat=inst_cat, known_cat=known_cat,
patterns=fmri_strings, raise_unmatched=True,
return_fmris=True, variants=True):
release = build_release = branch = \
@@ -3385,7 +3412,7 @@
pfmri=pfmri, licenses=licenses,
links=links, hardlinks=hardlinks, files=files,
dirs=dirs, dependencies=dependencies,
- description=description))
+ description=description, attrs=attrs))
except apx.InventoryException, e:
if e.illegal:
self.log_operation_end(
--- a/src/modules/lint/engine.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/modules/lint/engine.py Thu Aug 04 13:17:33 2011 -0700
@@ -39,7 +39,7 @@
import sys
PKG_CLIENT_NAME = "pkglint"
-CLIENT_API_VERSION = 64
+CLIENT_API_VERSION = 65
pkg.client.global_settings.client_name = PKG_CLIENT_NAME
class LintEngineException(Exception):
--- a/src/modules/publish/dependencies.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/modules/publish/dependencies.py Thu Aug 04 13:17:33 2011 -0700
@@ -1331,11 +1331,11 @@
pkg_list = None
if use_system:
# We make this a list because we want to reuse the list of
- # packages again, and despite it's name, get_pkg_list is a
+ # packages again, and despite its name, get_pkg_list is a
# generator.
pkg_list = list(api_inst.get_pkg_list(
api.ImageInterface.LIST_INSTALLED))
- for (pub, stem, ver), summ, cats, states in pkg_list:
+ for (pub, stem, ver), summ, cats, states, attrs in pkg_list:
pfmri = fmri.PkgFmri("pkg:/%s@%s" % (stem, ver), "5.11")
if pfmri.pkg_name in resolving_pkgs:
continue
@@ -1355,7 +1355,7 @@
# Build a list of all files delivered in the packages installed on
# the system.
if use_system:
- for (pub, stem, ver), summ, cats, states in pkg_list:
+ for (pub, stem, ver), summ, cats, states, attrs in pkg_list:
pfmri = fmri.PkgFmri("pkg:/%s@%s" % (stem, ver), "5.11")
if pfmri.pkg_name in resolving_pkgs:
continue
--- a/src/packagemanager.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/packagemanager.py Thu Aug 04 13:17:33 2011 -0700
@@ -4060,7 +4060,7 @@
self.special_package_names = []
pub_names = {}
for entry in pkgs_from_api:
- (pkg_pub, pkg_name, ver), summ, cats, states = entry
+ (pkg_pub, pkg_name, ver), summ, cats, states, attrs = entry
if debug:
print entry, ver
pkg_stem = "pkg://" + pkg_pub + "/" + pkg_name
--- a/src/pkgdep.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/pkgdep.py Thu Aug 04 13:17:33 2011 -0700
@@ -42,7 +42,7 @@
import pkg.publish.dependencies as dependencies
from pkg.misc import msg, emsg, PipeError
-CLIENT_API_VERSION = 64
+CLIENT_API_VERSION = 65
PKG_CLIENT_NAME = "pkgdepend"
DEFAULT_SUFFIX = ".res"
--- a/src/sysrepo.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/sysrepo.py Thu Aug 04 13:17:33 2011 -0700
@@ -53,7 +53,7 @@
orig_cwd = None
PKG_CLIENT_NAME = "pkg.sysrepo"
-CLIENT_API_VERSION = 64
+CLIENT_API_VERSION = 65
pkg.client.global_settings.client_name = PKG_CLIENT_NAME
# exit codes
--- a/src/tests/api/t_api_list.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/tests/api/t_api_list.py Thu Aug 04 13:17:33 2011 -0700
@@ -384,7 +384,7 @@
returned = []
for entry in api_obj.get_pkg_list(pkg_list, cats=cats,
patterns=patterns, pubs=pubs, variants=variants):
- (pub, stem, ver), summ, pcats, raw_states = entry
+ (pub, stem, ver), summ, pcats, raw_states, attrs = entry
sver = ver.split(":", 1)[0]
--- a/src/tests/api/t_linked_image.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/tests/api/t_linked_image.py Thu Aug 04 13:17:33 2011 -0700
@@ -309,14 +309,14 @@
pkg_list = apio.get_pkg_list(api.ImageInterface.LIST_INSTALLED)
return set(sorted([
"pkg://%s/%s@%s" % (pfmri[0], pfmri[1], pfmri[2])
- for pfmri, summ, cats, states in pkg_list
+ for pfmri, summ, cats, states, attrs in pkg_list
]))
def _list_all_packages(self, apio):
pkg_list = apio.get_pkg_list(api.ImageInterface.LIST_ALL)
return set(sorted([
"pkg://%s/%s@%s" % (pfmri[0], pfmri[1], pfmri[2])
- for pfmri, summ, cats, states in pkg_list
+ for pfmri, summ, cats, states, attrs in pkg_list
]))
# utility functions for use by test cases
@@ -984,7 +984,7 @@
pkg_list = list(api_objs[1].get_pkg_list(
api.ImageInterface.LIST_INSTALLED))
self.assertEqual(len(pkg_list), 1)
- pfmri, summ, cats, states = pkg_list[0]
+ pfmri, summ, cats, states, attrs = pkg_list[0]
pkg_installed = "%s@%s" % (pfmri[1], pfmri[2])
self.assertEqual(pkg_installed, self.p_foo1_name[2])
@@ -999,7 +999,7 @@
pkg_list = list(api_objs[1].get_pkg_list(
api.ImageInterface.LIST_INSTALLED))
self.assertEqual(len(pkg_list), 1)
- pfmri, summ, cats, states = pkg_list[0]
+ pfmri, summ, cats, states, attrs = pkg_list[0]
pkg_installed = "%s@%s" % (pfmri[1], pfmri[2])
self.assertEqual(pkg_installed, self.p_foo1_name[0])
--- a/src/tests/cli/t_pkg_info.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/tests/cli/t_pkg_info.py Thu Aug 04 13:17:33 2011 -0700
@@ -62,6 +62,12 @@
close
"""
+ human = """
+ open [email protected],5.11-0
+ add set name=pkg.human-version value=0.9.8r
+ close
+ """
+
misc_files = [ "tmp/bronzeA1", "tmp/bronzeA2", "tmp/bronze1",
"tmp/bronze2", "tmp/copyright1", "tmp/sh", "tmp/baz"]
@@ -69,7 +75,7 @@
pkg5unittest.SingleDepotTestCase.setUp(self)
self.make_misc_files(self.misc_files)
self.plist = self.pkgsend_bulk(self.rurl, (self.badfile10,
- self.baddir10, self.bronze10))
+ self.baddir10, self.bronze10, self.human))
def test_pkg_info_bad_fmri(self):
"""Test bad frmi's with pkg info."""
@@ -257,6 +263,13 @@
self.write_img_manifest(pfmri, bad_mdata)
self.pkg("info -r %s" % pfmri.pkg_name, exit=0)
+ def test_human_version(self):
+ """Verify that info returns the expected output for packages
+ with a human-readable version defined."""
+
+ self.image_create(self.rurl)
+ self.pkg("info -r human | grep 'Version: 0.9.8.18 (0.9.8r)'")
+
def test_renamed_packages(self):
"""Verify that info returns the expected output for renamed
packages."""
--- a/src/tests/pkg5unittest.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/tests/pkg5unittest.py Thu Aug 04 13:17:33 2011 -0700
@@ -127,7 +127,7 @@
# Version test suite is known to work with.
PKG_CLIENT_NAME = "pkg"
-CLIENT_API_VERSION = 64
+CLIENT_API_VERSION = 65
ELIDABLE_ERRORS = [ TestSkippedException, depotcontroller.DepotStateException ]
--- a/src/util/distro-import/importer.py Wed Aug 03 17:49:35 2011 -0700
+++ b/src/util/distro-import/importer.py Thu Aug 04 13:17:33 2011 -0700
@@ -56,7 +56,7 @@
from pkg.misc import emsg
from pkg.portable import PD_LOCAL_PATH, PD_PROTO_DIR, PD_PROTO_DIR_LIST
-CLIENT_API_VERSION = 64
+CLIENT_API_VERSION = 65
PKG_CLIENT_NAME = "importer.py"
pkg.client.global_settings.client_name = PKG_CLIENT_NAME