1551 def __comb_newer_fmris(self, fmri, dotrim=True, obsolete_ok=True): |
1551 def __comb_newer_fmris(self, fmri, dotrim=True, obsolete_ok=True): |
1552 """Returns tuple of set of fmris that are matched within |
1552 """Returns tuple of set of fmris that are matched within |
1553 CONSTRAINT.NONE of specified version and set of remaining |
1553 CONSTRAINT.NONE of specified version and set of remaining |
1554 fmris.""" |
1554 fmris.""" |
1555 |
1555 |
1556 if dotrim or not obsolete_ok or not fmri.version or \ |
1556 return self.__comb_common(fmri, dotrim, |
1557 not fmri.version.timestr: |
1557 version.CONSTRAINT_NONE, obsolete_ok) |
1558 return self.__comb_common(fmri, dotrim, |
|
1559 version.CONSTRAINT_NONE, obsolete_ok) |
|
1560 |
|
1561 # In the case that a precise version (down to timestamp) is |
|
1562 # provided, no trimming is being done, and obsoletes are ok, the |
|
1563 # set of matching FMRIs can be determined without using version |
|
1564 # comparison because the solver caches catalog FMRIs in |
|
1565 # ascending version order. |
|
1566 |
|
1567 # determine if the data is cacheable or cached: |
|
1568 tp = (fmri, dotrim, version.CONSTRAINT_NONE, obsolete_ok) |
|
1569 try: |
|
1570 return self.__cache[tp] |
|
1571 except KeyError: |
|
1572 pass |
|
1573 |
|
1574 mver = fmri.version |
|
1575 # Always use a copy in reverse order (so versions are in |
|
1576 # descending order); return value may be cached. |
|
1577 all_fmris = self.__get_catalog_fmris(fmri.pkg_name)[::-1] |
|
1578 |
|
1579 # frozensets are used so callers don't inadvertently |
|
1580 # update these sets (which may be cached). Iteration is |
|
1581 # performed in descending version order with the |
|
1582 # assumption that systems are generally up-to-date so it |
|
1583 # should be faster to start at the end and look for the |
|
1584 # older version. |
|
1585 last_ver = None |
|
1586 for i, f in enumerate(all_fmris): |
|
1587 if mver == f.version: |
|
1588 last_ver = i |
|
1589 elif last_ver is not None: |
|
1590 break |
|
1591 |
|
1592 if last_ver is not None: |
|
1593 matching = frozenset(all_fmris[:last_ver + 1]) |
|
1594 remaining = frozenset(all_fmris[last_ver + 1:]) |
|
1595 else: |
|
1596 matching = frozenset() |
|
1597 remaining = frozenset(all_fmris) |
|
1598 |
|
1599 # if we haven't finished trimming, don't cache this |
|
1600 if not self.__trimdone: |
|
1601 return matching, remaining |
|
1602 |
|
1603 # cache the result |
|
1604 self.__cache[tp] = (matching, remaining) |
|
1605 return self.__cache[tp] |
|
1606 |
|
1607 def __comb_common_noversion(self, fmri, dotrim, obsolete_ok): |
|
1608 """Implements versionless comb logic.""" |
|
1609 |
|
1610 all_fmris = self.__get_catalog_fmris(fmri.pkg_name) |
|
1611 matching = frozenset(( |
|
1612 f |
|
1613 for f in all_fmris |
|
1614 if not dotrim or f not in self.__trim_dict |
|
1615 if obsolete_ok or not self.__fmri_is_obsolete(f) |
|
1616 )) |
|
1617 remaining = frozenset(set(all_fmris) - matching) |
|
1618 return matching, remaining |
|
1619 |
|
1620 def __comb_common_version(self, fmri, dotrim, constraint, obsolete_ok): |
|
1621 """Implements versioned comb logic.""" |
|
1622 |
|
1623 # If using a version constraint that cares about branch (but not |
|
1624 # timestr), the fmris will have to be resorted so that the |
|
1625 # version chopping done here works as expected. This is because |
|
1626 # version sort order is release, branch, timestr which is |
|
1627 # different than is_successor() order. However, if the provided |
|
1628 # FMRI has a timestamp, doesn't have a branch, or we're applying |
|
1629 # a constraint that doesn't care about the branch, then we don't |
|
1630 # need to resort. |
|
1631 mver = fmri.version |
|
1632 branch_sort = not mver.timestr and mver.branch and \ |
|
1633 constraint not in (version.CONSTRAINT_NONE, |
|
1634 version.CONSTRAINT_RELEASE, |
|
1635 version.CONSTRAINT_RELEASE_MAJOR, |
|
1636 version.CONSTRAINT_RELEASE_MINOR) |
|
1637 |
|
1638 all_fmris = self.__get_catalog_fmris(fmri.pkg_name) |
|
1639 if branch_sort: |
|
1640 # The first version of this attempted to perform |
|
1641 # multiple passes to avoid the cost of sorting by |
|
1642 # finding the last entry that matched CONSTRAINT_RELEASE |
|
1643 # and then only resorting the slice of comb_fmris from |
|
1644 # first_ver to last_ver, but that actually ended up |
|
1645 # being slower because multiple passes with |
|
1646 # is_successor() (even over a small portion of |
|
1647 # comb_fmris) is more expensive than simply resorting |
|
1648 # the entire list. Ideally, we'd get the entries from |
|
1649 # __get_catalog_fmris() in this order already which |
|
1650 # would be faster since we'd avoid a second sort. |
|
1651 skey = operator.attrgetter( |
|
1652 'version.branch', 'version.release') |
|
1653 # Always use a copy; return value may be cached. |
|
1654 comb_fmris = sorted(all_fmris, key=skey, |
|
1655 reverse=True) |
|
1656 else: |
|
1657 # Always use a copy; return value may be cached. |
|
1658 comb_fmris = all_fmris[::-1] |
|
1659 |
|
1660 # Iteration is performed in descending version order with the |
|
1661 # assumption that systems are generally up-to-date so it should |
|
1662 # be faster to start at the end and look for the oldest version |
|
1663 # that matches. |
|
1664 first_ver = None |
|
1665 last_ver = None |
|
1666 for i, f in enumerate(comb_fmris): |
|
1667 fver = f.version |
|
1668 if ((fver.is_successor(mver, constraint=constraint) or |
|
1669 fver == mver)): |
|
1670 if first_ver is None: |
|
1671 first_ver = i |
|
1672 last_ver = i |
|
1673 elif last_ver is not None: |
|
1674 break |
|
1675 |
|
1676 if last_ver is not None: |
|
1677 # Oddly enough, it's a little bit faster to iterate |
|
1678 # through the slice of comb_fmris again and store |
|
1679 # matches here instead of above. Perhaps variable |
|
1680 # scoping overhead is to blame? |
|
1681 matching = [] |
|
1682 remaining = [] |
|
1683 for f in comb_fmris[first_ver:last_ver + 1]: |
|
1684 if ((not dotrim or |
|
1685 f not in self.__trim_dict) and |
|
1686 (obsolete_ok or not |
|
1687 self.__fmri_is_obsolete(f))): |
|
1688 matching.append(f) |
|
1689 else: |
|
1690 remaining.append(f) |
|
1691 matching = frozenset(matching) |
|
1692 remaining = frozenset(chain( |
|
1693 comb_fmris[:first_ver], |
|
1694 remaining, |
|
1695 comb_fmris[last_ver + 1:])) |
|
1696 else: |
|
1697 matching = frozenset() |
|
1698 remaining = frozenset(comb_fmris) |
|
1699 |
|
1700 return matching, remaining |
|
1701 |
1558 |
1702 def __comb_common(self, fmri, dotrim, constraint, obsolete_ok): |
1559 def __comb_common(self, fmri, dotrim, constraint, obsolete_ok): |
1703 """Underlying impl. of other comb routines""" |
1560 """Underlying impl. of other comb routines""" |
1704 |
1561 |
1705 self.__progress() |
1562 self.__progress() |
1707 tp = (fmri, dotrim, constraint, obsolete_ok) # cache index |
1564 tp = (fmri, dotrim, constraint, obsolete_ok) # cache index |
1708 # determine if the data is cacheable or cached: |
1565 # determine if the data is cacheable or cached: |
1709 if (not self.__trimdone and dotrim) or tp not in self.__cache: |
1566 if (not self.__trimdone and dotrim) or tp not in self.__cache: |
1710 # use frozensets so callers don't inadvertently update |
1567 # use frozensets so callers don't inadvertently update |
1711 # these sets (which may be cached). |
1568 # these sets (which may be cached). |
1712 if not fmri.version or not fmri.version.release: |
1569 all_fmris = set(self.__get_catalog_fmris(fmri.pkg_name)) |
1713 matching, remaining = \ |
1570 matching = frozenset([ |
1714 self.__comb_common_noversion(fmri, dotrim, |
1571 f |
1715 obsolete_ok) |
1572 for f in all_fmris |
1716 else: |
1573 if f not in self.__trim_dict or not dotrim |
1717 matching, remaining = \ |
1574 if not fmri.version or |
1718 self.__comb_common_version(fmri, dotrim, |
1575 fmri.version == f.version or |
1719 constraint, obsolete_ok) |
1576 f.version.is_successor(fmri.version, |
|
1577 constraint=constraint) |
|
1578 if obsolete_ok or not self.__fmri_is_obsolete(f) |
|
1579 ]) |
|
1580 remaining = frozenset(all_fmris - matching) |
1720 |
1581 |
1721 # if we haven't finished trimming, don't cache this |
1582 # if we haven't finished trimming, don't cache this |
1722 if not self.__trimdone: |
1583 if not self.__trimdone: |
1723 return matching, remaining |
1584 return matching, remaining |
1724 # cache the result |
1585 # cache the result |