public static IPythonProjectLaunchProperties Create(
            IPythonProject project,
            IServiceProvider site,
            IProjectLaunchProperties properties
        ) {
            var res = properties as IPythonProjectLaunchProperties;
            if (res != null) {
                return res;
            }
            
            res = project as IPythonProjectLaunchProperties;
            if (res != null) {
                // This should be the common case, as we implement
                // IPythonProjectLaunchProperties on our project.
                return res;
            }

            // Backwards compatibility shim to handle project implementations
            // that omit IPythonProjectLaunchProperties.

            string arguments, workingDir;
            Dictionary<string, string> environment, environmentWithPaths;
            properties = properties ?? (project as IProjectLaunchProperties);
            if (properties != null) {
                arguments = properties.GetArguments();
                workingDir = properties.GetWorkingDirectory();
                environment = new Dictionary<string, string>(properties.GetEnvironment(false));
                environmentWithPaths = new Dictionary<string, string>(properties.GetEnvironment(true));
            } else {
                arguments = project.GetProperty(CommonConstants.CommandLineArguments);
                workingDir = project.GetWorkingDirectory();

                environment = ParseEnvironment(project.GetProperty(PythonConstants.EnvironmentSetting));
                environmentWithPaths = new Dictionary<string, string>(environment);
                AddSearchPaths(environmentWithPaths, project, site);
            }

            string strValue;
            bool boolValue;
            bool? isWindowsApplication = null;
            strValue = project.GetProperty(PythonConstants.IsWindowsApplicationSetting);
            if (bool.TryParse(strValue, out boolValue)) {
                isWindowsApplication = boolValue;
            }

            IPythonInterpreterFactory interpreter;

            var ipp3 = project as IPythonProject3;
            if (ipp3 != null) {
                interpreter = ipp3.GetInterpreterFactoryOrThrow();
            } else {
                interpreter = project.GetInterpreterFactory();
                var service = site.GetComponentModel().GetService<IInterpreterOptionsService>();
                if (service == null || interpreter == service.NoInterpretersValue) {
                    throw new NoInterpretersException();
                }
            }

            var interpreterPath = (isWindowsApplication ?? false) ?
                interpreter.Configuration.WindowsInterpreterPath :
                interpreter.Configuration.InterpreterPath;

            var interpreterArguments = project.GetProperty(PythonConstants.InterpreterArgumentsSetting);

            bool? isNativeDebugging = null;
            strValue = project.GetProperty(PythonConstants.EnableNativeCodeDebugging);
            if (bool.TryParse(strValue, out boolValue)) {
                isNativeDebugging = boolValue;
            }

            return new PythonProjectLaunchProperties(
                arguments,
                workingDir,
                environment,
                environmentWithPaths,
                interpreterPath,
                interpreterArguments,
                isWindowsApplication,
                isNativeDebugging
            );
        }
        public static IPythonProjectLaunchProperties Create(
            IPythonProject project,
            IServiceProvider site,
            IProjectLaunchProperties properties
            )
        {
            var res = properties as IPythonProjectLaunchProperties;

            if (res != null)
            {
                return(res);
            }

            res = project as IPythonProjectLaunchProperties;
            if (res != null)
            {
                // This should be the common case, as we implement
                // IPythonProjectLaunchProperties on our project.
                return(res);
            }

            // Backwards compatibility shim to handle project implementations
            // that omit IPythonProjectLaunchProperties.

            string arguments, workingDir;
            Dictionary <string, string> environment, environmentWithPaths;

            properties = properties ?? (project as IProjectLaunchProperties);
            if (properties != null)
            {
                arguments            = properties.GetArguments();
                workingDir           = properties.GetWorkingDirectory();
                environment          = new Dictionary <string, string>(properties.GetEnvironment(false));
                environmentWithPaths = new Dictionary <string, string>(properties.GetEnvironment(true));
            }
            else
            {
                arguments  = project.GetProperty(CommonConstants.CommandLineArguments);
                workingDir = project.GetWorkingDirectory();

                environment          = ParseEnvironment(project.GetProperty(PythonConstants.EnvironmentSetting));
                environmentWithPaths = new Dictionary <string, string>(environment);
                AddSearchPaths(environmentWithPaths, project, site);
            }

            string strValue;
            bool   boolValue;
            bool?  isWindowsApplication = null;

            strValue = project.GetProperty(PythonConstants.IsWindowsApplicationSetting);
            if (bool.TryParse(strValue, out boolValue))
            {
                isWindowsApplication = boolValue;
            }

            IPythonInterpreterFactory interpreter;

            var ipp3 = project as IPythonProject3;

            if (ipp3 != null)
            {
                interpreter = ipp3.GetInterpreterFactoryOrThrow();
            }
            else
            {
                interpreter = project.GetInterpreterFactory();
                var service = site.GetComponentModel().GetService <IInterpreterOptionsService>();
                if (service == null || interpreter == service.NoInterpretersValue)
                {
                    throw new NoInterpretersException();
                }
            }

            var interpreterPath = (isWindowsApplication ?? false) ?
                                  interpreter.Configuration.WindowsInterpreterPath :
                                  interpreter.Configuration.InterpreterPath;

            var interpreterArguments = project.GetProperty(PythonConstants.InterpreterArgumentsSetting);

            bool?isNativeDebugging = null;

            strValue = project.GetProperty(PythonConstants.EnableNativeCodeDebugging);
            if (bool.TryParse(strValue, out boolValue))
            {
                isNativeDebugging = boolValue;
            }

            return(new PythonProjectLaunchProperties(
                       arguments,
                       workingDir,
                       environment,
                       environmentWithPaths,
                       interpreterPath,
                       interpreterArguments,
                       isWindowsApplication,
                       isNativeDebugging
                       ));
        }
        /// <summary>
        /// Merges two sets of properties.
        /// </summary>
        public static IProjectLaunchProperties Merge(
            IProjectLaunchProperties highPriority,
            IProjectLaunchProperties lowPriority,
            params string[] joinEnviromentValues
        ) {
            var args = highPriority.GetArguments();
            if (string.IsNullOrEmpty(args)) {
                args = lowPriority.GetArguments();
            }

            var workingDir = highPriority.GetWorkingDirectory();
            if (string.IsNullOrEmpty(workingDir)) {
                workingDir = lowPriority.GetWorkingDirectory();
            }

            var joinEnv = new HashSet<string>(joinEnviromentValues, StringComparer.OrdinalIgnoreCase);
            joinEnv.Add("PATH");

            var origHighEnv = highPriority.GetEnvironment(false);
            var origLowEnv = lowPriority.GetEnvironment(false);
            var env = new Dictionary<string, string>(origLowEnv);
            if (origHighEnv != null && origHighEnv.Any()) {
                foreach (var kv in origHighEnv) {
                    string existing;
                    if (joinEnv.Contains(kv.Key) && env.TryGetValue(kv.Key, out existing)) {
                        env[kv.Key] = kv.Value + Path.PathSeparator.ToString() + existing;
                    } else {
                        env[kv.Key] = kv.Value;
                    }
                }
            }

            var origHighSearchEnv = highPriority.GetEnvironment(true);
            var origLowSearchEnv = lowPriority.GetEnvironment(true);
            var envWithPath = new Dictionary<string, string>(env);
            if (origLowSearchEnv != null && origLowSearchEnv.Any()) {
                string existing;
                var newKeys = origLowSearchEnv.Keys.AsEnumerable();
                if (origLowEnv != null) {
                    newKeys = newKeys.Except(origLowEnv.Keys);
                }

                foreach (var key in newKeys) {
                    if (envWithPath.TryGetValue(key, out existing) && !string.IsNullOrEmpty(existing)) {
                        envWithPath[key] = origLowSearchEnv[key] + Path.PathSeparator.ToString() + existing;
                    } else {
                        envWithPath[key] = origLowSearchEnv[key];
                    }
                }

                newKeys = origHighSearchEnv.Keys.AsEnumerable();
                if (origHighEnv != null) {
                    newKeys = newKeys.Except(origHighEnv.Keys);
                }

                foreach (var key in newKeys) {
                    if (envWithPath.TryGetValue(key, out existing) && !string.IsNullOrEmpty(existing)) {
                        envWithPath[key] = origHighSearchEnv[key] + Path.PathSeparator.ToString() + existing;
                    } else {
                        envWithPath[key] = origHighSearchEnv[key];
                    }
                }
            }

            var highPython = highPriority as IPythonProjectLaunchProperties;
            var lowPython = lowPriority as IPythonProjectLaunchProperties;

            string interpreterPath = null, interpreterArgs = null;
            bool? isWindowsApp = null, isNativeDebug = null;

            if (highPython != null) {
                interpreterPath = highPython.GetInterpreterPath();
                interpreterArgs = highPython.GetInterpreterArguments();
                isWindowsApp = highPython.GetIsWindowsApplication();
                isNativeDebug = highPython.GetIsNativeDebuggingEnabled();
            }
            if (lowPython != null) {
                if (string.IsNullOrEmpty(interpreterPath)) {
                    interpreterPath = lowPython.GetInterpreterPath();
                }
                if (string.IsNullOrEmpty(interpreterArgs)) {
                    interpreterArgs = lowPython.GetInterpreterArguments();
                }
                if (isWindowsApp == null) {
                    isWindowsApp = lowPython.GetIsWindowsApplication();
                }
                if (isNativeDebug == null) {
                    isNativeDebug = lowPython.GetIsNativeDebuggingEnabled();
                }
            }

            return new PythonProjectLaunchProperties(
                args,
                workingDir,
                env,
                envWithPath,
                interpreterPath,
                interpreterArgs,
                isWindowsApp,
                isNativeDebug
            );
        }
        /// <summary>
        /// Merges two sets of properties.
        /// </summary>
        public static IProjectLaunchProperties Merge(
            IProjectLaunchProperties highPriority,
            IProjectLaunchProperties lowPriority,
            params string[] joinEnviromentValues
            )
        {
            var args = highPriority.GetArguments();

            if (string.IsNullOrEmpty(args))
            {
                args = lowPriority.GetArguments();
            }

            var workingDir = highPriority.GetWorkingDirectory();

            if (string.IsNullOrEmpty(workingDir))
            {
                workingDir = lowPriority.GetWorkingDirectory();
            }

            var joinEnv = new HashSet <string>(joinEnviromentValues, StringComparer.OrdinalIgnoreCase);

            joinEnv.Add("PATH");

            var origHighEnv = highPriority.GetEnvironment(false);
            var origLowEnv  = lowPriority.GetEnvironment(false);
            var env         = new Dictionary <string, string>(origLowEnv);

            if (origHighEnv != null && origHighEnv.Any())
            {
                foreach (var kv in origHighEnv)
                {
                    string existing;
                    if (joinEnv.Contains(kv.Key) && env.TryGetValue(kv.Key, out existing))
                    {
                        env[kv.Key] = kv.Value + Path.PathSeparator.ToString() + existing;
                    }
                    else
                    {
                        env[kv.Key] = env[kv.Value];
                    }
                }
            }

            var origHighSearchEnv = highPriority.GetEnvironment(true);
            var origLowSearchEnv  = lowPriority.GetEnvironment(true);
            var envWithPath       = new Dictionary <string, string>(env);

            if (origLowSearchEnv != null && origLowSearchEnv.Any())
            {
                string existing;
                var    newKeys = origLowSearchEnv.Keys.AsEnumerable();
                if (origLowEnv != null)
                {
                    newKeys = newKeys.Except(origLowEnv.Keys);
                }

                foreach (var key in newKeys)
                {
                    if (envWithPath.TryGetValue(key, out existing) && !string.IsNullOrEmpty(existing))
                    {
                        envWithPath[key] = origLowSearchEnv[key] + Path.PathSeparator.ToString() + existing;
                    }
                    else
                    {
                        envWithPath[key] = origLowSearchEnv[key];
                    }
                }

                newKeys = origHighSearchEnv.Keys.AsEnumerable();
                if (origHighEnv != null)
                {
                    newKeys = newKeys.Except(origHighEnv.Keys);
                }

                foreach (var key in newKeys)
                {
                    if (envWithPath.TryGetValue(key, out existing) && !string.IsNullOrEmpty(existing))
                    {
                        envWithPath[key] = origHighSearchEnv[key] + Path.PathSeparator.ToString() + existing;
                    }
                    else
                    {
                        envWithPath[key] = origHighSearchEnv[key];
                    }
                }
            }

            var highPython = highPriority as IPythonProjectLaunchProperties;
            var lowPython  = lowPriority as IPythonProjectLaunchProperties;

            string interpreterPath = null, interpreterArgs = null;
            bool?  isWindowsApp = null, isNativeDebug = null;

            if (highPython != null)
            {
                interpreterPath = highPython.GetInterpreterPath();
                interpreterArgs = highPython.GetInterpreterArguments();
                isWindowsApp    = highPython.GetIsWindowsApplication();
                isNativeDebug   = highPython.GetIsNativeDebuggingEnabled();
            }
            if (lowPython != null)
            {
                if (string.IsNullOrEmpty(interpreterPath))
                {
                    interpreterPath = lowPython.GetInterpreterPath();
                }
                if (string.IsNullOrEmpty(interpreterArgs))
                {
                    interpreterArgs = lowPython.GetInterpreterArguments();
                }
                if (isWindowsApp == null)
                {
                    isWindowsApp = lowPython.GetIsWindowsApplication();
                }
                if (isNativeDebug == null)
                {
                    isNativeDebug = lowPython.GetIsNativeDebuggingEnabled();
                }
            }

            return(new PythonProjectLaunchProperties(
                       args,
                       workingDir,
                       env,
                       envWithPath,
                       interpreterPath,
                       interpreterArgs,
                       isWindowsApp,
                       isNativeDebug
                       ));
        }