We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
non-conda-installed python packages can have missing version information
When reading up metadata out of site-packages, non-conda-installed python packages can sometime be missing version information. When this is the case, we should default to a hard-codes `0.0.0` version. I believe this behavior is consistent with python’s own `python setup.py —version` when no version is actually given in the `setup()` function. 

--- 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/prefix_data.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 fnmatch import filter as fnmatch_filter
7 from logging import getLogger
8 from os import listdir
9 from os.path import basename, isdir, isfile, join, lexists
10 
11 from ..base.constants import CONDA_TARBALL_EXTENSION, PREFIX_MAGIC_FILE
12 from ..base.context import context
13 from ..common.compat import JSONDecodeError, itervalues, odict, string_types, with_metaclass
14 from ..common.constants import NULL
15 from ..common.path import get_python_site_packages_short_path, win_path_ok
16 from ..common.pkg_formats.python import get_site_packages_anchor_files
17 from ..common.serialize import json_load
18 from ..exceptions import (
19     BasicClobberError, CondaDependencyError, CorruptedEnvironmentError, maybe_raise,
20 )
21 from ..gateways.disk.create import write_as_json_to_file
22 from ..gateways.disk.delete import rm_rf
23 from ..gateways.disk.read import read_python_record
24 from ..gateways.disk.test import file_path_is_writable
25 from ..models.match_spec import MatchSpec
26 from ..models.prefix_graph import PrefixGraph
27 from ..models.records import PackageRecord, PrefixRecord
28 
29 log = getLogger(__name__)
30 
31 
32 class PrefixDataType(type):
33     """Basic caching of PrefixData instance objects."""
34 
35     def __call__(cls, prefix_path, pip_interop_enabled=None):
36         if prefix_path in PrefixData._cache_:
37             return PrefixData._cache_[prefix_path]
38         elif isinstance(prefix_path, PrefixData):
39             return prefix_path
40         else:
41             prefix_data_instance = super(PrefixDataType, cls).__call__(prefix_path,
42                                                                        pip_interop_enabled)
43             PrefixData._cache_[prefix_path] = prefix_data_instance
44             return prefix_data_instance
45 
46 
47 @with_metaclass(PrefixDataType)
48 class PrefixData(object):
49     _cache_ = {}
50 
51     def __init__(self, prefix_path, pip_interop_enabled=None):
52         # pip_interop_enabled is a temporary paramater; DO NOT USE
53         # TODO: when removing pip_interop_enabled, also remove from meta class
54         self.prefix_path = prefix_path
55         self.__prefix_records = None
56         self.__is_writable = NULL
57         self._pip_interop_enabled = (context.pip_interop_enabled
58                                      if pip_interop_enabled is None
59                                      else pip_interop_enabled)
60 
61     def load(self):
62         self.__prefix_records = {}
63         _conda_meta_dir = join(self.prefix_path, 'conda-meta')
64         if lexists(_conda_meta_dir):
65             for meta_file in fnmatch_filter(listdir(_conda_meta_dir), '*.json'):
66                 self._load_single_record(join(_conda_meta_dir, meta_file))
67         if self._pip_interop_enabled:
68             self._load_site_packages()
69 
70     def reload(self):
71         self.load()
72         return self
73 
74     def insert(self, prefix_record):
75         assert prefix_record.name not in self._prefix_records
76 
77         assert prefix_record.fn.endswith(CONDA_TARBALL_EXTENSION)
78         filename = prefix_record.fn[:-len(CONDA_TARBALL_EXTENSION)] + '.json'
79 
80         prefix_record_json_path = join(self.prefix_path, 'conda-meta', filename)
81         if lexists(prefix_record_json_path):
82             maybe_raise(BasicClobberError(
83                 source_path=None,
84                 target_path=prefix_record_json_path,
85                 context=context,
86             ), context)
87             rm_rf(prefix_record_json_path)
88 
89         write_as_json_to_file(prefix_record_json_path, prefix_record)
90 
91         self._prefix_records[prefix_record.name] = prefix_record
92 
93     def remove(self, package_name):
94         assert package_name in self._prefix_records
95 
96         prefix_record = self._prefix_records[package_name]
97 
98         filename = prefix_record.fn[:-len(CONDA_TARBALL_EXTENSION)] + '.json'
99         conda_meta_full_path = join(self.prefix_path, 'conda-meta', filename)
100         if self.is_writable:
101             rm_rf(conda_meta_full_path)
102 
103         del self._prefix_records[package_name]
104 
105     def get(self, package_name, default=NULL):
106         try:
107             return self._prefix_records[package_name]
108         except KeyError:
109             if default is not NULL:
110                 return default
111             else:
112                 raise
113 
114     def iter_records(self):
115         return itervalues(self._prefix_records)
116 
117     def iter_records_sorted(self):
118         prefix_graph = PrefixGraph(self.iter_records())
119         return iter(prefix_graph.graph)
120 
121     def all_subdir_urls(self):
122         subdir_urls = set()
123         for prefix_record in itervalues(self._prefix_records):
124             subdir_url = prefix_record.channel.subdir_url
125             if subdir_url and subdir_url not in subdir_urls:
126                 log.debug("adding subdir url %s for %s", subdir_url, prefix_record)
127                 subdir_urls.add(subdir_url)
128         return subdir_urls
129 
130     def query(self, package_ref_or_match_spec):
131         # returns a generator
132         param = package_ref_or_match_spec
133         if isinstance(param, string_types):
134             param = MatchSpec(param)
135         if isinstance(param, MatchSpec):
136             return (prefix_rec for prefix_rec in self.iter_records()
137                     if param.match(prefix_rec))
138         else:
139             assert isinstance(param, PackageRecord)
140             return (prefix_rec for prefix_rec in self.iter_records() if prefix_rec == param)
141 
142     @property
143     def _prefix_records(self):
144         return self.__prefix_records or self.load() or self.__prefix_records
145 
146     def _load_single_record(self, prefix_record_json_path):
147         log.trace("loading prefix record %s", prefix_record_json_path)
148         with open(prefix_record_json_path) as fh:
149             try:
150                 json_data = json_load(fh.read())
151             except JSONDecodeError:
152                 raise CorruptedEnvironmentError(self.prefix_path, prefix_record_json_path)
153 
154             # TODO: consider, at least in memory, storing prefix_record_json_path as part
155             #       of PrefixRecord
156             prefix_record = PrefixRecord(**json_data)
157 
158             # check that prefix record json filename conforms to name-version-build
159             # apparently implemented as part of #2638 to resolve #2599
160             try:
161                 n, v, b = basename(prefix_record_json_path)[:-5].rsplit('-', 2)
162                 if (n, v, b) != (prefix_record.name, prefix_record.version, prefix_record.build):
163                     raise ValueError()
164             except ValueError:
165                 log.warn("Ignoring malformed prefix record at: %s", prefix_record_json_path)
166                 # TODO: consider just deleting here this record file in the future
167                 return
168 
169             self.__prefix_records[prefix_record.name] = prefix_record
170 
171     @property
172     def is_writable(self):
173         if self.__is_writable == NULL:
174             test_path = join(self.prefix_path, PREFIX_MAGIC_FILE)
175             if not isfile(test_path):
176                 is_writable = None
177             else:
178                 is_writable = file_path_is_writable(test_path)
179             self.__is_writable = is_writable
180         return self.__is_writable
181 
182     # # REMOVE: ?
183     def _has_python(self):
184         return 'python' in self._prefix_records
185 
186     @property
187     def _python_pkg_record(self):
188         """Return the prefix record for the package python."""
189         return next(
190             (prefix_record for prefix_record in itervalues(self.__prefix_records)
191              if prefix_record.name == 'python'),
192             None
193         )
194 
195     def _load_site_packages(self):
196         """
197         Load non-conda-installed python packages in the site-packages of the prefix.
198 
199         Python packages not handled by conda are installed via other means,
200         like using pip or using python setup.py develop for local development.
201 
202         Packages found that are not handled by conda are converted into a
203         prefix record and handled in memory.
204 
205         Packages clobbering conda packages (i.e. the conda-meta record) are
206         removed from the in memory representation.
207         """
208         python_pkg_record = self._python_pkg_record
209 
210         if not python_pkg_record:
211             return {}
212 
213         site_packages_dir = get_python_site_packages_short_path(python_pkg_record.version)
214         site_packages_path = join(self.prefix_path, win_path_ok(site_packages_dir))
215 
216         if not isdir(site_packages_path):
217             return {}
218 
219         # Get anchor files for corresponding conda (handled) python packages
220         prefix_graph = PrefixGraph(self.iter_records())
221         python_records = prefix_graph.all_descendants(python_pkg_record)
222         conda_python_packages = get_conda_anchor_files_and_records(python_records)
223 
224         # Get all anchor files and compare against conda anchor files to find clobbered conda
225         # packages and python packages installed via other means (not handled by conda)
226         sp_anchor_files = get_site_packages_anchor_files(site_packages_path, site_packages_dir)
227         conda_anchor_files = set(conda_python_packages)
228         clobbered_conda_anchor_files = conda_anchor_files - sp_anchor_files
229         non_conda_anchor_files = sp_anchor_files - conda_anchor_files
230 
231         # If there's a mismatch for anchor files between what conda expects for a package
232         # based on conda-meta, and for what is actually in site-packages, then we'll delete
233         # the in-memory record for the conda package.  In the future, we should consider
234         # also deleting the record on disk in the conda-meta/ directory.
235         for conda_anchor_file in clobbered_conda_anchor_files:
236             prefix_rec = self._prefix_records.pop(conda_python_packages[conda_anchor_file].name)
237             try:
238                 extracted_package_dir = basename(prefix_rec.extracted_package_dir)
239             except AttributeError:
240                 extracted_package_dir = "-".join((
241                     prefix_rec.name, prefix_rec.version, prefix_rec.build
242                 ))
243             prefix_rec_json_path = join(
244                 self.prefix_path, "conda-meta", '%s.json' % extracted_package_dir
245             )
246             try:
247                 rm_rf(prefix_rec_json_path)
248             except EnvironmentError:
249                 log.debug("stale information, but couldn't remove: %s", prefix_rec_json_path)
250             else:
251                 log.debug("removed due to stale information: %s", prefix_rec_json_path)
252 
253         # Create prefix records for python packages not handled by conda
254         new_packages = {}
255         for af in non_conda_anchor_files:
256             try:
257                 python_record = read_python_record(self.prefix_path, af, python_pkg_record.version)
258             except EnvironmentError:
259                 continue
260             if not python_record:
261                 continue
262             self.__prefix_records[python_record.name] = python_record
263             new_packages[python_record.name] = python_record
264 
265         return new_packages
266 
267 
268 def get_conda_anchor_files_and_records(python_records):
269     """Return the anchor files for the conda records of python packages."""
270     anchor_file_endings = ('.egg-info/PKG-INFO', '.dist-info/RECORD', '.egg-info')
271     conda_python_packages = odict()
272 
273     for prefix_record in python_records:
274         for fpath in prefix_record.files:
275             if fpath.endswith(anchor_file_endings) and 'site-packages' in fpath:
276                 # Then 'fpath' is an anchor file
277                 conda_python_packages[fpath] = prefix_record
278 
279     return conda_python_packages
280 
281 
282 def get_python_version_for_prefix(prefix):
283     # returns a string e.g. "2.7", "3.4", "3.5" or None
284     py_record_iter = (rcrd for rcrd in PrefixData(prefix).iter_records() if rcrd.name == 'python')
285     record = next(py_record_iter, None)
286     if record is None:
287         return None
288     next_record = next(py_record_iter, None)
289     if next_record is not None:
290         raise CondaDependencyError("multiple python records found in prefix %s" % prefix)
291     else:
292         return record.version[:3]
293 
294 
295 def delete_prefix_from_linked_data(path):
296     '''Here, path may be a complete prefix or a dist inside a prefix'''
297     linked_data_path = next((key for key in sorted(PrefixData._cache_, reverse=True)
298                              if path.startswith(key)),
299                             None)
300     if linked_data_path:
301         del PrefixData._cache_[linked_data_path]
302         return True
303     return False
304
```
--- 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/prefix_data.py b/conda/core/prefix_data.py
--- a/conda/core/prefix_data.py
+++ b/conda/core/prefix_data.py
@@ -8,6 +8,7 @@
 from os import listdir
 from os.path import basename, isdir, isfile, join, lexists
 
+from .._vendor.auxlib.exceptions import ValidationError
 from ..base.constants import CONDA_TARBALL_EXTENSION, PREFIX_MAGIC_FILE
 from ..base.context import context
 from ..common.compat import JSONDecodeError, itervalues, odict, string_types, with_metaclass
@@ -257,6 +258,16 @@
                 python_record = read_python_record(self.prefix_path, af, python_pkg_record.version)
             except EnvironmentError:
                 continue
+            except ValidationError:
+                import sys
+                exc_type, exc_value, exc_traceback = sys.exc_info()
+                import traceback
+                tb = traceback.format_exception(exc_type, exc_value, exc_traceback)
+                log.warn("Problem reading non-conda package record at %s. Please verify that you "
+                         "still need this, and if so, that this is still installed correctly. "
+                         "Reinstalling this package may help.", af)
+                log.debug("ValidationError: \n%s\n", "\n".join(tb))
+                continue
             if not python_record:
                 continue
             self.__prefix_records[python_record.name] = python_record

{"golden_diff": "diff --git a/conda/core/prefix_data.py b/conda/core/prefix_data.py\n--- a/conda/core/prefix_data.py\n+++ b/conda/core/prefix_data.py\n@@ -8,6 +8,7 @@\n from os import listdir\n from os.path import basename, isdir, isfile, join, lexists\n \n+from .._vendor.auxlib.exceptions import ValidationError\n from ..base.constants import CONDA_TARBALL_EXTENSION, PREFIX_MAGIC_FILE\n from ..base.context import context\n from ..common.compat import JSONDecodeError, itervalues, odict, string_types, with_metaclass\n@@ -257,6 +258,16 @@\n                 python_record = read_python_record(self.prefix_path, af, python_pkg_record.version)\n             except EnvironmentError:\n                 continue\n+            except ValidationError:\n+                import sys\n+                exc_type, exc_value, exc_traceback = sys.exc_info()\n+                import traceback\n+                tb = traceback.format_exception(exc_type, exc_value, exc_traceback)\n+                log.warn(\"Problem reading non-conda package record at %s. Please verify that you \"\n+                         \"still need this, and if so, that this is still installed correctly. \"\n+                         \"Reinstalling this package may help.\", af)\n+                log.debug(\"ValidationError: \\n%s\\n\", \"\\n\".join(tb))\n+                continue\n             if not python_record:\n                 continue\n             self.__prefix_records[python_record.name] = python_record\n", "issue": "non-conda-installed python packages can have missing version information\nWhen reading up metadata out of site-packages, non-conda-installed python packages can sometime be missing version information. When this is the case, we should default to a hard-codes `0.0.0` version. I believe this behavior is consistent with python\u2019s own `python setup.py \u2014version` when no version is actually given in the `setup()` function. \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 fnmatch import filter as fnmatch_filter\nfrom logging import getLogger\nfrom os import listdir\nfrom os.path import basename, isdir, isfile, join, lexists\n\nfrom ..base.constants import CONDA_TARBALL_EXTENSION, PREFIX_MAGIC_FILE\nfrom ..base.context import context\nfrom ..common.compat import JSONDecodeError, itervalues, odict, string_types, with_metaclass\nfrom ..common.constants import NULL\nfrom ..common.path import get_python_site_packages_short_path, win_path_ok\nfrom ..common.pkg_formats.python import get_site_packages_anchor_files\nfrom ..common.serialize import json_load\nfrom ..exceptions import (\n    BasicClobberError, CondaDependencyError, CorruptedEnvironmentError, maybe_raise,\n)\nfrom ..gateways.disk.create import write_as_json_to_file\nfrom ..gateways.disk.delete import rm_rf\nfrom ..gateways.disk.read import read_python_record\nfrom ..gateways.disk.test import file_path_is_writable\nfrom ..models.match_spec import MatchSpec\nfrom ..models.prefix_graph import PrefixGraph\nfrom ..models.records import PackageRecord, PrefixRecord\n\nlog = getLogger(__name__)\n\n\nclass PrefixDataType(type):\n    \"\"\"Basic caching of PrefixData instance objects.\"\"\"\n\n    def __call__(cls, prefix_path, pip_interop_enabled=None):\n        if prefix_path in PrefixData._cache_:\n            return PrefixData._cache_[prefix_path]\n        elif isinstance(prefix_path, PrefixData):\n            return prefix_path\n        else:\n            prefix_data_instance = super(PrefixDataType, cls).__call__(prefix_path,\n                                                                       pip_interop_enabled)\n            PrefixData._cache_[prefix_path] = prefix_data_instance\n            return prefix_data_instance\n\n\n@with_metaclass(PrefixDataType)\nclass PrefixData(object):\n    _cache_ = {}\n\n    def __init__(self, prefix_path, pip_interop_enabled=None):\n        # pip_interop_enabled is a temporary paramater; DO NOT USE\n        # TODO: when removing pip_interop_enabled, also remove from meta class\n        self.prefix_path = prefix_path\n        self.__prefix_records = None\n        self.__is_writable = NULL\n        self._pip_interop_enabled = (context.pip_interop_enabled\n                                     if pip_interop_enabled is None\n                                     else pip_interop_enabled)\n\n    def load(self):\n        self.__prefix_records = {}\n        _conda_meta_dir = join(self.prefix_path, 'conda-meta')\n        if lexists(_conda_meta_dir):\n            for meta_file in fnmatch_filter(listdir(_conda_meta_dir), '*.json'):\n                self._load_single_record(join(_conda_meta_dir, meta_file))\n        if self._pip_interop_enabled:\n            self._load_site_packages()\n\n    def reload(self):\n        self.load()\n        return self\n\n    def insert(self, prefix_record):\n        assert prefix_record.name not in self._prefix_records\n\n        assert prefix_record.fn.endswith(CONDA_TARBALL_EXTENSION)\n        filename = prefix_record.fn[:-len(CONDA_TARBALL_EXTENSION)] + '.json'\n\n        prefix_record_json_path = join(self.prefix_path, 'conda-meta', filename)\n        if lexists(prefix_record_json_path):\n            maybe_raise(BasicClobberError(\n                source_path=None,\n                target_path=prefix_record_json_path,\n                context=context,\n            ), context)\n            rm_rf(prefix_record_json_path)\n\n        write_as_json_to_file(prefix_record_json_path, prefix_record)\n\n        self._prefix_records[prefix_record.name] = prefix_record\n\n    def remove(self, package_name):\n        assert package_name in self._prefix_records\n\n        prefix_record = self._prefix_records[package_name]\n\n        filename = prefix_record.fn[:-len(CONDA_TARBALL_EXTENSION)] + '.json'\n        conda_meta_full_path = join(self.prefix_path, 'conda-meta', filename)\n        if self.is_writable:\n            rm_rf(conda_meta_full_path)\n\n        del self._prefix_records[package_name]\n\n    def get(self, package_name, default=NULL):\n        try:\n            return self._prefix_records[package_name]\n        except KeyError:\n            if default is not NULL:\n                return default\n            else:\n                raise\n\n    def iter_records(self):\n        return itervalues(self._prefix_records)\n\n    def iter_records_sorted(self):\n        prefix_graph = PrefixGraph(self.iter_records())\n        return iter(prefix_graph.graph)\n\n    def all_subdir_urls(self):\n        subdir_urls = set()\n        for prefix_record in itervalues(self._prefix_records):\n            subdir_url = prefix_record.channel.subdir_url\n            if subdir_url and subdir_url not in subdir_urls:\n                log.debug(\"adding subdir url %s for %s\", subdir_url, prefix_record)\n                subdir_urls.add(subdir_url)\n        return subdir_urls\n\n    def query(self, package_ref_or_match_spec):\n        # returns a generator\n        param = package_ref_or_match_spec\n        if isinstance(param, string_types):\n            param = MatchSpec(param)\n        if isinstance(param, MatchSpec):\n            return (prefix_rec for prefix_rec in self.iter_records()\n                    if param.match(prefix_rec))\n        else:\n            assert isinstance(param, PackageRecord)\n            return (prefix_rec for prefix_rec in self.iter_records() if prefix_rec == param)\n\n    @property\n    def _prefix_records(self):\n        return self.__prefix_records or self.load() or self.__prefix_records\n\n    def _load_single_record(self, prefix_record_json_path):\n        log.trace(\"loading prefix record %s\", prefix_record_json_path)\n        with open(prefix_record_json_path) as fh:\n            try:\n                json_data = json_load(fh.read())\n            except JSONDecodeError:\n                raise CorruptedEnvironmentError(self.prefix_path, prefix_record_json_path)\n\n            # TODO: consider, at least in memory, storing prefix_record_json_path as part\n            #       of PrefixRecord\n            prefix_record = PrefixRecord(**json_data)\n\n            # check that prefix record json filename conforms to name-version-build\n            # apparently implemented as part of #2638 to resolve #2599\n            try:\n                n, v, b = basename(prefix_record_json_path)[:-5].rsplit('-', 2)\n                if (n, v, b) != (prefix_record.name, prefix_record.version, prefix_record.build):\n                    raise ValueError()\n            except ValueError:\n                log.warn(\"Ignoring malformed prefix record at: %s\", prefix_record_json_path)\n                # TODO: consider just deleting here this record file in the future\n                return\n\n            self.__prefix_records[prefix_record.name] = prefix_record\n\n    @property\n    def is_writable(self):\n        if self.__is_writable == NULL:\n            test_path = join(self.prefix_path, PREFIX_MAGIC_FILE)\n            if not isfile(test_path):\n                is_writable = None\n            else:\n                is_writable = file_path_is_writable(test_path)\n            self.__is_writable = is_writable\n        return self.__is_writable\n\n    # # REMOVE: ?\n    def _has_python(self):\n        return 'python' in self._prefix_records\n\n    @property\n    def _python_pkg_record(self):\n        \"\"\"Return the prefix record for the package python.\"\"\"\n        return next(\n            (prefix_record for prefix_record in itervalues(self.__prefix_records)\n             if prefix_record.name == 'python'),\n            None\n        )\n\n    def _load_site_packages(self):\n        \"\"\"\n        Load non-conda-installed python packages in the site-packages of the prefix.\n\n        Python packages not handled by conda are installed via other means,\n        like using pip or using python setup.py develop for local development.\n\n        Packages found that are not handled by conda are converted into a\n        prefix record and handled in memory.\n\n        Packages clobbering conda packages (i.e. the conda-meta record) are\n        removed from the in memory representation.\n        \"\"\"\n        python_pkg_record = self._python_pkg_record\n\n        if not python_pkg_record:\n            return {}\n\n        site_packages_dir = get_python_site_packages_short_path(python_pkg_record.version)\n        site_packages_path = join(self.prefix_path, win_path_ok(site_packages_dir))\n\n        if not isdir(site_packages_path):\n            return {}\n\n        # Get anchor files for corresponding conda (handled) python packages\n        prefix_graph = PrefixGraph(self.iter_records())\n        python_records = prefix_graph.all_descendants(python_pkg_record)\n        conda_python_packages = get_conda_anchor_files_and_records(python_records)\n\n        # Get all anchor files and compare against conda anchor files to find clobbered conda\n        # packages and python packages installed via other means (not handled by conda)\n        sp_anchor_files = get_site_packages_anchor_files(site_packages_path, site_packages_dir)\n        conda_anchor_files = set(conda_python_packages)\n        clobbered_conda_anchor_files = conda_anchor_files - sp_anchor_files\n        non_conda_anchor_files = sp_anchor_files - conda_anchor_files\n\n        # If there's a mismatch for anchor files between what conda expects for a package\n        # based on conda-meta, and for what is actually in site-packages, then we'll delete\n        # the in-memory record for the conda package.  In the future, we should consider\n        # also deleting the record on disk in the conda-meta/ directory.\n        for conda_anchor_file in clobbered_conda_anchor_files:\n            prefix_rec = self._prefix_records.pop(conda_python_packages[conda_anchor_file].name)\n            try:\n                extracted_package_dir = basename(prefix_rec.extracted_package_dir)\n            except AttributeError:\n                extracted_package_dir = \"-\".join((\n                    prefix_rec.name, prefix_rec.version, prefix_rec.build\n                ))\n            prefix_rec_json_path = join(\n                self.prefix_path, \"conda-meta\", '%s.json' % extracted_package_dir\n            )\n            try:\n                rm_rf(prefix_rec_json_path)\n            except EnvironmentError:\n                log.debug(\"stale information, but couldn't remove: %s\", prefix_rec_json_path)\n            else:\n                log.debug(\"removed due to stale information: %s\", prefix_rec_json_path)\n\n        # Create prefix records for python packages not handled by conda\n        new_packages = {}\n        for af in non_conda_anchor_files:\n            try:\n                python_record = read_python_record(self.prefix_path, af, python_pkg_record.version)\n            except EnvironmentError:\n                continue\n            if not python_record:\n                continue\n            self.__prefix_records[python_record.name] = python_record\n            new_packages[python_record.name] = python_record\n\n        return new_packages\n\n\ndef get_conda_anchor_files_and_records(python_records):\n    \"\"\"Return the anchor files for the conda records of python packages.\"\"\"\n    anchor_file_endings = ('.egg-info/PKG-INFO', '.dist-info/RECORD', '.egg-info')\n    conda_python_packages = odict()\n\n    for prefix_record in python_records:\n        for fpath in prefix_record.files:\n            if fpath.endswith(anchor_file_endings) and 'site-packages' in fpath:\n                # Then 'fpath' is an anchor file\n                conda_python_packages[fpath] = prefix_record\n\n    return conda_python_packages\n\n\ndef get_python_version_for_prefix(prefix):\n    # returns a string e.g. \"2.7\", \"3.4\", \"3.5\" or None\n    py_record_iter = (rcrd for rcrd in PrefixData(prefix).iter_records() if rcrd.name == 'python')\n    record = next(py_record_iter, None)\n    if record is None:\n        return None\n    next_record = next(py_record_iter, None)\n    if next_record is not None:\n        raise CondaDependencyError(\"multiple python records found in prefix %s\" % prefix)\n    else:\n        return record.version[:3]\n\n\ndef delete_prefix_from_linked_data(path):\n    '''Here, path may be a complete prefix or a dist inside a prefix'''\n    linked_data_path = next((key for key in sorted(PrefixData._cache_, reverse=True)\n                             if path.startswith(key)),\n                            None)\n    if linked_data_path:\n        del PrefixData._cache_[linked_data_path]\n        return True\n    return False\n", "path": "conda/core/prefix_data.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 fnmatch import filter as fnmatch_filter\nfrom logging import getLogger\nfrom os import listdir\nfrom os.path import basename, isdir, isfile, join, lexists\n\nfrom .._vendor.auxlib.exceptions import ValidationError\nfrom ..base.constants import CONDA_TARBALL_EXTENSION, PREFIX_MAGIC_FILE\nfrom ..base.context import context\nfrom ..common.compat import JSONDecodeError, itervalues, odict, string_types, with_metaclass\nfrom ..common.constants import NULL\nfrom ..common.path import get_python_site_packages_short_path, win_path_ok\nfrom ..common.pkg_formats.python import get_site_packages_anchor_files\nfrom ..common.serialize import json_load\nfrom ..exceptions import (\n    BasicClobberError, CondaDependencyError, CorruptedEnvironmentError, maybe_raise,\n)\nfrom ..gateways.disk.create import write_as_json_to_file\nfrom ..gateways.disk.delete import rm_rf\nfrom ..gateways.disk.read import read_python_record\nfrom ..gateways.disk.test import file_path_is_writable\nfrom ..models.match_spec import MatchSpec\nfrom ..models.prefix_graph import PrefixGraph\nfrom ..models.records import PackageRecord, PrefixRecord\n\nlog = getLogger(__name__)\n\n\nclass PrefixDataType(type):\n    \"\"\"Basic caching of PrefixData instance objects.\"\"\"\n\n    def __call__(cls, prefix_path, pip_interop_enabled=None):\n        if prefix_path in PrefixData._cache_:\n            return PrefixData._cache_[prefix_path]\n        elif isinstance(prefix_path, PrefixData):\n            return prefix_path\n        else:\n            prefix_data_instance = super(PrefixDataType, cls).__call__(prefix_path,\n                                                                       pip_interop_enabled)\n            PrefixData._cache_[prefix_path] = prefix_data_instance\n            return prefix_data_instance\n\n\n@with_metaclass(PrefixDataType)\nclass PrefixData(object):\n    _cache_ = {}\n\n    def __init__(self, prefix_path, pip_interop_enabled=None):\n        # pip_interop_enabled is a temporary paramater; DO NOT USE\n        # TODO: when removing pip_interop_enabled, also remove from meta class\n        self.prefix_path = prefix_path\n        self.__prefix_records = None\n        self.__is_writable = NULL\n        self._pip_interop_enabled = (context.pip_interop_enabled\n                                     if pip_interop_enabled is None\n                                     else pip_interop_enabled)\n\n    def load(self):\n        self.__prefix_records = {}\n        _conda_meta_dir = join(self.prefix_path, 'conda-meta')\n        if lexists(_conda_meta_dir):\n            for meta_file in fnmatch_filter(listdir(_conda_meta_dir), '*.json'):\n                self._load_single_record(join(_conda_meta_dir, meta_file))\n        if self._pip_interop_enabled:\n            self._load_site_packages()\n\n    def reload(self):\n        self.load()\n        return self\n\n    def insert(self, prefix_record):\n        assert prefix_record.name not in self._prefix_records\n\n        assert prefix_record.fn.endswith(CONDA_TARBALL_EXTENSION)\n        filename = prefix_record.fn[:-len(CONDA_TARBALL_EXTENSION)] + '.json'\n\n        prefix_record_json_path = join(self.prefix_path, 'conda-meta', filename)\n        if lexists(prefix_record_json_path):\n            maybe_raise(BasicClobberError(\n                source_path=None,\n                target_path=prefix_record_json_path,\n                context=context,\n            ), context)\n            rm_rf(prefix_record_json_path)\n\n        write_as_json_to_file(prefix_record_json_path, prefix_record)\n\n        self._prefix_records[prefix_record.name] = prefix_record\n\n    def remove(self, package_name):\n        assert package_name in self._prefix_records\n\n        prefix_record = self._prefix_records[package_name]\n\n        filename = prefix_record.fn[:-len(CONDA_TARBALL_EXTENSION)] + '.json'\n        conda_meta_full_path = join(self.prefix_path, 'conda-meta', filename)\n        if self.is_writable:\n            rm_rf(conda_meta_full_path)\n\n        del self._prefix_records[package_name]\n\n    def get(self, package_name, default=NULL):\n        try:\n            return self._prefix_records[package_name]\n        except KeyError:\n            if default is not NULL:\n                return default\n            else:\n                raise\n\n    def iter_records(self):\n        return itervalues(self._prefix_records)\n\n    def iter_records_sorted(self):\n        prefix_graph = PrefixGraph(self.iter_records())\n        return iter(prefix_graph.graph)\n\n    def all_subdir_urls(self):\n        subdir_urls = set()\n        for prefix_record in itervalues(self._prefix_records):\n            subdir_url = prefix_record.channel.subdir_url\n            if subdir_url and subdir_url not in subdir_urls:\n                log.debug(\"adding subdir url %s for %s\", subdir_url, prefix_record)\n                subdir_urls.add(subdir_url)\n        return subdir_urls\n\n    def query(self, package_ref_or_match_spec):\n        # returns a generator\n        param = package_ref_or_match_spec\n        if isinstance(param, string_types):\n            param = MatchSpec(param)\n        if isinstance(param, MatchSpec):\n            return (prefix_rec for prefix_rec in self.iter_records()\n                    if param.match(prefix_rec))\n        else:\n            assert isinstance(param, PackageRecord)\n            return (prefix_rec for prefix_rec in self.iter_records() if prefix_rec == param)\n\n    @property\n    def _prefix_records(self):\n        return self.__prefix_records or self.load() or self.__prefix_records\n\n    def _load_single_record(self, prefix_record_json_path):\n        log.trace(\"loading prefix record %s\", prefix_record_json_path)\n        with open(prefix_record_json_path) as fh:\n            try:\n                json_data = json_load(fh.read())\n            except JSONDecodeError:\n                raise CorruptedEnvironmentError(self.prefix_path, prefix_record_json_path)\n\n            # TODO: consider, at least in memory, storing prefix_record_json_path as part\n            #       of PrefixRecord\n            prefix_record = PrefixRecord(**json_data)\n\n            # check that prefix record json filename conforms to name-version-build\n            # apparently implemented as part of #2638 to resolve #2599\n            try:\n                n, v, b = basename(prefix_record_json_path)[:-5].rsplit('-', 2)\n                if (n, v, b) != (prefix_record.name, prefix_record.version, prefix_record.build):\n                    raise ValueError()\n            except ValueError:\n                log.warn(\"Ignoring malformed prefix record at: %s\", prefix_record_json_path)\n                # TODO: consider just deleting here this record file in the future\n                return\n\n            self.__prefix_records[prefix_record.name] = prefix_record\n\n    @property\n    def is_writable(self):\n        if self.__is_writable == NULL:\n            test_path = join(self.prefix_path, PREFIX_MAGIC_FILE)\n            if not isfile(test_path):\n                is_writable = None\n            else:\n                is_writable = file_path_is_writable(test_path)\n            self.__is_writable = is_writable\n        return self.__is_writable\n\n    # # REMOVE: ?\n    def _has_python(self):\n        return 'python' in self._prefix_records\n\n    @property\n    def _python_pkg_record(self):\n        \"\"\"Return the prefix record for the package python.\"\"\"\n        return next(\n            (prefix_record for prefix_record in itervalues(self.__prefix_records)\n             if prefix_record.name == 'python'),\n            None\n        )\n\n    def _load_site_packages(self):\n        \"\"\"\n        Load non-conda-installed python packages in the site-packages of the prefix.\n\n        Python packages not handled by conda are installed via other means,\n        like using pip or using python setup.py develop for local development.\n\n        Packages found that are not handled by conda are converted into a\n        prefix record and handled in memory.\n\n        Packages clobbering conda packages (i.e. the conda-meta record) are\n        removed from the in memory representation.\n        \"\"\"\n        python_pkg_record = self._python_pkg_record\n\n        if not python_pkg_record:\n            return {}\n\n        site_packages_dir = get_python_site_packages_short_path(python_pkg_record.version)\n        site_packages_path = join(self.prefix_path, win_path_ok(site_packages_dir))\n\n        if not isdir(site_packages_path):\n            return {}\n\n        # Get anchor files for corresponding conda (handled) python packages\n        prefix_graph = PrefixGraph(self.iter_records())\n        python_records = prefix_graph.all_descendants(python_pkg_record)\n        conda_python_packages = get_conda_anchor_files_and_records(python_records)\n\n        # Get all anchor files and compare against conda anchor files to find clobbered conda\n        # packages and python packages installed via other means (not handled by conda)\n        sp_anchor_files = get_site_packages_anchor_files(site_packages_path, site_packages_dir)\n        conda_anchor_files = set(conda_python_packages)\n        clobbered_conda_anchor_files = conda_anchor_files - sp_anchor_files\n        non_conda_anchor_files = sp_anchor_files - conda_anchor_files\n\n        # If there's a mismatch for anchor files between what conda expects for a package\n        # based on conda-meta, and for what is actually in site-packages, then we'll delete\n        # the in-memory record for the conda package.  In the future, we should consider\n        # also deleting the record on disk in the conda-meta/ directory.\n        for conda_anchor_file in clobbered_conda_anchor_files:\n            prefix_rec = self._prefix_records.pop(conda_python_packages[conda_anchor_file].name)\n            try:\n                extracted_package_dir = basename(prefix_rec.extracted_package_dir)\n            except AttributeError:\n                extracted_package_dir = \"-\".join((\n                    prefix_rec.name, prefix_rec.version, prefix_rec.build\n                ))\n            prefix_rec_json_path = join(\n                self.prefix_path, \"conda-meta\", '%s.json' % extracted_package_dir\n            )\n            try:\n                rm_rf(prefix_rec_json_path)\n            except EnvironmentError:\n                log.debug(\"stale information, but couldn't remove: %s\", prefix_rec_json_path)\n            else:\n                log.debug(\"removed due to stale information: %s\", prefix_rec_json_path)\n\n        # Create prefix records for python packages not handled by conda\n        new_packages = {}\n        for af in non_conda_anchor_files:\n            try:\n                python_record = read_python_record(self.prefix_path, af, python_pkg_record.version)\n            except EnvironmentError:\n                continue\n            except ValidationError:\n                import sys\n                exc_type, exc_value, exc_traceback = sys.exc_info()\n                import traceback\n                tb = traceback.format_exception(exc_type, exc_value, exc_traceback)\n                log.warn(\"Problem reading non-conda package record at %s. Please verify that you \"\n                         \"still need this, and if so, that this is still installed correctly. \"\n                         \"Reinstalling this package may help.\", af)\n                log.debug(\"ValidationError: \\n%s\\n\", \"\\n\".join(tb))\n                continue\n            if not python_record:\n                continue\n            self.__prefix_records[python_record.name] = python_record\n            new_packages[python_record.name] = python_record\n\n        return new_packages\n\n\ndef get_conda_anchor_files_and_records(python_records):\n    \"\"\"Return the anchor files for the conda records of python packages.\"\"\"\n    anchor_file_endings = ('.egg-info/PKG-INFO', '.dist-info/RECORD', '.egg-info')\n    conda_python_packages = odict()\n\n    for prefix_record in python_records:\n        for fpath in prefix_record.files:\n            if fpath.endswith(anchor_file_endings) and 'site-packages' in fpath:\n                # Then 'fpath' is an anchor file\n                conda_python_packages[fpath] = prefix_record\n\n    return conda_python_packages\n\n\ndef get_python_version_for_prefix(prefix):\n    # returns a string e.g. \"2.7\", \"3.4\", \"3.5\" or None\n    py_record_iter = (rcrd for rcrd in PrefixData(prefix).iter_records() if rcrd.name == 'python')\n    record = next(py_record_iter, None)\n    if record is None:\n        return None\n    next_record = next(py_record_iter, None)\n    if next_record is not None:\n        raise CondaDependencyError(\"multiple python records found in prefix %s\" % prefix)\n    else:\n        return record.version[:3]\n\n\ndef delete_prefix_from_linked_data(path):\n    '''Here, path may be a complete prefix or a dist inside a prefix'''\n    linked_data_path = next((key for key in sorted(PrefixData._cache_, reverse=True)\n                             if path.startswith(key)),\n                            None)\n    if linked_data_path:\n        del PrefixData._cache_[linked_data_path]\n        return True\n    return False\n", "path": "conda/core/prefix_data.py"}]}