We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Similar packages in pkgs/main and pkgs/free are considered the same package
If `pkgs/main` and `pkgs/free` contain a package with the same subdir, name, version, build_number and build [conda considers](https://github.com/conda/conda/blob/4.6.6/conda/models/records.py#L253-L266) these packages identical and will not respect the sub-channel priority (main > free).  

A good example of this is the blas=1.0=mkl packages for the linux-64 platform.

Additionally packages in `pkgs/free` and `pkg/main` with the [same name, version and build](https://github.com/conda/conda/blob/4.6.6/conda/models/records.py#L272-L273) will be considered by the [solver](https://github.com/conda/conda/blob/4.6.6/conda/resolve.py#L612-L620) to be the same even if they have a different build_number.  This occurs with the blas=1.0=openblas packages for the linux-ppc64le platform which makes them un-installable.

cf: #8301 #8236 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `conda/core/index.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2012 Anaconda, Inc
3 # SPDX-License-Identifier: BSD-3-Clause
4 from __future__ import absolute_import, division, print_function, unicode_literals
5 
6 from itertools import chain
7 from logging import getLogger
8 
9 from .package_cache_data import PackageCacheData
10 from .prefix_data import PrefixData
11 from .subdir_data import SubdirData, make_feature_record
12 from .._vendor.boltons.setutils import IndexedSet
13 from .._vendor.toolz import concat, concatv
14 from ..base.context import context
15 from ..common.compat import itervalues
16 from ..common.io import ThreadLimitedThreadPoolExecutor, time_recorder
17 from ..exceptions import ChannelNotAllowed, InvalidSpec
18 from ..gateways.logging import initialize_logging
19 from ..models.channel import Channel, all_channel_urls
20 from ..models.enums import PackageType
21 from ..models.match_spec import MatchSpec
22 from ..models.records import EMPTY_LINK, PackageCacheRecord, PackageRecord, PrefixRecord
23 
24 log = getLogger(__name__)
25 
26 
27 def check_whitelist(channel_urls):
28     if context.whitelist_channels:
29         whitelist_channel_urls = tuple(concat(
30             Channel(c).base_urls for c in context.whitelist_channels
31         ))
32         for url in channel_urls:
33             these_urls = Channel(url).base_urls
34             if not all(this_url in whitelist_channel_urls for this_url in these_urls):
35                 raise ChannelNotAllowed(Channel(url))
36 
37 
38 LAST_CHANNEL_URLS = []
39 
40 @time_recorder("get_index")
41 def get_index(channel_urls=(), prepend=True, platform=None,
42               use_local=False, use_cache=False, unknown=None, prefix=None,
43               repodata_fn=context.repodata_fns[-1]):
44     """
45     Return the index of packages available on the channels
46 
47     If prepend=False, only the channels passed in as arguments are used.
48     If platform=None, then the current platform is used.
49     If prefix is supplied, then the packages installed in that prefix are added.
50     """
51     initialize_logging()  # needed in case this function is called directly as a public API
52 
53     if context.offline and unknown is None:
54         unknown = True
55 
56     channel_urls = calculate_channel_urls(channel_urls, prepend, platform, use_local)
57     del LAST_CHANNEL_URLS[:]
58     LAST_CHANNEL_URLS.extend(channel_urls)
59 
60     check_whitelist(channel_urls)
61 
62     index = fetch_index(channel_urls, use_cache=use_cache, repodata_fn=repodata_fn)
63 
64     if prefix:
65         _supplement_index_with_prefix(index, prefix)
66     if unknown:
67         _supplement_index_with_cache(index)
68     if context.track_features:
69         _supplement_index_with_features(index)
70     return index
71 
72 
73 def fetch_index(channel_urls, use_cache=False, index=None, repodata_fn=context.repodata_fns[-1]):
74     log.debug('channel_urls=' + repr(channel_urls))
75     index = {}
76     with ThreadLimitedThreadPoolExecutor() as executor:
77         subdir_instantiator = lambda url: SubdirData(Channel(url), repodata_fn=repodata_fn)
78         for f in executor.map(subdir_instantiator, channel_urls):
79             index.update((rec, rec) for rec in f.iter_records())
80     return index
81 
82 
83 def dist_str_in_index(index, dist_str):
84     match_spec = MatchSpec.from_dist_str(dist_str)
85     return any(match_spec.match(prec) for prec in itervalues(index))
86 
87 
88 def _supplement_index_with_prefix(index, prefix):
89     # supplement index with information from prefix/conda-meta
90     assert prefix
91     for prefix_record in PrefixData(prefix).iter_records():
92         if prefix_record in index:
93             # The downloaded repodata takes priority, so we do not overwrite.
94             # We do, however, copy the link information so that the solver (i.e. resolve)
95             # knows this package is installed.
96             current_record = index[prefix_record]
97             link = prefix_record.get('link') or EMPTY_LINK
98             index[prefix_record] = PrefixRecord.from_objects(
99                 current_record, prefix_record, link=link
100             )
101         else:
102             # If the package is not in the repodata, use the local data.
103             # If the channel is known but the package is not in the index, it
104             # is because 1) the channel is unavailable offline, or 2) it no
105             # longer contains this package. Either way, we should prefer any
106             # other version of the package to this one. On the other hand, if
107             # it is in a channel we don't know about, assign it a value just
108             # above the priority of all known channels.
109             index[prefix_record] = prefix_record
110 
111 
112 def _supplement_index_with_cache(index):
113     # supplement index with packages from the cache
114     for pcrec in PackageCacheData.get_all_extracted_entries():
115         if pcrec in index:
116             # The downloaded repodata takes priority
117             current_record = index[pcrec]
118             index[pcrec] = PackageCacheRecord.from_objects(current_record, pcrec)
119         else:
120             index[pcrec] = pcrec
121 
122 
123 def _make_virtual_package(name, version=None):
124     return PackageRecord(
125             package_type=PackageType.VIRTUAL_SYSTEM,
126             name=name,
127             version=version or '0',
128             build='0',
129             channel='@',
130             subdir=context.subdir,
131             md5="12345678901234567890123456789012",
132             build_number=0,
133             fn=name,
134     )
135 
136 def _supplement_index_with_features(index, features=()):
137     for feature in chain(context.track_features, features):
138         rec = make_feature_record(feature)
139         index[rec] = rec
140 
141 
142 def _supplement_index_with_system(index):
143     cuda_version = context.cuda_version
144     if cuda_version is not None:
145         rec = _make_virtual_package('__cuda', cuda_version)
146         index[rec] = rec
147 
148 
149 def calculate_channel_urls(channel_urls=(), prepend=True, platform=None, use_local=False):
150     if use_local:
151         channel_urls = ['local'] + list(channel_urls)
152     if prepend:
153         channel_urls += context.channels
154 
155     subdirs = (platform, 'noarch') if platform is not None else context.subdirs
156     return all_channel_urls(channel_urls, subdirs=subdirs)
157 
158 
159 def get_reduced_index(prefix, channels, subdirs, specs, repodata_fn):
160     records = IndexedSet()
161     collected_names = set()
162     collected_track_features = set()
163     pending_names = set()
164     pending_track_features = set()
165 
166     def push_spec(spec):
167         name = spec.get_raw_value('name')
168         if name and name not in collected_names:
169             pending_names.add(name)
170         track_features = spec.get_raw_value('track_features')
171         if track_features:
172             for ftr_name in track_features:
173                 if ftr_name not in collected_track_features:
174                     pending_track_features.add(ftr_name)
175 
176     def push_record(record):
177         try:
178             combined_depends = record.combined_depends
179         except InvalidSpec as e:
180             log.warning("Skipping %s due to InvalidSpec: %s",
181                         record.record_id(), e._kwargs["invalid_spec"])
182             return
183         push_spec(MatchSpec(record.name))
184         for _spec in combined_depends:
185             push_spec(_spec)
186         if record.track_features:
187             for ftr_name in record.track_features:
188                 push_spec(MatchSpec(track_features=ftr_name))
189 
190     if prefix:
191         for prefix_rec in PrefixData(prefix).iter_records():
192             push_record(prefix_rec)
193     for spec in specs:
194         push_spec(spec)
195 
196     while pending_names or pending_track_features:
197         while pending_names:
198             name = pending_names.pop()
199             collected_names.add(name)
200             spec = MatchSpec(name)
201             new_records = SubdirData.query_all(spec, channels=channels, subdirs=subdirs,
202                                                repodata_fn=repodata_fn)
203             for record in new_records:
204                 push_record(record)
205             records.update(new_records)
206 
207         while pending_track_features:
208             feature_name = pending_track_features.pop()
209             collected_track_features.add(feature_name)
210             spec = MatchSpec(track_features=feature_name)
211             new_records = SubdirData.query_all(spec, channels=channels, subdirs=subdirs,
212                                                repodata_fn=repodata_fn)
213             for record in new_records:
214                 push_record(record)
215             records.update(new_records)
216 
217     reduced_index = {rec: rec for rec in records}
218 
219     if prefix is not None:
220         _supplement_index_with_prefix(reduced_index, prefix)
221 
222     if context.offline or ('unknown' in context._argparse_args
223                            and context._argparse_args.unknown):
224         # This is really messed up right now.  Dates all the way back to
225         # https://github.com/conda/conda/commit/f761f65a82b739562a0d997a2570e2b8a0bdc783
226         # TODO: revisit this later
227         _supplement_index_with_cache(reduced_index)
228 
229     # add feature records for the solver
230     known_features = set()
231     for rec in itervalues(reduced_index):
232         known_features.update(concatv(rec.track_features, rec.features))
233     known_features.update(context.track_features)
234     for ftr_str in known_features:
235         rec = make_feature_record(ftr_str)
236         reduced_index[rec] = rec
237 
238     _supplement_index_with_system(reduced_index)
239 
240     return reduced_index
241
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/conda/core/index.py b/conda/core/index.py
--- a/conda/core/index.py
+++ b/conda/core/index.py
@@ -90,14 +90,26 @@
     assert prefix
     for prefix_record in PrefixData(prefix).iter_records():
         if prefix_record in index:
-            # The downloaded repodata takes priority, so we do not overwrite.
-            # We do, however, copy the link information so that the solver (i.e. resolve)
-            # knows this package is installed.
             current_record = index[prefix_record]
-            link = prefix_record.get('link') or EMPTY_LINK
-            index[prefix_record] = PrefixRecord.from_objects(
-                current_record, prefix_record, link=link
-            )
+            if current_record.channel == prefix_record.channel:
+                # The downloaded repodata takes priority, so we do not overwrite.
+                # We do, however, copy the link information so that the solver (i.e. resolve)
+                # knows this package is installed.
+                link = prefix_record.get('link') or EMPTY_LINK
+                index[prefix_record] = PrefixRecord.from_objects(
+                    current_record, prefix_record, link=link
+                )
+            else:
+                # If the local packages channel information does not agree with
+                # the channel information in the index then they are most
+                # likely referring to different packages.  This can occur if a
+                # multi-channel changes configuration, e.g. defaults with and
+                # without the free channel. In this case we need to fake the
+                # channel data for the existing package.
+                prefix_channel = prefix_record.channel
+                prefix_channel._Channel__canonical_name = prefix_channel.url()
+                del prefix_record._PackageRecord__pkey
+                index[prefix_record] = prefix_record
         else:
             # If the package is not in the repodata, use the local data.
             # If the channel is known but the package is not in the index, it

{"golden_diff": "diff --git a/conda/core/index.py b/conda/core/index.py\n--- a/conda/core/index.py\n+++ b/conda/core/index.py\n@@ -90,14 +90,26 @@\n     assert prefix\n     for prefix_record in PrefixData(prefix).iter_records():\n         if prefix_record in index:\n-            # The downloaded repodata takes priority, so we do not overwrite.\n-            # We do, however, copy the link information so that the solver (i.e. resolve)\n-            # knows this package is installed.\n             current_record = index[prefix_record]\n-            link = prefix_record.get('link') or EMPTY_LINK\n-            index[prefix_record] = PrefixRecord.from_objects(\n-                current_record, prefix_record, link=link\n-            )\n+            if current_record.channel == prefix_record.channel:\n+                # The downloaded repodata takes priority, so we do not overwrite.\n+                # We do, however, copy the link information so that the solver (i.e. resolve)\n+                # knows this package is installed.\n+                link = prefix_record.get('link') or EMPTY_LINK\n+                index[prefix_record] = PrefixRecord.from_objects(\n+                    current_record, prefix_record, link=link\n+                )\n+            else:\n+                # If the local packages channel information does not agree with\n+                # the channel information in the index then they are most\n+                # likely referring to different packages.  This can occur if a\n+                # multi-channel changes configuration, e.g. defaults with and\n+                # without the free channel. In this case we need to fake the\n+                # channel data for the existing package.\n+                prefix_channel = prefix_record.channel\n+                prefix_channel._Channel__canonical_name = prefix_channel.url()\n+                del prefix_record._PackageRecord__pkey\n+                index[prefix_record] = prefix_record\n         else:\n             # If the package is not in the repodata, use the local data.\n             # If the channel is known but the package is not in the index, it\n", "issue": "Similar packages in pkgs/main and pkgs/free are considered the same package\nIf `pkgs/main` and `pkgs/free` contain a package with the same subdir, name, version, build_number and build [conda considers](https://github.com/conda/conda/blob/4.6.6/conda/models/records.py#L253-L266) these packages identical and will not respect the sub-channel priority (main > free).  \r\n\r\nA good example of this is the blas=1.0=mkl packages for the linux-64 platform.\r\n\r\nAdditionally packages in `pkgs/free` and `pkg/main` with the [same name, version and build](https://github.com/conda/conda/blob/4.6.6/conda/models/records.py#L272-L273) will be considered by the [solver](https://github.com/conda/conda/blob/4.6.6/conda/resolve.py#L612-L620) to be the same even if they have a different build_number.  This occurs with the blas=1.0=openblas packages for the linux-ppc64le platform which makes them un-installable.\r\n\r\ncf: #8301 #8236 \n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (C) 2012 Anaconda, Inc\n# SPDX-License-Identifier: BSD-3-Clause\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nfrom itertools import chain\nfrom logging import getLogger\n\nfrom .package_cache_data import PackageCacheData\nfrom .prefix_data import PrefixData\nfrom .subdir_data import SubdirData, make_feature_record\nfrom .._vendor.boltons.setutils import IndexedSet\nfrom .._vendor.toolz import concat, concatv\nfrom ..base.context import context\nfrom ..common.compat import itervalues\nfrom ..common.io import ThreadLimitedThreadPoolExecutor, time_recorder\nfrom ..exceptions import ChannelNotAllowed, InvalidSpec\nfrom ..gateways.logging import initialize_logging\nfrom ..models.channel import Channel, all_channel_urls\nfrom ..models.enums import PackageType\nfrom ..models.match_spec import MatchSpec\nfrom ..models.records import EMPTY_LINK, PackageCacheRecord, PackageRecord, PrefixRecord\n\nlog = getLogger(__name__)\n\n\ndef check_whitelist(channel_urls):\n    if context.whitelist_channels:\n        whitelist_channel_urls = tuple(concat(\n            Channel(c).base_urls for c in context.whitelist_channels\n        ))\n        for url in channel_urls:\n            these_urls = Channel(url).base_urls\n            if not all(this_url in whitelist_channel_urls for this_url in these_urls):\n                raise ChannelNotAllowed(Channel(url))\n\n\nLAST_CHANNEL_URLS = []\n\n@time_recorder(\"get_index\")\ndef get_index(channel_urls=(), prepend=True, platform=None,\n              use_local=False, use_cache=False, unknown=None, prefix=None,\n              repodata_fn=context.repodata_fns[-1]):\n    \"\"\"\n    Return the index of packages available on the channels\n\n    If prepend=False, only the channels passed in as arguments are used.\n    If platform=None, then the current platform is used.\n    If prefix is supplied, then the packages installed in that prefix are added.\n    \"\"\"\n    initialize_logging()  # needed in case this function is called directly as a public API\n\n    if context.offline and unknown is None:\n        unknown = True\n\n    channel_urls = calculate_channel_urls(channel_urls, prepend, platform, use_local)\n    del LAST_CHANNEL_URLS[:]\n    LAST_CHANNEL_URLS.extend(channel_urls)\n\n    check_whitelist(channel_urls)\n\n    index = fetch_index(channel_urls, use_cache=use_cache, repodata_fn=repodata_fn)\n\n    if prefix:\n        _supplement_index_with_prefix(index, prefix)\n    if unknown:\n        _supplement_index_with_cache(index)\n    if context.track_features:\n        _supplement_index_with_features(index)\n    return index\n\n\ndef fetch_index(channel_urls, use_cache=False, index=None, repodata_fn=context.repodata_fns[-1]):\n    log.debug('channel_urls=' + repr(channel_urls))\n    index = {}\n    with ThreadLimitedThreadPoolExecutor() as executor:\n        subdir_instantiator = lambda url: SubdirData(Channel(url), repodata_fn=repodata_fn)\n        for f in executor.map(subdir_instantiator, channel_urls):\n            index.update((rec, rec) for rec in f.iter_records())\n    return index\n\n\ndef dist_str_in_index(index, dist_str):\n    match_spec = MatchSpec.from_dist_str(dist_str)\n    return any(match_spec.match(prec) for prec in itervalues(index))\n\n\ndef _supplement_index_with_prefix(index, prefix):\n    # supplement index with information from prefix/conda-meta\n    assert prefix\n    for prefix_record in PrefixData(prefix).iter_records():\n        if prefix_record in index:\n            # The downloaded repodata takes priority, so we do not overwrite.\n            # We do, however, copy the link information so that the solver (i.e. resolve)\n            # knows this package is installed.\n            current_record = index[prefix_record]\n            link = prefix_record.get('link') or EMPTY_LINK\n            index[prefix_record] = PrefixRecord.from_objects(\n                current_record, prefix_record, link=link\n            )\n        else:\n            # If the package is not in the repodata, use the local data.\n            # If the channel is known but the package is not in the index, it\n            # is because 1) the channel is unavailable offline, or 2) it no\n            # longer contains this package. Either way, we should prefer any\n            # other version of the package to this one. On the other hand, if\n            # it is in a channel we don't know about, assign it a value just\n            # above the priority of all known channels.\n            index[prefix_record] = prefix_record\n\n\ndef _supplement_index_with_cache(index):\n    # supplement index with packages from the cache\n    for pcrec in PackageCacheData.get_all_extracted_entries():\n        if pcrec in index:\n            # The downloaded repodata takes priority\n            current_record = index[pcrec]\n            index[pcrec] = PackageCacheRecord.from_objects(current_record, pcrec)\n        else:\n            index[pcrec] = pcrec\n\n\ndef _make_virtual_package(name, version=None):\n    return PackageRecord(\n            package_type=PackageType.VIRTUAL_SYSTEM,\n            name=name,\n            version=version or '0',\n            build='0',\n            channel='@',\n            subdir=context.subdir,\n            md5=\"12345678901234567890123456789012\",\n            build_number=0,\n            fn=name,\n    )\n\ndef _supplement_index_with_features(index, features=()):\n    for feature in chain(context.track_features, features):\n        rec = make_feature_record(feature)\n        index[rec] = rec\n\n\ndef _supplement_index_with_system(index):\n    cuda_version = context.cuda_version\n    if cuda_version is not None:\n        rec = _make_virtual_package('__cuda', cuda_version)\n        index[rec] = rec\n\n\ndef calculate_channel_urls(channel_urls=(), prepend=True, platform=None, use_local=False):\n    if use_local:\n        channel_urls = ['local'] + list(channel_urls)\n    if prepend:\n        channel_urls += context.channels\n\n    subdirs = (platform, 'noarch') if platform is not None else context.subdirs\n    return all_channel_urls(channel_urls, subdirs=subdirs)\n\n\ndef get_reduced_index(prefix, channels, subdirs, specs, repodata_fn):\n    records = IndexedSet()\n    collected_names = set()\n    collected_track_features = set()\n    pending_names = set()\n    pending_track_features = set()\n\n    def push_spec(spec):\n        name = spec.get_raw_value('name')\n        if name and name not in collected_names:\n            pending_names.add(name)\n        track_features = spec.get_raw_value('track_features')\n        if track_features:\n            for ftr_name in track_features:\n                if ftr_name not in collected_track_features:\n                    pending_track_features.add(ftr_name)\n\n    def push_record(record):\n        try:\n            combined_depends = record.combined_depends\n        except InvalidSpec as e:\n            log.warning(\"Skipping %s due to InvalidSpec: %s\",\n                        record.record_id(), e._kwargs[\"invalid_spec\"])\n            return\n        push_spec(MatchSpec(record.name))\n        for _spec in combined_depends:\n            push_spec(_spec)\n        if record.track_features:\n            for ftr_name in record.track_features:\n                push_spec(MatchSpec(track_features=ftr_name))\n\n    if prefix:\n        for prefix_rec in PrefixData(prefix).iter_records():\n            push_record(prefix_rec)\n    for spec in specs:\n        push_spec(spec)\n\n    while pending_names or pending_track_features:\n        while pending_names:\n            name = pending_names.pop()\n            collected_names.add(name)\n            spec = MatchSpec(name)\n            new_records = SubdirData.query_all(spec, channels=channels, subdirs=subdirs,\n                                               repodata_fn=repodata_fn)\n            for record in new_records:\n                push_record(record)\n            records.update(new_records)\n\n        while pending_track_features:\n            feature_name = pending_track_features.pop()\n            collected_track_features.add(feature_name)\n            spec = MatchSpec(track_features=feature_name)\n            new_records = SubdirData.query_all(spec, channels=channels, subdirs=subdirs,\n                                               repodata_fn=repodata_fn)\n            for record in new_records:\n                push_record(record)\n            records.update(new_records)\n\n    reduced_index = {rec: rec for rec in records}\n\n    if prefix is not None:\n        _supplement_index_with_prefix(reduced_index, prefix)\n\n    if context.offline or ('unknown' in context._argparse_args\n                           and context._argparse_args.unknown):\n        # This is really messed up right now.  Dates all the way back to\n        # https://github.com/conda/conda/commit/f761f65a82b739562a0d997a2570e2b8a0bdc783\n        # TODO: revisit this later\n        _supplement_index_with_cache(reduced_index)\n\n    # add feature records for the solver\n    known_features = set()\n    for rec in itervalues(reduced_index):\n        known_features.update(concatv(rec.track_features, rec.features))\n    known_features.update(context.track_features)\n    for ftr_str in known_features:\n        rec = make_feature_record(ftr_str)\n        reduced_index[rec] = rec\n\n    _supplement_index_with_system(reduced_index)\n\n    return reduced_index\n", "path": "conda/core/index.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (C) 2012 Anaconda, Inc\n# SPDX-License-Identifier: BSD-3-Clause\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nfrom itertools import chain\nfrom logging import getLogger\n\nfrom .package_cache_data import PackageCacheData\nfrom .prefix_data import PrefixData\nfrom .subdir_data import SubdirData, make_feature_record\nfrom .._vendor.boltons.setutils import IndexedSet\nfrom .._vendor.toolz import concat, concatv\nfrom ..base.context import context\nfrom ..common.compat import itervalues\nfrom ..common.io import ThreadLimitedThreadPoolExecutor, time_recorder\nfrom ..exceptions import ChannelNotAllowed, InvalidSpec\nfrom ..gateways.logging import initialize_logging\nfrom ..models.channel import Channel, all_channel_urls\nfrom ..models.enums import PackageType\nfrom ..models.match_spec import MatchSpec\nfrom ..models.records import EMPTY_LINK, PackageCacheRecord, PackageRecord, PrefixRecord\n\nlog = getLogger(__name__)\n\n\ndef check_whitelist(channel_urls):\n    if context.whitelist_channels:\n        whitelist_channel_urls = tuple(concat(\n            Channel(c).base_urls for c in context.whitelist_channels\n        ))\n        for url in channel_urls:\n            these_urls = Channel(url).base_urls\n            if not all(this_url in whitelist_channel_urls for this_url in these_urls):\n                raise ChannelNotAllowed(Channel(url))\n\n\nLAST_CHANNEL_URLS = []\n\n@time_recorder(\"get_index\")\ndef get_index(channel_urls=(), prepend=True, platform=None,\n              use_local=False, use_cache=False, unknown=None, prefix=None,\n              repodata_fn=context.repodata_fns[-1]):\n    \"\"\"\n    Return the index of packages available on the channels\n\n    If prepend=False, only the channels passed in as arguments are used.\n    If platform=None, then the current platform is used.\n    If prefix is supplied, then the packages installed in that prefix are added.\n    \"\"\"\n    initialize_logging()  # needed in case this function is called directly as a public API\n\n    if context.offline and unknown is None:\n        unknown = True\n\n    channel_urls = calculate_channel_urls(channel_urls, prepend, platform, use_local)\n    del LAST_CHANNEL_URLS[:]\n    LAST_CHANNEL_URLS.extend(channel_urls)\n\n    check_whitelist(channel_urls)\n\n    index = fetch_index(channel_urls, use_cache=use_cache, repodata_fn=repodata_fn)\n\n    if prefix:\n        _supplement_index_with_prefix(index, prefix)\n    if unknown:\n        _supplement_index_with_cache(index)\n    if context.track_features:\n        _supplement_index_with_features(index)\n    return index\n\n\ndef fetch_index(channel_urls, use_cache=False, index=None, repodata_fn=context.repodata_fns[-1]):\n    log.debug('channel_urls=' + repr(channel_urls))\n    index = {}\n    with ThreadLimitedThreadPoolExecutor() as executor:\n        subdir_instantiator = lambda url: SubdirData(Channel(url), repodata_fn=repodata_fn)\n        for f in executor.map(subdir_instantiator, channel_urls):\n            index.update((rec, rec) for rec in f.iter_records())\n    return index\n\n\ndef dist_str_in_index(index, dist_str):\n    match_spec = MatchSpec.from_dist_str(dist_str)\n    return any(match_spec.match(prec) for prec in itervalues(index))\n\n\ndef _supplement_index_with_prefix(index, prefix):\n    # supplement index with information from prefix/conda-meta\n    assert prefix\n    for prefix_record in PrefixData(prefix).iter_records():\n        if prefix_record in index:\n            current_record = index[prefix_record]\n            if current_record.channel == prefix_record.channel:\n                # The downloaded repodata takes priority, so we do not overwrite.\n                # We do, however, copy the link information so that the solver (i.e. resolve)\n                # knows this package is installed.\n                link = prefix_record.get('link') or EMPTY_LINK\n                index[prefix_record] = PrefixRecord.from_objects(\n                    current_record, prefix_record, link=link\n                )\n            else:\n                # If the local packages channel information does not agree with\n                # the channel information in the index then they are most\n                # likely referring to different packages.  This can occur if a\n                # multi-channel changes configuration, e.g. defaults with and\n                # without the free channel. In this case we need to fake the\n                # channel data for the existing package.\n                prefix_channel = prefix_record.channel\n                prefix_channel._Channel__canonical_name = prefix_channel.url()\n                del prefix_record._PackageRecord__pkey\n                index[prefix_record] = prefix_record\n        else:\n            # If the package is not in the repodata, use the local data.\n            # If the channel is known but the package is not in the index, it\n            # is because 1) the channel is unavailable offline, or 2) it no\n            # longer contains this package. Either way, we should prefer any\n            # other version of the package to this one. On the other hand, if\n            # it is in a channel we don't know about, assign it a value just\n            # above the priority of all known channels.\n            index[prefix_record] = prefix_record\n\n\ndef _supplement_index_with_cache(index):\n    # supplement index with packages from the cache\n    for pcrec in PackageCacheData.get_all_extracted_entries():\n        if pcrec in index:\n            # The downloaded repodata takes priority\n            current_record = index[pcrec]\n            index[pcrec] = PackageCacheRecord.from_objects(current_record, pcrec)\n        else:\n            index[pcrec] = pcrec\n\n\ndef _make_virtual_package(name, version=None):\n    return PackageRecord(\n            package_type=PackageType.VIRTUAL_SYSTEM,\n            name=name,\n            version=version or '0',\n            build='0',\n            channel='@',\n            subdir=context.subdir,\n            md5=\"12345678901234567890123456789012\",\n            build_number=0,\n            fn=name,\n    )\n\ndef _supplement_index_with_features(index, features=()):\n    for feature in chain(context.track_features, features):\n        rec = make_feature_record(feature)\n        index[rec] = rec\n\n\ndef _supplement_index_with_system(index):\n    cuda_version = context.cuda_version\n    if cuda_version is not None:\n        rec = _make_virtual_package('__cuda', cuda_version)\n        index[rec] = rec\n\n\ndef calculate_channel_urls(channel_urls=(), prepend=True, platform=None, use_local=False):\n    if use_local:\n        channel_urls = ['local'] + list(channel_urls)\n    if prepend:\n        channel_urls += context.channels\n\n    subdirs = (platform, 'noarch') if platform is not None else context.subdirs\n    return all_channel_urls(channel_urls, subdirs=subdirs)\n\n\ndef get_reduced_index(prefix, channels, subdirs, specs, repodata_fn):\n    records = IndexedSet()\n    collected_names = set()\n    collected_track_features = set()\n    pending_names = set()\n    pending_track_features = set()\n\n    def push_spec(spec):\n        name = spec.get_raw_value('name')\n        if name and name not in collected_names:\n            pending_names.add(name)\n        track_features = spec.get_raw_value('track_features')\n        if track_features:\n            for ftr_name in track_features:\n                if ftr_name not in collected_track_features:\n                    pending_track_features.add(ftr_name)\n\n    def push_record(record):\n        try:\n            combined_depends = record.combined_depends\n        except InvalidSpec as e:\n            log.warning(\"Skipping %s due to InvalidSpec: %s\",\n                        record.record_id(), e._kwargs[\"invalid_spec\"])\n            return\n        push_spec(MatchSpec(record.name))\n        for _spec in combined_depends:\n            push_spec(_spec)\n        if record.track_features:\n            for ftr_name in record.track_features:\n                push_spec(MatchSpec(track_features=ftr_name))\n\n    if prefix:\n        for prefix_rec in PrefixData(prefix).iter_records():\n            push_record(prefix_rec)\n    for spec in specs:\n        push_spec(spec)\n\n    while pending_names or pending_track_features:\n        while pending_names:\n            name = pending_names.pop()\n            collected_names.add(name)\n            spec = MatchSpec(name)\n            new_records = SubdirData.query_all(spec, channels=channels, subdirs=subdirs,\n                                               repodata_fn=repodata_fn)\n            for record in new_records:\n                push_record(record)\n            records.update(new_records)\n\n        while pending_track_features:\n            feature_name = pending_track_features.pop()\n            collected_track_features.add(feature_name)\n            spec = MatchSpec(track_features=feature_name)\n            new_records = SubdirData.query_all(spec, channels=channels, subdirs=subdirs,\n                                               repodata_fn=repodata_fn)\n            for record in new_records:\n                push_record(record)\n            records.update(new_records)\n\n    reduced_index = {rec: rec for rec in records}\n\n    if prefix is not None:\n        _supplement_index_with_prefix(reduced_index, prefix)\n\n    if context.offline or ('unknown' in context._argparse_args\n                           and context._argparse_args.unknown):\n        # This is really messed up right now.  Dates all the way back to\n        # https://github.com/conda/conda/commit/f761f65a82b739562a0d997a2570e2b8a0bdc783\n        # TODO: revisit this later\n        _supplement_index_with_cache(reduced_index)\n\n    # add feature records for the solver\n    known_features = set()\n    for rec in itervalues(reduced_index):\n        known_features.update(concatv(rec.track_features, rec.features))\n    known_features.update(context.track_features)\n    for ftr_str in known_features:\n        rec = make_feature_record(ftr_str)\n        reduced_index[rec] = rec\n\n    _supplement_index_with_system(reduced_index)\n\n    return reduced_index\n", "path": "conda/core/index.py"}]}