public async Task <IDocumentAnalysis> GetAnalysisAsync(IPythonModule module, int waitTime, CancellationToken cancellationToken)
        {
            var key = new AnalysisModuleKey(module);
            PythonAnalyzerEntry entry;

            lock (_syncObj) {
                if (!_analysisEntries.TryGetValue(key, out entry))
                {
                    var emptyAnalysis = new EmptyAnalysis(_services, (IDocument)module);
                    entry = new PythonAnalyzerEntry(emptyAnalysis);
                    _analysisEntries[key] = entry;
                }
            }

            if (waitTime < 0 || Debugger.IsAttached)
            {
                return(await GetAnalysisAsync(entry, default, cancellationToken));
        private void AnalyzeLoop(IDependencyChainLoopNode <PythonAnalyzerEntry> loopNode, Stopwatch stopWatch)
        {
            var version         = _walker.Version;
            var entries         = new Dictionary <AnalysisModuleKey, (IPythonModule Module, PythonAnalyzerEntry Entry)>();
            var variables       = new Dictionary <(AnalysisModuleKey Module, string Name), int>();
            var importNames     = new List <(AnalysisModuleKey From, int FromPosition, AnalysisModuleKey To, string ToName)>();
            var cachedVariables = new Dictionary <AnalysisModuleKey, IVariableCollection>();
            var asts            = new Dictionary <AnalysisModuleKey, PythonAst>();
            var startTime       = stopWatch.Elapsed;

            // Note: loop analysis is not cancellable. The reason is that when smaller loop
            // appears inside a larger loop gets canceled, it will not be re-walked during
            // the outer loop analysis. For example, functools <=> _functools loop and
            // the related CircularDependencyFunctools test.
            foreach (var entry in loopNode.Values)
            {
                ActivityTracker.OnEnqueueModule(entry.Module.FilePath);
                if (!CanUpdateAnalysis(entry, Version, out var module, out var ast))
                {
                    _log?.Log(TraceEventType.Verbose, $"Analysis of loop canceled.");
                    return;
                }

                var moduleKey = new AnalysisModuleKey(module);
                entries[moduleKey] = (module, entry);
                var analysis = _analyzer.TryRestoreCachedAnalysis(module);
                if (analysis != null)
                {
                    AddLoopImportsFromCachedAnalysis(importNames, variables, moduleKey, analysis);
                    cachedVariables.Add(moduleKey, analysis.GlobalScope.Variables);
                }
                else
                {
                    AddLoopImportsFromAst(importNames, variables, moduleKey, ast);
                    asts.Add(moduleKey, ast);
                }
            }

            if (asts.Count == 0)
            {
                // Fully cached loop
                if (_log != null && _log.LogLevel == TraceEventType.Verbose)
                {
                    var names = string.Join(", ", cachedVariables.Select(v => v.Key.Name));
                    _log?.Log(TraceEventType.Verbose, $"Fully cached modules cycle: {names}");
                }
                return;
            }

            var imports = new List <(AnalysisModuleKey From, int FromPosition, AnalysisModuleKey To, int ToPosition)>();

            foreach (var(fromModule, fromPosition, toModule, toName) in importNames)
            {
                if (!entries.ContainsKey(toModule))
                {
                    continue;
                }

                if (toName == null)
                {
                    imports.Add((fromModule, fromPosition, toModule, 0));
                }
                else if (variables.TryGetValue((toModule, toName), out var toPosition))
                {
                    imports.Add((fromModule, fromPosition, toModule, toPosition));
                }
            }

            var startingKeys = LocationLoopResolver <AnalysisModuleKey> .FindStartingItems(imports);

            var variableHandler = new LoopImportedVariableHandler(_services, asts, cachedVariables, () => false);

            foreach (var key in startingKeys)
            {
                if (asts.TryGetValue(key, out var startingAst) && entries.TryGetValue(key, out var me))
                {
                    variableHandler.WalkModule(me.Module, startingAst);
                }
            }

            foreach (var walker in variableHandler.Walkers)
            {
                asts.Remove(new AnalysisModuleKey(walker.Module));
            }

            while (asts.Count > 0)
            {
                var(moduleKey, ast) = asts.First();
                variableHandler.WalkModule(entries[moduleKey].Module, ast);

                foreach (var walker in variableHandler.Walkers)
                {
                    asts.Remove(new AnalysisModuleKey(walker.Module));
                }
            }

            foreach (var walker in variableHandler.Walkers)
            {
                var module    = (IDocument)walker.Module;
                var moduleKey = new AnalysisModuleKey(module);
                if (entries.TryGetValue(moduleKey, out var e))
                {
                    var analysis = CreateAnalysis(null, module, walker.Ast, version, walker);
                    CompleteAnalysis(e.Entry, module, version, analysis);
                }
            }

            loopNode.MarkWalked();
            LogCompleted(loopNode, entries.Values.Select(v => v.Module), stopWatch, startTime);
        }