/// <summary>
        /// Gets the absolute path to site packages directory.
        /// </summary>
        /// <param name="runtimeInfo">The runtime information.</param>
        /// <returns>Absolute path to site packages directory.</returns>
        /// <exception cref="DirectoryNotFoundException">site-packages not found for runtime '{runtimeInfo.Runtime}'</exception>
        /// <remarks>
        /// On non-Windows platforms, site-packages is sorted into python version directories.
        /// </remarks>
        public string GetSitePackagesDir(LambdaRuntimeInfo runtimeInfo)
        {
            if (this.platform.OSPlatform == OSPlatform.Windows)
            {
                return(Path.Combine(this.LibDir, "site-packages"));
            }

            // Linux/MacOs have a site packages for each python version
            var path = Path.Combine(this.LibDir, runtimeInfo.Runtime, "site-packages");

            if (!Directory.Exists(path))
            {
                throw new DirectoryNotFoundException($"site-packages not found for runtime '{runtimeInfo.Runtime}'");
            }

            return(path);
        }
        public PythonVirtualEnv Load(LambdaRuntimeInfo runtimeInfo)
        {
            var sitePackages = this.GetSitePackagesDir(runtimeInfo);

            foreach (var dist in Directory.EnumerateDirectories(
                         sitePackages,
                         "*.dist-info",
                         SearchOption.TopDirectoryOnly))
            {
                // RECORD file tells us where to find the code and is a 3 field header-less CSV
                var recordFile = Path.Combine(dist, "RECORD");

                // Name comes from the dist-info directory name
                // Package names always have hyphens, not underscores
                var module = new PythonModule
                {
                    Name = PackageNameRegex.Match(Path.GetFileName(dist)).Groups["packageName"].Value
                           .Replace('_', '-')
                };

                if (File.Exists(recordFile))
                {
                    var paths = new HashSet <string>();

                    // Pull all items from RECORD file into a set to get a unique list of what to include, ignoring dist-info files, Windows .pyd files and scripts placed in bin
                    foreach (var path in File.ReadAllLines(recordFile)
                             .Select(line => line.Split(',').First().Split('/').First())
                             .Where(path => !(path.EndsWith(".dist-info") || path.EndsWith(".pyd")) && path != ".." && path != "__pycache__"))
                    {
                        paths.Add(Path.Combine(sitePackages, path));
                    }

                    // TODO: Warn if a binary is a Windows DLL. Not likely to be good in Lambda (linux)
                    if (paths.Count == 0)
                    {
                        throw new PackagerException(
                                  $"Found no content for package {module.Name}");
                    }

                    foreach (var p in paths)
                    {
                        module.Paths.Add(Path.Combine(sitePackages, p));
                    }
                }
                else
                {
                    throw new FileNotFoundException(
                              "Unable to determine package location - cannot find RECORD file",
                              recordFile);
                }

                // METADATA file tells us any dependencies with Requires-Dist records.
                var metadataFile = Path.Combine(dist, "METADATA");

                if (File.Exists(metadataFile))
                {
                    foreach (var dependency in File.ReadAllLines(metadataFile)
                             .Where(l => l.StartsWith("Requires-Dist:")))
                    {
                        var includeDependency = true;

                        if (dependency.Contains(";"))
                        {
                            var expression = dependency.Split(';').Last();
                            var r          = this.parser.Parse(expression);

                            if (r.IsError)
                            {
                                var errorMessage = string.Join(
                                    Environment.NewLine,
                                    r.Errors.Select(e => e.ErrorMessage));
                                throw new PackagerException(
                                          $"Error parsing: {expression}\nIf this expression is valid, please raise an issue.\n{errorMessage}");
                            }

                            // Set variables according to targeted Python version.
                            this.pep508Variables["python_version"]      = runtimeInfo.RuntimeVersion;
                            this.pep508Variables["python_full_version"] = runtimeInfo.RuntimeVersion;
                            var evaluation = r.Result.Evaluate(new ExpressionContext(this.pep508Variables));

                            includeDependency = (bool?)evaluation ?? throw new PackagerException(
                                                          $"Error evaluating: {expression}\nIf this expression is valid, please raise an issue.");
                        }

                        if (includeDependency)
                        {
                            module.Dependencies.Add(RequirementsRegex.Match(dependency).Groups["dependency"].Value);
                        }
                    }
                }

                this.modules.Add(module);
            }

            return(this);
        }