All Downloads are FREE. Search and download functionalities are using the official Maven repository.

utils.PythonInspector.kt Maven / Gradle / Ivy

/*
 * Copyright (C) 2022 The ORT Project Authors (see )
 *
 * 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
 *
 *     https://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.
 *
 * SPDX-License-Identifier: Apache-2.0
 * License-Filename: LICENSE
 */

package org.ossreviewtoolkit.plugins.packagemanagers.python.utils

import java.io.File

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNamingStrategy
import kotlinx.serialization.json.decodeFromStream

import org.ossreviewtoolkit.utils.common.CommandLineTool
import org.ossreviewtoolkit.utils.common.safeDeleteRecursively
import org.ossreviewtoolkit.utils.ort.createOrtTempFile

import org.semver4j.RangesList
import org.semver4j.RangesListFactory

private val json = Json {
    ignoreUnknownKeys = true
    namingStrategy = JsonNamingStrategy.SnakeCase
}

internal object PythonInspector : CommandLineTool {
    override fun command(workingDir: File?) = "python-inspector"

    override fun transformVersion(output: String) = output.removePrefix("Python-inspector version: ")

    override fun getVersionRequirement(): RangesList = RangesListFactory.create("[0.9.2,)")

    fun inspect(
        workingDir: File,
        definitionFile: File,
        pythonVersion: String,
        operatingSystem: String,
        analyzeSetupPyInsecurely: Boolean
    ): Result {
        val outputFile = createOrtTempFile(prefix = "python-inspector", suffix = ".json")

        val commandLineOptions = buildList {
            add("--python-version")
            add(pythonVersion)

            add("--operating-system")
            add(operatingSystem)

            add("--json-pdt")
            add(outputFile.absolutePath)

            if (analyzeSetupPyInsecurely) {
                add("--analyze-setup-py-insecurely")
            }

            if (definitionFile.name == "setup.py") {
                add("--setup-py")
            } else {
                add("--requirement")
            }

            add(definitionFile.absolutePath)

            if (definitionFile.name != "setup.py") {
                // If a setup.py file exists, add it to the analysis to capture additional project metadata.
                val setupFile = definitionFile.resolveSibling("setup.py")
                if (setupFile.isFile) {
                    add("--setup-py")
                    add(setupFile.absolutePath)
                }
            }

            add("--verbose")
        }

        return try {
            run(workingDir, *commandLineOptions.toTypedArray())
            outputFile.inputStream().use { json.decodeFromStream(it) }
        } finally {
            outputFile.parentFile.safeDeleteRecursively()
        }
    }

    @Serializable
    internal data class Result(
        @SerialName("files") val projects: List,
        val resolvedDependenciesGraph: List,
        val packages: List
    )

    @Serializable
    internal data class Project(
        val path: String,
        val packageData: List
    )

    @Serializable
    internal data class PackageData(
        val namespace: String?,
        val name: String?,
        val version: String?,
        val description: String?,
        val parties: List,
        val homepageUrl: String?,
        val declaredLicense: DeclaredLicense?
    )

    @Serializable
    internal data class DeclaredLicense(
        val license: String? = null,
        val classifiers: List = emptyList()
    )

    @Serializable
    internal data class ResolvedDependency(
        val key: String,
        val packageName: String,
        val installedVersion: String,
        val dependencies: List
    )

    @Serializable
    internal data class Package(
        val type: String,
        val namespace: String?,
        val name: String,
        val version: String,
        val description: String,
        val parties: List,
        val homepageUrl: String?,
        val downloadUrl: String,
        val size: Long,
        val sha1: String?,
        val md5: String?,
        val sha256: String?,
        val sha512: String?,
        val codeViewUrl: String?,
        val vcsUrl: String?,
        val copyright: String?,
        val licenseExpression: String?,
        val declaredLicense: DeclaredLicense?,
        val sourcePackages: List,
        val repositoryHomepageUrl: String?,
        val repositoryDownloadUrl: String?,
        val apiDataUrl: String,
        val purl: String
    )

    @Serializable
    internal class Party(
        val type: String,
        val role: String,
        val name: String?,
        val email: String?,
        val url: String?
    )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy