| /* |
| * Copyright 2000-2013 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.jetbrains.python.psi.impl; |
| |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleUtilCore; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.projectRoots.Sdk; |
| import com.intellij.openapi.projectRoots.SdkTypeId; |
| import com.intellij.openapi.roots.JdkOrderEntry; |
| import com.intellij.openapi.roots.OrderEntry; |
| import com.intellij.openapi.roots.ProjectRootManager; |
| import com.intellij.openapi.roots.impl.ModuleLibraryOrderEntryImpl; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.openapi.vfs.LocalFileSystem; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.*; |
| import com.jetbrains.python.PyNames; |
| import com.jetbrains.python.psi.*; |
| import com.jetbrains.python.psi.resolve.PythonSdkPathCache; |
| import com.jetbrains.python.psi.types.*; |
| import com.jetbrains.python.sdk.PythonSdkType; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Provides access to Python builtins via skeletons. |
| */ |
| public class PyBuiltinCache { |
| public static final String BUILTIN_FILE = "__builtin__.py"; |
| public static final String BUILTIN_FILE_3K = "builtins.py"; |
| public static final String EXCEPTIONS_FILE = "exceptions.py"; |
| |
| private static final PyBuiltinCache DUD_INSTANCE = new PyBuiltinCache(null, null); |
| |
| /** |
| * Stores the most often used types, returned by getNNNType(). |
| */ |
| @NotNull private final Map<String, PyClassTypeImpl> myTypeCache = new HashMap<String, PyClassTypeImpl>(); |
| |
| @Nullable private PyFile myBuiltinsFile; |
| @Nullable private PyFile myExceptionsFile; |
| private long myModStamp = -1; |
| |
| public PyBuiltinCache() { |
| } |
| |
| public PyBuiltinCache(@Nullable final PyFile builtins, @Nullable PyFile exceptions) { |
| myBuiltinsFile = builtins; |
| myExceptionsFile = exceptions; |
| } |
| |
| /** |
| * Returns an instance of builtin cache. Instances differ per module and are cached. |
| * @param reference something to define the module from. |
| * @return an instance of cache. If reference was null, the instance is a fail-fast dud one. |
| */ |
| @NotNull |
| public static PyBuiltinCache getInstance(@Nullable PsiElement reference) { |
| if (reference != null && reference.isValid()) { |
| Sdk sdk = findSdkForFile(reference.getContainingFile()); |
| if (sdk != null) { |
| return PythonSdkPathCache.getInstance(reference.getProject(), sdk).getBuiltins(); |
| } |
| } |
| return DUD_INSTANCE; // a non-functional fail-fast instance, for a case when skeletons are not available |
| } |
| |
| @Nullable |
| public static Sdk findSdkForFile(PsiFileSystemItem psifile) { |
| if (psifile == null) { |
| return null; |
| } |
| Module module = ModuleUtilCore.findModuleForPsiElement(psifile); |
| if (module != null) { |
| return PythonSdkType.findPythonSdk(module); |
| } |
| return findSdkForNonModuleFile(psifile); |
| } |
| |
| @Nullable |
| public static Sdk findSdkForNonModuleFile(PsiFileSystemItem psiFile) { |
| Project project = psiFile.getProject(); |
| Sdk sdk = null; |
| final VirtualFile vfile = psiFile instanceof PsiFile ? ((PsiFile) psiFile).getOriginalFile().getVirtualFile() : psiFile.getVirtualFile(); |
| if (vfile != null) { // reality |
| final ProjectRootManager projectRootManager = ProjectRootManager.getInstance(project); |
| sdk = projectRootManager.getProjectSdk(); |
| if (sdk == null) { |
| final List<OrderEntry> orderEntries = projectRootManager.getFileIndex().getOrderEntriesForFile(vfile); |
| for (OrderEntry orderEntry : orderEntries) { |
| if (orderEntry instanceof JdkOrderEntry) { |
| sdk = ((JdkOrderEntry)orderEntry).getJdk(); |
| } |
| else if (orderEntry instanceof ModuleLibraryOrderEntryImpl) { |
| sdk = PythonSdkType.findPythonSdk(orderEntry.getOwnerModule()); |
| } |
| } |
| } |
| } |
| return sdk; |
| } |
| |
| @Nullable |
| public static PyFile getBuiltinsForSdk(@NotNull Project project, @NotNull Sdk sdk) { |
| return getSkeletonFile(project, sdk, PythonSdkType.getBuiltinsFileName(sdk)); |
| } |
| |
| @Nullable |
| public static PyFile getSkeletonFile(final @NotNull Project project, @NotNull Sdk sdk, @NotNull String name) { |
| SdkTypeId sdkType = sdk.getSdkType(); |
| if (sdkType instanceof PythonSdkType) { |
| // dig out the builtins file, create an instance based on it |
| final String[] urls = sdk.getRootProvider().getUrls(PythonSdkType.BUILTIN_ROOT_TYPE); |
| for (String url : urls) { |
| if (url.contains(PythonSdkType.SKELETON_DIR_NAME)) { |
| final String builtins_url = url + "/" + name; |
| File builtins = new File(VfsUtilCore.urlToPath(builtins_url)); |
| if (builtins.isFile() && builtins.canRead()) { |
| final VirtualFile builtins_vfile = LocalFileSystem.getInstance().findFileByIoFile(builtins); |
| if (builtins_vfile != null) { |
| final Ref<PyFile> result = Ref.create(); |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| @Override |
| public void run() { |
| PsiFile file = PsiManager.getInstance(project).findFile(builtins_vfile); |
| if (file instanceof PyFile) { |
| result.set((PyFile)file); |
| } |
| } |
| }); |
| return result.get(); |
| |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Nullable |
| static PyType createLiteralCollectionType(final PySequenceExpression sequence, final String name) { |
| final PyBuiltinCache builtinCache = getInstance(sequence); |
| final PyClass setClass = builtinCache.getClass(name); |
| if (setClass != null) { |
| return new PyLiteralCollectionType(setClass, false, sequence); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public PyFile getBuiltinsFile() { |
| return myBuiltinsFile; |
| } |
| |
| public boolean isValid() { |
| return myBuiltinsFile != null && myBuiltinsFile.isValid(); |
| } |
| |
| /** |
| * Looks for a top-level named item. (Package builtins does not contain any sensible nested names anyway.) |
| * @param name to look for |
| * @return found element, or null. |
| */ |
| @Nullable |
| public PsiElement getByName(@NonNls String name) { |
| if (myBuiltinsFile != null) { |
| final PsiElement element = myBuiltinsFile.getElementNamed(name); |
| if (element != null) { |
| return element; |
| } |
| } |
| if (myExceptionsFile != null) { |
| return myExceptionsFile.getElementNamed(name); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public PyClass getClass(@NonNls String name) { |
| if (myBuiltinsFile != null) { |
| return myBuiltinsFile.findTopLevelClass(name); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public PyClassTypeImpl getObjectType(@NonNls String name) { |
| PyClassTypeImpl val; |
| synchronized (myTypeCache) { |
| if (myBuiltinsFile != null) { |
| if (myBuiltinsFile.getModificationStamp() != myModStamp) { |
| myTypeCache.clear(); |
| myModStamp = myBuiltinsFile.getModificationStamp(); |
| } |
| } |
| val = myTypeCache.get(name); |
| } |
| if (val == null) { |
| PyClass cls = getClass(name); |
| if (cls != null) { // null may happen during testing |
| val = new PyClassTypeImpl(cls, false); |
| val.assertValid(name); |
| synchronized (myTypeCache) { |
| myTypeCache.put(name, val); |
| } |
| } |
| } |
| else { |
| val.assertValid(name); |
| } |
| return val; |
| } |
| |
| @Nullable |
| public PyClassType getObjectType() { |
| return getObjectType("object"); |
| } |
| |
| @Nullable |
| public PyClassType getListType() { |
| return getObjectType("list"); |
| } |
| |
| @Nullable |
| public PyClassType getDictType() { |
| return getObjectType("dict"); |
| } |
| |
| @Nullable |
| public PyClassType getSetType() { |
| return getObjectType("set"); |
| } |
| |
| @Nullable |
| public PyClassType getTupleType() { |
| return getObjectType("tuple"); |
| } |
| |
| @Nullable |
| public PyClassType getIntType() { |
| return getObjectType("int"); |
| } |
| |
| @Nullable |
| public PyClassType getFloatType() { |
| return getObjectType("float"); |
| } |
| |
| @Nullable |
| public PyClassType getComplexType() { |
| return getObjectType("complex"); |
| } |
| |
| @Nullable |
| public PyClassType getStrType() { |
| return getObjectType("str"); |
| } |
| |
| @Nullable |
| public PyClassType getBytesType(LanguageLevel level) { |
| if (level.isPy3K()) { |
| return getObjectType("bytes"); |
| } |
| else { |
| return getObjectType("str"); |
| } |
| } |
| |
| @Nullable |
| public PyClassType getUnicodeType(LanguageLevel level) { |
| if (level.isPy3K()) { |
| return getObjectType("str"); |
| } |
| else { |
| return getObjectType("unicode"); |
| } |
| } |
| |
| @Nullable |
| public PyType getStringType(LanguageLevel level) { |
| if (level.isPy3K()) { |
| return getObjectType("str"); |
| } |
| else { |
| return getStrOrUnicodeType(); |
| } |
| } |
| |
| @Nullable |
| public PyType getByteStringType(@NotNull LanguageLevel level) { |
| if (level.isPy3K()) { |
| return getObjectType("bytes"); |
| } |
| else { |
| return getStrOrUnicodeType(); |
| } |
| } |
| |
| private PyType getStrOrUnicodeType() { |
| return PyUnionType.union(getObjectType("str"), getObjectType("unicode")); |
| } |
| |
| @Nullable |
| public PyClassType getBoolType() { |
| return getObjectType("bool"); |
| } |
| |
| @Nullable |
| public PyClassType getOldstyleClassobjType() { |
| return getObjectType(PyNames.FAKE_OLD_BASE); |
| } |
| |
| @Nullable |
| public PyClassType getClassMethodType() { |
| return getObjectType("classmethod"); |
| } |
| |
| @Nullable |
| public PyClassType getStaticMethodType() { |
| return getObjectType("staticmethod"); |
| } |
| |
| /** |
| * @param target an element to check. |
| * @return true iff target is inside the __builtins__.py |
| */ |
| public boolean isBuiltin(@Nullable PsiElement target) { |
| if (target == null) return false; |
| if (! target.isValid()) return false; |
| final PsiFile the_file = target.getContainingFile(); |
| if (!(the_file instanceof PyFile)) { |
| return false; |
| } |
| // files are singletons, no need to compare URIs |
| return the_file == myBuiltinsFile || the_file == myExceptionsFile; |
| } |
| |
| public static boolean isInBuiltins(@NotNull PyExpression expression) { |
| if (expression instanceof PyQualifiedExpression && (((PyQualifiedExpression)expression).isQualified())) { |
| return false; |
| } |
| final String name = expression.getName(); |
| PsiReference reference = expression.getReference(); |
| if (reference != null && name != null) { |
| final PyBuiltinCache cache = getInstance(expression); |
| if (cache.getByName(name) != null) { |
| final PsiElement resolved = reference.resolve(); |
| if (resolved != null && cache.isBuiltin(resolved)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| } |