PY-53599 Simpler env smoke test for Tensorflow module structure

Namely, test only against the latest version of the library. Check completion
only for a few common submodules of tensorflow. Check resolve for all of them
in one inspection pass, instead of resolving each separately. Generate expected
test data dynamically through module introspection.

GitOrigin-RevId: df5c4c4f765116bc7329454d5cc0b2aa1a6a86b5
This commit is contained in:
Mikhail Golubev
2023-07-20 19:36:11 +00:00
committed by intellij-monorepo-bot
parent e90b6b09b1
commit 9b4bd066fa
9 changed files with 287 additions and 299 deletions
+3 -13
View File
@@ -179,20 +179,10 @@ envs {
true)
// For TensorFlow
createPython("py37_tensorflow1_oldpaths",
createPython("py37_tensorflow",
python37version,
["tensorflow < 1.15.0rc0"],
"tensorflow1\ntensorflow_oldpaths\npython3.7",
true)
createPython("py37_tensorflow1_newpaths",
python37version,
["tensorflow >= 1.15.0rc0, < 2.0.0a0"],
"tensorflow1\ntensorflow_newpaths\npython3.7",
true)
createPython("py37_tensorflow2_newpaths",
python37version,
["tensorflow >= 2.0.0rc0"],
"tensorflow2\ntensorflow_newpaths\npython3.7",
["tensorflow"],
"tensorflow\npython3.7",
true)
if (isUnix) {
@@ -0,0 +1,113 @@
# Generated with inspect_tf_submodules.py for tensorflow 2.12.0.
import tensorflow.audio
print(tensorflow.audio.__name__)
import tensorflow.autodiff
print(tensorflow.autodiff.__name__)
import tensorflow.autograph
print(tensorflow.autograph.__name__)
import tensorflow.bitwise
print(tensorflow.bitwise.__name__)
import tensorflow.compat
print(tensorflow.compat.__name__)
import tensorflow.config
print(tensorflow.config.__name__)
import tensorflow.data
print(tensorflow.data.__name__)
import tensorflow.debugging
print(tensorflow.debugging.__name__)
import tensorflow.distribute
print(tensorflow.distribute.__name__)
import tensorflow.dtypes
print(tensorflow.dtypes.__name__)
import tensorflow.errors
print(tensorflow.errors.__name__)
import tensorflow.estimator
print(tensorflow.estimator.__name__)
import tensorflow.experimental
print(tensorflow.experimental.__name__)
import tensorflow.feature_column
print(tensorflow.feature_column.__name__)
import tensorflow.graph_util
print(tensorflow.graph_util.__name__)
import tensorflow.image
print(tensorflow.image.__name__)
try:
import tensorflow.<warning descr="Module 'initializers' not found">initializers</warning>
print(tensorflow.initializers.__name__)
assert False
except ModuleNotFoundError:
pass
import tensorflow.io
print(tensorflow.io.__name__)
import tensorflow.keras
print(tensorflow.keras.__name__)
import tensorflow.linalg
print(tensorflow.linalg.__name__)
import tensorflow.lite
print(tensorflow.lite.__name__)
import tensorflow.lookup
print(tensorflow.lookup.__name__)
try:
import tensorflow.<warning descr="Module 'losses' not found">losses</warning>
print(tensorflow.losses.__name__)
assert False
except ModuleNotFoundError:
pass
import tensorflow.math
print(tensorflow.math.__name__)
try:
import tensorflow.<warning descr="Module 'metrics' not found">metrics</warning>
print(tensorflow.metrics.__name__)
assert False
except ModuleNotFoundError:
pass
import tensorflow.mlir
print(tensorflow.mlir.__name__)
import tensorflow.nest
print(tensorflow.nest.__name__)
import tensorflow.nn
print(tensorflow.nn.__name__)
try:
import tensorflow.<warning descr="Module 'optimizers' not found">optimizers</warning>
print(tensorflow.optimizers.__name__)
assert False
except ModuleNotFoundError:
pass
import tensorflow.profiler
print(tensorflow.profiler.__name__)
import tensorflow.quantization
print(tensorflow.quantization.__name__)
import tensorflow.queue
print(tensorflow.queue.__name__)
import tensorflow.ragged
print(tensorflow.ragged.__name__)
import tensorflow.random
print(tensorflow.random.__name__)
import tensorflow.raw_ops
print(tensorflow.raw_ops.__name__)
import tensorflow.saved_model
print(tensorflow.saved_model.__name__)
import tensorflow.sets
print(tensorflow.sets.__name__)
import tensorflow.signal
print(tensorflow.signal.__name__)
import tensorflow.sparse
print(tensorflow.sparse.__name__)
import tensorflow.strings
print(tensorflow.strings.__name__)
import tensorflow.summary
print(tensorflow.summary.__name__)
import tensorflow.sysconfig
print(tensorflow.sysconfig.__name__)
import tensorflow.test
print(tensorflow.test.__name__)
import tensorflow.tpu
print(tensorflow.tpu.__name__)
import tensorflow.train
print(tensorflow.train.__name__)
import tensorflow.types
print(tensorflow.types.__name__)
import tensorflow.version
print(tensorflow.version.__name__)
import tensorflow.xla
print(tensorflow.xla.__name__)
@@ -0,0 +1,93 @@
import dataclasses
import os.path
import textwrap
import types
from pathlib import Path
import tensorflow
# Also sys.getsitepackages()
_packaging_root = Path(tensorflow.__file__).parent.parent
# These names are not explicitly exported in tensorflow/__init__.py
_ignored_modules = ['tsl', 'dtensor', 'tools']
@dataclasses.dataclass
class TensorFlowModule:
name: str
importable: bool
def guess_module_own_qname(module: types.ModuleType) -> str | None:
import_path = Path(module.__file__)
if not import_path.is_relative_to(_packaging_root):
return None
import_path = import_path.relative_to(_packaging_root)
if import_path.name == "__init__.py":
import_path = import_path.parent
else:
import_path = import_path.with_suffix("")
return str(import_path).strip(os.sep).replace(os.sep, ".")
def collect_tensorflow_submodules() -> list[TensorFlowModule]:
result = []
for attr_name in dir(tensorflow):
if attr_name.startswith('_') or attr_name in _ignored_modules:
continue
attr_value = getattr(tensorflow, attr_name)
if not isinstance(attr_value, types.ModuleType):
continue
if not Path(attr_value.__file__).is_relative_to(_packaging_root):
continue
try:
__import__(f"tensorflow.{attr_name}")
except ModuleNotFoundError:
importable = False
else:
importable = True
result.append(TensorFlowModule(name=attr_name, importable=importable))
return result
def generate_attr_resolve_test(submodules: list[TensorFlowModule]) -> str:
attr_references = [f"print(tf.{m.name}.__name__)" for m in submodules]
return textwrap.dedent(f"""
# Generated with inspect_tf_submodules.py for tensorflow {tensorflow.__version__}.
import tensorflow as tf
""") + "\n".join(attr_references)
def generate_import_resolve_test(submodules: list[TensorFlowModule]) -> str:
module_imports = []
for m in submodules:
if m.importable:
module_import = textwrap.dedent(f"""\
import tensorflow.{m.name}
print(tensorflow.{m.name}.__name__)\
""")
else:
module_import = textwrap.dedent(f"""\
try:
import tensorflow.{m.name}
print(tensorflow.{m.name}.__name__)
assert False
except ModuleNotFoundError:
pass\
""")
module_imports.append(module_import)
return textwrap.dedent(f"""
# Generated with inspect_tf_submodules.py for tensorflow {tensorflow.__version__}.
""") + "\n".join(module_imports)
def main():
submodules = collect_tensorflow_submodules()
submodules.sort(key=lambda x: x.name)
print(generate_attr_resolve_test(submodules))
print(generate_import_resolve_test(submodules))
if __name__ == '__main__':
main()
@@ -1,17 +0,0 @@
import os
import tensorflow
def module_to_path(module):
file = module.__file__
sep = os.sep
return file[file.rfind("site-packages") + len("site-packages") + len(sep):-len("__init__.py") - len(sep)].replace(sep, ".")
root = tensorflow # or tensorflow.compat.v1
module_type = type(tensorflow)
print('\n'.join('%s %s' % (name, module_to_path(module))
for name in dir(root)
if not name.startswith('_')
for module in (getattr(root, name),)
if isinstance(module, module_type)))
@@ -0,0 +1,51 @@
# Generated with inspect_tf_submodules.py for tensorflow 2.12.0.
import tensorflow as tf
print(tf.audio.__name__)
print(tf.autodiff.__name__)
print(tf.autograph.__name__)
print(tf.bitwise.__name__)
print(tf.compat.__name__)
print(tf.config.__name__)
print(tf.data.__name__)
print(tf.debugging.__name__)
print(tf.distribute.__name__)
print(tf.dtypes.__name__)
print(tf.errors.__name__)
print(tf.estimator.__name__)
print(tf.experimental.__name__)
print(tf.feature_column.__name__)
print(tf.graph_util.__name__)
print(tf.image.__name__)
print(tf.initializers.__name__)
print(tf.io.__name__)
print(tf.keras.__name__)
print(tf.linalg.__name__)
print(tf.lite.__name__)
print(tf.lookup.__name__)
print(tf.losses.__name__)
print(tf.math.__name__)
print(tf.metrics.__name__)
print(tf.mlir.__name__)
print(tf.nest.__name__)
print(tf.nn.__name__)
print(tf.optimizers.__name__)
print(tf.profiler.__name__)
print(tf.quantization.__name__)
print(tf.queue.__name__)
print(tf.ragged.__name__)
print(tf.random.__name__)
print(tf.raw_ops.__name__)
print(tf.saved_model.__name__)
print(tf.sets.__name__)
print(tf.signal.__name__)
print(tf.sparse.__name__)
print(tf.strings.__name__)
print(tf.summary.__name__)
print(tf.sysconfig.__name__)
print(tf.test.__name__)
print(tf.tpu.__name__)
print(tf.train.__name__)
print(tf.types.__name__)
print(tf.version.__name__)
print(tf.xla.__name__)
@@ -1,54 +0,0 @@
app tensorflow_core._api.v1.app
audio tensorflow_core._api.v1.audio
autograph tensorflow_core._api.v1.autograph
bitwise tensorflow_core._api.v1.bitwise
compat tensorflow_core._api.v1.compat
config tensorflow_core._api.v1.config
data tensorflow_core._api.v1.data
debugging tensorflow_core._api.v1.debugging
distribute tensorflow_core._api.v1.distribute
distributions tensorflow_core._api.v1.distributions
dtypes tensorflow_core._api.v1.dtypes
errors tensorflow_core._api.v1.errors
estimator tensorflow_estimator.python.estimator.api._v1.estimator
experimental tensorflow_core._api.v1.experimental
feature_column tensorflow_core._api.v1.feature_column
gfile tensorflow_core._api.v1.gfile
graph_util tensorflow_core._api.v1.graph_util
image tensorflow_core._api.v1.image
initializers tensorflow_core._api.v1.initializers
io tensorflow_core._api.v1.io
keras tensorflow_core.python.keras.api._v1.keras
layers tensorflow_core._api.v1.layers
linalg tensorflow_core._api.v1.linalg
lite tensorflow_core._api.v1.lite
logging tensorflow_core._api.v1.logging
lookup tensorflow_core._api.v1.lookup
losses tensorflow_core._api.v1.losses
manip tensorflow_core._api.v1.manip
math tensorflow_core._api.v1.math
metrics tensorflow_core._api.v1.metrics
nest tensorflow_core._api.v1.nest
nn tensorflow_core._api.v1.nn
profiler tensorflow_core._api.v1.profiler
python_io tensorflow_core._api.v1.python_io
quantization tensorflow_core._api.v1.quantization
queue tensorflow_core._api.v1.queue
ragged tensorflow_core._api.v1.ragged
random tensorflow_core._api.v1.random
raw_ops tensorflow_core._api.v1.raw_ops
resource_loader tensorflow_core._api.v1.resource_loader
saved_model tensorflow_core._api.v1.saved_model
sets tensorflow_core._api.v1.sets
signal tensorflow_core._api.v1.signal
sparse tensorflow_core._api.v1.sparse
spectral tensorflow_core._api.v1.spectral
strings tensorflow_core._api.v1.strings
summary tensorflow_core._api.v1.summary
sysconfig tensorflow_core._api.v1.sysconfig
test tensorflow_core._api.v1.test
tpu tensorflow_core._api.v1.tpu
train tensorflow_core._api.v1.train
user_ops tensorflow_core._api.v1.user_ops
version tensorflow_core._api.v1.version
xla tensorflow_core._api.v1.xla
@@ -1,54 +0,0 @@
app tensorflow._api.v1.app
audio tensorflow._api.v1.audio
autograph tensorflow._api.v1.autograph
bitwise tensorflow._api.v1.bitwise
compat tensorflow._api.v1.compat
config tensorflow._api.v1.config
data tensorflow._api.v1.data
debugging tensorflow._api.v1.debugging
distribute tensorflow._api.v1.distribute
distributions tensorflow._api.v1.distributions
dtypes tensorflow._api.v1.dtypes
errors tensorflow._api.v1.errors
estimator tensorflow_estimator.python.estimator.api._v1.estimator
experimental tensorflow._api.v1.experimental
feature_column tensorflow._api.v1.feature_column
gfile tensorflow._api.v1.gfile
graph_util tensorflow._api.v1.graph_util
image tensorflow._api.v1.image
initializers tensorflow._api.v1.initializers
io tensorflow._api.v1.io
keras tensorflow.python.keras.api._v1.keras
layers tensorflow._api.v1.layers
linalg tensorflow._api.v1.linalg
lite tensorflow._api.v1.lite
logging tensorflow._api.v1.logging
lookup tensorflow._api.v1.lookup
losses tensorflow._api.v1.losses
manip tensorflow._api.v1.manip
math tensorflow._api.v1.math
metrics tensorflow._api.v1.metrics
nest tensorflow._api.v1.nest
nn tensorflow._api.v1.nn
profiler tensorflow._api.v1.profiler
python_io tensorflow._api.v1.python_io
quantization tensorflow._api.v1.quantization
queue tensorflow._api.v1.queue
ragged tensorflow._api.v1.ragged
random tensorflow._api.v1.random
raw_ops tensorflow._api.v1.raw_ops
resource_loader tensorflow._api.v1.resource_loader
saved_model tensorflow._api.v1.saved_model
sets tensorflow._api.v1.sets
signal tensorflow._api.v1.signal
sparse tensorflow._api.v1.sparse
spectral tensorflow._api.v1.spectral
strings tensorflow._api.v1.strings
summary tensorflow._api.v1.summary
sysconfig tensorflow._api.v1.sysconfig
test tensorflow._api.v1.test
tpu tensorflow._api.v1.tpu
train tensorflow._api.v1.train
user_ops tensorflow._api.v1.user_ops
version tensorflow._api.v1.version
xla tensorflow._api.v1.xla
@@ -1,47 +0,0 @@
audio tensorflow_core._api.v2.audio
autodiff tensorflow_core._api.v2.autodiff
autograph tensorflow_core._api.v2.autograph
bitwise tensorflow_core._api.v2.bitwise
compat tensorflow_core._api.v2.compat
config tensorflow_core._api.v2.config
data tensorflow_core._api.v2.data
debugging tensorflow_core._api.v2.debugging
distribute tensorflow_core._api.v2.distribute
dtypes tensorflow_core._api.v2.dtypes
errors tensorflow_core._api.v2.errors
estimator tensorflow_estimator.python.estimator.api._v2.estimator
experimental tensorflow_core._api.v2.experimental
feature_column tensorflow_core._api.v2.feature_column
graph_util tensorflow_core._api.v2.graph_util
image tensorflow_core._api.v2.image
initializers tensorflow_core.python.keras.api._v2.keras.initializers
io tensorflow_core._api.v2.io
keras tensorflow_core.python.keras.api._v2.keras
linalg tensorflow_core._api.v2.linalg
lite tensorflow_core._api.v2.lite
lookup tensorflow_core._api.v2.lookup
losses tensorflow_core.python.keras.api._v2.keras.losses
math tensorflow_core._api.v2.math
metrics tensorflow_core.python.keras.api._v2.keras.metrics
mixed_precision tensorflow_core._api.v2.mixed_precision
mlir tensorflow_core._api.v2.mlir
nest tensorflow_core._api.v2.nest
nn tensorflow_core._api.v2.nn
optimizers tensorflow_core.python.keras.api._v2.keras.optimizers
quantization tensorflow_core._api.v2.quantization
queue tensorflow_core._api.v2.queue
ragged tensorflow_core._api.v2.ragged
random tensorflow_core._api.v2.random
raw_ops tensorflow_core._api.v2.raw_ops
saved_model tensorflow_core._api.v2.saved_model
sets tensorflow_core._api.v2.sets
signal tensorflow_core._api.v2.signal
sparse tensorflow_core._api.v2.sparse
strings tensorflow_core._api.v2.strings
summary tensorboard.summary._tf.summary
sysconfig tensorflow_core._api.v2.sysconfig
test tensorflow_core._api.v2.test
tpu tensorflow_core._api.v2.tpu
train tensorflow_core._api.v2.train
version tensorflow_core._api.v2.version
xla tensorflow_core._api.v2.xla
@@ -1,138 +1,51 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.env.tensorFlow
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.process.CapturingProcessHandler
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.util.Computable
import com.intellij.psi.PsiElement
import com.intellij.testFramework.UsefulTestCase
import com.jetbrains.env.EnvTestTagsRequired
import com.jetbrains.env.PyEnvTestCase
import com.jetbrains.env.PyExecutionFixtureTestTask
import com.jetbrains.python.PythonFileType
import com.jetbrains.python.PythonTestUtil
import com.jetbrains.python.psi.PyFile
import com.jetbrains.python.psi.PyReferenceExpression
import com.jetbrains.python.psi.PyUtil
import com.jetbrains.python.psi.resolve.PyResolveContext
import com.jetbrains.python.psi.types.TypeEvalContext
import com.jetbrains.python.sdk.PySdkUtil
import com.jetbrains.python.PyPsiPackageUtil
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection
import com.jetbrains.python.packaging.PyPackageManager
import com.jetbrains.python.tools.sdkTools.SdkCreationType
import junit.framework.TestCase
import junit.framework.TestCase.assertNotNull
import org.junit.Test
import java.nio.file.Files
import java.nio.file.Paths
import java.util.concurrent.TimeUnit
class PyTensorFlowTest : PyEnvTestCase() {
@Test
@EnvTestTagsRequired(tags = ["tensorflow1", "tensorflow_oldpaths"])
fun tensorFlow1ModulesOldPaths() {
runPythonTest(TensorFlowModulesTask("tf1old.txt", setOf("lite")))
@EnvTestTagsRequired(tags = ["tensorflow"])
fun submoduleResolveAndCompletion() {
runPythonTest(TensorFlowModulesTask())
}
@Test
@EnvTestTagsRequired(tags = ["tensorflow1", "tensorflow_newpaths"])
fun tensorFlow1ModulesNewPaths() {
runPythonTest(TensorFlowModulesTask("tf1new.txt"))
}
@Test
@EnvTestTagsRequired(tags = ["tensorflow2", "tensorflow_newpaths"])
fun tensorFlow2ModulesNewPaths() {
runPythonTest(TensorFlowModulesTask("tf2new.txt"))
}
private class TensorFlowModulesTask(private val expectedModulesFile: String,
private val modulesToIgnoreInResolve: Set<String> = emptySet()) : PyExecutionFixtureTestTask(null) {
private class TensorFlowModulesTask : PyExecutionFixtureTestTask("packages/tensorflow") {
override fun runTestOn(sdkHome: String, existingSdk: Sdk?) {
val actualModules = loadActualModules(createTempSdk(sdkHome, SdkCreationType.SDK_PACKAGES_AND_SKELETONS))
val sdk = createTempSdk(sdkHome, SdkCreationType.SDK_PACKAGES_AND_SKELETONS)
val packages = PyPackageManager.getInstance(sdk).refreshAndGetPackages(false)
assertNotNull(PyPsiPackageUtil.findPackage (packages, "tensorflow"))
val expectedModules = loadModules(
Files.readAllLines(Paths.get(PythonTestUtil.getTestDataPath(), "packages", "tensorflow", expectedModulesFile))
)
TestCase.assertEquals(expectedModules, actualModules)
myFixture.enableInspections(PyUnresolvedReferencesInspection::class.java)
myFixture.configureByFile("submodulesExportedAsAttributes.py")
myFixture.checkHighlighting()
myFixture.configureByFile("importableSubmoduleExports.py")
myFixture.checkHighlighting()
runCompletion(actualModules.keys)
runResolve(actualModules)
}
myFixture.configureByText("a.py", "import tensorflow.<caret>")
assertNotNull(myFixture.completeBasic())
UsefulTestCase.assertContainsElements(myFixture.lookupElementStrings!!, "estimator", "keras", "summary", "config")
UsefulTestCase.assertDoesntContain(myFixture.lookupElementStrings!!, "optimizers", "metrics")
private fun loadActualModules(sdk: Sdk): Map<String, String> {
val script = Paths.get(PythonTestUtil.getTestDataPath(), "packages", "tensorflow", "modules.py").toAbsolutePath().toString()
val env = PySdkUtil.activateVirtualEnv(sdk)
val timeout = TimeUnit.SECONDS.toMillis(30).toInt()
val commandLine = GeneralCommandLine(sdk.homePath).withParameters(script).withEnvironment(env)
return loadModules(CapturingProcessHandler(commandLine).runProcess(timeout, true).stdoutLines)
}
private fun loadModules(lines: List<String>): Map<String, String> {
return lines
.asSequence()
.map { it.split(' ', limit = 2) }
.map { it[0] to it[1] }
.toMap()
}
private fun runCompletion(modules: Collection<String>) {
// `from tensorflow.<m>` completes
// `import tensorflow.<m>` completes
// `tensorflow.<m>` completes
configureAndCompleteAtCaret("from tensorflow.<caret>", modules)
configureAndCompleteAtCaret("import tensorflow.<caret>", modules)
configureAndCompleteAtCaret("import tensorflow\ntensorflow.<caret>", modules)
}
private fun runResolve(modules: Map<String, String>) {
// `from tensorflow.<m>` resolves
// `import tensorflow.<m>` resolves
// `tensorflow.<m>` resolves
// everything resolves to the same element
modules.asSequence().filter { (module, _) -> module !in modulesToIgnoreInResolve }.forEach { (module, path) ->
val first = configureAndResolveAtCaret("from tensorflow.$module<caret> import *")
val second = configureAndResolveAtCaret("import tensorflow.$module<caret>")
val third = configureAndResolveAtCaret("import tensorflow\ntensorflow.$module<caret>")
UsefulTestCase.assertSame("first: ${moduleToPath(first)} vs second: ${moduleToPath(second)}", first, second)
UsefulTestCase.assertSame("first: ${moduleToPath(first)} vs third: ${moduleToPath(third)}", first, third)
TestCase.assertEquals(path, moduleToPath(first))
}
}
private fun configureAndCompleteAtCaret(text: String, modules: Collection<String>) {
myFixture.configureByText(PythonFileType.INSTANCE, text)
myFixture.configureByText("a.py",
"""
import tensorflow as tf
tf.<caret>
""".trimIndent())
myFixture.completeBasic()
UsefulTestCase.assertContainsElements(myFixture.lookupElementStrings!!, modules)
}
private fun configureAndResolveAtCaret(text: String): PsiElement {
val file = myFixture.configureByText(PythonFileType.INSTANCE, text)
return ApplicationManager.getApplication().runReadAction(
Computable {
val reference = myFixture.file.findElementAt(myFixture.caretOffset - 1)!!.parent as PyReferenceExpression
val resolveContext = PyResolveContext.defaultContext(TypeEvalContext.codeAnalysis(project, file))
PyUtil.turnDirIntoInit(reference.followAssignmentsChain(resolveContext).element)!!
}
)
}
private fun moduleToPath(module: PsiElement): String {
return ApplicationManager.getApplication().runReadAction(
Computable {
var current = (module as PyFile).containingDirectory
val components = mutableListOf<String>()
while (current.name != "site-packages") {
components.add(current.name)
current = current.parent
}
components.asReversed().joinToString(".")
}
)
UsefulTestCase.assertContainsElements(myFixture.lookupElementStrings!!,
"estimator", "keras", "summary", "config", "optimizers", "metrics")
}
}
}